Skip to content

Commit c10c9ad

Browse files
committed
feat: add manual release workflow with controlled migrations
- Add release.yml for manual releases with test → migrate → deploy sequence - Migrations require GitHub Environment approval for safety - Simplify deploy-prod.yml to just build and push Docker image - Update documentation with release process instructions Release workflow options: - run_migrations: Run database migrations (requires approval) - deploy_functions: Deploy Supabase Edge Functions
1 parent 8e8de64 commit c10c9ad

File tree

3 files changed

+291
-259
lines changed

3 files changed

+291
-259
lines changed

.github/workflows/deploy-prod.yml

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
name: Deploy to Production
1+
name: Build Production Image
22

3+
# Auto-build on push to main (no deploy, just build)
34
on:
45
push:
56
branches: [main]
@@ -15,8 +16,6 @@ jobs:
1516
permissions:
1617
contents: read
1718
packages: write
18-
outputs:
19-
version: ${{ steps.version.outputs.VERSION }}
2019
steps:
2120
- uses: actions/checkout@v4
2221

@@ -27,85 +26,24 @@ jobs:
2726
- name: Set up Docker Buildx
2827
uses: docker/setup-buildx-action@v3
2928

30-
- name: Login to GitHub Container Registry
29+
- name: Login to GHCR
3130
uses: docker/login-action@v3
3231
with:
3332
registry: ${{ env.REGISTRY }}
3433
username: ${{ github.actor }}
3534
password: ${{ secrets.GITHUB_TOKEN }}
3635

37-
- name: Extract metadata
38-
id: meta
39-
uses: docker/metadata-action@v5
40-
with:
41-
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42-
tags: |
43-
type=raw,value=latest
44-
type=raw,value=${{ steps.version.outputs.VERSION }}
45-
type=sha,prefix=
46-
4736
- name: Build and push
4837
uses: docker/build-push-action@v5
4938
with:
5039
context: .
5140
push: true
52-
tags: ${{ steps.meta.outputs.tags }}
53-
labels: ${{ steps.meta.outputs.labels }}
41+
tags: |
42+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
43+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
44+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
5445
build-args: |
5546
VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL_PROD }}
5647
VITE_SUPABASE_PUBLISHABLE_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}
5748
cache-from: type=gha
5849
cache-to: type=gha,mode=max
59-
60-
deploy-edge-functions:
61-
name: Deploy Edge Functions
62-
runs-on: ubuntu-latest
63-
steps:
64-
- uses: actions/checkout@v4
65-
66-
- name: Setup Supabase CLI
67-
uses: supabase/setup-cli@v1
68-
with:
69-
version: latest
70-
71-
- name: Deploy functions
72-
run: supabase functions deploy --project-ref ${{ secrets.SUPABASE_PROJECT_REF_PROD }}
73-
env:
74-
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
75-
76-
deploy-to-hetzner:
77-
name: Deploy to Hetzner
78-
runs-on: ubuntu-latest
79-
needs: [build-and-push]
80-
steps:
81-
- name: Deploy via SSH
82-
uses: appleboy/[email protected]
83-
with:
84-
host: ${{ secrets.HETZNER_HOST }}
85-
username: ${{ secrets.HETZNER_USERNAME }}
86-
key: ${{ secrets.HETZNER_SSH_KEY }}
87-
script: |
88-
cd /opt/eryxon-flow
89-
docker compose pull
90-
docker compose up -d --remove-orphans
91-
docker image prune -f
92-
93-
create-release:
94-
name: Create Release
95-
runs-on: ubuntu-latest
96-
needs: [build-and-push, deploy-edge-functions, deploy-to-hetzner]
97-
permissions:
98-
contents: write
99-
steps:
100-
- uses: actions/checkout@v4
101-
102-
- name: Create Release
103-
uses: softprops/action-gh-release@v2
104-
with:
105-
tag_name: v${{ needs.build-and-push.outputs.version }}-${{ github.run_number }}
106-
generate_release_notes: true
107-
body: |
108-
## Docker Image
109-
```
110-
docker pull ghcr.io/${{ github.repository }}:${{ needs.build-and-push.outputs.version }}
111-
```

.github/workflows/release.yml

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
name: Release
2+
3+
# Manual trigger for controlled releases
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
run_migrations:
8+
description: 'Run database migrations'
9+
required: true
10+
default: 'false'
11+
type: boolean
12+
deploy_functions:
13+
description: 'Deploy Edge Functions'
14+
required: true
15+
default: 'true'
16+
type: boolean
17+
18+
env:
19+
REGISTRY: ghcr.io
20+
IMAGE_NAME: ${{ github.repository }}
21+
22+
jobs:
23+
# Step 1: Run all tests
24+
test:
25+
name: 1. Run Tests
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '20'
34+
cache: 'npm'
35+
36+
- name: Install dependencies
37+
run: npm ci
38+
39+
- name: Lint
40+
run: npm run lint
41+
42+
- name: Type check
43+
run: npx tsc --noEmit
44+
45+
- name: Build (verify)
46+
run: npm run build
47+
env:
48+
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL_PROD }}
49+
VITE_SUPABASE_PUBLISHABLE_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}
50+
51+
- name: Run tests
52+
run: npm run test --if-present
53+
54+
# Step 2: Run migrations (if enabled, requires approval)
55+
migrate:
56+
name: 2. Run Migrations
57+
runs-on: ubuntu-latest
58+
needs: test
59+
if: ${{ inputs.run_migrations == true }}
60+
environment: production # Requires manual approval
61+
steps:
62+
- uses: actions/checkout@v4
63+
64+
- name: Setup Supabase CLI
65+
uses: supabase/setup-cli@v1
66+
with:
67+
version: latest
68+
69+
- name: Link to production project
70+
run: supabase link --project-ref ${{ secrets.SUPABASE_PROJECT_REF_PROD }}
71+
env:
72+
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
73+
74+
- name: Run migrations
75+
run: supabase db push --linked
76+
env:
77+
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
78+
79+
# Step 3: Deploy Edge Functions (if enabled)
80+
deploy-functions:
81+
name: 3. Deploy Edge Functions
82+
runs-on: ubuntu-latest
83+
needs: [test, migrate]
84+
if: |
85+
always() &&
86+
needs.test.result == 'success' &&
87+
(needs.migrate.result == 'success' || needs.migrate.result == 'skipped') &&
88+
inputs.deploy_functions == true
89+
steps:
90+
- uses: actions/checkout@v4
91+
92+
- name: Setup Supabase CLI
93+
uses: supabase/setup-cli@v1
94+
with:
95+
version: latest
96+
97+
- name: Deploy Edge Functions
98+
run: supabase functions deploy --project-ref ${{ secrets.SUPABASE_PROJECT_REF_PROD }}
99+
env:
100+
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
101+
102+
# Step 4: Build and push Docker image
103+
build-and-push:
104+
name: 4. Build & Push Docker Image
105+
runs-on: ubuntu-latest
106+
needs: [test, migrate, deploy-functions]
107+
if: |
108+
always() &&
109+
needs.test.result == 'success' &&
110+
(needs.migrate.result == 'success' || needs.migrate.result == 'skipped') &&
111+
(needs.deploy-functions.result == 'success' || needs.deploy-functions.result == 'skipped')
112+
permissions:
113+
contents: read
114+
packages: write
115+
outputs:
116+
version: ${{ steps.version.outputs.VERSION }}
117+
steps:
118+
- uses: actions/checkout@v4
119+
120+
- name: Get version
121+
id: version
122+
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
123+
124+
- name: Set up Docker Buildx
125+
uses: docker/setup-buildx-action@v3
126+
127+
- name: Login to GHCR
128+
uses: docker/login-action@v3
129+
with:
130+
registry: ${{ env.REGISTRY }}
131+
username: ${{ github.actor }}
132+
password: ${{ secrets.GITHUB_TOKEN }}
133+
134+
- name: Build and push
135+
uses: docker/build-push-action@v5
136+
with:
137+
context: .
138+
push: true
139+
tags: |
140+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
141+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
142+
build-args: |
143+
VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL_PROD }}
144+
VITE_SUPABASE_PUBLISHABLE_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY_PROD }}
145+
cache-from: type=gha
146+
cache-to: type=gha,mode=max
147+
148+
# Step 5: Create GitHub release
149+
create-release:
150+
name: 5. Create Release
151+
runs-on: ubuntu-latest
152+
needs: build-and-push
153+
if: needs.build-and-push.result == 'success'
154+
permissions:
155+
contents: write
156+
steps:
157+
- uses: actions/checkout@v4
158+
159+
- name: Create Release
160+
uses: softprops/action-gh-release@v2
161+
with:
162+
tag_name: v${{ needs.build-and-push.outputs.version }}
163+
generate_release_notes: true
164+
body: |
165+
## Release v${{ needs.build-and-push.outputs.version }}
166+
167+
**What was deployed:**
168+
- Migrations: ${{ inputs.run_migrations }}
169+
- Edge Functions: ${{ inputs.deploy_functions }}
170+
171+
**Docker Image:**
172+
```
173+
docker pull ghcr.io/${{ github.repository }}:${{ needs.build-and-push.outputs.version }}
174+
```

0 commit comments

Comments
 (0)