Skip to content

Instantly share code, notes, and snippets.

@Ba4bes
Created January 17, 2026 18:14
Show Gist options
  • Select an option

  • Save Ba4bes/6e0f38b77674ee7d28bab5c4049db53a to your computer and use it in GitHub Desktop.

Select an option

Save Ba4bes/6e0f38b77674ee7d28bab5c4049db53a to your computer and use it in GitHub Desktop.
# ===================================================================
# GitHub Actions Workflow for Astro + Azure Static Web Apps
# ===================================================================
# This workflow automates building, testing, and deploying an Astro
# site to Azure Static Web Apps with infrastructure as code.
#
# Prerequisites:
# 1. Azure subscription with a Service Principal configured for OIDC
# 2. GitHub secrets configured (see "Required Secrets" section below)
# 3. Bicep template for infrastructure (optional, see deploy_infrastructure job)
# 4. Package manager: pnpm (can be changed to npm/yarn if needed)
#
# Required GitHub Secrets:
# - AZURE_CLIENT_ID: Service Principal Application (client) ID
# - AZURE_TENANT_ID: Azure AD Tenant ID
# - AZURE_SUBSCRIPTION_ID: Azure Subscription ID
# - AZURE_RG_NAME: Azure Resource Group name
#
# For OIDC setup instructions, see:
# https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure
# ===================================================================
name: Build and Deploy
# Trigger conditions for this workflow
on:
push:
branches:
- main # Deploy to production when code is merged to main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main # Create preview deployments for PRs
schedule:
# Optional: Daily rebuild to refresh dynamic content (e.g., from APIs)
# Remove this if your site is purely static
- cron: '0 6 * * *' # Runs at 6:00 AM UTC daily
workflow_dispatch: # Allow manual triggering from GitHub UI
# Permissions required for OIDC authentication and PR comments
permissions:
id-token: write # Required for OIDC authentication with Azure
contents: read # Required to checkout code (default permission)
pull-requests: write # Required to post preview URLs as PR comments
# Environment variables used across all jobs
# Customize these for your project
env:
OUTPUT_LOCATION: 'dist' # Astro's default build output directory
STATIC_WEB_APP_NAME: 'swa-website' # Change to your SWA name
NODE_VERSION: '20' # Node.js version for your project
PNPM_VERSION: '9' # pnpm version (change to npm/yarn if needed)
jobs:
# ===================================================================
# Job 1: Build and Test
# ===================================================================
# This job builds your Astro site and runs tests to ensure code quality
# before deployment. The build artifact is uploaded for use in later jobs.
# ===================================================================
build_and_test:
name: Build & Test
runs-on: ubuntu-latest
steps:
# Check out the repository code
- name: Checkout
uses: actions/checkout@v4
# Setup pnpm package manager
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
# Setup Node.js with caching for faster subsequent runs
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm' # Change to 'npm' or 'yarn' if using those
# Install project dependencies
# --frozen-lockfile ensures reproducible builds
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Run test suite to catch bugs before deployment
# Remove this step if you don't have tests configured
- name: Run tests
run: pnpm test:run
# Build the Astro site
# This creates optimized static files in the dist/ directory
- name: Build Astro site
run: pnpm build
# Upload build artifacts for deployment jobs
# This allows us to reuse the build across multiple deployment environments
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.OUTPUT_LOCATION }}
path: ${{ env.OUTPUT_LOCATION }}/
# ===================================================================
# Job 2: Deploy Infrastructure (Optional)
# ===================================================================
# This job deploys Azure infrastructure using Bicep (Infrastructure as Code).
# It creates or updates the Static Web App resource in Azure.
#
# Optional: If your Static Web App is already created manually in Azure Portal,
# you can remove this entire job and skip to deploy_preview/deploy_production.
# Just make sure to set STATIC_WEB_APP_NAME env variable to match your existing resource.
# ===================================================================
deploy_infrastructure:
name: Deploy Infrastructure
runs-on: ubuntu-latest
needs: build_and_test # Only runs after successful build
# Run for pushes to main, scheduled runs, manual triggers, and PRs
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
outputs:
# Output values that other jobs can reference
static_web_app_name: ${{ steps.deploy-bicep.outputs.staticWebAppName }}
default_hostname: ${{ steps.get-hostname.outputs.hostname }}
steps:
- name: Checkout
uses: actions/checkout@v4
# Authenticate to Azure using OIDC (OpenID Connect)
# This is more secure than using a Service Principal secret
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Deploy Bicep template to create/update Azure Static Web App
# Your Bicep file should be at ./infra/main.bicep
# Customize the template path if your structure is different
- name: Deploy Bicep Template
id: deploy-bicep
uses: azure/arm-deploy@v2
with:
resourceGroupName: ${{ secrets.AZURE_RG_NAME }}
template: ./infra/main.bicep
failOnStdErr: false
# Retrieve the Static Web App's hostname for preview URL generation
- name: Get Static Web App Hostname
id: get-hostname
run: |
HOSTNAME=$(az staticwebapp show \
--name ${{ env.STATIC_WEB_APP_NAME }} \
--resource-group ${{ secrets.AZURE_RG_NAME }} \
--query "defaultHostname" -o tsv)
echo "hostname=$HOSTNAME" >> $GITHUB_OUTPUT
# ===================================================================
# Job 3: Deploy to Preview Environment
# ===================================================================
# This job deploys your Astro site to a preview environment when a PR
# is opened or updated. Each PR gets its own isolated preview URL.
# ===================================================================
deploy_preview:
name: Deploy to Preview
runs-on: ubuntu-latest
needs: deploy_infrastructure # Requires infrastructure to be deployed first
# Only run for PR events (opened, synchronized, or reopened)
if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened')
steps:
- name: Checkout
uses: actions/checkout@v4
# Download the build artifact created in the build_and_test job
# This avoids rebuilding the site in each deployment job
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.OUTPUT_LOCATION }}
path: ${{ env.OUTPUT_LOCATION }}/
# Authenticate to Azure for deployment
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Install the Azure Static Web Apps CLI for deployment
- name: Install SWA CLI
run: npm install -g @azure/static-web-apps-cli
# Deploy the built site to a preview environment
# SWA automatically creates a preview environment for each PR
- name: Deploy to Preview Environment
run: |
swa deploy ${{ env.OUTPUT_LOCATION }} \
--resource-group ${{ secrets.AZURE_RG_NAME }} \
--app-name ${{ env.STATIC_WEB_APP_NAME }}
# Generate the preview URL for this PR
# Azure SWA preview URLs follow the pattern: https://{base}-{pr-number}.{region}.azurestaticapps.net
- name: Get Preview URL
id: preview-url
run: |
DEFAULT_HOSTNAME="${{ needs.deploy_infrastructure.outputs.default_hostname }}"
PR_NUMBER="${{ github.event.pull_request.number }}"
# Extract the base domain and construct preview URL
PREVIEW_URL=$(echo "$DEFAULT_HOSTNAME" | sed "s/^/https:\/\//" | sed "s/\./-${PR_NUMBER}./")
echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT
echo "### Preview Deployment" >> $GITHUB_STEP_SUMMARY
echo "Preview URL: $PREVIEW_URL" >> $GITHUB_STEP_SUMMARY
# Post a comment on the PR with the preview URL
# This makes it easy for reviewers to see the live preview
- name: Comment Preview URL on PR
uses: actions/github-script@v7
with:
script: |
const previewUrl = '${{ steps.preview-url.outputs.preview_url }}';
const comment = `**Preview deployment completed!**
**Preview URL:** ${previewUrl}
_Note: The preview environment is automatically created for this pull request._
**Next steps:**
- Review your changes at the preview URL
- Get your PR reviewed and approved
- Merge this PR to automatically deploy to production`;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: comment
});
# ===================================================================
# Job 4: Deploy to Production
# ===================================================================
# This job deploys your Astro site to the production environment.
# It runs when code is merged to main, on scheduled runs, or manual triggers.
# ===================================================================
deploy_production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy_infrastructure # Requires infrastructure to be deployed first
# Only run for main branch pushes, scheduled runs, or manual triggers
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout
uses: actions/checkout@v4
# Download the build artifact created in the build_and_test job
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.OUTPUT_LOCATION }}
path: ${{ env.OUTPUT_LOCATION }}/
# Authenticate to Azure for production deployment
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Install the Azure Static Web Apps CLI
- name: Install SWA CLI
run: npm install -g @azure/static-web-apps-cli
# Deploy to the production environment
# The --env production flag ensures deployment to the main production slot
- name: Deploy to Production Environment
run: |
swa deploy ${{ env.OUTPUT_LOCATION }} \
--resource-group ${{ secrets.AZURE_RG_NAME }} \
--app-name ${{ env.STATIC_WEB_APP_NAME }} \
--env production
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment