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.
Files changed (120) hide show
  1. package/README.md +34 -1
  2. package/dist/cli.js +92 -80
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/default/.changeset/config.json +11 -0
  6. package/templates/default/.claude/hooks/einja/plan-mode-skill-loader.sh +27 -0
  7. package/templates/default/.claude/settings.json +29 -1
  8. package/templates/default/.claude/skills/cli-package-specs/SKILL.md +247 -0
  9. package/templates/default/.einja-sync.json +1 -1
  10. package/templates/default/.env.personal.example +6 -2
  11. package/templates/default/.envrc +5 -0
  12. package/templates/default/.github/release.yml +10 -0
  13. package/templates/default/.github/workflows/changeset-status.yml +60 -0
  14. package/templates/default/.github/workflows/deploy-pr-preview.yml +23 -24
  15. package/templates/default/.github/workflows/deploy-stable-branches.yml +336 -100
  16. package/templates/default/.mcp.json +2 -12
  17. package/templates/default/.serena/project.yml +7 -0
  18. package/templates/default/CLAUDE.md +61 -10
  19. package/templates/default/README.md +22 -10
  20. package/templates/default/apps/admin/package.json +1 -1
  21. package/templates/default/apps/admin/tsconfig.json +2 -1
  22. package/templates/default/apps/web/package.json +1 -1
  23. package/templates/default/apps/web/tsconfig.json +2 -1
  24. package/templates/default/docs/plans/.gitkeep +0 -0
  25. package/templates/default/docs/plans/agile-munching-knuth.md +161 -0
  26. package/templates/default/docs/plans/agile-riding-nova.md +158 -0
  27. package/templates/default/docs/plans/agile-wibbling-dusk.md +91 -0
  28. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-a87e67c.md +221 -0
  29. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-ab73a1c.md +107 -0
  30. package/templates/default/docs/plans/ancient-greeting-flamingo.md +120 -0
  31. package/templates/default/docs/plans/ancient-watching-otter.md +152 -0
  32. package/templates/default/docs/plans/bright-sauteeing-bumblebee.md +30 -0
  33. package/templates/default/docs/plans/bright-stargazing-dawn.md +87 -0
  34. package/templates/default/docs/plans/calm-stirring-bonbon.md +196 -0
  35. package/templates/default/docs/plans/calm-watching-widget.md +111 -0
  36. package/templates/default/docs/plans/cheerful-wiggling-ullman.md +164 -0
  37. package/templates/default/docs/plans/compiled-humming-cherny.md +94 -0
  38. package/templates/default/docs/plans/composed-doodling-mountain.md +362 -0
  39. package/templates/default/docs/plans/dapper-launching-lynx.md +81 -0
  40. package/templates/default/docs/plans/dazzling-foraging-cascade.md +32 -0
  41. package/templates/default/docs/plans/effervescent-munching-kite-agent-ac08baf.md +672 -0
  42. package/templates/default/docs/plans/effervescent-munching-kite-agent-aecc373.md +442 -0
  43. package/templates/default/docs/plans/effervescent-munching-kite.md +263 -0
  44. package/templates/default/docs/plans/enchanted-wiggling-ember-agent-a5befd57d0ca4c7c7.md +177 -0
  45. package/templates/default/docs/plans/enchanted-wiggling-ember.md +170 -0
  46. package/templates/default/docs/plans/federated-questing-kahan.md +47 -0
  47. package/templates/default/docs/plans/fix-orphan-cleaner-review.md +25 -0
  48. package/templates/default/docs/plans/fix-sync-template-variables.md +162 -0
  49. package/templates/default/docs/plans/flickering-pondering-hearth.md +26 -0
  50. package/templates/default/docs/plans/fluttering-snuggling-sprout.md +172 -0
  51. package/templates/default/docs/plans/generic-sleeping-snowglobe-agent-a41d8da.md +179 -0
  52. package/templates/default/docs/plans/generic-sleeping-snowglobe.md +108 -0
  53. package/templates/default/docs/plans/generic-snuggling-pudding.md +57 -0
  54. package/templates/default/docs/plans/glimmering-giggling-sedgewick.md +126 -0
  55. package/templates/default/docs/plans/glittery-swimming-bachman.md +78 -0
  56. package/templates/default/docs/plans/happy-watching-toast.md +56 -0
  57. package/templates/default/docs/plans/harmonic-strolling-nebula.md +210 -0
  58. package/templates/default/docs/plans/idempotent-wiggling-cherny.md +122 -0
  59. package/templates/default/docs/plans/import-alias-refactor.md +75 -0
  60. package/templates/default/docs/plans/lazy-percolating-sloth-agent-abda679.md +346 -0
  61. package/templates/default/docs/plans/lazy-percolating-sloth.md +151 -0
  62. package/templates/default/docs/plans/linked-greeting-llama-agent-a7a6e5b.md +345 -0
  63. package/templates/default/docs/plans/linked-greeting-llama.md +467 -0
  64. package/templates/default/docs/plans/lovely-bubbling-rose.md +80 -0
  65. package/templates/default/docs/plans/optimized-watching-sprout.md +149 -0
  66. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a292da6.md +288 -0
  67. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a819699.md +366 -0
  68. package/templates/default/docs/plans/peaceful-beaming-toast-agent-ac11de2.md +474 -0
  69. package/templates/default/docs/plans/peaceful-beaming-toast.md +345 -0
  70. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6194c.md +300 -0
  71. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6900e.md +444 -0
  72. package/templates/default/docs/plans/purrfect-spinning-hickey.md +361 -0
  73. package/templates/default/docs/plans/recursive-fluttering-mitten.md +176 -0
  74. package/templates/default/docs/plans/recursive-kindling-lemon-agent-a42199e.md +186 -0
  75. package/templates/default/docs/plans/recursive-kindling-lemon.md +36 -0
  76. package/templates/default/docs/plans/seed-migration-tests.md +47 -0
  77. package/templates/default/docs/plans/sprightly-leaping-manatee.md +224 -0
  78. package/templates/default/docs/plans/stateful-wishing-lerdorf.md +161 -0
  79. package/templates/default/docs/plans/streamed-purring-wreath.md +40 -0
  80. package/templates/default/docs/plans/synthetic-percolating-pearl.md +101 -0
  81. package/templates/default/docs/plans/todo-create-einja-app-ux-fix.md +16 -0
  82. package/templates/default/docs/plans/todo-direnv-hang-fix.md +12 -0
  83. package/templates/default/docs/plans/todo-fix-sync-template-variables.md +21 -0
  84. package/templates/default/docs/plans/todo-github-actions-release-workflow.md +34 -0
  85. package/templates/default/docs/plans/todo-issue-spec-rename.md +24 -0
  86. package/templates/default/docs/plans/todo-phase4-marker-update.md +39 -0
  87. package/templates/default/docs/plans/todo-skill-creator-sync.md +23 -0
  88. package/templates/default/docs/plans/todo-skill-creator-upgrade.md +18 -0
  89. package/templates/default/docs/plans/typed-snuggling-parnas-agent-a6f6391.md +476 -0
  90. package/templates/default/docs/plans/typed-snuggling-parnas-agent-adb678b.md +144 -0
  91. package/templates/default/docs/plans/typed-snuggling-parnas.md +84 -0
  92. package/templates/default/docs/plans/velvety-chasing-spark.md +28 -0
  93. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a30aa4f.md +534 -0
  94. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a57a278.md +508 -0
  95. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a90b809.md +421 -0
  96. package/templates/default/docs/plans/warm-hopping-lighthouse.md +199 -0
  97. package/templates/default/docs/plans/wondrous-strolling-crystal-agent-a0615fc.md +215 -0
  98. package/templates/default/docs/plans/wondrous-strolling-crystal.md +182 -0
  99. package/templates/default/docs/plans/zesty-roaming-steele.md +74 -0
  100. package/templates/default/docs/verification-test.md +2 -0
  101. package/templates/default/gitignore +9 -1
  102. package/templates/default/package.json +6 -2
  103. package/templates/default/packages/admin-ui/package.json +1 -1
  104. package/templates/default/packages/server-core/tsconfig.json +6 -1
  105. package/templates/default/pnpm-lock.yaml +823 -57
  106. package/templates/default/scripts/ensure-serena.sh +75 -0
  107. package/templates/default/scripts/env-rotate-secrets.ts +66 -6
  108. package/templates/default/scripts/init-github.ts +363 -0
  109. package/templates/default/scripts/init.sh +11 -5
  110. package/templates/default/scripts/lib/worktree-config.ts +64 -0
  111. package/templates/default/scripts/setup-dev.ts +16 -1
  112. package/templates/default/scripts/stop-serena.sh +25 -0
  113. package/templates/default/scripts/worktree/dev.ts +2 -2
  114. package/templates/default/.claude/skills/create-einja-app-release/SKILL.md +0 -186
  115. package/templates/default/.claude/skills/dev-cli-release/SKILL.md +0 -173
  116. package/templates/default/.cursor/commands/spec-create.md +0 -227
  117. package/templates/default/.cursor/commands/task-exec.md +0 -287
  118. package/templates/default/.cursor/commands/update-docs-by-task-specs.md +0 -448
  119. /package/templates/default/scripts/{cli-template-update.ts → _cli-template-update.ts} +0 -0
  120. /package/templates/default/scripts/{template-update.ts → _template-update.ts} +0 -0
@@ -4,12 +4,20 @@ on:
4
4
  push:
5
5
  branches: [main, develop, staging]
6
6
 
7
+ # GITHUB_TOKENで作成されたpushイベントはデフォルトでワークフロー再トリガーしない
8
+ # 念のため明示的にbotコミットを除外(release-productionのバージョンバンプ対策)
9
+
10
+ permissions:
11
+ contents: write
12
+
7
13
  concurrency:
8
14
  group: deploy-${{ github.ref_name }}
9
15
  cancel-in-progress: false
10
16
 
11
17
  jobs:
12
18
  ci:
19
+ # github-actions[bot] のバージョンバンプコミットはスキップ
20
+ if: "!contains(github.event.head_commit.message, 'chore: release v')"
13
21
  runs-on: ubuntu-latest
14
22
  services:
15
23
  postgres:
@@ -42,6 +50,7 @@ jobs:
42
50
  outputs:
43
51
  web: ${{ steps.filter.outputs.web }}
44
52
  admin: ${{ steps.filter.outputs.admin }}
53
+ deploy_matrix: ${{ steps.matrix.outputs.matrix }}
45
54
  steps:
46
55
  - uses: actions/checkout@v4
47
56
  with:
@@ -62,6 +71,7 @@ jobs:
62
71
  - 'turbo.json'
63
72
  - 'tsconfig.json'
64
73
  - '.env.*'
74
+ - '.github/workflows/**'
65
75
  admin:
66
76
  - 'apps/admin/**'
67
77
  - 'packages/admin-ui/**'
@@ -73,72 +83,154 @@ jobs:
73
83
  - 'turbo.json'
74
84
  - 'tsconfig.json'
75
85
  - '.env.*'
86
+ - '.github/workflows/**'
76
87
 
77
- migrate:
78
- needs: ci
79
- if: github.ref_name == 'main' || github.ref_name == 'staging'
88
+ - name: Build deploy matrix
89
+ id: matrix
90
+ run: |
91
+ MATRIX='[]'
92
+ if [ "${{ steps.filter.outputs.web }}" = "true" ]; then
93
+ MATRIX=$(echo "$MATRIX" | jq -c '. + [{"app": "web"}]')
94
+ fi
95
+ if [ "${{ steps.filter.outputs.admin }}" = "true" ]; then
96
+ MATRIX=$(echo "$MATRIX" | jq -c '. + [{"app": "admin"}]')
97
+ fi
98
+ echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
99
+
100
+ # ========================================
101
+ # develop: 環境なし、承認なし、マイグレーションなし
102
+ # ========================================
103
+ deploy-develop:
104
+ needs: [ci, changes]
105
+ if: github.ref_name == 'develop' && needs.changes.outputs.deploy_matrix != '[]'
80
106
  runs-on: ubuntu-latest
107
+ strategy:
108
+ fail-fast: false
109
+ matrix:
110
+ include: ${{ fromJSON(needs.changes.outputs.deploy_matrix) }}
111
+ env:
112
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
113
+ VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
114
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
115
+ TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
81
116
  steps:
82
117
  - uses: actions/checkout@v4
83
118
 
84
- # main: production環境マイグレーション(シード無効)
85
- - name: Migrate production
86
- if: github.ref_name == 'main'
87
- uses: ./.github/actions/migrate
88
- with:
89
- dotenv-file: ${{ github.workspace }}/.env.production
90
- dotenv-private-key: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
91
- run-seed: 'false'
119
+ - name: Setup Environment
120
+ uses: ./.github/actions/setup
121
+
122
+ - name: Resolve Vercel Project ID
123
+ id: resolve
124
+ run: |
125
+ case "${{ matrix.app }}" in
126
+ web) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_WEB }}" >> $GITHUB_OUTPUT ;;
127
+ admin) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_ADMIN }}" >> $GITHUB_OUTPUT ;;
128
+ esac
129
+
130
+ - name: Set VERCEL_PROJECT_ID
131
+ run: echo "VERCEL_PROJECT_ID=${{ steps.resolve.outputs.project_id }}" >> $GITHUB_ENV
132
+
133
+ - name: Install Vercel CLI
134
+ run: pnpm add -g vercel@latest
135
+
136
+ - name: Set deploy environment
137
+ run: |
138
+ echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.develop" >> $GITHUB_ENV
139
+ echo "DEPLOY_VERCEL_ENV=preview" >> $GITHUB_ENV
140
+ echo "DEPLOY_PROD_FLAG=" >> $GITHUB_ENV
141
+ echo "DEPLOY_RUN_ALIAS=true" >> $GITHUB_ENV
142
+
143
+ - name: Extract alias domain
144
+ run: |
145
+ case "${{ matrix.app }}" in
146
+ web)
147
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
148
+ echo "VERCEL_ALIAS_DOMAIN=$VERCEL_ALIAS_DOMAIN_WEB" >> "$GITHUB_ENV"
149
+ '
150
+ ;;
151
+ admin)
152
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
153
+ echo "VERCEL_ALIAS_DOMAIN=$VERCEL_ALIAS_DOMAIN_ADMIN" >> "$GITHUB_ENV"
154
+ '
155
+ ;;
156
+ esac
157
+ env:
158
+ DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
159
+
160
+ - name: Pull Vercel Environment
161
+ run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
162
+
163
+ - name: Build Project
164
+ run: |
165
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- \
166
+ vercel build ${{ env.DEPLOY_PROD_FLAG }} --token=$VERCEL_TOKEN
167
+ env:
168
+ DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
169
+
170
+ - name: Deploy to Vercel
171
+ id: deploy
172
+ run: |
173
+ npx dotenvx run -f $DEPLOY_DOTENV_FILE -- bash -c '
174
+ declare -a ENV_FLAGS=()
175
+ while IFS= read -r key; do
176
+ case "$key" in
177
+ NEON_*|VERCEL_ALIAS_DOMAIN_*|DOTENV_PUBLIC_KEY_*) continue ;;
178
+ esac
179
+ value="${!key}"
180
+ if [ -n "$value" ]; then
181
+ echo "::add-mask::${value}"
182
+ fi
183
+ ENV_FLAGS+=("--env" "${key}=${value}")
184
+ done < <(grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" "$DEPLOY_DOTENV_FILE" | cut -d= -f1)
185
+
186
+ echo "Deploying with ${#ENV_FLAGS[@]} runtime env vars..."
187
+ DEPLOY_URL=$(vercel deploy --prebuilt $DEPLOY_PROD_FLAG "${ENV_FLAGS[@]}" --token="$VERCEL_TOKEN")
188
+ echo "url=$DEPLOY_URL" >> "$GITHUB_OUTPUT"
189
+ '
190
+ env:
191
+ DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
192
+
193
+ - name: Set Vercel alias
194
+ run: vercel alias ${{ steps.deploy.outputs.url }} "${{ env.VERCEL_ALIAS_DOMAIN }}" --token=$VERCEL_TOKEN
195
+
196
+ # ========================================
197
+ # staging: staging環境、承認なし
198
+ # ========================================
199
+ migrate-staging:
200
+ needs: ci
201
+ if: github.ref_name == 'staging'
202
+ runs-on: ubuntu-latest
203
+ steps:
204
+ - uses: actions/checkout@v4
92
205
 
93
- # staging: ステージング環境マイグレーション(シード無効)
94
206
  - name: Migrate staging
95
- if: github.ref_name == 'staging'
96
207
  uses: ./.github/actions/migrate
97
208
  with:
98
209
  dotenv-file: ${{ github.workspace }}/.env.staging
99
210
  dotenv-private-key: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
100
211
  run-seed: 'false'
101
212
 
102
- # Web/Adminデプロイ(並列実行・変更検知付き)
103
- deploy:
104
- needs: [ci, migrate, changes]
105
- if: always() && needs.ci.result == 'success' && needs.changes.result == 'success' && (needs.migrate.result == 'success' || needs.migrate.result == 'skipped')
213
+ deploy-staging:
214
+ needs: [ci, migrate-staging, changes]
215
+ if: github.ref_name == 'staging' && needs.changes.outputs.deploy_matrix != '[]'
216
+ environment: staging
106
217
  runs-on: ubuntu-latest
107
218
  strategy:
108
219
  fail-fast: false
109
220
  matrix:
110
- app: [web, admin]
221
+ include: ${{ fromJSON(needs.changes.outputs.deploy_matrix) }}
111
222
  env:
112
223
  VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
113
224
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
114
225
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
115
226
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
116
227
  steps:
117
- # 変更検知によるスキップ判定
118
- - name: Check if deploy is needed
119
- id: check
120
- run: |
121
- case "${{ matrix.app }}" in
122
- web) CHANGED="${{ needs.changes.outputs.web }}" ;;
123
- admin) CHANGED="${{ needs.changes.outputs.admin }}" ;;
124
- esac
125
- if [ "$CHANGED" = "true" ]; then
126
- echo "should_deploy=true" >> $GITHUB_OUTPUT
127
- echo "${{ matrix.app }}: changes detected, will deploy"
128
- else
129
- echo "should_deploy=false" >> $GITHUB_OUTPUT
130
- echo "${{ matrix.app }}: no changes detected, skipping deploy"
131
- fi
132
-
133
228
  - uses: actions/checkout@v4
134
- if: steps.check.outputs.should_deploy == 'true'
135
229
 
136
230
  - name: Setup Environment
137
- if: steps.check.outputs.should_deploy == 'true'
138
231
  uses: ./.github/actions/setup
139
232
 
140
233
  - name: Resolve Vercel Project ID
141
- if: steps.check.outputs.should_deploy == 'true'
142
234
  id: resolve
143
235
  run: |
144
236
  case "${{ matrix.app }}" in
@@ -147,43 +239,19 @@ jobs:
147
239
  esac
148
240
 
149
241
  - name: Set VERCEL_PROJECT_ID
150
- if: steps.check.outputs.should_deploy == 'true'
151
242
  run: echo "VERCEL_PROJECT_ID=${{ steps.resolve.outputs.project_id }}" >> $GITHUB_ENV
152
243
 
153
244
  - name: Install Vercel CLI
154
- if: steps.check.outputs.should_deploy == 'true'
155
245
  run: pnpm add -g vercel@latest
156
246
 
157
- # ブランチ判定 環境変数を $GITHUB_ENV に書き出す
158
- - name: Determine environment
159
- if: steps.check.outputs.should_deploy == 'true'
247
+ - name: Set deploy environment
160
248
  run: |
161
- BRANCH="${GITHUB_REF##*/}"
162
- case "$BRANCH" in
163
- main)
164
- echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.production" >> $GITHUB_ENV
165
- echo "DEPLOY_VERCEL_ENV=production" >> $GITHUB_ENV
166
- echo "DEPLOY_PROD_FLAG=--prod" >> $GITHUB_ENV
167
- echo "DEPLOY_RUN_ALIAS=false" >> $GITHUB_ENV
168
- ;;
169
- develop)
170
- echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.develop" >> $GITHUB_ENV
171
- echo "DEPLOY_VERCEL_ENV=preview" >> $GITHUB_ENV
172
- echo "DEPLOY_PROD_FLAG=" >> $GITHUB_ENV
173
- echo "DEPLOY_RUN_ALIAS=true" >> $GITHUB_ENV
174
- ;;
175
- staging)
176
- echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.staging" >> $GITHUB_ENV
177
- echo "DEPLOY_VERCEL_ENV=preview" >> $GITHUB_ENV
178
- echo "DEPLOY_PROD_FLAG=" >> $GITHUB_ENV
179
- echo "DEPLOY_RUN_ALIAS=true" >> $GITHUB_ENV
180
- ;;
181
- esac
182
- echo "Branch: $BRANCH, Environment: $(cat $GITHUB_ENV | grep DEPLOY_VERCEL_ENV)"
249
+ echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.staging" >> $GITHUB_ENV
250
+ echo "DEPLOY_VERCEL_ENV=preview" >> $GITHUB_ENV
251
+ echo "DEPLOY_PROD_FLAG=" >> $GITHUB_ENV
252
+ echo "DEPLOY_RUN_ALIAS=true" >> $GITHUB_ENV
183
253
 
184
- # Extract alias domain(自アプリ分のみ)
185
254
  - name: Extract alias domain
186
- if: steps.check.outputs.should_deploy == 'true' && env.DEPLOY_RUN_ALIAS == 'true'
187
255
  run: |
188
256
  case "${{ matrix.app }}" in
189
257
  web)
@@ -199,61 +267,229 @@ jobs:
199
267
  esac
200
268
  env:
201
269
  DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
202
- DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
203
270
 
204
- # Vercel pull
205
271
  - name: Pull Vercel Environment
206
- if: steps.check.outputs.should_deploy == 'true'
207
272
  run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
208
273
 
209
- # Vercel環境変数の自動同期(encrypted-only方式:dotenvxで管理しているもの = Vercelに同期すべきもの)
274
+ - name: Build Project
275
+ run: |
276
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- \
277
+ vercel build ${{ env.DEPLOY_PROD_FLAG }} --token=$VERCEL_TOKEN
278
+ env:
279
+ DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
280
+
281
+ - name: Deploy to Vercel
282
+ id: deploy
283
+ run: |
284
+ npx dotenvx run -f $DEPLOY_DOTENV_FILE -- bash -c '
285
+ declare -a ENV_FLAGS=()
286
+ while IFS= read -r key; do
287
+ case "$key" in
288
+ NEON_*|VERCEL_ALIAS_DOMAIN_*|DOTENV_PUBLIC_KEY_*) continue ;;
289
+ esac
290
+ value="${!key}"
291
+ if [ -n "$value" ]; then
292
+ echo "::add-mask::${value}"
293
+ fi
294
+ ENV_FLAGS+=("--env" "${key}=${value}")
295
+ done < <(grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" "$DEPLOY_DOTENV_FILE" | cut -d= -f1)
296
+
297
+ echo "Deploying with ${#ENV_FLAGS[@]} runtime env vars..."
298
+ DEPLOY_URL=$(vercel deploy --prebuilt $DEPLOY_PROD_FLAG "${ENV_FLAGS[@]}" --token="$VERCEL_TOKEN")
299
+ echo "url=$DEPLOY_URL" >> "$GITHUB_OUTPUT"
300
+ '
301
+ env:
302
+ DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
303
+
304
+ - name: Set Vercel alias
305
+ run: vercel alias ${{ steps.deploy.outputs.url }} "${{ env.VERCEL_ALIAS_DOMAIN }}" --token=$VERCEL_TOKEN
306
+
307
+ # staging: PreRelease作成(changeset未消費)
308
+ release-staging:
309
+ needs: deploy-staging
310
+ if: github.ref_name == 'staging'
311
+ runs-on: ubuntu-latest
312
+ steps:
313
+ - uses: actions/checkout@v4
314
+ with:
315
+ fetch-depth: 0
316
+
317
+ - name: Check for changesets
318
+ id: check
319
+ run: |
320
+ COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
321
+ echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
322
+
323
+ - name: Create PreRelease tag
324
+ if: steps.check.outputs.has_changesets == 'true'
325
+ id: tag
326
+ run: |
327
+ VERSION=$(node -p "require('./package.json').version")
328
+ TAG="v${VERSION}-rc.${{ github.run_number }}"
329
+ git tag "$TAG"
330
+ git push origin "$TAG"
331
+ echo "tag=$TAG" >> $GITHUB_OUTPUT
332
+
333
+ - name: Create GitHub PreRelease
334
+ if: steps.tag.outputs.tag
335
+ run: |
336
+ PREV=$(git tag --list 'v*-rc.*' --sort=-v:refname | grep -v "${{ steps.tag.outputs.tag }}" | head -1)
337
+ OPTS="--prerelease --target staging --generate-notes"
338
+ [[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
339
+ gh release create "${{ steps.tag.outputs.tag }}" --title "Pre-release ${{ steps.tag.outputs.tag }}" $OPTS
340
+ env:
341
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
342
+
343
+ # ========================================
344
+ # main: production環境、1名承認 → migrate → deploy → release
345
+ # ========================================
346
+ migrate-production:
347
+ needs: ci
348
+ if: github.ref_name == 'main'
349
+ environment: production
350
+ runs-on: ubuntu-latest
351
+ steps:
352
+ - uses: actions/checkout@v4
353
+
354
+ - name: Migrate production
355
+ uses: ./.github/actions/migrate
356
+ with:
357
+ dotenv-file: ${{ github.workspace }}/.env.production
358
+ dotenv-private-key: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
359
+ run-seed: 'false'
360
+
361
+ deploy-production:
362
+ needs: [migrate-production, changes]
363
+ if: github.ref_name == 'main' && needs.changes.outputs.deploy_matrix != '[]'
364
+ runs-on: ubuntu-latest
365
+ strategy:
366
+ fail-fast: false
367
+ matrix:
368
+ include: ${{ fromJSON(needs.changes.outputs.deploy_matrix) }}
369
+ env:
370
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
371
+ VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
372
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
373
+ TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
374
+ steps:
375
+ - uses: actions/checkout@v4
376
+
377
+ - name: Setup Environment
378
+ uses: ./.github/actions/setup
379
+
380
+ - name: Resolve Vercel Project ID
381
+ id: resolve
382
+ run: |
383
+ case "${{ matrix.app }}" in
384
+ web) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_WEB }}" >> $GITHUB_OUTPUT ;;
385
+ admin) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_ADMIN }}" >> $GITHUB_OUTPUT ;;
386
+ esac
387
+
388
+ - name: Set VERCEL_PROJECT_ID
389
+ run: echo "VERCEL_PROJECT_ID=${{ steps.resolve.outputs.project_id }}" >> $GITHUB_ENV
390
+
391
+ - name: Install Vercel CLI
392
+ run: pnpm add -g vercel@latest
393
+
394
+ - name: Set deploy environment
395
+ run: |
396
+ echo "DEPLOY_DOTENV_FILE=$GITHUB_WORKSPACE/.env.production" >> $GITHUB_ENV
397
+ echo "DEPLOY_VERCEL_ENV=production" >> $GITHUB_ENV
398
+ echo "DEPLOY_PROD_FLAG=--prod" >> $GITHUB_ENV
399
+ echo "DEPLOY_RUN_ALIAS=false" >> $GITHUB_ENV
400
+
401
+ - name: Pull Vercel Environment
402
+ run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
403
+
210
404
  - name: Sync environment variables to Vercel
211
- if: steps.check.outputs.should_deploy == 'true'
212
405
  run: |
213
- # dotenvxファイルから encrypted: を含むキー名を抽出
214
- ENCRYPTED_KEYS=$(grep 'encrypted:' ${{ env.DEPLOY_DOTENV_FILE }} | sed 's/=.*//')
215
-
216
- npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c "
217
- for key in $ENCRYPTED_KEYS; do
218
- value=\$(printenv \"\$key\" 2>/dev/null || true)
219
- if [ -n \"\$value\" ]; then
220
- echo \"Syncing \$key to Vercel (${{ env.DEPLOY_VERCEL_ENV }})...\"
221
- echo \"\$value\" | vercel env rm \"\$key\" ${{ env.DEPLOY_VERCEL_ENV }} --yes --token=\$VERCEL_TOKEN 2>/dev/null || true
222
- echo \"\$value\" | vercel env add \"\$key\" ${{ env.DEPLOY_VERCEL_ENV }} --token=\$VERCEL_TOKEN
406
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
407
+ grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" ${{ env.DEPLOY_DOTENV_FILE }} | cut -d= -f1 | while read -r key; do
408
+ case "$key" in
409
+ NEON_*) continue ;;
410
+ esac
411
+ value="${!key}"
412
+ if [ -n "$value" ]; then
413
+ echo "Syncing $key to Vercel (${{ env.DEPLOY_VERCEL_ENV }})..."
414
+ echo "$value" | vercel env rm "$key" ${{ env.DEPLOY_VERCEL_ENV }} --yes --token=$VERCEL_TOKEN 2>/dev/null || true
415
+ echo "$value" | vercel env add "$key" ${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
223
416
  fi
224
417
  done
225
- "
418
+ '
226
419
  env:
227
420
  DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
228
- DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
229
- DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
230
421
 
231
- # Re-pull after sync
232
- - name: Re-pull Vercel Environment
233
- if: steps.check.outputs.should_deploy == 'true'
422
+ - name: Re-pull Vercel Environment after sync
234
423
  run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
235
424
 
236
- # Vercel build (dotenvxでアプリ環境変数を注入)
237
425
  - name: Build Project
238
- if: steps.check.outputs.should_deploy == 'true'
239
426
  run: |
240
427
  npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- \
241
428
  vercel build ${{ env.DEPLOY_PROD_FLAG }} --token=$VERCEL_TOKEN
242
429
  env:
243
430
  DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
244
- DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
245
- DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
246
431
 
247
- # Vercel deploy
248
432
  - name: Deploy to Vercel
249
- if: steps.check.outputs.should_deploy == 'true'
250
433
  id: deploy
251
434
  run: |
252
- DEPLOY_URL=$(vercel deploy --prebuilt ${{ env.DEPLOY_PROD_FLAG }} --token=$VERCEL_TOKEN)
253
- echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT
254
- echo "Deployed to: $DEPLOY_URL"
435
+ npx dotenvx run -f $DEPLOY_DOTENV_FILE -- bash -c '
436
+ declare -a ENV_FLAGS=()
437
+ while IFS= read -r key; do
438
+ case "$key" in
439
+ NEON_*|VERCEL_ALIAS_DOMAIN_*|DOTENV_PUBLIC_KEY_*) continue ;;
440
+ esac
441
+ value="${!key}"
442
+ if [ -n "$value" ]; then
443
+ echo "::add-mask::${value}"
444
+ fi
445
+ ENV_FLAGS+=("--env" "${key}=${value}")
446
+ done < <(grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" "$DEPLOY_DOTENV_FILE" | cut -d= -f1)
255
447
 
256
- # Alias (develop/stagingのみ)
257
- - name: Set Vercel alias
258
- if: steps.check.outputs.should_deploy == 'true' && env.DEPLOY_RUN_ALIAS == 'true'
259
- run: vercel alias ${{ steps.deploy.outputs.url }} "${{ env.VERCEL_ALIAS_DOMAIN }}" --token=$VERCEL_TOKEN
448
+ echo "Deploying with ${#ENV_FLAGS[@]} runtime env vars..."
449
+ DEPLOY_URL=$(vercel deploy --prebuilt $DEPLOY_PROD_FLAG "${ENV_FLAGS[@]}" --token="$VERCEL_TOKEN")
450
+ echo "url=$DEPLOY_URL" >> "$GITHUB_OUTPUT"
451
+ '
452
+ env:
453
+ DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
454
+
455
+ # main: changeset消費 + Release作成
456
+ release-production:
457
+ needs: deploy-production
458
+ if: always() && github.ref_name == 'main' && needs.deploy-production.result != 'failure'
459
+ runs-on: ubuntu-latest
460
+ steps:
461
+ - uses: actions/checkout@v4
462
+ with:
463
+ fetch-depth: 0
464
+
465
+ - name: Setup
466
+ uses: ./.github/actions/setup
467
+
468
+ - name: Check for changesets
469
+ id: check
470
+ run: |
471
+ COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
472
+ echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
473
+
474
+ - name: Version packages
475
+ if: steps.check.outputs.has_changesets == 'true'
476
+ run: npx changeset version
477
+
478
+ - name: Commit version bump and create Release
479
+ if: steps.check.outputs.has_changesets == 'true'
480
+ id: release
481
+ run: |
482
+ VERSION=$(node -p "require('./package.json').version")
483
+ TAG="v${VERSION}"
484
+ git config user.name "github-actions[bot]"
485
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
486
+ git add -A
487
+ git commit -m "chore: release ${TAG}" || echo "No changes"
488
+ git tag "$TAG"
489
+ git push origin main --follow-tags
490
+ PREV=$(git tag --list 'v*' --sort=-v:refname | grep -v 'rc' | grep -v "$TAG" | head -1)
491
+ OPTS="--target main --generate-notes"
492
+ [[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
493
+ gh release create "$TAG" --title "Release ${TAG}" $OPTS
494
+ env:
495
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -32,18 +32,8 @@
32
32
  ]
33
33
  },
34
34
  "serena": {
35
- "type": "stdio",
36
- "command": "uvx",
37
- "args": [
38
- "--from",
39
- "git+https://github.com/oraios/serena",
40
- "serena",
41
- "start-mcp-server",
42
- "--context",
43
- "claude-code",
44
- "--open-web-dashboard",
45
- "false"
46
- ]
35
+ "type": "http",
36
+ "url": "http://127.0.0.1:${SERENA_PORT:-9850}/mcp"
47
37
  },
48
38
  "github": {
49
39
  "type": "http",
@@ -107,3 +107,10 @@ fixed_tools: []
107
107
  # override of the corresponding setting in serena_config.yml, see the documentation there.
108
108
  # If null or missing, the value from the global config is used.
109
109
  symbol_info_budget:
110
+
111
+ # The language backend to use for this project.
112
+ # If not set, the global setting from serena_config.yml is used.
113
+ # Valid values: LSP, JetBrains
114
+ # Note: the backend is fixed at startup. If a project with a different backend
115
+ # is activated post-init, an error will be returned.
116
+ language_backend: