From 5e71713321610b04d85096626bfad8285a9c8dd0 Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Tue, 20 May 2025 20:05:12 -0400 Subject: [PATCH 1/6] renamed nonprod gh actions workflow. --- ...d_push_image.yml => build_and_push_nonproduction_images.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{build_and_push_image.yml => build_and_push_nonproduction_images.yml} (98%) diff --git a/.github/workflows/build_and_push_image.yml b/.github/workflows/build_and_push_nonproduction_images.yml similarity index 98% rename from .github/workflows/build_and_push_image.yml rename to .github/workflows/build_and_push_nonproduction_images.yml index e073daf2..657abc51 100644 --- a/.github/workflows/build_and_push_image.yml +++ b/.github/workflows/build_and_push_nonproduction_images.yml @@ -1,4 +1,4 @@ -name: Build and Push Frontend Image +name: Build and Push Non-Production Images on: push: From db8de0ea704a19068ed783cb2f81accb17ca1022 Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Tue, 20 May 2025 20:20:36 -0400 Subject: [PATCH 2/6] adds build and push production images when frontend release is created. --- .../build_and_push_production_images.yml | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .github/workflows/build_and_push_production_images.yml diff --git a/.github/workflows/build_and_push_production_images.yml b/.github/workflows/build_and_push_production_images.yml new file mode 100644 index 00000000..2daf60e5 --- /dev/null +++ b/.github/workflows/build_and_push_production_images.yml @@ -0,0 +1,128 @@ +name: Build and Push Production Images # Workflow name for GitHub Actions UI +# Builds the frontend into production containers for release and pushes to GitHub Container Registry with stable tag + +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 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" \ No newline at end of file From fdcdc86b5be6918565934c74091edecf6207c5b8 Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Tue, 20 May 2025 20:34:42 -0400 Subject: [PATCH 3/6] adds deploy to do workflow for frontend. --- .github/workflows/deploy_to_do.yml | 86 ++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/deploy_to_do.yml diff --git a/.github/workflows/deploy_to_do.yml b/.github/workflows/deploy_to_do.yml new file mode 100644 index 00000000..66186687 --- /dev/null +++ b/.github/workflows/deploy_to_do.yml @@ -0,0 +1,86 @@ +name: Deploy to DigitalOcean via Tailscale # Workflow name displayed in GitHub UI + +# Defines when the workflow will run +on: + workflow_dispatch: # Allow manual triggering from GitHub UI + pull_request: + types: [opened, synchronize, reopened, closed] # Run on PR events + +jobs: + deploy: + name: Deploy Frontend to DigitalOcean # Job name for display + runs-on: ubuntu-24.04 # Use latest Ubuntu runner + + steps: + # Set up Tailscale connection to securely access the droplet + - name: Setup Tailscale # 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: Setup 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 + ' \ No newline at end of file From 60f2429b79a65bddfd2fa2227548694228661f0f Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Wed, 21 May 2025 19:14:47 -0400 Subject: [PATCH 4/6] refactor: update build and push workflow to include permissions and change install command --- .github/workflows/build_and_push_production_images.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_push_production_images.yml b/.github/workflows/build_and_push_production_images.yml index 2daf60e5..db730390 100644 --- a/.github/workflows/build_and_push_production_images.yml +++ b/.github/workflows/build_and_push_production_images.yml @@ -1,6 +1,8 @@ -name: Build and Push Production Images # Workflow name for GitHub Actions UI +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 @@ -22,7 +24,7 @@ jobs: cache: 'npm' # Enable npm caching for faster builds - name: Install dependencies - run: npm ci # Clean install of dependencies + run: npm run build --if-present - name: Run build run: npm run build --if-present # Build the Next.js application From 92e11bd9a2c42eab020ba71375473edfb9091fc2 Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Wed, 21 May 2025 19:17:37 -0400 Subject: [PATCH 5/6] adding back npm run ci to production workflow --- .github/workflows/build_and_push_production_images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_push_production_images.yml b/.github/workflows/build_and_push_production_images.yml index db730390..62b66330 100644 --- a/.github/workflows/build_and_push_production_images.yml +++ b/.github/workflows/build_and_push_production_images.yml @@ -24,7 +24,7 @@ jobs: cache: 'npm' # Enable npm caching for faster builds - name: Install dependencies - run: npm run build --if-present + run: npm run ci # Clean Install of dependencies - name: Run build run: npm run build --if-present # Build the Next.js application From 7b3086786cf786952ee83ab3708c701712ffeff3 Mon Sep 17 00:00:00 2001 From: Levi McDonough Date: Wed, 21 May 2025 19:32:38 -0400 Subject: [PATCH 6/6] refactor: update workflow permissions and improve step naming for clarity --- .github/workflows/deploy_to_do.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy_to_do.yml b/.github/workflows/deploy_to_do.yml index 66186687..3e36300a 100644 --- a/.github/workflows/deploy_to_do.yml +++ b/.github/workflows/deploy_to_do.yml @@ -1,19 +1,21 @@ -name: Deploy to DigitalOcean via Tailscale # Workflow name displayed in GitHub UI +name: Deploy to DigitalOcean via Tailscale UI -# Defines when the workflow will run +permissions: + contents: read # Read-only access to repository contents on: - workflow_dispatch: # Allow manual triggering from GitHub UI - pull_request: - types: [opened, synchronize, reopened, closed] # Run on PR events + 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: Setup Tailscale # Connect to Tailscale network + - 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 @@ -23,7 +25,7 @@ jobs: use-cache: 'true' # Cache Tailscale for faster setup # Verify SSH connection before attempting deployment - - name: Setup SSH Connection # Setup and verify SSH key + - 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