Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
node_modules
npm-debug.log
.git
.gitignore
.env*
!.env.example
Dockerfile
.dockerignore
README.md
.next
.cache
.vscode
.idea
*.log
coverage
.nyc_output
dist
requirements
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Environment Configuration Template
# Copy this file to .env.local for local development
# DO NOT commit .env.local to version control

# Public environment variables (available to client-side code)
# Note: The Vite proxy will handle /api routing to localhost:8080
PUBLIC_API_URL=http://localhost:4321/api
PUBLIC_SITE_URL=http://localhost:4321

# Node environment
NODE_ENV=development
1 change: 0 additions & 1 deletion .env.template

This file was deleted.

293 changes: 293 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
name: Deploy Frontend to GCP

on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]

env:
GCP_PROJECT_ID: fdrpkm
REGION: asia-southeast1
REGISTRY_HOSTNAME: asia-southeast1-docker.pkg.dev

jobs:
# Development deployment
deploy-dev:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
env:
BUCKET_NAME: freshmenfest2025-dev-static
SERVICE_NAME: freshmenfest2025-dev-frontend
API_URL: https://dev.freshmenfest2025.com/api
SITE_URL: https://dev.freshmenfest2025.com

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --no-frozen-lockfile

- name: Build Astro application (Development)
run: pnpm run build
env:
PUBLIC_API_URL: ${{ env.API_URL }}
PUBLIC_SITE_URL: ${{ env.SITE_URL }}

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
project_id: fdrpkm

- name: Configure Docker for GCP
run: |
gcloud auth configure-docker ${{ env.REGISTRY_HOSTNAME }} --quiet

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2

- name: Upload static assets to Cloud Storage (Dev)
run: |
# Upload Astro static assets
if [ -d "./dist/client/_astro" ]; then
gsutil -m rsync -r -d ./dist/client/_astro gs://${{ env.BUCKET_NAME }}/_astro/
fi

# Upload public images
if [ -d "./public/images" ]; then
gsutil -m cp -r ./public/images/* gs://${{ env.BUCKET_NAME }}/images/ || true
fi

# Set cache control headers for static assets
gsutil -m setmeta -h "Cache-Control:public, max-age=31536000, immutable" \
"gs://${{ env.BUCKET_NAME }}/_astro/**" || true
gsutil -m setmeta -h "Cache-Control:public, max-age=86400" \
"gs://${{ env.BUCKET_NAME }}/images/**" || true

- name: Build and push Docker image (Dev)
run: |
# Build image with build args
docker build --build-arg PUBLIC_API_URL=${{ env.API_URL }} --build-arg PUBLIC_SITE_URL=${{ env.SITE_URL }} -t ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }} -t ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:latest-dev .

# Push both tags
docker push ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:latest-dev

- name: Deploy to Cloud Run (Dev)
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image=${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }} \
--region=${{ env.REGION }} \
--platform=managed \
--allow-unauthenticated \
--port=4321 \
--memory=512Mi \
--cpu=1 \
--min-instances=0 \
--max-instances=10 \
--concurrency=80 \
--timeout=300 \
--set-env-vars="NODE_ENV=development,PUBLIC_API_URL=${{ env.API_URL }},PUBLIC_SITE_URL=${{ env.SITE_URL }}" \
--execution-environment=gen2

- name: Get Cloud Run URL (Dev)
run: |
SERVICE_URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} --region=${{ env.REGION }} --format="value(status.url)")
echo "Development service deployed to: $SERVICE_URL"

# Production deployment
deploy-prod:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
env:
BUCKET_NAME: freshmenfest2025-static
SERVICE_NAME: freshmenfest2025-frontend

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --no-frozen-lockfile

- name: Build Astro application (Production)
run: pnpm run build
env:
PUBLIC_API_URL: ${{ secrets.PROD_API_URL }}
PUBLIC_SITE_URL: ${{ secrets.PROD_SITE_URL }}

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
project_id: fdrpkm

- name: Configure Docker for GCP
run: |
gcloud auth configure-docker ${{ env.REGISTRY_HOSTNAME }} --quiet

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2

- name: Upload static assets to Cloud Storage (Prod)
run: |
# Upload Astro static assets
if [ -d "./dist/client/_astro" ]; then
gsutil -m rsync -r -d ./dist/client/_astro gs://${{ env.BUCKET_NAME }}/_astro/
fi

# Upload public images
if [ -d "./public/images" ]; then
gsutil -m cp -r ./public/images/* gs://${{ env.BUCKET_NAME }}/images/ || true
fi

# Set cache control headers for static assets
gsutil -m setmeta -h "Cache-Control:public, max-age=31536000, immutable" \
"gs://${{ env.BUCKET_NAME }}/_astro/**" || true
gsutil -m setmeta -h "Cache-Control:public, max-age=86400" \
"gs://${{ env.BUCKET_NAME }}/images/**" || true

- name: Build and push Docker image (Prod)
run: |
# Build image with build args
docker build --build-arg PUBLIC_API_URL=${{ secrets.PROD_API_URL }} --build-arg PUBLIC_SITE_URL=${{ secrets.PROD_SITE_URL }} -t ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }} -t ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:latest .

# Push both tags
docker push ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:latest

- name: Deploy to Cloud Run (Prod)
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image=${{ env.REGISTRY_HOSTNAME }}/${{ env.GCP_PROJECT_ID }}/cloud-run-source-deploy/${{ env.SERVICE_NAME }}:${{ github.sha }} \
--region=${{ env.REGION }} \
--platform=managed \
--allow-unauthenticated \
--port=4321 \
--memory=512Mi \
--cpu=1 \
--min-instances=0 \
--max-instances=20 \
--concurrency=100 \
--timeout=300 \
--set-env-vars="NODE_ENV=production,PUBLIC_API_URL=${{ secrets.PROD_API_URL }},PUBLIC_SITE_URL=${{ secrets.PROD_SITE_URL }}" \
--execution-environment=gen2

- name: Get Cloud Run URL (Prod)
run: |
SERVICE_URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} --region=${{ env.REGION }} --format="value(status.url)")
echo "Production service deployed to: $SERVICE_URL"

# Test job for PRs
test:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --no-frozen-lockfile

- name: Build application (Test)
run: pnpm run build
env:
PUBLIC_API_URL: http://localhost:8080/api
PUBLIC_SITE_URL: http://localhost:4321

- name: Test build output
run: |
if [ ! -f "./dist/server/entry.mjs" ]; then
echo "Build failed: entry.mjs not found"
exit 1
fi
echo "Build successful!"

- name: Test Docker build
run: |
docker build --build-arg PUBLIC_API_URL=http://localhost:8080/api --build-arg PUBLIC_SITE_URL=http://localhost:4321 -t test-build:pr-${{ github.event.pull_request.number }} .
echo "Docker build successful!"

- name: Run Docker container test
run: |
docker run -d --name test-container -p 4321:4321 test-build:pr-${{ github.event.pull_request.number }}
sleep 10
curl -f http://localhost:4321/ || (docker logs test-container && exit 1)
docker stop test-container
docker rm test-container
echo "Container test successful!"
Loading
Loading