ビルドした時点での環境変数を常に参照してほしいということは、運用上たまに起こる要求なのではないかと思います。
この要求を解決するためのそれっぽいライブラリはあるのですが1、要求に対して機能が過剰だなという感じでした。
ほしかったのは以下のような機能です。
- 入力を受け取って環境変数をロードするgoファイルを生成する
- 環境変数をロードするためにメインのコードでimportをする必要がない
- つまり、このライブラリがなくてもコンパイルを通すことが出来る
こんな感じのシンプルなライブラリが欲しかったので作りました。
使い方
環境変数を定義するシンタックスの標準入力を受け取り、環境変数をロードするためのコードを標準出力に出力します。
goのファイルにはシェルのパイプを使って出力することになります。
# システム環境変数を入力
echo FOO=$ENV_VAR1\nBAR=$ENV_VAR1 | envenb > envenb.go
# ファイルから入力
cat .env | envenb > envenb.go
出力されたファイルをビルド時に含めればコードからこの環境変数を利用出来るようになります。
go build mian.go envenb.go
package main
import (
"fmt"
"os"
)
func main {
fmt.Println(os.Getenv("FOO")) // foo
fmt.Println(os.Getenv("BAR")) // bar
}
ご覧の通り、メインのコードには環境変数を読み込むためのライブラリによる依存が全く見当たりません。
どうやって環境変数をロードしているのか?
Goのグローバル変数がプロセス起動時に評価されるのを利用しています。
グローバル変数への代入に即時関数を利用し、即時関数内で環境変数の設定を行うことでライブラリの依存なくコンパイルを通せるようにしているのです。
import "os"
var _ = func() interface{} {
var envValues = map[string]string{%v} // %vの箇所に標準入力から受け取った値が置換されて置かれます。
for key, value := range envValues {
if err := os.Setenv(key, value); err != nil {
log.Fatalln(err)
}
}
return nil
}()
あしたは @kikuchy の「Diverse Podcastを支える技術」です。
同じ会社にいながら、このあたりの話を全く知らないので楽しみですね!
-
名前忘れました… ↩