Skip to content

review by AI #256

Closed
Closed

Description

@ks6088ts

template-python 0.0.13 批判的レビューレポート

最新ノウハウ(2026年10月時点)に基づく修正事項と、新テンプレートの提案

項目 内容
対象 template-python-0.0.13.zip(36 ファイル。CI/CD 6 ワークフロー、Dockerfile、Makefile、Python パッケージ、テスト、MkDocs ドキュメント、uv.lock 144 パッケージ)
レビュー日 2026-10-12
成果物 本レポート、および新テンプレート一式 repos/template-python/(全アセット同梱)
方法 全ファイルの精読 / uv.lock の依存グラフ解析 / テスト挙動のオフライン再現 / 2026-10-12 時点の一次情報(GitHub Docs・Changelog、各ツールのリリースノート、PyPI)の調査

1. エグゼクティブサマリー

1.1 総評

現行テンプレートは「SHA ピン留め」「uv --locked」「import-linter」「zizmor/actionlint」など良い素材を揃えている一方で、品質ゲートとしての正しさとサプライチェーン上の検証可能性に重大な欠陥があり、そのままプロダクションのひな形として配布するのは推奨できません。主な理由は次の 5 点です。

  1. テストゲートが壊れている: ライブラリのロガーが propagate=False を設定しているため、pytest の caplog(root ロガーにハンドラを追加する方式)がログを受け取れず、tests/test_core.py の 3 テストは失敗します。同じ仕組みをオフラインで再現し、捕捉件数 0 件を確認しました(A-1)。
  2. リリースが誤ったものを公開しうる: プレリリースタグ v1.2.3-rc.1 を push すると、安定版タグ 1.2.3 と latest が上書きされます(D-1)。テスト結果を待たずに公開し(D-2)、Docker Hub と GHCR を別々にビルドするため同一バージョンでもダイジェストが一致しません(D-3)。
  3. 検証可能性(Provenance)が無い: Dockerfile は uv export --no-hashes → pip install でハッシュ検証を捨てています(C-1)。成果物の来歴証明(Artifact Attestations)、SBOM、イミュータブルリリースもありません(C-5)。
  4. 「失敗を可視化する」という自らの原則に反している: Trivy は非ブロッキング、zizmor は --offline --min-severity high のため、オンライン監査と medium 以下の指摘(credential の残留、過剰権限など)を素通りさせます(C-2, C-3)。
  5. GitHub プラットフォーム機能をほぼ使っていない: ルールセット、必須チェック、CodeQL、依存関係レビュー、シークレットスキャン、Environments、OIDC、Pages の Actions デプロイ、Copilot のリポジトリカスタマイズ(AGENTS.md、セットアップステップ、エージェント、スキル、フック)がありません(I-1, J-1 ほか)。

加えて、2026 年 10 月時点では Python 3.10 が EOL(2026-10-01)、Python 3.15 が GA(2026-10-09)、Actions ランナーから Node 20 が削除(2026-09-23)、MkDocs 1.x は保守停止・Material for MkDocs は保守モード(2027-05-05 まで)、ubuntu-latest が 26.04 へ移行開始(2026-10-19〜) という環境変化があり、テンプレートの前提そのものを更新する必要があります。

1.2 重大指摘(P0/P1 の上位 12 件)

ID 重要度 指摘 影響
A-1 P0 propagate=False により caplog がログを捕捉できず、テスト 3 件が失敗 品質ゲートが成立しない
D-1 P0 プレリリースタグが安定版タグと latest を上書きする 利用者に RC が配布される
C-1 P0 コンテナでハッシュ検証なしの pip install ロックの改ざん耐性が無効になる
D-2 P1 タグ push でテストを経ずに公開 壊れたコミットを公開しうる
D-3 P1 レジストリごとに二重ビルドし、ダイジェストが不一致 再現性・検証性が失われる
C-2 P1 zizmor がオフラインかつ high 以上のみ 権限・認証情報系の指摘を見逃す
C-3 P1 Trivy が非ブロッキング(Trivy 自体も 2026-03 に侵害事案あり) 脆弱イメージを公開しうる
C-5 P1 来歴証明・SBOM・イミュータブルリリースが無い 利用者が改ざんを検証できない
E-1 P1 コンテナが root で実行される 侵害時の影響範囲が拡大
F-1 P1 ライブラリがハンドラ・伝播・レベルを設定する アプリ側のログ設定を破壊する
I-1 P1 ルールセット・必須チェック・タグ保護が無い ゲートを迂回してマージ・公開できる
J-1 P1 copilot/** を CI 対象にしながら Copilot 用の構成が無い エージェントが規約・環境を知らない

1.3 観点別スコアカード

5 段階評価です(5 = プロダクション水準)。

観点 現行 新テンプレート 主な改善
テストの正しさ・網羅性 1 4 回帰テスト、CLI E2E、Hypothesis、ブランチカバレッジ 90%
静的解析 3 5 Ruff 34 ルール群、mypy strict に一本化、import-linter 2 契約、deptry
CI 設計 2 5 並列ジョブ、ci-success 集約、merge_group、concurrency、OS/Python 行列
サプライチェーン 2 5 ハッシュ検証、zizmor オンライン監査、pip-audit、依存関係レビュー、Scorecard
リリース/CD 1 5 タグ検証 → CI → attest → ネイティブマルチアーキ → イミュータブルリリース
コンテナ 2 4 非 root、venv のみコピー、ビルドチェックをエラー扱い、Trivy ブロック
アプリ設計 2 4 層分離、型付き設定、JSON ログ、終了コード設計
開発者体験 3 5 Dev Container/Codespaces、uv 単一管理、make check、プロジェクト初期化ツール
ドキュメント 2 4 Zensical(strict)、英日併記、ADR
GitHub ガバナンス 1 5 ルールセット JSON、設定のコード化、Environments、OIDC
AI 駆動開発 1 5 AGENTS.md、セットアップステップ、エージェント、スキル、フック、MCP

2. レビューの範囲・方法・前提

2.1 対象

ワークフロー 6 本(test / docker / docker-release / ghcr-release / github-pages / azure-static-web-app)、dependabot.yaml、.pre-commit-config.yaml、.devcontainer/devcontainer.json、.vscode/extensions.json、Dockerfile、.dockerignore、compose.yml、Makefile、pyproject.toml、uv.lock、template_python/(4 モジュール)、scripts/template.py、tests/(3 モジュール)、docs/(4 ページ)、mkdocs.yml、notebooks/templates.ipynb、README.md、LICENSE、.gitignore、.env.template。

2.2 方法

  • 精読: 全ファイルを 1 行ずつ確認しました。
  • ロック解析: uv.lock を解析しました(144 パッケージ。うち 69 は notebook グループ専用で、Python バージョンによる分岐が 3 パッケージ)。
  • 挙動の再現: サンドボックスにはネットワークがないため、pytest の caplog と同じ仕組み(root ロガーへのハンドラ追加)を標準ライブラリで再現し、現行の loggers.py / core.py で検証しました。
  • 最新動向の調査: 2026-10-12 時点の一次情報を調べました(付録 B)。GitHub Actions は新旧テンプレートで使う 22 アクションについて、最新リリースとそのコミット SHA(40 桁)を照合しています。

2.3 重要度の定義

重要度 定義
P0 (Critical) 誤った成果物の公開、セキュリティ事故、品質ゲートの無効化に直結する。直ちに修正する
P1 (High) 本番運用・保守で高確率に問題化する。テンプレート配布前に修正する
P2 (Medium) 品質・保守性・開発体験を損なう。次の改訂で修正する
P3 (Low) 改善推奨・衛生事項

3. 修正が必要な事項の一覧

P0 が 3 件、P1 が 25 件、P2 が 36 件、P3 が 10 件、合計 74 件です。「対応先」は新テンプレート内のファイルです。詳細は第 4 章を参照してください。

A. テストと品質ゲート

ID 重要度 指摘 対象 対応先
A-1 P0 propagate=False のため caplog がログを捕捉できず、test_core.py の 3 テストが失敗する template_python/loggers.py, tests/test_core.py logging_config.py, tests/test_logging_config.py(回帰テスト)
A-2 P1 load_dotenv() がプロセス環境をテストセッション全体に漏らす。CWD に依存し、アサーション文言も誤っている tests/test_settings.py tests/conftest.py(autouse の隔離 fixture)、tests/test_config.py
A-3 P1 CLI(scripts/template.py)が無テストかつカバレッジ対象外 pyproject.toml, tests/ tests/test_cli.py(CliRunner 9 件 + python -m)
A-4 P2 ログ文字列だけに依存する弱いアサーション、意味の無い parametrize、未使用の session fixture tests/test_core.py 純粋関数の値検証 + Hypothesis
A-5 P2 カバレッジ閾値なし、ブランチカバレッジ無効、CI にレポートが出ない pyproject.toml, test.yaml fail_under = 90, branch = true, Job Summary, artifact
A-6 P2 pytest 設定の不備(strict 系なし、警告がエラーにならない、常時 --cov、ログの二重大量出力、pythonpath=['.']) pyproject.toml, Makefile pytest 9 ネイティブ [tool.pytest] + strict = true
A-7 P2 型チェッカー 3 種(mypy、ty、pyrefly)の併用。ty は 0.0.x(beta)をゲートに使っている Makefile, pyproject.toml mypy strict に一本化(ADR-0002)
A-8 P2 Ruff が E, F, I, UP のみで、ロギングの f-string などを見逃す。ignore=["D203"] は無効な設定 pyproject.toml 34 ルール群を明示的に選択
A-9 P3 import-linter 契約が scripts → template_python だけで、ライブラリ内部と外部フレームワークへの依存は無制約 pyproject.toml layers 契約(exhaustive)+ forbidden 契約

B. CI 設計

ID 重要度 指摘 対象 対応先
B-1 P1 push と pull_request で二重起動、concurrency なし、merge_group 非対応 test.yaml, docker.yaml ci.yaml
B-2 P1 必須チェック用の集約ジョブが無い(行列名を変えるとブランチ保護が壊れる) test.yaml ci.yaml の ci-success
B-3 P2 setup-uv(python-version 指定)と setup-python で Python を二重にセットアップしている 3 ワークフロー setup-uv に一本化
B-4 P2 全ゲートを 1 ジョブで直列実行。docs ビルドと ruff の重複実行 test.yaml, github-pages.yaml lint / workflows / test / docs / package / container の並列実行
B-5 P2 sdist/wheel が CI で一度もビルド・検証されない (なし) package ジョブ(隔離環境でのスモークテスト)
B-6 P2 EOL の 3.10 を含み、GA の 3.15 を含まない。Windows/macOS とフリースレッドの検証が無い test.yaml 3.12〜3.15、Windows/macOS、3.14t(非ブロッキング)
B-7 P2 ubuntu-latest は 2026-10-19 から Ubuntu 26.04 へ段階移行するため再現性がない 全ワークフロー ubuntu-24.04 に固定
B-8 P2 uv のバージョンが 6 か所にハードコードされている devcontainer, workflows, Dockerfile, README [tool.uv] required-version を唯一の定義にする
B-9 P2 actionlint の検証が同一リリースの checksums.txt 頼み。ローカルは別途インストールが必要 test.yaml, Makefile actionlint-py を uv.lock でハッシュ固定
B-10 P3 github-pages.yaml に timeout が無く、全ワークフローでジョブ単位の権限が無い 全ワークフロー 全ジョブに timeout-minutes と permissions

C. サプライチェーン・セキュリティ

ID 重要度 指摘 対象 対応先
C-1 P0 uv export --no-hashes → pip install でハッシュ検証なしにインストールしている Dockerfile uv sync --locked(ハッシュ検証)
C-2 P1 zizmor が --offline --min-severity high のため、オンライン監査と medium 以下を見逃す Makefile オンライン監査 + --min-severity=low + .github/zizmor.yml
C-3 P1 Trivy が非ブロッキング。2026-03 の Trivy 侵害(GHSA-69fq-xp46-6x23)を踏まえた取得経路の固定も無い Makefile SHA 固定の trivy-action + version: v0.75.0 + fixed HIGH/CRITICAL でブロック
C-4 P1 CodeQL、依存関係レビュー、シークレットスキャン/プッシュ保護、依存脆弱性監査、Scorecard が無い (なし) CodeQL default setup、security.yaml、scorecard.yaml、設定スクリプト
C-5 P1 Artifact Attestations、SBOM、イミュータブルリリースが無い リリース系 actions/attest v4、BuildKit provenance/SBOM、イミュータブルリリース
C-6 P1 長期 PAT(Docker Hub)と長期デプロイトークン(SWA)をリポジトリシークレットで全ブランチに公開している docker-release.yaml, azure-static-web-app.yaml GHCR は GITHUB_TOKEN、Docker Hub は Environment 限定、Azure は OIDC
C-7 P2 Dependabot にグループ化が無く、cooldown は uv だけ dependabot.yaml 全エコシステムに cooldown 7 日とグループ化
C-8 P2 ワークフロー全体に packages: write / contents: write を付与している ghcr-release.yaml, github-pages.yaml permissions: {} + ジョブ単位の最小権限
C-9 P2 contents: write のトークンを persist-credentials 既定(true)のまま checkout に残している github-pages.yaml 全 checkout で persist-credentials: false
C-10 P2 原作者の Google Analytics ID がハードコードされ、利用者の閲覧データが第三者へ送られる mkdocs.yml 解析設定を削除
C-11 P2 設定の全量を INFO ログに出力し、SecretStr を使っていない scripts/template.py SecretStr、秘匿表示のテスト、config コマンド

D. リリース / CD

ID 重要度 指摘 対象 対応先
D-1 P0 v1.2.3-rc.1 が :1.2.3 と :latest を上書きする docker-release.yaml, ghcr-release.yaml metadata-action type=pep440 + latest=auto
D-2 P1 テスト結果を待たずにタグから公開する 同上 release.yaml で再利用 CI を needs
D-3 P1 レジストリごとに別ビルドし、ダイジェストが不一致になる 同上 1 回ビルドし、同一ダイジェストをミラー
D-4 P1 パッケージ版(0.0.1)とタグ(v0.0.13)が乖離。GIT_TAG / GIT_REVISION は未使用 pyproject.toml, Dockerfile verify ジョブ(タグ = uv version)、OCI ラベル
D-5 P1 GitHub Release(ノート・成果物)を作らない (なし) gh release create --generate-notes + .github/release.yml
D-6 P2 QEMU のセットアップなしで arm64 をビルドしている(エミュレーション前提で遅い) 同上 ネイティブ amd64/arm64 → ダイジェストのマージ
D-7 P2 mkdocs gh-deploy --force で gh-pages ブランチへ force push(旧方式、並行制御なし) github-pages.yaml actions/deploy-pages + github-pages 環境
D-8 P2 SWA ワークフローに単一値の matrix と Python の二重セットアップがある azure-static-web-app.yaml docs-azure-swa.yaml(OIDC)
D-9 P3 docs のビルドが strict でなく、リンク切れを検出しない Makefile zensical build --strict

E. コンテナ

ID 重要度 指摘 対象 対応先
E-1 P1 USER が無く root で実行される Dockerfile UID 10001、venv は root 所有で読み取り専用
E-2 P1 COPY . . でコンテキスト全体を同梱し、パッケージとしてインストールしていない Dockerfile 非 editable wheel を venv に入れ、venv のみコピー
E-3 P2 .dockerignore が JS 向けの流用で、インラインコメントによる無効パターンやファイル名の不一致がある .dockerignore allowlist 方式
E-4 P2 bookworm(2026-07-11 に通常サポート終了)を使い、ベースイメージが曖昧 Dockerfile, devcontainer python:3.14-slim-trixie
E-5 P2 compose が http.server で /app を 0.0.0.0 に公開し、.env が無いと起動しない compose.yml compose.yaml(read_only、cap_drop、no-new-privileges、required: false)
E-6 P3 OCI ラベルが無く、GHCR とリポジトリが紐付かない Dockerfile metadata-action のラベル + LABEL
E-7 P3 GIT_TAG の sed -e s/v//g がすべての v を削除する Makefile 版は uv version から取得

F. Python パッケージとアプリ設計

ID 重要度 指摘 対象 対応先
F-1 P1 ライブラリがハンドラ追加・propagate=False・レベル設定を行う loggers.py アプリ側の configure_logging()(冪等、JSON 対応)
F-2 P1 import 時の副作用。各モジュールが import 時に設定を読んで lru_cache する core.py, loggers.py 設定は composition root(CLI)だけが生成する
F-3 P1 --verbose が __main__ ロガーにしか効かず、basicConfig で二重出力になる scripts/template.py root に適用 + テスト
F-4 P1 load_dotenv(override=True) が実環境変数を .env で上書きする scripts/template.py pydantic-settings の優先順位(環境変数 > .env)
F-5 P2 flat layout で CLI がパッケージに含まれず、インストールできない pyproject.toml src layout、[project.scripts]、__main__.py
F-6 P2 設定の型が弱い(str のログレベル、接頭辞なしの汎用名)。python-dotenv を直接依存にしている settings/project.py Literal と正規化、TEMPLATE_PYTHON_ 接頭辞、frozen
F-7 P2 hello --name が name を使わず、stdout に何も出さない。終了コードの設計が無い scripts/template.py stdout/stderr を分離、終了コード 2 と 78
F-8 P2 ログに f-string を使い、「構造化ログ」と称しているのに実体はテキスト core.py, docs % 形式 + JSON フォーマッタ
F-9 P2 hatchling がバージョン無制約。メタデータ不足(license、classifiers、urls、py.typed) pyproject.toml uv_build>=0.13.0,<0.14、PEP 639
F-10 P2 requires-python >=3.10(2026-10-01 EOL) pyproject.toml >=3.12(既定は 3.14)
F-11 P3 コンテナの CMD が python -m template_python.core(デモ用の __main__) Dockerfile ENTRYPOINT ["template-python"]

G. 開発者体験

ID 重要度 指摘 対象 対応先
G-1 P1 devcontainer が dev- タグ(main から生成され上書き更新される開発ビルド)を使い、bookworm で、uv を毎回 pipx で導入している devcontainer.json 3-3.14-trixie + uv イメージ、prebuild 対応
G-2 P1 プロジェクト名や作者名(ks6088ts、template-python)が散在し、初期化手段が無い 全体 scripts/init_project.py + initialize.yaml
G-3 P2 make install-deps-docs(exact sync)が開発ツールを削除し、git フックが壊れる Makefile docs は uv run --only-group docs
G-4 P2 pre-commit の ruff と uv.lock の ruff が別管理でドリフトする。uv.lock の検査が無い .pre-commit-config.yaml local フック(uv run --frozen)+ uv lock --check
G-5 P2 VS Code の推奨拡張が devcontainer と不一致で、診断も重複する .vscode/, devcontainer 統一し、mypy 拡張に一本化
G-6 P3 ノートブックに実行出力(ANSI、タイムスタンプ)をコミットし、テスト対象外 notebooks/ 削除(必要なら nbstripout)

H. ドキュメント

ID 重要度 指摘 対象 対応先
H-1 P1 MkDocs 1.x は保守停止、Material for MkDocs は保守モード、static-i18n は凍結 mkdocs.yml Zensical(ADR-0003)
H-2 P2 変更履歴のような文言(「Git hooks and deployment workflows are unchanged…」)が残り、同じ説明が 4 か所に重複している README, docs ページ責務を整理し、ADR に分離
H-3 P2 事実と異なる記述(「structured logging」、「Surface failures」原則と非ブロッキングのスキャン) docs 実装と一致させる
H-4 P3 site_url と repo_url のオーナーが不一致。作者の SNS リンクが残っている mkdocs.yml プレースホルダー化

I. GitHub ガバナンス

ID 重要度 指摘 対象 対応先
I-1 P1 ルールセット、必須チェック、タグ保護、CODEOWNERS が無い(テンプレートは設定を複製しない) (なし) .github/rulesets/*.json + scripts/configure_repository.py
I-2 P1 Actions 設定(既定トークン read、SHA ピン必須、PR 承認の禁止)、イミュータブルリリース、PVR を有効化する手順が無い (なし) configure_repository.py
I-3 P2 SECURITY.md、CONTRIBUTING、行動規範、Issue Forms、PR テンプレートが無い (なし) 一式を同梱
I-4 P3 LICENSE の著作権者が原作者のまま LICENSE プレースホルダー + 初期化スクリプト

J. AI 駆動開発(GitHub Copilot)

ID 重要度 指摘 対象 対応先
J-1 P1 copilot/** を CI 対象にしながら AGENTS.md、指示ファイル、copilot-setup-steps.yml が無い .github/ 一式を同梱
J-2 P2 エージェントのガードレール(フック)、専門エージェント、スキル、MCP 設定が無い .github/ hooks(テスト付き)、agents、skills、.vscode/mcp.json
J-3 P3 Copilot コードレビューの自動依頼が構成されていない (なし) rulesets/copilot-code-review.json(任意)

4. 指摘事項の詳細

各項目は「現状 → 問題 → 修正方針 → 新テンプレートでの対応」の順に記載します。

4.A テストと品質ゲート

A-1 (P0) propagate=False で caplog が機能せず、テストが失敗する

現状

# template_python/loggers.py
logger.setLevel(log_level)
logger.propagate = False          # ← 親(root)へ伝播させない
if logger.handlers:
    return logger
handler = logging.StreamHandler()  # ← ライブラリが自前でハンドラを付ける
# tests/test_core.py
with caplog.at_level(logging.INFO, logger="template_python.core"):
    hello_world()
assert "Hello World" in caplog.text   # ← root に届かないので常に空

問題

  • pytest の caplog は root ロガーにハンドラを追加して記録を集めます(pytest 公式ドキュメント「The caplog fixture adds a handler to the root logger」)。at_level(logger=...) はレベルを変えるだけです。非伝播ロガーにも対応する PR(logging: also capture logs from non-propagating loggers pytest-dev/pytest#14375)は 2026-09 時点で未マージであり、ロックされている pytest 9.1.1 には含まれていません。
  • 同じ仕組みを標準ライブラリで再現すると、propagate = False、root 側の捕捉件数 0、"Hello World" in caplog.text → False になりました。test_hello_world_logs_message と parametrize された 2 ケースの計 3 件が失敗します。
  • ノートブックの出力(2026-05-10)には root 経由の重複行が残っています。伝播を止める変更が後から入り、テストを壊した可能性が高いと考えられます。CI がこれを検出していないなら、ゲートが実際には実行されていないか、結果が見落とされています。

修正方針

  • ライブラリは logging.getLogger(__name__) を使うだけにし、ハンドラ、伝播、レベルには一切触れません。
  • ログの構成はアプリケーションの入口で 1 回だけ、冪等に行います。
  • 「ライブラリのログが caplog に届くこと」を回帰テストで固定します。

新テンプレートでの対応

  • src/template_python/logging_config.py:
    • configure_logging() は自分の名前付きハンドラだけを置き換え、caplog などのハンドラには触れません。
    • JSON Lines 出力に対応しています。
  • tests/test_logging_config.py::test_library_records_reach_caplog(回帰テスト)。

A-2 (P1) テストが環境変数を汚染し、CWD に依存する

現状

test_settings.py は load_dotenv(dotenv_path=".env.template") を呼びます。これはプロセスの os.environ を書き換えるので、以降の全テストに PROJECT_NAME などが残ります。さらに、相対パスのため pytest を別ディレクトリから実行すると失敗します。アサーションの文言「Default project name should be 'template-python'」も誤りで、実際の既定値は default-project です。

問題

  • テストの順序に依存する、再現しにくい不具合の温床になります。
  • 開発者の .env や環境変数がテスト結果を変えてしまいます(非ハーメチック)。

修正方針・新テンプレート

  • tests/conftest.py の autouse fixture で、TEMPLATE_PYTHON_* を削除し、tmp_path へ chdir し、テスト後に自前のハンドラを除去します。
  • 設定のテストは monkeypatch.setenv と tmp_path / ".env" で行います。
  • 優先順位(環境変数 > .env)、正規化、不正値、秘匿、不変性を個別に検証します(tests/test_config.py、7 件)。

A-3 (P1) CLI が無テストで、カバレッジ対象外

現状

--cov=template_python だけが計測対象で、scripts/template.py は計測もテストもされていません。そのため F-3(--verbose の不具合)や F-7(出力が無い)を検出できません。

修正方針・新テンプレート

CLI をパッケージ内(cli.py)に移し、typer.testing.CliRunner で E2E テストします。

  • stdout/stderr の分離、--version、--verbose、不正入力時の終了コード 2、不正設定時の 78、--help が設定エラーの影響を受けないこと、秘匿表示を確認します。
  • python -m template_python をサブプロセスで実行します。

A-4 (P2) 弱いアサーションと無意味な parametrize

hello_world() は値を返さず、5 レベルのログを出すだけです。テストは「Hello World という文字列がどこかにあるか」しか見ておらず、DEBUG/INFO の parametrize も結果を区別しません。未使用の setup_session fixture はデッドコードです。

新テンプレートでは、ドメインを純粋関数 build_greeting() にして入出力を検証します。不変条件(前後空白の除去、長さ制限)は Hypothesis のプロパティテストで確認します。

A-5 (P2) カバレッジ閾値なし・ブランチカバレッジ無効

[tool.coverage.report] は show_missing だけで、閾値もブランチ計測もありません。CI はカバレッジを報告も保存もしません。

新テンプレートの設定は次のとおりです。

  • branch = true、fail_under = 90、exclude_also を設定します。
  • 基準プラットフォーム(Ubuntu、3.14)でのみ計測し、Markdown 表を Job Summary に出力します。
  • coverage.xml と junit.xml を artifact として保存します。

A-6 (P2) pytest 設定の不備

現状 問題 新テンプレート
[tool.pytest.ini_options] pytest 9 のネイティブ TOML ではない [tool.pytest](pytest ≥ 9)
strict 系オプションなし マーカーの typo や設定ミスが黙殺される strict = true(strict_config/markers/parametrization_ids/xfail。pytest はロックで固定)
filterwarnings 未設定 非推奨警告が蓄積する filterwarnings = ["error"]
addopts = "--cov=..." 単体実行やデバッグが常に遅くなる カバレッジは make cov と CI だけで計測
log_cli = true + --capture=no -vv ログが二重・大量に出てノイズになる 既定の捕捉を使い、失敗時だけ表示
pythonpath = ['.'] インストール状態ではなくソースツリーをテストし、パッケージングの欠陥を隠す src layout + --import-mode=importlib

A-7 (P2) 型チェッカー 3 種の併用

現状

make lint は mypy strict、ty、pyrefly をすべて実行します。

問題

  • 推論や診断の差異により、同じコードに 3 種の抑制コメント(# type: ignore、# ty: ignore、# pyrefly: ignore)が必要になります。
  • 実行時間と更新コストが 3 倍になります。
  • ty は 0.0.x(beta、診断の破壊的変更あり)で、ブロッキングゲートには不適です。

新テンプレート

  • mypy 2.x strict + pydantic プラグインを唯一のブロッキングゲートにしました(ADR-0002)。
  • エディタも同じ mypy(VS Code の mypy 拡張)を使い、Pylance の型検査はオフにして診断を一元化します。

A-8 (P2) Ruff のルール選択が最小限

現状

select = ["E","F","I","UP"] だけで、実際に以下を見逃しています。

  • ロギングの f-string(G004)。
  • subprocess、assert、秘密情報(S)。
  • バグの温床(B)と pytest スタイル(PT)。

ignore = ["D203"] は D を選択していないため意味がありません。

新テンプレート

  • 34 のルール群(A、ASYNC、B、BLE、C4、D(google)、DTZ、E、EM、F、FURB、G、I、ISC、LOG、N、PERF、PGH、PIE、PL、PT、PTH、RET、RSE、RUF、S、SIM、SLF、T10、T20、TID、TRY、UP、W)を明示的に選択しました。
  • Ruff 0.16 で既定ルールが 59 から 413 に、0.17 で既定のターゲットが変わったため、明示選択でアップグレード時の揺れを防ぎます。

A-9 (P3) アーキテクチャ契約が弱い

現状

scripts(__init__.py の無い名前空間)→ template_python の 1 契約だけです。ライブラリ内部の層と外部フレームワークへの依存は無制約です。

新テンプレート

  • layers 契約: cli → (config | logging_config) → domain。exhaustive = true で新規モジュールの層への分類を強制します。
  • forbidden 契約: domain から typer、pydantic、pydantic_settings を禁止します(include_external_packages = true)。

4.B CI 設計

B-1 (P1) 二重起動・並行制御なし・マージキュー非対応

現状

test.yaml と docker.yaml は push(main / feature/** / copilot/**)と pull_request(main)の両方で起動します。

問題

  • PR ブランチでは同じコミットに対して CI が 2 回走ります。
  • concurrency が無いため、追加 push 時に古い実行がキャンセルされません。
  • merge_group トリガーが無いので、マージキューを有効にすると必須チェックが永久に Pending になります。

新テンプレート

ci.yaml のトリガーは次のとおりです。

  • pull_request(全ブランチ宛)
  • push(main)
  • merge_group
  • workflow_dispatch
  • workflow_call(リリースから再利用)

concurrency は PR の場合だけ進行中の実行をキャンセルします。

B-2 (P1) 必須チェックの集約ジョブが無い

行列ジョブ名(test (3.13) など)を必須チェックにすると、行列を変えるたびにブランチ保護が壊れます。新テンプレートは ci-success ジョブが全ジョブの結果を検査します(skipped は許容し、failure / cancelled は失敗)。ルールセットはこの 1 つだけを必須にします。

B-3 (P2) Python の二重セットアップ

setup-uv に python-version を渡したうえで actions/setup-python も実行しています(test / azure-static-web-app / github-pages)。新テンプレートは setup-uv だけを使い、Python は .python-version または行列値から uv が取得します。

B-4 (P2) 直列の巨大ジョブと重複実行

現状

make ci-test は format → mypy → import-linter → ruff → ty → pyrefly → actionlint → zizmor → pytest を 1 ジョブで直列に実行します。docs は test.yaml と github-pages.yaml で 2 回ビルドされ、ruff は hooks ジョブでも再実行されます。

新テンプレート

lint / workflows / test(行列)/ docs / package / container を並列ジョブに分けました。ステップ名で失敗箇所が分かり、Ruff は --output-format=github で PR に注釈を付けます。

B-5 (P2) 配布物を一度もビルドしない

wheel/sdist の破損(パッケージデータの欠落、エントリポイントの誤り)は利用者側で初めて発覚します。新テンプレートは package ジョブで uv build --no-sources を実行し、uv run --isolated --no-project --with dist/*.whl template-python --version でインストール済みの状態をスモークテストします。

B-6 (P2) Python 行列が時代遅れ

現状

3.10〜3.14 を対象にしています。3.10 は 2026-10-01 に EOL で、3.15.0 は 2026-10-09 に GA です。Windows/macOS の検証もありません。

新テンプレート

  • Ubuntu で 3.12 / 3.13 / 3.14 / 3.15 をブロッキングで実行します(3.15 は pydantic 2.14 が対応済み)。
  • Windows 2025 と macOS 26 で 3.14 を実行します。
  • フリースレッドの 3.14t は早期警告用で、非ブロッキングです。
  • テスト行列は --no-default-groups --group test で最小限の依存だけを入れ、新しい Python で開発ツールのホイール欠如に巻き込まれないようにしています。

B-7 (P2) ubuntu-latest の暗黙移行

GitHub は 2026-10-19〜11-19 に ubuntu-latest を 26.04 へ段階移行します。新テンプレートは ubuntu-24.04 / ubuntu-24.04-arm / windows-2025 / macos-26 を明示し、移行は計画的に行います。

B-8 (P2) uv バージョンが 6 か所に散在

devcontainer、ワークフロー 4 本、Dockerfile、README に 0.12.19 が書かれています。新テンプレートは pyproject.toml の [tool.uv] required-version = ">=0.13.0,<0.14" を唯一の定義とし、setup-uv はこれを自動で読みます。Dockerfile と devcontainer の uv イメージタグは Dependabot(docker)の更新対象です。

B-9 (P2) actionlint の取得と検証

現状

gh release download で取得したバイナリを、同じリリースの checksums.txt で照合しています。リリース自体が改ざんされた場合は検出できません。ローカルでは別途インストールが必要で、CI とバージョンがずれます。

新テンプレート

actionlint-py(1.7.12.x)を lint 依存グループに加え、uv.lock のハッシュで固定しました。ローカル(make workflows、pre-commit)と CI で同じバイナリを使います。

B-10 (P3) timeout と権限の粒度

github-pages.yaml にタイムアウトがありません。権限はワークフローレベルだけです。新テンプレートは全ジョブに timeout-minutes と permissions を明示し、ワークフローレベルは permissions: {} にしました。

4.C サプライチェーン・セキュリティ

C-1 (P0) ハッシュ検証を捨てたコンテナビルド

現状

RUN uv export --locked --format requirements-txt --no-dev --no-hashes ... --output-file requirements.txt
...
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

問題

  • uv.lock が持つ配布物ハッシュを --no-hashes で捨てています。インデックスやミラーの改ざん、依存関係混乱(dependency confusion)を検出できません。
  • --upgrade の意図も不明確です。

新テンプレート

uv 公式 Docker ガイドの方式に従います。

  • uv sync --locked --no-install-project --no-editable で依存だけの層をキャッシュし、続けてプロジェクトを非 editable でインストールします。uv はロックのハッシュを検証します。
  • UV_COMPILE_BYTECODE=1、UV_LINK_MODE=copy、UV_NO_DEV=1、UV_PYTHON_DOWNLOADS=0 を設定します。
  • BuildKit のキャッシュマウントとバインドマウントを使います。

C-2 (P1) zizmor がオフラインかつ high 以上のみ

現状

zizmor --offline --strict-collection --min-severity high . で実行しています。

問題

  • オフラインでは impostor-commit(フォーク由来の偽 SHA)、ref-confusion、known-vulnerable-actions が無効です。
  • medium 以下の artipacked(認証情報の残留)、excessive-permissions、cache-poisoning などを全て見逃します。実際に github-pages.yaml は contents: write のトークンを checkout に残しています(C-9)。

新テンプレート

  • CI では GH_TOKEN を渡してオンライン監査を有効にし、--format=github --min-severity=low で実行します。
  • .github/zizmor.yml で全 uses: にハッシュピンを要求します。
  • pre-commit でもオフライン版を実行します。
  • 全ワークフローをこの基準に合わせて設計しました(run: 内での ${{ }} 展開なし、リリース系ジョブのキャッシュ無効化など)。

C-3 (P1) Trivy が非ブロッキング

現状

aquasec/trivy:0.74.0 image ... は --exit-code が無いため、常に成功します。README は「review its findings before enabling a blocking severity threshold」と自ら認めており、アーキテクチャ文書の原則 8「Surface failures」と矛盾します。

問題

  • 修正版があるのに放置された HIGH/CRITICAL の脆弱性を含むイメージを公開できてしまいます。
  • Trivy は 2026-03-19 に、公式リリース v0.69.4、trivy-action の 76 タグ、setup-trivy の全タグが改ざんされるサプライチェーン侵害を受けました(GHSA-69fq-xp46-6x23 / CVE-2026-33634)。2026-03-22 には Docker Hub の 0.69.5 / 0.69.6 も改ざんされています。スキャナ自身の取得経路を固定する設計が必須です。

新テンプレート

  • aquasecurity/trivy-action をコミット SHA で固定し(v0.36.0、侵害後のリリース)、スキャナ本体も version: v0.75.0 で明示しました。
  • ignore-unfixed: true、severity: CRITICAL,HIGH、exit-code: "1" で、修正版がある重大脆弱性をブロックします。
  • hadolint はバイナリを sha256 照合したうえで実行します。

C-4 (P1) 標準的なセキュリティ機能の欠落

機能 現行 新テンプレート
CodeQL(Python + Actions) 無し default setup を設定スクリプトで有効化。高度なセットアップ codeql.yaml は CODEQL_ADVANCED=true のときだけ実行(両者は併用不可)
シークレットスキャン + プッシュ保護 無し 設定スクリプトで有効化(private リポジトリは GitHub Secret Protection が必要)
依存関係レビュー 無し security.yaml(PR、fail-on-severity: moderate。private は CODE_SECURITY=true)
依存脆弱性監査 無し uv export --locked --all-groups → pip-audit --require-hashes --disable-pip(PR、push、週次スケジュール)
Dependabot アラート / セキュリティ更新 未設定 設定スクリプトで有効化(uv は 2025-12-16 からセキュリティ更新に対応)
OpenSSF Scorecard 無し scorecard.yaml(public のみ)
Private vulnerability reporting 無し 設定スクリプトで有効化し、SECURITY.md で案内

C-5 (P1) 来歴証明・SBOM・イミュータブルリリースが無い

問題

利用者は「このイメージやホイールが、このリポジトリのこのワークフローでビルドされた」ことを検証できません。リリース後のタグ付け替えや資産の差し替えも可能です。

新テンプレート

  • wheel/sdist とイメージに actions/attest v4 で SLSA build provenance を付与します。attest-build-provenance は v4 から actions/attest のラッパーになり、attest-sbom は非推奨です。
  • イメージには BuildKit の provenance: mode=max と sbom: true も付けます(private リポジトリでは既定が min なので明示)。
  • GitHub Release を gh release create で作成します(draft → 資産アップロード → 公開)。イミュータブルリリース(2025-10-28 GA)と互換で、設定スクリプトで有効化します。
  • 検証手順(gh attestation verify、gh release verify / verify-asset)を SECURITY.md に記載しました。
  • Free/Pro/Team の private リポジトリでは Attestations を使えないため、ATTESTATIONS 変数でガードし、失敗させずに運用できるようにしています。

C-6 (P1) 長期資格情報の広範な露出

現状

DOCKERHUB_TOKEN(PAT)と AZURE_STATIC_WEB_APPS_API_TOKEN(長期デプロイトークン)をリポジトリシークレットとして保持し、Environment による保護(承認、タグ限定)がありません。同一リポジトリの任意ブランチのワークフローから参照できます。

新テンプレート

  • GHCR へは GITHUB_TOKEN で push します(長期資格情報なし)。
  • Docker Hub は任意のミラーで、dockerhub Environment に限定したシークレットを使い、同一ダイジェストをコピーするだけです。
  • Azure SWA は azure/login の OIDC で一時的にログインし、実行時にデプロイトークンを取得します。GitHub には保存しません。
  • PyPI は Trusted Publishing(OIDC、pypi Environment は v* タグのみ)で、PEP 740 attestations が自動付与されます。

C-7 (P2) Dependabot の設定不足

現状

グループ化が無く、毎週エコシステムごとに個別 PR が大量に作られます。cooldown は uv だけで、Actions、Docker、pre-commit は公開直後の版を即座に提案します。devcontainers は対象外です。

新テンプレート

  • uv、github-actions、docker(/ と /.devcontainer)、devcontainers、pre-commit(2026-03-10 対応)の全エコシステムに cooldown.default-days: 7 を設定しました。
  • グループ化、ラベル、Conventional Commits 風の接頭辞を付けます。
  • uv は groups.dependency-type 非対応のため、update-types でグループ化します。
  • zizmor の dependabot-cooldown 監査にも適合します。

C-8 / C-9 (P2) 過剰権限と認証情報の残留

ghcr-release.yaml はワークフロー全体に packages: write を付与し、github-pages.yaml は contents: write で、checkout の persist-credentials も既定(true)のままです。新テンプレートはワークフローレベルを permissions: {} とし、ジョブ単位で最小権限を付与し、全 checkout で persist-credentials: false にしています。リポジトリ設定でも GITHUB_TOKEN の既定を read にします。

C-10 (P2) 第三者のアクセス解析 ID がテンプレートに残っている

mkdocs.yml の extra.analytics.property: G-0ZTKFXVQ5K は原作者の Google Analytics です。テンプレートから生成したすべてのドキュメントサイトの閲覧データが第三者に送られます。Cookie 同意やプライバシーポリシー上も問題です。新テンプレートには解析設定を含めません。

C-11 (P2) 設定全量のログ出力と秘匿化の欠如

logger.info(f"Settings from .env: {get_project_settings().model_dump_json(indent=2)}") は、将来 API キーなどを設定に追加した瞬間に秘密をログへ出力します。新テンプレートは秘密を SecretStr で受け、config コマンドは秘匿済み JSON だけを出力します(********** をテストで保証)。Typer は 0.23 からトレースバックにローカル変数を表示しないのが既定になったため、依存の下限を typer>=0.27 にしてこの挙動を担保しています。

4.D リリース / CD

D-1 (P0) プレリリースが安定版タグと latest を上書きする

現状

// docker-release.yaml / ghcr-release.yaml(Set Versions ステップ)
const tag = context.ref.substring(10)              // "v1.2.3-rc.1"
const no_v = tag.replace('v', '')                  // "1.2.3-rc.1"
const dash_index = no_v.lastIndexOf('-')
const no_dash = (dash_index > -1) ? no_v.substring(0, dash_index) : no_v   // "1.2.3"
tags: |
  ${{ secrets.DOCKERHUB_USERNAME }}/template-python:${{steps.set_version.outputs.no-dash}}  # :1.2.3
  ${{ secrets.DOCKERHUB_USERNAME }}/template-python:latest                                 # :latest

問題

RC/beta を試験公開しただけで、安定版 1.2.3 と latest が RC の中身に置き換わります。GHCR 側も type=semver,value=v<no-dash> と type=raw,value=latest を使っているので同じです。

新テンプレート

  • バージョンを PEP 440(例: v1.3.0rc1)で統一します。
  • docker/metadata-action を type=pep440,pattern={{version}} / {{major}}.{{minor}} と flavor: latest=auto で使います。プレリリースは {{version}} だけを生成し、latest を付けません。
  • type=ref,event=tag(プレリリースにも latest を付ける)は使いません。
  • GitHub Release もプレリリースとして作成します。

D-2 (P1) テストを経ずに公開する

タグ push を契機にした公開ワークフローに needs もゲートもありません。アーキテクチャ文書自身が「release workflows are delivery mechanisms, not substitutes for the test workflow」と書きながら、両者がつながっていません。新テンプレートの release.yaml は、verify(タグ = uv version --short)→ ci(ci.yaml を workflow_call で再利用)の成功後にだけビルドと公開を行います。

D-3 (P1) レジストリごとに別ビルドしている

問題

Docker Hub 向けと GHCR 向けを別ワークフローでビルドしています。タイムスタンプやベースイメージの更新により、同じ 1.2.3 でもダイジェストが異なるイメージが 2 つ存在することになります。来歴の検証やインシデント時の追跡ができません。

新テンプレート

  1. amd64/arm64 をネイティブランナーでビルドし、ダイジェストだけで push します。
  2. docker buildx imagetools create でマルチアーキのインデックスとタグを作成します。
  3. インデックスのダイジェストに attestation を付与します。
  4. Docker Hub は同じダイジェストをコピーするだけです(任意)。

D-4 (P1) バージョンの乖離と未使用のビルド引数

pyproject.toml は version = "0.0.1" のままですが、Git タグは v0.0.13 です。Dockerfile は ARG GIT_REVISION / GIT_TAG を宣言しているのに一切使っていません(ラベルにも環境変数にもならない)。新テンプレートは uv version --bump で版を管理し、タグとの一致をリリース時に強制します。OCI ラベル(source、revision、version、created)は metadata-action で付与します。

D-5 (P1) GitHub Release とリリースノートが無い

コンテナを push するだけで、変更点・成果物・検証手段が利用者に提供されません。新テンプレートは gh release create <tag> dist/* --verify-tag --generate-notes を実行し、イメージのダイジェストをノートに追記します。.github/release.yml のカテゴリ(Breaking / Features / Bug fixes / Security / Docs / Dependencies)で自動生成ノートを整理し、設定スクリプトがラベルを作成します。

D-6 (P2) エミュレーション前提のマルチアーキテクチャ

platforms: linux/amd64,linux/arm64 を指定していますが、setup-qemu-action がありません。GitHub ホストランナーの arm64 は 2026-01-29 から private リポジトリでも標準ランナーとして利用でき、エミュレーションより高速です。新テンプレートは ubuntu-24.04-arm でネイティブにビルドします。

D-7 (P2) 旧方式の Pages デプロイ

mkdocs gh-deploy --force は gh-pages ブランチへ force push する旧方式で、contents: write が必要です。並行制御も環境保護もありません。新テンプレートは actions/configure-pages → upload-pages-artifact → deploy-pages と github-pages 環境を使い、concurrency: pages を設定しています。Pages が有効なとき(has_pages)だけ実行するので、新規リポジトリで赤くなりません。

D-8 / D-9 (P2/P3) SWA ワークフローの不備と非 strict ビルド

単一値の matrix、Python の二重セットアップ、長期トークン(C-6)、strict でない docs ビルドがあります。新テンプレートは任意実行の docs-azure-swa.yaml(OIDC)と zensical build --strict に置き換えました。

4.E コンテナ

E-1 (P1) root 実行

USER 命令が無く、コンテナは root で動作します。新テンプレートでは次のようにしました。

  • 数値 UID/GID 10001 の専用ユーザーで実行します(hadolint DL3066 にも適合)。
  • venv は root 所有で、アプリから書き換えられません。
  • compose では read_only、cap_drop: [ALL]、no-new-privileges を既定にしています。

E-2 (P1) コンテキスト全体の同梱と非インストール実行

COPY . . で docs、notebooks、Makefile、scripts などを同梱し、ソースツリーを作業ディレクトリから直接 import して実行しています。新テンプレートは builder ステージでプロジェクトを非 editable の wheel として venv にインストールし、ランタイムには /app/.venv だけをコピーします(ソースもツールも入りません)。

E-3 (P2) 流用された .dockerignore

  • node_modules、bower_components、*.test.js など JavaScript 向けのパターンを含みます。
  • .DS_Store # macOS-specific はインラインコメントが使えないため無効なパターンです。
  • docker-compose.yml を除外していますが、実ファイルは compose.yml です。

新テンプレートは * で全除外し、必要なもの(pyproject.toml、uv.lock、README.md、LICENSE、src)だけを許可する方式にしました。Dockerfile 先頭の # check=error=true により、CopyIgnoredFile などの BuildKit ビルドチェック違反はビルドエラーになります。

E-4 (P2) 旧ベースイメージ

Debian 12 bookworm は 2026-07-11 に通常サポートを終え、LTS のみになっています。新テンプレートは python:3.14-slim-trixie(Debian 13)を使います。python:3.15-slim-trixie は 2026-10-12 時点で RC タグしか公開されていないため、コンテナの既定は 3.14 にしました。

ダイジェストの固定は推奨ですが、本成果物の作成環境では公開ダイジェストを検証できなかったため、タグで固定し Dependabot(docker)で更新する構成にしています。導入時に docker buildx imagetools inspect でダイジェストを付与する手順を 6 章に記載しました。

E-5 (P2) 危険な compose の例

command: ["python", "-m", "http.server", "8000"] は、/app(ソースコード)を 0.0.0.0:8000 で配信します。env_file: .env は必須扱いなので、.env の無い新規クローンでは起動に失敗します。container_name を固定しているためスケールもできません。新テンプレートの compose.yaml は CLI を実行するだけの最小例で、env_file は required: false です。

E-6 / E-7 (P3) OCI ラベルの欠如と GIT_TAG のバグ

org.opencontainers.image.source が無いと、GHCR のパッケージとリポジトリが自動で紐付きません。Makefile の sed -e s/v//g は v1.0.0-dev を 1.0.0-de に壊します。いずれも新テンプレートで解消しました。

4.F Python パッケージとアプリ設計

F-1 (P1) ライブラリがログ構成を所有している

get_logger() は呼び出しのたびにレベルを上書きし、StreamHandler を追加し、伝播を止めます。ライブラリとして組み込まれた場合、アプリケーション側の logging.config.dictConfig やログ基盤への転送(OpenTelemetry など)が効かず、出力の重複や欠落も起きます。Python 公式の Logging HOWTO が推奨するとおり、ライブラリはハンドラを構成しないことが原則です。

新テンプレートでは次のように分担します。

  • ライブラリ: logging.getLogger(__name__) だけを使います。
  • アプリ: cli.py から configure_logging(level, json_format=...) を 1 回呼びます。
    • テキストまたは JSON Lines で、出力先は stderr です。
    • extra= のフィールドと例外も JSON 化します。
    • 何度呼んでも安全です。

F-2 (P1) import 時の副作用

各モジュールが import 時に get_logger(__name__) を通じて get_project_settings() を呼び、lru_cache に保存します。設定は最初に import された時点の環境で固定されます。後から行う load_dotenv() や環境変数の変更は反映されず、テストの実行順にも依存します。

新テンプレートでは、Settings() は CLI のコマンド実行時(_runtime())にだけ生成されます。--help は不正な設定の影響を受けません。

F-3 (P1) --verbose が機能しない

コールバックは logging.basicConfig(level=DEBUG) と、__main__ ロガーのレベル変更しか行いません。template_python.core のロガーは import 時に INFO に固定されているため、ライブラリの DEBUG ログは出ません。さらに basicConfig が root にハンドラを追加するため、出力が二重になる状態も生じます(ノートブックの出力で両方を確認できます)。

新テンプレートでは、--verbose が root を DEBUG にします。test_verbose_flag_enables_debug_logs_on_stderr で保証しています。

F-4 (P1) .env が実環境変数を上書きする

load_dotenv(override=True) は、コンテナや Kubernetes の Secret などで実際に設定された環境変数を .env の値で上書きします。12-factor の優先順位が逆転し、作業ディレクトリに残った .env が本番設定を乗っ取る事故につながります。そもそも pydantic-settings が .env を読むので、python-dotenv の直接利用は不要です。

新テンプレートでは pydantic-settings の既定(環境変数 > .env)だけを使い、テスト(test_environment_variables_override_dotenv)で保証しています。

F-5 (P2) CLI を配布・インストールできない

flat layout で scripts/ はパッケージに含まれず、python scripts/template.py で起動するしかありません。新テンプレートは src layout にし、[project.scripts] template-python = "template_python.cli:app"、__main__.py(python -m template_python)、py.typed を備えます。

F-6 (P2) 弱い設定モデル

現状

  • project_log_level: str は任意の文字列を受け入れ、不正値は logging の実行時例外になります。
  • 接頭辞が無く、PROJECT_NAME のような汎用名は他ツールと衝突しやすいです。
  • 秘密情報の型(SecretStr)がありません。
  • python-dotenv を直接依存に持っています。

新テンプレート

  • Literal 型(LOG_LEVEL / LOG_FORMAT / ENVIRONMENT)に大小文字の正規化を組み合わせました。
  • TEMPLATE_PYTHON_ 接頭辞を付けます(初期化スクリプトで新名称に置換されます)。
  • frozen=True、SecretStr、min_length を設定しました。

F-7 (P2) 出力と終了コードの設計

hello --name は name を使わず、stdout に何も出しません(ログだけ)。そのため、シェルスクリプトから結果を受け取れず、テストもできません。

新テンプレートでは次のように整理しました。

  • 結果は stdout(typer.echo)、診断は stderr(ログ)に出します。
  • 入力エラーは typer.BadParameter(終了コード 2)、設定エラーは 78(sysexits の EX_CONFIG)にします。
  • ドメインの例外を CLI 層で変換します。

F-8〜F-11 (P2/P3) その他

  • F-8: f-string のログは、レベルで捨てられる場合でも文字列を生成します。構造化もできません。新テンプレートは % 形式の引数と JSON フォーマッタを使います。
  • F-9: build-system.requires = ["hatchling"] は無制約で、ビルドの再現性がありません。新テンプレートは uv_build>=0.13.0,<0.14 と PEP 639 の license = "MIT" / license-files を使い、classifiers と urls を追加しました。
  • F-10: requires-python >=3.10 は EOL の版を含みます。新テンプレートは >=3.12 で、PEP 695 構文や typing.override を使えます。既定は 3.14 です。
  • F-11: Docker の CMD ["python", "-m", "template_python.core"] はデモ用の __main__ ブロックです。新テンプレートは ENTRYPOINT ["template-python"] と CMD ["--help"] にしました。

4.G 開発者体験

G-1 (P1) devcontainer の dev- タグ

mcr.microsoft.com/devcontainers/python:dev-3.13-bookworm の dev- は、devcontainers/images の公式説明では main ブランチから生成される実験的な開発イメージで、インプレース更新されます。チームメンバーごとに環境が変わる恐れがあります。uv も毎回 pipx install しています。

新テンプレートの構成は次のとおりです。

  • .devcontainer/Dockerfile: mcr.microsoft.com/devcontainers/python:3-3.14-trixie に、CI・本番と同じ ghcr.io/astral-sh/uv:0.13.0 から uv をコピーします。
  • features: github-cli と docker-in-docker(コンテナ関連の make ターゲット用)を入れます。
  • hostRequirements を指定します。
  • updateContentCommand で依存を入れます(Codespaces の prebuild 対象)。
  • postCreateCommand で git フックを入れます。

G-2 (P1) テンプレート固有名の散在と初期化手段の欠如

ks6088ts(Makefile の DOCKER_REPO_NAME、mkdocs、README のバッジ、LICENSE)と template-python / template_python が各所に埋め込まれており、置換手順もツールもありません。

新テンプレートでは 2 つの手段を用意しました。

  • scripts/init_project.py(標準ライブラリのみ、PEP 723 メタデータ付き)が、配布名、パッケージ名、環境変数の接頭辞、オーナー、説明を一括置換し、パッケージディレクトリを改名し、uv.lock を削除します。名前の検証(キーワードや標準ライブラリとの衝突)とドライランも備え、テストがあります。
  • initialize.yaml が、初回 push 時の uv.lock 生成と、任意の改名(workflow_dispatch の入力)を GitHub 上で自動化します。

G-3 (P2) docs 用の同期が開発環境を壊す

make install-deps-docs は uv sync --locked --no-dev --group docs です。uv sync は exact モードのため、dev グループ(prek を含む)を venv から削除します。その結果、インストール済みの git フックが実行ファイルを見失い、コミットが失敗します。新テンプレートは docs を uv run --only-group docs(inexact)で実行し、CI では専用ジョブに分けています。

G-4 (P2) フックと CI のバージョンのドリフト

ruff-pre-commit の rev: v0.16.9 と uv.lock の ruff は別々に更新されます。uv.lock の整合チェックはなく、exclude: "generated/.*|artifacts/.*" は存在しないディレクトリを指しています。

新テンプレートでは次のようにしました。

  • 汎用フック(pre-commit-hooks。prek がネイティブ実行)はそのまま使います。
  • ruff、actionlint、zizmor は language: system + uv run --frozen で uv.lock の版を使います。
  • uv lock --check フックを追加しました。

G-5 (P2) エディタ設定の不一致

.vscode/extensions.json は pyrefly を、devcontainer は ruff と Copilot Chat を推奨しており、一致していません。Pylance と pyrefly の併用で診断も重複します。新テンプレートは両者を統一し(Ruff、mypy、Python、Copilot Chat、GitHub Actions、TOML)、保存時の整形と import 整理を設定しました。

G-6 (P3) ノートブックの実行結果をコミットしている

ANSI エスケープ、実行日時、相対パス(../scripts/template.py)に依存した出力がコミットされており、テストもされていません。新テンプレートでは削除しました。必要な場合は nbstripout 系のフックと一緒に追加してください。

4.H ドキュメント

H-1 (P1) ドキュメント基盤の保守終了

2026-10 時点の状況

項目 状況
MkDocs 1.x 18 か月以上リリースがなく、外部コントリビューションも停止
MkDocs 2.0 プレリリース段階。プラグイン機構が無く、設定は TOML で、移行パスも無い
Material for MkDocs 保守モード。致命的な不具合とセキュリティの修正のみで、2027-05-05 まで
mkdocs-static-i18n 凍結

新テンプレート

  • Material チームの後継である Zensical を zensical.toml で採用しました(ADR-0003)。
    • mkdocs.yml 互換で、--strict を備えます。
    • 0.0.69 は alpha です。2026-11-05 から 0.1.0 系が安定版の位置付けになる予定で、バージョンは uv.lock で固定されます。
  • Zensical はまだ i18n に対応していないため、英語(docs/)と日本語(docs/ja/)を 1 サイトの別ナビゲーションとして公開します。
  • アーキテクチャ判断は ADR(docs/adr/)として記録します。

H-2〜H-4 (P2/P3)

現状の問題

  • README には「Git hooks and deployment workflows are unchanged, and this does not add a pre-deployment gate.」のような PR 説明文の残骸があります。
  • import-linter の説明が README、docs/index、architecture(英日)の計 4 か所に重複しています。
  • 「structured logging」と書かれていますが、実装はテキストです。
  • 原則「Surface failures」に反してスキャンが非ブロッキングです。
  • site_url(ks6088ts-labs)と repo_url(ks6088ts)のオーナーが不一致です。

新テンプレート

ページの責務を分けました(Getting started / Development / Architecture / CI/CD / Security / Releasing / AI / Repository settings)。記述は実装と一致させ、URL はプレースホルダー(your-github-org)にして初期化スクリプトで置換します。

4.I GitHub ガバナンス

I-1 (P1) ルールセット・必須チェック・タグ保護が無い

GitHub のテンプレートリポジトリはファイルしか複製しないため、ブランチ保護などの設定はテンプレートに含められません。現行テンプレートには設定を再現する手段がなく、ゲートを迂回して main へ直接 push したり、任意のユーザーがリリースタグを作成したりできます。

新テンプレートは次の 3 つのルールセット JSON(UI からのインポートにも対応)を同梱し、scripts/configure_repository.py --apply で作成または更新します。

ルールセット 内容
default-branch.json PR 必須(承認 1、CODEOWNERS レビュー、最終 push 後の承認、スレッドの解決、stale レビューの却下)、ci-success 必須(strict)、線形履歴、force push・削除の禁止、squash のみ
release-tags.json refs/tags/v* の作成・更新・削除を admin のみに制限
copilot-code-review.json(任意) Copilot コードレビューを自動で依頼

I-2 (P1) リポジトリ設定のコード化

configure_repository.py は既定でドライランです。各手順は独立しており、プランや可視性の制約で失敗した手順は警告として扱います。テスト付きで、主に次を設定します。

  • Actions の既定トークンを read にし、Actions による PR 承認を禁止し、SHA ピン必須ポリシー(sha_pinning_required、2025-08 導入)を有効にします。
  • squash のみでマージし、auto-merge を許可し、マージ後にブランチを削除します。
  • Dependabot アラートとセキュリティ更新、シークレットスキャンとプッシュ保護、PVR、CodeQL default setup(extended)を有効にします。
  • イミュータブルリリースを有効にします。
  • Pages(Actions ソース)を有効にします。
  • pypi Environment を作成し、v* タグからのみデプロイできるようにします。
  • リリースノート用のラベルを作成します。

I-3 / I-4 (P2/P3) コミュニティ健全性ファイル

新テンプレートには次を同梱しました: SECURITY.md(非公開報告と検証手順)、CONTRIBUTING.md、CODE_OF_CONDUCT.md(Contributor Covenant 2.1 への参照)、Issue Forms(バグ報告と機能要望、ブランク Issue の無効化、セキュリティ報告への誘導)、PR テンプレート、CODEOWNERS。LICENSE はプレースホルダー化し、初期化スクリプトで置換します。

4.J AI 駆動開発(GitHub Copilot)

J-1 (P1) Copilot 用の構成が無い

test.yaml は copilot/** ブランチを CI 対象にしており、Copilot cloud agent(旧 coding agent。2026-04 に名称変更)の利用を想定しています。にもかかわらず、エージェント向けの指示も実行環境の定義もありません。エージェントは毎回、uv の導入方法や品質ゲートを推測することになり、規約違反の PR や CI の失敗を繰り返します。

新テンプレート(2026-10 時点の仕様に準拠)の構成は次のとおりです。

ファイル 役割
AGENTS.md エージェント共通の操作ガイド(cloud agent、コードレビュー、CLI、VS Code が読む)。プロジェクトマップ、コマンド、規約、禁止事項を記載
.github/copilot-instructions.md リポジトリ全体の指示(レビュー観点の優先順位)
.github/instructions/*.instructions.md パス別の指示(Python、テスト、Actions、ドキュメント)。applyTo はカンマ区切りの文字列
.github/workflows/copilot-setup-steps.yml cloud agent の実行環境。ジョブ名は copilot-setup-steps、uv sync --locked --all-groups
.vscode/mcp.json GitHub リモート MCP サーバー(VS Code)

エージェント用のシークレットは「Settings → Secrets and variables → Agents」で管理します(2026-05-08 に copilot 環境から移行)。

J-2 (P2) ガードレールと再利用可能な手順

種別 新テンプレート
フック(CLI と cloud agent) .github/hooks/policy.json + guard_tool_use.py。preToolUse で force push、--no-verify、curl | sh、rm -rf / 系、uv.lock の直接編集を拒否する。クラッシュすると全ツールが拒否される仕様のため、例外を握りつぶして常に終了コード 0 で返す。テスト 13 件
カスタムエージェント test-writer(テスト専任)、security-reviewer(読み取り専用ツールのみ)
スキル run-quality-gates、code-review(コードレビューが参照)、add-cli-command、cut-release、update-dependencies

プロンプトファイル(.prompt.md)は VS Code で非推奨になったため、スキルで実装しています。

J-3 (P3) コードレビューの自動依頼

copilot_code_review ルール(review_on_push)を任意のルールセットとして同梱しました。設定スクリプトの --copilot-review オプションで適用できます。

GitHub Agentic Workflows(gh-aw、2026-06 からパブリックプレビュー)は、コンパイル済みの .lock.yml のコミットが必須です。そのため本テンプレートには同梱せず、導入手順(gh aw add githubnext/agentics/ci-doctor)だけをドキュメントに記載しています。

Activity

  1. ks6088ts commented on Oct 13, 2026

    @ks6088ts
    OwnerAuthor

    5. 新テンプレートの設計

    5.1 設計原則

    1. 単一の真実の源: Python とツールの版は pyproject.toml と uv.lock、uv の版は required-version、ローカルと CI のコマンドは Makefile に集約し、版を各所に重複させません。
    2. ゲートは失敗させる: 型、層、依存、カバレッジ、ワークフローの安全性、脆弱性はすべてブロッキングにします。例外は「早期警告」と明示したフリースレッド版の行列だけです。
    3. 検証可能なリリース: タグと版の一致、全ゲート、来歴証明、SBOM、イミュータブルリリース、同一ダイジェストの配布を必須にします。
    4. 最小権限・短命資格情報: permissions: {}、ジョブ単位の権限、OIDC(Azure、PyPI)、GITHUB_TOKEN(GHCR)を使い、長期シークレットは原則持ちません。
    5. プラットフォーム設定のコード化: ルールセット JSON と設定スクリプトで、テンプレートが複製しない設定を再現します。
    6. 人とエージェントに同じ規約を: AGENTS.md、スキル、フック、セットアップステップで、Copilot も人と同じ手順とゲートに従わせます。
    7. 段階的に有効化できる: プランや可視性に依存する機能は変数(CODE_SECURITY、ATTESTATIONS、CODEQL_ADVANCED、PYPI_PUBLISH、DOCKERHUB_IMAGE、AZURE_*)や Pages の有無でガードし、新規リポジトリを赤くしません。

    5.2 アプリケーション構造

    flowchart LR
        subgraph interface["インターフェース層"]
            CLI["cli.py<br/>Typer / 構成ルート<br/>終了コード 0・2・78"]
        end
        subgraph adapters["アダプタ層(相互依存禁止)"]
            CFG["config.py<br/>Settings(pydantic-settings)<br/>TEMPLATE_PYTHON_* / .env"]
            LOG["logging_config.py<br/>configure_logging()<br/>text / JSON Lines → stderr"]
        end
        subgraph domain["ドメイン層(フレームワーク非依存)"]
            DOM["domain/greeting.py<br/>純粋関数・固有例外"]
        end
        CLI --> CFG
        CLI --> LOG
        CLI --> DOM
    
    Loading

    この依存方向は import-linter の 2 契約で CI が強制します。layers 契約(exhaustive = true)と、domain から typer、pydantic、pydantic_settings への依存を禁じる forbidden 契約です。

    5.3 CI/CD フロー

    flowchart TB
        PR["pull_request / push main / merge_group"] --> CI
        subgraph CI["ci.yaml(必須チェック: ci-success)"]
            L["lint<br/>ruff・mypy・import-linter・deptry・prek"]
            W["workflows<br/>actionlint・zizmor(オンライン)"]
            T["test 行列<br/>3.12〜3.15 / Win / mac / 3.14t"]
            D["docs<br/>zensical --strict"]
            P["package<br/>uv build + スモークテスト"]
            C["container<br/>hadolint・ビルドチェック・Trivy"]
            L & W & T & D & P & C --> S["ci-success"]
        end
        PR --> SEC["security.yaml<br/>pip-audit・依存関係レビュー"]
        TAG["git tag vX.Y.Z"] --> V["verify<br/>tag = uv version"]
        V --> RCI["ci(workflow_call)"]
        RCI --> B["build<br/>sdist/wheel + attest"]
        RCI --> IMG["image ×2<br/>amd64 / arm64 ネイティブ<br/>provenance max + SBOM"]
        IMG --> M["image-manifest<br/>pep440 タグ + attest"]
        B & M --> GR["GitHub Release<br/>draft → 資産 → 公開(イミュータブル)"]
        B -.->|PYPI_PUBLISH| PYPI["PyPI(Trusted Publishing)"]
        M -.->|DOCKERHUB_IMAGE| DH["Docker Hub(同一ダイジェスト)"]
    
    Loading

    5.4 ワークフロー一覧

    ワークフロー トリガー 目的
    ci.yaml PR、push main、merge_group、dispatch、workflow_call 必須品質ゲート(ci-success)
    security.yaml PR、push main、merge_group、週次、dispatch pip-audit(ブロッキング)、依存関係レビュー
    scorecard.yaml push main、ブランチ保護の変更、dispatch OpenSSF Scorecard(public のみ)
    codeql.yaml PR、push、merge_group、週次 CodeQL 高度なセットアップ(CODEQL_ADVANCED=true のときだけ)
    release.yaml tag v* 検証 → CI → attest → マルチアーキ → GitHub Release(+ 任意で PyPI / Docker Hub)
    docs.yaml push main(docs 変更時)、dispatch GitHub Pages(Pages が有効なときだけ)
    docs-azure-swa.yaml dispatch Azure Static Web Apps(OIDC、任意)
    initialize.yaml push main、dispatch 初回の uv.lock 生成と改名(任意) → CI を再実行
    copilot-setup-steps.yml dispatch、自身の変更時 Copilot cloud agent の実行環境

    5.5 ツールチェーン(2026-10-12 時点)

    領域 採用 備考
    Python ≥ 3.12(既定 3.14、CI は 3.12〜3.15) 3.10 は 2026-10-01 に EOL、3.15.0 は 2026-10-09 に GA
    パッケージ管理 uv 0.13(required-version)、uv_build>=0.13.0,<0.14 uv 0.13.0 は 2026-10-09 リリース
    整形・リント Ruff ≥ 0.17(34 ルール群を明示) 0.16 で既定ルールが 59 → 413 に変更
    型検査 mypy ≥ 2.4 strict + pydantic プラグイン ty(0.0.x beta)と pyrefly はゲートに使わない
    設計検査 import-linter ≥ 2.15、deptry ≥ 0.25.1 layers(| は独立した兄弟)+ forbidden
    テスト pytest ≥ 9.1([tool.pytest]、strict)、Hypothesis、coverage(branch、90%) pytest-cov 7
    フック prek ≥ 0.5.5(.pre-commit-config.yaml 互換) ruff、actionlint、zizmor は uv.lock の版を使う
    Actions 検査 actionlint-py 1.7.12.x、zizmor ≥ 1.30.1 ハッシュピン方針、オンライン監査
    依存監査 pip-audit ≥ 2.10.1、Dependabot(cooldown 7 日) uv audit はまだプレビューのため不採用
    コンテナ python:3.14-slim-trixie、uv 0.13.0、hadolint 2.15.1、Trivy 0.75.0 ビルドチェックはエラー扱い、非 root
    ドキュメント Zensical ≥ 0.0.69 MkDocs 1.x は保守停止
    Actions 21 アクションをコミット SHA で固定(すべて node24 / composite / docker) Node 20 は 2026-09-23 に削除済み

    5.6 旧ファイルから新ファイルへの対応表

    現行 新テンプレート 主な変更
    .github/workflows/test.yaml ci.yaml 並列化、ci-success、merge_group、行列の刷新、package/docs/container を統合
    .github/workflows/docker.yaml ci.yaml の container ジョブ hadolint の sha 検証、ビルドチェック、Trivy ブロック
    docker-release.yaml + ghcr-release.yaml release.yaml 1 回のビルドで同一ダイジェスト、attest、GitHub Release
    github-pages.yaml docs.yaml Pages を Actions でデプロイ、has_pages ガード
    azure-static-web-app.yaml docs-azure-swa.yaml OIDC と実行時のトークン取得
    (なし) security.yaml、scorecard.yaml、codeql.yaml、initialize.yaml、copilot-setup-steps.yml 新設
    .github/dependabot.yaml .github/dependabot.yml 全エコシステムに cooldown とグループ化、devcontainers
    .pre-commit-config.yaml 同名 uv.lock の版を使う local フック、uv lock --check、actionlint、zizmor
    .devcontainer/devcontainer.json 同名 + .devcontainer/Dockerfile 3-3.14-trixie、uv イメージ、features、prebuild
    Dockerfile、.dockerignore、compose.yml 同名(compose.yaml) uv 公式方式、非 root、allowlist、堅牢化
    Makefile 同名 setup / check / ci / audit / workflows / docs / bump-* など
    pyproject.toml 同名 uv_build、PEP 639、依存グループ(test/lint/docs/audit/dev)、各ツールの設定
    uv.lock (初回に生成) 7 章を参照
    template_python/ src/template_python/ cli / config / logging_config / domain、py.typed、__main__
    scripts/template.py src/template_python/cli.py パッケージに統合し、console script 化
    tests/* tests/*(7 モジュール) conftest で隔離、CLI E2E、Hypothesis、スクリプトとフックのテスト
    mkdocs.yml、docs/ zensical.toml、docs/(英)、docs/ja/(日)、docs/adr/ Zensical、strict、ADR
    notebooks/templates.ipynb 削除 出力付きノートブックは除外
    .env.template .env.example 接頭辞付きの変数
    (なし) scripts/init_project.py、scripts/configure_repository.py 初期化とリポジトリ設定のコード化
    (なし) .github/rulesets/*.json、release.yml、zizmor.yml、CODEOWNERS、Issue Forms、PR テンプレート ガバナンス
    (なし) AGENTS.md、.github/copilot-instructions.md、instructions/、agents/、skills/、hooks/、.vscode/mcp.json AI 駆動開発
    (なし) SECURITY.md、CONTRIBUTING.md、CODE_OF_CONDUCT.md、README.ja.md コミュニティ

    6. 導入手順

    6.1 テンプレートリポジトリとして公開する(初回のみ)

    1. repos/template-python/ の内容を新しい GitHub リポジトリへ push し、Settings → General で Template repository を有効にします。
    2. uv.lock を生成してコミットします。本成果物の作成環境はインターネットに接続できず、正しいロックを生成できなかったため、ロックファイルは同梱していません。次のどちらかで生成してください。
      • ローカル(Dev Container や Codespaces を含む)で make setup を実行し、uv.lock をコミットします。
      • 何もしない場合は、初回 push を契機に initialize.yaml が uv.lock を生成・コミットし、CI を再実行します(最初の CI は「uv.lock is missing」で明示的に失敗し、数分後に緑になります)。
    3. make check、make docs、make workflows、make docker-build docker-scan をローカルで実行し、問題が無いことを確認します(7 章の未実施項目を解消する作業です)。
    4. 必要に応じて、ベースイメージのダイジェストを固定します。
    docker buildx imagetools inspect python:3.14-slim-trixie --format '{{json .Manifest.Digest}}'
    docker buildx imagetools inspect ghcr.io/astral-sh/uv:0.13.0 --format '{{json .Manifest.Digest}}'
    # FROM python:3.14-slim-trixie@sha256:... のように追記すると、以後は Dependabot が更新します

    6.2 テンプレートから新規プロジェクトを作る

    # 1) GitHub で "Use this template" → 新規リポジトリを作成し、clone する
    # 2) 名前を置換する(パッケージ名、環境変数の接頭辞、URL、LICENSE の著作権者)
    uv run scripts/init_project.py acme-billing --owner acme-corp --description "Billing service"
    #    (GitHub 上だけで行う場合: Actions → Initialize repository → project-name を入力)
    # 3) 依存を固定して開発を始める
    make setup && make check
    git add -A && git commit -m "chore: initialize project" && git push
    # 4) GitHub の設定をコード化した内容で適用する(まずドライラン、次に --apply)
    python scripts/configure_repository.py
    python scripts/configure_repository.py --apply            # --copilot-review で自動レビューも有効化

    GitHub 上だけで改名する場合も、手順 4(ルールセットの適用)より前に実行してください。Initialize workflow は GITHUB_TOKEN で main に直接 push するため、ルールセット適用後は拒否されます。また GITHUB_TOKEN はワークフローファイルを変更する push ができないため、ワークフローにはプロジェクト名を一切書かない設計にし、テストで担保しています。

    6.3 プランや可視性に応じた設定

    機能 public private(Free/Pro/Team) private(Enterprise Cloud ほか) 設定
    CodeQL default setup / シークレットスキャン / プッシュ保護 無料 GitHub Code Security / Secret Protection が必要 同左 設定スクリプト
    依存関係レビュー 有効 Code Security が必要 同左 変数 CODE_SECURITY=true
    Artifact Attestations 有効 利用不可(ステップをスキップ) 有効 変数 ATTESTATIONS=true
    OpenSSF Scorecard 有効 実行しない 実行しない なし
    マージキュー 組織所有のリポジトリで可 不可 Enterprise Cloud で可 ルールセットに merge_queue を追加(ci.yaml は merge_group 対応済み)
    arm64 ネイティブランナー 4 vCPU 2 vCPU(2026-01-29 から利用可) 同左 なし

    6.4 任意機能の有効化

    機能 必要な設定
    PyPI 公開 PyPI で Trusted Publisher(このリポジトリ、release.yaml、Environment pypi)を登録し、変数 PYPI_PUBLISH=true を設定
    Docker Hub ミラー Environment dockerhub に変数 DOCKERHUB_USERNAME とシークレット DOCKERHUB_TOKEN を設定し、変数 DOCKERHUB_IMAGE=docker.io/<user>/<image> を設定
    Azure Static Web Apps Entra ID アプリにフェデレーション資格情報(Environment azure-static-web-apps)を作成し、AZURE_CLIENT_ID、AZURE_TENANT_ID、AZURE_SUBSCRIPTION_ID、AZURE_RESOURCE_GROUP、AZURE_STATIC_WEB_APP_NAME を設定
    CodeQL 高度なセットアップ default setup を無効にしてから、変数 CODEQL_ADVANCED=true を設定
    Copilot 組織の Copilot ポリシーで cloud agent とコードレビューを許可し、「Settings → Copilot」でファイアウォールの許可リスト(PyPI など)を確認。エージェント用シークレットは「Secrets and variables → Agents」に登録

    切り替え用の変数(PYPI_PUBLISH、DOCKERHUB_IMAGE、AZURE_STATIC_WEB_APP_NAME、CODE_SECURITY、ATTESTATIONS、CODEQL_ADVANCED)は、リポジトリ(または組織)の変数として設定してください。ジョブの if: は Environment にだけ設定した変数を参照できません。シークレットと Azure の各 ID は Environment に置けます。

    6.5 リリース

    uv version --bump minor            # プレリリースは: uv version --bump minor --bump rc
    git switch -c release/v0.2.0 && git commit -am "chore: release v0.2.0" && gh pr create --fill
    # PR をマージした後、メンテナーがマージコミットにタグを付けて push する(release-tags ルールセットで保護)
    git tag v0.2.0 <merge-sha> && git push origin v0.2.0
    # 検証
    gh release verify v0.2.0
    gh attestation verify oci://ghcr.io/<owner>/<repo>:0.2.0 --repo <owner>/<repo>

    6.6 継続的な運用の推奨

    • 組織レベルへの昇格: 複数リポジトリで使う場合は次のように進めると、テンプレートからの逸脱検知と更新の伝播を、配布後の各リポジトリへ手作業で行う必要がなくなります。
      • CI の共通部分を、組織の .github リポジトリなどに置いた版付きの再利用ワークフローへ切り出します。
      • ルールセットと required workflows を組織ルールセットで強制します。
    • 追随が必要な変化:
      • 2026-11-05 の Zensical 0.1.0 系。
      • python:3.15-slim-trixie の公開(コンテナの既定を 3.15 に上げるかを判断)。
      • ubuntu-26.04 への移行。
      • GitHub の workflow dependency locking(gh actions-lock、現在はテクニカルプレビュー)の GA。
      • uv audit の正式化(pip-audit から置き換えるか判断)。
    • AI 活用の発展: 安定した段階で gh-aw の CI 失敗調査(gh aw add githubnext/agentics/ci-doctor)を試行し、.lock.yml と合わせてコミットします。

    7. 検証結果と残る制約

    本成果物を作成した環境はインターネットに接続できません。そのため、パッケージの取得が必要な検証(uv lock、ruff や mypy の実行、Docker ビルドなど)はできていません。ここでは、実施した検証と実施できなかった検証を分けて示します。後者は、テンプレートを GitHub に置いた後の初回 CI と make check で確認してください。

    7.1 実施した検証

    検証 内容 結果
    構文と形式 すべての .py のコンパイル、YAML / TOML / JSON のパース、ワークフローの run: ブロックを bash -n で確認 問題なし
    ワークフローの方針 全アクションが 40 桁の SHA で固定され、付録 A の確認済み SHA と一致すること。トップレベル permissions: {} とジョブ単位の最小権限。全ジョブの timeout-minutes。checkout の persist-credentials: false。run: 内に ${{ }} を直接書かない。ci-success が全ジョブを needs に含むこと 違反 0 件
    pre-commit 相当の確認 行末の空白(Markdown の改行用 2 スペースは除く)、最終改行、CRLF、マージ衝突マーカー、秘密鍵の文字列、500 KB 超のファイル 違反 0 件
    Python の簡易静的チェック 未使用 import、ライブラリ内の print()、ロギング呼び出しでの f-string、モジュール docstring、120 文字超の行 違反 0 件
    ドキュメント zensical.toml の nav が指す全ページの存在、Markdown の相対リンク切れ、docs/ の外を指すリンク(strict ビルドで失敗する) 違反 0 件
    テスト 全 58 件のうち、pydantic と Typer に依存しない 42 件(domain、ロギング、init_project.py、configure_repository.py、Copilot フック、ワークフローに置換対象の名前が無いこと)を、pytest 互換の小さなランナーで -W error を付けて実行 42 件成功
    名前の置換 最終版のツリー(ドキュメントを含む)を複製し、init_project.py acme-billing --owner contoso を実行 43 件の変更(ファイルの書き換えとパッケージディレクトリの改名)、旧名の残り 0 件、ワークフローは無変更、置換後も方針チェック違反 0 件・42 件成功
    セキュリティ静的解析 bandit で src、scripts、.github/hooks を走査 High / Medium 0 件。Low 3 件は gh を subprocess で呼ぶ箇所で、意図どおり(コードに理由を明記)
    現行テンプレートの不具合 pytest の caplog と同じ仕組みを標準ライブラリで再現 test_core.py の 3 件が失敗することを確認(A-01)
    方針チェッカー自体 わざと違反を入れたワークフローで、検出できることを確認 検出できた

    補足:

    • 42 件のうち Hypothesis を使うプロパティテスト 1 件は、ランダム生成ではなく、境界値を集めた固定の例で実行しました。
    • 依存ライブラリの代わりに使ったスタブは import を通すためだけのもので、pydantic の検証ロジックは再現していません。

    7.2 実施できなかった検証(初回 CI で確認)

    項目 理由 確認方法
    uv.lock の生成 ネットワークが必要 make setup を実行するか、Initialize workflow に任せる(6.1)
    ruff 0.17 / mypy 2.4 / import-linter / deptry パッケージを取得できない make check(CI の lint ジョブ)
    actionlint / zizmor / hadolint 同上 make workflows(CI の workflows ジョブ)
    設定と CLI のテスト(16 件) pydantic と Typer を導入できない make test(CI の test ジョブ)
    Windows / macOS / free-threaded 3.14t ランナーがない CI の test マトリクス
    Docker ビルド、Trivy、構成証明(attestation) Docker とネットワークがない make docker-build docker-scan、CI の container ジョブ、初回リリース
    Zensical のビルド パッケージを取得できない make docs(CI の docs ジョブ)
    GitHub API の呼び出し 認証とネットワークがない configure_repository.py をドライランで確認してから --apply
    Copilot のフックとエージェントの実機動作 Copilot の実行環境がない cloud agent に小さな Issue を割り当てて試す

    7.3 初回に起きうる問題と対処

    リスク 起きうること 対処
    ruff のルール追加 ルールはファミリー単位で明示しているため 0.16 の既定ルール拡大(59 → 413)の影響は受けないが、手元で実行していないため初回に指摘が出る可能性がある。ruff の更新で同じファミリーに新ルールが加わると指摘が増えることもある uv run ruff check --fix で直し、残りは個別に修正するか per-file-ignores に理由付きで追加。版は uv.lock で固定され、更新は Dependabot の PR の CI で検出される
    zizmor の判定 cache-poisoning などの判定が版によって変わる リリース系ではキャッシュを無効化済み。指摘が出たら .github/zizmor.yml に理由付きで除外を追加
    ランナーラベル macos-26 などが actionlint の既知ラベルにない .github/actionlint.yaml に登録済み。新しいラベルを使うときは追記
    ベースイメージの脆弱性 修正版のある HIGH 以上の脆弱性で container ジョブが失敗する ベースイメージを更新(Dependabot の docker 更新をマージ)。急ぐ場合は期限付きで .trivyignore に記録
    Zensical の仕様変更 alpha 版のため設定項目が変わる可能性がある(0.1.0 は 2026-11-05 予定) 依存は zensical>=0.0.69 の下限のみで、uv.lock で固定される。更新時は make docs で確認。問題があれば Material for MkDocs(2027-05 まで保守)に戻せる
    ルールセットの bypass 設定 管理者ロールを表す actor_id: 5 は公式ドキュメントに明記がない API が拒否した場合は bypass_actors を外して再適用(スクリプトは警告を出して続行)
    pydantic の mypy プラグイン mypy 2.x との互換が崩れた場合に型チェックが失敗する pyproject.toml の plugins から外しても型チェックは動く
    ubuntu-latest の 26.04 移行 2026-10-19 から 11-19 にかけて段階的に移行する 本テンプレートは ubuntu-24.04 を明示しているので影響しない。26.04 への切り替えは計画的に行う
    Python 3.15 3.15.0 は 2026-10-09 リリースで、依存側の対応が追いついていない場合がある 3.15 はマトリクスに含めて早期に検出し、既定(.python-version、イメージ)は 3.14 のまま
    trivy-action の SHA 完全な SHA の確認元が第三者ページ 1 件のみ zizmor のオンライン監査(impostor-commit)が CI で照合する。初回に公式リリースページでも確認する

    7.4 本番利用の前に決めておくこと

    • ライセンスと著作権者(LICENSE は MIT、著作権者はプレースホルダー。init_project.py で置換)
    • 公開範囲とプラン(Public、Private、Enterprise)。プランによって使える機能が変わります(6.3)
    • PyPI に公開するか、イメージをどのレジストリに置くか(6.4 の変数で切り替え)
    • CODEOWNERS のチーム名と、必須レビュー人数(ルールセット)
    • 対応する Python の範囲(既定は 3.12 以上。3.12 のセキュリティサポートは 2028-10 まで)

    付録 A. 固定した GitHub Actions(2026-10-12 時点の最新安定版)

    すべてコミット SHA(40 桁)で固定し、行末に版のコメントを付けています。現行テンプレートが使っていた 10 アクションの SHA(checkout v7.0.1、setup-python v7.0.0、cache v6.1.0、github-script v9.0.0、Docker 系 4 種、static-web-apps-deploy v1)も、各リリースページの SHA と一致することを確認しました。setup-uv だけは v10.2.0 から v10.3.0(uv 0.13.0 対応)へ更新しています。

    アクション 版 SHA
    actions/checkout v7.0.1 3d3c42e5aac5ba805825da76410c181273ba90b1
    astral-sh/setup-uv v10.3.0 1c37ad07a6a961277cf70c0d37d6f313000f5884
    actions/upload-artifact v7.0.2 cf430e030ddbb5b0abf93d22962f4752f3646cd9
    actions/download-artifact v8.0.2 9000827ccba6bdab643e8b6fd33ac0654aef8333
    actions/attest v4.2.2 1e69f48acb82d1966a394da916b4c1698aa569d6
    actions/configure-pages v6.0.0 45bfe0192ca1faeb007ade9deae92b16b8254a0d
    actions/upload-pages-artifact v5.0.0 fc324d3547104276b827a68afc52ff2a11cc49c9
    actions/deploy-pages v5.0.1 368f82528645a54fb793d4d04e342629a3f51346
    actions/dependency-review-action v5.0.0 a1d282b36b6f3519aa1f3fc636f609c47dddb294
    github/codeql-action(init / analyze / upload-sarif) v4.38.3 24c54180a607b1449ed407dd24f251e4e9147c8d
    ossf/scorecard-action v2.4.4 2d1146689b8cda280b9bc96326124645441f03bc
    docker/setup-buildx-action v4.4.1 f87e5991a6d7451dcb8d9637bfbc97413f497069
    docker/login-action v4.6.0 dbcb813823bdd20940b903addbd779551569679f
    docker/metadata-action v6.2.0 dc802804100637a589fabce1cb79ff13a1411302
    docker/build-push-action v7.4.0 c3c9e263c25d99ce0380d002d59b67737d91b0dc
    aquasecurity/trivy-action v0.36.0(scanner v0.75.0) ed142fd0673e97e23eac54620cfb913e5ce36c25
    pypa/gh-action-pypi-publish v1.14.2 dc37677b2e1c63e2034f94d8a5b11f265b73ba33
    azure/login v3.1.0 a641126d1b8aa4d1fa005f4f92df94a3a4c4c906
    Azure/static-web-apps-deploy v1(tag) 1a947af9992250f3bc2e68ad0754c0b0c11566c9

    補足:

    • Azure/static-web-apps-deploy には同名の v1 ブランチ(HEAD は 4d27395…)もあります。SHA で固定すると曖昧さはなくなります。
    • trivy-action の完全な SHA は第三者ページ 1 件でしか確認できていません(短縮 SHA ed142fd は公式リリースページと一致)。CI の zizmor オンライン監査(impostor-commit)が誤りを検出する構成にしています。

    付録 B. 参照した一次情報(2026-10-12 確認)

    GitHub プラットフォーム

    GitHub Copilot

    Python エコシステム

    コンテナ・ドキュメント

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions