Skip to content

Commit 90a0e08

Browse files
committed
fix(ci): update GitHub Pages workflow to use gh-pages branch with preview support
- Switch from actions/deploy-pages to peaceiris/actions-gh-pages - Support preview deployments for feature branches in /preview/<branch>/ - Add custom domain (CNAME) for dev.codebuilder.org - Deploy main to gh-pages root, previews to subdirectories - Add PR comment with preview URL
1 parent e5c2015 commit 90a0e08

1 file changed

Lines changed: 105 additions & 41 deletions

File tree

.github/workflows/nextjs-static-gh-pages.yml

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,152 @@ name: 🚀 Deploy Static Next.js to GitHub Pages
22

33
on:
44
push:
5-
branches: ['main'] # Triggers on push to main branch
5+
branches: ['**'] # Triggers on push to any branch
6+
pull_request:
7+
types: [opened, synchronize, reopened]
68
workflow_dispatch: # Allows manual triggering
79

810
permissions:
9-
contents: read
10-
pages: write
11-
id-token: write
11+
contents: write
12+
pull-requests: write
1213

1314
concurrency:
14-
group: 'pages'
15-
cancel-in-progress: false
15+
group: 'pages-${{ github.head_ref || github.ref_name }}'
16+
cancel-in-progress: true
1617

1718
jobs:
18-
build:
19-
name: 🏗 Build Static Site
19+
build-and-deploy:
20+
name: 🏗 Build & Deploy
2021
runs-on: ubuntu-latest
2122

22-
23-
2423
steps:
2524
- name: 🔍 Checkout repository
2625
uses: actions/checkout@v4
2726

27+
- name: 🔧 Set deployment variables
28+
id: vars
29+
run: |
30+
REPO_NAME="${{ github.event.repository.name }}"
31+
# Custom domain means no repo name prefix in the URL path.
32+
# Set CUSTOM_DOMAIN to your domain, or leave empty to use github.io/<repo> URLs.
33+
CUSTOM_DOMAIN="dev.codebuilder.org"
34+
35+
# Get the branch name (works for both push and PR events)
36+
if [ "${{ github.event_name }}" = "pull_request" ]; then
37+
BRANCH="${{ github.head_ref }}"
38+
else
39+
BRANCH="${{ github.ref_name }}"
40+
fi
41+
42+
# Sanitize branch name for use in URL paths
43+
SAFE_BRANCH=$(echo "$BRANCH" | sed 's/[^a-zA-Z0-9._-]/-/g')
44+
45+
if [ -n "$CUSTOM_DOMAIN" ]; then
46+
BASE_URL="https://${CUSTOM_DOMAIN}"
47+
else
48+
BASE_URL="https://${{ github.repository_owner }}.github.io/${REPO_NAME}"
49+
fi
50+
51+
if [ "$BRANCH" = "main" ]; then
52+
if [ -n "$CUSTOM_DOMAIN" ]; then
53+
echo "base_path=" >> $GITHUB_OUTPUT
54+
else
55+
echo "base_path=/${REPO_NAME}" >> $GITHUB_OUTPUT
56+
fi
57+
echo "dest_dir=" >> $GITHUB_OUTPUT
58+
echo "keep_files=false" >> $GITHUB_OUTPUT
59+
echo "preview_url=${BASE_URL}/" >> $GITHUB_OUTPUT
60+
else
61+
echo "base_path=/preview/${SAFE_BRANCH}" >> $GITHUB_OUTPUT
62+
echo "dest_dir=preview/${SAFE_BRANCH}" >> $GITHUB_OUTPUT
63+
echo "keep_files=true" >> $GITHUB_OUTPUT
64+
echo "preview_url=${BASE_URL}/preview/${SAFE_BRANCH}/" >> $GITHUB_OUTPUT
65+
fi
66+
67+
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
68+
echo "safe_branch=$SAFE_BRANCH" >> $GITHUB_OUTPUT
69+
2870
- name: 🔎 Detect package manager
2971
id: detect-pm
3072
run: |
3173
if [ -f "pnpm-lock.yaml" ]; then
32-
echo "manager=pnpm" >> $GITHUB_ENV
33-
echo "command=install" >> $GITHUB_ENV
34-
echo "runner=pnpm exec" >> $GITHUB_ENV
74+
echo "manager=pnpm" >> $GITHUB_OUTPUT
75+
echo "command=install" >> $GITHUB_OUTPUT
76+
echo "runner=pnpm exec" >> $GITHUB_OUTPUT
3577
else
36-
echo "manager=npm" >> $GITHUB_ENV
37-
echo "command=ci" >> $GITHUB_ENV
38-
echo "runner=npx --no-install" >> $GITHUB_ENV
78+
echo "manager=npm" >> $GITHUB_OUTPUT
79+
echo "command=ci" >> $GITHUB_OUTPUT
80+
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
3981
fi
4082
4183
- name: 📦 Install pnpm
42-
if: env.manager == 'pnpm'
84+
if: steps.detect-pm.outputs.manager == 'pnpm'
4385
run: npm install -g pnpm
4486

4587
- name: ⚙️ Setup Node.js
4688
uses: actions/setup-node@v4
4789
with:
48-
node-version: '20'
49-
cache: ${{ env.manager }}
50-
51-
- name: 🌐 Setup GitHub Pages
52-
uses: actions/configure-pages@v5
53-
54-
# NOTE: We do NOT need to create .env or secrets here, as we provide them directly to the build step below.
55-
90+
node-version: '24'
91+
cache: ${{ steps.detect-pm.outputs.manager }}
5692

5793
- name: 🚫 Ephemerally delete server/api files
5894
run: |
5995
echo "Deleting src/app/api, src/server, src/proxy.ts, and src/app/jobs/[id] for static build..."
6096
rm -rf src/app/api src/server src/proxy.ts src/app/jobs/[id] src/app/[...not-found] prisma.config.ts
6197
6298
- name: 📥 Install dependencies
63-
run: ${{ env.manager }} ${{ env.command }}
99+
run: ${{ steps.detect-pm.outputs.manager }} ${{ steps.detect-pm.outputs.command }}
64100

65101
- name: 🏗 Generate Static Build
66102
env:
67103
NEXT_OUTPUT_MODE: export
68104
GITHUB_PAGES: 1
105+
NEXT_BASE_PATH: ${{ steps.vars.outputs.base_path }}
69106
run: |
70107
echo "Building static files for GitHub Pages..."
108+
echo "Base path: $NEXT_BASE_PATH"
109+
echo "Preview URL: ${{ steps.vars.outputs.preview_url }}"
71110
pnpm build
72111
touch out/.nojekyll
73112
74-
- name: 📤 Upload static site
75-
uses: actions/upload-pages-artifact@v3
113+
- name: 🚀 Deploy to GitHub Pages
114+
uses: peaceiris/actions-gh-pages@v4
76115
with:
77-
path: ./out # With 'output: export', Next.js automatically puts files here.
78-
79-
deploy:
80-
name: 🚀 Deploy to GitHub Pages
81-
environment:
82-
name: github-pages
83-
url: ${{ steps.deployment.outputs.page_url }}
84-
runs-on: ubuntu-latest
85-
needs: build
86-
steps:
87-
- name: 🌍 Deploy
88-
id: deployment
89-
uses: actions/deploy-pages@v4
116+
github_token: ${{ secrets.GITHUB_TOKEN }}
117+
publish_dir: ./out
118+
destination_dir: ${{ steps.vars.outputs.dest_dir }}
119+
keep_files: ${{ steps.vars.outputs.keep_files }}
120+
cname: dev.codebuilder.org
121+
122+
- name: 💬 Comment preview URL on PR
123+
if: github.event_name == 'pull_request'
124+
uses: actions/github-script@v7
125+
with:
126+
script: |
127+
const url = '${{ steps.vars.outputs.preview_url }}';
128+
const sha = context.sha.substring(0, 7);
129+
const body = `🚀 **Preview deployment ready!**\n\n📎 **Preview URL:** ${url}\n\n_Deployed from commit \`${sha}\`_`;
130+
131+
const { data: comments } = await github.rest.issues.listComments({
132+
owner: context.repo.owner,
133+
repo: context.repo.repo,
134+
issue_number: context.issue.number,
135+
});
136+
137+
const botComment = comments.find(c => c.body.includes('Preview deployment ready!'));
138+
139+
if (botComment) {
140+
await github.rest.issues.updateComment({
141+
owner: context.repo.owner,
142+
repo: context.repo.repo,
143+
comment_id: botComment.id,
144+
body,
145+
});
146+
} else {
147+
await github.rest.issues.createComment({
148+
owner: context.repo.owner,
149+
repo: context.repo.repo,
150+
issue_number: context.issue.number,
151+
body,
152+
});
153+
}

0 commit comments

Comments
 (0)