GitHub ActionsのIssueの内容をパラメータファイルにしてプログラムを起動する

こんにちは。Kaneyasuです。

私の周りでは楽をするために作った小さいプログラムが多数あります。
それらは便利ではあるのですが、CLI止まりなので属人性が高いのが悩みです。
画面を作ればベストですが、そこまでの力は注げません。
そこで、GitHub　ActionsとIssueでプログラムの実行を若干楽にできないかと考えてみました。

本記事の目標は、Issueの起票をトリガーに、Issueの内容を引数にプログラムを起動することとしています。
本記事では、起動するプログラムをPythonで書いたAWS Lambda関数としています。

ワークフローで起動するプログラムはAWS Lambda関数である必要はありません。
本記事が元々はAPI用に作っていたコードを流用したのと、Lambda関数は引数をJSONで渡せるのがサンプルとしてちょうどいいのでそのままとしました。
みなさんが動かすものはLambda関数ではないかもしれませんが、パラメータをファイルで渡す手法は広く使われています。
従って、私が行ったIssueをファイル化してプログラムに渡す流れが参考になるのではと思います。

# ワークフローの流れ

1. Issueの起票をトリガーに、GitHub Actionsのワークフローを起動
2. PythonとAWS SAMをインストール（ここは説明を省略します）
3. Issueの本文をJSONファイル化してパラメータファイルとする
4. パラメータファイルを渡しながらLambda関数を起動

# Issueの起票をトリガーに、GitHub Actionsのワークフローを起動

リポジトリの`.github/workflows`にYAMLファイルを置くと、なんらかのイベントをトリガーにYAMLに応じたワークフローを起動することができます。

https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

トリガーにできるイベントは様々で、Issueの作成をトリガーにすることもできます。
今回はこれを利用します。

https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues

Issueの作成をトリガーにするとこのような記述になります。

```yaml
name: Issue Triggered Workflow

on:
  issues:
    types:
      - opened
```
# Issueの本文をJSONファイル化してパラメータファイルとする

## Issueのフォーマットを決める

Issueのフォーマットを下記のように決めます。
コードブロックでJSONを書いてもらう形式で、JSONの部分がLambda関数に渡す引数となります。
上側は説明ブロックです。
JSONはコメントが書けないので、上側に説明を持ってきました。
コードブロックはトリプルバッククォートではなく、「~~~」で括っています。
「```」ですると、この後に出てくるファイル出力で各行がコマンドと認識されてしまうのでその回避策です。

```
| プロパティ | 内容 |
| --- | --- |
| column1 | column1の説明 |
| column2 | column2の説明 |
| column3 | column3の説明 |

~~~json
{
  "body": {
    "column1": "100",
    "column2": "200",
    "column3": "300",
  }
}
```

## Issueのテンプレート

Issueはテンプレートを作れます。
実際に動かすときは上記フォーマットをテンプレートにしました。

https://docs.github.com/ja/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository

## JSONファイル化してパラメータファイルとする

GitHub Actionsのワークフローの途中に、Issueのファイル化、そして不要な部分をカットしてJSON化するコマンドを仕込みます。
このようにしてみました。

```yaml
      - name: Export issue contents to file
        run: |
          cat <<EOF > issue_body.md
          ${{ github.event.issue.body }}
          EOF
          cat issue_body.md
          sed -n '/~~~json/,/~~~/p' issue_body.md | sed '1d;$d' > issue_body.json
          cat issue_body.json
```

ワークフローが実際に動くと、`${{ github.event.issue.body }}`の部分がIssueの本文に置き換わります。
これを`cat`コマンド＋リダイレクトでファイル出力します。
この時、Issueの本文にトリプルバッククォートがあると、「cat <<EOF > issue_body.md」から「EOF」の間にトリプルバッククォートが入り込み、正常に動作しなくなります。
前述のIssueのフォーマットで、コードブロックを「~~~」としているのはこのためです。

次に`sed`コマンドを用いて、「~~~json」から「~~~」までを切り取っています。
これをまたリダイレクトによるファイル出力でJSONファイルを作っています。

できあがったJSONファイルをパラメータファイルとします。

# パラメータファイルを渡しながらLambda関数を起動

Lambda関数をローカル実行するコマンドで、パラメータファイル＝出力したJSONファイルを指定します。
これでLambda関数にパラメータが渡ります。

```yaml
      - name: Invoke Lambda 
        run: sam local invoke "SampleFunction" --event issue_body.json
```

# リポジトリの構成変数のJSONファイル化

前項までで、Lambda関数にパラメータを渡せました。
Lambda関数をローカル実行するコマンドでは、JSONファイルを指定することで環境変数も渡せます。
DBのテーブル名など、固定値的なものは環境変数で渡すことが多いので、ついでにこれもやってみます。

GitHubにはリポジトリ単位で設定できる構成変数があるので、ついでにこれを環境変数の設定場所に活用してみます。

https://docs.github.com/ja/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository

構成変数には「Secrets」と「Variables」があります。
APIキーの秘密情報は「Secrets」、それ以外を「Variables」で設定すると使い分けることにします。

```yaml
      - name: Create env.json file
        run: |
          cat << EOF > env.json
          {
              "Parameters": {
                  "SAMPLE_API_KEY": "${{ secrets.SAMPLE_API_KEY }}",
                  "SAMPLE_VAR": "${{ vars.SAMPLE_VAR }}"
              }
          }
          EOF
          cat env.json
```

これで、構成変数をJSONファイル化できます。
構成変数の「Secrets」の値は`secrets`で取れます。
「Variables」の値は`vars`で取れます。

構成変数から作ったJSONファイルもLambda関数をローカル実行するコマンドで指定します。
これでLambda関数にリポジトリの構成変数が環境変数として渡ります。

```yaml
      - name: Invoke Lambda 
        run: sam local invoke "SampleFunction" --event issue_body.json --env-vars env.json
```

# 全文

ここまでを踏まえた、ワークフローの全文です。

```yaml:issue_triggerd.yml
name: Issue Triggered Workflow

on:
  issues:
    types:
      - opened
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code    
        uses: actions/checkout@v3

      - name: Set up Python 3.11.x
        uses: actions/setup-python@v5
        with:
          python-version: '3.11.6'
          cache: 'pipenv'

      - name: Install pipenv
        run: |
          python -m pip install --upgrade pip
          python -m pip install pipenv
          
      - name: Install dependencies
        run: |
          pipenv install

      - name: install AWS SAM    
        uses: aws-actions/setup-sam@v2
        with:
          use-installer: true

      - name: Create env.json file
        run: |
          cat << EOF > env.json
          {
              "Parameters": {
                  "SAMPLE_API_KEY": "${{ secrets.SAMPLE_API_KEY }}",
                  "SAMPLE_VAR": "${{ vars.SAMPLE_VAR }}"
              }
          }
          EOF
          cat env.json

      - name: Export issue contents to file
        run: |
          cat <<EOF > issue_body.md
          ${{ github.event.issue.body }}
          EOF
          cat issue_body.md
          sed -n '/~~~json/,/~~~/p' issue_body.md | sed '1d;$d' > issue_body.json
          cat issue_body.json

      - name: Build 
        run: pipenv requirements > requirements_layer/requirements.txt; sam build

      - name: Invoke Lambda 
        run: sam local invoke "SampleFunction" --event issue_body.json --env-vars env.json

```

何か他にいい方法があれば教えていただけると幸いです。