Skip to content
Open
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
65 changes: 65 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# CI/CD と開発者向けドキュメント

この `.github/` ディレクトリには、チーム情報とプロフィール表示を自動で同期するためのワークフローが置かれています。リポジトリ全体の概要は [リポジトリ root の README](../README.md) を参照してください。

## Workflow 一覧

| Workflow | Trigger | 役割 |
| --- | --- | --- |
| `.github/workflows/tf-plan.yaml` | `terraform/**` を含む PR | `terraform plan` を実行し差分を PR コメントに投稿 |
| `.github/workflows/tf-apply.yaml` | main への push(`terraform/**`) / 手動 | `terraform apply` → 続けて `sync-core-members` を起動 |
| `.github/workflows/sync-core-members.yml` | `tf-apply` からの起動 / 6時間ごとの cron / 手動 / `repository_dispatch` | 最新年度の `core-members-YYYY` team を取得し `data/core_members.yaml` と `profile/README.md` を再生成する PR を自動作成 |
| `.github/workflows/validate-profile.yml` | `data/core_members.yaml` や `profile/README.md` を含む PR | `render_members.py --check` で README の再生成漏れを検出 |

## 認証

- **GitHub API (team / membership の書込)**: GitHub App (`Organization → Members: Read & Write`) の installation token を `actions/create-github-app-token@v2` で発行し Terraform provider に渡します。tfcmt の PR コメント投稿や `sync-core-members` の起動 (`gh workflow run`) には App token ではなく Actions 標準の `GITHUB_TOKEN` を使います(App には repo 権限がないため)。
- **自動生成 PR (`sync-core-members`)**: 別の GitHub App から発行した token で署名付き commit を作成し PR を出します。`peter-evans/create-pull-request` の `sign-commits: true` を利用するため、App installation token 経由の GraphQL 署名になり、GitHub 上で "Verified" バッジが付きます。App 側の必要権限:
- Organization → Members: Read
- Repository → Contents: Read & Write
- Repository → Pull requests: Read & Write
- **Terraform state**: Google Cloud Storage bucket `ynufes-tech-gh-tf-state` に保存します。GitHub Actions から Workload Identity Federation で SA `gh-actions@ynufes-tech-gh.iam.gserviceaccount.com` に impersonate してアクセスします。

### 必要なリポジトリ secrets

| 名前 | 用途 |
| --- | --- |
| `TF_GH_APP_ID`, `TF_GH_APP_PRIVATE_KEY` | `tf-plan` / `tf-apply` |
| `SYNC_GH_APP_ID`, `SYNC_GH_APP_PRIVATE_KEY` | `sync-core-members`(署名付き PR 作成) |

## ローカル開発

ツールは [aqua](https://aquaproj.github.io/) で管理しています。

```bash
aqua i -l
```

Python の依存は [uv](https://docs.astral.sh/uv/) で管理しています。

```bash
uv sync
```

### プロフィール README の再生成チェック

```bash
uv run scripts/render_members.py --check
```

### 現在の Core Members を取得して YAML を更新 (要 `gh` ログイン)

```bash
uv run scripts/fetch_core_members.py --org ynufes-tech --out data/core_members.yaml
uv run scripts/render_members.py
```

### Terraform

```bash
cd terraform
terraform init
terraform plan
```

state backend は GCS です。ローカルから叩く場合は `gcloud auth application-default login` の後、SA への impersonate を許可するか、同 SA の鍵で認証してください。
112 changes: 112 additions & 0 deletions .github/workflows/sync-core-members.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Sync Core Members

on:
schedule:
- cron: "17 */6 * * *"
workflow_dispatch:
repository_dispatch:
types:
- core-members-changed

permissions:
contents: write
pull-requests: write

concurrency:
group: sync-core-members
cancel-in-progress: false

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate GitHub App token
id: app_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.SYNC_GH_APP_ID }}
private-key: ${{ secrets.SYNC_GH_APP_PRIVATE_KEY }}
owner: ynufes-tech

- name: Setup uv
uses: astral-sh/setup-uv@v5

- name: Snapshot previous members
run: cp data/core_members.yaml /tmp/core_members_prev.yaml

- name: Fetch current Core Members
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
run: |
uv run scripts/fetch_core_members.py \
--org ynufes-tech \
--out data/core_members.yaml

- name: Render profile README
run: uv run scripts/render_members.py

- name: Build PR body
id: diff
run: |
uv run python - <<'PY' > /tmp/pr_body.md
import pathlib
import yaml

prev = yaml.safe_load(pathlib.Path("/tmp/core_members_prev.yaml").read_text()) or {}
curr = yaml.safe_load(pathlib.Path("data/core_members.yaml").read_text()) or {}

def names(section):
return set(section.get("members", []) or [])

def diff_section(label, prev_section, curr_section):
prev_names = names(prev_section or {})
curr_names = names(curr_section or {})
added = sorted(curr_names - prev_names, key=str.lower)
removed = sorted(prev_names - curr_names, key=str.lower)
slug = (curr_section or {}).get("team_slug", "-")
out = [
f"### {label} (team: `{slug}`)",
f"- 現在のメンバー数: {len(curr_names)}",
]
if added:
out += ["", "**追加**", *[f"- @{n}" for n in added]]
if removed:
out += ["", "**削除**", *[f"- @{n}" for n in removed]]
if not added and not removed:
out += ["", "差分なし"]
return out

lines = ["## Core Members / Members 同期", ""]
lines += diff_section(
"Core Members", prev.get("core_members"), curr.get("core_members")
)
lines += ["", ""]
lines += diff_section(
"Members", prev.get("all_members"), curr.get("all_members")
)
lines += [
"",
"このPRは GitHub Actions が自動生成しています。",
"メンバー構成そのものの編集は `terraform/team_memberships.tf` で行ってください。",
]
print("\n".join(lines))
PY

- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.app_token.outputs.token }}
branch: automation/core-members-sync
commit-message: "chore: sync Core Members membership"
title: "chore: Core Members のメンバー一覧を同期"
body-path: /tmp/pr_body.md
delete-branch: true
sign-commits: true
committer: "${{ steps.app_token.outputs.app-slug }}[bot] <${{ steps.app_token.outputs.app-slug }}[bot]@users.noreply.github.com>"
author: "${{ steps.app_token.outputs.app-slug }}[bot] <${{ steps.app_token.outputs.app-slug }}[bot]@users.noreply.github.com>"
labels: |
members
automation
67 changes: 67 additions & 0 deletions .github/workflows/tf-apply.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Terraform Apply

on:
push:
branches:
- main
paths:
- terraform/**
- .github/workflows/tf-apply.yaml
workflow_dispatch:

permissions:
id-token: write
contents: read
actions: write
pull-requests: write

concurrency:
group: terraform-apply
cancel-in-progress: false

jobs:
apply:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate GitHub App token (org admin)
id: app_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.TF_GH_APP_ID }}
private-key: ${{ secrets.TF_GH_APP_PRIVATE_KEY }}
owner: ynufes-tech

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/516174501149/locations/global/workloadIdentityPools/default/providers/github
service_account: gh-actions@ynufes-tech-gh.iam.gserviceaccount.com

- name: Setup aqua
uses: aquaproj/aqua-installer@v3.1.0
with:
aqua_version: v2.36.1

- name: Terraform init
run: terraform init -input=false

- name: Terraform apply (tfcmt)
env:
TF_VAR_github_token: ${{ steps.app_token.outputs.token }}
# tfcmt のコメント投稿は repo 権限を持つ Actions 標準トークンで行う
# (App token は org Members 権限のみで PR コメントを投稿できない)
GITHUB_TOKEN: ${{ github.token }}
run: tfcmt apply -- terraform apply -input=false -auto-approve

- name: Trigger profile sync
env:
# workflow_dispatch は GITHUB_TOKEN 起動でも新しい run が作られる
# (App token には Actions: write がないため使えない)
GH_TOKEN: ${{ github.token }}
run: gh workflow run sync-core-members.yml -R ${{ github.repository }}
58 changes: 58 additions & 0 deletions .github/workflows/tf-plan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Terraform Plan

on:
pull_request:
paths:
- terraform/**
- .github/workflows/tf-plan.yaml

permissions:
id-token: write
contents: read
pull-requests: write

jobs:
plan:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate GitHub App token (org admin)
id: app_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.TF_GH_APP_ID }}
private-key: ${{ secrets.TF_GH_APP_PRIVATE_KEY }}
owner: ynufes-tech

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/516174501149/locations/global/workloadIdentityPools/default/providers/github
service_account: gh-actions@ynufes-tech-gh.iam.gserviceaccount.com

- name: Setup aqua
uses: aquaproj/aqua-installer@v3.1.0
with:
aqua_version: v2.36.1

- name: Terraform fmt
run: terraform fmt -check -recursive

- name: Terraform init
run: terraform init -input=false

- name: Terraform validate
run: terraform validate -no-color

- name: Terraform plan (tfcmt)
env:
TF_VAR_github_token: ${{ steps.app_token.outputs.token }}
# tfcmt の PR コメント投稿は repo 権限を持つ Actions 標準トークンで行う
# (App token は org Members 権限のみで PR コメントを投稿できない)
GITHUB_TOKEN: ${{ github.token }}
run: tfcmt plan -patch -- terraform plan -input=false -no-color -out=tfplan
23 changes: 23 additions & 0 deletions .github/workflows/validate-profile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Validate Profile Render

on:
pull_request:
paths:
- data/core_members.yaml
- scripts/render_members.py
- profile/README.md
- .github/workflows/validate-profile.yml
workflow_dispatch:

jobs:
validate-render:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup uv
uses: astral-sh/setup-uv@v5

- name: Verify README is rendered
run: uv run scripts/render_members.py --check
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.venv/
__pycache__/
*.pyc

# Terraform
terraform/.terraform/
terraform/tfplan
terraform/*.tfplan
terraform/*.tfstate
terraform/*.tfstate.*
2 changes: 2 additions & 0 deletions .tfcmt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
ci: github-actions
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ynufes-tech/.github

`ynufes-tech` Org のチームの管理と、プロフィールページを管理するレポジトリです。

- `profile/README.md`: Organization プロフィールとして表示される README
- `terraform/`: Organization のチームを Terraform で管理
- `scripts/` + `.github/workflows/`: team のメンバー情報を取得し、プロフィールのメンバー一覧を自動更新するスクリプト

## 全体の流れ

メンバーは Terraform コード(`terraform/team_memberships.tf`)で管理しています。メンバー情報を更新するにはこのファイルを編集しPRを作成してください。
Mergeされるとチーム情報とプロフィール表示の両方が自動で更新されます。

```mermaid
flowchart TB
subgraph Phase1["1. GitHub チーム情報の更新"]
direction TB
PR1["① Terraform コードを編集<br/>(team / メンバーの追加・削除)"]
Review1["② Review & Merge"]
Apply["③ GitHub Actions: Terraform apply<br/>GitHub team / メンバーシップを更新"]
Teams[("GitHub Organization の<br/>team / 所属")]
PR1 --> Review1 ==> Apply --> Teams
end

subgraph Phase2["2. Org README の更新"]
direction TB
Sync["④ GitHub Actions: team メンバーを取得して<br/>Org README を再生成してPRを作成"]
Review2["⑤ 自動生成された<br/>Org README 更新PRをMerge"]
OrgReadme[("Org README メンバー一覧")]
Sync ==> Review2 --> OrgReadme
end

Teams --> Sync
```

## チームについて

| team | 用途 |
| --- | --- |
| `core-members-YYYY` | 年度ごとの現役メンバー。Organization README のメンバー一覧表示は最新年度が対象 |
| `all-members` | 技術部局に所属したことのあるメンバー |

チーム情報の変更は必ず `terraform/team_memberships.tf` を編集する PR で行ってください。

## CI/CD とローカル開発

ワークフローの詳細・必要な secrets・ローカル開発手順は [.github/README.md](.github/README.md) を参照してください。
7 changes: 7 additions & 0 deletions aqua.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
registries:
- type: standard
ref: v4.360.0
packages:
- name: hashicorp/terraform@v1.9.5
- name: suzuki-shunsuke/tfcmt@v4.14.0
Loading
Loading