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
6 changes: 2 additions & 4 deletions .github/actions/pulumi-setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# pulumi.yml の preview と up に共通の準備。checkout は呼ぶ側でする(このファイル自体が checkout の中にある)。
# pulumi.yml の preview と up に共通の準備。checkout は呼ぶ側でする。
name: pulumi setup
description: bun と Pulumi の CLI を入れ、SDK を作って bun install と型の確認まで済ませる
runs:
Expand All @@ -7,12 +7,10 @@ runs:
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: pulumi/.bun-version
# CLI だけを入れる (command なし)
- uses: pulumi/actions@v7
with:
pulumi-version-file: pulumi/.pulumi-version
# SDK (Pulumi.yaml の packages) を作って bun install まで済ませる。lock がずれたら落とす (--frozen-lockfile の代わり)。
# Renovate の枝では postUpgradeTasks が同じ pulumi install で lock を作るので、ずれない (pulumi/README.md)
# lock がずれたら落とす(--frozen-lockfile の代わり)
- shell: bash
working-directory: pulumi
run: |
Expand Down
24 changes: 7 additions & 17 deletions .github/workflows/cilium-drift.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
# **cilium の値が git とクラスタでズレていないかを見る。読むだけ。**
#
# cilium は Argo CD の管理外(CNI 本体なので、それが動いていないと Argo CD も動かない)。
# machine config の inlineManifest に載っていて、bootstrap/cilium/values.yaml を直しても
# **人が render.sh → apply-config → upgrade-k8s を流すまでクラスタには届かない。**
# `upgrade-k8s` は流せば差分を出すが、流し忘れたことは教えてくれない。逆に、
# クラスタ側だけ手でいじった設定も git には出てこない(`node-port-range` で実際に起きた)。
#
# **当てるところまでは自動化しない**(talos/README.md「適用は CI にやらせない」)。
# **代わりにズレを見つけるところまでを自動でやる。** 権限は cilium-config の `get` だけ。
# cilium の値が git とクラスタでズレていないかを見る(読むだけ。権限は cilium-config の get だけ)。
# cilium は inlineManifest なので、人が render.sh → apply-config → upgrade-k8s を流すまで届かない。
# 当てるのは自動化しない(talos/README.md)。
name: cilium drift

on:
Expand All @@ -16,7 +9,7 @@ on:
paths:
- bootstrap/cilium/**
- .github/workflows/cilium-drift.yml
# クラスタ側だけを手でいじったときは push が無いので、定期的にも見る。
# クラスタ側だけ手でいじったときのため
schedule:
- cron: "0 3 * * 1"
workflow_dispatch:
Expand Down Expand Up @@ -61,8 +54,7 @@ jobs:
--certificate-authority=bootstrap/apiserver/ca.crt \
--token="$T" "$@"
}
# **`--kube-version` を渡すこと。** チャートは Capabilities を見て分岐するので、
# 渡さないと helm の既定版で描かれて偽の差分が出る。
# --kube-version が無いと helm の既定版で描かれて偽の差分が出る
KV=$(k version -o json | jq -r .serverVersion.gitVersion)
echo "kube-version=$KV"
export HOWTO="talos/README.md「inlineManifests を直したとき」の render.sh → apply-config → upgrade-k8s を流すこと"
Expand All @@ -75,12 +67,10 @@ jobs:

k -n kube-system get cm cilium-config -o json | jq '.data' > live.json

# YAML はここまでで JSON にしてある。python は標準ライブラリだけで済ませる
# (ランナーに PyYAML が入っている保証は無い)。
# PyYAML は無いかもしれないので JSON で渡す
python3 - <<'PY'
import os, sys, json
# 値が空文字のキーは両側から落とす。Talos の inlineManifest で当てると live に残らない
# (2026-10-17 から debug-verbose・nodeport-addresses・policy-cidr-match-mode が「git にしか無い」で毎回落ちていた)
# 値が空文字のキーは inlineManifest で当てると live に残らないので両側から落とす
r = {k: v for k, v in json.load(open("rendered.json")).items() if v != ""}
l = {k: v for k, v in json.load(open("live.json")).items() if v != ""}
only_git = sorted(set(r) - set(l))
Expand Down
37 changes: 7 additions & 30 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,13 @@ name: Claude Code Review

on:
pull_request:
# **`labeled` が要る。** bot の PR は `opened` の時点ではスキップされるので、
# あとから `needs-review` を貼っても再実行されなかった(2026-09-07 に #85 で発覚)。
# 下の `if` がラベルの有無を見ているので、無関係なラベルで起動しても素通りする。
# `labeled` は、あとから needs-review を貼った bot の PR を拾うため
types: [opened, synchronize, reopened, ready_for_review, labeled]

jobs:
claude-review:
# Draft PR はスキップ。fork からの PR も対象外 (secrets が渡らないため) —
# そちらは claude-code-review-fork.yml がラベル起動で処理する。
#
# bot の PR も原則スキップするが、**`needs-review` ラベルが付いていれば見る**。
# 自分でマージされる PR をレビューしても誰も読まないので飛ばす。人を待つ PR は
# レビューする価値がある、という切り分け。Renovate は自動マージしないまとまり
# (メジャーと chart) にだけこのラベルを付ける (5ym/repo-config)。
#
# 実例: #7 に postgres 17 → 18 が入っていた。PVC の PG_VERSION は 17 なので
# そのまま上げると起動を拒否する。1 行の差分でも、依存そのものに内在するリスクは
# レビューで拾える。
# Draft と fork(secrets が渡らない)はスキップ。bot の PR は needs-review が付いたもの
# (Renovate が自動マージしないまとまり)だけ見る。
if: >-
github.event.pull_request.draft == false &&
github.event.pull_request.head.repo.full_name == github.repository &&
Expand All @@ -43,13 +32,8 @@ jobs:
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true
# **Renovate の PR をレビューするのに要る。** action 自身が bot 起動を拒否し、
# `Workflow initiated by non-human actor` で落ちる(2026-09-07 に #85 で発覚)。
# 上の `if` で「bot は needs-review が付いているときだけ」に絞ってあるので、
# ここは `*` ではなく renovate だけを名指しする。`*` は公開リポジトリでは
# 外部の App がプロンプトを持ち込める(action のドキュメントの警告)。
# Renovate は 2026-10 から自前の GitHub App(`doa-renovate`)で動いている。名前が変わると
# ここで黙って落ちる(#206 で発覚)ので、両方を名指ししておく
# 無いと action が bot 起動を拒否する。`*` にしない(外部の App がプロンプトを持ち込める)。
# Renovate の App 名が変わるとここで黙って落ちる
allowed_bots: "renovate[bot],doa-renovate[bot]"
prompt: |
REPO: ${{ github.repository }}
Expand Down Expand Up @@ -81,14 +65,7 @@ jobs:
突き合わせて**影響を言うこと (例: PVC の PG_VERSION と major の不一致)
- リリースノートが本当に見つからなければ、探した先を書いたうえで「要確認」とすること

# WebFetch と gh release はリリースノートを読むため。**読み取りだけ**に絞る。
# 取ってくる中身は第三者が書いたもので、レビュー本文への指示が混ざりうる
# (プロンプトインジェクション)。このワークフローはコメントしかできず、
# マージも push もできないので影響は限定される。
#
# **注釈をこのブロックの中に書かないこと。** `claude_args: |` は YAML の
# ブロックスカラーなので `#` はコメントにならず、そのまま CLI の引数として
# 渡って --allowedTools の解釈が壊れる(2026-09-07 に踏んだ。レビューが
# 「WebFetch の権限が無い」と言うのに設定上は許可されている、という形で出た)。
# WebFetch と gh release はリリースノート用で、読み取りだけに絞る。
# 注釈をこのブロックの中に書かない(`#` がそのまま CLI の引数になり --allowedTools が壊れる)
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh release view:*),Bash(gh release list:*),WebFetch"
65 changes: 10 additions & 55 deletions .github/workflows/pulumi.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,7 @@
# **Cloudflare・Entra・NetBird の設定を Pulumi で当てる。** 中身は pulumi/。
#
# PR → preview。差分を PR にコメントする(読み取りの鍵だけ)
# main → up。**マージした時点で当たる**(書き込みの鍵は Environment `pulumi-apply` に置き、
# main からしか使えない)
# 毎週月曜 → preview --refresh --expect-no-changes。Cloudflare 側を直接いじったら落ちる。
# Pulumi で表せない残り(カスタムドメイン・容量)も pulumi/report.ts で見る
# 鍵の期限も見る(pulumi/expiry.sh。30 日を切ったら Matrix の部屋 server に投稿)
#
# 書き込みの鍵は R2 の設定を書き換えられる(= バックアップのバケットを消せる)。
# 守りは 2 つ: Environment で main に限ること、doany-restic に `protect` が付いていること
# (外すと Pulumi は消すのを拒否しなくなる。pulumi/index.ts)。
#
# 秘密(リポジトリの Actions secrets):
# PULUMI_BACKEND_URL state の置き場(R2 の doany-pulumi。endpoint にアカウント ID が入るので secrets)
# PULUMI_CONFIG_PASSPHRASE state の暗号化。state に秘密は入れていないので、失くしたら作り直せる(pulumi/README.md)
# CLOUDFLARE_ACCOUNT_ID
# PREVIEW_CLOUDFLARE_API_TOKEN / PREVIEW_AWS_ACCESS_KEY_ID / PREVIEW_AWS_SECRET_ACCESS_KEY
# R2 の設定の読み取り + doany-pulumi の読み書き(state のロックに要る)
# PREVIEW_NB_PAT NetBird の読み取り (サービスユーザー gitops-pulumi-preview、Auditor)
# MATRIX_SERVER_WEBHOOK_URL 鍵の期限の投稿先(expiry)
# Environment `pulumi-apply` の秘密:
# CLOUDFLARE_API_TOKEN / AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
# R2 の設定の書き込み + doany-pulumi の読み書き
# NB_PAT NetBird の書き込み (サービスユーザー gitops-pulumi-apply、Admin)
# AWS_* は state の置き場(R2 の S3 API)のため。値は同じ API トークンから作る(ID と値の sha256)。
#
# Entra(pulumi/entra.ts)には**鍵を置かずに**入る。GitHub Actions の OIDC トークンを Entra の
# ワークロード ID のフェデレーションで受ける(id-token: write が要る)。ID は秘密ではないので Variables:
# AZURE_TENANT_ID リポジトリの Variables
# PREVIEW_AZURE_CLIENT_ID リポジトリの Variables。アプリ登録「gitops pulumi preview (Entra read)」
# (Application.Read.All。PR と main から入れる)
# AZURE_CLIENT_ID Environment `pulumi-apply` の Variables。「gitops pulumi apply (Entra write)」
# (Application.ReadWrite.OwnedBy。所有者になっている共用のアプリ登録しか書けない)
# Cloudflare・Entra・NetBird を Pulumi で当てる(pulumi/)。PR → preview、main → up、
# 毎週 → refresh して差分があれば落とす + report.ts + 鍵の期限。
# 書き込みの鍵はバックアップのバケットを消せるので Environment `pulumi-apply`(main だけ)に置く。
# 秘密と Variables の一覧は pulumi/README.md。
name: pulumi

on:
Expand Down Expand Up @@ -59,11 +29,8 @@ env:
PULUMI_BACKEND_URL: ${{ secrets.PULUMI_BACKEND_URL }}
PULUMI_CONFIG_PASSPHRASE: ${{ secrets.PULUMI_CONFIG_PASSPHRASE }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# doany.io のゾーン(dns.ts)。アカウント ID と同じく git に置かない
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
# Cloudflare の通知の送り先(notifications.ts)。hookshot の受け口の ID を含むので secrets に
CLOUDFLARE_NOTIFY_WEBHOOK_URL: ${{ secrets.CLOUDFLARE_NOTIFY_WEBHOOK_URL }}
# cf / Pulumi のテレメトリを切る
DO_NOT_TRACK: "1"
PULUMI_SKIP_UPDATE_CHECK: "true"
# azuread provider は GitHub Actions の OIDC トークン(ACTIONS_ID_TOKEN_REQUEST_*)を自分で拾う
Expand All @@ -74,27 +41,23 @@ jobs:
preview:
if: github.event_name != 'push'
runs-on: ubuntu-latest
# 同じ PR の古い preview は捨ててよい。up とは別の列にする ── 1 列にまとめると、GitHub は
# 待ちを 1 件しか持たないので、マージ後の up が後から来た preview に押し出されて消えうる
# up と別の列にする(待ちは 1 件だけなので、up が preview に押し出されうる)
concurrency:
group: pulumi-preview-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
# 差分を PR にコメントする
pull-requests: write
# Entra へ OIDC で入る
id-token: write
env:
ARM_CLIENT_ID: ${{ vars.PREVIEW_AZURE_CLIENT_ID }}
# NetBird のサービスユーザー gitops-pulumi-preview (Auditor、読み取りだけ)
NB_PAT: ${{ secrets.PREVIEW_NB_PAT }}
CLOUDFLARE_API_TOKEN: ${{ secrets.PREVIEW_CLOUDFLARE_API_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.PREVIEW_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.PREVIEW_AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v7
# bun・Pulumi の CLI・SDK・型の確認(up と同じ)
- uses: ./.github/actions/pulumi-setup

- uses: pulumi/actions@v7
Expand All @@ -106,7 +69,7 @@ jobs:
pulumi-version-file: pulumi/.pulumi-version
comment-on-pr: true
edit-pr-comment: true
# 毎週の実行(と手動実行)は実物を読み直し、1 つでも差分があれば落とす(= Cloudflare 側を直接いじった)
# 週次と手動は実物を読み直し、差分があれば落とす
refresh: ${{ github.event_name != 'pull_request' }}
expect-no-changes: ${{ github.event_name != 'pull_request' }}

Expand All @@ -119,19 +82,15 @@ jobs:
if: github.event_name == 'push'
runs-on: ubuntu-latest
environment: pulumi-apply
# 走っている up は止めない(途中で切ると state がロックされたまま残る)。待ちが押し出されても、
# 残るのは新しい main なので取りこぼしは無い。preview と同時に走ると片方がロックで落ちるが、
# state は壊れない(落ちた方を流し直す)
# 走っている up は止めない(途中で切ると state がロックされたまま残る)
concurrency:
group: pulumi-up
cancel-in-progress: false
permissions:
contents: read
# Entra へ OIDC で入る
id-token: write
env:
ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
# NetBird のサービスユーザー gitops-pulumi-apply (Admin)
NB_PAT: ${{ secrets.NB_PAT }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
Expand All @@ -149,10 +108,8 @@ jobs:
pulumi-version-file: pulumi/.pulumi-version
always-include-summary: true

# 鍵の期限(pulumi/expiry.sh)。30 日を切ったか切れていたら Matrix の部屋 server に投稿する。
# PR では投稿せずログに出すだけ。手動実行で `report` を付けると全部を投稿する。
# 鍵は自分の期限しか読めないので、preview 用と apply 用をそれぞれで見る。apply 用は Environment `pulumi-apply`
# (main だけ)にあるので PR では見ない。
# 鍵の期限。PR ではログだけ。鍵は自分の期限しか読めないので preview 用と apply 用を別々に見る
# (apply 用は main だけ)。
expiry:
if: github.event_name != 'push'
strategy:
Expand All @@ -163,14 +120,12 @@ jobs:
environment: ${{ matrix.key == 'apply' && 'pulumi-apply' || '' }}
permissions:
contents: read
# Entra へ OIDC で入る (preview だけ)
id-token: write
env:
KEY: ${{ matrix.key }}
NB_PAT: ${{ matrix.key == 'apply' && secrets.NB_PAT || secrets.PREVIEW_NB_PAT }}
CF_TOKEN: ${{ matrix.key == 'apply' && secrets.CLOUDFLARE_API_TOKEN || secrets.PREVIEW_CLOUDFLARE_API_TOKEN }}
CF_ACCOUNT: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# hookshot の部屋 server の受け口 (https://m.doany.io/webhook/<Infisical /matrix/matrix の hook-server>)
WEBHOOK: ${{ secrets.MATRIX_SERVER_WEBHOOK_URL }}
DRY_RUN: ${{ github.event_name == 'pull_request' }}
REPORT_ALL: ${{ inputs.report == true }}
Expand All @@ -184,7 +139,7 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
allow-no-subscriptions: true
- run: pulumi/expiry.sh
# 確かめられなかったことも知らせる (鍵が切れて 401 になったときなど。黙って落ちると気づかない)
# 確かめられなかったことも知らせる(鍵が切れて 401 など)
- if: failure() && github.event_name != 'pull_request'
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
Expand Down
11 changes: 3 additions & 8 deletions .github/workflows/renovate-validate.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# **renovate.json を壊さないための見張り。**
#
# Renovate は**知らないトップレベルのキーをエラーにする。** `ignorePathsDescription`
# のような「それっぽい自己文書化のキー」も通らない(2026-09-08 に実際に書いて、
# レビューで指摘されるまで気づかなかった)。**壊れていても Renovate が黙って
# 動かなくなるだけ**で、PR も来なくなるので気づきにくい。
# renovate.json の見張り。壊れていても Renovate は黙って止まるだけで気づきにくい
# (知らないキーは `ignorePathsDescription` のようなものもエラー)。
name: renovate validate

on:
Expand All @@ -27,6 +23,5 @@ jobs:
- uses: actions/setup-node@v7
with:
node-version: 24.22.0
# 版は固定しない ── **Renovate 本体が受け付けるか**を見たいので、
# そのときの最新の判定に合わせるのが正しい。
# 版は固定しない(最新の Renovate が受け付けるかを見る)
- run: npx --yes --package renovate renovate-config-validator --strict
Loading
Loading