goでcliツールを作ってみる

この記事は[Fusic Advent Calendar](https://qiita.com/advent-calendar/2019/fusic)15日目の記事です。

こばんは。お久しぶりです。まっさんです。
アドベントカレンダー何を書こうか迷っていたら、こんな時間になっていまいました。良い子はちゃんと事前に準備しときましょう。

とくに何も閃かなかったので、[弊社](https://fusic.co.jp/)の一部のメンバーで行っている朝の15分輪読会で、絶賛勉強中のgo言語について書いてみようと思います。（golang初学者ですので、誤り等ありました。ご指摘いただければ幸いです）


## 社内のニコニコカレンダーのcliツールを作ってみる

弊社には、@Y_uuu が作成した、nicoleというニコニコカレンダーがあります。
社員の20〜５0%が日々投稿する人気ツールです。
そんなニコニコカレンダーのcliツールを作ってみました。リファクタリングとか、してないので、色々ツッコミどころ満載ですが、こんな感じです。

```Go
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

type PostData struct {
	Mood    string `json:"mood"`
	Message string `json:"message"`
	Date    string `json:"date"`
}

func main() {
	postJson := createJson()
	if postJson == nil {
		return
	}
	key := getEnvKey()

	req, err := http.NewRequest("POST", "{{ ニコニコカレンダーのAPIのURL }}", bytes.NewBuffer(postJson))
	req.Header.Set("API-KEY", key)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Sprintf("req err: %s", err)
	}
	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err == nil {
		fmt.Sprintf("resp err: %s", err)
	}
	fmt.Println(string(respBody))
}

func createJson() []byte {
	t := time.Now()
	defaultMood := "normal"
	defaultMessage := "普通"
	mood := flag.String("mood", defaultMood, "気分")
	key := flag.String("api_key", "", "あなたのApiキーを入力して下さい。")
	message := flag.String("message", defaultMessage, "今日の一言")
	date := flag.String("date", t.Format("2006-01-02"), "日時")
	flag.Parse()

	if *key != "" {
		setEnvKey(*key)
		return nil
	}
	postData := PostData{*mood, *message, *date}
	outputJSON, err := json.Marshal(&postData)
	if err != nil {
		fmt.Sprintf("json err: %s", err)
	}
	return outputJSON
}

func setEnvKey(key string) {
	file, err := os.Create(`./.env`)
	if err != nil {
		fmt.Sprintf("create file err: %s", err)
	}
	defer file.Close()
	output := key
	file.Write(([]byte)(output))
}

func getEnvKey() string {
	file, err := os.Open("./.env")
	if err != nil {
		fmt.Sprintf("open file err: %s", err)
	}
	defer file.Close()

	var envKey string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		envKey = line
	}
	if err := scanner.Err(); err != nil {
		fmt.Sprintf("read file err: %s", err)
	}
	return envKey
}
```

## 躓いたポイント

社内のニコニコカレンダーのAPIを叩くために、マイページで作成した `API_KEY` が必要になります。
ファイルに書き出すよりも、環境変数にセットしたかったので、ググったところ、`os`パッケージをインポートすると、いい感じに環境変数の読み取り、書き込みができるとのこと。
こんな感じ。

### 読み取り
```Go
package main

import (
    "fmt"
    "os"
)

func main() {
    val := os.Getenv("HOGE")
    fmt.Println(val)
}
```
設定されてない場合は、空文字が返ります。

### 書き込み
```Go
package main

import (
    "fmt"
    "os"
)

func main() {
    os.Setenv("HOGE", "hoge_value")
}
```

とても、簡単ですね。しかし、いざ使ってみると、上手く`API_KEY`が取得できない。

どうも、Goの動いているプロセスに対して、環境変数がセットされるようで、そのプロセスが死ぬの環境変数も消えてしまう？っぽい。（間違っていたらすみません。）

## 外部コマンドに頼る

`os`パッケージを使って環境変数にセットしよう作戦はあえなく、失敗したので、`Go`から`export HOGE=hoge`のように`os`のコマンドを呼び出して無理やり環境変数を設定したいと思います。
`os/exec` パッケージを使用して外部コマンドを実行できるようです。こんな感じ。

```Go
package main

import (
	"fmt"
	"os/exec"
)

func main() {
	out, err := exec.Command("ls", "-la").Output()
	if err != nil {
		fmt.Println(err)
	}

	fmt.Printf(string(out))
}
```

`exec.Command` で環境変数をセットしようとしたのですが、こちらは

```
exec: "export": executable file not found in $PATH
```
とエラーになり上手く行かず。（理由もまだわかって何ので、わかったら追記します。。。）

## おとなしくドットファイルに書き込む

環境変数にセットするのは、上手く行かなかったので、おとなしくドットファイルを作成して、そこで書き込みと読み取りを行うことにしました。

## 終わりに

まだまだ、`Go`っぽい書き方とか`Go`の良さが活かせていないので、日々の輪読をとして精進しようと思います。

明日は、　@yoshitake_1201 の投稿ですので、皆様お楽しみにー！！！