Skip to content

Commit 6757a85

Browse files
author
Pier Dolique
committed
refactor(ci): extract reusable workflow for deployment
- ♻️ Create reusable-deploy.yml with parameterized inputs - 🔧 Add working-directory support for monorepo compatibility - 🔐 Move Cloudflare credentials to workflow secrets - ✨ Support customizable build and deploy commands - 🎯 Enable optional typecheck job via input parameter - 🚀 Simplify build-and-deploy.yml to use reusable workflow - 📦 Use defaults.run.working-directory for cleaner syntax
1 parent 891d3e2 commit 6757a85

2 files changed

Lines changed: 218 additions & 149 deletions

File tree

Lines changed: 5 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
name: Build and Deploy
2-
description: Build the project and deploy to the appropriate environment.
32

43
permissions:
54
contents: read
@@ -16,156 +15,13 @@ on:
1615
branches:
1716
- master
1817

19-
env:
20-
ARTIFACT_NAME: deployment-artifact
21-
ARTIFACT_PATH: .output
22-
2318
concurrency:
2419
group: build-and-deploy-${{ github.ref }}
2520
cancel-in-progress: true
2621

2722
jobs:
28-
# Check Typescript types correctness
29-
test-typecheck:
30-
name: Typecheck
31-
runs-on: ubuntu-24.04
32-
steps:
33-
- name: Checkout repository
34-
uses: actions/checkout@v5
35-
- uses: ./.github/actions/setup-pnpm
36-
with:
37-
install-dependencies: true
38-
- name: Run typecheck
39-
run: |
40-
pnpm test:typecheck
41-
42-
# Build the project and upload artifact
43-
build:
44-
name: Build artifact
45-
runs-on: ubuntu-24.04
46-
steps:
47-
- name: Checkout repository
48-
uses: actions/checkout@v5
49-
- uses: ./.github/actions/setup-pnpm
50-
with:
51-
install-dependencies: true
52-
- name: Build project
53-
run: |
54-
pnpm run build
55-
- name: Upload build artifact
56-
id: upload-artifact
57-
uses: actions/upload-artifact@v5
58-
with:
59-
include-hidden-files: true
60-
if-no-files-found: error
61-
name: ${{ env.ARTIFACT_NAME }}
62-
path: ${{ env.ARTIFACT_PATH }}
63-
64-
deploy-to-staging:
65-
if: github.event_name == 'pull_request'
66-
name: Deploy to staging
67-
runs-on: ubuntu-24.04
68-
permissions:
69-
pull-requests: write
70-
outputs:
71-
wrangler-log: ${{ steps.set-wrangler-log.outputs.wrangler-log }}
72-
needs:
73-
- build
74-
steps:
75-
# Checkout repository
76-
- name: Checkout repository
77-
uses: actions/checkout@v5
78-
79-
# Setup, and download artifact
80-
- uses: ./.github/actions/pre-deploy
81-
with:
82-
artifact-name: ${{ env.ARTIFACT_NAME }}
83-
unpack-path: ${{ env.ARTIFACT_PATH }}
84-
85-
# Set wrangler log file path for capturing output
86-
- name: Set wrangler output file path
87-
id: set-wrangler-log
88-
run: |
89-
WRANGLER_LOG=/tmp/wrangler-$(date '+%s%N').log
90-
echo "wrangler-log=$WRANGLER_LOG" >> $GITHUB_OUTPUT
91-
92-
# Deploy to Cloudflare Workers
93-
- name: Deploy version to staging
94-
id: deploy-version
95-
continue-on-error: true
96-
env:
97-
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
98-
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
99-
run: |
100-
export WRANGLER_OUTPUT_FILE_PATH=${{ steps.set-wrangler-log.outputs.wrangler-log }}
101-
pnpm run deploy:versions:staging
102-
103-
# Extract deployment URL from wrangler log
104-
- name: Get deployment URL
105-
id: get-deployment-url
106-
continue-on-error: true
107-
run: |
108-
PREVIEW_URL=$(jq --raw-output --slurp 'first(.[] | select(.preview_url) | .preview_url)' ${{ steps.set-wrangler-log.outputs.wrangler-log }})
109-
110-
if [ -z "$PREVIEW_URL" ]; then
111-
echo "Error: No preview_url found in wrangler log"
112-
exit 1
113-
fi
114-
115-
echo "preview-url=$PREVIEW_URL" >> $GITHUB_OUTPUT
116-
117-
# Comment on the pull request with success status
118-
- name: Comment successful deployment URL
119-
if: ${{ steps.get-deployment-url.outcome == 'success' }}
120-
uses: marocchino/sticky-pull-request-comment@v2
121-
with:
122-
message: |
123-
## 🎉 Deployed to Cloudflare!
124-
125-
- Commit: `${{ github.sha }}`
126-
- Preview URL: ${{ steps.get-deployment-url.outputs.preview-url }}
127-
128-
# Comment on the pull request with failure status
129-
- name: Comment deployment failure
130-
if: ${{ steps.get-deployment-url.outcome == 'failure' }}
131-
uses: marocchino/sticky-pull-request-comment@v2
132-
with:
133-
message: |
134-
## ❌ Deployment to Cloudflare failed!
135-
136-
- Commit: `${{ github.sha }}`
137-
- Please check the [workflow logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details.
138-
139-
# Fail the workflow if deployment failed
140-
- name: Fail workflow if deployment failed
141-
if: ${{ steps.get-deployment-url.outcome == 'failure' }}
142-
run: |
143-
echo "Deployment to staging failed."
144-
exit 1
145-
146-
deploy-to-production:
147-
if: github.event_name == 'push'
148-
name: Deploy to production
149-
runs-on: ubuntu-24.04
150-
needs:
151-
- build
152-
- test-typecheck
153-
steps:
154-
# Checkout repository
155-
- name: Checkout repository
156-
uses: actions/checkout@v5
157-
158-
# Setup, and download artifact
159-
- uses: ./.github/actions/pre-deploy
160-
with:
161-
artifact-name: ${{ env.ARTIFACT_NAME }}
162-
unpack-path: ${{ env.ARTIFACT_PATH }}
163-
164-
# Deploy to Cloudflare Workers
165-
- name: Deploy to production
166-
id: deploy-production
167-
env:
168-
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
169-
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
170-
run: |
171-
pnpm run deploy:production
23+
deploy:
24+
uses: ./.github/workflows/reusable-deploy.yml
25+
secrets:
26+
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
27+
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
name: Reusable Deploy Workflow
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
workflow_call:
8+
inputs:
9+
working-directory:
10+
description: Working directory for the project
11+
required: false
12+
default: '.'
13+
type: string
14+
artifact-path:
15+
description: Path to the build output directory
16+
required: false
17+
default: '.output'
18+
type: string
19+
artifact-name:
20+
description: Name of the deployment artifact
21+
required: false
22+
default: 'deployment-artifact'
23+
type: string
24+
build-command:
25+
description: pnpm script name for building the project
26+
required: false
27+
default: 'build'
28+
type: string
29+
typecheck-command:
30+
description: pnpm script name for type checking (optional, skipped if empty)
31+
required: false
32+
default: 'test:typecheck'
33+
type: string
34+
deploy-staging-command:
35+
description: pnpm script name for deploying to staging
36+
required: false
37+
default: 'deploy:versions:staging'
38+
type: string
39+
deploy-production-command:
40+
description: pnpm script name for deploying to production
41+
required: false
42+
default: 'deploy:production'
43+
type: string
44+
secrets:
45+
cloudflare-account-id:
46+
description: Cloudflare account ID
47+
required: true
48+
cloudflare-api-token:
49+
description: Cloudflare API token
50+
required: true
51+
52+
concurrency:
53+
group: ${{ inputs.artifact-name }}-${{ github.ref }}
54+
cancel-in-progress: true
55+
56+
jobs:
57+
# Check Typescript types correctness
58+
test-typecheck:
59+
if: inputs.typecheck-command != ''
60+
name: Typecheck
61+
runs-on: ubuntu-24.04
62+
defaults:
63+
run:
64+
working-directory: ${{ inputs.working-directory }}
65+
steps:
66+
- name: Checkout repository
67+
uses: actions/checkout@v5
68+
- uses: ./.github/actions/setup-pnpm
69+
with:
70+
install-dependencies: true
71+
- name: Run typecheck
72+
run: |
73+
pnpm ${{ inputs.typecheck-command }}
74+
75+
# Build the project and upload artifact
76+
build:
77+
name: Build artifact
78+
runs-on: ubuntu-24.04
79+
defaults:
80+
run:
81+
working-directory: ${{ inputs.working-directory }}
82+
steps:
83+
- name: Checkout repository
84+
uses: actions/checkout@v5
85+
- uses: ./.github/actions/setup-pnpm
86+
with:
87+
install-dependencies: true
88+
- name: Build project
89+
run: |
90+
pnpm run ${{ inputs.build-command }}
91+
- name: Upload build artifact
92+
id: upload-artifact
93+
uses: actions/upload-artifact@v5
94+
with:
95+
include-hidden-files: true
96+
if-no-files-found: error
97+
name: ${{ inputs.artifact-name }}
98+
path: ${{ inputs.artifact-path }}
99+
100+
deploy-to-staging:
101+
if: github.event_name == 'pull_request'
102+
name: Deploy to staging
103+
runs-on: ubuntu-24.04
104+
permissions:
105+
pull-requests: write
106+
outputs:
107+
wrangler-log: ${{ steps.set-wrangler-log.outputs.wrangler-log }}
108+
needs:
109+
- build
110+
defaults:
111+
run:
112+
working-directory: ${{ inputs.working-directory }}
113+
steps:
114+
# Checkout repository
115+
- name: Checkout repository
116+
uses: actions/checkout@v5
117+
118+
# Setup, and download artifact
119+
- uses: ./.github/actions/pre-deploy
120+
with:
121+
artifact-name: ${{ inputs.artifact-name }}
122+
unpack-path: ${{ inputs.artifact-path }}
123+
124+
# Set wrangler log file path for capturing output
125+
- name: Set wrangler output file path
126+
id: set-wrangler-log
127+
run: |
128+
WRANGLER_LOG=/tmp/wrangler-$(date '+%s%N').log
129+
echo "wrangler-log=$WRANGLER_LOG" >> $GITHUB_OUTPUT
130+
131+
# Deploy to Cloudflare Workers
132+
- name: Deploy version to staging
133+
id: deploy-version
134+
continue-on-error: true
135+
env:
136+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.cloudflare-account-id }}
137+
CLOUDFLARE_API_TOKEN: ${{ secrets.cloudflare-api-token }}
138+
run: |
139+
export WRANGLER_OUTPUT_FILE_PATH=${{ steps.set-wrangler-log.outputs.wrangler-log }}
140+
pnpm run ${{ inputs.deploy-staging-command }}
141+
142+
# Extract deployment URL from wrangler log
143+
- name: Get deployment URL
144+
id: get-deployment-url
145+
continue-on-error: true
146+
run: |
147+
PREVIEW_URL=$(jq --raw-output --slurp 'first(.[] | select(.preview_url) | .preview_url)' ${{ steps.set-wrangler-log.outputs.wrangler-log }})
148+
149+
if [ -z "$PREVIEW_URL" ]; then
150+
echo "Error: No preview_url found in wrangler log"
151+
exit 1
152+
fi
153+
154+
echo "preview-url=$PREVIEW_URL" >> $GITHUB_OUTPUT
155+
156+
# Comment on the pull request with success status
157+
- name: Comment successful deployment URL
158+
if: ${{ steps.get-deployment-url.outcome == 'success' }}
159+
uses: marocchino/sticky-pull-request-comment@v2
160+
with:
161+
message: |
162+
## 🎉 Deployed to Cloudflare!
163+
164+
- Commit: `${{ github.sha }}`
165+
- Preview URL: ${{ steps.get-deployment-url.outputs.preview-url }}
166+
167+
# Comment on the pull request with failure status
168+
- name: Comment deployment failure
169+
if: ${{ steps.get-deployment-url.outcome == 'failure' }}
170+
uses: marocchino/sticky-pull-request-comment@v2
171+
with:
172+
message: |
173+
## ❌ Deployment to Cloudflare failed!
174+
175+
- Commit: `${{ github.sha }}`
176+
- Please check the [workflow logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details.
177+
178+
# Fail the workflow if deployment failed
179+
- name: Fail workflow if deployment failed
180+
if: ${{ steps.get-deployment-url.outcome == 'failure' }}
181+
run: |
182+
echo "Deployment to staging failed."
183+
exit 1
184+
185+
deploy-to-production:
186+
if: github.event_name == 'push'
187+
name: Deploy to production
188+
runs-on: ubuntu-24.04
189+
needs:
190+
- build
191+
- test-typecheck
192+
defaults:
193+
run:
194+
working-directory: ${{ inputs.working-directory }}
195+
steps:
196+
# Checkout repository
197+
- name: Checkout repository
198+
uses: actions/checkout@v5
199+
200+
# Setup, and download artifact
201+
- uses: ./.github/actions/pre-deploy
202+
with:
203+
artifact-name: ${{ inputs.artifact-name }}
204+
unpack-path: ${{ inputs.artifact-path }}
205+
206+
# Deploy to Cloudflare Workers
207+
- name: Deploy to production
208+
id: deploy-production
209+
env:
210+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.cloudflare-account-id }}
211+
CLOUDFLARE_API_TOKEN: ${{ secrets.cloudflare-api-token }}
212+
run: |
213+
pnpm run ${{ inputs.deploy-production-command }}

0 commit comments

Comments
 (0)