0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions の基本を「actionlint → push → gh run watch」のサイクルで動かして学び直す

0
Posted at

はじめに

GitHub Actions をちゃんと学び直したくなったので、学習用リポジトリを 1 つ作り、同じサイクルを何周も回しながら基本を整理してみました。

ブラウザで Actions タブを開いて結果を待つと手が止まりがちなので、書く → ローカルで lint → push → ターミナルで見守る → 失敗したらログを読むまでをすべてターミナルで完結させています。

この記事で学べることは次の 4 つです。

手順 テーマ 使うもの
1 ワークフローの最小構成とトリガー push / workflow_dispatch、コンテキスト
2 push 前に間違いを見つける actionlint
3 複数の Node.js バージョンで動かして、落ちたらログを読む matrix / --log-failed
4 アクセスキーなしで AWS に入る OIDC、IAM ロール

完成版は次のリポジトリに置いています。

やってみた

手順 0:準備

GitHub CLI(gh)がログイン済みか確認し(未ログインなら gh auth login)、ワークフローの静的チェックツール actionlint をインストールします。
そのうえで、学習用の public リポジトリを作って clone し、ブランチ名を main にそろえます。

brew install actionlint
gh auth status
gh repo create gha-basics-demo --public --clone
cd gha-basics-demo
git switch -c main

手順 1:最初のワークフロー(hello.yml)

.github/workflows/ に YAML を置くだけで、それがワークフローになります。

中身は「いつ動くか(on)」と「何をするか(jobs)」の 2 つです。

ジョブは 1 台の仮想マシン上で動き、その中のステップが上から順に実行されます。

push に加えて workflow_dispatch(手動実行)も付けておくと、後から gh workflow run で何度でも動かせて便利です(ワークフロー構文)。

mkdir -p .github/workflows

cat > .github/workflows/hello.yml <<'EOF'
name: hello

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Say hello
        run: |
          echo "Hello from ${{ github.repository }}"
          echo "event=${{ github.event_name }} os=${{ runner.os }}"
          ls -la
EOF

actionlint

git add . && git commit -m "Add hello workflow" && git push -u origin main

${{ }} は式で、github.event_name のようなコンテキスト(実行時の情報が入った変数)を参照でき、シェルに渡る前に値へ置き換わります(コンテキスト)。

push したら、最新の実行が終わるまでターミナルで見守ります(gh run watch)。

sleep 10

gh run watch $(gh run list --workflow hello.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status

gh run view $(gh run list --workflow hello.yml --limit 1 --json databaseId --jq '.[0].databaseId') --log | grep "event="

スクリーンショット 2026-10-05 0.18.45.png

ログに event=push os=Linux と出ていれば成功です。

手順 2:actionlint で push 前に間違いを見つける

ワークフローの間違いは、push してみるまで気づけないことが多いです。 そこで、わざと 2 か所を間違えて actionlint にかけてみます。

sed -i '' -e 's/github.event_name/github.evnt_name/' -e 's/runs-on: ubuntu-latest/runs_on: ubuntu-latest/' .github/workflows/hello.yml

actionlint

スクリーンショット 2026-10-05 0.21.47.png

runs_on のキー名ミス(あわせて「runs-on が無い」という指摘も出ます)と、存在しないプロパティ evnt_name の計 3 件が、行番号付きで指摘されます。

では、lint しないで push したらどうなるでしょうか。evnt_name のタイポだけを残して試してみます。

git checkout .github/workflows/hello.yml

sed -i '' 's/github.event_name/github.evnt_name/' .github/workflows/hello.yml

git commit -am "typo test" && git push origin main

sleep 10

gh run watch $(gh run list --workflow hello.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status

gh run view $(gh run list --workflow hello.yml --limit 1 --json databaseId --jq '.[0].databaseId') --log | grep "event="

スクリーンショット 2026-10-05 0.26.52.png

実行は成功扱いになり、ログには event= os=Linux と空の値が出るだけでした。 存在しないプロパティは空文字として評価される仕様で、エラーで止まらないので、push 前の actionlint が効いてくるのはこういうミスです。

確認できたら元に戻します。

git revert --no-edit HEAD && git push origin main

VS Code なら公式の GitHub Actions 拡張機能でも同じミスが編集中に表示されます。actionlint は終了コードで合否が分かるので、CI や AI エージェントに確認させる用途にも向いています。

手順 3:matrix で複数の Node.js バージョンに流し、落ちたログを読む(ci.yml)

matrix を使うと、同じジョブを Node.js のバージョンごとに並べて実行できます(ジョブのバリエーション)。
ライブラリやアプリが「どのバージョンで動くか」を確かめる、matrix の定番の使い方です。

ここでは、配列をグループ分けする Object.groupBy を Node.js 20 / 22 / 24 で動かしてみます。

cat > .github/workflows/ci.yml <<'EOF'
name: ci

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        node-version: [20, 22, 24]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node-version }}
      - name: Group numbers
        run: |
          node -e 'console.log(JSON.stringify(Object.groupBy([1, 2, 3, 4], (n) => (n % 2 ? "odd" : "even"))))'
EOF

actionlint

git add . && git commit -m "Add matrix CI" && git push origin main

sleep 10

gh run watch $(gh run list --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status

${{ matrix.node-version }} に 20 / 22 / 24 が順に入り、actions/setup-node がそのバージョンの Node.js を用意します。
fail-fast: false は「1 つ落ちても残りを止めない」設定です。これがないと、1 つが落ちた時点で他がキャンセルされることがあります。

22 と 24 は {"odd":[1,3],"even":[2,4]} と表示されて成功し、20 だけが失敗します。 失敗したステップのログだけを見るには --log-failed を使います(gh run view)。

gh run view $(gh run list --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') --log-failed

スクリーンショット 2026-10-05 0.56.14.png

TypeError: Object.groupBy is not a function が原因です。 Object.groupBy は Node.js 21 で追加された機能なので、20 には存在しません。

対応は「コードを 20 でも動く書き方に変える」か「20 をサポート対象から外す」かのどちらかです。Node.js 20 は 2026-04-30 にサポートが終了しているので、matrix から外すことが対応として妥当です。

手順 4:アクセスキーなしで AWS に入る(aws.yml)

GitHub Actions から AWS を操作するには、AWS のアクセスキーを GitHub に保存する方法もあります。
ただ、OIDC を使えば、GitHub が実行のたびに発行する「短命の身分証」を AWS に見せるだけで入れます(AWS での OIDC の構成)。

AWS 側には「このリポジトリの main ブランチから来た身分証だけを受け入れる」ロールを作ります。 身分証の中でリポジトリを表す欄を sub と呼び、その値をロールの条件に書きます。

aws iam list-open-id-connect-providers --output text | grep token.actions.githubusercontent.com \
  || aws iam create-open-id-connect-provider --url https://token.actions.githubusercontent.com --client-id-list sts.amazonaws.com

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

SUB=$(gh api repos/{owner}/{repo} --jq '"repo:\(.owner.login)@\(.owner.id)/\(.name)@\(.id):ref:refs/heads/main"')

echo "$SUB"

cat > trust.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"},
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {"StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "${SUB}"
    }}
  }]
}
EOF

ROLE_ARN=$(aws iam create-role --role-name gha-basics-demo-role \
  --assume-role-policy-document file://trust.json --query Role.Arn --output text)

gh variable set AWS_ROLE_ARN --body "$ROLE_ARN"

rm trust.json

最初の 2 行は、GitHub を AWS に登録する作業です。

作ったロールの ARN はリポジトリ変数に入れて、ワークフローから vars.AWS_ROLE_ARN で読めるようにしています。

ワークフロー側は、id-token: write(身分証をもらう許可)を付けて、公式の Action にロールを渡すだけです。

cat > .github/workflows/aws.yml <<'EOF'
name: aws

on:
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

jobs:
  whoami:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
          mask-aws-account-id: true
      - run: aws sts get-caller-identity --query Arn --output text
EOF

actionlint

git add . && git commit -m "Add AWS OIDC workflow" && git push origin main

gh workflow run aws.yml

sleep 10

gh run watch $(gh run list --workflow aws.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status

スクリーンショット 2026-10-05 1.17.23.png

最後のステップで assumed-role/gha-basics-demo-role/GitHubActions と出れば、アクセスキーなしで AWS に入れています。

ハマりどころ:新しいリポジトリは sub に ID が付く

ネット上でよく見る書き方では、今回は失敗しました。2026-07-15 より後に作ったリポジトリは、sub に名前だけでなく ID(@数字)も入るためです。

ロールに書いた sub 結果
repo:leomaro7/gha-basics-demo:ref:refs/heads/main 失敗
repo:leomaro7@50868643/gha-basics-demo@1403381713:ref:refs/heads/main 成功

リポジトリ名は、手放すと他人が同じ名前で作り直せます。名前だけで信頼すると、別人のリポジトリを受け入れてしまうおそれがあるため、ID が加わりました。上の手順で gh api から ID を取っているのはこのためです。

手順 5:後片付け

IAM ロールとリポジトリ変数を削除します。ID プロバイダーは、手順 4 で新しく作った場合だけ削除してください(他のワークフローが使っている可能性があるため)。

aws iam delete-role --role-name gha-basics-demo-role
gh variable delete AWS_ROLE_ARN
# 手順 4 で ID プロバイダーを新規作成した場合のみ
# aws iam delete-open-id-connect-provider --open-id-connect-provider-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):oidc-provider/token.actions.githubusercontent.com"

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?