Skip to content
Merged
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
125 changes: 123 additions & 2 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,134 @@ inputs:
fails with a path validation error.
required: false
default: "true"
tools:
description: >-
Space-separated mise tools to install instead of every tool in
mise.toml.
required: false
default: ""
pnpm-store-cache:
description: >-
Whether to restore the pnpm store through actions/cache. Where the store
archive restores slower than the registry downloads it, disable this.
required: false
default: "true"
sticky-disks:
description: >-
Whether to mount dependency caches from Blacksmith sticky disks on
Blacksmith Linux runners. Disks are read-only unless commit-sticky-disks
is set.
required: false
default: "false"
commit-sticky-disks:
description: >-
Whether this job writes its sticky disks back. Only the fill workflow
sets it, so test jobs never race it with partial snapshots.
required: false
default: "false"
docker-images:
description: >-
Whether to keep Docker's image store on a Blacksmith sticky disk so
stack images are not pulled again on every job. Requires sticky-disks.
Callers must stop Docker in a final if: always() step so the disk
unmounts cleanly.
required: false
default: "false"
native-artifacts:
description: >-
Whether to keep the stack tests' native service artifacts on a
Blacksmith sticky disk. Requires sticky-disks.
required: false
default: "false"

runs:
using: "composite"
steps:
# Sticky disks are shared by every run in the repository, so only the fill
# job on trusted events (pushes, schedules, manual runs) commits; pull request
# and merge queue code could otherwise plant dependencies or images that
# trusted runs later execute.
# Committing runs prune what the current lockfile no longer uses; bump the
# -vN key segment to reset a disk outright.
- name: Detect sticky disks
id: sticky
if: inputs.sticky-disks == 'true'
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
COMMIT: ${{ inputs.commit-sticky-disks }}
run: |
if [ "$RUNNER_OS" != Linux ]; then
exit 0
fi
if [ -z "${BLACKSMITH_RUNNER_MESSAGE_WAIT_MS:-}" ]; then
echo "::notice::Sticky disks requested but this is not a Blacksmith runner; using actions/cache."
exit 0
fi
echo "enabled=true" >> "$GITHUB_OUTPUT"
case "$COMMIT:$EVENT_NAME" in
true:push | true:schedule | true:workflow_dispatch) echo "commit=on-change" >> "$GITHUB_OUTPUT" ;;
*) echo "commit=false" >> "$GITHUB_OUTPUT" ;;
esac

- name: Stop Docker before mounting its image disk
if: inputs.docker-images == 'true' && steps.sticky.outputs.enabled == 'true'
shell: bash
run: sudo systemctl stop docker.socket docker.service

- name: Mount Docker image disk
if: inputs.docker-images == 'true' && steps.sticky.outputs.enabled == 'true'
uses: useblacksmith/stickydisk@b6cb58d1d31d6d32a25248fab12493d831f7bd48 # v1.7.2
with:
key: ${{ github.repository }}-docker-images-v3-${{ runner.arch }}
path: /var/lib/docker
commit: ${{ steps.sticky.outputs.commit }}

- name: Restart Docker on the image disk
if: inputs.docker-images == 'true' && steps.sticky.outputs.enabled == 'true'
shell: bash
run: |
sudo systemctl start docker.socket docker.service
docker info --format '{{.DockerRootDir}} {{.Driver}}'
docker image ls --format '{{.Repository}}:{{.Tag}}' | wc -l

- name: Locate native artifact cache
id: native-artifacts
if: inputs.native-artifacts == 'true' && steps.sticky.outputs.enabled == 'true'
shell: bash
run: echo "path=${XDG_CACHE_HOME:-$HOME/.cache}/supabase-stack-artifacts" >> "$GITHUB_OUTPUT"

- name: Mount native artifact disk
if: inputs.native-artifacts == 'true' && steps.sticky.outputs.enabled == 'true'
uses: useblacksmith/stickydisk@b6cb58d1d31d6d32a25248fab12493d831f7bd48 # v1.7.2
with:
key: ${{ github.repository }}-stack-artifacts-v2-${{ runner.arch }}
path: ${{ steps.native-artifacts.outputs.path }}
commit: ${{ steps.sticky.outputs.commit }}

- name: Install toolchains
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
with:
version: 2026.9.0
install_args: ${{ inputs.tools }}
cache_key: ${{ inputs.tools != '' && format('mise-{0}-{1}-{2}-{3}', runner.os, runner.arch, inputs.tools, hashFiles('mise.toml', 'mise.lock', '.node-version', '.bun-version', 'package.json')) || '' }}

- name: Resolve pnpm store path
if: inputs.dependency-cache == 'true'
id: pnpm-store
shell: bash
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
run: |
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"

# A sticky disk mounts a block-level snapshot in seconds, while
# actions/cache downloads and untars the whole store on every job.
- name: Mount pnpm store disk
if: inputs.dependency-cache == 'true' && steps.sticky.outputs.enabled == 'true'
uses: useblacksmith/stickydisk@b6cb58d1d31d6d32a25248fab12493d831f7bd48 # v1.7.2
with:
key: ${{ github.repository }}-pnpm-store-v3-${{ runner.arch }}
path: ${{ steps.pnpm-store.outputs.path }}
commit: ${{ steps.sticky.outputs.commit }}

# Cache only the content-addressable half of the store (package files plus
# the SQLite index), never the global virtual store under links/. That
Expand All @@ -42,7 +156,7 @@ runs:
# so restores never match the earlier whole-store archives, which still
# carry a links/ tree.
- name: Configure pnpm dependency cache
if: inputs.dependency-cache == 'true'
if: inputs.dependency-cache == 'true' && inputs.pnpm-store-cache == 'true' && steps.sticky.outputs.enabled != 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
Expand Down Expand Up @@ -91,3 +205,10 @@ runs:
echo "//firewall.depthfirst.com/npm/:_authToken=${DEPENDENCY_FIREWALL_TOKEN}"
} > "$npmrc"
NPM_CONFIG_USERCONFIG="$npmrc" pnpm install --frozen-lockfile

# Pruning keeps only this checkout's packages, so only the default branch
# prunes; other branches would evict each other's dependencies.
- name: Prune pnpm store disk
if: inputs.dependency-cache == 'true' && steps.sticky.outputs.commit == 'on-change' && github.ref_name == (github.event.repository.default_branch || 'develop')
shell: bash
run: pnpm store prune
88 changes: 88 additions & 0 deletions .github/workflows/fill-sticky-disks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Fill sticky disks

# Test jobs mount the pnpm and Docker image caches from Blacksmith sticky
# disks but pull request runs never commit them. This workflow is the trusted
# writer: it runs on protected branches, which are the only refs Blacksmith's
# sticky-disk branch protection lets commit.
on:
push:
branches:
- develop
- next
- main
schedule:
- cron: "23 4 * * *"
workflow_dispatch:

permissions:
contents: read

concurrency:
# Every ref writes the same repository-wide disks, so fills run one at a time.
group: fill-sticky-disks
cancel-in-progress: false

jobs:
fill:
name: Fill sticky disks
# Manual dispatches from other branches would commit unreviewed caches.
if: contains(fromJSON('["refs/heads/develop", "refs/heads/next", "refs/heads/main"]'), github.ref)
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 60
steps:
- name: Checkout
uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1.0.0-beta

- name: Setup
uses: ./.github/actions/setup
with:
dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }}
sticky-disks: "true"
commit-sticky-disks: "true"
docker-images: "true"
native-artifacts: "true"

# The nightly fill clears artifacts of older service versions.
- name: Download native stack artifacts
run: |
if [ "$GITHUB_EVENT_NAME" = schedule ]; then
rm -rf "${XDG_CACHE_HOME:-$HOME/.cache}/supabase-stack-artifacts/"*
fi
pnpm --filter @supabase/stack prepare:test-artifacts

- name: Build CLI
run: pnpm exec turbo run supabase#build

# Starting a default project pulls every image `supabase start` uses.
- name: Pull CLI images
env:
SUPABASE_CLI_BINARY_OVERRIDE: ${{ github.workspace }}/apps/cli/dist/supabase
run: |
project="$(mktemp -d)"
cli="$GITHUB_WORKSPACE/apps/cli/dist/supabase.js"
cd "$project"
node "$cli" init
node "$cli" start
# The nightly fill drops images of older versions; the next step pulls the catalog back.
if [ "$GITHUB_EVENT_NAME" = schedule ]; then
docker image prune --all --force
fi
node "$cli" stop --no-backup

- name: Pull stack package images
run: |
pnpm --silent --filter @supabase/stack prepare:test-artifacts --images |
xargs --max-procs 4 --max-args 1 docker pull --quiet

# Also runs after a failed step so leftover stack containers and volumes never persist.
- name: Prune Docker before saving its image disk
if: always()
run: |
docker ps --all --quiet | xargs --no-run-if-empty docker rm --force
docker system prune --force
docker volume prune --all --force

# The image disk only commits once nothing holds /var/lib/docker open.
- name: Stop Docker so its image disk can be saved
if: always()
run: sudo systemctl stop docker.socket docker.service
2 changes: 1 addition & 1 deletion .github/workflows/github-scripts-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
test:
name: Test and type-check
runs-on: ubuntu-latest
# The shared setup installs the full workspace + Go toolchain via mise, which
# The shared setup installs the full workspace toolchain via mise, which
# runs ~9-10 min; a 10-minute cap raced the install and got cancelled on a
# cold cache. 20 gives that install headroom. (This check is heavier than it
# needs to be for two scripts — slimming the setup is a possible follow-up.)
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/run-ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: run-ci

# Opt-in full develop CI for PRs that Test.yml does not already cover: stacked
# PRs (base is not develop, main, next or v*.x) and drafts. Ready PRs into
# those stay on the existing workflows so required check names and concurrency
# are unchanged.
# Opt-in full CI, Windows and macOS included, for drafts, stacked PRs, and
# ready PRs into develop or next. Ready PRs into main or v*.x already run
# everything through Test.yml. On ready PRs the Linux jobs run a second time
# alongside Test.yml's own run.
#
# Add the `run-ci` label to start the suite; remove it to cancel in-progress
# runs via this workflow's concurrency group. Other labels do not retrigger.
Expand Down Expand Up @@ -43,13 +43,14 @@ jobs:
!startsWith(github.head_ref, 'release-notes/') &&
contains(github.event.pull_request.labels.*.name, 'run-ci') &&
(github.event.pull_request.draft ||
(github.base_ref != 'develop' && github.base_ref != 'main' && github.base_ref != 'next' &&
(github.base_ref != 'main' &&
!(startsWith(github.base_ref, 'v') && endsWith(github.base_ref, '.x')))) &&
((github.event.action != 'labeled' &&
github.event.action != 'unlabeled') ||
github.event.label.name == 'run-ci')
uses: ./.github/workflows/test.yml
with:
force: true
full: true
secrets:
DF_FIREWALL_TOKEN: ${{ secrets.DF_FIREWALL_TOKEN }}
Loading
Loading