create-einja-app 0.2.15 → 0.2.18

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 (272) hide show
  1. package/package.json +2 -2
  2. package/templates/default/.claude/hooks/einja/biome-format.sh +2 -2
  3. package/templates/default/.claude/hooks/einja/playwright-resize.sh +12 -2
  4. package/templates/default/.claude/settings.json +15 -0
  5. package/templates/default/.cursor/commands/task-vibe-kanban-loop.md +107 -42
  6. package/templates/default/.env.develop +0 -4
  7. package/templates/default/.env.example +1 -0
  8. package/templates/default/.env.preview +0 -4
  9. package/templates/default/.env.staging +19 -0
  10. package/templates/default/.github/actions/ci/action.yml +39 -0
  11. package/templates/default/.github/actions/migrate/action.yml +39 -0
  12. package/templates/default/.github/actions/neon-export-env/action.yml +28 -0
  13. package/templates/default/.github/actions/setup/action.yml +20 -0
  14. package/templates/default/.github/workflows/claude.yml +1 -0
  15. package/templates/default/.github/workflows/{cleanup-neon-branches.yml → cleanup-pr-preview-db.yml} +28 -24
  16. package/templates/default/.github/workflows/cleanup-pr-preview-on-close.yml +50 -0
  17. package/templates/default/.github/workflows/deploy-pr-preview.yml +398 -0
  18. package/templates/default/.github/workflows/deploy-stable-branches.yml +259 -0
  19. package/templates/default/.github/workflows/release-create-einja-app.yml +95 -0
  20. package/templates/default/.mcp.json +6 -9
  21. package/templates/default/.serena/project.yml +22 -1
  22. package/templates/default/CLAUDE.md +52 -10
  23. package/templates/default/README.md +5 -14
  24. package/templates/default/apps/admin/next.config.ts +11 -0
  25. package/templates/default/apps/admin/package.json +55 -0
  26. package/templates/default/apps/admin/postcss.config.cjs +5 -0
  27. package/templates/default/apps/admin/src/app/(auth)/forgot-password/page.tsx +97 -0
  28. package/templates/default/apps/admin/src/app/(auth)/layout.tsx +18 -0
  29. package/templates/default/apps/admin/src/app/(auth)/otp/page.tsx +121 -0
  30. package/templates/default/apps/admin/src/app/(auth)/sign-in/page.tsx +145 -0
  31. package/templates/default/apps/admin/src/app/(auth)/sign-up/page.tsx +199 -0
  32. package/templates/default/apps/admin/src/app/(errors)/401/page.tsx +27 -0
  33. package/templates/default/apps/admin/src/app/(errors)/403/page.tsx +28 -0
  34. package/templates/default/apps/admin/src/app/(errors)/500/page.tsx +29 -0
  35. package/templates/default/apps/admin/src/app/(errors)/layout.tsx +7 -0
  36. package/templates/default/apps/admin/src/app/(errors)/maintenance/page.tsx +25 -0
  37. package/templates/default/apps/admin/src/app/dashboard/_components/analytics-chart.tsx +68 -0
  38. package/templates/default/apps/admin/src/app/dashboard/_components/analytics.tsx +182 -0
  39. package/templates/default/apps/admin/src/app/dashboard/_components/dashboard-page.tsx +74 -0
  40. package/templates/default/apps/admin/src/app/dashboard/_components/metric-cards.tsx +49 -0
  41. package/templates/default/apps/admin/src/app/dashboard/_components/overview-chart.tsx +73 -0
  42. package/templates/default/apps/admin/src/app/dashboard/_components/recent-sales.tsx +75 -0
  43. package/templates/default/apps/admin/src/app/dashboard/apps/_components/apps-page.tsx +135 -0
  44. package/templates/default/apps/admin/src/app/dashboard/apps/page.tsx +10 -0
  45. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-list.tsx +82 -0
  46. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-messages.tsx +194 -0
  47. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chats-page.tsx +99 -0
  48. package/templates/default/apps/admin/src/app/dashboard/chats/_components/new-chat.tsx +118 -0
  49. package/templates/default/apps/admin/src/app/dashboard/chats/page.tsx +10 -0
  50. package/templates/default/apps/admin/src/app/dashboard/layout.tsx +9 -0
  51. package/templates/default/apps/admin/src/app/dashboard/not-found.tsx +14 -0
  52. package/templates/default/apps/admin/src/app/dashboard/page.tsx +10 -0
  53. package/templates/default/apps/admin/src/app/dashboard/settings/_components/content-section.tsx +20 -0
  54. package/templates/default/apps/admin/src/app/dashboard/settings/_components/sidebar-nav.tsx +66 -0
  55. package/templates/default/apps/admin/src/app/dashboard/settings/account/page.tsx +173 -0
  56. package/templates/default/apps/admin/src/app/dashboard/settings/appearance/page.tsx +156 -0
  57. package/templates/default/apps/admin/src/app/dashboard/settings/display/page.tsx +125 -0
  58. package/templates/default/apps/admin/src/app/dashboard/settings/layout.tsx +30 -0
  59. package/templates/default/apps/admin/src/app/dashboard/settings/notifications/page.tsx +196 -0
  60. package/templates/default/apps/admin/src/app/dashboard/settings/page.tsx +5 -0
  61. package/templates/default/apps/admin/src/app/dashboard/settings/profile/page.tsx +176 -0
  62. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-bulk-actions.tsx +183 -0
  63. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-row-actions.tsx +79 -0
  64. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-columns.tsx +107 -0
  65. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-dialogs.tsx +71 -0
  66. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-import-dialog.tsx +106 -0
  67. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-multi-delete-dialog.tsx +90 -0
  68. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-mutate-drawer.tsx +207 -0
  69. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-page.tsx +31 -0
  70. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-primary-buttons.tsx +19 -0
  71. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-provider.tsx +37 -0
  72. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-table.tsx +155 -0
  73. package/templates/default/apps/admin/src/app/dashboard/tasks/page.tsx +14 -0
  74. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-bulk-actions.tsx +136 -0
  75. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-row-actions.tsx +62 -0
  76. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-action-dialog.tsx +297 -0
  77. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-columns.tsx +121 -0
  78. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-delete-dialog.tsx +72 -0
  79. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-dialogs.tsx +49 -0
  80. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-invite-dialog.tsx +139 -0
  81. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-multi-delete-dialog.tsx +89 -0
  82. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-page.tsx +30 -0
  83. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-primary-buttons.tsx +19 -0
  84. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-provider.tsx +35 -0
  85. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-table.tsx +157 -0
  86. package/templates/default/apps/admin/src/app/dashboard/users/page.tsx +10 -0
  87. package/templates/default/apps/admin/src/app/globals.css +109 -0
  88. package/templates/default/apps/admin/src/app/layout.tsx +32 -0
  89. package/templates/default/apps/admin/src/app/not-found.tsx +14 -0
  90. package/templates/default/apps/admin/src/app/page.tsx +5 -0
  91. package/templates/default/apps/admin/src/components/layout/admin-layout.tsx +16 -0
  92. package/templates/default/apps/admin/src/components/layout/app-sidebar.tsx +52 -0
  93. package/templates/default/apps/admin/src/components/layout/nav-config.ts +131 -0
  94. package/templates/default/apps/admin/src/components/providers/theme-provider.tsx +10 -0
  95. package/templates/default/apps/admin/src/components/shared/long-text.tsx +78 -0
  96. package/templates/default/apps/admin/src/components/shared/search-input.tsx +16 -0
  97. package/templates/default/apps/admin/src/components/shared/select-dropdown.tsx +64 -0
  98. package/templates/default/apps/admin/src/data/apps.tsx +116 -0
  99. package/templates/default/apps/admin/src/data/chats.ts +114 -0
  100. package/templates/default/apps/admin/src/data/tasks.ts +114 -0
  101. package/templates/default/apps/admin/src/data/users.ts +90 -0
  102. package/templates/default/apps/admin/src/hooks/use-dialog-state.ts +17 -0
  103. package/templates/default/apps/admin/src/hooks/use-table-url-state.ts +243 -0
  104. package/templates/default/apps/admin/src/lib/show-submitted-data.tsx +12 -0
  105. package/templates/default/apps/admin/src/types/table.d.ts +9 -0
  106. package/templates/default/apps/admin/tsconfig.json +32 -0
  107. package/templates/default/apps/web/next.config.ts +1 -0
  108. package/templates/default/apps/web/package.json +1 -22
  109. package/templates/default/apps/web/postcss.config.cjs +0 -1
  110. package/templates/default/apps/web/src/app/(authenticated)/dashboard/page.tsx +4 -20
  111. package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +4 -4
  112. package/templates/default/apps/web/src/app/(authenticated)/data/page.tsx +1 -1
  113. package/templates/default/apps/web/src/app/(authenticated)/profile/page.tsx +1 -1
  114. package/templates/default/apps/web/src/app/error.tsx +8 -70
  115. package/templates/default/apps/web/src/app/global-error.tsx +8 -70
  116. package/templates/default/apps/web/src/app/globals.css +20 -0
  117. package/templates/default/apps/web/src/app/not-found.tsx +5 -39
  118. package/templates/default/apps/web/src/app/page.tsx +27 -203
  119. package/templates/default/apps/web/src/app/signin/page.tsx +27 -191
  120. package/templates/default/apps/web/src/app/signup/page.tsx +33 -240
  121. package/templates/default/apps/web/src/components/dashboard/dashboard-stats.tsx +11 -75
  122. package/templates/default/apps/web/src/components/shared/Sidebar.tsx +3 -3
  123. package/templates/default/apps/web/src/components/shared/header.tsx +17 -112
  124. package/templates/default/apps/web/tsconfig.json +0 -6
  125. package/templates/default/biome.json +30 -2
  126. package/templates/default/components.json +2 -2
  127. package/templates/default/docker-compose.yml +1 -1
  128. package/templates/default/gitignore +4 -0
  129. package/templates/default/package.json +2 -0
  130. package/templates/default/packages/admin-ui/catalog/catalog.css +54 -0
  131. package/templates/default/packages/admin-ui/catalog/catalog.tsx +401 -0
  132. package/templates/default/packages/admin-ui/catalog/index.html +12 -0
  133. package/templates/default/packages/admin-ui/catalog/main.tsx +9 -0
  134. package/templates/default/packages/admin-ui/components.json +21 -0
  135. package/templates/default/packages/admin-ui/package.json +105 -0
  136. package/templates/default/packages/admin-ui/src/command-menu/index.tsx +174 -0
  137. package/templates/default/packages/admin-ui/src/data-table/bulk-actions.tsx +215 -0
  138. package/templates/default/packages/admin-ui/src/data-table/column-header.tsx +73 -0
  139. package/templates/default/packages/admin-ui/src/data-table/data-table.tsx +127 -0
  140. package/templates/default/packages/admin-ui/src/data-table/faceted-filter.tsx +148 -0
  141. package/templates/default/packages/admin-ui/src/data-table/index.tsx +9 -0
  142. package/templates/default/packages/admin-ui/src/data-table/pagination.tsx +101 -0
  143. package/templates/default/packages/admin-ui/src/data-table/toolbar.tsx +87 -0
  144. package/templates/default/packages/admin-ui/src/data-table/view-options.tsx +57 -0
  145. package/templates/default/packages/admin-ui/src/hooks/use-mobile.tsx +23 -0
  146. package/templates/default/packages/admin-ui/src/layout/header.tsx +55 -0
  147. package/templates/default/packages/admin-ui/src/layout/index.ts +10 -0
  148. package/templates/default/packages/admin-ui/src/layout/main.tsx +23 -0
  149. package/templates/default/packages/admin-ui/src/layout/nav-group.tsx +111 -0
  150. package/templates/default/packages/admin-ui/src/layout/nav-user.tsx +114 -0
  151. package/templates/default/packages/admin-ui/src/layout/theme-switch.tsx +40 -0
  152. package/templates/default/packages/admin-ui/src/layout/types.ts +21 -0
  153. package/templates/default/packages/admin-ui/src/lib/utils.ts +6 -0
  154. package/templates/default/packages/admin-ui/src/styles/base.css +65 -0
  155. package/templates/default/packages/admin-ui/src/styles/tokens.css +91 -0
  156. package/templates/default/packages/admin-ui/src/tanstack-table.d.ts +10 -0
  157. package/templates/default/packages/admin-ui/src/ui/alert-dialog.tsx +157 -0
  158. package/templates/default/packages/admin-ui/src/ui/alert.tsx +66 -0
  159. package/templates/default/packages/admin-ui/src/ui/avatar.tsx +53 -0
  160. package/templates/default/packages/admin-ui/src/ui/badge.tsx +46 -0
  161. package/templates/default/packages/admin-ui/src/ui/breadcrumb.tsx +108 -0
  162. package/templates/default/packages/admin-ui/src/ui/button.tsx +59 -0
  163. package/templates/default/packages/admin-ui/src/ui/calendar.tsx +69 -0
  164. package/templates/default/packages/admin-ui/src/ui/card.tsx +92 -0
  165. package/templates/default/packages/admin-ui/src/ui/chart.tsx +345 -0
  166. package/templates/default/packages/admin-ui/src/ui/checkbox.tsx +32 -0
  167. package/templates/default/packages/admin-ui/src/ui/collapsible.tsx +27 -0
  168. package/templates/default/packages/admin-ui/src/ui/command.tsx +161 -0
  169. package/templates/default/packages/admin-ui/src/ui/confirm-dialog.tsx +72 -0
  170. package/templates/default/packages/admin-ui/src/ui/date-picker.tsx +53 -0
  171. package/templates/default/packages/admin-ui/src/ui/dialog.tsx +143 -0
  172. package/templates/default/packages/admin-ui/src/ui/dropdown-menu.tsx +257 -0
  173. package/templates/default/packages/admin-ui/src/ui/form.tsx +168 -0
  174. package/templates/default/packages/admin-ui/src/ui/input-otp.tsx +84 -0
  175. package/templates/default/packages/admin-ui/src/ui/input.tsx +21 -0
  176. package/templates/default/packages/admin-ui/src/ui/label.tsx +24 -0
  177. package/templates/default/packages/admin-ui/src/ui/pagination.tsx +126 -0
  178. package/templates/default/packages/admin-ui/src/ui/password-input.tsx +46 -0
  179. package/templates/default/packages/admin-ui/src/ui/popover.tsx +48 -0
  180. package/templates/default/packages/admin-ui/src/ui/progress.tsx +31 -0
  181. package/templates/default/packages/admin-ui/src/ui/radio-group.tsx +45 -0
  182. package/templates/default/packages/admin-ui/src/ui/scroll-area.tsx +52 -0
  183. package/templates/default/packages/admin-ui/src/ui/select.tsx +185 -0
  184. package/templates/default/packages/admin-ui/src/ui/separator.tsx +28 -0
  185. package/templates/default/packages/admin-ui/src/ui/sheet.tsx +149 -0
  186. package/templates/default/packages/admin-ui/src/ui/sidebar.tsx +728 -0
  187. package/templates/default/packages/admin-ui/src/ui/skeleton.tsx +13 -0
  188. package/templates/default/packages/admin-ui/src/ui/sonner.tsx +25 -0
  189. package/templates/default/packages/admin-ui/src/ui/switch.tsx +31 -0
  190. package/templates/default/packages/admin-ui/src/ui/table.tsx +116 -0
  191. package/templates/default/packages/admin-ui/src/ui/tabs.tsx +66 -0
  192. package/templates/default/packages/admin-ui/src/ui/textarea.tsx +18 -0
  193. package/templates/default/packages/admin-ui/src/ui/toggle-group.tsx +60 -0
  194. package/templates/default/packages/admin-ui/src/ui/toggle.tsx +44 -0
  195. package/templates/default/packages/admin-ui/src/ui/tooltip.tsx +61 -0
  196. package/templates/default/packages/admin-ui/tsconfig.json +8 -0
  197. package/templates/default/packages/admin-ui/vite.config.ts +11 -0
  198. package/templates/default/packages/config/package.json +0 -2
  199. package/templates/default/packages/server-core/package.json +2 -0
  200. package/templates/default/packages/ui/components.json +21 -0
  201. package/templates/default/packages/ui/package.json +42 -5
  202. package/templates/default/packages/ui/src/accordion.tsx +1 -1
  203. package/templates/default/packages/ui/src/alert-dialog.tsx +4 -4
  204. package/templates/default/packages/ui/src/alert.tsx +1 -1
  205. package/templates/default/packages/ui/src/avatar.tsx +1 -1
  206. package/templates/default/packages/ui/src/badge.tsx +1 -1
  207. package/templates/default/packages/ui/src/breadcrumb.tsx +1 -1
  208. package/templates/default/packages/ui/src/button.tsx +1 -1
  209. package/templates/default/packages/ui/src/card.tsx +1 -1
  210. package/templates/default/packages/ui/src/checkbox.tsx +1 -1
  211. package/templates/default/packages/ui/src/dialog.tsx +3 -3
  212. package/templates/default/packages/ui/src/drawer.tsx +3 -3
  213. package/templates/default/packages/ui/src/dropdown-menu.tsx +3 -3
  214. package/templates/default/packages/ui/src/form.tsx +2 -2
  215. package/templates/default/packages/ui/src/hover-card.tsx +2 -2
  216. package/templates/default/packages/ui/src/input.tsx +1 -1
  217. package/templates/default/packages/ui/src/label.tsx +1 -1
  218. package/templates/default/packages/ui/src/pagination.tsx +2 -2
  219. package/templates/default/packages/ui/src/popover.tsx +2 -2
  220. package/templates/default/packages/ui/src/progress.tsx +1 -1
  221. package/templates/default/packages/ui/src/select.tsx +2 -2
  222. package/templates/default/packages/ui/src/separator.tsx +1 -1
  223. package/templates/default/packages/ui/src/skeleton.tsx +1 -1
  224. package/templates/default/packages/ui/src/table.tsx +1 -1
  225. package/templates/default/packages/ui/src/tabs.tsx +1 -1
  226. package/templates/default/packages/ui/src/textarea.tsx +1 -1
  227. package/templates/default/packages/ui/src/tooltip.tsx +3 -3
  228. package/templates/default/packages/ui/src/typography.tsx +1 -1
  229. package/templates/default/packages/ui/tsconfig.json +1 -6
  230. package/templates/default/pnpm-lock.yaml +1319 -936
  231. package/templates/default/postcss.config.cjs +0 -1
  232. package/templates/default/turbo.json +17 -8
  233. package/templates/default/worktree.config.json +5 -0
  234. package/templates/default/.env.ci +0 -32
  235. package/templates/default/.github/workflows/ci.yml +0 -99
  236. package/templates/default/.github/workflows/preview-db.yml +0 -134
  237. package/templates/default/.playwright-mcp/dashboard.png +0 -0
  238. package/templates/default/.playwright-mcp/web-home.png +0 -0
  239. package/templates/default/apps/web/panda.config.ts +0 -114
  240. package/templates/default/apps/web/src/components/ui/accordion.tsx +0 -64
  241. package/templates/default/apps/web/src/components/ui/alert-dialog.tsx +0 -135
  242. package/templates/default/apps/web/src/components/ui/alert.tsx +0 -60
  243. package/templates/default/apps/web/src/components/ui/aspect-ratio.tsx +0 -9
  244. package/templates/default/apps/web/src/components/ui/avatar.tsx +0 -41
  245. package/templates/default/apps/web/src/components/ui/badge.tsx +0 -39
  246. package/templates/default/apps/web/src/components/ui/breadcrumb.tsx +0 -101
  247. package/templates/default/apps/web/src/components/ui/button.tsx +0 -56
  248. package/templates/default/apps/web/src/components/ui/card.tsx +0 -75
  249. package/templates/default/apps/web/src/components/ui/checkbox.tsx +0 -29
  250. package/templates/default/apps/web/src/components/ui/data-table.tsx +0 -189
  251. package/templates/default/apps/web/src/components/ui/dialog-hook.tsx +0 -210
  252. package/templates/default/apps/web/src/components/ui/dialog.tsx +0 -129
  253. package/templates/default/apps/web/src/components/ui/drawer.tsx +0 -124
  254. package/templates/default/apps/web/src/components/ui/dropdown-menu.tsx +0 -228
  255. package/templates/default/apps/web/src/components/ui/form.tsx +0 -152
  256. package/templates/default/apps/web/src/components/ui/hover-card.tsx +0 -38
  257. package/templates/default/apps/web/src/components/ui/input.tsx +0 -21
  258. package/templates/default/apps/web/src/components/ui/label.tsx +0 -21
  259. package/templates/default/apps/web/src/components/ui/pagination.tsx +0 -105
  260. package/templates/default/apps/web/src/components/ui/popover.tsx +0 -42
  261. package/templates/default/apps/web/src/components/ui/progress.tsx +0 -28
  262. package/templates/default/apps/web/src/components/ui/select.tsx +0 -170
  263. package/templates/default/apps/web/src/components/ui/separator.tsx +0 -28
  264. package/templates/default/apps/web/src/components/ui/skeleton.tsx +0 -13
  265. package/templates/default/apps/web/src/components/ui/sonner.tsx +0 -25
  266. package/templates/default/apps/web/src/components/ui/table.tsx +0 -92
  267. package/templates/default/apps/web/src/components/ui/tabs.tsx +0 -54
  268. package/templates/default/apps/web/src/components/ui/textarea.tsx +0 -18
  269. package/templates/default/apps/web/src/components/ui/tooltip.tsx +0 -57
  270. package/templates/default/apps/web/src/components/ui/typography.tsx +0 -158
  271. package/templates/default/packages/config/panda.config.ts +0 -114
  272. package/templates/default/panda.config.ts +0 -114
@@ -0,0 +1,398 @@
1
+ name: PR Preview
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened, synchronize]
6
+
7
+ permissions:
8
+ contents: read
9
+ pull-requests: write
10
+
11
+ concurrency:
12
+ group: pr-preview-${{ github.event.number }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ # CI チェック(PostgreSQLサービスコンテナ付き)
17
+ ci:
18
+ if: github.event.pull_request.head.repo.full_name == github.repository
19
+ runs-on: ubuntu-latest
20
+ services:
21
+ postgres:
22
+ image: postgres:15-alpine
23
+ env:
24
+ POSTGRES_DB: test_db
25
+ POSTGRES_USER: postgres
26
+ POSTGRES_PASSWORD: postgres
27
+ ports:
28
+ - 5432:5432
29
+ options: >-
30
+ --health-cmd pg_isready
31
+ --health-interval 10s
32
+ --health-timeout 5s
33
+ --health-retries 5
34
+ env:
35
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
36
+ TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: ./.github/actions/ci
40
+ with:
41
+ database-url: postgresql://postgres:postgres@localhost:5432/test_db
42
+
43
+ # Neon Branch作成 + スキーマ同期 + シード
44
+ neon-and-schema:
45
+ needs: ci
46
+ runs-on: ubuntu-latest
47
+ outputs:
48
+ branch_id: ${{ steps.create-branch.outputs.branch_id }}
49
+ neon_parent: ${{ steps.neon-parent.outputs.parent }}
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - uses: ./.github/actions/setup
53
+
54
+ # 0. .env.previewからNeon環境変数をエクスポート
55
+ - name: Export Neon environment variables
56
+ uses: ./.github/actions/neon-export-env
57
+ with:
58
+ dotenv-private-key-preview: ${{ secrets.DOTENV_PRIVATE_KEY_PREVIEW }}
59
+
60
+ - name: Generate artifacts
61
+ run: pnpm generate
62
+
63
+ # 1. Neon親ブランチの動的選択(フォールバック付き)
64
+ - name: Determine Neon parent branch
65
+ id: neon-parent
66
+ run: |
67
+ BASE_REF="${{ github.event.pull_request.base.ref }}"
68
+
69
+ # Gitブランチ名 → Neonブランチ名のマッピング
70
+ case "$BASE_REF" in
71
+ main) NEON_BRANCH="production" ;;
72
+ develop) NEON_BRANCH="development" ;;
73
+ *) NEON_BRANCH="$BASE_REF" ;;
74
+ esac
75
+
76
+ echo "Git base branch: $BASE_REF → Neon branch: $NEON_BRANCH"
77
+
78
+ # Neon APIでブランチの存在を確認
79
+ HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
80
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches?search=$NEON_BRANCH" \
81
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
82
+ -H "Accept: application/json")
83
+
84
+ if [ "$HTTP_STATUS" = "200" ]; then
85
+ # ブランチ一覧から完全一致を確認
86
+ BRANCH_EXISTS=$(curl -s \
87
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches?search=$NEON_BRANCH" \
88
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
89
+ -H "Accept: application/json" | jq -r ".branches[] | select(.name == \"$NEON_BRANCH\") | .name")
90
+
91
+ if [ -n "$BRANCH_EXISTS" ]; then
92
+ echo "parent=$NEON_BRANCH" >> $GITHUB_OUTPUT
93
+ echo "Neon parent branch: $NEON_BRANCH"
94
+ else
95
+ echo "parent=production" >> $GITHUB_OUTPUT
96
+ echo "Neon parent branch: production (fallback - '$NEON_BRANCH' not found in Neon)"
97
+ fi
98
+ else
99
+ echo "parent=production" >> $GITHUB_OUTPUT
100
+ echo "Neon parent branch: production (fallback - API returned $HTTP_STATUS)"
101
+ fi
102
+
103
+ # 2. Neon Branch 作成(v6 + expires_at)
104
+ - name: Create Neon Branch
105
+ id: create-branch
106
+ uses: neondatabase/create-branch-action@v6
107
+ with:
108
+ project_id: ${{ env.NEON_PROJECT_ID }}
109
+ branch_name: preview/pr-${{ github.event.number }}
110
+ api_key: ${{ env.NEON_API_KEY }}
111
+ parent_branch: ${{ steps.neon-parent.outputs.parent }}
112
+ # 1日間アクセスがなければサスペンド(コスト削減)
113
+ suspend_timeout: 86400
114
+
115
+ # 2.5. DB URLの解決(connection_uri API方式)
116
+ - name: Resolve DB URLs
117
+ id: resolve-db
118
+ run: |
119
+ DB_URL="${{ steps.create-branch.outputs.db_url }}"
120
+ DB_URL_POOLED="${{ steps.create-branch.outputs.db_url_pooled }}"
121
+
122
+ if [ -z "$DB_URL" ] || [ -z "$DB_URL_POOLED" ]; then
123
+ echo "::notice::Branch was reused — fetching DB URLs from Neon API..."
124
+ BRANCH_ID="${{ steps.create-branch.outputs.branch_id }}"
125
+
126
+ # connection_uri APIで直接URL取得(sed不要)
127
+ DB_URL=$(curl -s \
128
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/connection_uri?branch_id=$BRANCH_ID&pooled=false&database_name=neondb" \
129
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
130
+ -H "Accept: application/json" | jq -r '.uri')
131
+
132
+ DB_URL_POOLED=$(curl -s \
133
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/connection_uri?branch_id=$BRANCH_ID&pooled=true&database_name=neondb" \
134
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
135
+ -H "Accept: application/json" | jq -r '.uri')
136
+
137
+ if [ -z "$DB_URL" ] || [ "$DB_URL" = "null" ] || [ -z "$DB_URL_POOLED" ] || [ "$DB_URL_POOLED" = "null" ]; then
138
+ echo "::error::Failed to resolve DB URLs from Neon API"
139
+ exit 1
140
+ fi
141
+ fi
142
+
143
+ # ログへの漏洩を防止
144
+ echo "::add-mask::$DB_URL"
145
+ echo "::add-mask::$DB_URL_POOLED"
146
+
147
+ # heredoc形式でGITHUB_OUTPUTに書き込み(特殊文字対応)
148
+ {
149
+ echo "db_url<<NEON_EOF"
150
+ echo "$DB_URL"
151
+ echo "NEON_EOF"
152
+ echo "db_url_pooled<<NEON_EOF"
153
+ echo "$DB_URL_POOLED"
154
+ echo "NEON_EOF"
155
+ } >> "$GITHUB_OUTPUT"
156
+
157
+ # 3. スキーマ同期 + シード(直接接続)
158
+ - name: Push Schema
159
+ run: pnpm db:push -- --accept-data-loss
160
+ env:
161
+ DATABASE_URL: ${{ steps.resolve-db.outputs.db_url }}
162
+
163
+ - name: Run Seed
164
+ run: pnpm --filter @repo/server-core db:seed
165
+ env:
166
+ DATABASE_URL: ${{ steps.resolve-db.outputs.db_url }}
167
+
168
+ # Web/Adminデプロイ(並列実行)
169
+ deploy:
170
+ needs: neon-and-schema
171
+ runs-on: ubuntu-latest
172
+ strategy:
173
+ fail-fast: false
174
+ matrix:
175
+ app: [web, admin]
176
+ env:
177
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
178
+ VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
179
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
180
+ TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
181
+ steps:
182
+ - uses: actions/checkout@v4
183
+ - uses: ./.github/actions/setup
184
+
185
+ # Neon環境変数のエクスポート(DB URL取得に必要)
186
+ - name: Export Neon environment variables
187
+ uses: ./.github/actions/neon-export-env
188
+ with:
189
+ dotenv-private-key-preview: ${{ secrets.DOTENV_PRIVATE_KEY_PREVIEW }}
190
+
191
+ # Neon APIからDB URLを取得(connection_uri API方式)
192
+ - name: Fetch DB URLs from Neon API
193
+ id: db-urls
194
+ run: |
195
+ BRANCH_ID="${{ needs.neon-and-schema.outputs.branch_id }}"
196
+
197
+ # connection_uri APIで直接URL取得
198
+ DB_URL=$(curl -s \
199
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/connection_uri?branch_id=$BRANCH_ID&pooled=false&database_name=neondb" \
200
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
201
+ -H "Accept: application/json" | jq -r '.uri')
202
+
203
+ DB_URL_POOLED=$(curl -s \
204
+ "https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/connection_uri?branch_id=$BRANCH_ID&pooled=true&database_name=neondb" \
205
+ -H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
206
+ -H "Accept: application/json" | jq -r '.uri')
207
+
208
+ if [ -z "$DB_URL" ] || [ "$DB_URL" = "null" ] || [ -z "$DB_URL_POOLED" ] || [ "$DB_URL_POOLED" = "null" ]; then
209
+ echo "::error::Failed to resolve DB URLs from Neon API"
210
+ exit 1
211
+ fi
212
+
213
+ echo "::add-mask::$DB_URL"
214
+ echo "::add-mask::$DB_URL_POOLED"
215
+
216
+ {
217
+ echo "db_url<<NEON_EOF"
218
+ echo "$DB_URL"
219
+ echo "NEON_EOF"
220
+ echo "db_url_pooled<<NEON_EOF"
221
+ echo "$DB_URL_POOLED"
222
+ echo "NEON_EOF"
223
+ } >> "$GITHUB_OUTPUT"
224
+
225
+ - name: Validate DATABASE_URL
226
+ run: |
227
+ if [ -z "${{ steps.db-urls.outputs.db_url_pooled }}" ]; then
228
+ echo "::error::DATABASE_URL is empty. Neon API may have failed."
229
+ exit 1
230
+ fi
231
+
232
+ - name: Resolve Vercel Project ID
233
+ id: resolve
234
+ run: |
235
+ case "${{ matrix.app }}" in
236
+ web) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_WEB }}" >> $GITHUB_OUTPUT ;;
237
+ admin) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_ADMIN }}" >> $GITHUB_OUTPUT ;;
238
+ esac
239
+
240
+ - name: Set VERCEL_PROJECT_ID
241
+ run: echo "VERCEL_PROJECT_ID=${{ steps.resolve.outputs.project_id }}" >> $GITHUB_ENV
242
+
243
+ - name: Install Vercel CLI
244
+ run: pnpm add -g vercel@latest
245
+
246
+ - name: Pull Vercel Environment (apps/${{ matrix.app }})
247
+ run: vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
248
+
249
+ - name: Get Vercel project name
250
+ id: project-name
251
+ run: |
252
+ PROJECT_NAME=$(curl -s \
253
+ "https://api.vercel.com/v9/projects/${{ steps.resolve.outputs.project_id }}" \
254
+ -H "Authorization: Bearer $VERCEL_TOKEN" | jq -r '.name')
255
+ if [ -z "$PROJECT_NAME" ] || [ "$PROJECT_NAME" = "null" ]; then
256
+ echo "::warning::プロジェクト名の取得に失敗しました。aliasは設定されません。"
257
+ echo "name=" >> $GITHUB_OUTPUT
258
+ else
259
+ # DNS安全のため小文字に正規化
260
+ PROJECT_NAME=$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]')
261
+ echo "name=$PROJECT_NAME" >> $GITHUB_OUTPUT
262
+ fi
263
+
264
+ # Vercel環境変数の自動同期(encrypted-only方式:dotenvxで管理しているもの = Vercelに同期すべきもの)
265
+ - name: Sync environment variables to Vercel
266
+ run: |
267
+ # .env.previewから encrypted: を含むキー名を抽出
268
+ # NEON_*とDATABASE_*を除外(Neon APIから動的取得・--envで注入するため)
269
+ ENCRYPTED_KEYS=$(grep 'encrypted:' $GITHUB_WORKSPACE/.env.preview | sed 's/=.*//' | grep -vE '^(NEON_|DATABASE_)')
270
+
271
+ npx dotenvx run -f $GITHUB_WORKSPACE/.env.preview -- bash -c "
272
+ for key in $ENCRYPTED_KEYS; do
273
+ value=\$(printenv \"\$key\" 2>/dev/null || true)
274
+ if [ -n \"\$value\" ]; then
275
+ echo \"Syncing \$key to Vercel (preview)...\"
276
+ echo \"\$value\" | vercel env rm \"\$key\" preview --yes --token=\$VERCEL_TOKEN 2>/dev/null || true
277
+ echo \"\$value\" | vercel env add \"\$key\" preview --token=\$VERCEL_TOKEN
278
+ fi
279
+ done
280
+ "
281
+ env:
282
+ DOTENV_PRIVATE_KEY_PREVIEW: ${{ secrets.DOTENV_PRIVATE_KEY_PREVIEW }}
283
+
284
+ # NOTE: dotenvxはデフォルトで既存の環境変数を上書きしない
285
+ # env:で設定したDATABASE_URLがNeonブランチのURLとして優先される
286
+ - name: Build Project (apps/${{ matrix.app }})
287
+ run: |
288
+ npx dotenvx run -f $GITHUB_WORKSPACE/.env.preview -- \
289
+ vercel build --token=$VERCEL_TOKEN
290
+ env:
291
+ DOTENV_PRIVATE_KEY_PREVIEW: ${{ secrets.DOTENV_PRIVATE_KEY_PREVIEW }}
292
+ DATABASE_URL: ${{ steps.db-urls.outputs.db_url_pooled }}
293
+
294
+ # Vercel Deploy(ランタイムにもDATABASE_URLを渡す)
295
+ - name: Deploy to Vercel
296
+ id: deploy
297
+ run: |
298
+ DEPLOY_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN \
299
+ --env DATABASE_URL="${{ steps.db-urls.outputs.db_url_pooled }}")
300
+ echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT
301
+
302
+ - name: Set Vercel alias
303
+ if: steps.project-name.outputs.name != ''
304
+ id: set-alias
305
+ continue-on-error: true
306
+ run: |
307
+ ALIAS="${{ steps.project-name.outputs.name }}-pr-${{ github.event.number }}.vercel.app"
308
+ vercel alias ${{ steps.deploy.outputs.url }} "$ALIAS" --token=$VERCEL_TOKEN
309
+ echo "url=https://$ALIAS" >> $GITHUB_OUTPUT
310
+
311
+ # デプロイURLをartifactとして保存
312
+ - name: Save deploy URL
313
+ run: |
314
+ ALIAS_URL="${{ steps.set-alias.outputs.url }}"
315
+ DEPLOY_URL="${{ steps.deploy.outputs.url }}"
316
+ if [ -n "$ALIAS_URL" ]; then
317
+ echo "$ALIAS_URL" > deploy-url-${{ matrix.app }}.txt
318
+ else
319
+ echo "::warning::Alias未設定のためランダムURLを使用: $DEPLOY_URL"
320
+ echo "$DEPLOY_URL" > deploy-url-${{ matrix.app }}.txt
321
+ fi
322
+
323
+ - name: Upload deploy URL
324
+ uses: actions/upload-artifact@v4
325
+ with:
326
+ name: deploy-url-${{ matrix.app }}
327
+ path: deploy-url-${{ matrix.app }}.txt
328
+ retention-days: 1
329
+
330
+ # PRコメント
331
+ comment:
332
+ needs: [neon-and-schema, deploy]
333
+ if: always() && needs.neon-and-schema.result == 'success'
334
+ runs-on: ubuntu-latest
335
+ steps:
336
+ - name: Download deploy URLs
337
+ uses: actions/download-artifact@v4
338
+ with:
339
+ pattern: deploy-url-*
340
+ merge-multiple: true
341
+ continue-on-error: true
342
+
343
+ - name: Read deploy URLs
344
+ id: urls
345
+ run: |
346
+ if [ -f deploy-url-web.txt ]; then
347
+ echo "web=$(cat deploy-url-web.txt)" >> $GITHUB_OUTPUT
348
+ else
349
+ echo "web=Deploy failed" >> $GITHUB_OUTPUT
350
+ fi
351
+ if [ -f deploy-url-admin.txt ]; then
352
+ echo "admin=$(cat deploy-url-admin.txt)" >> $GITHUB_OUTPUT
353
+ else
354
+ echo "admin=Deploy failed" >> $GITHUB_OUTPUT
355
+ fi
356
+
357
+ - name: Comment on PR
358
+ uses: actions/github-script@v7
359
+ with:
360
+ script: |
361
+ const marker = '<!-- neon-preview-url -->';
362
+ const body = `${marker}
363
+ ## 🚀 Preview Deployment
364
+
365
+ | Item | Value |
366
+ |------|-------|
367
+ | **Web Preview URL** | ${{ steps.urls.outputs.web }} |
368
+ | **Admin Preview URL** | ${{ steps.urls.outputs.admin }} |
369
+ | **Neon Branch** | \`preview/pr-${{ github.event.number }}\` |
370
+ | **Neon Parent** | \`${{ needs.neon-and-schema.outputs.neon_parent }}\` |
371
+ | **Commit** | ${{ github.event.pull_request.head.sha }} |
372
+ `;
373
+
374
+ const { data: comments } = await github.rest.issues.listComments({
375
+ owner: context.repo.owner,
376
+ repo: context.repo.repo,
377
+ issue_number: context.issue.number,
378
+ });
379
+
380
+ const existing = comments.find(c =>
381
+ c.user.type === 'Bot' && c.body.includes(marker)
382
+ );
383
+
384
+ if (existing) {
385
+ await github.rest.issues.updateComment({
386
+ owner: context.repo.owner,
387
+ repo: context.repo.repo,
388
+ comment_id: existing.id,
389
+ body,
390
+ });
391
+ } else {
392
+ await github.rest.issues.createComment({
393
+ owner: context.repo.owner,
394
+ repo: context.repo.repo,
395
+ issue_number: context.issue.number,
396
+ body,
397
+ });
398
+ }
@@ -0,0 +1,259 @@
1
+ name: Deploy Stable Branches
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop, staging]
6
+
7
+ concurrency:
8
+ group: deploy-${{ github.ref_name }}
9
+ cancel-in-progress: false
10
+
11
+ jobs:
12
+ ci:
13
+ runs-on: ubuntu-latest
14
+ services:
15
+ postgres:
16
+ image: postgres:15-alpine
17
+ env:
18
+ POSTGRES_DB: test_db
19
+ POSTGRES_USER: postgres
20
+ POSTGRES_PASSWORD: postgres
21
+ ports:
22
+ - 5432:5432
23
+ options: >-
24
+ --health-cmd pg_isready
25
+ --health-interval 10s
26
+ --health-timeout 5s
27
+ --health-retries 5
28
+ env:
29
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
30
+ TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+
34
+ - name: Run CI checks
35
+ uses: ./.github/actions/ci
36
+ with:
37
+ database-url: postgresql://postgres:postgres@localhost:5432/test_db
38
+
39
+ # 変更検知
40
+ changes:
41
+ runs-on: ubuntu-latest
42
+ outputs:
43
+ web: ${{ steps.filter.outputs.web }}
44
+ admin: ${{ steps.filter.outputs.admin }}
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ with:
48
+ fetch-depth: 0
49
+
50
+ - uses: dorny/paths-filter@v3
51
+ id: filter
52
+ with:
53
+ filters: |
54
+ web:
55
+ - 'apps/web/**'
56
+ - 'packages/ui/**'
57
+ - 'packages/front-core/**'
58
+ - 'packages/server-core/**'
59
+ - 'packages/config/**'
60
+ - 'package.json'
61
+ - 'pnpm-lock.yaml'
62
+ - 'turbo.json'
63
+ - 'tsconfig.json'
64
+ - '.env.*'
65
+ admin:
66
+ - 'apps/admin/**'
67
+ - 'packages/admin-ui/**'
68
+ - 'packages/front-core/**'
69
+ - 'packages/server-core/**'
70
+ - 'packages/config/**'
71
+ - 'package.json'
72
+ - 'pnpm-lock.yaml'
73
+ - 'turbo.json'
74
+ - 'tsconfig.json'
75
+ - '.env.*'
76
+
77
+ migrate:
78
+ needs: ci
79
+ if: github.ref_name == 'main' || github.ref_name == 'staging'
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - uses: actions/checkout@v4
83
+
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'
92
+
93
+ # staging: ステージング環境マイグレーション(シード無効)
94
+ - name: Migrate staging
95
+ if: github.ref_name == 'staging'
96
+ uses: ./.github/actions/migrate
97
+ with:
98
+ dotenv-file: ${{ github.workspace }}/.env.staging
99
+ dotenv-private-key: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
100
+ run-seed: 'false'
101
+
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')
106
+ runs-on: ubuntu-latest
107
+ strategy:
108
+ fail-fast: false
109
+ matrix:
110
+ app: [web, admin]
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 }}
116
+ 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
+ - uses: actions/checkout@v4
134
+ if: steps.check.outputs.should_deploy == 'true'
135
+
136
+ - name: Setup Environment
137
+ if: steps.check.outputs.should_deploy == 'true'
138
+ uses: ./.github/actions/setup
139
+
140
+ - name: Resolve Vercel Project ID
141
+ if: steps.check.outputs.should_deploy == 'true'
142
+ id: resolve
143
+ run: |
144
+ case "${{ matrix.app }}" in
145
+ web) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_WEB }}" >> $GITHUB_OUTPUT ;;
146
+ admin) echo "project_id=${{ secrets.VERCEL_PROJECT_ID_ADMIN }}" >> $GITHUB_OUTPUT ;;
147
+ esac
148
+
149
+ - name: Set VERCEL_PROJECT_ID
150
+ if: steps.check.outputs.should_deploy == 'true'
151
+ run: echo "VERCEL_PROJECT_ID=${{ steps.resolve.outputs.project_id }}" >> $GITHUB_ENV
152
+
153
+ - name: Install Vercel CLI
154
+ if: steps.check.outputs.should_deploy == 'true'
155
+ run: pnpm add -g vercel@latest
156
+
157
+ # ブランチ判定 — 環境変数を $GITHUB_ENV に書き出す
158
+ - name: Determine environment
159
+ if: steps.check.outputs.should_deploy == 'true'
160
+ 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)"
183
+
184
+ # Extract alias domain(自アプリ分のみ)
185
+ - name: Extract alias domain
186
+ if: steps.check.outputs.should_deploy == 'true' && env.DEPLOY_RUN_ALIAS == 'true'
187
+ run: |
188
+ case "${{ matrix.app }}" in
189
+ web)
190
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
191
+ echo "VERCEL_ALIAS_DOMAIN=$VERCEL_ALIAS_DOMAIN_WEB" >> "$GITHUB_ENV"
192
+ '
193
+ ;;
194
+ admin)
195
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
196
+ echo "VERCEL_ALIAS_DOMAIN=$VERCEL_ALIAS_DOMAIN_ADMIN" >> "$GITHUB_ENV"
197
+ '
198
+ ;;
199
+ esac
200
+ env:
201
+ DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
202
+ DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
203
+
204
+ # Vercel pull
205
+ - name: Pull Vercel Environment
206
+ if: steps.check.outputs.should_deploy == 'true'
207
+ run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
208
+
209
+ # Vercel環境変数の自動同期(encrypted-only方式:dotenvxで管理しているもの = Vercelに同期すべきもの)
210
+ - name: Sync environment variables to Vercel
211
+ if: steps.check.outputs.should_deploy == 'true'
212
+ 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
223
+ fi
224
+ done
225
+ "
226
+ env:
227
+ 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
+
231
+ # Re-pull after sync
232
+ - name: Re-pull Vercel Environment
233
+ if: steps.check.outputs.should_deploy == 'true'
234
+ run: vercel pull --yes --environment=${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
235
+
236
+ # Vercel build (dotenvxでアプリ環境変数を注入)
237
+ - name: Build Project
238
+ if: steps.check.outputs.should_deploy == 'true'
239
+ run: |
240
+ npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- \
241
+ vercel build ${{ env.DEPLOY_PROD_FLAG }} --token=$VERCEL_TOKEN
242
+ env:
243
+ 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
+
247
+ # Vercel deploy
248
+ - name: Deploy to Vercel
249
+ if: steps.check.outputs.should_deploy == 'true'
250
+ id: deploy
251
+ 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"
255
+
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