Skip to content

Commit 754399c

Browse files
Deploy Next.js Wedding Website with GitHub-Hosted Runners: Static (GitHub Pages) + Full-Stack (Vercel + Azure) Options (#239)
2 parents 9a74df3 + f7e0752 commit 754399c

17 files changed

+4660
-25
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# This workflow will build and push the Next.js wedding website to Azure Web App
2+
# when a commit is pushed to main branch or manually triggered.
3+
#
4+
# Prerequisites:
5+
# 1. Create an Azure Web App (Node.js 20 LTS runtime)
6+
# 2. Download the Publish Profile from Azure Portal
7+
# 3. Add AZURE_WEBAPP_PUBLISH_PROFILE secret to GitHub repository
8+
# 4. Update AZURE_WEBAPP_NAME environment variable below
9+
10+
name: Deploy to Azure Web App
11+
12+
on:
13+
push:
14+
branches: ["main"]
15+
workflow_dispatch:
16+
17+
env:
18+
AZURE_WEBAPP_NAME: sharothee-wedding # Set this to your Azure Web App name
19+
AZURE_WEBAPP_PACKAGE_PATH: 'client' # Path to the Next.js project
20+
NODE_VERSION: '20.x' # Node.js version
21+
22+
permissions:
23+
contents: read
24+
25+
jobs:
26+
build:
27+
runs-on: ubuntu-latest
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
- name: Set up Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: ${{ env.NODE_VERSION }}
37+
cache: 'npm'
38+
cache-dependency-path: 'client/package-lock.json'
39+
40+
- name: Install dependencies
41+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
42+
run: npm ci
43+
44+
- name: Create production environment file
45+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
46+
run: |
47+
echo "Creating production environment file..."
48+
cat << EOF > .env.production
49+
# Database - Azure will use local SQLite file
50+
DATABASE_URL="file:./prisma/prod.db"
51+
52+
# NextAuth - Set these in Azure App Settings
53+
NEXTAUTH_SECRET="${{ secrets.NEXTAUTH_SECRET }}"
54+
NEXTAUTH_URL="${{ secrets.NEXTAUTH_URL }}"
55+
56+
# Admin Credentials
57+
ADMIN_EMAIL="${{ secrets.ADMIN_EMAIL }}"
58+
ADMIN_PASSWORD="${{ secrets.ADMIN_PASSWORD }}"
59+
60+
# Email Service (Gmail)
61+
GMAIL_USER="${{ secrets.GMAIL_USER }}"
62+
GMAIL_APP_PASSWORD="${{ secrets.GMAIL_APP_PASSWORD }}"
63+
GMAIL_FROM="${{ secrets.GMAIL_FROM }}"
64+
65+
# Cloudinary (optional)
66+
CLOUDINARY_CLOUD_NAME="${{ secrets.CLOUDINARY_CLOUD_NAME }}"
67+
CLOUDINARY_API_KEY="${{ secrets.CLOUDINARY_API_KEY }}"
68+
CLOUDINARY_API_SECRET="${{ secrets.CLOUDINARY_API_SECRET }}"
69+
EOF
70+
echo "Environment file created"
71+
72+
- name: Generate Prisma Client
73+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
74+
run: npx prisma generate
75+
76+
- name: Build Next.js application
77+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
78+
run: npm run build
79+
80+
- name: Run tests
81+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
82+
run: npm test -- --ci --coverage --watchAll=false
83+
continue-on-error: true
84+
85+
- name: Create deployment package
86+
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
87+
run: |
88+
# Copy necessary files for deployment
89+
mkdir -p ../deploy
90+
cp -r .next ../deploy/
91+
cp -r public ../deploy/
92+
cp -r prisma ../deploy/
93+
cp -r node_modules ../deploy/
94+
cp package.json ../deploy/
95+
cp package-lock.json ../deploy/
96+
cp next.config.ts ../deploy/
97+
cp .env.production ../deploy/
98+
99+
# Create web.config for Azure
100+
cat << 'WEBCONFIG' > ../deploy/web.config
101+
<?xml version="1.0" encoding="utf-8"?>
102+
<configuration>
103+
<system.webServer>
104+
<handlers>
105+
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
106+
</handlers>
107+
<rewrite>
108+
<rules>
109+
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
110+
<match url="^server.js\/debug[\/]?" />
111+
</rule>
112+
<rule name="StaticContent">
113+
<action type="Rewrite" url="public{REQUEST_URI}"/>
114+
</rule>
115+
<rule name="DynamicContent">
116+
<conditions>
117+
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
118+
</conditions>
119+
<action type="Rewrite" url="server.js"/>
120+
</rule>
121+
</rules>
122+
</rewrite>
123+
<security>
124+
<requestFiltering>
125+
<hiddenSegments>
126+
<remove segment="bin"/>
127+
</hiddenSegments>
128+
</requestFiltering>
129+
</security>
130+
<httpErrors existingResponse="PassThrough" />
131+
<iisnode node_env="%node_env%" nodeProcessCountPerApplication="1" maxConcurrentRequestsPerProcess="1024" maxNamedPipeConnectionRetry="100" namedPipeConnectionRetryDelay="250" maxNamedPipeConnectionPoolSize="512" maxNamedPipePooledConnectionAge="30000" asyncCompletionThreadCount="0" initialRequestBufferSize="4096" maxRequestBufferSize="65536" watchedFiles="*.js;iisnode.yml" uncFileChangesPollingInterval="5000" gracefulShutdownTimeout="60000" loggingEnabled="true" logDirectory="iisnode" debuggingEnabled="false" debugHeaderEnabled="false" debuggerPortRange="5058-6058" debuggerPathSegment="debug" maxLogFileSizeInKB="128" maxTotalLogFileSizeInKB="1024" maxLogFiles="20" devErrorsEnabled="false" flushResponse="false" enableXFF="false" promoteServerVars="" configOverrides="iisnode.yml" />
132+
</system.webServer>
133+
</configuration>
134+
WEBCONFIG
135+
136+
# Create server.js for Azure
137+
cat << 'SERVERJS' > ../deploy/server.js
138+
const { createServer } = require('http')
139+
const { parse } = require('url')
140+
const next = require('next')
141+
142+
const dev = process.env.NODE_ENV !== 'production'
143+
const hostname = process.env.WEBSITE_HOSTNAME || 'localhost'
144+
const port = process.env.PORT || 3000
145+
146+
const app = next({ dev, hostname, port })
147+
const handle = app.getRequestHandler()
148+
149+
app.prepare().then(() => {
150+
createServer(async (req, res) => {
151+
try {
152+
const parsedUrl = parse(req.url, true)
153+
await handle(req, res, parsedUrl)
154+
} catch (err) {
155+
console.error('Error occurred handling', req.url, err)
156+
res.statusCode = 500
157+
res.end('internal server error')
158+
}
159+
}).listen(port, (err) => {
160+
if (err) throw err
161+
console.log(`> Ready on http://${hostname}:${port}`)
162+
})
163+
})
164+
SERVERJS
165+
166+
- name: Upload artifact for deployment job
167+
uses: actions/upload-artifact@v4
168+
with:
169+
name: node-app
170+
path: deploy/
171+
172+
deploy:
173+
permissions:
174+
contents: none
175+
runs-on: ubuntu-latest
176+
needs: build
177+
environment:
178+
name: 'Production'
179+
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
180+
181+
steps:
182+
- name: Download artifact from build job
183+
uses: actions/download-artifact@v4
184+
with:
185+
name: node-app
186+
187+
- name: Display deployment package contents
188+
run: |
189+
echo "Deployment package contents:"
190+
ls -la
191+
echo "Checking for required files:"
192+
[ -f "server.js" ] && echo "✓ server.js found" || echo "✗ server.js missing"
193+
[ -f "package.json" ] && echo "✓ package.json found" || echo "✗ package.json missing"
194+
[ -d ".next" ] && echo "✓ .next directory found" || echo "✗ .next directory missing"
195+
[ -f "web.config" ] && echo "✓ web.config found" || echo "✗ web.config missing"
196+
197+
- name: Deploy to Azure Web App
198+
id: deploy-to-webapp
199+
uses: azure/webapps-deploy@v2
200+
with:
201+
app-name: ${{ env.AZURE_WEBAPP_NAME }}
202+
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
203+
package: .
204+
205+
- name: Post-deployment verification
206+
run: |
207+
echo "Deployment completed successfully!"
208+
echo "Web App URL: ${{ steps.deploy-to-webapp.outputs.webapp-url }}"
209+
echo "Please verify the deployment by visiting the URL above."
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Deploy Full-Stack App to Vercel
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
workflow_dispatch:
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
16+
- name: Setup Node.js 20
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: '20'
20+
cache: 'npm'
21+
cache-dependency-path: 'client/package-lock.json'
22+
23+
- name: Install dependencies
24+
working-directory: ./client
25+
run: npm ci
26+
27+
- name: Generate Prisma Client
28+
working-directory: ./client
29+
run: npx prisma generate
30+
31+
- name: Run tests
32+
working-directory: ./client
33+
run: npm test -- --ci --coverage --watchAll=false
34+
continue-on-error: true
35+
36+
- name: Install Vercel CLI
37+
run: npm install --global vercel@latest
38+
39+
- name: Pull Vercel Environment Information
40+
working-directory: ./client
41+
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
42+
env:
43+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
44+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
45+
46+
- name: Build Project Artifacts
47+
working-directory: ./client
48+
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
49+
env:
50+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
51+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
52+
53+
- name: Deploy Project Artifacts to Vercel
54+
working-directory: ./client
55+
id: deploy
56+
run: |
57+
url=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }})
58+
echo "deployment_url=$url" >> $GITHUB_OUTPUT
59+
echo "Deployed to: $url"
60+
env:
61+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
62+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
63+
64+
- name: Comment deployment URL on PR
65+
if: github.event_name == 'pull_request'
66+
uses: actions/github-script@v7
67+
with:
68+
github-token: ${{ secrets.GITHUB_TOKEN }}
69+
script: |
70+
github.rest.issues.createComment({
71+
issue_number: context.issue.number,
72+
owner: context.repo.owner,
73+
repo: context.repo.repo,
74+
body: '🚀 Deployed to Vercel: ${{ steps.deploy.outputs.deployment_url }}'
75+
})

.github/workflows/nextjs.yml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
# Workflow for building and deploying a static Next.js site to GitHub Pages
22
#
3-
# NOTE: This workflow builds a static-only version of the site without:
4-
# - API routes (forms will show email contact information instead)
5-
# - Admin pages (authentication requires server-side functionality)
6-
# - For full-featured deployment with backend, use Hostinger VPS deployment
7-
# - See HOSTINGER_VPS_DEPLOYMENT_PLAN.md and ci-cd-pipeline.yml for server deployment
3+
# FEATURES:
4+
# ✅ All public-facing pages (Home, Events, Gallery, Live, Travel, Contact, RSVP)
5+
# ✅ Working contact and RSVP forms (using Web3Forms API)
6+
# ✅ All images and static assets
7+
# ✅ Responsive design and mobile-friendly
8+
#
9+
# SETUP REQUIRED:
10+
# 1. Get free Web3Forms API key from https://web3forms.com
11+
# 2. Add as repository secret: WEB3FORMS_ACCESS_KEY
12+
# 3. Enable GitHub Pages in repository Settings → Pages → Source: GitHub Actions
13+
#
14+
# EXCLUDED (requires server):
15+
# ❌ API routes (not supported in static export)
16+
# ❌ Admin pages (require authentication)
17+
# ❌ Database operations
18+
#
19+
# For full-featured deployment with backend, see HOSTINGER_VPS_DEPLOYMENT_PLAN.md
20+
# For quick setup instructions, see QUICK_DEPLOY_GITHUB_PAGES.md
821
#
922
name: Deploy Next.js site to Pages
1023

@@ -102,11 +115,13 @@ jobs:
102115
CLOUDINARY_API_KEY="placeholder"
103116
CLOUDINARY_API_SECRET="placeholder"
104117
105-
# Web3Forms for static form submissions (optional - add your key)
106-
# Get free key at https://web3forms.com
107-
NEXT_PUBLIC_WEB3FORMS_KEY="YOUR_KEY_HERE"
118+
# Web3Forms for static form submissions
119+
# Get your free access key at https://web3forms.com
120+
# Add as repository secret: WEB3FORMS_ACCESS_KEY
121+
NEXT_PUBLIC_WEB3FORMS_KEY="${{ secrets.WEB3FORMS_ACCESS_KEY }}"
108122
EOF
109123
echo "Environment file created"
124+
echo "Web3Forms key configured: ${{ secrets.WEB3FORMS_ACCESS_KEY != '' && 'Yes' || 'No (using fallback)' }}"
110125
- name: Generate Prisma client
111126
working-directory: ./client
112127
run: npx prisma generate

0 commit comments

Comments
 (0)