Skip to content

chore: Storage v2 removal #4217

chore: Storage v2 removal

chore: Storage v2 removal #4217

Workflow file for this run

name: ci
on:
pull_request:
merge_group:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
full-pr:
# Fork PRs execute untrusted repository code. Keep them off Depot runners,
# which inject DEPOT_CACHE_TOKEN independently of GitHub secret filtering.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork && 'ubuntu-24.04' || 'depot-ubuntu-24.04-16' }}
timeout-minutes: 120
permissions:
contents: read
# Required to mint the OIDC token for Workload Identity Federation when
# authenticating to the Spanner test instance (trusted runs only).
id-token: write
env:
# Moon marks failed tasks by color alone, so a failed `moon ci` can end at
# "Tasks: 1 failed" with no named target. The summary names them in plain
# text and replays each failed task's own output. Green runs pay only a
# pass/fail list; the replay section is empty when nothing failed.
MOON_SUMMARY: detailed
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
- uses: pnpm/action-setup@v5
with:
run_install: false
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
- name: Install dependencies
run: corepack pnpm install --frozen-lockfile
# Bare `moon` below is the workspace's own pinned @moonrepo/cli, so CI
# runs the exact version the lockfile resolves — same as local dev.
- name: Put workspace binaries on PATH
run: echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH"
# A PR title is attacker-controlled text on a public repo, so it reaches the
# script through the environment — never interpolated into the shell body.
- name: Check PR title
if: ${{ github.event_name == 'pull_request' }}
env:
PR_TITLE: ${{ github.event.pull_request.title }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: node scripts/check-pr-title.mjs --title "$PR_TITLE" --base "$BASE_SHA" --summary
# Emits `mode` plus per-lane affected gates (go_tests, snapshot,
# journeys, suites_*, browsers) computed from moon's affected task
# selection. Gates fail open — unclaimed files, empty diffs, and query
# failures all force a full run. Logic + tests: scripts/ci-mode.mjs.
- name: Detect CI mode
id: ci-mode
run: node scripts/ci-mode.mjs "${{ github.event.pull_request.base.sha || 'origin/main' }}"
- name: Check Go generated files
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' }}
run: moon run server:check-generate
- name: Cache Playwright browsers
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.browsers == 'true' }}
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright Chromium
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.browsers == 'true' }}
run: corepack pnpm --filter @zitadel/components exec playwright install chromium
- name: Run Moon build and test graph
id: moon-ci
if: ${{ steps.ci-mode.outputs.mode == 'full' }}
run: moon ci :lint :typecheck :build :test :test-browser :check-adrs
- name: Report Moon CI failures
if: ${{ failure() && steps.moon-ci.conclusion == 'failure' }}
run: node scripts/report-moon-failures.mjs
- name: Run Go tests
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' }}
run: moon run server:test
- name: Run Go integration tests (postgres)
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' }}
run: moon run server:test-postgres
# Auth when SPANNER_TEST_INSTANCE is set (trusted runs). Vars are safe in
# if:; secrets stay only in with:. Unset var / forks → emulator step.
# Auth hard-fails if the var is set but WIF is broken (no silent fallback).
- name: Authenticate to Google Cloud (Spanner test instance)
id: spanner-auth
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork) && vars.SPANNER_TEST_INSTANCE != '' }}
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- name: Run Go integration tests (spanner emulator)
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' && steps.spanner-auth.outcome != 'success' }}
run: moon run server:test-spanner
- name: Run Go integration tests (spanner test instance)
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' && steps.spanner-auth.outcome == 'success' }}
env:
ZITADEL_TEST_SPANNER_INSTANCE: ${{ vars.SPANNER_TEST_INSTANCE }}
run: moon run server:test-spanner
- name: Run Go integration tests (sqlite)
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.go_tests == 'true' }}
run: moon run server:test-sqlite
# The snapshot exists here to feed the journeys, so it shares their gate
# (the tarball handoff is a filesystem contract moon cannot see).
- name: Build release snapshot without container
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.snapshot == 'true' }}
run: env -u CI -u GITHUB_ACTIONS moon run release:snapshot -- --skip-container
- name: Run binary fresh-app journey
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.journeys == 'true' }}
run: |
version="$(node -p "require('./apps/server/package.json').version")"
env -u CI -u GITHUB_ACTIONS moon run cli-journey-e2e:e2e-local -- \
--runtime binary \
--concurrency 5 \
--work-dir "${RUNNER_TEMP}/ci-journey" \
--tarballs-dir "dist/release/${version}/npm"
# One framework is enough here: the preset decides the scaffolded
# flow shape, not the SDK — the matrix above already covers SDKs.
- name: Run passkey-first preset journey
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.journeys == 'true' }}
run: |
version="$(node -p "require('./apps/server/package.json').version")"
env -u CI -u GITHUB_ACTIONS moon run cli-journey-e2e:e2e-local -- \
--runtime binary \
--framework next \
--preset passkey-first \
--work-dir "${RUNNER_TEMP}/ci-journey-passkey-first" \
--tarballs-dir "dist/release/${version}/npm"
# Customer-configuration proof for @zitadel/testing: a fresh app
# installs the kit from the journey registry and runs its withZitadel()
# suite against the published binary — embedded UIs, no repo env
# overrides (unlike the in-repo e2e-real lanes below).
- name: Run test-kit consumer journey
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.journeys == 'true' }}
run: |
version="$(node -p "require('./apps/server/package.json').version")"
env -u CI -u GITHUB_ACTIONS moon run cli-journey-e2e:e2e-testkit -- \
--work-dir "${RUNNER_TEMP}/ci-journey-testkit" \
--tarballs-dir "dist/release/${version}/npm"
# Gates the test-kit's central promise: boot a real seeded instance,
# then drive the real browser login with per-test users. Reuses the Go
# build cache and Playwright Chromium installed above.
- name: Run @zitadel/testing real-instance suites
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.suites_testing_demo == 'true' }}
run: env -u CI -u GITHUB_ACTIONS moon run testing:test-integration demo-next-e2e:e2e-real
# Dogfoods @zitadel/testing through a second consumer. Keep this separate
# from the demo suite so two local-server instances do not contend.
- name: Run console real-instance suite
if: ${{ steps.ci-mode.outputs.mode == 'full' && steps.ci-mode.outputs.suites_console == 'true' }}
run: env -u CI -u GITHUB_ACTIONS moon run console-e2e:e2e-real
- name: Validate version PR
if: ${{ steps.ci-mode.outputs.mode == 'version-only' }}
run: env -u CI -u GITHUB_ACTIONS moon run release:version
- name: Pack version PR artifacts
if: ${{ steps.ci-mode.outputs.mode == 'version-only' }}
run: env -u CI -u GITHUB_ACTIONS moon run release:pack
- name: Verify version PR tarballs
if: ${{ steps.ci-mode.outputs.mode == 'version-only' }}
run: |
version="$(node -p "require('./apps/server/package.json').version")"
node apps/cli-journey-e2e/scripts/verify-tarballs.mjs "dist/release/${version}/npm"
- name: Upload release snapshot
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: release-snapshot
path: dist/release
if-no-files-found: ignore
retention-days: 7
- name: Upload journey diagnostics
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: journey-diagnostics
path: |
${{ runner.temp }}/ci-journey/diagnostics
${{ runner.temp }}/ci-journey-passkey-first/diagnostics
${{ runner.temp }}/ci-journey-testkit/diagnostics
if-no-files-found: ignore
retention-days: 7
# The handshake is deliberately excluded: it carries the project secret.
- name: Upload console e2e diagnostics
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: console-e2e-diagnostics
path: |
apps/console-e2e/test-results
apps/console-e2e/playwright-report
if-no-files-found: ignore
retention-days: 7