create-einja-app 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/dist/cli.js +92 -80
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/.changeset/config.json +11 -0
- package/templates/default/.claude/hooks/einja/plan-mode-skill-loader.sh +27 -0
- package/templates/default/.claude/settings.json +29 -1
- package/templates/default/.claude/skills/cli-package-specs/SKILL.md +247 -0
- package/templates/default/.einja-sync.json +1 -1
- package/templates/default/.env.personal.example +6 -2
- package/templates/default/.envrc +5 -0
- package/templates/default/.github/release.yml +10 -0
- package/templates/default/.github/workflows/changeset-status.yml +60 -0
- package/templates/default/.github/workflows/deploy-pr-preview.yml +23 -24
- package/templates/default/.github/workflows/deploy-stable-branches.yml +336 -100
- package/templates/default/.mcp.json +2 -12
- package/templates/default/.serena/project.yml +7 -0
- package/templates/default/CLAUDE.md +61 -10
- package/templates/default/README.md +22 -10
- package/templates/default/apps/admin/package.json +1 -1
- package/templates/default/apps/admin/tsconfig.json +2 -1
- package/templates/default/apps/web/package.json +1 -1
- package/templates/default/apps/web/tsconfig.json +2 -1
- package/templates/default/docs/plans/.gitkeep +0 -0
- package/templates/default/docs/plans/agile-munching-knuth.md +161 -0
- package/templates/default/docs/plans/agile-riding-nova.md +158 -0
- package/templates/default/docs/plans/agile-wibbling-dusk.md +91 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo-agent-a87e67c.md +221 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo-agent-ab73a1c.md +107 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo.md +120 -0
- package/templates/default/docs/plans/ancient-watching-otter.md +152 -0
- package/templates/default/docs/plans/bright-sauteeing-bumblebee.md +30 -0
- package/templates/default/docs/plans/bright-stargazing-dawn.md +87 -0
- package/templates/default/docs/plans/calm-stirring-bonbon.md +196 -0
- package/templates/default/docs/plans/calm-watching-widget.md +111 -0
- package/templates/default/docs/plans/cheerful-wiggling-ullman.md +164 -0
- package/templates/default/docs/plans/compiled-humming-cherny.md +94 -0
- package/templates/default/docs/plans/composed-doodling-mountain.md +362 -0
- package/templates/default/docs/plans/dapper-launching-lynx.md +81 -0
- package/templates/default/docs/plans/dazzling-foraging-cascade.md +32 -0
- package/templates/default/docs/plans/effervescent-munching-kite-agent-ac08baf.md +672 -0
- package/templates/default/docs/plans/effervescent-munching-kite-agent-aecc373.md +442 -0
- package/templates/default/docs/plans/effervescent-munching-kite.md +263 -0
- package/templates/default/docs/plans/enchanted-wiggling-ember-agent-a5befd57d0ca4c7c7.md +177 -0
- package/templates/default/docs/plans/enchanted-wiggling-ember.md +170 -0
- package/templates/default/docs/plans/federated-questing-kahan.md +47 -0
- package/templates/default/docs/plans/fix-orphan-cleaner-review.md +25 -0
- package/templates/default/docs/plans/fix-sync-template-variables.md +162 -0
- package/templates/default/docs/plans/flickering-pondering-hearth.md +26 -0
- package/templates/default/docs/plans/fluttering-snuggling-sprout.md +172 -0
- package/templates/default/docs/plans/generic-sleeping-snowglobe-agent-a41d8da.md +179 -0
- package/templates/default/docs/plans/generic-sleeping-snowglobe.md +108 -0
- package/templates/default/docs/plans/generic-snuggling-pudding.md +57 -0
- package/templates/default/docs/plans/glimmering-giggling-sedgewick.md +126 -0
- package/templates/default/docs/plans/glittery-swimming-bachman.md +78 -0
- package/templates/default/docs/plans/happy-watching-toast.md +56 -0
- package/templates/default/docs/plans/harmonic-strolling-nebula.md +210 -0
- package/templates/default/docs/plans/idempotent-wiggling-cherny.md +122 -0
- package/templates/default/docs/plans/import-alias-refactor.md +75 -0
- package/templates/default/docs/plans/lazy-percolating-sloth-agent-abda679.md +346 -0
- package/templates/default/docs/plans/lazy-percolating-sloth.md +151 -0
- package/templates/default/docs/plans/linked-greeting-llama-agent-a7a6e5b.md +345 -0
- package/templates/default/docs/plans/linked-greeting-llama.md +467 -0
- package/templates/default/docs/plans/lovely-bubbling-rose.md +80 -0
- package/templates/default/docs/plans/optimized-watching-sprout.md +149 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-a292da6.md +288 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-a819699.md +366 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-ac11de2.md +474 -0
- package/templates/default/docs/plans/peaceful-beaming-toast.md +345 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6194c.md +300 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6900e.md +444 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey.md +361 -0
- package/templates/default/docs/plans/recursive-fluttering-mitten.md +176 -0
- package/templates/default/docs/plans/recursive-kindling-lemon-agent-a42199e.md +186 -0
- package/templates/default/docs/plans/recursive-kindling-lemon.md +36 -0
- package/templates/default/docs/plans/seed-migration-tests.md +47 -0
- package/templates/default/docs/plans/sprightly-leaping-manatee.md +224 -0
- package/templates/default/docs/plans/stateful-wishing-lerdorf.md +161 -0
- package/templates/default/docs/plans/streamed-purring-wreath.md +40 -0
- package/templates/default/docs/plans/synthetic-percolating-pearl.md +101 -0
- package/templates/default/docs/plans/todo-create-einja-app-ux-fix.md +16 -0
- package/templates/default/docs/plans/todo-direnv-hang-fix.md +12 -0
- package/templates/default/docs/plans/todo-fix-sync-template-variables.md +21 -0
- package/templates/default/docs/plans/todo-github-actions-release-workflow.md +34 -0
- package/templates/default/docs/plans/todo-issue-spec-rename.md +24 -0
- package/templates/default/docs/plans/todo-phase4-marker-update.md +39 -0
- package/templates/default/docs/plans/todo-skill-creator-sync.md +23 -0
- package/templates/default/docs/plans/todo-skill-creator-upgrade.md +18 -0
- package/templates/default/docs/plans/typed-snuggling-parnas-agent-a6f6391.md +476 -0
- package/templates/default/docs/plans/typed-snuggling-parnas-agent-adb678b.md +144 -0
- package/templates/default/docs/plans/typed-snuggling-parnas.md +84 -0
- package/templates/default/docs/plans/velvety-chasing-spark.md +28 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a30aa4f.md +534 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a57a278.md +508 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a90b809.md +421 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse.md +199 -0
- package/templates/default/docs/plans/wondrous-strolling-crystal-agent-a0615fc.md +215 -0
- package/templates/default/docs/plans/wondrous-strolling-crystal.md +182 -0
- package/templates/default/docs/plans/zesty-roaming-steele.md +74 -0
- package/templates/default/docs/verification-test.md +2 -0
- package/templates/default/gitignore +9 -1
- package/templates/default/package.json +6 -2
- package/templates/default/packages/admin-ui/package.json +1 -1
- package/templates/default/packages/server-core/tsconfig.json +6 -1
- package/templates/default/pnpm-lock.yaml +823 -57
- package/templates/default/scripts/ensure-serena.sh +75 -0
- package/templates/default/scripts/env-rotate-secrets.ts +66 -6
- package/templates/default/scripts/init-github.ts +363 -0
- package/templates/default/scripts/init.sh +11 -5
- package/templates/default/scripts/lib/worktree-config.ts +64 -0
- package/templates/default/scripts/setup-dev.ts +16 -1
- package/templates/default/scripts/stop-serena.sh +25 -0
- package/templates/default/scripts/worktree/dev.ts +2 -2
- package/templates/default/.claude/skills/create-einja-app-release/SKILL.md +0 -186
- package/templates/default/.claude/skills/dev-cli-release/SKILL.md +0 -173
- package/templates/default/.cursor/commands/spec-create.md +0 -227
- package/templates/default/.cursor/commands/task-exec.md +0 -287
- package/templates/default/.cursor/commands/update-docs-by-task-specs.md +0 -448
- /package/templates/default/scripts/{cli-template-update.ts → _cli-template-update.ts} +0 -0
- /package/templates/default/scripts/{template-update.ts → _template-update.ts} +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# einja sync: 孤児ファイル検出・削除機能
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
`einja sync` はテンプレートからプロジェクトへファイルを同期するが、テンプレート側でファイルが削除されても利用者側のファイルはそのまま残る。`.einja-sync.json` に過去の同期記録があるため、「メタデータにあるがテンプレートにないファイル」=孤児として検出可能。
|
|
6
|
+
|
|
7
|
+
## 方針
|
|
8
|
+
|
|
9
|
+
- `sync` 実行時に常に孤児を検出・警告表示
|
|
10
|
+
- `--clean` フラグで実際に削除(確認プロンプト + バックアップ付き)
|
|
11
|
+
- 既存のUXパターン(dry-run, JSON出力, --yes, --no-backup)に完全準拠
|
|
12
|
+
|
|
13
|
+
## 変更対象ファイル
|
|
14
|
+
|
|
15
|
+
| ファイル | 変更内容 |
|
|
16
|
+
|---------|---------|
|
|
17
|
+
| `packages/cli/src/types/index.ts` | `SyncOptions` に `clean?: boolean` 追加 |
|
|
18
|
+
| `packages/cli/src/types/sync.ts` | `OrphanFile`, `OrphanReport` 型追加、`JsonOutput.summary` に `orphansDetected?`, `orphansDeleted?` 追加、`JsonOutput.orphans?` 追加 |
|
|
19
|
+
| `packages/cli/src/lib/sync/orphan-cleaner.ts` | **新規**: 孤児検出・レポート生成クラス |
|
|
20
|
+
| `packages/cli/src/lib/sync/metadata-manager.ts` | `removeFiles()` メソッド追加 |
|
|
21
|
+
| `packages/cli/src/commands/sync.ts` | 孤児検出→警告表示→`--clean`時の削除処理を統合 |
|
|
22
|
+
| `packages/cli/src/cli.ts` | `--clean` オプション追加 |
|
|
23
|
+
| `packages/cli/src/lib/sync/orphan-cleaner.test.ts` | **新規**: OrphanCleanerのユニットテスト |
|
|
24
|
+
| `packages/cli/src/lib/sync/metadata-manager.test.ts` | `removeFiles` テスト追加 |
|
|
25
|
+
|
|
26
|
+
## 実装ステップ
|
|
27
|
+
|
|
28
|
+
### Step 1: 型定義追加
|
|
29
|
+
|
|
30
|
+
**`types/index.ts`** — `SyncOptions` に追加:
|
|
31
|
+
```typescript
|
|
32
|
+
clean?: boolean;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**`types/sync.ts`** — 型追加:
|
|
36
|
+
```typescript
|
|
37
|
+
export interface OrphanFile {
|
|
38
|
+
path: string;
|
|
39
|
+
category: string | null;
|
|
40
|
+
exists: boolean; // ディスク上に実在するか
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface OrphanReport {
|
|
44
|
+
hasOrphans: boolean;
|
|
45
|
+
orphans: OrphanFile[];
|
|
46
|
+
total: number;
|
|
47
|
+
existingCount: number; // ディスク上に実在する数
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`JsonOutput.summary` に `orphansDetected?`, `orphansDeleted?` を追加。`JsonOutput` に `orphans?: OrphanFile[]` を追加。
|
|
52
|
+
|
|
53
|
+
### Step 2: OrphanCleaner クラス(新規)
|
|
54
|
+
|
|
55
|
+
**`lib/sync/orphan-cleaner.ts`** — 既存の `ConflictReporter` と同じ設計パターン:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
export class OrphanCleaner {
|
|
59
|
+
constructor(private projectRoot: string, private fileFilter: FileFilter) {}
|
|
60
|
+
|
|
61
|
+
async detectOrphans(
|
|
62
|
+
metadata: SyncMetadata,
|
|
63
|
+
currentTemplateFiles: string[],
|
|
64
|
+
categories?: string[]
|
|
65
|
+
): Promise<OrphanFile[]>
|
|
66
|
+
// metadata.files のキー vs currentTemplateFiles を比較
|
|
67
|
+
// カテゴリフィルタ: fileFilter.getCategoryFromPath() で判定
|
|
68
|
+
// fs.pathExists でディスク実在チェック
|
|
69
|
+
|
|
70
|
+
createReport(orphans: OrphanFile[]): OrphanReport
|
|
71
|
+
|
|
72
|
+
formatReport(report: OrphanReport): string
|
|
73
|
+
// 例: " 🗑️ .claude/skills/einja-old/SKILL.md (存在)"
|
|
74
|
+
|
|
75
|
+
formatHelpMessage(): string
|
|
76
|
+
// "💡 削除するには --clean オプションを使用してください"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 3: MetadataManager 拡張
|
|
81
|
+
|
|
82
|
+
**`lib/sync/metadata-manager.ts`** に追加:
|
|
83
|
+
```typescript
|
|
84
|
+
removeFiles(metadata: SyncMetadata, filePaths: string[]): SyncMetadata
|
|
85
|
+
// metadata.files から指定パスのエントリを削除して返す
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Step 4: CLI オプション追加
|
|
89
|
+
|
|
90
|
+
**`cli.ts`** の sync コマンドに追加:
|
|
91
|
+
```typescript
|
|
92
|
+
.option("--clean", "テンプレートから削除されたファイル(孤児)を削除")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Step 5: sync.ts への統合
|
|
96
|
+
|
|
97
|
+
既存フローへの挿入位置:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
1. メタデータ読み込み
|
|
101
|
+
2. 同期対象スキャン (targets)
|
|
102
|
+
3. 差分計算
|
|
103
|
+
4. ★ 孤児検出(常に実行)
|
|
104
|
+
5. dry-run モード
|
|
105
|
+
- 既存の差分表示
|
|
106
|
+
- ★ 孤児レポート表示
|
|
107
|
+
6. 確認プロンプト
|
|
108
|
+
7. バックアップ作成
|
|
109
|
+
8. ファイルマージ処理
|
|
110
|
+
9. ★ 孤児削除処理(--clean 時のみ)
|
|
111
|
+
- 確認プロンプト(デフォルトNo、--yes でスキップ)
|
|
112
|
+
- バックアップ作成
|
|
113
|
+
- ファイル削除
|
|
114
|
+
- メタデータから削除
|
|
115
|
+
10. メタデータ保存
|
|
116
|
+
11. 結果出力
|
|
117
|
+
- ★ 孤児レポート(--clean 無しでも警告表示)
|
|
118
|
+
- ★ JSON出力に orphans フィールド追加
|
|
119
|
+
12. 依存関係チェック
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Step 6: テスト
|
|
123
|
+
|
|
124
|
+
**orphan-cleaner.test.ts(新規):**
|
|
125
|
+
- メタデータにあるがテンプレートにないファイルを検出
|
|
126
|
+
- カテゴリフィルタが正しく適用される
|
|
127
|
+
- ディスク実在チェックが正しい
|
|
128
|
+
- レポートフォーマットが正しい
|
|
129
|
+
|
|
130
|
+
**metadata-manager.test.ts(追加):**
|
|
131
|
+
- `removeFiles` で複数ファイルが正しく削除される
|
|
132
|
+
|
|
133
|
+
## UX例
|
|
134
|
+
|
|
135
|
+
### 通常 sync(孤児あり、--clean なし)
|
|
136
|
+
```
|
|
137
|
+
✅ 同期完了!
|
|
138
|
+
- 成功: 3ファイル
|
|
139
|
+
- スキップ: 12ファイル
|
|
140
|
+
|
|
141
|
+
⚠️ 孤児ファイルが検出されました:
|
|
142
|
+
🗑️ .claude/skills/einja-old/SKILL.md
|
|
143
|
+
🗑️ docs/einja/deprecated.md
|
|
144
|
+
|
|
145
|
+
💡 削除するには --clean オプションを使用してください
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `--clean` 実行時
|
|
149
|
+
```
|
|
150
|
+
⚠️ 以下の孤児ファイルを削除します:
|
|
151
|
+
🗑️ .claude/skills/einja-old/SKILL.md
|
|
152
|
+
🗑️ docs/einja/deprecated.md
|
|
153
|
+
|
|
154
|
+
? 2ファイルを削除します。続行しますか? (y/N) y
|
|
155
|
+
|
|
156
|
+
- 孤児削除: 2ファイル
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 検証方法
|
|
160
|
+
|
|
161
|
+
1. `pnpm -F @einja/dev-cli test` — 全テスト通過
|
|
162
|
+
2. `pnpm -F @einja/dev-cli typecheck` — 型チェック通過
|
|
163
|
+
3. `pnpm -F @einja/dev-cli lint` — lint通過
|
|
164
|
+
4. 手動テスト: `.einja-sync.json` に存在しないファイルエントリを追加 → `einja sync --dry-run` で孤児検出 → `einja sync --clean --yes` で削除確認
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Plan: project-privateセクションの簡素化
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
project-privateセクションの冗長な形式を簡素化する。現在の形式:
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<!-- @einja:project-private:start id="xxx" -->
|
|
11
|
+
## プロジェクト固有の設定
|
|
12
|
+
|
|
13
|
+
<!-- このセクションはプロジェクト固有の内容を追記する場所です -->
|
|
14
|
+
<!-- einja syncで上書きされません -->
|
|
15
|
+
<!-- @einja:project-private:end -->
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
新しい形式:
|
|
19
|
+
|
|
20
|
+
```markdown
|
|
21
|
+
<!-- @einja:project-private:start id="xxx" -->
|
|
22
|
+
<!-- プロジェクト固有の情報を記入 -->
|
|
23
|
+
<!-- @einja:project-private:end -->
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
変更点:
|
|
27
|
+
1. 直前の `---` 罫線を削除
|
|
28
|
+
2. `## プロジェクト固有の設定` 見出しを削除
|
|
29
|
+
3. 空行 + 2行コメントを1行コメントに統合
|
|
30
|
+
|
|
31
|
+
## 互換性
|
|
32
|
+
|
|
33
|
+
CLIの `marker-processor.ts` はstart/endタグのみでパースするため、中身のコメント変更は完全に互換。
|
|
34
|
+
|
|
35
|
+
## 対象ファイル
|
|
36
|
+
|
|
37
|
+
### 一括置換対象(63ファイル)
|
|
38
|
+
|
|
39
|
+
| カテゴリ | ファイル数 |
|
|
40
|
+
|---------|-----------|
|
|
41
|
+
| `.claude/agents/einja/` | 16 |
|
|
42
|
+
| `.claude/commands/einja/` | 7 |
|
|
43
|
+
| `.claude/skills/einja-*/SKILL.md` | 13 |
|
|
44
|
+
| `docs/einja/steering/` | 20 |
|
|
45
|
+
| `docs/einja/instructions/` | 7 |
|
|
46
|
+
|
|
47
|
+
### 対象外
|
|
48
|
+
|
|
49
|
+
| カテゴリ | 理由 |
|
|
50
|
+
|---------|------|
|
|
51
|
+
| `CLAUDE.md` | 修正済み |
|
|
52
|
+
| `docs/einja/memory/` | ファイル全体がproject-private、見出しは意味あるコンテンツ |
|
|
53
|
+
| `packages/cli/` | ソースコード・テスト(文字列リテラル・テストデータ) |
|
|
54
|
+
| `packages/create-einja-app/` | 同上 |
|
|
55
|
+
| `docs/plans/` | 過去のplan(参照用) |
|
|
56
|
+
| `.claude/skills/einja-skill-creator/scripts/init_skill.py` | 説明用コードブロック内(短い形式で既にOK) |
|
|
57
|
+
| `.claude/skills/einja-skill-creator/SKILL.md` | 説明用コードブロック + 自身のproject-private |
|
|
58
|
+
| `.claude/skills/einja-coding-standards/references/testing-strategy.md` | 確認が必要 |
|
|
59
|
+
|
|
60
|
+
## 実装手順
|
|
61
|
+
|
|
62
|
+
### Step 1: sedで一括置換
|
|
63
|
+
|
|
64
|
+
2つのパターンに対応:
|
|
65
|
+
|
|
66
|
+
**パターンA**: `---` + 空行 + `---` + 空行 + project-private(1ファイル: `frontend-implement.md`)
|
|
67
|
+
**パターンB**: `---` + 空行 + project-private(約62ファイル)
|
|
68
|
+
|
|
69
|
+
両パターンをカバーするPerl正規表現で一括処理:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# .claude/ と docs/einja/ 配下の .md ファイルを対象に、
|
|
73
|
+
# "---\n\n" (1つまたは2つ) + 冗長なproject-private を簡素な形式に置換
|
|
74
|
+
find .claude/agents .claude/commands .claude/skills docs/einja/steering docs/einja/instructions \
|
|
75
|
+
-name '*.md' -exec perl -0777 -pi -e '
|
|
76
|
+
s/\n---\n(\n---\n)?\n<!-- @einja:project-private:start id="([^"]+)" -->\n## プロジェクト固有の設定\n\n<!-- このセクションはプロジェクト固有の内容を追記する場所です -->\n<!-- einja syncで上書きされません -->\n<!-- @einja:project-private:end -->/\n<!-- \@einja:project-private:start id="$2" -->\n<!-- プロジェクト固有の情報を記入 -->\n<!-- \@einja:project-private:end -->/g' {} +
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step 2: SKILL.md内のドキュメント例も更新
|
|
80
|
+
|
|
81
|
+
`einja-skill-creator/SKILL.md` 内の説明用コードブロック部分は手動で確認・更新。
|
|
82
|
+
|
|
83
|
+
### Step 3: 検証
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 旧形式が残っていないことを確認
|
|
87
|
+
grep -r "## プロジェクト固有の設定" .claude/ docs/einja/steering/ docs/einja/instructions/ --include="*.md"
|
|
88
|
+
|
|
89
|
+
# 新形式の件数を確認(63ファイル程度)
|
|
90
|
+
grep -r "<!-- プロジェクト固有の情報を記入 -->" .claude/ docs/einja/ --include="*.md" | wc -l
|
|
91
|
+
|
|
92
|
+
# テスト実行(marker-processor関連)
|
|
93
|
+
cd packages/cli && pnpm test -- marker-processor
|
|
94
|
+
```
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# GitHub Actions リリースワークフロー + 承認フロー設計
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
現在、main/staging/developへのpushで自動Vercelデプロイが走るが、GitHub Release作成・承認フローがない。リリースの可視性と本番デプロイの安全性を向上させるため、以下を導入する:
|
|
6
|
+
|
|
7
|
+
- staging マージ → GitHub **PreRelease** 自動作成
|
|
8
|
+
- main マージ → GitHub **Release** 自動作成
|
|
9
|
+
- production デプロイに **承認ゲート**(Environment Protection)
|
|
10
|
+
|
|
11
|
+
## 設計方針
|
|
12
|
+
|
|
13
|
+
| 項目 | 決定 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| リリース順序 | PRマージ → 自動リリース作成 |
|
|
16
|
+
| 昇格フロー | staging → main 直接マージPR |
|
|
17
|
+
| バージョニング | changesets(Turborepo公式推奨) |
|
|
18
|
+
| 承認フロー | GitHub Environments + Required Reviewer 1名(productionのみ) |
|
|
19
|
+
|
|
20
|
+
## 全体フロー
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
feature/* → staging PR(changeset含む)
|
|
24
|
+
↓ マージ
|
|
25
|
+
[staging] → CI + Migrate + Deploy(承認不要)
|
|
26
|
+
→ PreRelease自動作成(v0.2.0-rc.42)
|
|
27
|
+
※ changeset未消費(タグ作成のみ)
|
|
28
|
+
|
|
29
|
+
staging → main PR(昇格)
|
|
30
|
+
↓ マージ
|
|
31
|
+
[main] → CI + ⚠️承認待ち → Migrate + Deploy
|
|
32
|
+
→ changeset version(バージョン確定)→ Release自動作成(v0.2.0)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Codexレビュー反映事項
|
|
36
|
+
|
|
37
|
+
| # | 問題 | 修正内容 |
|
|
38
|
+
|---|------|---------|
|
|
39
|
+
| C-1 | stagingでchangeset消費すると main で残らない | `changeset version` はmainのみ。stagingはRCタグ作成のみ(未消費) |
|
|
40
|
+
| C-2 | `workflow_run` はdefault branch制約がある | release処理を `deploy-stable-branches.yml` 内ジョブに統合(独立ワークフロー廃止) |
|
|
41
|
+
| C-3 | `--previous-tag` は存在しないCLIオプション | `--notes-start-tag` に修正 |
|
|
42
|
+
| C-4 | 承認前にDBマイグレーションが走る | migrate + deploy 両方にproduction environment適用 |
|
|
43
|
+
| H-1 | `ci`ジョブのみの無限ループ防止は不十分 | `GITHUB_TOKEN` はデフォルトでワークフロー再起動しない特性を活用。ワークフロー全体ガード追加 |
|
|
44
|
+
| H-2 | `permissions: contents: write` 未定義 | 明示追加 |
|
|
45
|
+
| H-3 | `environment: ''` の動作が不確実 | ジョブ分割で解消(develop/staging/production別ジョブ) |
|
|
46
|
+
| H-4 | ブランチ保護との衝突リスク | version bumpはmainのみ。GITHUB_TOKEN使用(bot名義でprotection bypass可能な設定前提) |
|
|
47
|
+
|
|
48
|
+
## 実装ステップ
|
|
49
|
+
|
|
50
|
+
### Phase 1: changesets基盤導入
|
|
51
|
+
|
|
52
|
+
**変更ファイル:**
|
|
53
|
+
- `package.json` — `@changesets/cli`, `@changesets/changelog-github` をdevDependenciesに追加。scriptsに `"changeset": "changeset"` 追加
|
|
54
|
+
- `.changeset/config.json` — 新規作成
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
|
59
|
+
"changelog": ["@changesets/changelog-github", { "repo": "einja-inc/einja-management-template" }],
|
|
60
|
+
"commit": false,
|
|
61
|
+
"fixed": [],
|
|
62
|
+
"linked": [],
|
|
63
|
+
"access": "restricted",
|
|
64
|
+
"baseBranch": "main",
|
|
65
|
+
"updateInternalDependencies": "patch",
|
|
66
|
+
"ignore": ["@einja/dev-cli", "create-einja-app"]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**リスク:** なし。既存フローに影響しない
|
|
71
|
+
|
|
72
|
+
### Phase 2: GitHub Environments設定(手動・GitHub UI)
|
|
73
|
+
|
|
74
|
+
| Environment | Required Reviewers | Wait Timer | Deployment Branches |
|
|
75
|
+
|------------|-------------------|------------|-------------------|
|
|
76
|
+
| `staging` | なし | なし | `staging`のみ |
|
|
77
|
+
| `production` | 1名 | なし | `main`のみ |
|
|
78
|
+
|
|
79
|
+
**リスク:** なし。ワークフローに`environment`参照がないため、設定だけでは動作変更なし
|
|
80
|
+
|
|
81
|
+
### Phase 3: `deploy-stable-branches.yml` 大規模改修
|
|
82
|
+
|
|
83
|
+
**変更概要:** デプロイジョブをブランチ別に分割 + リリース作成ジョブを統合
|
|
84
|
+
|
|
85
|
+
**改修前の構造:**
|
|
86
|
+
```
|
|
87
|
+
ci → changes → migrate → deploy(全ブランチ共通)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**改修後の構造:**
|
|
91
|
+
```
|
|
92
|
+
ci → changes → (ブランチ別分岐)
|
|
93
|
+
├─ [develop] → deploy-develop(環境なし)
|
|
94
|
+
├─ [staging] → deploy-staging(staging環境)→ release-staging(PreRelease作成)
|
|
95
|
+
└─ [main] → migrate-production(production環境・承認待ち)→ deploy-production → release-production(Release作成)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**主要な変更点:**
|
|
99
|
+
|
|
100
|
+
#### 3-1. ワークフロー全体ガード(無限ループ防止)
|
|
101
|
+
```yaml
|
|
102
|
+
on:
|
|
103
|
+
push:
|
|
104
|
+
branches: [main, develop, staging]
|
|
105
|
+
|
|
106
|
+
# GITHUB_TOKENで作成されたイベントはデフォルトでワークフロー再起動しない
|
|
107
|
+
# 念のため明示的にbotコミットを除外
|
|
108
|
+
jobs:
|
|
109
|
+
ci:
|
|
110
|
+
# github-actions[bot] のバージョンバンプコミットはスキップ
|
|
111
|
+
if: "!contains(github.event.head_commit.message, 'chore: release v')"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### 3-2. デプロイジョブをブランチ別に分割
|
|
115
|
+
```yaml
|
|
116
|
+
# develop: 環境なし、承認なし
|
|
117
|
+
deploy-develop:
|
|
118
|
+
needs: [ci, changes]
|
|
119
|
+
if: github.ref_name == 'develop' && needs.changes.outputs.deploy_matrix != '[]'
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
strategy: ...
|
|
122
|
+
steps: ... # 既存のdeployステップをそのまま
|
|
123
|
+
|
|
124
|
+
# staging: staging環境、承認なし
|
|
125
|
+
deploy-staging:
|
|
126
|
+
needs: [ci, migrate, changes]
|
|
127
|
+
if: github.ref_name == 'staging' && ...
|
|
128
|
+
environment: staging
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
strategy: ...
|
|
131
|
+
steps: ... # 既存のdeployステップをそのまま
|
|
132
|
+
|
|
133
|
+
# main: production環境、1名承認 → migrate → deploy
|
|
134
|
+
migrate-production:
|
|
135
|
+
needs: ci
|
|
136
|
+
if: github.ref_name == 'main'
|
|
137
|
+
environment: production # ← ここで承認待ち(migrate前にブロック)
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
steps: ... # 既存のmigrateステップ
|
|
140
|
+
|
|
141
|
+
deploy-production:
|
|
142
|
+
needs: [migrate-production, changes]
|
|
143
|
+
if: github.ref_name == 'main' && needs.changes.outputs.deploy_matrix != '[]'
|
|
144
|
+
runs-on: ubuntu-latest
|
|
145
|
+
strategy: ...
|
|
146
|
+
steps: ... # 既存のdeployステップ
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### 3-3. リリース作成ジョブを統合(release-app.yml廃止)
|
|
150
|
+
```yaml
|
|
151
|
+
permissions:
|
|
152
|
+
contents: write # タグ作成・Release作成に必要
|
|
153
|
+
|
|
154
|
+
# staging: PreRelease作成(changeset未消費)
|
|
155
|
+
release-staging:
|
|
156
|
+
needs: deploy-staging
|
|
157
|
+
if: github.ref_name == 'staging'
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v4
|
|
161
|
+
with:
|
|
162
|
+
fetch-depth: 0
|
|
163
|
+
- name: Check for changesets
|
|
164
|
+
id: check
|
|
165
|
+
run: |
|
|
166
|
+
COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
|
|
167
|
+
echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
|
168
|
+
- name: Create PreRelease tag
|
|
169
|
+
if: steps.check.outputs.has_changesets == 'true'
|
|
170
|
+
id: tag
|
|
171
|
+
run: |
|
|
172
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
173
|
+
TAG="v${VERSION}-rc.${{ github.run_number }}"
|
|
174
|
+
git tag "$TAG"
|
|
175
|
+
git push origin "$TAG"
|
|
176
|
+
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
177
|
+
- name: Create GitHub PreRelease
|
|
178
|
+
if: steps.tag.outputs.tag
|
|
179
|
+
run: |
|
|
180
|
+
PREV=$(git tag --list 'v*-rc.*' --sort=-v:refname | grep -v "${{ steps.tag.outputs.tag }}" | head -1)
|
|
181
|
+
OPTS="--prerelease --target staging --generate-notes"
|
|
182
|
+
[[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
|
|
183
|
+
gh release create "${{ steps.tag.outputs.tag }}" --title "Pre-release ${{ steps.tag.outputs.tag }}" $OPTS
|
|
184
|
+
env:
|
|
185
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
186
|
+
|
|
187
|
+
# main: changeset消費 + Release作成
|
|
188
|
+
release-production:
|
|
189
|
+
needs: deploy-production
|
|
190
|
+
if: github.ref_name == 'main'
|
|
191
|
+
runs-on: ubuntu-latest
|
|
192
|
+
steps:
|
|
193
|
+
- uses: actions/checkout@v4
|
|
194
|
+
with:
|
|
195
|
+
fetch-depth: 0
|
|
196
|
+
- name: Setup
|
|
197
|
+
uses: ./.github/actions/setup
|
|
198
|
+
- name: Check for changesets
|
|
199
|
+
id: check
|
|
200
|
+
run: |
|
|
201
|
+
COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
|
|
202
|
+
echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
|
|
203
|
+
- name: Version packages
|
|
204
|
+
if: steps.check.outputs.has_changesets == 'true'
|
|
205
|
+
run: npx changeset version
|
|
206
|
+
- name: Commit version bump and create Release
|
|
207
|
+
if: steps.check.outputs.has_changesets == 'true'
|
|
208
|
+
id: release
|
|
209
|
+
run: |
|
|
210
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
211
|
+
TAG="v${VERSION}"
|
|
212
|
+
git config user.name "github-actions[bot]"
|
|
213
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
214
|
+
git add -A
|
|
215
|
+
git commit -m "chore: release ${TAG}" || echo "No changes"
|
|
216
|
+
git tag "$TAG"
|
|
217
|
+
git push origin main --follow-tags
|
|
218
|
+
PREV=$(git tag --list 'v*' --sort=-v:refname | grep -v 'rc' | grep -v "$TAG" | head -1)
|
|
219
|
+
OPTS="--target main --generate-notes"
|
|
220
|
+
[[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
|
|
221
|
+
gh release create "$TAG" --title "Release ${TAG}" $OPTS
|
|
222
|
+
env:
|
|
223
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**無限ループ防止の多重防御:**
|
|
227
|
+
1. `GITHUB_TOKEN` で作成されたpushイベントはデフォルトでワークフローを再トリガーしない
|
|
228
|
+
2. コミットメッセージ `chore: release v` でのフィルタリング
|
|
229
|
+
3. バージョンバンプコミットは `github-actions[bot]` 名義
|
|
230
|
+
|
|
231
|
+
**リスク:** 高。ジョブ分割により既存ワークフローの構造が大きく変わる。段階的にテストが必要
|
|
232
|
+
|
|
233
|
+
### Phase 4: `.github/release.yml` リリースノート設定
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
changelog:
|
|
237
|
+
exclude:
|
|
238
|
+
labels: ["dependencies", "skip-changelog"]
|
|
239
|
+
categories:
|
|
240
|
+
- title: "New Features"
|
|
241
|
+
labels: ["enhancement", "feature"]
|
|
242
|
+
- title: "Bug Fixes"
|
|
243
|
+
labels: ["bug", "fix"]
|
|
244
|
+
- title: "Other Changes"
|
|
245
|
+
labels: ["*"]
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Phase 5: `changeset-status.yml` 新規作成(任意)
|
|
249
|
+
|
|
250
|
+
PR上にchangesetの有無を表示するワークフロー。開発体験向上目的。
|
|
251
|
+
|
|
252
|
+
### Phase 6: einja-create-pr Skill新規作成
|
|
253
|
+
|
|
254
|
+
**新規ファイル:** `.claude/skills/einja-create-pr/SKILL.md`
|
|
255
|
+
|
|
256
|
+
**責務:** PR作成時にchangeset自動生成 + ラベル付与 + PR作成を一括実行
|
|
257
|
+
|
|
258
|
+
**処理フロー(5ステップ):**
|
|
259
|
+
|
|
260
|
+
1. **差分分析**: `git log --format="%s%n%b" origin/{base}..HEAD` + `git diff --name-only origin/{base}..HEAD`
|
|
261
|
+
2. **changeset生成判定**:
|
|
262
|
+
- スキップ条件: staging→main昇格PR、apps/配下に変更なし(docs/CI/設定のみ)、既にchangesetファイルがある
|
|
263
|
+
- 変更種別推定: コミットメッセージプレフィックスの最大値(feat+fix→minor, feat!→major)
|
|
264
|
+
- パッケージ判定: ファイルパスから(apps/web/** → @repo/web等)
|
|
265
|
+
3. **changeset生成**: `.changeset/{ランダム名}.md` を作成 → コミット
|
|
266
|
+
4. **ラベル判定**: PRタイトルプレフィックスから単一ラベル選択(enhancement/bug/maintenance)
|
|
267
|
+
5. **PR作成**: `gh pr create --title ... --body ... --label ...`
|
|
268
|
+
|
|
269
|
+
**動作モード:**
|
|
270
|
+
| モード | トリガー | changeset確認 | ラベル確認 |
|
|
271
|
+
|--------|---------|--------------|-----------|
|
|
272
|
+
| 自動 | task-exec/issue-exec経由 | 推定値で自動決定 | 自動 |
|
|
273
|
+
| 対話 | 手動 `/einja-create-pr` | AskUserQuestionで確認 | AskUserQuestionで確認 |
|
|
274
|
+
|
|
275
|
+
**既存ワークフローへの統合:**
|
|
276
|
+
- `issue-exec.md` のManagerのPR作成部分を `/einja-create-pr` 呼び出しに変更
|
|
277
|
+
- `task-exec.md` は変更不要(現状PR作成は含まない)
|
|
278
|
+
|
|
279
|
+
**ラベル判定ルール(単一ラベル、優先度順):**
|
|
280
|
+
```
|
|
281
|
+
feat!/BREAKING → breaking-change
|
|
282
|
+
feat: → enhancement
|
|
283
|
+
fix: → bug
|
|
284
|
+
その他 → maintenance
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Phase 7: 既存ドキュメントへの追記
|
|
288
|
+
|
|
289
|
+
**`docs/einja/steering/infrastructure/deployment.md`** — 以下のセクションを追記:
|
|
290
|
+
- 「8. リリース管理」: GitHub Release/PreRelease自動作成フロー
|
|
291
|
+
- 「9. バージョニング戦略」: changesets採番ルール、タグ形式
|
|
292
|
+
- 「10. 承認フロー」: GitHub Environments設定、production承認ゲート
|
|
293
|
+
- ワークフロー一覧テーブルに release-staging/release-production ジョブを追加
|
|
294
|
+
|
|
295
|
+
**`docs/einja/steering/development-workflow.md`** — 以下を追記:
|
|
296
|
+
- changesetの運用フロー(PRにchangeset含める手順)
|
|
297
|
+
- 「Phase A: 仕様書作成」→「Phase B: タスク実行」→「Phase C: リリース」の流れ
|
|
298
|
+
|
|
299
|
+
## バージョニング採番ルール
|
|
300
|
+
|
|
301
|
+
### セマンティックバージョニング(changeset指定)
|
|
302
|
+
|
|
303
|
+
| 変更種別 | changeset指定 | バージョン変更例 | 使用シーン |
|
|
304
|
+
|---------|--------------|----------------|----------|
|
|
305
|
+
| 破壊的変更 | `major` | `0.1.0` → `1.0.0` | API仕様変更、DB破壊的マイグレーション |
|
|
306
|
+
| 新機能追加 | `minor` | `0.1.0` → `0.2.0` | 新画面、新API追加 |
|
|
307
|
+
| バグ修正 | `patch` | `0.1.0` → `0.1.1` | 不具合修正、パフォーマンス改善 |
|
|
308
|
+
|
|
309
|
+
### タグ形式
|
|
310
|
+
|
|
311
|
+
| 環境 | タグ形式 | 例 | GitHub Release種別 |
|
|
312
|
+
|------|---------|-----|------------------|
|
|
313
|
+
| staging | `v{version}-rc.{run_number}` | `v0.2.0-rc.42` | PreRelease |
|
|
314
|
+
| production | `v{version}` | `v0.2.0` | Release |
|
|
315
|
+
|
|
316
|
+
### changeset消費タイミング
|
|
317
|
+
|
|
318
|
+
| ブランチ | changeset消費 | バージョンバンプ | タグ形式 |
|
|
319
|
+
|---------|-------------|----------------|---------|
|
|
320
|
+
| staging | **消費しない** | なし(package.json据え置き) | `v{current}-rc.{run_number}` |
|
|
321
|
+
| main | `changeset version` で消費 | package.json更新 | `v{new_version}` |
|
|
322
|
+
|
|
323
|
+
## 変更ファイル一覧
|
|
324
|
+
|
|
325
|
+
| ファイル | 操作 | Phase |
|
|
326
|
+
|---------|------|-------|
|
|
327
|
+
| `package.json` | 改修(devDeps + scripts追加) | 1 |
|
|
328
|
+
| `.changeset/config.json` | 新規 | 1 |
|
|
329
|
+
| `.github/workflows/deploy-stable-branches.yml` | 大規模改修(ジョブ分割 + リリース統合) | 3 |
|
|
330
|
+
| `.github/release.yml` | 新規 | 4 |
|
|
331
|
+
| `.github/workflows/changeset-status.yml` | 新規(任意) | 5 |
|
|
332
|
+
| `.claude/skills/einja-create-pr/SKILL.md` | 新規(changeset生成 + ラベル付与 + PR作成) | 6 |
|
|
333
|
+
| `.claude/commands/einja/issue-exec.md` | 改修(PR作成部分をeinja-create-pr呼び出しに変更) | 6 |
|
|
334
|
+
| `docs/einja/steering/infrastructure/deployment.md` | 追記(リリース管理・バージョニング・承認フロー) | 7 |
|
|
335
|
+
| `docs/einja/steering/development-workflow.md` | 追記(changeset運用フロー) | 7 |
|
|
336
|
+
|
|
337
|
+
**変更しないファイル:**
|
|
338
|
+
- `release-cli.yml` / `release-create-einja-app.yml`(NPMリリースは独立運用を維持)
|
|
339
|
+
- `deploy-pr-preview.yml` / `cleanup-*.yml` / `claude.yml`
|
|
340
|
+
- Composite Actions(`setup`, `ci`, `migrate`, `neon-export-env`)
|
|
341
|
+
|
|
342
|
+
**廃止:**
|
|
343
|
+
- `release-app.yml` は作成しない(deploy-stable-branches.yml内に統合)
|
|
344
|
+
|
|
345
|
+
## NPMリリースとの棲み分け
|
|
346
|
+
|
|
347
|
+
| タグパターン | 用途 | 生成元 |
|
|
348
|
+
|-------------|------|--------|
|
|
349
|
+
| `v1.2.0` | アプリ Stable Release | deploy-stable-branches.yml内 release-production ジョブ |
|
|
350
|
+
| `v1.2.0-rc.42` | アプリ PreRelease | deploy-stable-branches.yml内 release-staging ジョブ |
|
|
351
|
+
| `cli-v0.1.41` | @einja/dev-cli | 手動タグ(既存運用) |
|
|
352
|
+
| `create-einja-app-v0.3.2` | create-einja-app | 手動タグ(既存運用) |
|
|
353
|
+
|
|
354
|
+
## 検証方法
|
|
355
|
+
|
|
356
|
+
1. **Phase 1検証**: `pnpm changeset` で対話UIが起動し、`.changeset/` にmdファイルが生成されること
|
|
357
|
+
2. **Phase 3検証**:
|
|
358
|
+
- developブランチにpush → deploy-developが承認なしで実行(従来通り)
|
|
359
|
+
- stagingブランチにpush → deploy-stagingが承認なしで実行 → release-stagingでPreRelease作成
|
|
360
|
+
- mainブランチにpush → migrate-productionがGitHub UIで承認待ち → 承認後migrate+deploy → release-productionでRelease作成
|
|
361
|
+
- バージョンバンプコミットでワークフローが再トリガーされない(無限ループなし)ことを確認
|
|
362
|
+
3. **Phase 6検証**: `/einja-create-pr` でchangeset生成 + ラベル付与 + PR作成が一括実行されること
|