Github Actionsなんもわからん部～Terraformで書いてpull requestでplan～

# Github Actionsなんもわからん
Github Actionsなんも分かりません。わからないのでおててを動かしていきます。

# Github actionsとは？
GithubActinosは、CI(Continuous Integration)/CD(Continuous delivery/deployment)のためのツールになります。
gpt4oに聞いてみました。

>GitHub Actions（ギットハブ・アクションズ）は、GitHub上で自動化されたワークフロー（作業の流れ）を実行できるCI/CDツールです。
>
>もっと具体的に言うと…
> GitHub Actionsを使うと、以下のような作業を自動で行えます：
> - コードがプッシュされたときに自動でテストを実行する
> - プルリクエストが作られたときにコードをビルドして確認する
> - 本番環境にデプロイする
> - 定期的なバッチ処理を走らせる（毎日・毎週など）
> - 静的解析やフォーマットチェックを行う

だそうです。
コードを書いた後のテストやデプロイなどの工程を自動化することで、効率化を図れるサービスのようです。自動化万歳！

## まずは、公式のチュートリアルを見て学ぶ

今回は、まずは公式のチュートリアルがあったのでそれをすべて見てから話を進めていきます。

https://github.com/skills/hello-github-actions/tree/main

(このチュートリアル、ページをリロードするとREADME.mdが徐々に更新されるというなかなか粋なものなのでぜひとも味わってほしい。)

### step1 リポジトリを作成する

上記のページから飛んで、start courseというところをクリックし、リポジトリを作成しました。その後、少し待ってからページをリロードしました。

### step2 pull requestを出したうえでworkflowを設定する

Github Actionsについての説明を読んだ後、`main`ブランチへのプルリクエストを出した後、`welcome-workflow`ブランチ内部`.github/workfrows`の下に次のファイルを作成しました。
コードの説明はgpt-4oにお願いしました。

```yml:welcome.yml
name: Post welcome comment # github actionsのタブに表示される名前
on: # イベントのトリガー　ワークフローが開始する条件
  pull_request: # プルリクエストをトリガーとして指定
    types: [opened]  # プルリクエストが新規に作成された場合にのみ反応する
permissions: # github apiのアクセス権限を指定
  pull-requests: write # プルリクエストに書き込みをする権限を与えるもの　
```

### step3 workflowを編集する

先ほど作成した`welcome.yml`を以下のように編集しました。

```yml:welcome.yml
name: Post welcome comment
on:
  pull_request:
    types: [opened]
permissions:
  pull-requests: write
jobs: # 実際に行う処理の流れを定義(jobs)
  build: # それぞれの個別処理の名前 (jobのID)
    name: Post welcome comment # 表示されるjobの名前
    runs-on: ubuntu-latest # ジョブが実行される仮想環境(ランナー)の指定 今回はubuntu-latest
```

これによって、workflowを実行する環境を設定できた。

### step4 実際にpull-requestにコメントを自動で添付する

次に、githubのアクセストークンを発行しました。作成方法は以下を参考にしました。

https://qiita.com/soranjiro/items/3909b8e0598c01cac5e7

1\. githubのサイトにログイン
2\. profile(画面右上のアカウント画像クリック)
3\. 開発者設定
4\. personal access tokensのtokens(classic)を選択
5\. generate new token -> generate new token (classic)
6\. repoとworkflowにチェックを入れて作製
7\. 表示されたトークンをコピー
8\. 自分のリポジトリの上の方の一番右のsettingsを選択
9\. 左サイドバーのsecrets and variablesのactionsを選択
10\. リポジトリの環境変数に、secretわ設定する。(name:TOKEN_GITHUBACTIONSに設定。)

:::note 
リポジトリの環境変数の名前を、`github_`から始まるものにすることはできないとのことです。なので、今回は名称をサンプルコードから変更しています。
:::

```yml:welcome.yml
name: Post welcome comment
on:
  pull_request:
    types: [opened]
permissions:
  pull-requests: write
jobs:
  build:
    name: Post welcome comment
    runs-on: ubuntu-latest
    steps:
      - run: gh pr comment $PR_URL --body "Welcome to the repository!"
        env:
          GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUBACTIONS }}
          PR_URL: ${{ github.event.pull_request.html_url }}
```

実際にworkflowが終了すると、リポジトリのactionsから、workflowの実行結果を確認できます。
今回はこのようになっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806752/a67cf2d1-dec5-4c49-998a-0298a8de0327.png)

jobsの部分が、設定したPost welcome commentになっているのが確認できるかと思います。

最後に、pull requestからmerge、confirmしてブランチをマージしました。

## チュートリアルまとめ
以上で公式のチュートリアルは終了しました。しかしながら、まだ全然わかった気がしません。
なので、次に、公式のサンプルをいろいろとみていこうと思います。

# Actionsのドキュメントを読もうの会

読もうの会です。サンプルコードがいろいろとあるので、興味のあるコードを拾ってみていきます。

ドキュメント

https://docs.github.com/ja/actions/writing-workflows

## ワークフローについて
まずはワークフローそのものについてです。

### ワークフローのトリガー
ひたすらにコードを見ていきます。

https://docs.github.com/ja/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow

```yml:trigger
# 任意ブランチへのプッシュで実行
on: push 

# 任意ブランチへのプッシュorフォークで実行
on: [push, fork] 

# ラベル作成時、mainブランチへのプッシュ、github pages対応ブランチへのプッシュ
on:
  label:
    types:
      - created
  push:
    branches:
      - main
  page_build:

# issueのopenとラベル付けで開始(二つ引っかかると二回実行)
on:
  issues:
    types:
      - opened
      - labeled

#ブランチへのフィルター
on:
  push:
    branches: 
      - main
      - 'releases/**' # releases/とパターン一致するブランチ
      - '!releases/**-alpha' # 逆に、パターン一致するものを無視する。(!での否定)
    branches-ignore: # 直接無視するものを指定もできる
      - 'mona/octocat'
      - 'releases/**-alpha'
    tags: # commit時に指定できるタグに関連する指定
      - v2
      - v1.*
    tags-ignore:
      - v2
      - v1.*
    paths: # パス一致があると実行
      - '**.js'
      - 'sub-project/**'
      - '!sub-project/docs/**'
    paths-ignore: # パス一致があっても無視
      - 'docs/**'

# workflow_runイベント関連
# 以下の者は、指定ブランチで"Build"という名前のワークフローが稼働している場合のみ実行
on:
  workflow_run:
    workflows: ["Build"]
    types: [requested]
    branches:
      - 'releases/**'

# workflow_dispatchイベント
# ワークフローを手動でトリガーできるようにするイベント
# 具体的には、log_level, print:tags, tags, environmentを入力することでワークフローが実行される。
on:
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
        type: choice
        options:
          - info
          - warning
          - debug
      print_tags:
        description: 'True to print to STDOUT'
        required: true
        type: boolean
      tags:
        description: 'Test scenario tags'
        required: true
        type: string
      environment:
        description: 'Environment to run tests against'
        type: environment
        required: true

jobs:
  print-tag:
    runs-on: ubuntu-latest
    if: ${{ inputs.print_tags }} 
    steps:
      - name: Print the input tag to STDOUT
        run: echo  The tags are ${{ inputs.tags }}

# イベント情報の取得
jobs:
  print_context:
    runs-on: ubuntu-latest
    steps:
      - env:
          EVENT_CONTEXT: ${{ toJSON(github.event) }}
        run: |
          echo $EVENT_CONTEXT
          # シンプルにイベントで使用できるプロパティの確認
          
# イベント情報活用の例
# 特定のユーザー以外のpull requestを許さない例
on:
  pull_request:
    types:
      - opened
    paths:
      - '.github/workflows/**'
      - '.github/CODEOWNERS'
      - 'package*.json'

jobs:
  triage:
    if: >- # イベント情報(ユーザーid)による条件一致
      github.event.pull_request.user.login != 'octobot' &&
      github.event.pull_request.user.login != 'dependabot[bot]'
    runs-on: ubuntu-latest
    steps:
      - name: "Comment about changes we can't accept"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR: ${{ github.event.pull_request.html_url }}
        run: |
          gh pr edit $PR --add-label 'invalid'
          gh pr comment $PR --body 'It looks like you edited `package*.json`, `.github/CODEOWNERS`, or `.github/workflows/**`. We do not allow contributions to these files. Please review our [contributing guidelines](https://github.com/octo-org/octo-repo/blob/main/CONTRIBUTING.md) for what contributions are accepted.'

# 条件分岐
on:
  issues:
    types:
      - labeled

jobs:
  run_if_label_matches: 
    if: github.event.label.name == 'bug' # 条件分岐設定
    runs-on: ubuntu-latest
    steps:
      - run: echo 'The label was bug'

# イベントタイプ情報による分岐
on:
  issues:
    types:
      - closed
  pull_request:
    types:
      - closed

jobs:
  state_event_type:
    runs-on: ubuntu-latest
    steps:
    - name: if_issue
      if: github.event.issue # issueの場合のみ実施
      run: |
        echo An issue was closed
    - name: if_pr
      if: github.event.pull_request # pull_requestのみ実施
      run: |
        echo A pull request was closed

# job同士の依存関係の明示
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build
        run: |
          echo 'building'

  publish:
    needs: [build] # build jobが実行されてから実行される
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: publish
        run: |
          echo 'publishing'

```

## ワークフロー構文

ひたすらにコードを見ていきます。

https://docs.github.com/ja/actions/writing-workflows/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows

```yml:syntax
# スケジュールでトリガー
on:
  schedule:
    # * is a special character in YAML so you have to quote this string
    - cron:  '30 5,17 * * *'
    - cron: '30 5 * * 2,4'
    # 分、時間、日、月、曜日で指定
    # 複数トリガーも可能
jobs:
  test_schedule:
    runs-on: ubuntu-latest
    steps:
      - name: Not on Monday or Wednesday
        if: github.event.schedule != '30 5 * * 1,3'
        run: echo "This step will be skipped on Monday and Wednesday"
      - name: Every time
        run: echo "This step will always run"

# 再利用可能な形でのワークフロー定義
# トリガーをworkflow_callにすることで可能になる
# 事前に、必要となる変数を定義しておく必要がある
# 呼び出す際に、それらのパラメーターを渡す必要あり。
# 再利用可能なワークフローの例
name: Reusable workflow example

on:
  workflow_call: # 必要となる変数の定義
    inputs:
      config-path:
        required: true
        type: string
    secrets:
      token:
        required: true

jobs: # 変数とともに呼び出されたときに実施されるワークフロー
  triage:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/labeler@v4
      with:
        repo-token: ${{ secrets.token }}
        configuration-path: ${{ inputs.config-path }}

# 実際に呼び出す場合
jobs:
  call-workflow-passing-data:
    uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
    # ワークフローファイルのパス@ブランチ名で指定
    with: # inputsの入力
      config-path: .github/labeler.yml
    secrets: # secretsの入力
      personal_access_token: ${{ secrets.token }}

# 変数で回して実施
jobs:
  ReuseableMatrixJobForDeployment:
    strategy: # 複数変数渡すときのやつ
      matrix:
        target: [dev, stage, prod]
    uses: octocat/octo-repo/.github/workflows/deployment.yml@main
    with:
      target: ${{ matrix.target }}

# 入れ子関係でのシークレットの受け渡し
# ただし、入れ子関係は4つ連結までが限界
# 今、A→B→Cの関係で読み出している
# A→Bでの呼び出し(シークレット情報はA→Bですべて受け渡す)
jobs:
  workflowA-calls-workflowB:
    uses: octo-org/example-repo/.github/workflows/B.yml@main
    secrets: inherit # pass all secrets
# B→Cでの呼び出し(シークレット情報はB→Cでtokenのみ受け渡す)
jobs:
  workflowB-calls-workflowC:
    uses: different-org/example-repo/.github/workflows/C.yml@main
    secrets:
      repo-token: ${{ secrets.personal_access_token }} # pass just this secret


# ワークフローでの変数を受け渡す
name: Reusable workflow

on:
  workflow_call:
    # Map the workflow outputs to job outputs
    # 事前にoutputを定義
    outputs:
      firstword:
        description: "The first output string"
        value: ${{ jobs.example_job.outputs.output1 }}
      secondword:
        description: "The second output string"
        value: ${{ jobs.example_job.outputs.output2 }}

jobs:
  example_job:
    name: Generate output
    runs-on: ubuntu-latest
    # Map the job outputs to step outputs
    outputs: # 実際にoutputの出力
      output1: ${{ steps.step1.outputs.firstword }}
      output2: ${{ steps.step2.outputs.secondword }}
    steps:
      - id: step1
        run: echo "firstword=hello" >> $GITHUB_OUTPUT
      - id: step2
        run: echo "secondword=world" >> $GITHUB_OUTPUT
    # 変数の受け渡しを行う場合、$GITHUB_OUTPUTに投げるのが公式のやり方なので投げている。

# ワークフローを呼び出し、その結果を受け取るjob
name: Call a reusable workflow and use its outputs

on:
  workflow_dispatch: # 手動実行

jobs:
  job1:
    uses: octo-org/example-repo/.github/workflows/called-workflow.yml@v1

  job2:
    runs-on: ubuntu-latest
    needs: job1
    steps:
      - run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }}
    # $GITHUB_OUTPUTに入れたが、引き出す際は、こんな感じで引き出す。

```

## ワークフローテンプレート

ひたすらにコードを見ていきます。
ただ、サンプルがめちゃ多いので、それぞれ気になるやつだけ見ます。

### ワークフローテンプレート CI編
まずはコンテナから
```yml:docker-image.yml
name: Docker Image CI

on:
  push:
    branches: [ $default-branch ]
  pull_request:
    branches: [ $default-branch ]

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    # 事前作成されたテンプレの使用(use)
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)
    # docker imageのビルド
```

<details><summary>actions/chechout@v4って何？</summary>
公式の出しているワークフローテンプレートの一つになります。
実際に何をしているかについて、gptくんに聞いてみました。

> ✅ actions/checkout@v4 の主な役割
> GitHub Actions のワークフローは、GitHub が提供する仮想マシン（ランナー）上で実行されます。このランナーは、デフォルトでは空の作業ディレクトリから開始されます。actions/checkout@v4 を使用することで、リポジトリのコードをランナーの作業ディレクトリ（$GITHUB_WORKSPACE）にクローンし、後続のステップでコードにアクセスできるようにします。
> これにより、ビルド、テスト、デプロイなどの処理を行うためのコードベースがランナー上に準備されます。 
> 
> 🔍 具体的な動作
> actions/checkout@v4 を使用すると、以下のような操作が行われます：
> - リポジトリのコードをクローンします。
> - デフォルトでは、ワークフローをトリガーしたイベント（例：push や pull_request）に関連する特定のコミットがチェックアウトされます。
> - クローンされたコードは、ランナーの作業ディレクトリ（$GITHUB_WORKSPACE）に配置されます。
> 
> これにより、後続のステップでリポジトリのコードにアクセスし、必要な処理を実行できるようになります。 

とのことです。なので、基本的には空のランナーにコード情報を入れ込むため、と思っておいてよさそうです。(何ならおまじないって感じでＯＫっぽそう)

公式の出しているusageでは、変数の説明もされていたので記載しておきます。

https://github.com/actions/checkout

```yml:actions/chechout_usage.yml
- uses: actions/checkout@v4
  with:
    # Repository name with owner. For example, actions/checkout
    # Default: ${{ github.repository }}
    # 使うrepositoryの名前
    repository: ''

    # The branch, tag or SHA to checkout. When checking out the repository that
    # triggered a workflow, this defaults to the reference or SHA for that event.
    # Otherwise, uses the default branch.
    ref: ''

    # Personal access token (PAT) used to fetch the repository. The PAT is configured
    # with the local git config, which enables your scripts to run authenticated git
    # commands. The post-job step removes the PAT.
    #
    # We recommend using a service account with the least permissions necessary. Also
    # when generating a new PAT, select the least scopes necessary.
    #
    # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
    #
    # Default: ${{ github.token }}
    token: ''

    # SSH key used to fetch the repository. The SSH key is configured with the local
    # git config, which enables your scripts to run authenticated git commands. The
    # post-job step removes the SSH key.
    #
    # We recommend using a service account with the least permissions necessary.
    #
    # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
    ssh-key: ''

    # Known hosts in addition to the user and global host key database. The public SSH
    # keys for a host may be obtained using the utility `ssh-keyscan`. For example,
    # `ssh-keyscan github.com`. The public key for github.com is always implicitly
    # added.
    ssh-known-hosts: ''

    # Whether to perform strict host key checking. When true, adds the options
    # `StrictHostKeyChecking=yes` and `CheckHostIP=no` to the SSH command line. Use
    # the input `ssh-known-hosts` to configure additional hosts.
    # Default: true
    ssh-strict: ''

    # The user to use when connecting to the remote SSH host. By default 'git' is
    # used.
    # Default: git
    ssh-user: ''

    # Whether to configure the token or SSH key with the local git config
    # Default: true
    persist-credentials: ''

    # Relative path under $GITHUB_WORKSPACE to place the repository
    path: ''

    # Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching
    # Default: true
    clean: ''

    # Partially clone against a given filter. Overrides sparse-checkout if set.
    # Default: null
    filter: ''

    # Do a sparse checkout on given patterns. Each pattern should be separated with
    # new lines.
    # Default: null
    sparse-checkout: ''

    # Specifies whether to use cone-mode when doing a sparse checkout.
    # Default: true
    sparse-checkout-cone-mode: ''

    # Number of commits to fetch. 0 indicates all history for all branches and tags.
    # Default: 1
    fetch-depth: ''

    # Whether to fetch tags, even if fetch-depth > 0.
    # Default: false
    fetch-tags: ''

    # Whether to show progress status output when fetching.
    # Default: true
    show-progress: ''

    # Whether to download Git-LFS files
    # Default: false
    lfs: ''

    # Whether to checkout submodules: `true` to checkout submodules or `recursive` to
    # recursively checkout submodules.
    #
    # When the `ssh-key` input is not provided, SSH URLs beginning with
    # `git@github.com:` are converted to HTTPS.
    #
    # Default: false
    submodules: ''

    # Add repository path as safe.directory for Git global config by running `git
    # config --global --add safe.directory <path>`
    # Default: true
    set-safe-directory: ''

    # The base URL for the GitHub instance that you are trying to clone from, will use
    # environment defaults to fetch from the same instance that the workflow is
    # running from unless specified. Example URLs are https://github.com or
    # https://my-ghes-server.example.com
    github-server-url: ''
```
</details>

次に、python関連です。
ここでは、pushされたコードに対して、ランナーにconda環境を整備し、そこでコード解析用のパッケージ(flake8, pytest)を実行しています。

```yml:python-package-conda.yml
name: Python Package using Conda

on: [push]

jobs:
  build-linux:
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 5
      # matrixでとりうる値すべてに対してジョブが生成されるが、同時実行数は5に制限している

    steps:
    - uses: actions/checkout@v4
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: '3.10'
    - name: Add conda to system path
      run: |
        # $CONDA is an environment variable pointing to the root of the miniconda directory
        echo $CONDA/bin >> $GITHUB_PATH
    - name: Install dependencies
      run: |
        conda env update --file environment.yml --name base
    - name: Lint with flake8 # pythonコードの静的解析ツール
      run: |
        conda install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # countでエラーや警告の総数を出力
        # selectで、エラーコードを指定してチェック
        # E9：runtime error
        # F63, F7, F82：不明(ヒットせんかった)
        
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
        # exit-zero：エラーあってもstatus codeを0にする
        # max-complexity：コード複雑さの制限
        # 機械的テストのむずかしさ？らしい
        # max_line_lengthで行あたりの文字数指定
        # statisticsで統計情報の表示
    - name: Test with pytest # pythonのテストフレームワーク
      run: |
        conda install pytest
        pytest
```

complexityの参考資料

https://qiita.com/suzuki_sh/items/824c36b8d53dd2f1efcb

今回はこれしか触れませんが、サンプルコードはマジでたくさんあるので、ぜひ見てみてください。(node.jsとかandroidとか)

https://github.com/actions/starter-workflows/tree/main/ci

### ワークフローテンプレート デプロイ編
次にデプロイ編です。ここではawsとterraformを見ていきます。

```yml:aws.yml
# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the $default-branch branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
#    For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
#    Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
#    Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
#    For example, follow the Getting Started guide on the ECS console:
#      https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
#    Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
#    Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
#    The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
#    Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
#    Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
#    in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
#    See the documentation for each action used below for the recommended IAM policies for this IAM user,
#    and best practices on handling the access key credentials.

name: Deploy to Amazon ECS

on:
  push:
    branches: [ $default-branch ]

env:
  AWS_REGION: MY_AWS_REGION                   # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: MY_ECR_REPOSITORY           # set this to your Amazon ECR repository name
  ECS_SERVICE: MY_ECS_SERVICE                 # set this to your Amazon ECS service name
  ECS_CLUSTER: MY_ECS_CLUSTER                 # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition
                                               # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: MY_CONTAINER_NAME           # set this to the name of the container in the
                                               # containerDefinitions section of your task definition

permissions:
  contents: read

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ env.AWS_REGION }}
    # awsへの接続情報を登録
    # aws-session-tokenもここで登録可能

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    # ECRへのログイン

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} # ログインしたレジストリー名の取得
        IMAGE_TAG: ${{ github.sha }} # 一位性のたんぽ
      run: |
        # Build a docker container and
        # push it to ECR so that it can
        # be deployed to ECS.
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
    # docker imageをビルドしてプッシュし、imageの場所:tagをoutputに流す

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: ${{ env.ECS_TASK_DEFINITION }}
        container-name: ${{ env.CONTAINER_NAME }}
        image: ${{ steps.build-image.outputs.image }}
    # 新しいイメージを使用するように更新

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: ${{ env.ECS_SERVICE }}
        cluster: ${{ env.ECS_CLUSTER }}
        wait-for-service-stability: true
    # ECSサービスのデプロイ

```

次に、terraformです。

```yml:terraform.yml
# This workflow installs the latest version of Terraform CLI and configures the Terraform CLI configuration file
# with an API token for Terraform Cloud (app.terraform.io). On pull request events, this workflow will run
# `terraform init`, `terraform fmt`, and `terraform plan` (speculative plan via Terraform Cloud). On push events
# to the $default-branch branch, `terraform apply` will be executed.
#
# Documentation for `hashicorp/setup-terraform` is located here: https://github.com/hashicorp/setup-terraform
#
# To use this workflow, you will need to complete the following setup steps.
#
# 1. Create a `main.tf` file in the root of this repository with the `remote` backend and one or more resources defined.
#   Example `main.tf`:
#     # The configuration for the `remote` backend.
#     terraform {
#       backend "remote" {
#         # The name of your Terraform Cloud organization.
#         organization = "example-organization"
#
#         # The name of the Terraform Cloud workspace to store Terraform state files in.
#         workspaces {
#           name = "example-workspace"
#         }
#       }
#     }
#
#     # An example resource that does nothing.
#     resource "null_resource" "example" {
#       triggers = {
#         value = "A example resource that does nothing!"
#       }
#     }
#
#
# 2. Generate a Terraform Cloud user API token and store it as a GitHub secret (e.g. TF_API_TOKEN) on this repository.
#   Documentation:
#     - https://www.terraform.io/docs/cloud/users-teams-organizations/api-tokens.html
#     - https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
#
# 3. Reference the GitHub secret in step using the `hashicorp/setup-terraform` GitHub Action.
#   Example:
#     - name: Setup Terraform
#       uses: hashicorp/setup-terraform@v1
#       with:
#         cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

name: 'Terraform'

on:
  push:
    branches: [ $default-branch ]
  pull_request:

permissions:
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash

    steps:
    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v4

    # Install the latest version of Terraform CLI and configure the Terraform CLI configuration file with a Terraform Cloud user API token
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
    # terraformのセットアップをやってくれる事前定義ワークフロー

    # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
    - name: Terraform Init
      run: terraform init

    # Checks that all Terraform configuration files adhere to a canonical format
    - name: Terraform Format
      run: terraform fmt -check

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      run: terraform plan -input=false

      # On push to $default-branch, build or change infrastructure according to Terraform configuration files
      # Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
    - name: Terraform Apply
      if: github.ref == 'refs/heads/$default-branch' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false
```

terraformのinstall、そこそこめんどくさいんですが、それを事前定義してくださっているの、ありがたすぎる。

hashicorp/setup-terraform@v1に感謝

https://github.com/actions/starter-workflows/tree/main/deployments

### ワークフローテンプレート 自動化編

このパートは毛色が少し変わります。

```yml:greetings.yml
name: Greetings

on: [pull_request_target, issues]

jobs:
  greeting:
    runs-on: ubuntu-latest
    permissions: # 書き込み権限
      issues: write
      pull-requests: write
    steps:
    - uses: actions/first-interaction@v1
      with: # オプションで指定することで、ifでeventタイプを見なくても適切に反応させられる。
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        issue-message: "Message that will be displayed on users' first issue"
        pr-message: "Message that will be displayed on users' first pull request"
```

```yml:label.yml
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.

name: Labeler
on: [pull_request_target]

jobs:
  label:

    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
    - uses: actions/labeler@v4
      with:
        repo-token: "${{ secrets.GITHUB_TOKEN }}"
```

次は、手動でのワークフロー実行サンプルです。

```yml:manual.yml
# This is a basic workflow that is manually triggered

name: Manual workflow

# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
  workflow_dispatch: # 手動ワークフローのための定義
    # Inputs the workflow accepts.
    inputs:
      name:
        # Friendly description to be shown in the UI instead of 'name'
        description: 'Person to greet'
        # Default value if no value is explicitly provided
        default: 'World'
        # Input has to be provided for the workflow to run
        required: true
        # The data type of the input
        type: string

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "greet"
  greet:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Runs a single command using the runners shell
    - name: Send greeting
      run: echo "Hello ${{ inputs.name }}"
```

次は、一定期間アクティビティがなかったissueとpull-requestにコメントをつけるワークフローです。

```yml:stale.yml
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests

on:
  schedule:
  - cron: $cron-daily
  # cronでアクティビティのなかった機関の長さを指定できる

jobs:
  stale:

    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write

    steps:
    - uses: actions/stale@v5
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        stale-issue-message: 'Stale issue message'
        stale-pr-message: 'Stale pull request message'
        stale-issue-label: 'no-issue-activity'
        stale-pr-label: 'no-pr-activity'
```

最後に、AIを使ったsummaryの作成です。

```summary.yml
name: Summarize new issues

on:
  issues:
    types: [opened]

jobs:
  summary:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      models: read
      contents: read
      # コンテンツを読み出す権限を与えないとうまくいかないっぽい

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Run AI inference
        id: inference
        uses: actions/ai-inference@v1
        with:
          prompt: |
            Summarize the following GitHub issue in one paragraph:
            Title: ${{ github.event.issue.title }}
            Body: ${{ github.event.issue.body }}

      - name: Comment with AI summary
        run: |
          gh issue comment $ISSUE_NUMBER --body '${{ steps.inference.outputs.response }}'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
          RESPONSE: ${{ steps.inference.outputs.response }}
```

<details><summary>actions/ai-inferenceとは？</summary>

変数については、以下の通りです。

https://github.com/actions/ai-inference/blob/main/action.yml

```yml:action.yml
name: 'AI Inference'
description: Generate an AI response based on a provided prompt
author: 'GitHub'

# Add your action's branding here. This will appear on the GitHub Marketplace.
branding:
  icon: 'message-square'
  color: red

# Define your inputs here.
inputs:
  prompt:
    description: The prompt for the model
    required: false
    default: ''
  prompt-file:
    description: Path to a file containing the prompt
    required: false
    default: ''
  model:
    description: The model to use
    required: false
    default: 'openai/gpt-4o'
  endpoint:
    description: The endpoint to use
    required: false
    default: 'https://models.github.ai/inference'
  system-prompt:
    description: The system prompt for the model
    required: false
    default: 'You are a helpful assistant'
  max-tokens:
    description: The maximum number of tokens to generate
    required: false
    default: '200'
  token:
    description: The token to use
    required: false
    default: ${{ github.token }}

# Define your outputs here.
outputs:
  response:
    description: The response from the model
  response-file:
    description: The file path where the response is saved

runs:
  using: node20
  main: dist/index.js
```

基本的に、なにも指定しなくてもOKらしい。
(アクセストークンすら必須ではないので、本当に恵まれている・・・)

参考

https://note.com/mnuma/n/ne5dbb93a340e

</details>

https://github.com/actions/starter-workflows/tree/main/automation

### 高度なワークフロー機能

ここからは、高度な機能についてのサンプルです。
まずは、トークンなどの直接書きたくないものを乗せる場合のサンプルです。
```yml:secrets.yml
jobs:
  example-job:
    runs-on: ubuntu-latest
    steps:
      - name: Retrieve secret
        env:
          super_secret: ${{ secrets.SUPERSECRET }}
          # secrets.変数名で取り出せる
        run: |
          example-command "$super_secret"
```
次に、ジョブの依存関係の明示についてです。
```yml:depends.yml
jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - run: ./setup_server.sh
  build:
    needs: setup # setupが終わってから実行される
    runs-on: ubuntu-latest
    steps:
      - run: ./build_server.sh
  test:
    needs: build #buildが終わってから実行される
    runs-on: ubuntu-latest
    steps:
      - run: ./test_server.sh
# 依存関係のあるstepから変数を取り出すには、need.step_id.output.変数キーでOK
```
次に、マトリックスについてです。
これを設定することで、変数の組み合わせに基づく複数ジョブ実行が出来ます。
```yml:matrix.yml
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [14, 16] # バージョンを14と16の二パターンで実行する。
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
```
次に、キャッシュの利用についてです。
パッケージインストールなどの、繰り返しやるけど作業時間はかかる、みたいなものをキャッシュしておくことで高速化するためのものです。なので、基本的にはパッケージ依存管理ツールと関係しているみたいです。
基本的には、cacheアクションは、指定した`key`に基づいてキャッシュ復元をするそうです。`key`と完全一致でキャッシュヒット、完全一致がなかったらキャッシュミス扱いとなり、`path`で指定されたファイルのキャッシュを作成するそうです。
サンプルは以下の通り

```yml:cache.yml
jobs:
  example-job:
    steps:
      - name: Cache node modules
        uses: actions/cache@v4
        env:
          cache-name: cache-node-modules
        with:
          path: ~/.npm
          
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
       

name: Caching with npm
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Cache node modules
        id: cache-npm
        uses: actions/cache@v4
        env:
          cache-name: cache-node-modules
        with:
          # npm cache files are stored in `~/.npm` on Linux/macOS
          path: ~/.npm
          # linuxにおけるパッケージ管理アプリのnpmのキャッシュファイルの保存場所を指定
          # keyがヒットしたら、ここに依存関係のキャッシュが復元される
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          # キャッシュファイルを探索するkeyを指定
          # OS依存性、環境依存性を考慮
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-
         # keyにヒットしなかった場合に検索するためのもの

      - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
      # keyでcacheが見つからなかった場合の処理
      # cache-hitで見つかったかのフラグをとれる
        name: List the state of node modules
        continue-on-error: true
        run: npm list

      - name: Install dependencies
        run: npm install

      - name: Build
        run: npm run build

      - name: Test
        run: npm test
```

いまいちわからないので、なじみ深いpython pipでのキャッシュ利用コードをgpt-4oに出してもらいました。

```yml:python-cache.yml
name: Restore pip cache

on:
  push:
    branches: [main]

jobs:
  restore-pip-cache:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
        
      # キャッシュの作成or読み出し
      - name: Restore pip cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-
      # 依存関係に基づいたinstall
      # ここで依存関係の情報が必要なので、cache読み出しは前のステップ
      - name: Install dependencies
        run: pip install -r requirements.txt

```

参考：キャッシュについて
https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows

https://github.com/actions/cache/tree/v4.0.0?tab=readme-ov-file

関連事項として、成果物のダウンロード/アップロードコードを見ていきます。
```yml:artifacts.yml
name: Share data between jobs

on: [push]

jobs:
  job_1:
    name: Add 3 and 7
    runs-on: ubuntu-latest
    steps:
      - shell: bash
        run: |
          expr 3 + 7 > math-homework.txt
      - name: Upload math result for job 1
        uses: actions/upload-artifact@v4
        with:
          name: homework_pre # artifactの名前(なくても可)
          path: math-homework.txt # 実際の保存場所

  job_2:
    name: Multiply by 9
    needs: job_1
    runs-on: windows-latest
    steps:
      - name: Download math result for job 1
        uses: actions/download-artifact@v4
        with:
          name: homework_pre # 名前を指定すればこれで引っ張れる
      - shell: bash
        run: |
          value=`cat math-homework.txt`
          expr $value \* 9 > math-homework.txt
      - name: Upload math result for job 2
        uses: actions/upload-artifact@v4
        with:
          name: homework_final
          path: math-homework.txt
```

最後に、コンテナ化されたサービスを利用するサンプルを見ます。

```yml:container.yml
jobs:
  container-job:
    runs-on: ubuntu-latest
    container: node:20-bookworm-slim # docker hubからのimageを引っ張る
    services:
      postgres: # サービス名
        image: postgres # 使うイメージ名
    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
      - name: Install dependencies
        run: npm ci
      - name: Connect to PostgreSQL
        run: node client.js
        env:
          POSTGRES_HOST: postgres
          POSTGRES_PORT: 5432
```
こんな感じで、`docker-compose.yml`みたいなノリで使えるみたいです。

## ドキュメントを読もうの会まとめ
読みました。かなり自由度が高いようなので、いろいろなことが出来るみたいです。
また、actionsで定義されたものを使いまくるようにすれば、初期コストもかからずでいい感じですね

# おててを使って遊ぶ(はんずおん！)
ここまでで実際にgithub actionsのコードをいろいろとみてきたので、実際に実装してみようと思います。

## どんなコード？
今回は、githubにコードをアップロードし、mainブランチへのプルリクエストを行った段階でterraform plan(terraformで記述したリソースの起動前確認)を行うコードをgpt4.1とともに作りました。
作成するリソースは、VPC、サブネット、s3、ec2です。
コード自体はgithubに上げました。(が、エラーが出てまだ治っていないです。)

https://github.com/akimi2259/test_actions_with_terraform

```yml:github_actions.yml
name: 'Terraform'

on:
  pull_request:
    branches:
      - main

permissions:
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash

    steps:
    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v4

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
        aws-region: ap-northeast-1
    # awsへの接続情報を登録
    # aws-session-tokenもここで登録可能

    # Terraformのセットアップ（共通処理っぽい）
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: 1.5.7  # ここは必要なバージョンに合わせて！

    # Terraform init
    - name: Terraform Init
      run: terraform init
      working-directory: ./environment/develop/

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      run: terraform plan -input=false
      working-directory: ./environment/develop/
```

これらのコードによって、githubactionsを用いて、pull requestをトリガーとして`terraform plan`を実行させることに成功しました。
やったね！！！


# まとめ
今回は、github actionsのコードをひたすら読んで、terraformのコードを実行させるということをしてみました。
構文的な面で慣れないというより、エラーが起きるまでの時間が長いことの方が個人的にはストレスでした。
なので、次からはもう少しエラーが起きないことを事前にチェックする部分に力を入れたいです。

(あとシンプルに、terraformでアプライできる状況になっていないので、それを直したい。)