DEV Community

Cover image for Built a Lightweight GitHub Action for Deploying to Azure Static Web Apps
Tatsuro Shibamura for Polymind Inc.

Posted on

Built a Lightweight GitHub Action for Deploying to Azure Static Web Apps

TL;DR

I created shibayan/swa-deploy — a lightweight GitHub Action that only deploys to Azure Static Web Apps, without the Docker-based build overhead of the official action. It wraps the same StaticSitesClient that SWA CLI uses internally, includes automatic caching, and supports both Deployment Token and azure/login authentication.

The Problem with the Official Action

When deploying static sites (built with Astro, Vite, etc.) to Azure Static Web Apps, the standard approach is to use the official Azure/static-web-apps-deploy action that gets auto-generated when you link a GitHub repo to your SWA resource.

Unlike other Azure deployment actions (e.g., for App Service or Azure Functions), this action uses Oryx — the build engine used across Azure App Service — to build your application internally. It runs inside Docker, which means:

  • The build process is a black box with hidden behaviors
  • Docker image creation adds significant overhead
  • Customizing the build pipeline is limited

While Oryx is a capable multi-platform build engine, the Docker-based approach makes deploys slower than they need to be, especially when you just want to push pre-built static files.

The skip_app_build Option Isn't Enough

You can set skip_app_build: true in the official action to skip the application build step. This lets you build in GitHub Actions and only deploy through the action. However, it still needs to pull/build the Docker image, so the overhead remains.

For reference, the official documentation covers this configuration:

Build configuration for Azure Static Web Apps | Microsoft Learn

Using SWA CLI: Better, But Not Ideal

To avoid the Docker overhead, I switched to using the Static Web Apps CLI for deploy-only workflows. This approach works well — it's lighter than Docker — but comes with its own trade-offs:

  • Installation time: npm install -g @azure/static-web-apps-cli takes a while
  • Cache complexity: To speed things up, you need to add npm global cache steps, which adds more workflow YAML than the actual deploy
  • Cognitive overhead: You end up thinking more about caching npm packages than deploying your app

The Solution: shibayan/swa-deploy

I finally decided to build a dedicated GitHub Action that does one thing well: deploy to Azure Static Web Apps.

GitHub logo shibayan / swa-deploy

A GitHub Action to deploy prebuilt frontend assets and Azure Functions APIs to Azure Static Web Apps

Deploy Azure Static Web Apps

CI Coverage

A GitHub Action that deploys prebuilt frontend assets, Azure Functions APIs, and staticwebapp.config.json to Azure Static Web Apps.

It follows the same deployment model as swa deploy from the Azure Static Web Apps CLI — download the StaticSitesClient binary, resolve paths, and upload content using a deployment token. When deployment-token is omitted, this action can also resolve the token at runtime through Azure Resource Manager after azure/login The binary is cached automatically across workflow runs.

Note

This action does not build your application. Run your build step before calling this action.

Usage

Deploy a built frontend

name: Deploy
on:
  push:
    branches: [master]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm ci && npm run build

      - name: Deploy to Azure Static Web Apps
        id: deploy
Enter fullscreen mode Exit fullscreen mode

Under the hood, this action uses the same StaticSitesClient binary that SWA CLI uses. It's essentially a thin wrapper around that CLI with automatic caching built in.

Using a Deployment Token

The simplest approach — just pass your deployment token:

- name: Deploy to Azure Static Web Apps
  id: deploy
  uses: shibayan/swa-deploy@v1
  with:
    app-location: dist
    deployment-token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Using azure/login

If you prefer using azure/login with a service principal or federated credentials, pass app-name instead. The action will automatically look up the resource via the Azure Resource Manager API, retrieve the deployment token, and deploy:

- name: Azure login
  uses: azure/login@v2
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Deploy to Azure Static Web Apps
  uses: shibayan/swa-deploy@v1
  with:
    app-location: dist
    app-name: my-static-web-app
    resource-group-name: my-resource-group
Enter fullscreen mode Exit fullscreen mode

Note: The resource-group-name parameter is optional. You only need it if you have multiple SWA resources with the same name across different resource groups in your subscription — since SWA names are unique at the resource group scope, not the subscription scope.

Why Use This?

Compared to the official action and SWA CLI:

  • No Docker overhead — no image build step
  • Automatic caching — the StaticSitesClient binary is cached automatically
  • Simple workflow YAML — no npm cache boilerplate
  • Lightweight — does exactly one thing: deploy

I've migrated all of my SWA deployments to this action and it's been working great.

Implementation Note

The entire action was built using GitHub Copilot Chat with GPT-5.4 and Claude Opus 4.6, starting from the official template. AI-assisted development made it straightforward to implement and test.

Top comments (0)