環境変数周りの改善

# はじめに

Web系のプロジェクトで開発をしていると`.env` `.env.development.` `.env.test`などにシークレット情報や、環境ごとに異なる設定を書くことが多い

環境変数からデータを読み込めために（言語やフレームワークに関わらず）おおよそ下記のようなファイルを用意する


1 機密情報を格納するためのファイル(.env)の準備

```.env
API_KEY='xs9djq4iq'
DEBUG='TRUE'
```

2 当然.gitignoreで.envをバージョン管理から除外

```.gitignore
.env
```

３ バージョン管理から除外すると`.env`にどのような値が存在するかの管理が難しくなるので`.env.sample`を用意
```.env.sample
API_KEY='CHANGE ME'
DEBUG='true'
```

4 環境変数を取得するコードを書く(下記はnode.jsの場合)

```env.ts
const API_KEY: string  = process.env.API_KEY
const DEBUG  : boolean = process.env.DEBUG === true
```






# `.env`に値を追加するとき

`.env`と`env.ts`と`.env.sample`の3箇所に同じような記述をする必要がある。

```.env
API_KEY='xs9djq4iq'
DEBUG='TRUE'
NEW_ENV='xxx'
```

```env.ts
const API_KEY: string  = process.env.API_KEY
const DEBUG  : boolean = process.env.DEBUG === true
const NEW_ENV: string  = xxx
```

```.env.sample
API_KEY='CHANGE ME'
DEBUG='true'
NEW_ENV='Change ME'
```

## 辛い点１ タイポ
↓こういうタイポが起きる(ことがある)

```.env.sample
NEW_EMV='Change ME'
```
環境変数周りは、エディターがタイポを検出するのが難しいので、目検になる。辛い。

## 辛い点2 煩わしい

3箇所に似たような記述したくない。

# 作ったもの1

https://github.com/ppdx999/deno-env-codegen

`.env`からTypescriptのコードを自動生成する→煩わしさやタイポリスクが減る

## Usage

```.env
# this is a comment
str="value"
nospace=nospace

# a few blank lines

is_true=true
is_false=false
num=123
zero=0
str_start_zero=0123
```

```sh
$ deno run cli -A -i 'path to env file' -o 'path to output file' -t 'deno or node'
```

```output.ts
/************************************
* This file is generated by deno-env-codegen
************************************/

const getEnv = (key: string) => {
  const val = process.env[key];
  if (val === undefined) {
    throw new Error(`Environment variable "\${key}" is not defined`);
  }
  return val;
};

const toNumber = (val: string) => {
  return Number(val);
};

const toBoolean = (val: string) => {
  return val === "true";
};

// this is a comment
export const str = getEnv("str");
export const nospace = getEnv("nospace");

// a few blank lines

export const is_true = toBoolean(getEnv("is_true"));
export const is_false = toBoolean(getEnv("is_false"));
export const num = toNumber(getEnv("num"));
export const zejro = toNumber(getEnv("zero"));
export const str_start_zero = getEnv("str_start_zero");
```


# 作ったもの2

https://github.com/ppdx999/deno-env-diff

`.env`と`.env.sample`のdiffを表示する→タイポミスの防止& `.env`と`.env.sample`の乖離防止

```.env
env='env'
env2='env2'
env3='env3'
env_and_sample='env_and_sample-in-env'
env_and_sample2='env_and_sample-in-env2'
```

```.env.sample
sample='sample'
sample2='sample2'
sample3='sample3'
env_and_sample='env-and-sample-in-sample'
env_and_sample2='env_and_sample-in-sample2'
```

```shell
$ deno run --allow-read=. cli.ts .env .env.sample
Keys that exist in .env.sample but not in .env: sample, sample2, sample3
Keys that exist in .env but not in .env.sample: env, env2, env3
```

# 課題
- `.env`にはあるが、コード中には必要ない値も`env.ts`に生成されてしまう。
- `.env`と`.env.sample`の整合性チェックは、結局のところ人間の目に頼ってしまっている
- cli形式でパッと読み込める形でパッケージ化していない
- 整合性チェック弱い※

# ※整合性チェックが弱い件について
「作ったもの２」ではtsのコードを自動生成するという方法を取ったけど、`zod`みたいなものを利用したスキーマ定義を`.env`を自動生成する形の方が安全で使いやすいかもしれない。(たとえば、「作ったもの２」だと `DEBUG=ture`みたいな明らかなタイポでさえも検知できない`src/env.ts`ではbooleanの`false`になってしまう)

例)
```terminal
$ env-gen -i '.env' -o './src/env.ts'
```

```.env
PORT=8080
HOST=localhost
NODE_ENV=development
DEBUG=true
```

```src/env.ts
import z from 'zod'
import process from 'process'

export type Env = typeof env;
export const env = z.object({
    PORT: z.number(),
    HOST: z.string(),
    NODE_ENV: z.enum(["development", "production"]).default('development']),
    DEBUG: z.boolean(),
}).parse(process.env);
```
みたいに使えた方が安全でメンテナンスしやすいかもしれない(開発の要件にもよる)