Skip to content

test(release): pin collapsed Safari guidance #1865

test(release): pin collapsed Safari guidance

test(release): pin collapsed Safari guidance #1865

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
# Cancel in-progress runs for the same PR / branch
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Change detection ──────────────────────────────────────────────────
# Routes jobs by changed paths so docs-only changes skip extension and
# native builds. `core` is a catch-all: any file NOT classified as docs,
# app, native, or repo prose (markdown/meta) triggers the full CI.
changes:
name: Changes
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
docs: ${{ steps.filter.outputs.docs }}
app: ${{ steps.filter.outputs.app }}
native: ${{ steps.filter.outputs.native }}
core: ${{ steps.catchall.outputs.core }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- id: filter
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
docs:
- 'docs/**'
- 'CNAME'
- 'README.md'
- '.github/README_*.md'
- '.github/workflows/deploy-docs.yml'
- '.github/workflows/sponsors.yml'
- 'scripts/generate-sponsors.cjs'
- 'scripts/generate-sponsors.test.js'
- 'scripts/update-readme-badges.mjs'
- 'scripts/update-readme-badges.test.js'
- 'sponsorkit/**'
app:
- 'src/**'
- 'public/**'
# Read by src/pages/popup/__tests__/releaseArtifacts.test.ts
- '.github/RELEASE_TEMPLATE.md'
native:
- 'Voyager/**'
# Everything not matched below forces the full CI (build configs,
# workflows, scripts, dependencies, and any unclassified new file).
- id: catchall
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
predicate-quantifier: 'every'
filters: |
core:
- '!docs/**'
- '!CNAME'
- '!src/**'
- '!public/**'
- '!Voyager/**'
- '!.github/workflows/deploy-docs.yml'
- '!.github/workflows/sponsors.yml'
- '!scripts/generate-sponsors.cjs'
- '!scripts/generate-sponsors.test.js'
- '!scripts/update-readme-badges.mjs'
- '!scripts/update-readme-badges.test.js'
- '!sponsorkit/**'
- '!**/*.md'
- '!LICENSE'
- '!.gitignore'
- '!.gitattributes'
# ── Prettier ───────────────────────────────────────────────────────────
format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run format:check
# ── ESLint ─────────────────────────────────────────────────────────────
lint:
name: Lint
needs: changes
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run lint:check
# ── TypeScript ─────────────────────────────────────────────────────────
typecheck:
name: Typecheck
needs: changes
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run typecheck
# ── Tests ──────────────────────────────────────────────────────────────
test:
name: Test
needs: changes
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run test
# ── Native Swift Tests + Xcode Build ─────────────────────────────────
# app-only (src/public) changes skip this: the Safari web bundle is already
# packaging-verified by the Build (safari) matrix job on ubuntu; this job's
# extra coverage (swift test + xcodebuild app assembly) only varies with
# native code or build configs.
native-test:
name: Native Swift Test & Xcode Build
needs: changes
if: >-
needs.changes.outputs.native == 'true' ||
needs.changes.outputs.core == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run build:safari
- run: swift test --package-path "Voyager"
- run: >-
xcodebuild
-project "Voyager/Voyager.xcodeproj"
-scheme "Voyager"
-configuration Debug
-destination "platform=macOS"
-derivedDataPath .build/ci-xcode-derived
CODE_SIGNING_ALLOWED=NO
build
# ── Build (all browsers) ──────────────────────────────────────────────
build:
name: Build
needs: changes
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chrome, edge, firefox, safari]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- run: bun run build:${{ matrix.browser }}
# ── Docs build ────────────────────────────────────────────────────────
docs-build:
name: Docs Build
needs: changes
if: needs.changes.outputs.docs == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- run: bun install --frozen-lockfile
- name: Test docs tooling
run: |
node scripts/update-readme-badges.mjs --self-test
bun run test scripts/generate-sponsors.test.js scripts/update-readme-badges.test.js
- run: bun run docs:build
# ── i18n consistency ──────────────────────────────────────────────────
i18n:
name: i18n
needs: changes
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.core == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Check locale key consistency
run: node scripts/check-locale-keys.mjs
# ── PR size label ─────────────────────────────────────────────────────
pr-size:
name: PR Size
# Skip on fork PRs — GITHUB_TOKEN lacks write permission for labels
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Label PR by changed lines
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const { owner, repo } = context.repo;
const pr = context.payload.pull_request.number;
const { data: files } = await github.rest.pulls.listFiles({ owner, repo, pull_number: pr, per_page: 100 });
const changes = files.reduce((sum, f) => sum + f.changes, 0);
let label;
if (changes < 50) label = 'size/S';
else if (changes < 200) label = 'size/M';
else if (changes < 500) label = 'size/L';
else label = 'size/XL';
// Remove old size labels
const { data: existing } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number: pr });
for (const l of existing) {
if (l.name.startsWith('size/')) {
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr, name: l.name });
}
}
await github.rest.issues.addLabels({ owner, repo, issue_number: pr, labels: [label] });
core.info(`PR #${pr}: ${changes} changed lines → ${label}`);
# ── Summary (required-check anchor) ───────────────────────────────────
# Always runs, so branch protection can require this single check even
# when path filtering skips individual jobs. Skipped jobs pass; any
# failed or cancelled job fails the summary.
ci-summary:
name: CI Summary
if: always()
needs: [changes, format, lint, typecheck, test, native-test, build, docs-build, i18n]
runs-on: ubuntu-latest
steps:
- name: Verify job results
env:
NEEDS_JSON: ${{ toJSON(needs) }}
run: |
echo "$NEEDS_JSON"
if echo "$NEEDS_JSON" | grep -Eq '"result": "(failure|cancelled)"'; then
echo "One or more CI jobs failed or were cancelled."
exit 1
fi
echo "All CI jobs succeeded or were skipped by path filtering."