Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and Push Frontend Image
name: Build and Push Non-Production Images

on:
push:
Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/build_and_push_production_images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Build and Push Production Images
# Builds the frontend into production containers for release and pushes to GitHub Container Registry with stable tag

permissions:
contents: read # Default to read-only permissions for all jobs
on:
release:
types: [released] # This workflow only runs when a new GitHub release is *actually* released publicly
workflow_dispatch: # Also allows manual triggering from GitHub UI

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }} # Repository path for the image

jobs:
build_test_run:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4 # Checkout the repository code
- name: Set up Node.js
uses: actions/setup-node@v3 # Set up Node.js environment
with:
node-version: 21.x # Using Node.js 21.x as in the regular CI workflow
cache: 'npm' # Enable npm caching for faster builds

- name: Install dependencies
run: npm run ci # Clean Install of dependencies
- name: Run build
run: npm run build --if-present # Build the Next.js application

# TODO Uncomment when tests are implemented
# - name: Run tests
# run: npm test # Run tests when they are implemented

build_and_push_docker:
runs-on: ubuntu-24.04
needs: build_test_run
permissions:
contents: read # Read repository contents
packages: write # Write to GitHub Packages
attestations: write # Allow attestation creation
id-token: write # Required for OIDC signing

steps:
- uses: actions/checkout@v4 # Checkout code for the Docker build

- name: Setup QEMU
uses: docker/setup-qemu-action@v2 # Set up QEMU for multi-architecture builds
with:
platforms: linux/amd64,linux/arm64 # Build for both Intel/AMD and ARM architectures

- name: Docker login
uses: docker/login-action@v2 # Log in to GitHub Container Registry
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/setup-buildx-action@v3 # Set up Docker Buildx
with:
install: true # Install Buildx

- name: Show Docker Build Cache (Before)
run: | # Display cache info before build
echo "🔍 Checking buildx cache BEFORE build..."
docker buildx du || echo "No cache found yet."
# Compute stable tag for production image
- name: Determine Image Tags
id: tags # Set output ID for this step
run: |
IMAGE_NAME="${{ env.REGISTRY }}/${{ github.repository }}"
echo "frontend_tags=$IMAGE_NAME:stable" >> $GITHUB_OUTPUT # Tag image as "stable"
echo "frontend_image_name=$IMAGE_NAME" >> $GITHUB_OUTPUT
- name: Build + Push Frontend
id: push_frontend # Set ID for this step to reference outputs
uses: docker/build-push-action@v5
with:
context: . # Build context is repository root
file: ./Dockerfile # Use the Dockerfile at repository root
target: runner # Use the runner stage from the Dockerfile
platforms: linux/amd64,linux/arm64 # Multi-architecture build
push: true # Push image to registry
provenance: true # Enable provenance metadata
sbom: true # Generate Software Bill of Materials
build-args: | # Environment variables for the build
NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL=${{ secrets.BACKEND_SERVICE_PROTOCOL }}
NEXT_PUBLIC_BACKEND_SERVICE_HOST=${{ secrets.BACKEND_SERVICE_HOST }}
NEXT_PUBLIC_BACKEND_SERVICE_PORT=${{ secrets.BACKEND_PORT }}
NEXT_PUBLIC_BACKEND_API_VERSION=${{ secrets.BACKEND_API_VERSION }}
FRONTEND_SERVICE_PORT=${{ secrets.FRONTEND_SERVICE_PORT }}
FRONTEND_SERVICE_INTERFACE=${{ secrets.FRONTEND_SERVICE_INTERFACE }}
tags: ${{ steps.tags.outputs.frontend_tags }} # Use "stable" tag from previous step
cache-from: type=gha # Use GitHub Actions cache
cache-to: type=gha,mode=max # Cache for future builds

- name: Show Docker Build Cache (After)
run: | # Display cache info after build
echo "📦 Checking buildx cache AFTER build..."
docker buildx du || echo "Failed to get updated cache info."
# Install Cosign for image signing
- name: Install Cosign
uses: sigstore/cosign-installer@v3

# Sign the container image
- name: Sign image with Cosign
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign --yes ${{ steps.tags.outputs.frontend_image_name }}:stable
# Attest build provenance
- name: Attest Frontend
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' # Only attest on release or manual trigger
uses: actions/attest-build-provenance@v2 # Use GitHub attestation action
with:
subject-name: ${{ steps.tags.outputs.frontend_image_name }} # Image name to attest
subject-digest: ${{ steps.push_frontend.outputs.digest }} # Image digest to attest
push-to-registry: true # Push attestation to registry

- name: Print Usage Instructions
run: | # Print usage instructions
echo "Frontend Image Pushed to ghcr.io as STABLE:"
echo " docker pull ${{ steps.tags.outputs.frontend_image_name }}:stable"
echo "Run it locally:"
echo " docker run --rm --env-file .env -p 3000:3000 ${{ steps.tags.outputs.frontend_image_name }}:stable"
echo "Verify signature:"
echo " cosign verify ${{ steps.tags.outputs.frontend_image_name }}:stable"
88 changes: 88 additions & 0 deletions .github/workflows/deploy_to_do.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Deploy to DigitalOcean via Tailscale UI

permissions:
contents: read # Read-only access to repository contents
on:
workflow_dispatch:

jobs:
deploy:
name: Deploy Frontend to DigitalOcean # Job name for display
runs-on: ubuntu-24.04 # Use latest Ubuntu runner
permissions: # job-level permissions necessary for Tailscale
contents: read # Read repository contents
id-token: write # Required for OIDC token operations (Tailscale)

steps:
# Set up Tailscale connection to securely access the droplet
- name: Set up Tailscale Connection # Connect to Tailscale network
uses: tailscale/github-action@v3 # Use Tailscale's official action
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} # Tailscale OAuth client ID
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} # Tailscale OAuth secret
tags: tag:github-actions # Tag for this Tailscale node
version: latest # Use latest Tailscale version
use-cache: 'true' # Cache Tailscale for faster setup

# Verify SSH connection before attempting deployment
- name: Set up SSH Connection # Setup and verify SSH key
run: |
mkdir -p ~/.ssh # Create SSH directory
echo "${{ secrets.DO_SSH_KEY }}" > ~/.ssh/id_ed25519 # Save SSH key from secrets
chmod 600 ~/.ssh/id_ed25519 # Set correct permissions
# Add host key to known_hosts to prevent prompts
ssh-keyscan -H ${{ secrets.DO_TAILSCALE_NAME }} >> ~/.ssh/known_hosts
# Test connection to ensure everything is working
ssh -o BatchMode=yes -i ~/.ssh/id_ed25519 ${{ secrets.DO_USERNAME }}@${{ secrets.DO_TAILSCALE_NAME }} 'echo "SSH connection successful"'
# Main deployment step - SSH to the server and deploy the frontend
- name: Deploy Frontend Container # Main deployment step
run: |
# Set up SSH connection
ssh -o BatchMode=yes -i ~/.ssh/id_ed25519 ${{ secrets.DO_USERNAME }}@${{ secrets.DO_TAILSCALE_NAME }} '
set -e # Exit on any error
echo "📦 Starting frontend deployment..." # Announce deployment start
# Login to GitHub Container Registry
echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
# Pull the latest frontend image
docker pull ${{ secrets.FRONTEND_IMAGE_NAME }}
# Stop and remove existing container if it exists
docker stop nextjs-app 2>/dev/null || true
docker rm nextjs-app 2>/dev/null || true
# Run new container with environment variables
docker run -d --name nextjs-app \
--restart unless-stopped \
-p ${{ secrets.FRONTEND_SERVICE_PORT }}:${{ secrets.FRONTEND_SERVICE_PORT }} \
-e NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL=${{ secrets.NEXT_PUBLIC_BACKEND_SERVICE_PROTOCOL }} \
-e NEXT_PUBLIC_BACKEND_SERVICE_HOST=${{ secrets.NEXT_PUBLIC_BACKEND_SERVICE_HOST }} \
-e NEXT_PUBLIC_BACKEND_SERVICE_PORT=${{ secrets.NEXT_PUBLIC_BACKEND_SERVICE_PORT }} \
-e NEXT_PUBLIC_BACKEND_API_VERSION=${{ secrets.NEXT_PUBLIC_BACKEND_API_VERSION }} \
-e NEXT_PUBLIC_TIPTAP_APP_ID=${{ secrets.NEXT_PUBLIC_TIPTAP_APP_ID }} \
-e FRONTEND_SERVICE_INTERFACE=${{ secrets.FRONTEND_SERVICE_INTERFACE }} \
-e FRONTEND_SERVICE_PORT=${{ secrets.FRONTEND_SERVICE_PORT }} \
${{ secrets.FRONTEND_IMAGE_NAME }}
# Verify the container is running
echo "⏳ Waiting for container to initialize..." # Wait for startup
sleep 5 # Brief pause
# Check container status
if docker ps | grep -q nextjs-app; then
echo "✅ Deployment successful! Frontend container is running."
else
echo "❌ Deployment failed. Container logs:"
docker logs nextjs-app
exit 1 # Fail the workflow
fi
# Display container info
echo "🔍 Container details:"
docker ps | grep nextjs-app
'