Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
self-hosted-runner:
# Labels of self-hosted runner in array of strings.
labels: []

# Configuration variables in array of strings defined in your repository or
# organization. `null` means disabling configuration variables check.
# Empty array means no configuration variable is allowed.
config-variables: null

# Configuration for file paths. The keys are glob patterns to match to file
# paths relative to the repository root. The values are the configurations for
# the file paths. Note that the path separator is always '/'.
# The following configurations are available.
#
# "ignore" is an array of regular expression patterns. Matched error messages
# are ignored. This is similar to the "-ignore" command line option.
paths:
.github/workflows/**/*.yml:
ignore:
- 'shellcheck reported issue in this script: SC[0-9]+:(info|style)'
4 changes: 4 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@
description: 'リリース済み'

# ── Size ──────────────────────────────────────────
- name: 'size/XS'
color: 'c2e0c6'
description: 'PR サイズ Extra Small'

- name: 'size/S'
color: '2cbe4e'
description: 'PR サイズ Small'
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ on:
- '**.mjs'
- '**.cjs'
- '**.json'
- '**.yml'
- '**.yaml'
- '**.sh'
- '**.bats'
- '.github/workflows/**'
- '.github/actionlint.yaml'
- 'package.json'
- 'package-lock.json'
- '.eslintrc.*'
Expand All @@ -25,9 +28,12 @@ on:
- '**.mjs'
- '**.cjs'
- '**.json'
- '**.yml'
- '**.yaml'
- '**.sh'
- '**.bats'
- '.github/workflows/**'
- '.github/actionlint.yaml'
- 'package.json'
- 'package-lock.json'
- '.eslintrc.*'
Expand Down Expand Up @@ -75,6 +81,7 @@ jobs:
- 'test/integration/**'
workflows:
- '.github/workflows/**'
- '.github/actionlint.yaml'
dependencies:
- 'package.json'
- 'package-lock.json'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/manual-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
echo "Version to release: $(cat $GITHUB_OUTPUT | grep version)"

- name: Fix workspace permissions
run: sudo chown -R $(id -u):$(id -g) "$GITHUB_WORKSPACE"
run: sudo chown -R "$(id -u):$(id -g)" "$GITHUB_WORKSPACE"

- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4

Expand Down
31 changes: 27 additions & 4 deletions .github/workflows/quality-gate-fallback.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
# Quality Gate Fallback
#
# CI ワークフローが paths フィルタでスキップされた場合に
# Required Status Check "Quality Gate" を Pass で報告する。
# CI が実行された場合は ci.yml 側の Quality Gate が優先される。
# Required Status Check "Quality Gate" を必ず報告するための fallback ワークフロー。
#
# 必要な背景:
# - setup-team-protection.sh はブランチ保護に "Quality Gate" を必須チェックとして登録する
# - ci.yml の Quality Gate ジョブが paths フィルタでスキップされた場合や、
# ci.yml 自体が存在しない / 走らない場合、PR は永遠に
# "Expected — Waiting for status to be reported" のまま blocked になる
# - この fallback は ci.yml の実行可否をチェックし、走っていなければ Pass を emit する
#
# 動作:
# - ci.yml が走っている / 成功している場合 → 何もしない (本体の Quality Gate が優先)
# - ci.yml が走っていない場合 → Pass で Quality Gate を emit
#
# 使い方:
# .github/workflows/quality-gate-fallback.yml にコピーして配置
# setup-team-protection.sh で "Quality Gate" を必須チェックに設定する場合は必須
#
# 既知の制限:
# - GITHUB_TOKEN で push されたコミット (claude[bot] / dependabot[bot] が
# GITHUB_TOKEN で押した場合など) では `pull_request` イベントが発火しないため、
# この fallback も実行されない。その場合は別途 PAT 経由の push か
# workflow_dispatch / repository_dispatch での再トリガーが必要。
#
name: CI Fallback

Expand Down Expand Up @@ -52,10 +71,14 @@ jobs:
core.setOutput('ci_running', 'false');
}
} catch (err) {
// 権限不足 (actions: read 未付与) や一時的な API 障害でクエリ失敗時は
// 安全側に倒す。"CI 実行中扱い" にして fallback の Pass emit をスキップし、
// 本体の Quality Gate (ci.yml) の結果を待つ。Required check が
// 未報告のままになる可能性はあるが、誤った Pass / Fail よりは安全。
core.warning(`Could not query workflow runs (${err.status || err.message}). Skipping fallback.`);
core.setOutput('ci_running', 'true');
}

- name: Pass (CI skipped)
if: steps.check.outputs.ci_running == 'false'
run: echo "Quality Gate passed (CI skipped — no code changes detected)."
run: echo "Quality Gate passed (CI skipped — no code changes detected or workflow disabled)."
2 changes: 1 addition & 1 deletion .github/workflows/templates/monorepo-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
id: changes
run: |
# Get the previous commit
if [ $(git rev-list --count HEAD) -eq 1 ]; then
if [ "$(git rev-list --count HEAD)" -eq 1 ]; then
# First commit, consider all packages changed
echo "package1_changed=true" >> $GITHUB_OUTPUT
echo "package2_changed=true" >> $GITHUB_OUTPUT
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/update-dev-tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ jobs:
- name: Compare versions
id: compare
run: |
UPDATES=""
HAS_UPDATES="false"

compare_version() {
Expand Down
34 changes: 34 additions & 0 deletions templates/github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@
color: 'bfdadc'
description: '自動検出された機能・改善'

- name: 'npm'
color: 'cb3837'
description: 'npm/yarn 依存関係の更新'

- name: 'docker'
color: '0db7ed'
description: 'Docker 依存関係の更新'

# ── CI/CD ─────────────────────────────────────────
- name: 'ci'
color: '006b75'
Expand All @@ -102,3 +110,29 @@
- name: 'infrastructure'
color: '5319e7'
description: 'インフラ関連'

# ── Release ───────────────────────────────────────
- name: 'released'
color: 'ededed'
description: 'リリース済み'

# ── Size ──────────────────────────────────────────
- name: 'size/XS'
color: 'c2e0c6'
description: 'PR サイズ Extra Small'

- name: 'size/S'
color: '2cbe4e'
description: 'PR サイズ Small'

- name: 'size/M'
color: 'fbca04'
description: 'PR サイズ Medium'

- name: 'size/L'
color: 'e99695'
description: 'PR サイズ Large'

- name: 'size/XL'
color: 'd93f0b'
description: 'PR サイズ XLarge'
16 changes: 9 additions & 7 deletions templates/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#
# Issue や PR で @claude をメンションすると Claude Code が応答する。
# Bot からのイベントは自動除外。
# PR イベント(pull_request_review_comment / pull_request_review)では Draft PR も除外。
# issue_comment イベントでは Draft 判定不可のため除外なし。
# PR イベントと PR 上の issue_comment では Draft PR も除外。
#
# 使い方:
# .github/workflows/claude.yml にコピーして配置
Expand Down Expand Up @@ -55,7 +54,8 @@ jobs:
github.event.issue.author_association
) &&
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body || '', '@claude')) ||
(github.event_name == 'issue_comment' && contains(github.event.comment.body || '', '@claude') &&
!(github.event.issue.pull_request && github.event.issue.pull_request.url && github.event.issue.draft == true)) ||
Comment on lines +57 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude draft PR issue comments correctly

For issue_comment events on PR conversation comments, github.event.issue is the issue-shaped payload and does not include the PR's draft field, so github.event.issue.draft == true never becomes true. In repositories using this template, commenting @claude on a draft PR still starts the Claude job even though the new header says PR issue comments are excluded for drafts; the workflow needs to fetch the PR or otherwise use a payload that actually contains pull_request.draft before making this decision.

Useful? React with 👍 / 👎.

(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body || '', '@claude') &&
github.event.pull_request.draft == false) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body || '', '@claude') &&
Expand All @@ -69,26 +69,28 @@ jobs:
pull-requests: write
issues: write
id-token: write
actions: read
actions: read # Required for Claude to read CI results on PRs
checks: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89
uses: anthropics/claude-code-action@d5726de019ec4498aa667642bc3a80fca83aa102 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
checks: read

# System prompt to automatically create PRs when working on Issues
claude_args: |
--allowedTools "Bash(git:*),Bash(npm:*),Bash(pnpm:*),Bash(npx:*)"
--allowedTools "Bash(git:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh pr status:*),Bash(npm:*),Bash(pnpm:*),Bash(npx:*)"
--system-prompt "When working on GitHub Issues (not PRs), after completing all code changes, commit and push the changes to the Claude branch. Do not run 'gh pr create'; this workflow creates the pull request from the branch after Claude finishes."

- name: Create pull request from Claude branch
Expand Down
10 changes: 5 additions & 5 deletions templates/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
# 更新種別を取得(patch / minor / major)
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -44,7 +44,7 @@ jobs:
# gh pr merge --auto が enablePullRequestAutoMerge エラーで落ちるため、
# 既知エラーだけ自動承認へフォールバックしてワークフロー自体は green を維持する。
- name: Auto-merge patch updates
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
if: github.actor == 'dependabot[bot]' && steps.metadata.outputs.update-type == 'version-update:semver-patch'
run: |
set +e
MERGE_OUTPUT=$(gh pr merge "$PR_URL" --auto --merge 2>&1)
Expand All @@ -69,7 +69,7 @@ jobs:
# GITHUB_TOKEN は既定で PR 承認不可のため continue-on-error: true で失敗しても CI をブロックしない
# リポジトリ設定「Allow GitHub Actions to create and approve pull requests」を有効にすると承認も動作する
- name: Label minor updates
if: steps.metadata.outputs.update-type == 'version-update:semver-minor'
if: github.actor == 'dependabot[bot]' && steps.metadata.outputs.update-type == 'version-update:semver-minor'
run: |
gh label create "dependabot-minor" --color "0366d6" --description "Minor dependency update" --force
gh pr edit "$PR_URL" --add-label "dependabot-minor"
Expand All @@ -78,7 +78,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Auto-approve minor updates
if: steps.metadata.outputs.update-type == 'version-update:semver-minor'
if: github.actor == 'dependabot[bot]' && steps.metadata.outputs.update-type == 'version-update:semver-minor'
continue-on-error: true
run: |
gh pr review "$PR_URL" --approve --body "Auto-approved: minor version update"
Expand All @@ -88,7 +88,7 @@ jobs:

# major: ラベル付与してレビュー必須
- name: Label major updates for review
if: steps.metadata.outputs.update-type == 'version-update:semver-major'
if: github.actor == 'dependabot[bot]' && steps.metadata.outputs.update-type == 'version-update:semver-major'
run: |
gh label create "needs-review" --color "fbca04" --description "レビュー待ち" --force
gh label create "breaking-change" --color "b60205" --description "破壊的変更あり" --force
Expand Down
2 changes: 1 addition & 1 deletion templates/workflows/label-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Sync labels
uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2
Expand Down
4 changes: 2 additions & 2 deletions templates/workflows/scheduled-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
fi

- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
Expand All @@ -54,7 +54,7 @@ jobs:

- name: Run scheduled maintenance
id: maintenance
uses: anthropics/claude-code-action@b47fd721da662d48c5680e154ad16a73ed74d2e0 # v1
uses: anthropics/claude-code-action@d5726de019ec4498aa667642bc3a80fca83aa102 # v1
env:
CLAUDE_BRANCH: ${{ env.CLAUDE_BRANCH }}
with:
Expand Down
10 changes: 10 additions & 0 deletions test/integration/workflows.bats
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ load ../test_helper/test_helper
grep -q "npm run test:coverage" "$workflow"
}

@test "CI workflow runs workflow lint when actionlint config changes" {
local workflow="${REPO_ROOT}/.github/workflows/ci.yml"
assert_file_exists "$workflow"

grep -Fq "'**.yml'" "$workflow"
grep -Fq "'**.yaml'" "$workflow"
grep -Fq "'.github/actionlint.yaml'" "$workflow"
grep -Fq "workflows:" "$workflow"
}

@test "CI workflow uses secure practices" {
local workflow="${REPO_ROOT}/.github/workflows/ci.yml"

Expand Down
10 changes: 10 additions & 0 deletions test/template-workflows.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,5 +317,15 @@ describe('Template workflow contracts', () => {
expect(labels).toContain("name: 'breaking-change'");
},
);

test.each(['templates/github/labels.yml', '.github/labels.yml'])(
'%s: should define all labels used by PR size check',
(labelsPath) => {
const labels = readWorkflow(labelsPath);
for (const size of ['XS', 'S', 'M', 'L', 'XL']) {
expect(labels).toContain(`name: 'size/${size}'`);
}
},
);
});
});
Loading