Skip to content
Draft
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
50 changes: 38 additions & 12 deletions .github/workflows/release-studio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,36 @@ env:
IMAGE_NAME: ${{ github.repository }}/studio
NGINX_IMAGE_NAME: ${{ github.repository }}/studio-nginx

# Runner selection, driven by repository variables so the release path can be
# moved onto self-hosted runners — and moved back — without a PR. All three
# default to the hosted runners this workflow has always used, so an unset
# variable is a no-op.
#
# CI_RUNNER_RELEASE non-docker jobs (build-dist, publish-npm,
# release, bump-deco-apps-cd, notify-docs-agent)
# CI_RUNNER_RELEASE_DIND docker jobs, linux/amd64
# CI_RUNNER_RELEASE_DIND_ARM64 docker jobs, linux/arm64
#
# Deliberately SEPARATE from CI_RUNNER_LINUX in test.yml. The risk is
# asymmetric: a stuck PR check annoys one author, a stuck release blocks
# shipping. Prove the checks on self-hosted for a week before touching these.
#
# The arm64 variable exists because this matrix builds arm64 NATIVELY on
# ubuntu-24.04-arm rather than under QEMU. Pointing that leg at an amd64
# runner would silently regress it to emulation and roughly triple the build,
# so it must resolve to a genuinely arm64 scale set or stay hosted.
#
# `prepare` is grouped with the docker jobs, not the light ones: it runs
# docker/login-action and `docker manifest inspect`, which need the docker CLI
# that the stock ARC runner image only has wired up in dind mode.
#
# Rollback for any of them: `gh variable delete <NAME> --repo decocms/studio`,
# effective on the next run.

jobs:
prepare:
name: Prepare release metadata
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE_DIND || 'ubuntu-latest' }}
# Releases are cut by the [release]: version-bump commit that release-tagging
# pushes to main (the bot push uses the App token, which re-triggers
# workflows). We run on EVERY push here — a non-bump push just reads an
Expand Down Expand Up @@ -114,7 +140,7 @@ jobs:
needs.prepare.outputs.version-changed == 'true' ||
needs.prepare.outputs.image-exists == 'false' ||
needs.prepare.outputs.nginx-image-exists == 'false'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE || 'ubuntu-latest' }}
permissions:
contents: read
steps:
Expand Down Expand Up @@ -174,7 +200,7 @@ jobs:
name: Publish to NPM
needs: [prepare, build-dist]
if: needs.prepare.outputs.version-changed == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE || 'ubuntu-latest' }}
permissions:
contents: read
id-token: write
Expand Down Expand Up @@ -221,10 +247,10 @@ jobs:
include:
- arch: amd64
platform: linux/amd64
runner: ubuntu-latest
runner: ${{ vars.CI_RUNNER_RELEASE_DIND || 'ubuntu-latest' }}
- arch: arm64
platform: linux/arm64
runner: ubuntu-24.04-arm
runner: ${{ vars.CI_RUNNER_RELEASE_DIND_ARM64 || 'ubuntu-24.04-arm' }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -296,10 +322,10 @@ jobs:
include:
- arch: amd64
platform: linux/amd64
runner: ubuntu-latest
runner: ${{ vars.CI_RUNNER_RELEASE_DIND || 'ubuntu-latest' }}
- arch: arm64
platform: linux/arm64
runner: ubuntu-24.04-arm
runner: ${{ vars.CI_RUNNER_RELEASE_DIND_ARM64 || 'ubuntu-24.04-arm' }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -360,7 +386,7 @@ jobs:
always() &&
needs.prepare.outputs.image-exists == 'false' &&
needs.build-docker.result == 'success'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE_DIND || 'ubuntu-latest' }}
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -419,7 +445,7 @@ jobs:
always() &&
needs.prepare.outputs.nginx-image-exists == 'false' &&
needs.build-nginx-docker.result == 'success'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE_DIND || 'ubuntu-latest' }}
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -485,7 +511,7 @@ jobs:
(needs.publish-npm.result == 'success' || needs.publish-npm.result == 'skipped') &&
(needs.merge-docker.result == 'success' || needs.merge-docker.result == 'skipped') &&
(needs.merge-nginx-docker.result == 'success' || needs.merge-nginx-docker.result == 'skipped')
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE || 'ubuntu-latest' }}
permissions:
contents: write
steps:
Expand Down Expand Up @@ -578,7 +604,7 @@ jobs:
needs.merge-nginx-docker.result == 'success'
)
)
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE || 'ubuntu-latest' }}
# Reuses DEPLOY_REPO_TOKEN — the decobot machine-user PAT already used to
# dispatch the Studio image bump (non-personal; decobot is admin on
# decocms/deco-apps-cd). Same bot, same mechanism as the legacy repo, no
Expand Down Expand Up @@ -633,7 +659,7 @@ jobs:
name: Notify docs agent
needs: [prepare, release]
if: always() && needs.release.result == 'success'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_RELEASE || 'ubuntu-latest' }}
env:
WEBHOOK_URL: ${{ vars.DOCS_AGENT_WEBHOOK_URL }}
WEBHOOK_TOKEN: ${{ secrets.DOCS_AGENT_WEBHOOK_TOKEN }}
Expand Down
39 changes: 34 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# Runner selection for the four cheap-but-queued jobs below (format, lint,
# typecheck, test). Resolved from a repository variable so the pilot can be
# switched on and rolled back WITHOUT a PR:
#
# on: gh variable set CI_RUNNER_LINUX --body deco-linux-amd64 --repo decocms/studio
# off: gh variable delete CI_RUNNER_LINUX --repo decocms/studio
#
# Unset resolves to ubuntu-latest, so this file is a no-op until someone sets
# the variable. That matters because these are required checks: if the
# self-hosted scale set is unavailable, jobs targeting its label sit queued
# until GitHub's 24h timeout rather than failing fast, and nothing merges.
# Deleting the variable restores hosted runners on the next run.
#
# Why these five: they have waited up to ~90 min at the p99 (hosted
# concurrency ceiling saturating at ~915 runs/day) for 0.4-2 min of work. They
# also need nothing beyond checkout + setup-bun, so they run on the stock ARC
# runner image — no sudo, no docker, no privileged sidecar.
#
# Two jobs stay hosted:
# changes 1 min, and it gates the rest — keeping it hosted means
# a scale-set outage still produces a run that reports.
# web-component-tests `playwright install --with-deps` / `install-deps`
# shell out to sudo apt-get, which the stock runner
# image has no root for. Needs a custom image first.
#
# Inlined per job rather than hoisted into `env:` because `runs-on` cannot read
# the `env` context — only `github`, `needs`, `strategy`, `matrix`, `inputs`
# and `vars`.

jobs:
# Gate: skip the (required) check jobs when a PR only touches workflow YAML,
# docs, or markdown. Required checks can't be skipped via workflow-level
Expand Down Expand Up @@ -88,7 +117,7 @@ jobs:
format:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -102,7 +131,7 @@ jobs:
lint:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -121,7 +150,7 @@ jobs:
typecheck:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -140,7 +169,7 @@ jobs:
test:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }}
# The suite normally finishes in ~1 min. A single hung file under the
# parallel runner stalls the whole job silently (per-file output is
# buffered, so the culprit prints nothing) — without this bound that's
Expand All @@ -162,7 +191,7 @@ jobs:
build:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down