Skip to content

design(icon): Redesigned ringdrill icon #21

design(icon): Redesigned ringdrill icon

design(icon): Redesigned ringdrill icon #21

Workflow file for this run

name: Deploy web
# Production deploys to Netlify. The Flutter web build runs here instead of
# inside Netlify so we can:
# - upload sources and source maps to Sentry for unobfuscated stack traces;
# - keep Netlify build minutes available (Netlify only ships the artifact);
# - cache pub-cache and Flutter SDK across runs for faster iteration.
#
# Required secrets, all on the PROD environment
# (Settings -> Environments -> PROD -> Secrets):
# SENTRY_AUTH_TOKEN - Sentry user/org token with the `project:releases`
# scope. Read by sentry_dart_plugin to associate the
# uploaded files with the release defined in
# pubspec.yaml.
# NETLIFY_AUTH_TOKEN - Personal access token from Netlify
# (User settings -> Applications).
# NETLIFY_SITE_ID - Project ID, formerly known as Site ID
# (Netlify dashboard -> Project configuration ->
# General -> Project information -> Project ID).
# The environment variable name is still
# NETLIFY_SITE_ID; only the UI label changed.
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: deploy-web
cancel-in-progress: true
jobs:
build-and-deploy:
name: Build and deploy
runs-on: ubuntu-latest
# The deploy targets the PROD environment in GitHub
# (Settings -> Environments -> PROD). All required secrets
# (SENTRY_AUTH_TOKEN, NETLIFY_AUTH_TOKEN, NETLIFY_SITE_ID) live on
# that environment, so declaring it here is what makes them visible
# to the job. Environment scope also lets us layer on required
# reviewers or deploy windows later without touching the workflow.
environment: PROD
timeout-minutes: 25
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
with:
# sentry_dart_plugin reads git refs when stamping the release.
# Shallow clones drop those refs and break the upload.
fetch-depth: 0
- name: Verify required secrets are present
# Fail fast if any secret is empty. Without this, the job runs
# all the way through `flutter build web` (5+ minutes) before
# discovering that, for example, SENTRY_AUTH_TOKEN was added
# under Environment secrets instead of Repository secrets and
# resolves to an empty string. The length print never reveals
# the value — GitHub still masks it through ${#…} — but tells
# us whether the variable arrived at all.
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
run: |
missing=()
[ -z "$SENTRY_AUTH_TOKEN" ] && missing+=("SENTRY_AUTH_TOKEN")
[ -z "$NETLIFY_AUTH_TOKEN" ] && missing+=("NETLIFY_AUTH_TOKEN")
[ -z "$NETLIFY_SITE_ID" ] && missing+=("NETLIFY_SITE_ID")
if [ ${#missing[@]} -ne 0 ]; then
echo "::error::Missing or empty repository secrets: ${missing[*]}"
echo "Check Settings -> Secrets and variables -> Actions -> Repository secrets."
echo "Environment-scoped secrets are NOT visible here unless the job"
echo "declares 'environment: <name>'."
exit 1
fi
echo "SENTRY_AUTH_TOKEN length: ${#SENTRY_AUTH_TOKEN}"
echo "NETLIFY_AUTH_TOKEN length: ${#NETLIFY_AUTH_TOKEN}"
echo "NETLIFY_SITE_ID length: ${#NETLIFY_SITE_ID}"
- name: Setup Flutter
# Pinned to a release tag rather than @v2. The mutable @v2 tag
# has previously pointed at SHAs that codeload could no longer
# serve, breaking the workflow with "An action could not be
# found at the URI ...". A concrete tag is fetched fresh and
# documents which version we tested against.
uses: subosito/flutter-action@v2.23.0
with:
channel: stable
cache: true
- name: Cache pub-cache and .dart_tool
uses: actions/cache@v5
with:
path: |
~/.pub-cache
.dart_tool
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
restore-keys: |
pub-${{ runner.os }}-
- name: Install Flutter dependencies
run: flutter pub get
- name: Setup Node for Netlify functions
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: npm
- name: Install function dependencies
# Netlify CLI bundles functions from node_modules/, so the deps
# must be installed before the deploy step picks them up.
run: npm ci
- name: Build web
# Compiles the web bundle with source maps and stages the
# assetlinks.json file. The same command works locally for a
# one-off prod build sanity check.
run: make build-web
- name: Upload sources and source maps to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: make upload-symbols-web
- name: Strip source maps from published artifact
# Source maps were uploaded to Sentry in the previous step.
# Serving them from the public CDN would expose the original
# Dart source to anyone who opens DevTools, so drop them
# before deploy.
run: make strip-source-maps-web
- name: Deploy to Netlify
# Use the first-party Netlify CLI (installed as a devDependency
# in package.json) rather than a third-party deploy action. The
# action wrappers around the Netlify API have repeatedly bitten
# us: they ignore the [functions] block in netlify.toml, and
# their bundled zip-it-and-ship-it is old enough that it cannot
# resolve modern `node:`-prefixed builtin imports. The CLI
# reads netlify.toml natively (functions, redirects, headers)
# and stays in lockstep with what Netlify's own builds use.
#
# DEPLOY_MESSAGE is passed via env, not interpolated into the
# shell, so a commit message with quotes/backticks/newlines does
# not break --message= or worse get interpreted as code.
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
DEPLOY_MESSAGE: ${{ github.event.head_commit.message }}
run: |
npx netlify deploy \
--prod \
--dir=build/web \
--message="$DEPLOY_MESSAGE"
- name: Wait for Netlify CDN to propagate
# `netlify deploy --prod` returns the moment the new artifact
# is committed on origin, but the global edge network needs
# a few seconds to invalidate caches. Without this wait the
# Lighthouse step below has occasionally audited a stale HTML
# (e.g. flagged the old meta-viewport even though the new one
# was already live on origin), giving misleading reports.
run: sleep 30
- name: Lighthouse audit
# Runs against the production URL so the score reflects the live
# CDN, headers, service worker, and edge functions — not just
# the static bundle in isolation. Reports are uploaded to
# lighthouse's temporary public storage; the action log prints
# clickable links to each report. Three runs per URL, median
# taken — single-run scores have too much variance to compare
# over time.
#
# No `assert` / threshold gate yet: baseline a few runs first
# so we know what "good" looks like for RingDrill before
# failing deploys on regression. Add `configPath:` and a
# lighthouserc.json once we want enforcement.
uses: treosh/lighthouse-ci-action@v12
with:
urls: |
https://ringdrill.app/
https://ringdrill.app/program
https://ringdrill.app/map
runs: 3
uploadArtifacts: true
temporaryPublicStorage: true