GitHubで管理しているAngularプロジェクト内に、シークレットとか見られたくない変数を定義したくない時の対処法

angularは環境変数をenvironment.tsおよびenvironment.prod.tsに定義することでビルド時に読み込み先を振り分けてますが、これだと環境変数をベタ書きしないといけないため、publicリポジトリで管理するような場合にAPI Keyやシークレット等、他の人に見られたらまずいものがある場合にどうしようとなってしまいます。。

いろいろ調べたら、どうにかできたのでその方法を残しておきます。


# 解決策概要

- ローカル環境では.envをenvironment.tsにマージ
    - .envはGit管理に追加しない
- 本番環境（GitHub Pages）ではActions Secretsで定義した変数をenvironment.tsにマージ

これで他の人から見えない変数定義が可能となります




# 対応

### 準備

```shell
# ts-nodeインストール（後述のバッチ実行用）
npm install --save-dev ts-node

# dotenvインストール
npm install --save-dev dotenv
```

### .env作成

今回はAPI_KEYを隠したい想定で作成します

```.env
API_KEY=test
```

### environmentの修正

- environment.tsを直接書き換えても良いですが、リスクがあるので動的に書き換える部分は外出しします
    - dynamic.tsに出力する想定でenvironment.tsおよびprod.tsを修正します
    - 後述のdynamic.tsへの書き込みにより、.envとマージされることになります

```src/environments/environment.ts
import { dynamic } from './dynamic';

export const environment = {
  production: false,
  dynamic,
};
```

### environmentの書き換えバッチ作成

こいつで.envの内容をdynamic.tsに書き込んでいきます

```config/write-env.ts
import * as fs from 'fs';
import { join } from 'path';
import { config } from 'dotenv';

const dotenv = config()?.parsed;

// 一応システムの環境変数でも動かせるようにデフォルトはシステムの環境変数にする
const env = {
  apiKey: process.env.API_KEY,
};

// .envが読めた場合は、.envの内容を設定
if (dotenv) {
  env.apiKey = dotenv.API_KEY;
}

// dynamic.tsに書き込み
const contents = 'export const dynamic = ' + JSON.stringify(env);
fs.writeFileSync(
  join(__dirname, '../src/environments/dynamic.ts'),
  contents
);

```

### .gitignore修正

せっかく見えないようにしたので、.envとdynamic.tsはGit管理対象外にします

```.gitignore
.env
src/environment/dynamic.ts
```



### package.jsonの修正

- `write-env`を追加し、上記で追加したバッチを呼び出します
- startとbuildで、最初にwrite-envを呼ぶようにします

```package.json
  "scripts": {
    "ng": "ng",
    "start": "npm run write-env && ng serve --port 8000",
    "build": "npm run write-env && ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "write-env": "ts-node  -O '{\"module\": \"commonjs\"}' ./config/write-env.ts"
  },
```


ローカル環境はこれでOKです！


# GitHub Pagesへのデプロイ

### Actions Secretの追加

- GitHubにて「Settings -> Secrets」からActions Secretを登録します
    - 今回はAPI_KEYを追加します

### デプロイ設定

- 基本の設定は以前に書いた以下の記事の内容となります
    - https://qiita.com/teracy55/items/ea40bb94bef809180f1c
- ビルド前に.env作成ジョブを追加します
    - `${{ secrets.API_KEY }}`でActions Secretに追加した値を参照できます
    - `echo API_KEY=$API_KEY >> .env`で.envに出力します

```.github/workflows/deploy.yml
name: Node CI

on:
  push:
    branches: main

jobs:
  build:
    runs-on: ubuntu-18.04

    strategy:
      matrix:
        node-version: [12.x]

    steps:
      - uses: actions/checkout@v1
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: create .env
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          echo API_KEY=$API_KEY >> .env
      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present -- --prod
          npm test -- --watch=false --browsers=ChromeHeadless
          cp -a dist/{project name}/index.html dist/{project name}/404.html
        env:
          CI: true
      - name: Deploy 🚀
        uses: JamesIves/github-pages-deploy-action@3.7.1
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BRANCH: gh-pages
          FOLDER: dist/{project name}
          CLEAN: true

```

これでmainブランチにプッシュしたときに自動でGitHub Pagesにデプロイされます！



# 最後に

結構長いこと気になってたことが解決できました！
これで安心してpublicリポジトリで公開できますね＾＾


# 参考

[GitHub Actions: Hide And Set Angular Environment Variables](https://itnext.io/github-actions-hide-and-set-angular-environment-variables-e753d06d16a8)