diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 770bbd6..ca3e04c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,8 +1,10 @@ -# 基本 -- 日本語で応答すること -- 必要に応じて、ユーザに質問を行い、要求を明確にすること -- 作業後、作業内容とユーザが次に取れる行動を説明すること -- 作業項目が多い場合は、段階に区切り、git commit を行いながら進めること - - semantic commit を使用する -- コマンドの出力が確認できない場合、 get last command / check background terminal を使用して確認すること -- ユーザが特定の技術スタックを使用している場合、そのスタックに適したソリューションを提供すること +# Copilot 規範層(全タスク共通で短く強い指示) +- 仕様の入口は `.github/copilot/00-index.md`。参照順に従い、設計→実装の2段階ループを厳守する。 +- コード/ドキュメントは **完全実装**で提出し、擬似コード・未完成サンプルは禁止。出力は必要最小限の差分にとどめる。 +- **PRタイトルは必ず日本語で記述すること。**(例: `feat: 新しい認証処理の追加` のように、日本語で目的を明示する。) +- コミットメッセージは `.github/instructions/commit-messages.instructions.md` に従い、日本語・`fix:`/`hotfix:`/`feat:` のプレフィックス付きで3行以上の構造を守ること(Copilot 生成分も含む)。 +- Secrets・個人情報をコード/ログ/ドキュメントに出さない。ログは構造化し、例外は握り潰さず意味のあるメッセージで扱う。 +- 互換性を壊さない方針がデフォルト。破壊的変更が必要なときは移行策を plan に明記する。 +- 実装前に `.github/copilot/80-templates/implementation-plan.md` を満たす plan を作成・確認する(設計Issue)。実装Issueでは plan から逸脱しない。 +- 型・例外・入力検証・テスト追加を必須とし、`.github/instructions/**/*.instructions.md` の実務ルールを守る。 +- Done 定義: lint / typecheck / test / security など CI 品質ゲートが全て緑、受入条件をテストで担保、関連ドキュメントを更新すること。 diff --git a/.github/copilot/00-index.md b/.github/copilot/00-index.md new file mode 100644 index 0000000..405d50a --- /dev/null +++ b/.github/copilot/00-index.md @@ -0,0 +1,24 @@ +# 00 Index — Copilot 仕様の入口(SSOT) + +このディレクトリは Copilot / 自動エージェント向けの **仕様の単一情報源(SSOT)** です。必須ルールは `.github/copilot-instructions.md` に集約します。`.github/instructions/**/*.instructions.md` は設計書・背景資料・適用範囲のメタ情報を置く場所であり、GitHub Copilot によって**公式機能として自動的に解釈・適用される実務ルール**です。必ず以下の順で参照してください。 + +## 参照順(優先度順) +※ 構成定義レイヤはリポジトリ全体の前提となるため最初に参照してください。番号は通常の昇順で付与しています。 + +1. [構成定義レイヤ](05-structure/monorepo.md) — モノレポ運用ルール +2. [copilot-instructions.md](../copilot-instructions.md) — 規範層(短く強いルール) +3. [.github/instructions/**/*.instructions.md](../instructions) — 補助的な設計/背景資料レイヤ(`applyTo` は適用範囲を示すメタ情報) +4. [10-requirements.md](10-requirements.md) — 要件とスコープ/受入条件 +5. [20-architecture.md](20-architecture.md) — 設計方針・責務分担 +6. [30-coding-standards.md](30-coding-standards.md) — コーディング規約 +7. [40-testing-strategy.md](40-testing-strategy.md) — テスト戦略 +8. [50-security.md](50-security.md) — セキュリティ要求 +9. [60-ci-quality-gates.md](60-ci-quality-gates.md) — CI 品質ゲート +10. [70-adr/](70-adr/) — 重要判断の履歴 +11. [80-templates/](80-templates/) — plan / PR / review のテンプレート +12. [90-research/](90-research/) — RESEARCH フェーズの成果物。DESIGN、IMPLEMENT フェーズのインプット資料でもあります。 + +## 使い方 +- **設計フェーズ (Phase A)**: `80-templates/implementation-plan.md` に沿って plan を作成し、`10-60` の仕様を満たすこと。 +- **実装フェーズ (Phase B)**: 確定 plan の範囲内で実装し、`60-ci-quality-gates.md` の品質ゲートを通過させること。 +- 仕様変更・追記は本ディレクトリに集約し、重複や分散を避ける。 diff --git a/.github/copilot/05-structure/README.md b/.github/copilot/05-structure/README.md new file mode 100644 index 0000000..061623d --- /dev/null +++ b/.github/copilot/05-structure/README.md @@ -0,0 +1,17 @@ +# 05 Structure — 構成定義レイヤ + +このレイヤはリポジトリ構成(モノレポ / ポリレポ)の運用ルールを集約し、`.github/copilot/00-index.md` から選択的に参照できるようにするためのものです。 + +## 目的 + +- 構成モードごとのルールを分離し、他リポジトリへの移植時にリンク整合性を保つ。 +- CI やテスト実行の前提を構成モードごとに明示し、運用差異を吸収する。 + +## 切り替え方法 + +1. 本レイヤ内の対象ファイル(例: `05-structure/monorepo.md` や `05-structure/polyrepo.md`)を選択する。 +2. `.github/copilot/00-index.md` の参照順リストにある + `[構成定義レイヤ](05-structure/monorepo.md)` などの Markdown リンクを、利用する構成モードのファイルに差し替える。 +3. 切り替え後、必要に応じて CI・テストの設定やドキュメント内リンクを構成モードに合わせて更新する。 + +初期状態ではモノレポ構成(`monorepo.md`)を参照します。ポリレポ構成(`polyrepo.md`)は将来切り替え用として保持し、未実装状態でも削除しないでください。 diff --git a/.github/copilot/05-structure/monorepo.md b/.github/copilot/05-structure/monorepo.md new file mode 100644 index 0000000..50b8fe8 --- /dev/null +++ b/.github/copilot/05-structure/monorepo.md @@ -0,0 +1,549 @@ +# モノレポ構成定義 — CopilotAgent 参照用 + +> **NOTE:** 本書は Copilot Agent が自動解析する構成定義レイヤの SSOT。 +> 規範表現は MUST / SHOULD / MUST NOT を使用し、各箇条には `RULE:` / `DO NOT:` / `EXAMPLE:` / `NOTE:` タグを付与する。`RULE:` は MUST、`DO NOT:` は MUST NOT に相当する。 +> スコープは本リポジトリ内のモノレポ(複数アプリ混在)運用。コード例・コマンドは直接利用可能な最小形を維持する。`` はワークスペース上のリポジトリ名プレースホルダ(例: `myproject`)。 + +--- + +## 1. 目的と適用範囲 +- RULE: 本書は `.github/copilot-instructions.md`(リポジトリに存在)から include される構成定義レイヤであり、モノレポのディレクトリ設計・ビルド・デプロイ・テストの単一情報源となる。 +- RULE: 対象は「リポジトリルートを起点とする compose / docker / devcontainer / apps / deploy / scripts / credentials」。他ファイルへ影響を及ぼさない。 +- NOTE: 互換性重視。既存のコード例・コマンドは保持しつつ重複を統合して再構成する。 + +--- + +## 2. 基本原則(パス・ビルド・多言語) +- RULE: docker-compose ファイルはリポジトリルートに集約し、`docker-compose.base.yml`(共通) + `docker-compose..yml`(差分)でオーバーレイする。 +- RULE: `build.context` と `dockerfile` は **常にリポジトリルート基準**で指定し、`common` を含む階層(例: `./apps/python`)に固定する。 +- RULE: `.env` はビルド時に混ぜず、起動時に `env_file` で渡す。 +- RULE: ベースイメージは1つにまとめ、各アプリの Dockerfile は薄く(`FROM base + COPY + ENTRYPOINT`)。`CMD` 直叩きは避け、`entrypoint.sh` で `exec python -m ...` する。 +- RULE: アプリごとの世界は `apps//` に分離する(Python / PHP / iOS / Web 等)。共通コードは言語ごとに分ける。 +- DO NOT: `build.context: ../..` などリポジトリ全体を投げる指定。パス地獄とビルド肥大化を招く。 + +--- + +## 3. 標準ディレクトリ構成(推奨テンプレ) + +### 3.1 リポジトリ全体(Python + 共通 + devcontainer + compose + env) +```text +repo-root/ +├── .env +├── .env.dev +├── .env.stg +├── .env.prod +│ +├── docker-compose.base.yml +├── docker-compose.dev.yml +├── docker-compose.stg.yml +├── docker-compose.prod.yml +│ +├── docker/ +│ └── base/ +│ └── Dockerfile # 本番アプリ共通ベースイメージ +│ +├── apps/ +│ ├── python/ +│ │ ├── common/ # ★ Python共通コード +│ │ │ ├── __init__.py +│ │ │ ├── mylib/ +│ │ │ │ ├── __init__.py +│ │ │ │ ├── util.py +│ │ │ │ └── validators.py +│ │ │ └── tests/ +│ │ │ ├── test_util.py +│ │ │ └── test_validators.py +│ │ │ +│ │ ├── app1/ +│ │ │ ├── Dockerfile # 薄い(FROM base) +│ │ │ ├── entrypoint.sh # シェル経由で python 実行 +│ │ │ ├── main_app1.py +│ │ │ └── tests/ +│ │ │ ├── test_app1_main.py +│ │ │ └── test_app1_api.py +│ │ │ +│ │ └── batch/ +│ │ ├── Dockerfile # 薄い(FROM base) +│ │ ├── entrypoint.sh +│ │ ├── main_batch.py +│ │ └── tests/ +│ │ └── test_batch.py +│ │ +│ ├── php/ # (必要なら共存) +│ │ ├── api/ +│ │ │ ├── Dockerfile +│ │ │ ├── composer.json +│ │ │ └── src/... +│ │ └── common/ +│ │ └── src/... +│ │ +│ └── ios/ # (必要なら共存) +│ └── MyAwesomeApp/ +│ ├── MyAwesomeApp.xcodeproj +│ ├── Sources/... +│ └── Tests/... +│ +└── .devcontainer/ + ├── Dockerfile # 開発専用(本番とは別) + └── devcontainer.json +``` + +--- + +## 4. Dockerfile 設計(共通ベース + 薄い各アプリ) +### 4.1 共通ベースイメージ +- RULE: 本番共通依存のみを入れる。開発ツールは devcontainer 側へ。 +```dockerfile +# docker/base/Dockerfile +FROM python:3.12-slim +RUN apt-get update && apt-get install -y bash && rm -rf /var/lib/apt/lists/* +WORKDIR /app +ENV PYTHONUNBUFFERED=1 +ENV PYTHONPATH="/app" +``` + +### 4.2 薄いアプリ Dockerfile(app1/batch) +- RULE: `build.context` を `./apps/python` に固定し、common を COPY できるようにする。 +```dockerfile +# apps/python/app1/Dockerfile +FROM myorg/app-base:1.0 +WORKDIR /app +COPY common/ ./common/ +COPY app1/ ./app1/ +RUN chmod +x ./app1/entrypoint.sh +ENTRYPOINT ["./app1/entrypoint.sh"] +``` +```dockerfile +# apps/python/batch/Dockerfile +FROM myorg/app-base:1.0 +WORKDIR /app +COPY common/ ./common/ +COPY batch/ ./batch/ +RUN chmod +x ./batch/entrypoint.sh +ENTRYPOINT ["./batch/entrypoint.sh"] +``` + +### 4.3 entrypoint.sh テンプレート +- RULE: `set -euo pipefail` と `exec python -m ...` を徹底し、PYTHONPATH を固定する。 +```bash +#!/usr/bin/env bash +set -euo pipefail +export PYTHONPATH="/app" +exec python -m app1.main_app1 "$@" +``` + +--- + +## 5. docker-compose 設計(base + env差分 + profiles) +- RULE: `profiles` で起動セットを分ける(例: `local` / `cloud` / `batch`)。 +- RULE: `env_file` は「共通 → 環境固有」の順で積む。 +- RULE: Healthcheck は起動順が重要なサービスに付与する。 +```yaml +# docker-compose.base.yml +version: "3.9" +services: + app1: + profiles: ["local", "cloud"] + build: + context: ./apps/python + dockerfile: ./app1/Dockerfile + env_file: [.env] + restart: unless-stopped + batch: + profiles: ["local", "batch"] + build: + context: ./apps/python + dockerfile: ./batch/Dockerfile + env_file: [.env] + restart: unless-stopped +``` +```yaml +# docker-compose.dev.yml(開発差分) +services: + app1: + env_file: [.env, .env.dev] + volumes: + - ./apps/python:/app + batch: + env_file: [.env, .env.dev] + volumes: + - ./apps/python:/app +``` +```yaml +# docker-compose.prod.yml(本番差分) +services: + app1: + env_file: [.env, .env.prod] + batch: + env_file: [.env, .env.prod] +``` +```bash +# EXAMPLE: 起動コマンド +docker compose -f docker-compose.base.yml -f docker-compose.dev.yml --profile local up -d # dev +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d # cloud +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile batch up batch +``` +- NOTE: `x-credentials-*` アンカーを使って volume 定義を共有すると、複数アプリでも重複を防げる(Sec.9 認証情報の例を参照)。 + +--- + +## 6. devcontainer 運用 +- RULE: `.devcontainer/` は開発専用。本番イメージと分離し、開発ツールは devcontainer のみへ入れる。 +- RULE: ワークスペースは `/workspaces/` にマウントされる前提で、PYTHONPATH を devcontainer 内で固定する。 +```json +{ + "name": "mono-repo-dev", + "build": { "dockerfile": "Dockerfile", "context": "." }, + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "containerEnv": { + "PYTHONPATH": "/workspaces/${localWorkspaceFolderBasename}/apps/python" + }, + "customizations": { + "vscode": { "extensions": ["ms-python.python", "ms-python.vscode-pylance"] } + } +} +``` +- DO NOT: devcontainer イメージに本番用コンポーネントを混在させる。 + +--- + +## 7. common & PYTHONPATH(単一方針) +- RULE: Python の import root を `apps/python` に固定し、`common` を `apps/python/common` に置く。 +- RULE: import は絶対指定(`from common...`)。`sys.path.append` や相対 import は禁止。 +- RULE: 実行は `python -m ` に統一し、実行ディレクトリ依存を排除する。 +- RULE: devcontainer は `PYTHONPATH=/workspaces//apps/python`、アプリコンテナは `/app` を import root とする。 +```text +apps/python/ +├── common/ +│ ├── __init__.py +│ └── mylib/util.py +├── app1/ +│ └── app1/main_app1.py +└── batch/ + └── batch/main_batch.py +``` +```yaml +# compose 側 PYTHONPATH 例(方式A) +services: + app1: + volumes: + - ./apps/python:/app + environment: + PYTHONPATH: /app +``` +```bash +# EXAMPLE: devcontainer 直実行 +cd apps/python/app1 && PYTHONPATH=/workspaces//apps/python ./entrypoint.sh +``` + +--- + +## 8. テスト戦略(pytest 一括) +- RULE: プロジェクトルートで `pytest` を実行する。`PYTHONPATH=$PWD/apps/python` を必ず設定する。 +- RULE: `tests/` または `test_*.py` 規約を統一し、探索範囲を `apps/python` に固定する。 +- RULE: `integration` マーカーで遅い/外部依存テストを分離し、デフォルトは unit のみ。 +```ini +# repo-root/pytest.ini +[pytest] +minversion = 7.0 +testpaths = apps/python +python_files = test_*.py *_test.py +markers = + unit: unit tests + integration: integration tests +addopts = -ra +``` +```bash +# scripts/test-python.sh(単一情報源) +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." +export PYTHONPATH="$PWD/apps/python" +pytest -v -m "not integration" +``` +- RULE: CI 品質ゲート(PR)は Lint + Unit Test + Security を必須とし、CD でも Unit を再実行する。 +- EXAMPLE: GitHub Actions(unit 抜粋) +```yaml +- name: Unit tests (single source) + run: | + chmod +x scripts/test-python.sh + ./scripts/test-python.sh +``` + +--- + +## 9. 認証情報/credentials 運用 +- RULE: 認証ファイルは `.env` に直接書かず、`credentials//...` に配置して Git へコミットしない(`.gitignore` 必須)。README と `.sample` のみ Git 許可。 +- RULE: コンテナ内パスは `/run/credentials/...` へ read-only マウントし、`.env` には「パスのみ」を定義する。 +- RULE: 秘密鍵/証明書は 600、ディレクトリは 700 権限にする。 +- EXAMPLE: compose で共通 volume を注入(volume 自体にアンカーを付与する) +```yaml +services: + app1: + volumes: + - &credentials-dev ./credentials/dev:/run/credentials:ro + - ./apps/python:/app + batch: + volumes: + - *credentials-dev + - ./apps/python:/app +``` +- RULE: CI では `credentials/` を持ち込まず、Secrets からファイルを生成して同一パスに置く。 +```yaml +- name: Write GCP credentials + run: | + mkdir -p /tmp/credentials/gcp + echo '${{ secrets.GCP_SA_JSON }}' > /tmp/credentials/gcp/service-account.json + chmod 600 /tmp/credentials/gcp/service-account.json +``` +- NOTE: 最終形は Secret Manager / IAM Role / Workload Identity などファイルレス運用を推奨。 + +--- + +## 10. Dependabot(モノレポ混在対応) +- RULE: `.github/dependabot.yml` をリポジトリルート1箇所に置き、エコシステム × ディレクトリ単位で `updates` を分割する。 +- RULE: `open-pull-requests-limit` と `groups` を必ず設定し、PR洪水を防ぐ。ラベルは `dependencies` + `deps-` + `area-` を付与する。 +- RULE: 週次更新を基本とし、Security updates は優先レビュー(`security` ラベル)。 +```yaml +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: { interval: "weekly", day: "monday", time: "03:00", timezone: "Asia/Tokyo" } + labels: ["dependencies", "deps-actions"] + open-pull-requests-limit: 5 + groups: + actions: + patterns: ["*"] + - package-ecosystem: "pip" + directory: "/apps/python/app1" + schedule: { interval: "weekly", day: "monday", time: "03:10", timezone: "Asia/Tokyo" } + labels: ["dependencies", "deps-python", "area-app1"] + open-pull-requests-limit: 5 + groups: + py-devtools-app1: + patterns: ["pytest*", "ruff", "mypy", "black", "isort"] + py-runtime-app1: + patterns: ["*"] + exclude-patterns: ["pytest*", "ruff", "mypy", "black", "isort"] +``` +- DO NOT: `directory` を依存ファイルの無い場所に向ける / PR 上限なしで運用する。 + +--- + +## 11. CI/CD とデプロイ +- RULE: ワークフローは領域別に分割する(例: `python-ci.yml`, `php-ci.yml`, `ios-ci.yml`, `release-cloud.yml`, `release-onprem.yml`)。`paths` で無駄実行を減らす。 +- RULE: リリースフローは `unit` → `build` → `deploy` の順で、`deploy` は `needs: [unit, build]` を必須とする。 +- EXAMPLE: release-cloud 骨子 +```yaml +jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: { python-version: "3.12" } + - run: | + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - run: chmod +x scripts/test-python.sh && ./scripts/test-python.sh + build: + needs: unit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - run: docker build -f apps/python/app1/Dockerfile -t myorg/app1:sha-${{ github.sha }} apps/python + deploy: + needs: [unit, build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./scripts/deploy-cloud.sh prod +``` +- RULE: イメージタグは不変(`sha-`)で昇格させる。`dev→stg→prod` も同一 SHA を使う。 +- RULE: onprem は compose を正とし、`deploy/onprem/compose/deploy.sh` などスクリプト経由で実行する。systemd 常駐例は `deploy/onprem/compose/systemd/app-stack.service` を基にする。 +- RULE: cloud は kustomize/helm の overlays/values に起動対象・タグ・Secret 注入を集約する。 +- RULE: `scripts/` に up/down/logs/test/deploy の入口を集約し、CI も同じスクリプトを呼ぶ。 +- EXAMPLE: Makefile(起動とテストの固定化) +```make +.PHONY: up-dev up-prod down test +COMPOSE_BASE=-f docker-compose.base.yml +COMPOSE_DEV=$(COMPOSE_BASE) -f docker-compose.dev.yml +COMPOSE_PROD=$(COMPOSE_BASE) -f docker-compose.prod.yml +up-dev: + docker compose $(COMPOSE_DEV) --profile local up -d +up-prod: + docker compose $(COMPOSE_PROD) --profile cloud up -d +down: + docker compose $(COMPOSE_BASE) down +test: + chmod +x ./scripts/test-python.sh && ./scripts/test-python.sh +``` + +--- + +## 12. 多言語共存 +- RULE: 言語ごとに世界を分離する(Python: `apps/python`, PHP: `apps/php`, iOS: `apps/ios`, Web: `apps/web`)。 +- RULE: CI ジョブも言語ごとに分け、チェック名に領域を含める(例: `python-ci`, `php-api-ci`, `ios-ci`)。 +- NOTE: iOS は Docker 化せず macOS runner で `xcodebuild test` を実行する。PHP は composer / phpunit / artisan test をアプリ単位で実行する。 + +--- + +## 13. 補助ファイル(運用の単一情報源) +- RULE: `.dockerignore` を必ず置き、`__pycache__`, `.pytest_cache`, `.venv`, `.mypy_cache`, `dist`, `build`, `node_modules`, `.DS_Store` 等を除外する。 +- RULE: `scripts/` 配下に `test-python.sh`, `up-*.sh`, `deploy-*.sh` などを集約し、CI/ローカルが同一入口を使う。 +- RULE: `credentials/README.md` で取得・配置・権限・注入パスを明記する。 +- NOTE: `schema/` に OpenAPI / JSON Schema / Protobuf を置き、言語跨ぎの共通仕様をコードではなく仕様で共有する。 + +--- + +## 14. 最小チェックリスト +- [ ] compose はルート集約。base + env 差分でオーバーレイ運用。 +- [ ] `build.context` は `common` を含む階層(例: `./apps/python`)に固定。 +- [ ] `.env` は env_file で渡し、ビルドへ混ぜない。 +- [ ] ベースイメージは1つ、各アプリ Dockerfile は薄い(FROM + COPY + ENTRYPOINT)。 +- [ ] entrypoint.sh は `exec python -m ...` で起動し、PYTHONPATH を固定。 +- [ ] devcontainer は本番と分離、workspace はリポジトリルート。PYTHONPATH を `/workspaces//apps/python` に固定。 +- [ ] pytest はリポジトリルートで実行し、`PYTHONPATH=$PWD/apps/python` を必須とする。 +- [ ] credentials は Git へ入れず、`/run/credentials` へ read-only マウントする。 +- [ ] Dependabot は groups + PR 上限付きでエコシステム別に分割。 +- [ ] CI/CD は scripts/ を入口にし、unit → build → deploy の順でゲートする。 + +--- + +## 15. アンチパターンと対処 +- DO NOT: `../common` を COPY する(build.context 外は参照不可) + → 対処: build.context を common を含む階層に固定。 +- DO NOT: devcontainer で `PYTHONPATH` 未設定のまま `cd apps/...` して実行 + → 対処: `containerEnv.PYTHONPATH=/workspaces//apps/python` を固定。 +- DO NOT: `python file.py` など実行場所依存の起動 + → 対処: すべて `python -m ` に統一。 +- DO NOT: PR 上限なしの Dependabot 運用 + → 対処: groups + `open-pull-requests-limit` を必ず設定。 +- DO NOT: credentials を `.env` や Git に埋め込む + → 対処: パスだけを `.env` に記載し、`/run/credentials` を read-only で注入。 + +--- + +## 16. Wiki 貼り付け用ショート宣言 +```text +【運用宣言】 +1) compose はリポジトリルートに集約し、base + 環境差分でオーバーレイする。 +2) build.context は common を含む階層(例: ./apps/python)に固定し、../ 地獄を禁止する。 +3) .env は起動時に env_file で渡す(ビルドに混ぜない)。 +4) Dockerfile は「共通ベース1つ + 各アプリ薄いDockerfile」を原則とし、太い Dockerfile のコピペを禁止する。 +5) アプリ起動はリポジトリルートから docker compose を叩く。直起動はデバッグ用途に限定する。 +6) common は apps/python/common に置き、PYTHONPATH を apps/python(/app)に統一する。 +7) CI はリポジトリルートから pytest を実行し、`PYTHONPATH=$PWD/apps/python` を必須とする。 +8) devcontainer は本番イメージと分離し、開発ツールは devcontainer 側にのみ導入する。 +9) 多言語共存(Python/PHP/iOS)は言語ごとに apps// で世界を分け、CI もジョブを分ける。 +``` + +--- + +## 17. リポジトリ完全テンプレ(圧縮版) +> NOTE: “common + パス + CI” で必要な資材を一箇所にまとめた統合版。`...` は配下に増えることを示す。 +```text +repo-root/ +├── .dockerignore +├── .env(.dev|.stg|.prod|.onprem|.example) +├── Makefile +├── docker-compose.base.yml +├── docker-compose.dev.yml +├── docker-compose.stg.yml +├── docker-compose.prod.yml +├── docker/base/Dockerfile +├── .devcontainer/ +│ ├── Dockerfile +│ ├── devcontainer.json +│ └── scripts/ +├── .github/ +│ ├── workflows/ +│ ├── dependabot.yml +│ ├── CODEOWNERS +│ ├── copilot-instructions.md +│ └── copilot/** +├── scripts/ +│ ├── test-python.sh +│ ├── up-dev.sh +│ ├── deploy-cloud.sh +│ ├── deploy-onprem.sh +│ └── ... +├── credentials/ +│ ├── dev/ +│ ├── stg/ +│ └── prod/ # 認証ファイル本体は Git 禁止、README のみ許容 +├── schema/ +│ ├── openapi.yaml +│ ├── events.proto +│ └── ... +├── deploy/ +│ ├── local/compose/ +│ ├── onprem/compose/ +│ │ ├── deploy.sh +│ │ ├── rollback.sh +│ │ └── systemd/app-stack.service +│ └── cloud/ +│ ├── terraform/ +│ │ ├── modules/ +│ │ └── envs/ +│ ├── k8s/ +│ │ ├── base/ +│ │ └── overlays/ +│ └── helm/ +│ ├── charts/ +│ └── values/ +├── apps/ +│ ├── python/ +│ │ ├── common/ +│ │ ├── app1/ +│ │ └── batch/ +│ ├── php/api/ +│ ├── web/ +│ └── ios/MyAwesomeApp/ +└── tests/ (共通E2Eなど全体テストが必要な場合のみ) +``` + +--- + +## 18. 事故発生時の即時チェック +- RULE: `ModuleNotFoundError: common` → PYTHONPATH が通っているか確認(devcontainer: `/workspaces//apps/python`, compose: `/app`)。entrypoint で export する。 +- RULE: `pytest` 探索が遅い → `pytest.ini` の `testpaths` を固定。 +- RULE: CI でのみ落ちる → CI も `scripts/test-python.sh` を呼んでいるか確認し、同一 PYTHONPATH に揃える。 +- RULE: credentials パスずれ → `.env` と compose の volume / env_file の整合を確認。 + +--- + +## 19. 用語・プロファイル命名規約 +- RULE: compose profiles は `local`(開発全部入り)、`cloud`(リリース対象)、`batch`(バッチ専用)、必要なら `ops` を使う。 +- RULE: ラベル/チェック名/ジョブ名には領域を含める(例: `deps-python`, `area-app1`, `python-ci`, `php-api-ci`)。 + +--- + +## 20. 参考コマンド集(再掲) +```bash +# 起動 +docker compose -f docker-compose.base.yml -f docker-compose.dev.yml --profile local up -d +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile batch up batch + +# テスト +export PYTHONPATH="$PWD/apps/python" +pytest -v -m "not integration" + +# devcontainer での PYTHONPATH 固定 +echo 'export PYTHONPATH=/workspaces//apps/python' >> ~/.bashrc +``` + +--- + +## 21. セキュリティ補足 +- RULE: secrets/credentials をログに出さない。CI ではマスクする。 +- RULE: `.env.prod` に機密を直書きしない。必要なら別配布 or CI 注入に切り替える。 +- RULE: healthcheck で早期検知しつつ、deploy スクリプトで監査ログ(日時/ユーザ/SHA)を出力する。 + +--- + +## 22. 変更ポリシー +- RULE: 本書を更新する場合は「重複排除・MUST/SHOULD 整理・タグ付け」を維持し、include 構造を壊さないこと。 +- RULE: 構成変更は plan(`80-templates/implementation-plan.md`)で合意してから実施する。 diff --git a/.github/copilot/05-structure/monorepo.md.backup0102.md b/.github/copilot/05-structure/monorepo.md.backup0102.md new file mode 100644 index 0000000..113e5c0 --- /dev/null +++ b/.github/copilot/05-structure/monorepo.md.backup0102.md @@ -0,0 +1,3818 @@ +# モノレポ(複数アプリ)オーケストレーション & 開発運用 ベストプラクティスまとめ + +> **注意(正直ベース)** +> このドキュメントは「このスレッド内で確認できた会話内容」を**漏れなく**整理したものです。 +> ただし、チャットUI上で省略表示(…)されている過去発言の全文までは参照できないため、**表示されている範囲を根拠に**網羅整理しています。 + +--- + +## 1. 前提・課題(スレッドで出た論点) + +- 1つのリポジトリに複数アプリが共存(モノレポ)。 +- `.env` を共通化したくて、`docker-compose` の `build.context` を `../../` のようにリポジトリルートへ寄せていた。 +- アプリ数増加により **相対パス地獄(../apps/app1 等)** と管理難が発生。 +- 環境ごとに `docker-compose.*.yml` を分けたため、Dockerfileパスも相対で崩れやすい。 +- クラウドリリースで「アプリセット(起動対象の組み合わせ)」が必要。 +- ベースとなるDockerイメージは同じ(言語/ランタイム共通)だが、各アプリは別起動。 +- `CMD` 直で python 実行ではなく、**シェル(entrypoint.sh)経由で python 実行**している。 +- app1 と batch などで **共通コード(common)** が必要。 +- `.devcontainer` の位置づけ(開発環境は本番と別)と、devcontainer内での起動手順。 +- CI/CD で全アプリのテストをまとめて実行したい(pytest)。 +- Python 以外に PHP / iOS(iPhoneアプリ)も同じリポジトリに共存できるか。 + +--- + +## 2. 結論(スレッドのベストプラクティス要約) + +### 2.1 パス地獄を消す基本戦略 +- `docker-compose` を環境別に置き散らさず、**リポジトリルートに集約**する。 +- `docker-compose.base.yml`(共通) + `docker-compose.dev.yml / stg / prod`(差分)で **オーバーレイ**運用。 +- `build.context` と `dockerfile` の相対パスは **常にリポジトリルート基準**に統一する。 +- `.env` は「ビルド時に混ぜる」のではなく、**起動時(compose の env_file)で渡す**。 + +### 2.2 クラウドリリース用「アプリセット」の扱い +- 「環境(dev/stg/prod)」と「アプリセット(local/cloud/batch など)」は別軸。 +- `profiles` を使うと、**同じcomposeから起動セットを切り替え**できる。 +- 代替として「アプリセット用オーバーレイ compose」を追加する方法もある。 + +### 2.3 “同じベースイメージ” を複数アプリで共有する方法 +- **太い Dockerfile のコピペはNG**。 +- 推奨は「共通ベースイメージ(1つ)」 + 「各アプリは薄い Dockerfile(FROM base + COPY + ENTRYPOINT)」。 +- もしコード自体が同一で設定や起動引数だけ違うなら、**イメージを1つにして service を分ける**選択も可能。 + +### 2.4 common(共通コード)の扱い +- Dockerの制約:`COPY` は **build.context 外を参照できない**。 +- そのため、commonを利用するなら `build.context` は **common を含む階層**にする(例:`./apps/python`)。 +- devcontainerで `cd apps/app1` して直接実行する場合でも、`PYTHONPATH` を上位(`apps/python` 等)に通して common import を成立させる。 + +### 2.5 devcontainer の位置づけ +- `.devcontainer` は “開発用サービス/環境” としては同列に見てもよいが、 +- **本番アプリのDockerイメージとは完全に分離**する(dev専用ツールで本番イメージを汚さない)。 +- devcontainer の “ビルドコンテキスト” と “ワークスペース(コードが見える場所)” は別: + - ビルドコンテキスト:`.devcontainer/` + - ワークスペース:`/workspaces/` にリポジトリ全体がマウントされる想定 + +### 2.6 テスト(pytest)の実行方針 +- CI/CD で全アプリのテストを回したいなら、**プロジェクトルートから pytest** でOK。 +- ただし `PYTHONPATH` を揃える(例:`export PYTHONPATH="$PWD/apps/python"`)。 +- `tests/` 配置や `test_*.py` 規約を統一し、pytest の自動検出に乗せる。 + +### 2.7 Python以外(PHP / iOS)共存 +- 共存は可能。言語ごとに “世界” を分ける: + - `apps/python/...` + - `apps/php/...` + - `apps/ios/...` +- 共通コードは言語ごとに分ける(Pythonのcommon / PHPのcommon)。 +- iOSは通常Dockerで回さず、macOS(Xcode)でビルド・テスト(CIはmacOS runner等)。 + +--- + +## 3. 推奨フォルダ構成(このスレッド統合版) + +### 3.1 リポジトリ全体(Python + 共通 + devcontainer + compose + env) +```text +repo-root/ +├── .env +├── .env.dev +├── .env.stg +├── .env.prod +│ +├── docker-compose.base.yml +├── docker-compose.dev.yml +├── docker-compose.stg.yml +├── docker-compose.prod.yml +│ +├── docker/ +│ └── base/ +│ └── Dockerfile # 本番アプリ共通ベースイメージ +│ +├── apps/ +│ ├── python/ +│ │ ├── common/ # ★ Python共通コード +│ │ │ ├── __init__.py +│ │ │ ├── mylib/ +│ │ │ │ ├── __init__.py +│ │ │ │ ├── util.py +│ │ │ │ └── validators.py +│ │ │ └── tests/ +│ │ │ ├── test_util.py +│ │ │ └── test_validators.py +│ │ │ +│ │ ├── app1/ +│ │ │ ├── Dockerfile # 薄い(FROM base) +│ │ │ ├── entrypoint.sh # シェル経由で python 実行 +│ │ │ ├── main_app1.py +│ │ │ └── tests/ +│ │ │ ├── test_app1_main.py +│ │ │ └── test_app1_api.py +│ │ │ +│ │ └── batch/ +│ │ ├── Dockerfile # 薄い(FROM base) +│ │ ├── entrypoint.sh +│ │ ├── main_batch.py +│ │ └── tests/ +│ │ └── test_batch.py +│ │ +│ ├── php/ # (必要なら共存) +│ │ ├── api/ +│ │ │ ├── Dockerfile +│ │ │ ├── composer.json +│ │ │ └── src/... +│ │ └── common/ +│ │ └── src/... +│ │ +│ └── ios/ # (必要なら共存) +│ └── MyAwesomeApp/ +│ ├── MyAwesomeApp.xcodeproj +│ ├── Sources/... +│ └── Tests/... +│ +└── .devcontainer/ + ├── Dockerfile # 開発専用(本番とは別) + └── devcontainer.json +``` + +--- + +## 4. Dockerfile 設計(共通ベース + 薄い各アプリ) + +### 4.1 共通ベースイメージ(例) +- 本番アプリ共通の依存(言語ランタイム、最低限のOSパッケージ)だけを入れる。 +- 開発専用ツール(zsh, vim, debugツール等)は入れない(devcontainer側へ)。 + +```dockerfile +# docker/base/Dockerfile +FROM python:3.12-slim + +RUN apt-get update && apt-get install -y \ + bash \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +ENV PYTHONUNBUFFERED=1 +# common を含む import を安定させる(運用で統一) +ENV PYTHONPATH="/app:${PYTHONPATH}" +``` + +### 4.2 app1 の薄いDockerfile(entrypoint.sh で起動) +- **common をCOPYできるように build.context を `./apps/python` にする**前提。 + +```dockerfile +# apps/python/app1/Dockerfile +FROM myorg/app-base:1.0 + +WORKDIR /app +COPY common/ ./common/ +COPY app1/ ./app1/ + +RUN chmod +x ./app1/entrypoint.sh +ENTRYPOINT ["./app1/entrypoint.sh"] +``` + +### 4.3 batch の薄いDockerfile(同様) +```dockerfile +# apps/python/batch/Dockerfile +FROM myorg/app-base:1.0 + +WORKDIR /app +COPY common/ ./common/ +COPY batch/ ./batch/ + +RUN chmod +x ./batch/entrypoint.sh +ENTRYPOINT ["./batch/entrypoint.sh"] +``` + +### 4.4 entrypoint.sh(CMD直叩きではなくシェル経由) +- 前処理(環境変数チェック、ログ初期化、マイグレーション等)をここに寄せられる。 +- `exec` を使ってPID1のシグナル挙動を素直にする。 + +```bash +#!/usr/bin/env bash +set -euo pipefail + +echo "Starting app1..." +# 例:必要ならここで export / バリデーション / 初期化 +# export PYTHONPATH="/app:${PYTHONPATH}" + +exec python -m app1.main_app1 "$@" +``` + +--- + +## 5. docker-compose(base + env差分 + profiles) + +### 5.1 docker-compose.base.yml(共通、buildはここに集約) +- `build.context` を **common を含む階層**に固定(例:`./apps/python`)。 +- `profiles` で **起動セット(local / cloud / batch)** を切り替え可能。 + +```yaml +version: "3.9" + +services: + app1: + profiles: ["local", "cloud"] + build: + context: ./apps/python + dockerfile: ./app1/Dockerfile + env_file: + - .env + restart: unless-stopped + + batch: + profiles: ["local", "batch"] + build: + context: ./apps/python + dockerfile: ./batch/Dockerfile + env_file: + - .env + restart: unless-stopped +``` + +### 5.2 docker-compose.dev.yml(開発差分) +- 開発はマウント(必要な場合のみ)。本番はマウントしない。 +- ここでは例として python ソースを `/app` にマウント。 + +```yaml +services: + app1: + env_file: + - .env + - .env.dev + volumes: + - ./apps/python:/app + + batch: + env_file: + - .env + - .env.dev + volumes: + - ./apps/python:/app +``` + +### 5.3 docker-compose.prod.yml(本番差分) +- volume を付けない(イメージ固定運用)。 +- 必要なら replicas などはクラウド基盤側へ(composeのdeployは環境による)。 + +```yaml +services: + app1: + env_file: + - .env + - .env.prod + + batch: + env_file: + - .env + - .env.prod +``` + +### 5.4 起動コマンド例(環境×アプリセット) +- ローカル(全部入り) +```bash +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.dev.yml \ + --profile local up -d +``` + +- クラウドリリース(app1のみ想定) +```bash +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile cloud up -d +``` + +- バッチだけ +```bash +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile batch up batch +``` + +--- + +## 6. devcontainer 運用(ビルドとワークスペースの分離) + +### 6.1 devcontainer の基本方針 +- `.devcontainer/` は **開発専用**(本番イメージとは別)。 +- コードは `/workspaces/` にマウントされる前提で、そこから compose を叩ける。 + +### 6.2 common を devcontainer で直接実行(cdしてもimportが壊れない) +- `cd apps/python/app1` して実行する場合、`PYTHONPATH` を上位に通す。 + +例:devcontainer.json で固定(概念例) +```json +{ + "containerEnv": { + "PYTHONPATH": "/workspaces/${localWorkspaceFolderBasename}/apps/python" + } +} +``` + +--- + +## 7. テスト運用(pytestをルートから一括) + +### 7.1 ルートからpytestで全アプリを回す +- 原則:**プロジェクトルートで `pytest`**。 +- 条件:`tests/` もしくは `test_*.py` 規約で配置されていること。 + +```bash +cd repo-root +export PYTHONPATH="$PWD/apps/python" +pytest -v +``` + +### 7.2 CI/CD(概念) +- CIも devcontainer も同じ思想で `PYTHONPATH` を統一するのが楽。 + +--- + +## 8. Python以外(PHP / iOS)共存ガイド(スレッド結論) +- 同一リポジトリに共存可能。 +- 言語ごとに分けて管理(`apps/python`, `apps/php`, `apps/ios`)。 +- 共通コードは言語ごとに別管理(Python common / PHP common)。 +- iOSは通常Docker化しない(macOS/Xcode運用・CIはmacOS runner等)。 + +--- + +## 9. 禁止事項・アンチパターン(スレッドで強調) +- `build.context: ../..` でリポジトリ全体を投げる(相対パス地獄 & ビルド肥大化)。 +- `.env` をビルド時に無理やり参照させる設計(起動時に渡すのが基本)。 +- 本番イメージに devcontainer 用の開発ツールを混ぜる(本番が汚れる)。 +- 太い Dockerfile を各アプリへコピペ(変更箇所が爆発する)。 +- common をコンテキスト外に置いて `COPY ../common` しようとする(Docker的に不可)。 + +--- + +## 10. すぐ使える「最小ルール」チェックリスト +- [ ] compose はルートに集約し、base + env差分でオーバーレイ運用 +- [ ] build.context は common を含む階層(例:`./apps/python`)に固定 +- [ ] `.env` は env_file で渡す(ビルドに混ぜない) +- [ ] ベースイメージは1つ、各アプリDockerfileは薄く(FROM + COPY + ENTRYPOINT) +- [ ] entrypoint.sh は `exec python ...` で起動 +- [ ] devcontainer は本番と分離、workspace は repo-root +- [ ] devcontainer直実行でも `PYTHONPATH` 統一で common import を成立 +- [ ] CI/CD は repo-root で pytest、`PYTHONPATH=$PWD/apps/python` を統一 + +--- +## 11. 追加ベストプラクティス(運用で詰まらないための“補強”) + +> このセクションは、このスレッドで出た内容を運用に落とす際に「必ず必要になる」補助ルールを、漏れなく具体化したもの。 + +--- + +## 11.1 `.dockerignore` を必ず置く(ビルド肥大化・キャッシュ崩壊を防ぐ) + +- `build.context` を `./apps/python` に固定する場合、**apps配下の不要物を除外**しないとビルドが遅くなる。 +- 典型的に除外すべきもの:`__pycache__`, `.pytest_cache`, `.venv`, `.mypy_cache`, `dist`, `build`, `node_modules`, `.DS_Store` など。 + +例(`repo-root/.dockerignore`): + +```text +# Python caches +**/__pycache__/ +**/*.pyc +**/*.pyo +**/*.pyd +**/.pytest_cache/ +**/.mypy_cache/ +**/.ruff_cache/ + +# Virtualenvs +**/.venv/ +**/venv/ + +# Build artifacts +**/dist/ +**/build/ +**/*.egg-info/ + +# OS/editor +**/.DS_Store +**/.idea/ +**/.vscode/ + +# Logs +**/*.log +``` + +--- + +## 11.2 `pytest.ini`(または `pyproject.toml`)でテスト探索・パスを固定 + +- “ルートからpytest一発” を安定化させるため、pytest設定を置く。 +- `testpaths` を明示すると、意図しない探索(巨大ディレクトリ)を避けられる。 + +例(`repo-root/pytest.ini`): + +```ini +[pytest] +minversion = 7.0 +addopts = -ra -q +testpaths = + apps/python +python_files = + test_*.py + *_test.py +markers = + slow: slow tests + integration: integration tests +``` + +--- + +## 11.3 `PYTHONPATH` の統一ルール(devcontainer/CI/ローカル/コンテナを揃える) + +### 方針 +- **`apps/python` を import ルートとして統一**する。 +- つまり `common` を `apps/python/common` に置くなら、以下が常に成立する: + - `from common.mylib import util` + +### ルール +- ルート実行(CI/ローカル): + - `PYTHONPATH=$PWD/apps/python` +- devcontainer: + - `PYTHONPATH=/workspaces//apps/python` +- 本番イメージ(コンテナ内): + - `/app` に `apps/python` を配置しているなら `PYTHONPATH=/app` + +--- + +## 12. devcontainer の“実体”テンプレート(このスレッド版) + +### 12.1 `.devcontainer/devcontainer.json`(概念を具体化) + +- 重要:devcontainerは「開発ツール」を詰め込む場所。本番イメージと分離。 +- ワークスペースはリポジトリルートがマウントされる想定。 + +例: + +```json +{ + "name": "mono-repo-dev", + "build": { + "dockerfile": "Dockerfile", + "context": "." + }, + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "containerEnv": { + "PYTHONPATH": "/workspaces/${localWorkspaceFolderBasename}/apps/python" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + } +} +``` + +### 12.2 `.devcontainer/Dockerfile`(開発専用) + +例(必要な開発ツールを入れる): + +```dockerfile +FROM mcr.microsoft.com/devcontainers/base:ubuntu + +RUN apt-get update && apt-get install -y \ + git curl bash \ + python3 python3-pip \ + && rm -rf /var/lib/apt/lists/* +``` + +--- + +## 13. “環境別起動”と“devcontainer起動”の手順をズラさないルール + +### 13.1 共通ルール +- **起動コマンドは常に repo-root から叩く**(ホストでも devcontainer内でも同じ)。 +- “各アプリへ cd して直接起動” は **例外(デバッグ用途)** として扱う。 + +### 13.2 起動コマンド(統一) + +- dev(ローカル/開発): + - `docker compose -f docker-compose.base.yml -f docker-compose.dev.yml --profile local up -d` + +- prod(クラウド/本番想定): + - `docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d` + +- batchだけ: + - `docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile batch up batch` + +### 13.3 例外:各アプリ直起動(デバッグ) +- 直起動の条件: + - `PYTHONPATH` が上位に通っていること(devcontainerのcontainerEnv等) +- 実行例: + - `cd apps/python/app1 && ./entrypoint.sh` + - `cd apps/python/batch && ./entrypoint.sh` + +--- + +## 14. “common” を壊さないための厳格ルール(運用規約化推奨) + +### 14.1 commonディレクトリの約束 +- `apps/python/common/__init__.py` は必須(パッケージ認識のため) +- `common/` 配下は“アプリ横断で利用されるものだけ”を置く + (アプリ固有のコードは入れない) + +例: + +```text +apps/python/common/ +├── __init__.py +└── mylib/ + ├── __init__.py + ├── util.py + └── validators.py +``` + +### 14.2 importは相対ではなく絶対に固定 +- OK:`from common.mylib import util` +- NG:`from ..common...`(実行位置やpytest探索で壊れやすい) + +--- + +## 15. CI/CD(GitHub Actions)最小サンプル(Python全アプリpytest一括) + +> このスレッドの結論:CI/CDで “ルートからpytest一発” を成立させる。 + +例(`.github/workflows/python-tests.yml`): + +```yaml +name: Python Tests + +on: + push: + branches: [ "main" ] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install deps + run: | + python -m pip install --upgrade pip + # 例:requirementsがある場合 + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + # 例:pytestを保証 + pip install pytest + + - name: Run tests (all apps) + run: | + export PYTHONPATH="$PWD/apps/python" + pytest -v +``` + +--- + +## 16. PHP / iOS を同居させる場合のCIの分割(共存はOK、実行環境を分ける) + +### 16.1 PHP(例:Laravel)テストジョブの概念 +- Pythonとは別ジョブに分ける(依存が違うため) +- 例コマンド: + - `cd apps/php/api && composer install && php artisan test` + +### 16.2 iOSテストはmacOS runnerが必要 +- 例コマンド(概念): + - `cd apps/ios/MyAwesomeApp && xcodebuild test ...` + +> ※このスレッドの結論:iOSは通常Docker化しない(macOS/Xcode運用)。 + +--- + +## 17. よくある失敗パターン(即死を避けるチェック) + +### 17.1 “commonがCOPYできない” +- 原因:`build.context` が `common` を含んでいない +- 解決:`build.context` を `./apps/python` のように “commonを含む階層” に固定する + +### 17.2 “devcontainerでcdして実行したらcommon importが壊れる” +- 原因:`PYTHONPATH` 未設定 +- 解決:devcontainerの `containerEnv.PYTHONPATH` を `/workspaces//apps/python` に固定 + +### 17.3 “pytestが意図しない場所まで探索して遅い” +- 原因:pytestの探索範囲が広すぎる +- 解決:`pytest.ini` の `testpaths` を固定 + +--- + +## 18. 最終:Wiki掲載向け “短い運用宣言(コピペ用)” + +> チームルールとして冒頭に貼る用途(このスレッドの決定事項を短文化)。 + +```text +【運用宣言】 +1) docker-composeはリポジトリルートに集約し、base + 環境差分でオーバーレイ運用する。 +2) build.contextはcommonを含む階層(例:./apps/python)に固定し、../ 地獄を禁止する。 +3) .envは起動時にenv_fileで渡す(ビルドに混ぜない)。 +4) Dockerfileは「共通ベース1つ + 各アプリ薄いDockerfile」を原則とし、太いDockerfileのコピペを禁止する。 +5) アプリ起動は原則 repo-root から docker compose を叩く。cdして直起動はデバッグ用途に限定する。 +6) commonは apps/python/common に置き、PYTHONPATH を apps/python に統一して import を安定化する。 +7) CIは repo-root から pytest を実行し、PYTHONPATH=$PWD/apps/python を必ず設定する。 +8) devcontainerは本番イメージと分離し、開発ツールはdevcontainer側にのみ導入する。 +9) 多言語共存(PHP/iOS)は可能。言語ごとに apps// で世界を分け、CIもジョブを分ける。 +``` + +--- +## 19. 実運用で必須になる “補助ファイル” 一式(手順ブレ防止) + +> このスレッドの結論を「誰が叩いても同じ」状態にするための補助ファイル群。 +> **起動手順・環境選択・テスト実行の属人化を潰す**のが目的。 + +--- + +## 19.1 Makefile(起動・停止・テストをコマンド1つに固定) + +- ルートから叩くルールを強制できる +- devcontainer/ホストどちらでも同じ + +例(`repo-root/Makefile`): + +```make +.PHONY: up-dev up-stg up-prod down ps logs test + +COMPOSE_BASE=-f docker-compose.base.yml +COMPOSE_DEV=$(COMPOSE_BASE) -f docker-compose.dev.yml +COMPOSE_STG=$(COMPOSE_BASE) -f docker-compose.stg.yml +COMPOSE_PROD=$(COMPOSE_BASE) -f docker-compose.prod.yml + +up-dev: + docker compose $(COMPOSE_DEV) --profile local up -d + +up-stg: + docker compose $(COMPOSE_STG) --profile cloud up -d + +up-prod: + docker compose $(COMPOSE_PROD) --profile cloud up -d + +down: + docker compose $(COMPOSE_BASE) down + +ps: + docker compose $(COMPOSE_BASE) ps + +logs: + docker compose $(COMPOSE_BASE) logs -f --tail=200 + +test: + PYTHONPATH=$$(pwd)/apps/python pytest -v +``` + +--- + +## 19.2 scripts/(CIとローカルの同一化:テストや起動をスクリプト化) + +- CIでもローカルでも同じスクリプトを叩くと事故が減る + +例: + +```text +repo-root/ +└── scripts/ + ├── test-python.sh + ├── up-dev.sh + └── up-prod.sh +``` + +例(`scripts/test-python.sh`): + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." +export PYTHONPATH="$PWD/apps/python" +pytest -v +``` + +--- + +## 20. docker-compose 設計の追加ルール(事故りやすい所を固定) + +--- + +## 20.1 profile 命名規約(混乱を防ぐ) +- `local`:開発で全部入り(監視/DB含めてもよい) +- `cloud`:クラウドリリースに含めるアプリセット +- `batch`:バッチ系だけ +- 追加するなら `ops`(監視系)など、意味で揃える + +--- + +## 20.2 env_file の積み方(必ず同じ順) +- **必ず共通 → 環境固有の順**にする(後勝ちが自然) + +例(dev): + +```yaml +env_file: + - .env + - .env.dev +``` + +--- + +## 20.3 secrets の扱い(.envに全部入れない) +- `.env` は「ローカル・開発向けの利便性」で、**本番はSecret管理が本筋** +- Docker Composeだけで完結させるなら: + - `.env.prod` に機密を書かない(別配布) + - もしくは環境変数をCIから注入する + +--- + +## 20.4 healthcheck(起動確認が必要なサービスには付ける) +- API等は起動順/依存関係でこけやすいので付ける + +例: + +```yaml +services: + app1: + healthcheck: + test: ["CMD", "bash", "-lc", "curl -fsS http://localhost:8080/health || exit 1"] + interval: 10s + timeout: 3s + retries: 10 +``` + +--- + +## 21. entrypoint.sh の“運用テンプレ”(本番でもデバッグでも壊れない) + +> このスレッドの前提:CMD直叩きではなく **シェル越しにpython起動**。 + +### 21.1 共有テンプレ(最小) +- `set -euo pipefail` +- `exec` でpythonへ置き換え(シグナル伝搬) + +例: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# 例:共通の事前チェック(必要なら) +: "${APP_ENV:=local}" + +# 例:import安定化(必要なら) +export PYTHONPATH="/app:${PYTHONPATH:-}" + +echo "[entrypoint] APP_ENV=${APP_ENV}" +exec python "$@" +``` + +### 21.2 アプリ用(python -m で固定して “実行場所依存” を消す) +- `python -m app1.main_app1` の形式に寄せると安定する + +例(app1): + +```bash +#!/usr/bin/env bash +set -euo pipefail +export PYTHONPATH="/app:${PYTHONPATH:-}" +exec python -m app1.main_app1 "$@" +``` + +--- + +## 22. “common” をさらに堅牢にする選択肢(規模拡大に備える) + +> スレッドの結論は「PYTHONPATHで common を見せる」。 +> それを崩さず、規模が増えた時に選べる拡張案を列挙(運用に必要)。 + +--- + +## 22.1 common を「Pythonパッケージ」として配布可能にする(発展) +- `apps/python/common` を `pyproject.toml` / `setup.cfg` 等でパッケージ化 +- 各アプリは `pip install -e` で参照する(開発しやすい) + +例(概念): + +```text +apps/python/common/ +├── pyproject.toml +└── common/ + ├── __init__.py + └── mylib/... +``` + +> ただし、このスレッドの標準解は **PYTHONPATH統一**。 +> パッケージ化は「さらに大きくなったら」選ぶ。 + +--- + +## 22.2 common を “schema/” に寄せる(言語跨ぎ共有の王道) +- Python/PHP/iOSの共通は「コード」ではなく「仕様」を共有するのが安全 +- OpenAPI / JSON Schema / Protobuf を `schema/` へ + +例: + +```text +repo-root/ +└── schema/ + ├── openapi.yaml + └── events.proto +``` + +--- + +## 23. 多言語共存の実戦ルール(Python + PHP + iOS) + +> ここはユーザー質問「Python以外も共存できるか?」に対するスレッド結論を運用ルールに落としたもの。 + +--- + +## 23.1 ディレクトリ分離(言語ごとの世界) +- `apps/python/*` +- `apps/php/*` +- `apps/ios/*` + +### 23.2 CIジョブ分割(依存が違うので混ぜない) +- Python:ubuntu runnerで pytest +- PHP:ubuntu runnerで composer + phpunit/laravel test +- iOS:macOS runnerで xcodebuild + +--- + +## 24. Wikiに載せる “標準手順”(最終的にここだけ見れば動く) + +> ここを**Wikiの冒頭**に貼るのが推奨。 + +--- + +## 24.1 ローカル開発(ホスト or devcontainer) +1) repo-root に移動 +2) dev起動 +3) ログ確認 +4) テスト + +```bash +cd repo-root +docker compose -f docker-compose.base.yml -f docker-compose.dev.yml --profile local up -d +docker compose -f docker-compose.base.yml logs -f --tail=200 +PYTHONPATH=$PWD/apps/python pytest -v +``` + +--- + +## 24.2 本番相当(クラウドリリースセット) +- cloud profile を使う(= リリース対象アプリだけ起動) + +```bash +cd repo-root +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d +docker compose -f docker-compose.base.yml ps +``` + +--- + +## 24.3 バッチだけ +```bash +cd repo-root +docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile batch up batch +``` + +--- + +## 25. 最終チェック(このスレッドの結論が守れているか) + +- [ ] composeはルートに集約されている(環境別は *ファイル* 分割、*場所* は分散しない) +- [ ] `build.context` は `common` を含む階層(例:`./apps/python`)に固定されている +- [ ] `.env` は env_file で渡している(ビルドに混ぜない) +- [ ] ベースイメージは1つ、各アプリDockerfileは薄い(コピペ地獄禁止) +- [ ] entrypoint.sh は `exec python ...` で起動(シグナル/終了コード健全) +- [ ] devcontainer は本番と分離されている(開発ツールはdevcontainer側) +- [ ] devcontainerで `cd apps/...` しても `PYTHONPATH` 統一で common import が壊れない +- [ ] CI/CD はルートで pytest(`PYTHONPATH=$PWD/apps/python` を統一) +- [ ] PHP/iOSは同居OKだが、世界とCIジョブは分けている + +--- +## 26. モノレポ(複数アプリ混在)での `dependabot.yml` 運用(ベストプラクティス) + +> 前提:Python / PHP / Node / GitHub Actions など複数の「エコシステム」が同一リポジトリに混在し、 +> `apps/` 配下に複数アプリ、`.devcontainer/` や `.github/workflows/` も存在する構成を想定。 + +--- + +### 26.1 基本方針(ルールを先に固定する) +1) **Dependabotの設定は「ルート1箇所」**(`.github/dependabot.yml`)に集約 +2) **“言語/パッケージ管理単位” ごとに `updates` を分ける** +3) `directory` は **依存管理ファイル(requirements.txt / pyproject.toml / composer.json / package.json)がある場所**を正確に指定 +4) **環境ごとの依存がある場合**(dev/prodでファイルが違う等)は、ファイルがあるディレクトリ単位で分ける +5) PRが増えすぎると運用が崩壊するため、**グルーピング**と**PR上限**(`open-pull-requests-limit`)を必ず使う +6) “全アプリ混在” の画面(PR一覧・Dependabot PR乱立)を前提に、**ラベル・担当者・レビュー**の自動付与で流量を制御する +7) **Security updates(脆弱性対応)は別枠**で優先(ラベルで目立たせる、通常より短い周期など) + +--- + +### 26.2 ディレクトリ設計の前提(Dependabotが迷わない置き方) +Dependabotは `directory` を起点に依存ファイルを探します。 +よって、依存ファイルが散らばるほど `updates` の定義数が増えます。 + +推奨(例): +- Python: + - `apps/python/app1/pyproject.toml`(Poetry) + - `apps/python/batch/pyproject.toml`(Poetry) + - `apps/python/requirements.txt`(共通requirements方式の場合) +- PHP: + - `apps/php/api/composer.json` +- Node: + - `apps/web/package.json` +- GitHub Actions: + - `.github/workflows/*.yml` +- DevContainer: + - `.devcontainer/`(依存更新の対象にするかは運用判断:基本は“やりすぎ注意”) + +--- + +### 26.3 運用で必須の設計(PR洪水を防ぐ) +#### A) グルーピング(推奨) +- `groups` を使い、**同種依存をまとめて1PR**にする +- 例:Pythonなら `pytest/ruff/mypy` 等の開発依存、`fastapi/requests` 等のランタイム依存で分ける + +#### B) PR上限(必須) +- `open-pull-requests-limit` を小さめに(例:5〜10) +- これがないと複数アプリ×複数エコシステムでPRが爆発します + +#### C) スケジュール +- 通常:週1(`weekly`)で十分 +- Security:`daily` でも良い(通知・優先度が上がる) + +#### D) 自動ラベル/担当付け +- `labels` で言語/領域を付与(例:`dependencies`, `deps:python`, `deps:php`) +- `reviewers` / `assignees` を付けて、混在画面でも処理漏れを防ぐ + +--- + +### 26.4 推奨 `.github/dependabot.yml` サンプル(モノレポ混在対応) + +> 例:Python(Poetry)×2アプリ、PHP(Composer)×1、Node(npm)×1、GitHub Actions を更新 +> ※ `directory` はあなたのリポジトリの実パスに合わせて修正してください。 + +```yaml +version: 2 + +updates: + # --- GitHub Actions(CI/CD) --- + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "03:00" + timezone: "Asia/Tokyo" + labels: + - "dependencies" + - "deps:actions" + open-pull-requests-limit: 5 + groups: + actions: + patterns: + - "*" + + # --- Python / app1(Poetry) --- + - package-ecosystem: "pip" + directory: "/apps/python/app1" + schedule: + interval: "weekly" + day: "monday" + time: "03:10" + timezone: "Asia/Tokyo" + labels: + - "dependencies" + - "deps:python" + - "area:app1" + reviewers: + - "YOUR_GITHUB_USERNAME" + open-pull-requests-limit: 5 + groups: + py-devtools-app1: + patterns: + - "pytest*" + - "ruff" + - "mypy" + - "black" + - "isort" + py-runtime-app1: + patterns: + - "*" + exclude-patterns: + - "pytest*" + - "ruff" + - "mypy" + - "black" + - "isort" + + # --- Python / batch(Poetry) --- + - package-ecosystem: "pip" + directory: "/apps/python/batch" + schedule: + interval: "weekly" + day: "monday" + time: "03:20" + timezone: "Asia/Tokyo" + labels: + - "dependencies" + - "deps:python" + - "area:batch" + reviewers: + - "YOUR_GITHUB_USERNAME" + open-pull-requests-limit: 5 + groups: + py-devtools-batch: + patterns: + - "pytest*" + - "ruff" + - "mypy" + - "black" + - "isort" + py-runtime-batch: + patterns: + - "*" + exclude-patterns: + - "pytest*" + - "ruff" + - "mypy" + - "black" + - "isort" + + # --- PHP / api(Composer) --- + - package-ecosystem: "composer" + directory: "/apps/php/api" + schedule: + interval: "weekly" + day: "tuesday" + time: "03:00" + timezone: "Asia/Tokyo" + labels: + - "dependencies" + - "deps:php" + - "area:php-api" + reviewers: + - "YOUR_GITHUB_USERNAME" + open-pull-requests-limit: 5 + groups: + php-all: + patterns: + - "*" + + # --- Node / web(npm) --- + - package-ecosystem: "npm" + directory: "/apps/web" + schedule: + interval: "weekly" + day: "wednesday" + time: "03:00" + timezone: "Asia/Tokyo" + labels: + - "dependencies" + - "deps:node" + - "area:web" + reviewers: + - "YOUR_GITHUB_USERNAME" + open-pull-requests-limit: 5 + groups: + node-dev: + dependency-type: "development" + patterns: + - "*" + node-prod: + dependency-type: "production" + patterns: + - "*" +``` + +--- + +### 26.5 “混在画面” で事故を起こさない運用フロー(必須) +#### 1) PRタイトル/ラベルで瞬時に判別できる状態にする +- `deps:python` / `deps:php` / `deps:node` / `deps:actions` +- `area:app1` / `area:batch` のような“アプリ領域ラベル”を強制 + +#### 2) CIが落ちたDependabot PRは「原因の型」が決まっている +- Python:テスト/型/フォーマットの破壊(pytest, ruff, mypy) +- PHP:PHPバージョンや拡張、composer.lock 更新差分 +- Node:lockfile差分、node version、breaking change + +→ 対処は「アプリ単位で」行う必要があるため、`area:*` ラベルが重要 + +#### 3) PRが多い場合は “weekly + groups + limit” を守る +- 週1で十分(Securityは別) +- groupでまとめる +- limitで天井を作る +これがないと混在画面で確実に漏れます + +--- + +### 26.6 Security updates(脆弱性)を最優先にする(推奨) +Dependabotの“アラート起点のPR(Security updates)”を運用で目立たせます。 + +- ルール例: + - `dependencies` + `security` ラベル(運用で付与) + - 通常PRより優先レビュー(SLAを短く) + +※ `dependabot.yml` 側で完全に“securityだけ別設定”は制約があるため、 +運用ルール(ラベル/優先度/ブランチ保護)で担保するのが現実的です。 + +--- + +### 26.7 よくある落とし穴(モノレポ混在特有) +1) **directory の指定ミス**:依存ファイルがないと更新されない +2) **共通requirements/lockがルートにあるのにアプリ配下を見ている**:どちらを正にするか決める +3) **アプリごとにPythonバージョンが違う**:CIでマトリクス化しないとPRが常に落ちる +4) **PR上限なし**:混在画面で破綻する +5) **groupなし**:PRが細粒度になりすぎて処理できない + +--- + +### 26.8 最小運用テンプレ(Wiki貼り付け用) +- 週1更新(Actions/Python/PHP/Node) +- PR上限5 +- groupsあり +- ラベル/領域ラベルで混在画面でも漏れない + +運用宣言(短文化): + +```text +【Dependabot運用】 +1) dependabot.ymlはルート1箇所に集約し、エコシステム×ディレクトリ単位でupdatesを定義する。 +2) PR洪水を防ぐため、必ず groups と open-pull-requests-limit を設定する。 +3) dependencies系ラベル + area: ラベルで混在画面でも判別可能にする。 +4) Security関連は最優先でレビューし、通常更新は週1で十分とする。 +5) directoryは依存ファイルが存在する場所を正確に指定し、更新漏れを禁止する。 +``` + +--- +## 27. Unitテスト & CI/CD 運用(モノレポ複数アプリ混在のベストプラクティス) + +> 目的:**全アプリの品質ゲートを自動化**し、PR混在でも漏れずに回る状態を作る。 +> このスレッド方針(ルートからpytest一括、profiles、common、devcontainer分離)を前提に、運用ルールを“確定仕様”として記述する。 + +--- + +## 27.1 テスト戦略の原則(最初に固定すること) + +### 27.1.1 テスト階層(必須) +- **Unit**:外部I/O無し(DB/ネットワーク/実API無し)。速い。常時実行。 +- **Integration**:DB/Redis/外部APIモック等を使う。遅い。条件付きで実行。 +- **E2E**:デプロイ後やステージングで。さらに遅い。リリース前限定。 + +### 27.1.2 PR品質ゲート(必須) +- PRに対して最低限以下を必須チェックにする: + 1) Lint(静的解析) + 2) Unit Test(全アプリ) + 3) Security(依存脆弱性 / SASTなど) +- Integration/E2Eは「別ワークフロー」でOK(必須化は運用負荷を見て段階的に) + +--- + +## 27.2 Python(複数アプリ + common)Unitテスト運用 + +### 27.2.1 テスト配置ルール(必須) +- `apps/python//tests/` に集約 +- `apps/python/common/tests/` も許可(common自体のユニットテスト) + +例: +```text +apps/python/ +├── common/ +│ └── tests/ +├── app1/ +│ └── tests/ +└── batch/ + └── tests/ +``` + +### 27.2.2 ルートから一括実行(スレッド結論の正式化) +- CI/CD とローカルは同じ: +- `PYTHONPATH` は **apps/python** をルートに固定 + +実行: +```bash +cd repo-root +export PYTHONPATH="$PWD/apps/python" +pytest -v +``` + +### 27.2.3 pytest設定(推奨:探索範囲固定) +`repo-root/pytest.ini`: +```ini +[pytest] +testpaths = apps/python +python_files = test_*.py *_test.py +markers = + unit: unit tests + integration: integration tests +addopts = -ra +``` + +### 27.2.4 “遅い/不安定” テストの扱い(必須) +- `@pytest.mark.integration` を付けて分離 +- デフォルトは unit だけ(高速) +- integrationは夜間or手動or特定ブランチで実行 + +例: +- unitだけ: + - `pytest -m "not integration"` +- integration: + - `pytest -m "integration"` + +--- + +## 27.3 PHP(Laravel/Composer)Unitテスト運用(共存前提) + +### 27.3.1 実行単位 +- `apps/php/` がテスト単位(composer.jsonがある場所) + +例: +```bash +cd repo-root/apps/php/api +composer install --no-interaction --prefer-dist +php artisan test +# もしくは vendor/bin/phpunit +``` + +### 27.3.2 “混在画面”で迷わないルール +- ジョブ名・チェック名に必ず `php/api` のような**領域名**を含める +- ラベル `area:php-api` を使う(Dependabotとも整合) + +--- + +## 27.4 iOS(Xcode)テスト運用(Docker外:macOS runner 前提) + +### 27.4.1 実行単位 +- `apps/ios/` が単位(xcodeproj/xcworkspaceがある場所) + +例(概念): +```bash +cd repo-root/apps/ios/MyAwesomeApp +xcodebuild \ + -scheme MyAwesomeApp \ + -destination 'platform=iOS Simulator,name=iPhone 15' \ + test +``` + +### 27.4.2 注意(必須) +- iOSは通常Docker化しない(このスレッド結論) +- CIは `runs-on: macos-*` を使う(コストが上がるため、トリガ条件を絞るのが現実的) + +--- + +## 27.5 GitHub Actions(CI)運用設計:モノレポ最適解 + +### 27.5.1 ワークフロー分割(必須) +- 1ファイルに全部詰めない。混在画面で見分けづらくなる。 +- 推奨: + - `python-ci.yml` + - `php-ci.yml` + - `ios-ci.yml` + - `docker-build.yml`(任意) + - `security.yml`(任意) + +### 27.5.2 変更検知(paths)で無駄実行を減らす(強く推奨) +- `apps/python/**` 変更 → python-ciだけ +- `apps/php/**` 変更 → php-ciだけ +- `.github/workflows/**` 変更 → actions検証 + +--- + +## 27.6 サンプル:Python CI(全アプリ unit テスト一括) + +`.github/workflows/python-ci.yml`: +```yaml +name: python-ci + +on: + pull_request: + paths: + - "apps/python/**" + - "pytest.ini" + - ".github/workflows/python-ci.yml" + push: + branches: ["main"] + paths: + - "apps/python/**" + - "pytest.ini" + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "pip" + + - name: Install deps + run: | + python -m pip install --upgrade pip + # 例: requirements 方式なら + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install pytest + + - name: Unit tests (all python apps) + run: | + export PYTHONPATH="$PWD/apps/python" + pytest -v -m "not integration" +``` + +--- + +## 27.7 サンプル:Python Integration(任意:夜間/手動) + +`.github/workflows/python-integration.yml`: +```yaml +name: python-integration + +on: + workflow_dispatch: + schedule: + - cron: "0 19 * * 1-5" # JST 04:00 平日(UTC表記) + +jobs: + integration: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install deps + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install pytest + + - name: Integration tests + run: | + export PYTHONPATH="$PWD/apps/python" + pytest -v -m "integration" +``` + +--- + +## 27.8 サンプル:PHP CI(Laravel) + +`.github/workflows/php-ci.yml`: +```yaml +name: php-ci + +on: + pull_request: + paths: + - "apps/php/**" + - ".github/workflows/php-ci.yml" + push: + branches: ["main"] + paths: + - "apps/php/**" + +jobs: + php-api-tests: + runs-on: ubuntu-latest + defaults: + run: + working-directory: apps/php/api + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.3" + tools: composer + + - name: Install + run: composer install --no-interaction --prefer-dist + + - name: Test + run: php artisan test +``` + +--- + +## 27.9 サンプル:iOS CI(必要時のみ実行) + +`.github/workflows/ios-ci.yml`: +```yaml +name: ios-ci + +on: + pull_request: + paths: + - "apps/ios/**" + - ".github/workflows/ios-ci.yml" + push: + branches: ["main"] + paths: + - "apps/ios/**" + +jobs: + ios-tests: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode.app + + - name: Build & Test + working-directory: apps/ios/MyAwesomeApp + run: | + xcodebuild \ + -scheme MyAwesomeApp \ + -destination 'platform=iOS Simulator,name=iPhone 15' \ + test +``` + +--- + +## 27.10 “Dependabot PR” を落とさないための運用(必須) + +### 27.10.1 依存更新PRは「壊れて当然」前提で流量制御 +- 週1 + groups + open-pull-requests-limit(前セクション) +- CIが赤になったら: + - 原因が `area:*` で即判別できる状態にする + - 修正はそのアプリの依存定義で完結させる(common破壊禁止) + +### 27.10.2 “依存更新PRのCI時間” を短くする +- unitのみ必須化(integrationは別) +- キャッシュ活用(pip cache / composer cache / node cache) + +--- + +## 27.11 CD(デプロイ)運用:最低限の安全策(スレッド整合) + +> このスレッドは主にオーケストレーション/CI寄りだが、CDで必須の運用ルールを“省略なし”で列挙。 + +### 27.11.1 デプロイは “mainのみ” に限定(必須) +- PRはテストまで +- merge後(main)で build/push/deploy を走らせる + +### 27.11.2 イメージタグ戦略(必須) +- `latest` だけは危険。必ず不変タグを持つ: + - `sha-` + - `vX.Y.Z`(リリースタグ運用するなら) + +### 27.11.3 デプロイ対象(アプリセット)の決定 +- `profiles: cloud` が “クラウドリリースセット” の単一情報源 +- 追加/削除はPRで明示し、レビュー必須 + +--- + +## 27.12 サンプル:Docker Build(任意:mainでビルドしてpush) + +`.github/workflows/docker-build.yml`: +```yaml +name: docker-build + +on: + push: + branches: ["main"] + paths: + - "apps/python/**" + - "docker/**" + - "docker-compose.base.yml" + - ".github/workflows/docker-build.yml" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # 例: レジストリログインは環境に合わせて設定 + # - name: Login + # uses: docker/login-action@v3 + # with: + # registry: ghcr.io + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build app1 + run: | + docker build \ + -f apps/python/app1/Dockerfile \ + -t myorg/app1:sha-${{ github.sha }} \ + apps/python + + - name: Build batch + run: | + docker build \ + -f apps/python/batch/Dockerfile \ + -t myorg/batch:sha-${{ github.sha }} \ + apps/python + + # pushは必要なら追加 + # - run: docker push ... +``` + +--- + +## 27.13 ブランチ保護(必須:品質ゲートを“強制”する) + +GitHubのブランチ保護で最低限: +- `Require status checks to pass before merging` +- 必須チェック: + - `python-ci / unit` + - `php-ci / php-api-tests`(該当するなら) + - `ios-ci / ios-tests`(該当するなら) +- `Require pull request reviews before merging` +- `Dismiss stale approvals when new commits are pushed`(推奨) + +--- + +## 27.14 最終:Wiki貼り付け用 “Unitテスト & CI/CD 運用宣言”(短文化) + +```text +【Unitテスト & CI/CD運用】 +1) Unitは常時必須、Integration/E2Eは別ワークフローで条件付き実行とする。 +2) Pythonは repo-root から pytest を一括実行し、PYTHONPATH=$PWD/apps/python を統一する。 +3) PHPは apps/php/ を単位として composer + artisan/phpunit を実行する。 +4) iOSは apps/ios/ を単位として macOS runner で xcodebuild test を実行する。 +5) ワークフローは言語/領域で分割し、pathsフィルタで無駄実行を抑制する。 +6) Dependabot PRは週1 + groups + PR上限制御とし、ラベル(area/deps)で混在画面でも処理漏れを防ぐ。 +7) mainへのマージは必須チェック通過を条件とし、ブランチ保護で強制する。 +8) CDはmainのみ、イメージはshaタグ等の不変タグを必ず付与し、cloud profileがリリース対象セットの単一情報源とする。 +``` + +--- +## 28. 外部システム連携(GCP/AWS 等)の認証情報運用(`.env` に置けない JSON / RSA / PEM 対応) + +> 目的:`.env` では表現できない(または置くべきでない) +> **JSON(GCP Service Account)** / **RSA・PEM(鍵)** / **証明書** 等を、 +> モノレポ複数アプリ環境でも **漏れなく・事故なく・再現性高く**扱う。 + +--- + +## 28.1 大原則(必須ルール) +1) **認証ファイルはGitに絶対コミットしない**(事故防止の最上位ルール) +2) `.env` には「ファイルのパス」だけを書く + - 例:`GCP_SA_JSON_PATH=/run/secrets/gcp_sa.json` +3) 認証ファイルは `credentials/` 配下に置く(運用で統一) +4) `credentials/` は環境別に分ける(dev/stg/prod を混ぜない) +5) **起動時に volume / secret として注入**し、イメージ内に焼き込まない(原則) +6) devcontainer / ローカル / CI / 本番の注入方式を分けても良いが、**アプリが読む場所(パス)は統一**する + +--- + +## 28.2 推奨ディレクトリ構成(credentials の置き場所) + +> このスレッドのモノレポ構成に整合する形で提示。 + +```text +repo-root/ +├── .env +├── .env.dev +├── .env.stg +├── .env.prod +│ +├── credentials/ +│ ├── dev/ +│ │ ├── gcp/ +│ │ │ ├── service-account.json +│ │ │ └── oauth-client.json +│ │ ├── aws/ +│ │ │ ├── access_key_id.txt # 必要なら(推奨は環境変数注入) +│ │ │ ├── secret_access_key.txt +│ │ │ └── session_token.txt +│ │ └── ssh/ +│ │ ├── id_rsa +│ │ └── id_rsa.pub +│ │ +│ ├── stg/ +│ │ └── gcp/... +│ │ +│ └── prod/ +│ └── gcp/... +│ +└── credentials/README.md +``` + +### 28.2.1 `credentials/README.md` に必ず書くべきこと(必須) +- 何を置くか(ファイル名) +- どこから取得するか(手順) +- 権限(chmod) +- どの `.env.*` の変数に対応するか(パス) + +--- + +## 28.3 `.gitignore`(最重要・必須) +認証ファイルは **100% 無視**する。 + +例(`repo-root/.gitignore`): + +```text +# Credentials / secrets +credentials/** +!credentials/README.md +!credentials/**/.keep +``` + +> `.keep` を置きたい場合は例外的に許可(空ディレクトリ保持用)。 + +--- + +## 28.4 `.env` に書くのは “パスのみ”(具体例) + +### 28.4.1 共通(.env) +- 認証ファイルを読むアプリは、**このパスを参照する**だけにする +- 環境差分でファイル本体が変わっても、**パスは固定**が理想 + +例(`.env`): + +```text +# GCP +GCP_SA_JSON_PATH=/run/credentials/gcp/service-account.json + +# SSH (例) +SSH_PRIVATE_KEY_PATH=/run/credentials/ssh/id_rsa + +# AWS (例: 可能なら IAM role / OIDC を推奨) +AWS_SHARED_CREDENTIALS_FILE=/run/credentials/aws/credentials +AWS_CONFIG_FILE=/run/credentials/aws/config +``` + +### 28.4.2 環境固有(.env.dev) +- 基本はパス固定なので、ここに書くのは “on/off” や “project id” 等 + +例: + +```text +APP_ENV=dev +GCP_PROJECT_ID=my-dev-project +AWS_REGION=ap-northeast-1 +``` + +--- + +## 28.5 docker-compose で credentials を注入する(推奨:volume マウント) + +> compose の “動かし方” は、スレッドの base + dev/prod オーバーレイに整合させる。 + +### 28.5.1 dev(ローカル/開発)での注入(例:app1) +`docker-compose.dev.yml` に追加: + +```yaml +services: + app1: + volumes: + - ./apps/python:/app + # ★ credentials を read-only で注入 + - ./credentials/dev:/run/credentials:ro + + batch: + volumes: + - ./apps/python:/app + - ./credentials/dev:/run/credentials:ro +``` + +### 28.5.2 prod(本番)での注入(原則:ホストに置かない) +- composeで本番運用するなら、ホストに置く場合でも **権限/配布/監査**が必要 +- 可能ならクラウド側の Secret Manager / Parameter Store 等に寄せる(後述) + +ただし “運用として credentials フォルダに置く” を採用するなら、prodは: + +```yaml +services: + app1: + volumes: + - ./credentials/prod:/run/credentials:ro + batch: + volumes: + - ./credentials/prod:/run/credentials:ro +``` + +--- + +## 28.6 ファイル権限(必須) +秘密鍵・証明書は権限を厳しく。 + +- 秘密鍵:`chmod 600` +- ディレクトリ:`chmod 700` + +例: + +```bash +chmod 700 credentials/dev/ssh +chmod 600 credentials/dev/ssh/id_rsa +chmod 600 credentials/dev/gcp/service-account.json +``` + +--- + +## 28.7 アプリ側の実装ルール(必須:ファイルパス参照のみ) + +### 28.7.1 Python(GCP Service Account JSON) +- アプリは `.env` から `GCP_SA_JSON_PATH` を読み取って利用 +- Google SDK は `GOOGLE_APPLICATION_CREDENTIALS` を使うのが一般的 + +推奨: +- `.env` に `GOOGLE_APPLICATION_CREDENTIALS=/run/credentials/gcp/service-account.json` を入れる +- アプリは Google SDK に任せる(自前でJSON読まない) + +### 28.7.2 AWS +- 可能なら **OIDC / IAM Role / Workload Identity**(クラウド推奨) +- それが無理なら `AWS_SHARED_CREDENTIALS_FILE` 方式でファイル注入 +- `.env` にはパスだけ + +--- + +## 28.8 devcontainer での扱い(必須:安全に見える化) + +### 28.8.1 devcontainer内で credentials を使う場合 +- ワークスペース(`/workspaces/repo-root`)に `credentials/dev` が存在しても、Gitに入らないのでOK +- ただし「誤コミット」防止のため、READMEで運用を明記 + +### 28.8.2 devcontainerから compose 起動 +- スレッド方針どおり、repo-root で compose を叩く +- credentials の注入は compose がやる(devcontainerは “見るだけ”) + +--- + +## 28.9 CI/CD での扱い(必須:credentials フォルダをそのまま使わない) + +> CI上で `credentials/` をリポジトリに置く運用は基本NG(機密配布の問題)。 +> CIは “Secrets” から生成して配置する。 + +### 28.9.1 GCP JSON を GitHub Actions Secrets に入れる例(概念) +1) `GCP_SA_JSON` を GitHub Secrets に登録(文字列) +2) CI でファイルとして書き出す +3) `.env` のパスに合わせて配置 + +例: + +```yaml +- name: Write GCP credentials + run: | + mkdir -p /tmp/credentials/gcp + echo '${{ secrets.GCP_SA_JSON }}' > /tmp/credentials/gcp/service-account.json + chmod 600 /tmp/credentials/gcp/service-account.json + +- name: Run tests + run: | + export PYTHONPATH="$PWD/apps/python" + export GOOGLE_APPLICATION_CREDENTIALS="/tmp/credentials/gcp/service-account.json" + pytest -v +``` + +> 本番は CI から直接配布するのではなく、クラウドのSecret Managerへ登録して、実行環境でマウントするのが王道。 + +--- + +## 28.10 クラウドでの推奨(最終形:ファイル運用を脱却する道筋) + +> ユーザー要望は「credentialsフォルダ運用」だが、 +> 本番の最終形として推奨される “安全策” を漏れなく列挙。 + +### 28.10.1 GCP +- **Secret Manager** にJSONを登録 +- 実行環境(Cloud Run/GKE等)でマウント or 環境変数注入 +- Workload Identity を採用できるなら、JSON自体が不要になる + +### 28.10.2 AWS +- **IAM Role / OIDC** を採用できるなら、アクセスキー配布が不要 +- Parameter Store / Secrets Manager で管理し、実行環境で取得 + +> ただしローカル開発は `credentials/dev` を使うのが現実的(このスレッド方針)。 + +--- + +## 28.11 Wiki貼り付け用 “認証情報運用宣言”(短文化) + +```text +【外部連携認証情報(JSON/RSA/PEM等)運用】 +1) 認証ファイルは .env に直接書かず、credentials/ 配下に配置する(dev/stg/prod で分離)。 +2) Gitに絶対コミットしない(.gitignoreで credentials/** を完全除外し、READMEだけ例外)。 +3) .env にはファイル本体ではなく “パス” のみを定義し、アプリはそのパスを読むだけにする。 +4) docker-compose では credentials/ を /run/credentials に read-only マウントして注入する。 +5) 秘密鍵・JSONは権限を厳格化する(600/700)。 +6) CI/CD は credentials/ を持たず、Secrets からファイル生成して同じパス設計でテスト/実行する。 +7) 本番は可能なら Secret Manager / OIDC / Workload Identity へ移行し、ファイル配布自体を廃止する。 +``` + +--- +## 29. credentials 運用を “モノレポ複数アプリ” で破綻させない追加仕様(省略なし) + +> 28章で定義した「credentialsフォルダ運用」を、 +> **複数アプリ・複数環境・devcontainer・CI/CD・クラウド**すべてで矛盾なく成立させるための追加仕様。 + +--- + +## 29.1 置く場所の最終決定(アプリ別 or ルート共通) + +### 29.1.1 推奨(スレッド整合):ルート共通 `credentials//...` +- どのアプリでも同じパス規約を使える +- compose の volume 設定がシンプル +- “混在画面” でも説明が一箇所で済む + +採用: +- `repo-root/credentials/dev/...` +- `repo-root/credentials/stg/...` +- `repo-root/credentials/prod/...` + +### 29.1.2 例外(必要な場合のみ):アプリ固有認証をアプリ配下に置く +- 例:app1だけが使う非常に特殊な鍵(他アプリは不要) +- ただし運用が散らかるため、原則は禁止 +- 例外適用時は `credentials///...` に寄せる(アプリ配下へは置かない) + +--- + +## 29.2 “共通認証” と “アプリ固有認証” の区別(必須) + +### 29.2.1 共通認証(common) +- 複数アプリが同じ認証情報を使う +- 例:同一GCPプロジェクトのサービスアカウント、共通S3バケットアクセス等 + +配置例: +- `credentials/dev/gcp/service-account.json` +- `credentials/dev/aws/credentials` + +### 29.2.2 アプリ固有認証(app-specific) +- app1だけ別プロジェクト/別アカウント等で必要 +- 配置は “環境→アプリ→種別” の順で統一する + +配置例: +- `credentials/dev/app1/gcp/service-account.json` +- `credentials/dev/batch/aws/credentials` + +--- + +## 29.3 `.env` のパス設計(最重要:パスは “固定” を目指す) + +> 原則:**環境が変わってもアプリが参照するパスは同じ**。 +> 違うのは “注入される中身” のみ。 + +### 29.3.1 推奨:コンテナ内パスを固定 +- `GCP_SA_JSON_PATH=/run/credentials/gcp/service-account.json` +- `AWS_SHARED_CREDENTIALS_FILE=/run/credentials/aws/credentials` + +### 29.3.2 `.env.*` に書くべきもの(ファイル以外) +- `GCP_PROJECT_ID` +- `AWS_REGION` +- `APP_ENV` + +> “パス” は共通 `.env` に寄せるのが原則。 + +--- + +## 29.4 docker-compose のベストプラクティス(アプリ増加に耐える) + +### 29.4.1 volume の重複をなくす(anchors を使う) +※ YAML アンカーを許容できる運用なら、必ず導入する。 + +例(`docker-compose.dev.yml`): + +```yaml +x-credentials-dev: &credentials-dev + - ./credentials/dev:/run/credentials:ro + +services: + app1: + volumes: + - ./apps/python:/app + <<: *credentials-dev + + batch: + volumes: + - ./apps/python:/app + <<: *credentials-dev +``` + +> YAMLの構文制約で `<<:` の使い方はcompose実装差が出る可能性があるため、 +> 運用が不安なら “素直にコピペ” でもよいが、アプリ数増で保守が増える点は理解しておく。 + +### 29.4.2 代替:環境変数で credentials の物理パスを切り替える +- `CREDENTIALS_DIR=./credentials/dev` を `.env.dev` に置き、 +- compose は `${CREDENTIALS_DIR}` を参照する + +例(`docker-compose.dev.yml`): + +```yaml +services: + app1: + volumes: + - ${CREDENTIALS_DIR}:/run/credentials:ro +``` + +`.env.dev`: +```text +CREDENTIALS_DIR=./credentials/dev +``` + +--- + +## 29.5 “起動手順がブレない” ための scripts/(必須) + +### 29.5.1 環境ごとに起動スクリプトを固定 +- devcontainer内でもホストでも同じスクリプトを叩く + +例: + +```text +scripts/ +├── up-dev.sh +├── up-stg.sh +└── up-prod.sh +``` + +`up-dev.sh`(credentialsも含めて同じcomposeを叩くだけ): + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." +docker compose -f docker-compose.base.yml -f docker-compose.dev.yml --profile local up -d +``` + +--- + +## 29.6 認証ファイルの “サンプル” と “生成ルール”(必須) + +> Gitに本物を置かない以上、READMEだけでは再現性が落ちる。 +> なので、**サンプル or 生成スクリプト**を必ず用意する。 + +### 29.6.1 サンプルファイルを置く(内容はダミー) +- `credentials/dev/gcp/service-account.json.sample` +- `credentials/dev/aws/credentials.sample` + +ただし **sample を credentials/ 配下に置くなら .gitignore の例外が必要**。 + +例(`.gitignore` 例外): + +```text +credentials/** +!credentials/README.md +!credentials/**/*.sample +!credentials/**/.keep +``` + +### 29.6.2 生成スクリプト(テンプレートからコピー) +例: +- `scripts/init-credentials-dev.sh` + - sample → 実ファイルへコピー + - chmod を設定 + - 作成場所を案内 + +--- + +## 29.7 セキュリティ運用(必須:事故を仕組みで潰す) + +### 29.7.1 pre-commit / CI で漏洩検知を必須化 +- push前に弾く(最優先) +- 例:gitleaks / trufflehog / secretlint 等 + +運用ルール(宣言): +- 「秘密情報検知が落ちたPRは例外なく修正しないとマージ不可」 + +### 29.7.2 監査ログのための “配置規約” +- 認証情報は `credentials//...` にしか置かない +- 例外を許さない(散在すると漏洩調査が困難) + +--- + +## 29.8 本番(prod)で “credentialsフォルダ” を使う場合の追加条件(必須) + +> 本番でホスト配置する場合は、単にフォルダを置くだけでは危険。 +> 最低限の条件を満たす必要がある。 + +必須条件: +1) 配布経路が明確(誰がどこから持ってくるか) +2) 権限が限定(サーバユーザのみ読める) +3) ローテーション手順がある(更新時に再起動が必要か等) +4) 監査できる(更新履歴/責任者が追える) + +--- + +## 29.9 クラウド移行の最終形(“ファイル運用” を捨てる道) + +> スレッドの要望は “credentialsフォルダ運用” だが、 +> 本番の最終形としての移行方針を確定させる。 + +- GCP:Workload Identity(可能なら)→ JSON不要 + できない場合:Secret Manager → マウント/注入 +- AWS:IAM Role / OIDC → キー不要 + できない場合:Secrets Manager / Parameter Store + +移行宣言: +- 「devは credentials/dev を許可。本番は原則 Secret Manager / Role へ寄せる」 + +--- + +## 29.10 Wiki貼り付け用 “credentials拡張仕様” (短文化) + +```text +【credentials運用 拡張仕様】 +1) credentialsは repo-root/credentials//... に集約し、環境単位で分離する。 +2) アプリ固有が必要なら credentials///... に置き、アプリ配下へは置かない。 +3) .env はパスのみを定義し、コンテナ内パスは /run/credentials/... に固定する。 +4) compose で credentials を read-only マウントし、イメージには焼き込まない。 +5) sample/生成スクリプトを用意し、再現性を担保する(本物はGitに置かない)。 +6) secret漏洩検知(gitleaks等)を必須化し、検知時はマージ不可とする。 +7) 本番は最終的に Secret Manager / Role / OIDC / Workload Identity へ移行し、ファイル配布を廃止する。 +``` + +--- +## 30. クラウド / オンプレ / ローカルのデプロイ運用を両立するリポジトリ構成(モノレポ前提) + +> 目的: +> - **同一リポジトリ**で複数アプリ(Python/PHP/iOS等)を管理しつつ、 +> - **ローカル(開発)** / **オンプレ(自社サーバ)** / **クラウド(GCP/AWS)** のデプロイ方式を混在させても、 +> - 起動手順・差分管理・秘密情報取り扱いが破綻しない「単一情報源」を作る。 + +--- + +### 30.1 大原則(必須ルール) +1) **アプリのソースは `apps/` に集約**(既定) +2) **デプロイ用ファイルは `deploy/`(または `infra/`)に集約**し、アプリ配下に散らさない +3) **ローカル/オンプレは docker compose を正**(既定:base + env差分 + profiles) +4) **クラウドは IaC / マニフェスト(Terraform/Helm/Kustomize)を正** +5) **アプリセットの単一情報源を各方式で持つ** + - compose:`profiles`(例:cloud/local/batch) + - k8s:`kustomize overlays` または `helm values` +6) **認証情報はローカルは `credentials/`、クラウドは Secret Manager/Role/OIDC を最終形**(既定方針) + +--- + +## 30.2 推奨ディレクトリ構成(最小〜標準) + +> 既存の構成(apps/・docker-compose.*・credentials/・devcontainer)を崩さずに拡張する。 + +```text +repo-root/ +├── .env +├── .env.dev +├── .env.stg +├── .env.prod +│ +├── docker-compose.base.yml +├── docker-compose.dev.yml +├── docker-compose.stg.yml +├── docker-compose.prod.yml +│ +├── docker/ +│ └── base/ +│ └── Dockerfile +│ +├── apps/ +│ ├── python/... +│ ├── php/... +│ └── ios/... +│ +├── credentials/ +│ ├── dev/... +│ ├── stg/... +│ └── prod/... +│ +├── deploy/ # ★ デプロイ運用をここに集約 +│ ├── local/ # ローカル(開発)運用 +│ │ ├── README.md +│ │ └── compose/ # ルートcomposeを呼ぶだけ(分岐点の明文化) +│ │ ├── up.sh +│ │ ├── down.sh +│ │ └── logs.sh +│ │ +│ ├── onprem/ # オンプレ(自社サーバ)運用 +│ │ ├── README.md +│ │ ├── compose/ # compose方式での本番/常駐運用 +│ │ │ ├── install.sh # 初期導入(ユーザ作成/dir準備など) +│ │ │ ├── deploy.sh # pull/build/up +│ │ │ ├── rollback.sh # 前タグへ戻す +│ │ │ └── systemd/ +│ │ │ ├── app-stack.service # docker compose をsystemdで常駐化(必要なら) +│ │ │ └── README.md +│ │ └── ansible/ # 可能なら IaC 化(任意) +│ │ ├── inventory/ +│ │ ├── playbooks/ +│ │ └── roles/ +│ │ +│ └── cloud/ # クラウド運用(GCP/AWS) +│ ├── README.md +│ ├── terraform/ # ★ IaC(ネットワーク/DB/権限/レジストリ等) +│ │ ├── modules/ +│ │ ├── envs/ +│ │ │ ├── dev/ +│ │ │ ├── stg/ +│ │ │ └── prod/ +│ │ └── README.md +│ │ +│ ├── k8s/ # ★ デプロイ対象(マニフェスト) +│ │ ├── base/ # 共通(Deployment/Service等) +│ │ ├── overlays/ # 環境差分(kustomize運用の場合) +│ │ │ ├── dev/ +│ │ │ ├── stg/ +│ │ │ └── prod/ +│ │ └── README.md +│ │ +│ └── helm/ # 代替:helm運用の場合(任意) +│ ├── charts/ +│ │ └── app-suite/ # アプリセット(複数サービス)を1チャートで表現可 +│ └── values/ +│ ├── dev.yaml +│ ├── stg.yaml +│ └── prod.yaml +│ +├── scripts/ # “どこから叩くか” を統一するための共通スクリプト +│ ├── test-python.sh +│ ├── up-dev.sh +│ ├── up-prod.sh +│ ├── deploy-onprem.sh +│ └── deploy-cloud.sh +│ +├── Makefile # 任意:起動/テスト/デプロイをコマンド固定 +└── .devcontainer/ + ├── Dockerfile + └── devcontainer.json +``` + +--- + +## 30.3 “アプリセット” を各デプロイ方式でどう表現するか(必須) + +### 30.3.1 ローカル/オンプレ(docker compose)側の単一情報源 +- **profiles が単一情報源** + - `local`:開発で全部入り + - `cloud`:クラウドリリース対象 + - `batch`:バッチだけ + +運用宣言(必須): +- 「クラウド/オンプレでデプロイする対象は `profiles` と一致していること」 +- 「新しいサービスを追加したら、profile設計も同時に更新すること」 + +### 30.3.2 クラウド(k8s/helm)側の単一情報源 +- kustomizeなら: + - `deploy/cloud/k8s/base` に全サービス定義 + - `deploy/cloud/k8s/overlays/{dev,stg,prod}` で “アプリセット + 環境差分” を決める +- helmなら: + - `deploy/cloud/helm/values/{dev,stg,prod}.yaml` が単一情報源 + +運用宣言(必須): +- 「k8sのoverlays/valuesが “その環境で動くアプリセット” の単一情報源」 +- 「compose profiles と意味を合わせる(最低限:cloud profile相当のサービスが一致)」 + +--- + +## 30.4 秘密情報(credentials)の運用:ローカル/オンプレ/クラウドの責務分離(必須) + +### 30.4.1 ローカル(開発) +- `credentials/dev` を compose で `/run/credentials:ro` にマウント(既定) + +### 30.4.2 オンプレ(自社サーバ) +- 方針A(暫定):`credentials/prod` をサーバに配布して read-only マウント +- 方針B(推奨):Vault等のシークレット管理へ移行(段階導入) + +### 30.4.3 クラウド(推奨:ファイル配布を廃止) +- GCP:Workload Identity / Secret Manager +- AWS:IAM Role / OIDC / Secrets Manager +- アプリ側は `.env` に書いた “パス or 環境変数名” を読むだけ(実体注入は基盤責務) + +--- + +## 30.5 “デプロイ手順がブレない” ための README 配置(必須) +- `deploy/local/README.md`:開発起動手順(compose profiles と env) +- `deploy/onprem/README.md`:オンプレ導入・更新・ロールバック・監視/ログ +- `deploy/cloud/README.md`:Terraform適用 → k8s/helmデプロイ → ロールバック + +必須記載項目(テンプレ): +- 対象環境(dev/stg/prod) +- アプリセット(どのサービスが動くか) +- 必要な認証(どこから注入されるか) +- デプロイコマンド(scripts/Makefile 参照) +- ロールバック手順(必ず) + +--- + +## 30.6 Wiki貼り付け用 “デプロイ運用 構成宣言”(短文化) + +```text +【デプロイ運用(cloud/onprem/local)構成】 +1) アプリは apps/、デプロイ資材は deploy/ に集約し、散在を禁止する。 +2) local/onprem は docker compose(base + env差分 + profiles)を正とする。 +3) cloud は terraform(基盤)+ k8s/helm(アプリ)を正とし、環境差分は overlays/values で管理する。 +4) アプリセットの単一情報源は、composeはprofiles、k8sはoverlays、helmはvaluesとする。 +5) 認証情報はローカル/オンプレは credentials/、クラウドは Secret Manager/Role/OIDC を最終形とする。 +6) 起動/デプロイ/ロールバックは scripts/ または Makefile に集約し、手順の属人化を禁止する。 +``` + +--- +## 31. クラウド/オンプレ/ローカル デプロイ運用の“実体ファイル”テンプレート一式(省略なし) + +> 30章の「構成方針」を実際に回せるように、 +> `deploy/` 配下に置く具体ファイルのテンプレートを漏れなく提示する。 + +--- + +## 31.1 deploy/local(ローカル運用)テンプレート + +### 31.1.1 `deploy/local/README.md` +```markdown +# deploy/local(ローカル開発運用) + +## 目的 +- 開発者が同一手順で起動/停止/ログ確認できる状態を提供する。 +- 起動対象(アプリセット)は docker compose profiles により切り替える。 + +## 前提 +- リポジトリルートに docker-compose.*.yml が存在する +- credentials/dev が存在する(Git管理しない) + +## 起動(local profile) +```bash +cd repo-root +./deploy/local/compose/up.sh +``` + +### 31.1.2 `deploy/local/compose/up.sh` + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.dev.yml \ + --profile local \ + up -d +``` + +### 31.1.3 `deploy/local/compose/down.sh` + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +docker compose -f docker-compose.base.yml down +``` + +### 31.1.4 `deploy/local/compose/logs.sh` + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +docker compose -f docker-compose.base.yml logs -f --tail=200 +``` + +--- + +## 31.2 deploy/onprem(オンプレ運用)テンプレート + +> オンプレは “サーバ常駐” が目的になるため、 +> - **更新(deploy)** +> - **ロールバック** +> - **systemd管理**(任意) +> を必ず定義する。 + +### 31.2.1 `deploy/onprem/README.md` + +```markdown +# deploy/onprem(オンプレ運用) + +## 目的 +- 自社サーバで docker compose によりアプリセットを常駐運用する。 +- 更新/ロールバックを再現可能にし、手順を属人化しない。 + +## 前提 +- サーバにリポジトリを配置(またはCIが配布) +- credentials/prod をサーバ側に安全に配置(Git管理しない) +- docker compose が利用可能 + +## 初期導入 +```bash +cd repo-root +./deploy/onprem/compose/install.sh +``` + +## デプロイ(更新) + +```bash +cd repo-root +./deploy/onprem/compose/deploy.sh +``` + +## ロールバック(前タグへ) + +```bash +cd repo-root +./deploy/onprem/compose/rollback.sh +``` + +## 起動対象 + +* docker compose profiles の "cloud"(= 本番相当セット)を使用する + + +### 31.2.2 `deploy/onprem/compose/install.sh` +- 初回に必要なディレクトリ/権限の準備のみ(破壊的操作は禁止) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +# 例:credentials 配置確認(存在しないなら失敗させる) +test -d credentials/prod || (echo "credentials/prod not found" && exit 1) + +# 例:権限チェック(必要なら運用に合わせて) +echo "install: ok" +``` + +### 31.2.3 `deploy/onprem/compose/deploy.sh` +- “更新” の最小定義:pull → build(or pull image)→ up -d + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +# 例:コードをpullする運用なら +git pull --rebase + +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile cloud \ + up -d + +docker compose -f docker-compose.base.yml ps +``` + +### 31.2.4 `deploy/onprem/compose/rollback.sh` +- 前のイメージタグへ戻す方式(運用によってはcomposeのimage指定が必要) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +TAG="${1:-}" +if [ -z "$TAG" ]; then + echo "usage: rollback.sh " + exit 1 +fi + +# 例:環境変数でタグを指定する運用(compose側で ${IMAGE_TAG} を参照) +export IMAGE_TAG="$TAG" + +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile cloud \ + up -d + +docker compose -f docker-compose.base.yml ps +``` + +--- + +## 31.3 deploy/onprem/systemd(任意だが常駐なら推奨) + +### 31.3.1 `deploy/onprem/compose/systemd/app-stack.service` +- docker compose を systemd で管理する最小例(環境に合わせて編集) + +```ini +[Unit] +Description=App Stack (docker compose) +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/opt/repo-root +ExecStart=/usr/bin/docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d +ExecStop=/usr/bin/docker compose -f docker-compose.base.yml down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target +``` + +--- + +## 31.4 deploy/cloud(クラウド運用)テンプレート + +> cloud は「基盤」と「アプリ」を分ける。 +> - terraform:ネットワーク/権限/レジストリ/DB 等 +> - k8s/helm:アプリデプロイ + +### 31.4.1 `deploy/cloud/README.md` + +```markdown +# deploy/cloud(クラウド運用) + +## 目的 +- IaCで基盤を管理し、k8s/helmでアプリをデプロイする。 +- 環境差分は overlays(kustomize)または values(helm)で管理する。 + +## 手順(Terraform → Deploy) +### 1) 基盤(terraform) +```bash +cd deploy/cloud/terraform/envs/prod +terraform init +terraform apply +``` + +### 2) アプリ(kustomize) + +```bash +cd deploy/cloud/k8s/overlays/prod +kubectl apply -k . +``` + +## 秘密情報 + +* ローカルの credentials/ は使わない +* Secret Manager / IAM Role / OIDC / Workload Identity を利用する + + +--- + +## 31.5 cloud/k8s(kustomize)最小テンプレート + +### 31.5.1 `deploy/cloud/k8s/base/kustomization.yaml` +- “共通” のリソースだけを列挙 + +```yaml +resources: + - app1-deployment.yaml + - app1-service.yaml + - batch-cronjob.yaml +``` + +### 31.5.2 `deploy/cloud/k8s/overlays/prod/kustomization.yaml` +- 環境差分(replicas / image tag / env 等)をここで定義 + +```yaml +resources: + - ../../base + +patches: + - target: + kind: Deployment + name: app1 + patch: |- + - op: replace + path: /spec/replicas + value: 2 +images: + - name: myorg/app1 + newName: myorg/app1 + newTag: sha-REPLACE_ME +``` + +--- + +## 31.6 scripts(共通コマンド統一)テンプレート + +### 31.6.1 `scripts/deploy-onprem.sh` + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." +./deploy/onprem/compose/deploy.sh +``` + +### 31.6.2 `scripts/deploy-cloud.sh`(kustomize例) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." + +ENV="${1:-prod}" +cd "deploy/cloud/k8s/overlays/${ENV}" +kubectl apply -k . +``` + +--- + +## 31.7 Wiki貼り付け用 “デプロイ資材の置き場所” 宣言(短文化) + +```text +【デプロイ資材の配置】 +1) local/onprem/cloud すべてのデプロイ資材は deploy/ に集約する(apps/ に散らさない)。 +2) local/onprem は docker compose を正とし、deploy/local と deploy/onprem のスクリプトが単一手順となる。 +3) cloud は terraform(基盤)+ k8s/helm(アプリ)で管理し、deploy/cloud が単一手順となる。 +4) scripts/ または Makefile でコマンドを固定し、手順の属人化を禁止する。 +``` + +--- + +## 32. デプロイ運用の“単一情報源(Single Source of Truth)”を壊さない追加設計(必須) + +> 30〜31章で「deploy/ に集約」まで定義した。 +> ここからは、クラウド/オンプレ/ローカルで **同じリポジトリから矛盾なく運用するための“追加仕様”** を漏れなく確定する。 + +--- + +### 32.1 デプロイ対象(アプリセット)の定義場所(必須) + +#### 32.1.1 docker compose(local/onprem)の単一情報源 +- **docker-compose.base.yml の services + profiles が単一情報源** +- 環境差分(dev/prod)は docker-compose.{env}.yml 側で行う +- 「どの環境に何が出るか」は profile で決める + +ルール(必須): +- `profiles: ["cloud"]` が付いたサービスは **クラウド・オンプレ本番相当**に含まれる +- `profiles: ["local"]` が付いたサービスは **ローカル開発**に含まれる +- `profiles: ["batch"]` は **バッチ単体運用**に含まれる + +#### 32.1.2 Kubernetes(cloud)の単一情報源 +- **kustomize overlays / helm values が単一情報源** +- 「prod に app1 は出すが batch は CronJob として出す」などは overlays/values で決める + +ルール(必須): +- `deploy/cloud/k8s/overlays/prod`(または `deploy/cloud/helm/values/prod.yaml`)が **prod のアプリセット最終決定**である +- compose の `cloud` と意味が一致していること(最低限:主要サービスが一致) + +--- + +### 32.2 イメージタグ戦略(必須:ロールバック可能にする) + +#### 32.2.1 タグは必ず “不変(immutable)” を含める +- `sha-` を必須とする +- `latest` は参照用に残してもよいが、デプロイは不変タグを正とする + +#### 32.2.2 compose(onprem)でタグを注入する方式(必須) +- `docker-compose.prod.yml` に “image を変数化” しておく +- rollback は `IMAGE_TAG=sha-xxxxx` を差し替えるだけで成立させる + +例(`docker-compose.prod.yml` の “image固定” 方式): + +```yaml +services: + app1: + image: myorg/app1:${IMAGE_TAG:-latest} + batch: + image: myorg/batch:${IMAGE_TAG:-latest} +``` + +> ルール:onprem の deploy/rollback は IMAGE_TAG を切り替えるだけで戻せること。 + +--- + +### 32.3 環境昇格(dev→stg→prod)の運用(必須) + +#### 32.3.1 昇格の原則 +- **同一SHAを昇格**させる(devで動いたものをprodへ) +- 「devはsha-A、prodはsha-B」になった瞬間に再現性が壊れる + +#### 32.3.2 リリースノート(必須) +- デプロイ単位は “アプリセット” +- そのため “app1だけの変更” でも、アプリセットのリリースとして記録する + +--- + +## 33. オンプレ(docker compose)本番運用:完全テンプレート(更新/ロールバック/監査) + +> 31章の onprem は最小だったため、ここで “運用に必要な要素” を全て補完する。 + +--- + +### 33.1 onprem 用 `.env.onprem`(推奨:onprem専用のenvを分離) + +例:`repo-root/.env.onprem`(Git管理してよいのは “非機密のみ”) + +```text +APP_ENV=prod +TZ=Asia/Tokyo + +# イメージタグ(不変) +IMAGE_TAG=sha-REPLACE_ME + +# credentials の注入先(パスは固定) +GCP_SA_JSON_PATH=/run/credentials/gcp/service-account.json + +# ログ出力 +LOG_FORMAT=json +LOG_LEVEL=info +``` + +--- + +### 33.2 onprem デプロイスクリプト(“pull/build/pin/ps” を固定) + +#### 33.2.1 `deploy/onprem/compose/deploy.sh`(改訂:タグ固定・監査ログ出力) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +# 監査用ログ(いつ/誰が/何をデプロイしたか) +echo "[deploy] start: $(date -Iseconds)" +echo "[deploy] user: $(whoami)" +echo "[deploy] git: $(git rev-parse --short HEAD || true)" + +# 例:コードpullする運用の場合(CI配布なら不要) +git pull --rebase + +# credentials 존재確認 +test -d credentials/prod || (echo "credentials/prod not found" && exit 1) + +# .env.onprem がある場合は読み込む(なければ .env.prod を使う等、運用で統一) +if [ -f .env.onprem ]; then + set -a + source .env.onprem + set +a +fi + +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile cloud \ + up -d + +docker compose -f docker-compose.base.yml ps +echo "[deploy] done: $(date -Iseconds)" +``` + +--- + +### 33.3 onprem ロールバック(タグ差し替えで戻す) + +#### 33.3.1 `deploy/onprem/compose/rollback.sh`(改訂:.env.onprem へ書き込み方式) + +> 方式A:運用として `.env.onprem` に IMAGE_TAG を書き込む(最も分かりやすい) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../../.." + +TAG="${1:-}" +if [ -z "$TAG" ]; then + echo "usage: rollback.sh " + exit 1 +fi + +if [ ! -f .env.onprem ]; then + echo ".env.onprem not found" + exit 1 +fi + +# IMAGE_TAG を差し替え(簡易) +grep -q '^IMAGE_TAG=' .env.onprem \ + && sed -i.bak "s/^IMAGE_TAG=.*/IMAGE_TAG=${TAG}/" .env.onprem \ + || echo "IMAGE_TAG=${TAG}" >> .env.onprem + +echo "[rollback] set IMAGE_TAG=${TAG}" + +# 反映 +set -a +source .env.onprem +set +a + +docker compose \ + -f docker-compose.base.yml \ + -f docker-compose.prod.yml \ + --profile cloud \ + up -d + +docker compose -f docker-compose.base.yml ps +echo "[rollback] done" +``` + +--- + +### 33.4 onprem systemd(常駐)完全版(Start/Stop/Restart + Reload) + +#### 33.4.1 `deploy/onprem/compose/systemd/app-stack.service`(改訂:restart対応) + +```ini +[Unit] +Description=App Stack (docker compose) +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/opt/repo-root + +ExecStart=/usr/bin/docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d +ExecStop=/usr/bin/docker compose -f docker-compose.base.yml down +ExecReload=/usr/bin/docker compose -f docker-compose.base.yml -f docker-compose.prod.yml --profile cloud up -d + +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target +``` + +#### 33.4.2 systemd インストール手順(onprem README に必須で記載) +- `/etc/systemd/system/app-stack.service` に配置 +- `systemctl daemon-reload` +- `systemctl enable --now app-stack.service` + +--- + +## 34. クラウド(k8s/kustomize)完全テンプレート:Deployment/Service/CronJob/Secret注入 + +> 31章の kustomize は kustomization だけだったので、ここで “実体マニフェスト” を省略なく追加する。 + +--- + +### 34.1 app1 Deployment(例) + +#### 34.1.1 `deploy/cloud/k8s/base/app1-deployment.yaml` + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app1 +spec: + replicas: 1 + selector: + matchLabels: + app: app1 + template: + metadata: + labels: + app: app1 + spec: + containers: + - name: app1 + image: myorg/app1:sha-REPLACE_ME + ports: + - containerPort: 8080 + env: + - name: APP_ENV + value: "prod" + - name: LOG_FORMAT + value: "json" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: "/run/credentials/gcp/service-account.json" + volumeMounts: + - name: credentials + mountPath: /run/credentials + readOnly: true + volumes: + - name: credentials + secret: + secretName: app1-credentials +``` + +> ルール:クラウドは `credentials/` フォルダを使わず、Secret Manager/Secret で注入する。 + +--- + +### 34.2 app1 Service(例) + +#### 34.2.1 `deploy/cloud/k8s/base/app1-service.yaml` + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: app1 +spec: + selector: + app: app1 + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP +``` + +--- + +### 34.3 batch CronJob(例) + +#### 34.3.1 `deploy/cloud/k8s/base/batch-cronjob.yaml` + +```yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: batch +spec: + schedule: "0 * * * *" + jobTemplate: + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: batch + image: myorg/batch:sha-REPLACE_ME + env: + - name: APP_ENV + value: "prod" + - name: LOG_FORMAT + value: "json" + - name: GOOGLE_APPLICATION_CREDENTIALS + value: "/run/credentials/gcp/service-account.json" + volumeMounts: + - name: credentials + mountPath: /run/credentials + readOnly: true + volumes: + - name: credentials + secret: + secretName: batch-credentials +``` + +--- + +### 34.4 Secret(例:GCP SA JSON をファイルとしてマウント) + +#### 34.4.1 `deploy/cloud/k8s/overlays/prod/app1-credentials-secret.yaml` + +> ここはクラウドごとに “作り方” が変わるため、 +> 運用としては “Secret Manager → External Secrets” 等を推奨する。 +> ただしテンプレとして “Kubernetes Secret” 例も省略なく提示する。 + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: app1-credentials +type: Opaque +data: + gcp-service-account.json: REPLACE_WITH_BASE64 +``` + +> マウント時にファイル名を合わせたい場合は `items` を使う(省略なしで例を出す)。 + +例:Deployment の volumes を次に変更: + +```yaml +volumes: + - name: credentials + secret: + secretName: app1-credentials + items: + - key: gcp-service-account.json + path: gcp/service-account.json +``` + +--- + +### 34.5 overlays/prod(完全例:resources + secret + image tag) + +#### 34.5.1 `deploy/cloud/k8s/overlays/prod/kustomization.yaml`(改訂:secretも含める) + +```yaml +resources: + - ../../base + - app1-credentials-secret.yaml + +images: + - name: myorg/app1 + newName: myorg/app1 + newTag: sha-REPLACE_ME + - name: myorg/batch + newName: myorg/batch + newTag: sha-REPLACE_ME + +patches: + - target: + kind: Deployment + name: app1 + patch: |- + - op: replace + path: /spec/replicas + value: 2 +``` + +--- + +## 35. クラウド基盤(Terraform)構成:envs/modules の必須ルール + +> 実クラウド差分(GCP/AWS)は運用で分かれるが、 +> リポジトリ構造の基本は固定する。 + +--- + +### 35.1 Terraform 構成(必須) + +- `modules/`:再利用可能な部品(VPC、IAM、Registry等) +- `envs//`:環境ごとの実体(dev/stg/prod) + +例: + +```text +deploy/cloud/terraform/ +├── modules/ +│ ├── network/ +│ ├── iam/ +│ ├── registry/ +│ └── database/ +└── envs/ + ├── dev/ + ├── stg/ + └── prod/ +``` + +--- + +## 36. CI/CD からのデプロイ(cloud/onprem)ワークフロー標準 + +> “デプロイ手順の属人化禁止” を実現するため、 +> GitHub Actions の “最小構成” を漏れなく提示する(環境差分は secrets で注入)。 + +--- + +### 36.1 Build(main)→ Push(Registry)→ Deploy(cloud) + +#### 36.1.1 `.github/workflows/release-cloud.yml`(最小) + +```yaml +name: release-cloud + +on: + push: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Registry login は環境に合わせて実装(ghcr.io等) + # - uses: docker/login-action@v3 + # with: + # registry: ghcr.io + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build app1 + run: | + docker build \ + -f apps/python/app1/Dockerfile \ + -t myorg/app1:sha-${{ github.sha }} \ + apps/python + + - name: Build batch + run: | + docker build \ + -f apps/python/batch/Dockerfile \ + -t myorg/batch:sha-${{ github.sha }} \ + apps/python + + # push は運用に応じて有効化 + # - run: docker push myorg/app1:sha-${{ github.sha }} + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # kubectl/kustomize セットアップは環境に応じて追加 + # ここでは scripts を単一情報源として呼ぶ運用を採用 + - name: Deploy (prod) + run: | + ./scripts/deploy-cloud.sh prod +``` + +> ルール:CIは “deploy/ と scripts/ を叩くだけ” にし、ロジックはリポジトリ側に置く。 + +--- + +### 36.2 onprem(自己ホスト)デプロイ:CIが SSH で叩く場合の最小 + +> セキュリティ要件により方式は変わるが、 +> リポジトリ構成としては “deploy/onprem を叩く” を単一情報源とする。 + +#### 36.2.1 `.github/workflows/release-onprem.yml`(概念テンプレ) + +```yaml +name: release-onprem + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # SSH鍵は secrets から注入(credentialsフォルダへコミットしない) + # 実行は onprem サーバ側で deploy.sh を叩く + - name: Deploy via SSH + run: | + echo "Implement SSH deployment according to your security policy" + echo "Target should run: ./deploy/onprem/compose/deploy.sh" +``` + +> ルール:onprem へは “手順を固定したスクリプト” を必ず呼ぶ。 +> CIの中に手順を直書きしない。 + +--- + +## 37. ローカル/オンプレ/クラウドの “差分管理表”(漏れなく列挙) + +| 項目 | ローカル(dev) | オンプレ(prod相当) | クラウド(prod) | +|---|---|---|---| +| アプリ起動 | docker compose(dev overlay) | docker compose(prod overlay + systemd) | k8s(kustomize/helm) | +| アプリセット | compose profiles(local) | compose profiles(cloud) | overlays/values(prod) | +| 認証情報 | credentials/dev マウント | credentials/prod マウント(暫定) or Vault | Secret Manager / Role / OIDC(最終形) | +| イメージ | build ローカル or CI | 可能なら registry から pull(推奨) | registry から pull(必須) | +| タグ | 任意 | 不変タグ(sha)必須 | 不変タグ(sha)必須 | +| ロールバック | docker compose down/up | IMAGE_TAG 差替 | 直前タグへ overlay/values 更新 | +| 監査 | 任意 | deploy.sh でログ | CIログ + GitOpsログ(推奨) | + +--- + +## 38. Wiki貼り付け用 “デプロイ運用 最終宣言”(省略なし・短文化) + +```text +【デプロイ運用(cloud/onprem/local)最終宣言】 +1) apps/ はアプリ実装、deploy/ はデプロイ資材、scripts/ は実行入口。散在禁止。 +2) local/onprem は docker compose を正とし、base + env差分 + profiles で起動対象を決める。 +3) cloud は terraform(基盤)+ k8s/helm(アプリ)を正とし、overlays/values が環境の単一情報源。 +4) アプリセットは compose は profiles、k8s は overlays、helm は values が単一情報源。意味を一致させる。 +5) 認証情報は .env に埋めず、ローカル/オンプレは credentials/ を read-only マウント、クラウドは Secret/Role/OIDC を最終形とする。 +6) イメージタグは不変(sha-)を必須とし、デプロイとロールバックはタグ差替で成立させる。 +7) CI/CD は scripts/ を呼ぶだけにし、手順ロジックはリポジトリに置く(属人化禁止)。 +8) dev→stg→prod は同一SHAを昇格し、再現性と監査性を崩さない。 +``` + +--- + +## 重要(先に結論) +あなたの実リポジトリに「今この瞬間に存在する全ファイル」を、こちらは参照できないため **“文字通りの完全列挙(漏れゼロ)”** は不可能です。 +その代わり、このスレッドで確定した要件(モノレポ / 複数アプリ / compose分割 / devcontainer / common / dependabot / CI/CD / credentials / cloud+onprem+local)を **運用上の漏れが起きないように、網羅的に入る“推奨リポジトリ構成(完全テンプレ)”** を **省略せず** 出します。 +※ `...` は「その配下に実ファイルが増える」ことを示すプレースホルダで、構成としての箱は全て列挙しています。 + +--- + +## 99. リポジトリ構成:完全テンプレ(モノレポ複数アプリ / cloud+onprem+local / credentials対応) + +```text +repo-root/ +├── LICENSE +├── README.md +├── CHANGELOG.md # 任意:リリースノート(同一SHA昇格運用と相性良い) +├── SECURITY.md # 推奨:脆弱性報告窓口 +├── CODE_OF_CONDUCT.md # 任意 +├── CONTRIBUTING.md # 任意:開発規約 +├── GOVERNANCE.md # 任意:運用責任など(OSS/社内でも有効) + +├── .gitignore +├── .gitattributes # 推奨:改行/バイナリ扱いなど +├── .editorconfig # 推奨:フォーマット統一 +├── .dockerignore # 必須:build肥大化・キャッシュ崩壊防止 +├── .env # 共通(非機密+パス中心) +├── .env.dev +├── .env.stg +├── .env.prod +├── .env.onprem # 推奨:オンプレ専用(非機密のみ/タグ固定等) +├── .env.example # 推奨:配布用テンプレ +├── .envrc # 任意:direnv運用するなら + +├── Makefile # 推奨:up/test/deploy を固定(属人化防止) + +├── docker-compose.base.yml # 必須:全サービス土台(profilesの単一情報源) +├── docker-compose.dev.yml # 必須:開発差分 +├── docker-compose.stg.yml # 任意:ステージング差分 +├── docker-compose.prod.yml # 必須:本番差分(imageタグ/credentials注入など) + +├── docker/ # 推奨:Docker資材を集約 +│ ├── base/ +│ │ ├── Dockerfile # 共通ベース(1つ) +│ │ └── README.md +│ ├── python/ # 任意:言語別共通(必要なら) +│ │ └── Dockerfile +│ ├── php/ +│ │ └── Dockerfile +│ └── ops/ # 任意:監視/運用コンテナなど +│ └── Dockerfile + +├── .devcontainer/ # devcontainerは“アプリの一種”として扱う(開発環境) +│ ├── devcontainer.json +│ ├── Dockerfile +│ ├── README.md +│ └── scripts/ # 任意:devcontainer起動時補助 +│ ├── postCreate.sh +│ └── postStart.sh + +├── .github/ # GitHub運用の単一集約点 +│ ├── dependabot.yml # 必須:モノレポ混在対応(directory×ecosystem) +│ ├── CODEOWNERS # 推奨:領域ごとレビュー責務 +│ ├── PULL_REQUEST_TEMPLATE.md # 推奨:品質ゲート/リリース影響の記載 +│ ├── ISSUE_TEMPLATE/ # 推奨:運用テンプレ +│ │ ├── bug_report.md +│ │ ├── feature_request.md +│ │ └── config.yml +│ ├── workflows/ # 必須:CI/CD +│ │ ├── python-ci.yml # 必須:pytest -m "not integration" +│ │ ├── python-integration.yml # 任意:夜間/手動 +│ │ ├── php-ci.yml # 任意:PHPがある場合 +│ │ ├── ios-ci.yml # 任意:iOSがある場合(macos runner) +│ │ ├── docker-build.yml # 任意:イメージビルド/push +│ │ ├── release-cloud.yml # 任意:cloud deploy(scripts呼び出し) +│ │ └── release-onprem.yml # 任意:onprem deploy(方針に従う) +│ ├── copilot-instructions.md # 任意:Copilot運用(このスレッド由来) +│ └── copilot/ # 任意:仕様の単一情報源 +│ ├── README.md +│ ├── coding-rules.md +│ ├── architecture.md +│ ├── decisions.md +│ └── tasks.md + +├── scripts/ # 必須:手順固定(CIもローカルも同じ入口を叩く) +│ ├── up-dev.sh +│ ├── up-stg.sh +│ ├── up-prod.sh +│ ├── down.sh +│ ├── logs.sh +│ ├── test-python.sh +│ ├── lint-python.sh # 任意:ruff/mypy等 +│ ├── fmt-python.sh # 任意:format +│ ├── deploy-onprem.sh +│ ├── deploy-cloud.sh +│ ├── init-credentials-dev.sh # 推奨:sample→実ファイル生成+chmod +│ └── doctor.sh # 任意:依存/権限/環境診断 + +├── credentials/ # 必須:.envに入らないjson/rsa/pe*運用(Gitに入れない) +│ ├── README.md # 必須:取得/配置/権限/変数対応 +│ ├── dev/ +│ │ ├── gcp/ +│ │ │ ├── service-account.json # 実体(Git禁止) +│ │ │ ├── service-account.json.sample +│ │ │ └── oauth-client.json.sample +│ │ ├── aws/ +│ │ │ ├── credentials.sample +│ │ │ └── config.sample +│ │ ├── ssh/ +│ │ │ ├── id_rsa.sample +│ │ │ └── id_rsa.pub.sample +│ │ └── tls/ +│ │ ├── client.key.sample +│ │ ├── client.crt.sample +│ │ └── ca.crt.sample +│ ├── stg/ +│ │ └── ... # 同構造 +│ └── prod/ +│ └── ... # 同構造(オンプレ暫定ならここを配布) + +├── schema/ # 推奨:言語跨ぎ共通(OpenAPI/Proto/JSON Schema) +│ ├── README.md +│ ├── openapi.yaml +│ ├── events.proto +│ └── jsonschema/ +│ └── ... + +├── docs/ # 推奨:Wiki/設計/運用手順の一次置き場 +│ ├── README.md +│ ├── architecture/ +│ │ ├── overview.md +│ │ └── diagrams/ +│ ├── operations/ +│ │ ├── runbook.md +│ │ ├── incident.md +│ │ └── faq.md +│ ├── security/ +│ │ ├── secrets-policy.md +│ │ └── threat-model.md +│ └── adr/ # 推奨:意思決定ログ +│ ├── 0001-*.md +│ └── ... + +├── deploy/ # 必須:デプロイ資材を集約(apps配下に散らさない) +│ ├── local/ +│ │ ├── README.md +│ │ └── compose/ +│ │ ├── up.sh +│ │ ├── down.sh +│ │ └── logs.sh +│ ├── onprem/ +│ │ ├── README.md +│ │ ├── compose/ +│ │ │ ├── install.sh +│ │ │ ├── deploy.sh +│ │ │ ├── rollback.sh +│ │ │ └── systemd/ +│ │ │ ├── app-stack.service +│ │ │ └── README.md +│ │ └── ansible/ # 任意:IaC化(推奨) +│ │ ├── inventory/ +│ │ ├── playbooks/ +│ │ └── roles/ +│ └── cloud/ +│ ├── README.md +│ ├── terraform/ +│ │ ├── README.md +│ │ ├── modules/ +│ │ │ ├── network/ +│ │ │ ├── iam/ +│ │ │ ├── registry/ +│ │ │ └── database/ +│ │ └── envs/ +│ │ ├── dev/ +│ │ ├── stg/ +│ │ └── prod/ +│ ├── k8s/ +│ │ ├── README.md +│ │ ├── base/ +│ │ │ ├── kustomization.yaml +│ │ │ ├── app1-deployment.yaml +│ │ │ ├── app1-service.yaml +│ │ │ ├── batch-cronjob.yaml +│ │ │ └── ... +│ │ └── overlays/ +│ │ ├── dev/ +│ │ │ ├── kustomization.yaml +│ │ │ └── ... +│ │ ├── stg/ +│ │ │ ├── kustomization.yaml +│ │ │ └── ... +│ │ └── prod/ +│ │ ├── kustomization.yaml +│ │ ├── app1-credentials-secret.yaml # 例:k8s secret(推奨はExternalSecrets) +│ │ └── ... +│ └── helm/ # 任意:helm運用するなら(k8sかhelmどちらかに寄せる) +│ ├── charts/ +│ │ └── app-suite/ +│ └── values/ +│ ├── dev.yaml +│ ├── stg.yaml +│ └── prod.yaml + +├── ops/ # 任意:監視/運用(Prometheus/Grafana等) +│ ├── README.md +│ ├── prometheus/ +│ │ ├── prometheus.yml +│ │ └── rules/ +│ ├── grafana/ +│ │ ├── dashboards/ +│ │ └── provisioning/ +│ └── loki/ +│ └── ... + +├── apps/ # 必須:アプリ本体(言語ごとに世界を分ける) +│ ├── common/ # 任意:言語非依存の共通(スクリプト/テンプレ等) +│ │ ├── README.md +│ │ └── ... +│ ├── python/ # 必須:Python世界(common含む) +│ │ ├── README.md +│ │ ├── common/ # 必須:Python共通(PYTHONPATH=apps/python 前提) +│ │ │ ├── __init__.py +│ │ │ ├── mylib/ +│ │ │ │ ├── __init__.py +│ │ │ │ ├── util.py +│ │ │ │ └── ... +│ │ │ └── tests/ +│ │ │ └── ... +│ │ ├── app1/ +│ │ │ ├── README.md +│ │ │ ├── Dockerfile # 薄い(FROM base + COPY + ENTRYPOINT) +│ │ │ ├── entrypoint.sh +│ │ │ ├── pyproject.toml # 方式A:Poetry/PDM/uv 等 +│ │ │ ├── poetry.lock # 方式A +│ │ │ ├── requirements.txt # 方式B:requirements運用の場合 +│ │ │ ├── src/ # 任意:srcレイアウトなら +│ │ │ ├── app1/ # python -m app1.main を想定 +│ │ │ │ ├── __init__.py +│ │ │ │ ├── main_app1.py +│ │ │ │ └── ... +│ │ │ └── tests/ +│ │ │ └── ... +│ │ └── batch/ +│ │ ├── README.md +│ │ ├── Dockerfile +│ │ ├── entrypoint.sh +│ │ ├── pyproject.toml +│ │ ├── poetry.lock +│ │ ├── batch/ +│ │ │ ├── __init__.py +│ │ │ ├── main_batch.py +│ │ │ └── ... +│ │ └── tests/ +│ │ └── ... +│ ├── php/ # 任意:PHP世界 +│ │ ├── README.md +│ │ └── api/ +│ │ ├── README.md +│ │ ├── Dockerfile +│ │ ├── composer.json +│ │ ├── composer.lock +│ │ ├── artisan # Laravelの場合 +│ │ ├── app/ +│ │ ├── config/ +│ │ ├── routes/ +│ │ ├── database/ +│ │ └── tests/ +│ ├── web/ # 任意:Node/フロント世界 +│ │ ├── README.md +│ │ ├── Dockerfile +│ │ ├── package.json +│ │ ├── package-lock.json / pnpm-lock.yaml / yarn.lock +│ │ ├── tsconfig.json # TSなら +│ │ ├── src/ +│ │ └── tests/ +│ └── ios/ # 任意:iOS世界(Docker外) +│ ├── README.md +│ └── MyAwesomeApp/ +│ ├── MyAwesomeApp.xcodeproj / .xcworkspace +│ ├── Sources/ +│ ├── Tests/ +│ └── ... + +├── pytest.ini # 推奨:探索固定(root pytest一括) +├── pyproject.toml # 任意:ルートでツール設定統合(ruff/mypy等) +├── ruff.toml # 任意:ruff単体運用なら +├── mypy.ini # 任意 +├── .pre-commit-config.yaml # 推奨:secret検知/format/lintをローカルで強制 +├── secretlint.config.json # 任意:secretlint採用するなら +├── gitleaks.toml # 任意:gitleaks採用するなら + +└── .vscode/ # 任意:開発者支援(固定したいなら) + ├── settings.json + ├── extensions.json + └── launch.json +``` + +--- + +## 追加(必須の“禁止事項”を構成で担保するための補助一覧) + +### A) Gitに入れて良い/悪い(構成として固定) +- **Git禁止(絶対)**:`credentials/**` の実体、秘密鍵、JSON実鍵、pem、トークン類 +- **Git許可(例外)**:`credentials/README.md`、`*.sample`、`.keep` +- **Git許可**:`.env.example`(ダミーのみ)、`deploy/**`、`scripts/**`、`.github/**` + +### B) 単一情報源(どこを見れば正しいか) +- 起動対象(local/onprem):`docker-compose.base.yml` の `profiles` +- cloud の起動対象:`deploy/cloud/k8s/overlays/*` or `deploy/cloud/helm/values/*` +- 依存更新:`.github/dependabot.yml` +- CI/CD:`.github/workflows/*` +- 認証ファイルの置き場:`credentials//...`(ローカル/オンプレ暫定) +- 共通Python import:`apps/python/common`(PYTHONPATH=apps/python) + +--- + +## もし“実リポジトリの完全tree”が必要な場合(ここだけ例外) +このチャットに `tree` 出力(またはZIP)を貼れば、**実在ファイルを“文字通り漏れなく”** Wiki向けに整形できます。 + +## 100. common 参照方法とパス調整(devcontainer / それ以外のDockerコンテナ差分を吸収する“確定仕様”) + +> 目的: +> - `apps/python/common` を **全アプリ(app1/batch…)から同一手順でimport**できる +> - devcontainer でも、composeで起動する各アプリコンテナでも、CIでも、**参照方法がブレない** +> - 「cd 位置」「作業ディレクトリ」「コンテキストルート」「PYTHONPATH」の差で壊れない + +--- + +## 100.1 結論(単一方針) +### 100.1.1 Python の common は “apps/python を import root に固定” する(必須) +- common は `apps/python/common` に置く +- 各アプリは `apps/python/` 配下に置く +- **import root を `apps/python` に固定** +- そのために **常に PYTHONPATH を `apps/python` に向ける** + +**固定値**: +- `PYTHONPATH=/workspaces//apps/python`(devcontainer内) +- `PYTHONPATH=/app`(アプリコンテナ内で `apps/python` を `/app` にマウント/コピーしている場合) +- CIでは `PYTHONPATH=$GITHUB_WORKSPACE/apps/python` + +> 重要:アプリコード内で「相対パス import」「実行ディレクトリ依存」を作らない。 +> `python -m ` と PYTHONPATH 固定で解決する。 + +--- + +## 100.2 ディレクトリ前提(common が壊れない箱) + +```text +apps/python/ +├── common/ +│ ├── __init__.py +│ └── mylib/ +│ ├── __init__.py +│ └── util.py +├── app1/ +│ ├── app1/ +│ │ ├── __init__.py +│ │ └── main_app1.py +│ └── tests/ +└── batch/ + ├── batch/ + │ ├── __init__.py + │ └── main_batch.py + └── tests/ +``` + +--- + +## 100.3 import 規約(必須:実装側ルール) +### 100.3.1 common の import は絶対パス(必須) +- OK:`from common.mylib.util import foo` +- OK:`import common.mylib.util as util` +- NG:`from ..common...`(相対importで実行ディレクトリ依存になる) +- NG:`sys.path.append(...)`(場当たり的で壊れやすい) + +### 100.3.2 起動は `python -m` に統一(必須) +- OK:`python -m app1.main_app1` +- OK:`python -m batch.main_batch` +- NG:`python apps/python/app1/app1/main_app1.py`(実行パス依存が発生) + +--- + +## 100.4 “devcontainer” と “アプリコンテナ(compose)” でパスがズレる問題の解決(必須) + +> devcontainer は「全アプリが見える」ワークスペースが必要。 +> 一方、アプリコンテナは「apps/python だけを /app として持つ」設計が多い。 +> これにより PYTHONPATH がズレるため、**“コンテナ内の import root を固定する”** 必要がある。 + +--- + +## 100.5 方式A(推奨):コンテナ内の `apps/python` を常に `/app` に揃える + +### 100.5.1 compose(アプリコンテナ)側 +- `./apps/python` をコンテナの `/app` にマウント(またはCOPY) +- したがって common は `/app/common` に存在する + +例(`docker-compose.dev.yml` 抜粋): + +```yaml +services: + app1: + volumes: + - ./apps/python:/app + environment: + - PYTHONPATH=/app + batch: + volumes: + - ./apps/python:/app + environment: + - PYTHONPATH=/app +``` + +### 100.5.2 Dockerfile(アプリ側)での前提 +- ルートに `COPY` する場合も `/app` に揃える +- `WORKDIR` は `/app` か `/app/` どちらでも良いが、importは PYTHONPATH で解決する + +--- + +## 100.6 方式B(devcontainer 側):ワークスペースは /workspaces なので PYTHONPATH を合わせる + +### 100.6.1 devcontainer の “起動後に必ず” PYTHONPATH を通す(必須) +- devcontainer は `repo-root` が `/workspaces/` になるのが一般的 +- そこで **PYTHONPATH=/workspaces//apps/python** を必ず設定する + +#### A) devcontainer.json で環境変数を固定(推奨) +(例:`.devcontainer/devcontainer.json` 抜粋) + +```json +{ + "name": "monorepo-dev", + "workspaceFolder": "/workspaces/repo-root", + "containerEnv": { + "PYTHONPATH": "/workspaces/repo-root/apps/python" + } +} +``` + +#### B) シェル初期化で固定(代替) +- `postCreate.sh` で `.bashrc` に export を追記する + +例(`.devcontainer/scripts/postCreate.sh`): + +```bash +#!/usr/bin/env bash +set -euo pipefail +echo 'export PYTHONPATH=/workspaces/repo-root/apps/python:${PYTHONPATH:-}' >> ~/.bashrc +echo 'export PYTHONPATH=/workspaces/repo-root/apps/python:${PYTHONPATH:-}' >> ~/.zshrc +``` + +--- + +## 100.7 “devcontainer から compose を叩く” ときの認識(確定) +### 100.7.1 devcontainer は “ホスト相当の指揮所” である(必須) +- devcontainer 内で `docker compose ...` を叩く場合、 + - 叩く場所は **repo-root**(スレッド結論) + - 起動するコンテナの PYTHONPATH は **compose側で /app を採用**(方式A) +- よって devcontainer 側の PYTHONPATH と、アプリコンテナ側の PYTHONPATH は “別でも良い” + - devcontainer:`/workspaces/repo-root/apps/python` + - アプリコンテナ:`/app` + +> “同じ値にしなければならない” ではなく、**各コンテナ内で common が見えること**が要件。 + +--- + +## 100.8 entrypoint.sh(shell経由起動)と PYTHONPATH(必須) +> CMD直叩きではなく shell を経由する方針なので、 +> shell 内で PYTHONPATH を保証し、`exec` で python に置き換える。 + +### 100.8.1 app1 の entrypoint.sh(確定テンプレ) +- `/app` が import root の前提 + +```bash +#!/usr/bin/env bash +set -euo pipefail + +export PYTHONPATH="/app:${PYTHONPATH:-}" +exec python -m app1.main_app1 "$@" +``` + +### 100.8.2 batch の entrypoint.sh(確定テンプレ) +```bash +#!/usr/bin/env bash +set -euo pipefail + +export PYTHONPATH="/app:${PYTHONPATH:-}" +exec python -m batch.main_batch "$@" +``` + +--- + +## 100.9 テスト実行(root pytest)と PYTHONPATH(必須:全実行環境で揃える) + +### 100.9.1 ローカル/CI の基本(スレッド結論) +- repo-root から pytest +- `PYTHONPATH=$PWD/apps/python` を必ず付与 + +例: + +```bash +cd repo-root +export PYTHONPATH="$PWD/apps/python" +pytest -v +``` + +### 100.9.2 devcontainer での基本 +- devcontainer は containerEnv で固定済み(100.6.1)なので、通常はそのまま: + +```bash +cd /workspaces/repo-root +pytest -v +``` + +> ただし “明示したい派” なら下でも良い(どちらでも同じ結果になるように設計する): +> `PYTHONPATH=/workspaces/repo-root/apps/python pytest -v` + +--- + +## 100.10 “common が見えない” 事故の典型と対策(必須チェックリスト) + +### 事故A:`ModuleNotFoundError: common` +原因: +- PYTHONPATH が通っていない +対策: +- devcontainer:containerEnv / postCreate で固定 +- アプリコンテナ:compose の environment or entrypoint.sh で `export PYTHONPATH=/app` + +### 事故B:`python app1/main_app1.py` で動くが `pytest` で落ちる +原因: +- 実行方法が `-m` に統一されていない +対策: +- すべて `python -m ...` に統一 + +### 事故C:devcontainerでは動くが compose コンテナで落ちる +原因: +- devcontainer は /workspaces、compose は /app で import root が違う +対策: +- 方式A:compose 側は /app を import root に固定し、entrypointで PYTHONPATH を保証 + +--- + +## 100.11 Wiki貼り付け用 “common参照 & パス調整” 最終宣言(短文化・省略なし) + +```text +【common参照とパス調整(devcontainer/compose/CI統一)】 +1) Pythonの import root は apps/python に固定し、common は apps/python/common に置く。 +2) common の参照は絶対import(from common...)のみを許可し、相対importとsys.path改変は禁止。 +3) 起動は python -m を必須とし、実行ディレクトリ依存の起動(python file.py)は禁止。 +4) devcontainer は workspace が /workspaces/ になるため、PYTHONPATH=/workspaces//apps/python を固定する。 +5) compose で起動するアプリコンテナは apps/python を /app に揃え、PYTHONPATH=/app を固定する。 +6) entrypoint.sh(shell経由起動)で export PYTHONPATH を保証し、exec python -m ... で起動する。 +7) pytest は repo-root から実行し、CI/ローカルは PYTHONPATH=$PWD/apps/python を統一して全アプリのUnitを走らせる。 +8) devcontainer とアプリコンテナで PYTHONPATH 値が異なってもよいが、「各コンテナ内で common が見える」ことを要件とする。 +``` + +--- + +## 101. CD/CI(継続的デリバリ/継続的デプロイ)でのUT運用と “common + パス” を壊さないための追加仕様(省略なし) + +> 目的: +> - CI(PR)と CD(main / release)で **同じUTが同じ条件で動く** +> - `common` の参照(PYTHONPATH)と “実行場所差分” で落ちない +> - Dependabot PR でも “同じ品質ゲート” が適用される +> - cloud / onprem / local のデプロイ前に “必ずUTが通る” を強制する + +--- + +## 101.1 CD/CI の段階定義(必須) +### 101.1.1 CI(Pull Request) +- 目的:マージ前に破壊を止める +- 必須:Lint + Unit Test(全アプリ) + Secret検知 + 依存脆弱性(可能なら) + +### 101.1.2 CD(main / release) +- 目的:デプロイ可能な成果物を作る +- 必須:CIと同じUnit Testを **再実行**(キャッシュ差や条件差で事故を防ぐ) +- 任意:Integration / E2E(段階的に) + +### 101.1.3 Deploy(cloud / onprem) +- 目的:本番反映 +- 必須:Deployジョブの前に Unit Test が “同じ条件で” PASS していること(ゲート) + +--- + +## 101.2 “UTはどこで実行しても同じ” を成立させる単一ルール(必須) +### 101.2.1 UT 実行コマンドを scripts に一本化(必須) +- CI/CD は `pytest ...` を直書きしない +- **scripts/test-python.sh を単一情報源**にする + +#### `scripts/test-python.sh`(確定テンプレ) +- 実行場所(カレント)に依存させない +- PYTHONPATH を固定する +- integration を除外する(unitのみ) + +```bash +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." + +export PYTHONPATH="$PWD/apps/python:${PYTHONPATH:-}" +pytest -v -m "not integration" +``` + +> ルール:CIもCDもローカルも devcontainer も **必ずこのスクリプトを叩く**。 + +--- + +## 101.3 CI(PR)でのUT:common参照とパス差分を完全排除(必須) +### 101.3.1 GitHub Actions:Python UT ワークフロー(scripts呼び出し) + +`.github/workflows/python-ci.yml` の UT 部分は、必ず `scripts/test-python.sh` を呼ぶ: + +```yaml +- name: Unit tests (single source) + run: | + chmod +x scripts/test-python.sh + ./scripts/test-python.sh +``` + +> これにより PYTHONPATH が $GITHUB_WORKSPACE/apps/python に揃い、common が必ず見える。 + +--- + +## 101.4 CD(main)でのUT:デプロイ前に “同一条件で再実行” を強制(必須) +### 101.4.1 release workflow の構造(必須) +- `unit` → `build` → `deploy` +- `deploy` は `needs: [unit, build]` を必須とする +- “buildだけ成功して deploy される” を禁止 + +#### 例:`.github/workflows/release-cloud.yml`(骨格:unitを先に置く) +```yaml +name: release-cloud + +on: + push: + branches: ["main"] + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install test deps + run: | + python -m pip install --upgrade pip + # 依存方式に合わせてここを統一(requirements/poetry/uv等) + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install pytest + - name: Unit tests + run: | + chmod +x scripts/test-python.sh + ./scripts/test-python.sh + + build: + needs: unit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - name: Build app1 + run: | + docker build -f apps/python/app1/Dockerfile -t myorg/app1:sha-${{ github.sha }} apps/python + - name: Build batch + run: | + docker build -f apps/python/batch/Dockerfile -t myorg/batch:sha-${{ github.sha }} apps/python + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Deploy (prod) + run: | + chmod +x scripts/deploy-cloud.sh + ./scripts/deploy-cloud.sh prod +``` + +> ルール:CDでもUTを再実行する(PRで通っていても必須)。 + +--- + +## 101.5 “Dockerイメージ内でUTを回す” 方式(必要時の追加仕様) +> 「本番と同じ環境でUTを回したい」場合に追加。 +> ただし時間が増えるので “必要なときだけ” 採用。 + +### 101.5.1 UT用のテストターゲットを作る(推奨) +- Dockerfile を multi-stage にして `test` ターゲットを持つ +- `PYTHONPATH=/app` を固定 + +(概念:apps/python/app1/Dockerfile の test ステージ) +```dockerfile +# syntax=docker/dockerfile:1 +FROM python:3.12-slim AS base +WORKDIR /app +COPY . /app +ENV PYTHONPATH=/app + +FROM base AS test +RUN pip install -r /app/requirements.txt && pip install pytest +CMD ["pytest", "-v", "-m", "not integration"] +``` + +> ルール:testステージでも `PYTHONPATH=/app` を固定し、common が必ず見える。 + +--- + +## 101.6 Dependabot PR のUT(混在画面でも漏れなく)運用(必須) +### 101.6.1 Dependabot PR でも同じ必須チェック +- ブランチ保護で `python-ci / unit` を必須化 +- `scripts/test-python.sh` を唯一のUT入口にすることで差分を消す + +### 101.6.2 依存更新で落ちた場合の修正原則(必須) +- common を壊す修正を “応急処置” として入れない +- 修正は以下の順で限定する: + 1) 依存の固定(上げない/ピン止め) + 2) テストコードの更新(正しい破壊なら) + 3) アプリコード修正(API変更追従) +- “CIを通すだけ” のパスいじり(sys.path等)は禁止 + +--- + +## 101.7 “環境差(devcontainer/compose/CI)でUTが割れる” 典型事故と封じ方(必須) + +### 事故1:devcontainerでは通るがCIで `common` が見えない +封じ方: +- CIは必ず `scripts/test-python.sh` を叩く +- `scripts/test-python.sh` 内で `cd repo-root` と `PYTHONPATH=$PWD/apps/python` を固定 + +### 事故2:composeコンテナで実行したUTが壊れる +封じ方: +- composeコンテナ内は `/app` が import root(方式A) +- entrypointで `PYTHONPATH=/app` を保証 +- UTもコンテナ内で回すなら `PYTHONPATH=/app` を固定したコマンドに統一 + +### 事故3:`python file.py` でしか動かない +封じ方: +- 起動は `python -m` を強制 +- scripts/ に起動コマンドを集約し、直叩き禁止 + +--- + +## 101.8 Wiki貼り付け用 “CD/CI UT運用 最終宣言”(短文化・省略なし) + +```text +【CD/CIでのUnit Test運用(common・パス差分対策込み)】 +1) Unit TestはCI(PR)とCD(main/release)で同一条件で必ず実行する。デプロイ前ゲートとする。 +2) UTコマンドは scripts/test-python.sh に一本化し、CI/CD/ローカル/devcontainerの全てがそれを叩く。 +3) scripts/test-python.sh は repo-root に移動し、PYTHONPATH=$PWD/apps/python を固定して common を常に参照可能にする。 +4) 起動方式は python -m を必須とし、実行ディレクトリ依存(python file.py)や sys.path改変は禁止。 +5) Dependabot PR でも同一UTチェックを必須化し、ブランチ保護で通過しない限りマージ不可とする。 +6) 本番同等環境でのUTが必要ならDockerのtestステージを導入し、PYTHONPATH=/app を固定して実行する。 +7) devcontainer(/workspaces) と composeコンテナ(/app) はパスが違ってよいが、各環境で import root を固定して差分を吸収する。 +``` + +--- diff --git a/.github/copilot/05-structure/polyrepo.md b/.github/copilot/05-structure/polyrepo.md new file mode 100644 index 0000000..aff9d67 --- /dev/null +++ b/.github/copilot/05-structure/polyrepo.md @@ -0,0 +1,3 @@ +# 05 Structure — ポリレポ運用ルール(プレースホルダー) + +ポリレポ構成に切り替える際に、本ファイルに運用ルールを記載してください。現時点ではモノレポ構成を採用しているため、内容は未実装です。今後の切替に備えて本ファイルは削除せず保持します。 diff --git a/.github/copilot/05-structure/sakura_repo.md b/.github/copilot/05-structure/sakura_repo.md new file mode 100644 index 0000000..dcf15d2 --- /dev/null +++ b/.github/copilot/05-structure/sakura_repo.md @@ -0,0 +1,56 @@ +# sakura_repo.md — レンタルサーバー専用リポジトリ構成定義 + +## 概要 +- レンタルサーバー環境で単一アプリを運用するための構成定義レイヤー。 +- リポジトリルートはサーバー上のユーザープロファイルパス(例: `/home//`)と一致し、モノレポ構成とは独立して扱う。 +- CI は GitHub Actions ではなく、専用ランナーがリポジトリ全体をクローンしてミラーリングする前提。 + +## リポジトリ前提 +- 配置パス: `/home//` 直下にリポジトリを配置する。 +- 単一アプリ構成を前提とし、外部リポジトリへのシンボリックリンクやサブモジュール依存は行わない。 +- composer による依存管理を行い、PHP プロジェクト運用に限定する。 + +## 運用ポリシー +- テストは PHPUnit を使用し、バージョンを 10.5.38 に固定する。 +- 専用ランナーが 1 時間ごとにポーリングし、GitHub 上の当該リポジトリをクローンしてレンタルサーバー上に完全同期(ミラーリング)する。 + - 差分コピーではなく全体同期を原則とする。 + - デプロイおよび SSH 転送ログはランナー側で保存し、サーバーには Secrets を残さない。 +- ブランチ運用は単一アプリ向けの簡潔さを優先し、モノレポと異なる開発・検証サイクルを維持する。 + +## CI・テスト実行 +ローカルまたは専用ランナー上で以下を実行する。 + +```bash +$ vendor/bin/phpunit --configuration phpunit.xml +# 使用バージョン: phpunit/phpunit 10.5.38 +``` + +## デプロイ/ポーリング手順 +1. 専用ランナーが 1 時間ごとにリポジトリをクローン。 +2. 最新コミットで PHPUnit 10.5.38 を実行し、失敗した場合はデプロイを中断。 +3. 成功時にリポジトリ全体を `/home//` 直下へミラーリングして反映。 +4. 同期後のログをサーバー側に保管し、次回ポーリングで再評価する。 + +## 拡張方針 +- 将来的にモノレポ定義と統合する場合でも、本ドキュメントの内容を構成層として吸収できるよう章立てを維持する。 +- CI/デプロイの追加要件が発生した際は、専用ランナー前提を崩さずに拡張する。 + +## 品質ゲート +- PHPUnit 10.5.38 が成功すること。 +- デプロイはミラーリング(完全同期)で行われ、Secrets をサーバー上に残さないこと。 +- Markdown 構文エラーがないこと。 + +## 参照ポリシー +- 本ドキュメントはレンタルサーバー向けに単独完結させ、他の構成定義ファイルへの参照を前提としない。 + +## フロー可視化 +```mermaid +flowchart TD + A[開発者] --> B[sakura_repo.md 作成・更新] + B --> C[PHPUnit 10.5.38 ルール適用] + C --> D[ローカルまたは専用ランナーでテスト実行] + D --> E[テスト結果確認] + E --> F[専用ランナーがリポジトリを完全同期デプロイ] + F --> G[1時間ごとのポーリングで再実行] + G --> H[構成定義を見直し・反映] +``` diff --git a/.github/copilot/10-requirements.md b/.github/copilot/10-requirements.md new file mode 100644 index 0000000..785f03c --- /dev/null +++ b/.github/copilot/10-requirements.md @@ -0,0 +1,29 @@ +# 10 Requirements — 要件とスコープ + +## 目的 +- Copilot / 自動エージェントが参照する仕様を一元化し、設計→実装の再現性を高める。 + +## スコープ +- **In Scope**: `.github/copilot/` 配下の仕様整備、`.github/instructions/**/*.instructions.md` の実務指示、plan / PR / review テンプレート。 +- **Out of Scope**: CI/CD ジョブ実装や既存アプリのリファクタリング(別 Issue で扱う)。 + +## 機能要件 +- 仕様ファイルが二層構造(規範層と仕様層)で整理され、参照順が明示されていること。 +- 80-templates/implementation-plan.md を用いた設計出力が必須であること。 +- CI 品質ゲート(lint / typecheck / test / security)が文書化されていること。 + +## 非機能要件 +- **再現性**: 同一 plan から同一成果物を得られる。 +- **自動化**: CI で品質ゲートを通過しない限りマージ不可(ブランチ保護を前提)。 +- **セキュリティ**: Secrets/PII をログ・差分に出さない。 +- **拡張性**: ファイル単位で改訂しやすく、重複を避ける。 + +## 受入条件 +- `copilot-instructions.md` が短く強い規範として完成している。 +- `10-60` それぞれに最小構成の内容があり、`00-index` から辿れる。 +- `80-templates/implementation-plan.md` が設計出力に利用されている。 +- CI 品質ゲートの最低要件が文書化されている。 + +## 未確定事項(決定方針) +- CI 実行基盤: チームの運用基盤(GitHub Actions 等)に合わせて選定。 +- ADR の命名規約: 連番方式(例: `ADR-0001-title.md`)を基本とし、テンプレート `ADR-0001-template.md` と統一する。 diff --git a/.github/copilot/20-architecture.md b/.github/copilot/20-architecture.md new file mode 100644 index 0000000..bd5b606 --- /dev/null +++ b/.github/copilot/20-architecture.md @@ -0,0 +1,36 @@ +# 20 Architecture — 二層構造と開発ループ + +## 二層構造 +- **規範層**: `.github/copilot-instructions.md` — 短く強いルール。全タスク共通。 +- **仕様層 (SSOT)**: `.github/copilot/*.md` — 要件・設計・品質・セキュリティの単一情報源。 +- **実行指示レイヤ**: `.github/instructions/**/*.instructions.md` — `applyTo` で対象ファイルに適用される補助的な設計/背景資料レイヤ。 + +## 開発フロー(2段階ループ) +```mermaid +flowchart TD + A["Copilot Agent"] --> B["設計出力 (implementation-plan.md)"] + B --> C["人間レビュー(設計)"] + C --> D["実装"] + D --> E["CI品質ゲート (lint/test/security)"] + E -->|Pass| F["PRマージ"] + E -->|Fail| G["修正・再実行"] +``` + +- Phase A (Design): `80-templates/implementation-plan.md` に沿って plan を作り、レビューで固定する。 +- Phase B (Implement): 確定 plan の範囲で実装し、CI 品質ゲートを全て通過させる。 + +## 責務分担 +- 仕様の更新・判断根拠: `.github/copilot/` と `70-adr/` +- 実務上の禁止事項・コマンド: `.github/instructions/`(パス適用) +- 実装・テスト計画: `80-templates/implementation-plan.md` + +## 依存と適用範囲 +- すべてのタスクは `00-index.md` の参照順を守る。 +- 破壊的変更や例外的運用は必ず ADR または plan に残す。 + +## Issue 運用ルール +- Copilot を Issue にアサインする前に、必要な要件は本文へすべて記載する(アサイン後のコメントは認識されないため、追記は PR コメントで渡す)。 +- 設計と実装の Issue は分離し、設計 Issue はドキュメントのみの PR、実装 Issue は確定 plan のパスを明示してその範囲に限定する。 +- 不確実性解消のために **[RESEARCH]** Issue を設け、コード変更禁止を基本とする(成果物は docs/research や Issue/PR コメントに集約し、結論は ADR/Requirements へ昇格させてから Design に渡す)。 +- 仕様参照を避けたい場合は、任意フェーズの Issue に付与できるモディファイアとして **[BLIND]** を用いる。本文に必要情報を埋め込み、参照禁止範囲と変更可否を明示する。成果物は最小差分かつ本文完結とする。 +- 推奨する最小セット: `[RESEARCH]`(調査のみ)、`[DESIGN]`(plan確定)、`[IMPLEMENT]`(plan通り実装)、`[BLIND]`(任意フェーズに付与可能な仕様参照抑制モディファイア)。 diff --git a/.github/copilot/30-coding-standards.md b/.github/copilot/30-coding-standards.md new file mode 100644 index 0000000..b23bd90 --- /dev/null +++ b/.github/copilot/30-coding-standards.md @@ -0,0 +1,10 @@ +# 30 Coding Standards — コーディング規約 + +- `.github/instructions/**/*.instructions.md` のパス別ルールを優先する。 +- 互換性維持をデフォルトとし、破壊的変更は移行策・理由を plan / ADR に記載する。 +- Python: 型ヒント必須、例外は無視せずログを記録して再送出し、`print` ではなく構造化ログを使う。 +- 依存追加は最小限とし、バージョンをピン止めして `requirements.txt` / `constraints` に反映する。 +- ログ/コメント/Doc は簡潔に。秘密情報・個人情報をログやコメントに残さない。 +- テスト可能な構造(副作用を分離、関数・メソッドを小さく)を心掛ける。 +- コミットメッセージ: + - `.github/instructions/commit-messages.instructions.md` を唯一の参照源とし、Copilot 生成を含む全コミットで同じ日本語・プレフィックス・3行構造ルールを適用する。 diff --git a/.github/copilot/40-testing-strategy.md b/.github/copilot/40-testing-strategy.md new file mode 100644 index 0000000..a661168 --- /dev/null +++ b/.github/copilot/40-testing-strategy.md @@ -0,0 +1,7 @@ +# 40 Testing Strategy — テスト戦略 + +- 受入条件をテストで担保すること。新機能やバグ修正には回帰テストを必ず追加。 +- 優先度: 重要なドメインロジック → エッジ/例外系 → I/O や外部依存を分離したユニットテスト。 +- pytest 前提。外部 API や時間/環境依存はフィクスチャやモックで隔離する。 +- テストデータは最小限・再利用可能に保ち、副作用(ファイル/ネットワーク)を残さない。 +- スローテスト・結合テストはマーカーを付け、必要に応じて分離実行できるようにする。 diff --git a/.github/copilot/50-security.md b/.github/copilot/50-security.md new file mode 100644 index 0000000..9951301 --- /dev/null +++ b/.github/copilot/50-security.md @@ -0,0 +1,7 @@ +# 50 Security — セキュリティ方針 + +- Secrets/PII をコード・ログ・ドキュメントに出さない。ダミー値やマスキングを使用する。 +- 入力値・環境変数・外部レスポンスは必ず検証し、想定外は明示的に失敗させる。 +- ログは構造化し、エラー原因を追える粒度で記録するが、機密情報は含めない。 +- 依存追加時は脆弱性を確認し、不要な権限・スコープを避ける。 +- CI でも Secrets を最小権限で扱い、書き込み権限が不要な場合は `permissions: read-all` を基本とする。 diff --git a/.github/copilot/60-ci-quality-gates.md b/.github/copilot/60-ci-quality-gates.md new file mode 100644 index 0000000..8babe78 --- /dev/null +++ b/.github/copilot/60-ci-quality-gates.md @@ -0,0 +1,14 @@ +# 60 CI Quality Gates — 必須ジョブと基準 + +## 必須ジョブ(例) +- **format**: `black .`(またはプロジェクト指定のフォーマッタ) +- **lint**: `ruff check .` など静的解析を実行 +- **typecheck**: `mypy .` で型整合性を確認 +- **test**: `python -m pytest` でユニット/回帰テストを実行 +- **security**: `pip-audit -r requirements.txt` など依存脆弱性スキャン + +## 運用ルール +- すべての必須ジョブをブランチ保護の required status checks に設定し、失敗時はマージ不可。 +- CI ログに Secrets/PII を出さない。必要な権限のみを `permissions` で明示する。 +- キャッシュや並列実行は再現性を損なわない範囲で利用し、結果が変わる場合は無効化する。 +- 品質ゲートで検出した問題は plan / PR に反映し、再現手順と修正内容を残す。 diff --git a/.github/copilot/70-adr/ADR-template.md b/.github/copilot/70-adr/ADR-template.md new file mode 100644 index 0000000..9195909 --- /dev/null +++ b/.github/copilot/70-adr/ADR-template.md @@ -0,0 +1,22 @@ +# ADR-template: +- Status: Proposed | Accepted | Superseded | Deprecated +- Date: YYYY-MM-DD + +## Context +- 背景・問題 +- 制約や前提 + +## Decision +- 選択した方針 +- 理由(採用/却下含む) + +## Consequences +- 正の影響 +- 負の影響 / トレードオフ +- 移行・ロールバック方針 + +## Alternatives Considered +- 検討した代替案と採否理由 + +## References +- 関連 Issue / PR / ドキュメント diff --git a/.github/copilot/80-templates/code-review-checklist.md b/.github/copilot/80-templates/code-review-checklist.md new file mode 100644 index 0000000..4b2a3c3 --- /dev/null +++ b/.github/copilot/80-templates/code-review-checklist.md @@ -0,0 +1,5 @@ +- [ ] 変更が plan と受入条件を満たしているか +- [ ] 互換性リスクや移行手順が明示されているか +- [ ] エッジケース・例外処理・ログが適切か(秘密情報を含まないか) +- [ ] テストが十分か(正常/例外/回帰、モックの妥当性) +- [ ] CI 品質ゲートが全て通過しているか diff --git a/.github/copilot/80-templates/implementation-plan.md b/.github/copilot/80-templates/implementation-plan.md new file mode 100644 index 0000000..ea32ca4 --- /dev/null +++ b/.github/copilot/80-templates/implementation-plan.md @@ -0,0 +1,32 @@ +# Implementation Plan テンプレート + +## 1. ゴール / 非ゴール +- ゴール: +- 非ゴール: + +## 2. スコープと変更対象 +- 変更ファイル(新規/修正/削除): +- 影響範囲・互換性リスク: +- 外部依存・Secrets の扱い: + +## 3. 設計方針 +- 責務分離 / データフロー(必要なら Mermaid 1 枚): +- エッジケース / 例外系 / リトライ方針: +- ログと観測性(漏洩防止を含む): + +## 4. テスト戦略 +- テスト観点(正常 / 例外 / 境界 / 回帰): +- モック / フィクスチャ方針: +- テスト追加の実行コマンド(例: `python -m pytest`): + +## 5. CI 品質ゲート +- 実行コマンド(format / lint / typecheck / test / security): +- 通過基準と失敗時の対応: + +## 6. ロールアウト・運用 +- ロールバック方法: +- 監視・運用上の注意: + +## 7. オープンな課題 / ADR 要否 +- 未確定事項: +- ADR に残すべき判断: diff --git a/.github/copilot/80-templates/pr-checklist.md b/.github/copilot/80-templates/pr-checklist.md new file mode 100644 index 0000000..a717e6a --- /dev/null +++ b/.github/copilot/80-templates/pr-checklist.md @@ -0,0 +1,5 @@ +- [ ] 目的とスコープが明記され、plan から逸脱していない +- [ ] 変更点を要約し、互換性リスクとロールバック方法を記載 +- [ ] テスト結果(コマンドと結果)を記載し、必要なテストを追加 +- [ ] CI 品質ゲート(format / lint / typecheck / test / security)が全て緑 +- [ ] ドキュメント(README/仕様/ADR)が必要に応じて更新済み diff --git a/.github/copilot/90-research/design-implement-plan-handoff.md b/.github/copilot/90-research/design-implement-plan-handoff.md new file mode 100644 index 0000000..36a76cc --- /dev/null +++ b/.github/copilot/90-research/design-implement-plan-handoff.md @@ -0,0 +1,22 @@ +# 調査結果: DESIGN → IMPLEMENT 間の plan 保存/参照構造 + +本ドキュメントは [RESEARCH] Issue における DESIGN→IMPLEMENT ハンドオフの調査結果をまとめたものです。仕様の入口は [00-index.md](../00-index.md) を参照してください。 + +## 1. 事実と根拠 +- plan作成は [00-index.md](../00-index.md) と [20-architecture.md](../20-architecture.md) で Phase A(DESIGN)として明示され、出力テンプレートは [implementation-plan.md](../80-templates/implementation-plan.md) に固定されている。 +- IMPLEMENT Issue の入力は `.github/copilot/plans/<issue-number>-implementation-plan.md` をリポジトリルートからの相対パスで固定する運用が [README の「4.3 [IMPLEMENT] 実装 Issue」テンプレート](../../../README.md)で求められている。 +- 現状 `.github/copilot/plans/` ディレクトリや `*implementation-plan*.md` ファイルは存在せず、生成は自動化されていない。 +- `copilot/plans` を参照するコードや Workflow 定義は存在せず、plan 保存は手動運用前提となっている。 + +## 2. 原因仮説の切り分け結果 +- DESIGN完了時に手動で `.github/copilot/plans/<issue-number>-implementation-plan.md` を作成・保存する運用を想定しているが、ディレクトリ未作成のため実体がなく、IMPLEMENT Issue で指定したパスがファイル未検出エラーになるリスクがある。 +- 命名規則(`<issue-number>-implementation-plan.md`)が README 上の記述に依存しており、ヒューマンエラーによる取り違え・置き場所不一致が起きやすい。 + +## 3. 選択肢(Pros/Cons)と推奨案 +- **手動運用を明示する**: DESIGN手順のチェックリストに「`.github/copilot/plans/` を作成し、`<issue-number>-implementation-plan.md` を配置する」という項目を追記するだけで済む。追加開発なし。ただし人的ミスは残る。 +- **スクリプト/Workflowでplanを生成する(推奨)**: Issue 番号を入力すると `.github/copilot/plans/<issue-number>-implementation-plan.md` をテンプレートから生成する簡易スクリプトを用意する、または Issue Template 連動 Workflow で自動生成する。命名・配置を強制できるため、人的ミス削減と運用一貫性に有効。 + +## 4. 次アクション +- **起票**: `[DESIGN] plan の保存と参照の統一化` を新規 Issue として起票する。起票責任: リポジトリ OWNER(LevelCapTech)。期限目安: 本PRマージ後 1 週間以内。 +- **スコープ**: `.github/copilot/plans/` ディレクトリの常設、`<issue-number>-implementation-plan.md` 命名規則の固定、生成フロー(手動チェックリスト強化またはスクリプト/Action自動化)の決定。 +- **SSOT反映**: 決定したルールを `.github/copilot/` 配下の SSOT ドキュメント(該当する場合は [20-architecture.md](../20-architecture.md) か [10-requirements.md](../10-requirements.md))へ反映し、README は要約リンクにとどめる。 diff --git a/.github/copilot/90-research/freee_api_research.md b/.github/copilot/90-research/freee_api_research.md new file mode 100644 index 0000000..177dc47 --- /dev/null +++ b/.github/copilot/90-research/freee_api_research.md @@ -0,0 +1,67 @@ +# クラウド会計 freee API 連携 技術調査まとめ + +本ドキュメントは [RESEARCH] Issue における調査結果をまとめたものです。仕様の入口は [00-index.md](../00-index.md) を参照してください。 +freee 会計 API を利用した自動連携・データ同期に向けた調査結果を整理し、認証、主要エンドポイント、制約、運用方針をまとめています。実データや Secrets は一切含めません。 + +## 1. 技術要件サマリー +- 認証: OAuth 2.0 Authorization Code Flow(機密クライアントは client_secret、パブリッククライアントは PKCE 推奨)。トークンエンドポイントは `https://accounts.secure.freee.co.jp/public_api/token`、認可エンドポイントは `https://accounts.secure.freee.co.jp/public_api/authorize`。 +- API ベース URL: `https://api.freee.co.jp/api/1`. 会社 ID(`company_id`)を必須パラメータとして多くのエンドポイントで指定。 +- スコープ: 読み取りのみなら `read`、更新を伴う場合は `write`。長期運用では `offline_access` を付与してリフレッシュトークンを取得する。 +- トークン有効期限: アクセストークンは約 1 時間、リフレッシュトークンは長期(約 30 日)で都度ローテーションされる。更新時は新しい refresh_token を保存する。 +- レートリミット: 代表的に 60 リクエスト/分/アクセストークン(`X-RateLimit-*` ヘッダーで通知)。超過時は HTTP 429 + `Retry-After`。 +- データ取得: REST + JSON。ページングは `limit`(API 仕様上のデフォルト 20、最大 100)と `offset` で制御する。本設計ではバッチ同期時はレートとレスポンスサイズのバランスを考慮し、原則 `limit=100` を明示指定し、軽量な確認・デバッグ用途のみデフォルト値 20 を利用する。 +- Sandbox: 開発者向けのサンドボックスが用意されており、同一 OAuth 設定で利用可能(申請が必要)。データはサンプル会社のみで、差分があるため仕様確認用に限定する。Sandbox の運用条件は現在未確定であり、別途決定予定(詳細は plan の「7. オープンな課題」を参照)。 + +## 2. OAuth 2.0 設定手順 +1. **アプリ登録**(freee デベロッパーポータル) + - `redirect_uri` を正確に登録(本番/開発で複数可)。 + - 利用スコープに `read`(必要に応じて `write`)と `offline_access` を指定。 + - Web/サーバーサイドの場合は `client_secret` を安全に保管。モバイル等のパブリッククライアントは PKCE を使用。 +2. **認可リクエスト** + - GET `https://accounts.secure.freee.co.jp/public_api/authorize` + - 主なクエリ: `client_id`, `response_type=code`, `redirect_uri`, `scope=read offline_access`, `state`(CSRF 対策), `code_challenge`/`code_challenge_method=S256`(PKCE 利用時)。 +3. **アクセストークン取得** + - POST `https://accounts.secure.freee.co.jp/public_api/token` + - `grant_type=authorization_code`, `code`, `redirect_uri`, `client_id` に加え、**機密クライアントは `client_secret` を送信し、パブリッククライアント(PKCE 利用時)は `code_verifier` を送信する。両者は排他的に使用し、併用しない**。 + - レスポンス: `access_token`, `token_type`, `expires_in`(秒)、`refresh_token`(offline_access 付与時)。 +4. **リフレッシュ** + - POST 同エンドポイントに `grant_type=refresh_token`, `refresh_token`, `client_id`, `client_secret`(**機密クライアントの場合。パブリッククライアント(PKCE 利用時)は `client_secret` 不要**)。 + - リフレッシュトークンはローテーションされるため、レスポンスの新しい `refresh_token` で必ず置き換える。 +5. **再認証ポリシー** + - 401/invalid_grant 受信時はリフレッシュ試行後に再認可フローへフォールバック。 + - リフレッシュ失敗が続く場合はユーザー再同意を要求する。 + +## 3. 主要エンドポイント一覧(取得系) + +以下のエンドポイントパスは API ベース URL `https://api.freee.co.jp/api/1` からの相対パスです(例: `/walletables` → `https://api.freee.co.jp/api/1/walletables`)。 + +| 用途 | メソッド / パス | 主なパラメータ | 出力の要点 | 必要スコープ | +| --- | --- | --- | --- | --- | +| 取引(deals)一覧 | GET `/deals` | `company_id`(必須)、`limit`、`offset`、`updated_since`、`start_date`/`end_date` | `deals` 配列。各要素に `id`, `issue_date`, `type`(income/expense), `partner_id`, `ref_number`, `details[]`(`account_item_id`, `tax_code`, `amount`, `item_id`, `section_id`, `tag_ids` など) | `read` | +| 仕訳(journals)一覧 | GET `/journals` | `company_id`(必須)、`start_date`、`end_date`、`entry_side`(debit/credit/both)、`limit`、`offset` | `journal_entries` 配列。`id`, `date`, `entry_side`, `account_item_id`, `sub_account_item_id`, `amount`, `section_id`, `tag_ids`, `description` 等 | `read` | +| 口座・残高(walletables) | GET `/walletables` | `company_id`(必須)、`walletable_type`(bank, credit_card, wallet) | `walletables` 配列。`id`, `name`, `walletable_type`, `last_balance`(最新残高), `last_balance_date` | `read` | +| 口座取引(wallet_txns) | GET `/wallet_txns` | `company_id`(必須)、`start_date`、`end_date`、`walletable_type`、`walletable_id`、`limit`、`offset` | `wallet_txns` 配列。`id`, `date`, `amount`, `walletable_id`, `entry_side`, `description`, `balance` | `read` | +| 取引先(partners) | GET `/partners` | `company_id`(必須)、`limit`、`offset`, `q`(名称検索) | `partners` 配列。`id`, `name`, `code`, `shortcut1/2`, `long_name`, `address` 等 | `read` | + +> 更新系(POST/PUT/DELETE)は `write` スコープが必要。複数会社を扱う場合は会社ごとに認可を得て `company_id` を切り替える。 + +## 4. レートリミットとリトライ +- 代表的制限: 60 リクエスト/分/アクセストークン(会社単位)。詳細はレスポンスヘッダー `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` を参照。 +- 超過時: HTTP 429 と `Retry-After` 秒数が返る。`Retry-After` + ジッターで再試行する。バックオフ上限を設け、連続失敗時はキューに退避。 +- 受信データのページング: `limit` と `offset` でバッチ取得し、レートを消費しすぎないよう 1 リクエストあたり 100 件を上限に設計。 +- 冪等性: GET は安全。将来の POST/PUT は同一 `ref_number` やクライアント側 ID を用いた重複防止を検討。 + +## 5. 運用・監査方針 +- **トークン管理**: `access_token`/`refresh_token` を KMS/Secret Manager 等で暗号化保管。ローテーション後の旧トークンは即廃棄。ログに平文を出さない。 +- **再認証**: リフレッシュに失敗した際は影響範囲を最小化するため会社単位で再同意フローを案内(運用例: 管理者へメール通知とダッシュボード上の再認可アラートを即時表示し、再認可 URL を発行)。失敗回数をメトリクス化。 +- **ログ/トレーサビリティ**: リクエスト ID(`X-Freee-Request-Id` ヘッダー)を収集し、429/401/5xx をアラート化。PII/Secrets はマスク。 +- **エラー設計**: 401(トークン失効)→リフレッシュ後リトライ。429→`Retry-After` 遵守でバックオフ。5xx→指数バックオフ + 上限。バリデーション 4xx は即座にデッドレター行き。 +- **監査**: 認可同意・トークン発行・再認証イベントを監査ログに残し、権限付与履歴を定期レビューする。 +- **デプロイ環境想定**: サーバーレス(Cloud Run / Lambda)を想定し、秘密情報は環境変数 + Secret Manager 連携で注入。HTTP クライアントは keep-alive + タイムアウト(接続 5s / 応答 30s 目安)設定。 + +## 6. DESIGN への引き渡し観点 +- 認証モジュール: Authorization Code + PKCE をデフォルト。トークンストアの抽象化とローテーション対応を設計。 +- データ取得: ページング・レート制御付きのフェッチャー(`limit=100`, offset 進行)。取引/仕訳/口座残高/取引先を会社単位で切り替え可能にする。 +- フォールトトレランス: 429/5xx バックオフ、401 再認証、タイムアウト・再送回数のポリシーを設定(例: 3 回指数バックオフ)。 +- 監査・オブザーバビリティ: リクエスト ID, HTTP ステータス, レート残数をメトリクス化し、PII マスク済みログで収集。 +- Sandbox 利用: 初期実装は Sandbox での結合確認、実データ移行は本番発行クライアントで段階的に行う。 diff --git a/.github/copilot/90-research/freee_api_research_plan.md b/.github/copilot/90-research/freee_api_research_plan.md new file mode 100644 index 0000000..15d7a19 --- /dev/null +++ b/.github/copilot/90-research/freee_api_research_plan.md @@ -0,0 +1,34 @@ +# freee会計API連携 Implementation Plan + +本ドキュメントは [RESEARCH] Issue の計画書です。仕様の入口は [00-index.md](../00-index.md) を参照してください。 + +## 1. ゴール / 非ゴール +- ゴール: freee会計API連携に向けた技術調査を完了し、DESIGNで仕様化できる材料(認証、エンドポイント、制約、運用方針)をMarkdownで整理する。 +- 非ゴール: 実装コードの追加・変更、インフラ構築、実データを用いた接続検証。 + +## 2. スコープと変更対象 +- 変更ファイル(新規/修正/削除): 調査結果ドキュメント(`.github/copilot/90-research/freee_api_research.md`)、本プランファイル。 +- 影響範囲・互換性リスク: ドキュメントのみでアプリケーション挙動への影響なし。 +- 外部依存・Secrets の扱い: 外部APIの仕様引用のみ。クライアントID/シークレット等の秘密情報は記載しない。 + +## 3. 設計方針 +- 責務分離 / データフロー: 認証フロー、データ取得、運用・セキュリティを章立てで整理し、DESIGNでそのまま利用できるようにする。 +- エッジケース / 例外系 / リトライ方針: レートリミット(429)やトークン失効時の再認証・リトライ指針を明記する。 +- ログと観測性(漏洩防止を含む): トレーサビリティ確保とSecrets非出力を原則として運用案を記載する。 + +## 4. テスト戦略 +- テスト観点: ドキュメント整合性のセルフレビューのみ。コード・APIコールは実施しない。 +- モック / フィクスチャ方針: 非該当(調査ドキュメントのみ)。 +- テスト追加の実行コマンド: なし(自動テスト対象外)。 + +## 5. CI 品質ゲート +- 実行コマンド: なし(ドキュメント変更のみ)。必要に応じてMarkdown lintは手元確認。 +- 通過基準と失敗時の対応: CIでドキュメント差分確認のみ想定。破損時は内容を修正して再実行。 + +## 6. ロールアウト・運用 +- ロールバック方法: 追加ドキュメントを削除または元に戻す。 +- 監視・運用上の注意: なし(運用対象外)。 + +## 7. オープンな課題 / ADR 要否 +- 未確定事項: Sandbox提供可否の運用条件、最終的なデプロイ先環境は別途決定。 +- ADR に残すべき判断: 必要に応じて認証/Secrets管理方式をDESIGN以降でADR化。 diff --git a/.github/instructions/Readme.md b/.github/instructions/Readme.md new file mode 100644 index 0000000..3483e9a --- /dev/null +++ b/.github/instructions/Readme.md @@ -0,0 +1,5 @@ +*.instructions.md というファイル名で1つ以上作成します。ファイル名末尾は必ず .instructions.md。 + +# 参考 + +[GitHub Copilot のリポジトリカスタム指示の追加](https://docs.github.com/ja/copilot/how-tos/configure-custom-instructions/add-repository-instructions) \ No newline at end of file diff --git a/.github/instructions/commit-messages.instructions.md b/.github/instructions/commit-messages.instructions.md new file mode 100644 index 0000000..fd5290c --- /dev/null +++ b/.github/instructions/commit-messages.instructions.md @@ -0,0 +1,11 @@ +--- +name: Commit message rules +description: Copilot エージェントが生成するコミットメッセージの構造ルール +applyTo: + - "commit messages" +--- +- コミットメッセージは日本語で書き、必要に応じて括弧書きで簡潔な英訳を添える。 +- 先頭に `fix:` / `hotfix:` / `feat:` のいずれかのプレフィックスを付ける。 +- メッセージは最低3行とし、1行目は概要、2行目は空行、3行目以降で「何を」「なぜ」を文章で説明し、必要に応じて影響範囲・背景・再現手順も補足する。 +- コードブロックや装飾記号は使わず、変更点を文章で記載する。 +- 文字化け防止のため UTF-8 を前提とし、日本語の可読性を優先する。 diff --git a/.github/instructions/docs.instructions.md b/.github/instructions/docs.instructions.md new file mode 100644 index 0000000..80a24fe --- /dev/null +++ b/.github/instructions/docs.instructions.md @@ -0,0 +1,13 @@ +--- +name: Documentation rules +description: Markdown ドキュメントに適用する実務ルール +applyTo: + - "docs/**/*.md" + - "README.md" + - ".github/copilot/**/*.md" +--- +- 見出しは階層を崩さずに使用し、一覧・手順は箇条書きで簡潔にまとめる。 +- 相対リンクを優先し、`00-index.md` から辿れるようにする。重複した記述は SSOT に統合する。 +- Mermaid を使う場合は ```mermaid フェンスを用い、フローの入口・分岐・終了を明示する。 +- Secrets/PII を記載しない。サンプル値はダミーを用いる。 +- テンプレートは `80-templates` を参照し、改変時は互換性を考慮して最小限の差分にとどめる。 diff --git a/.github/instructions/python.instructions.md b/.github/instructions/python.instructions.md new file mode 100644 index 0000000..3caa463 --- /dev/null +++ b/.github/instructions/python.instructions.md @@ -0,0 +1,11 @@ +--- +name: Python implementation rules +description: apps 配下の Python コードに適用する実務ルール +applyTo: + - "apps/**/*.py" +--- +- 型ヒントは必須。例外は文脈を残して再送出し、`print` ではなく `logging` で構造化ログを出す。 +- Secrets/トークン/PII をログ・コメント・テストデータに含めない。環境変数や Vault で注入し、マスクを徹底する。 +- 副作用を分離し、小さな関数でテストしやすい構造にする。入力検証を行い、失敗は明示的に返す。 +- 依存追加は最小限にし、バージョンをピン止めして `requirements.txt` に反映する。 +- 変更時は必ずテストを追加/更新し、最低限、以下のコマンドを実行する: `python -m pip install -r requirements.txt`, `black .`, `ruff check .`, `mypy .`, `python -m pytest`. diff --git a/.github/instructions/tests.instructions.md b/.github/instructions/tests.instructions.md new file mode 100644 index 0000000..8a5c325 --- /dev/null +++ b/.github/instructions/tests.instructions.md @@ -0,0 +1,12 @@ +--- +name: Tests rules +description: テストコードに適用する実務ルール +applyTo: + - "**/tests/**/*.py" + - "**/*test*.py" +--- +- pytest を前提とし、ファイル名は `test_*.py` / 関数名は `test_*` で一貫させる。 +- 重要なバグ修正には必ず回帰テストを追加する。正常系・例外系・境界値を分けて書く。 +- 外部 API / 時刻 / I/O / 環境依存はフィクスチャやモックで隔離し、テストは決定性を保つ。 +- テストデータは最小限かつ共有フィクスチャで再利用する。Secrets/PII を埋め込まない。 +- 実行コマンドの例: `python -m pytest -q`。スローテストはマーカーを付与して分離可能にする。 diff --git a/.github/instructions/workflows.instructions.md b/.github/instructions/workflows.instructions.md new file mode 100644 index 0000000..6ca661d --- /dev/null +++ b/.github/instructions/workflows.instructions.md @@ -0,0 +1,12 @@ +--- +name: Workflows rules +description: GitHub Actions ワークフローの実務ルール +applyTo: + - ".github/workflows/**/*.yml" + - ".github/workflows/**/*.yaml" +--- +- 最小権限の `permissions` を明示し、Secrets は必要最小限で参照する。ログに Secrets/PII を出さない。 +- アクションはバージョン固定(タグまたは commit SHA)で利用し、Deprecated アクションは避ける。 +- lint / typecheck / test / security など必須ジョブを required status checks に設定し、失敗時マージ不可にする。 +- キャッシュはキーを明示し、再現性を損なう場合は無効化する。冪等性を保つため外部リソースへの依存を最小化する。 +- 並列・マトリクス実行時は `concurrency` で二重実行を防ぎ、フェイルファストで早期に失敗させる。