CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
extension / lint (push) Successful in 21s
CI and images / frontend-build (push) Successful in 21s
CI and images / backend-lint-and-test (push) Successful in 31s
CI and images / integration (push) Successful in 2m10s
CI and images / sign-extension (push) Successful in 3s
CI and images / build-agent (push) Successful in 6s
CI and images / build-web (push) Successful in 1m51s
CI and images / smoke-web (push) Successful in 57s
CI and images / promote (push) Skipped
Operator, 2026-09-23: *"tighten the gate so :dev can't publish on red tests"*, then *"I don't want failing builds to publish anywhere going forward."* Run 7348 is the worked example. The backend unit lane went red on `2f8f0bc` and `build-web` pushed `:dev` in the same minute, because the lanes and the build were SEPARATE WORKFLOWS on the same push trigger. Neither could see the other's verdict. `:dev` was a "it built" signal, never a "it passed" one, and nothing about that was visible from either run. Two workflows cannot express the gate. A `needs:` edge only exists inside one graph. So `ci.yml`'s five lanes move into `build.yml` and `ci.yml` is deleted; `sign-extension`, `build-web` and `build-agent` now need all five. Nothing here is a new mechanism — it is the same edge that has gated `promote` since milestone 362 step 4, and it keeps that step's hardest-won property: **not running is not the same as passing.** `needs` treats a SKIPPED dependency as unsatisfied, so a lane that silently skips itself blocks the publish exactly as a failing one does. Run 5290 is why that is worth stating. Scope, said plainly rather than implied: - Gated: every image tag (`:dev`, `:latest`, `:c-<sha>`), the weekly base refresh, and the `ext-<version>` signed-XPI release asset — `sign-extension` publishes too, so it is gated with the rest. - Not gated, deliberately: `extension.yml` publishes nothing, and `release.yml` runs on a `v*` tag, generates notes rather than an artifact, and its commit already went through main's gated build. - `pull_request` (Renovate bumps into `dev`) comes across with the lanes. Its runs are the lanes and nothing else, via an `if:` on each publishing job rather than an inference from the `needs` chain. The cost, accepted knowingly: this workflow queues per branch and never cancels, so on two pushes in quick succession the second's lint feedback waits out the first's build. A slower red beats a fast red that ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
87 lines
3.8 KiB
YAML
87 lines
3.8 KiB
YAML
name: extension
|
|
# Lint + unit tests. Deliberately NOT a publishing lane, which is why it is
|
|
# not part of build.yml's gate. The sign-and-publish dance moved into build.yml's
|
|
# `sign-extension` job (2026-05-25) — `:latest` now always bundles the XPI
|
|
# because sign-extension runs as a build-web dependency in the SAME workflow,
|
|
# eliminating the prior race between build.yml and a separate extension.yml.
|
|
# Signed XPIs are cached in Forgejo Release Assets named `ext-<version>`.
|
|
on:
|
|
push:
|
|
branches: [dev, main]
|
|
paths:
|
|
- 'extension/**'
|
|
- '.forgejo/workflows/extension.yml'
|
|
# test/version.spec.js asserts things ABOUT build.yml — that it does not
|
|
# inline the packaged-file set, and that build.yml derives
|
|
# the shipped version rather than reading it out of the repo. A
|
|
# workflow-only edit can therefore break this suite, so it has to trigger
|
|
# it. build.yml joined the list at milestone 271 step 5, when the spec
|
|
# started asserting against it.
|
|
- '.forgejo/workflows/build.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- 'extension/**'
|
|
- '.forgejo/workflows/build.yml'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
lint:
|
|
runs-on: python-ci
|
|
container:
|
|
image: node:24-bookworm-slim
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Not --no-save: vitest and web-ext are both real devDependencies now,
|
|
# and the suite needs vitest resolvable from node_modules.
|
|
- name: Install dev dependencies
|
|
run: cd extension && npm install --no-audit --no-fund
|
|
- name: Lint
|
|
run: cd extension && npm run lint
|
|
# Pure-logic specs over lib/url.js and lib/platforms.js plus manifest /
|
|
# package version-consistency checks. No browser, no network.
|
|
- name: Unit tests
|
|
run: cd extension && npm run test:unit
|
|
|
|
# Everything else about packaging is asserted against our own declaration
|
|
# of what ships. This is the only check that asks web-ext what it ACTUALLY
|
|
# put in the archive. Until now that was an unverified assumption about
|
|
# glob semantics — and a fragile one: `test/**` reaches web-ext intact
|
|
# only because callers `set -f` first, so losing that quoting would
|
|
# silently start shipping dev files with no other signal.
|
|
- name: Verify XPI contents
|
|
run: |
|
|
set -eu
|
|
command -v unzip >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y -qq unzip; }
|
|
cd extension
|
|
npm run build
|
|
ZIP=$(ls web-ext-artifacts/*.zip | head -1)
|
|
echo "=== packaged entries in $ZIP ==="
|
|
unzip -Z1 "$ZIP" | sort
|
|
echo "=== end ==="
|
|
ENTRIES=$(unzip -Z1 "$ZIP")
|
|
fail=0
|
|
# Must NOT ship: repo infrastructure with no business in a user's browser.
|
|
for pat in 'test/' 'scripts/' 'vitest.config.js' 'package.json' 'package-lock.json' 'README.md' 'node_modules/' 'web-ext-artifacts/'; do
|
|
if echo "$ENTRIES" | grep -q "^$pat"; then
|
|
echo "ERROR: '$pat' was packaged into the XPI but must not be"
|
|
fail=1
|
|
fi
|
|
done
|
|
# Must ship: if an exclusion pattern ever over-matches, the extension
|
|
# breaks at runtime rather than at build time, so assert presence too.
|
|
for req in 'manifest.json' 'lib/url.js' 'lib/api.js' 'lib/platforms.js' 'lib/cookies.js'; do
|
|
if ! echo "$ENTRIES" | grep -q "^$req$"; then
|
|
echo "ERROR: '$req' is missing from the XPI"
|
|
fail=1
|
|
fi
|
|
done
|
|
for dir in 'background/' 'popup/' 'options/' 'content/' 'icons/'; do
|
|
if ! echo "$ENTRIES" | grep -q "^$dir"; then
|
|
echo "ERROR: nothing from '$dir' was packaged"
|
|
fail=1
|
|
fi
|
|
done
|
|
[ "$fail" -eq 0 ] || exit 1
|
|
echo "XPI contents verified."
|