chadtmiller.com/knotless
git that lives in your Atmosphere account
7
# The pages used as people use them, as `just e2e` runs them: in Chromium,
# logging in through the PDS's own approval page, then every write the
# pages make, each read back on the page and from the PDS
# (harness/e2e/flows.spec.mjs), over a world seeded for the run as the
# shots' is. Then a private repository's, over another on the spaces
# alpha, each read back on the page and from its space, and as not there
# to anyone else (private.spec.mjs). The shots show what the pages draw;
# this, that what they do lands. Each flow that fails leaves the page as
# it was then among the check's outputs, as a picture.
#
# Node, the packages, Chromium, the libraries it needs, the alpha and the
# build are kept between runs, as main's runs leave them: nothing is fetched again
# until what it is made from changes.
image: rust:1.98
# Every push to main, and pulls into it or a feature/ branch, at their
# heads merged with their base, that change the pages, the server and
# client they log in to and write through, the world and harness they run
# over, or this file. A pull changing none passes at once, noted as
# skipped.
on:
push: {refs: [refs/heads/main]}
pull:
base: [refs/heads/main, refs/heads/feature/*]
paths: [web/**, lexicons/**, crates/knotless/**, crates/knotless-serve/**, crates/knotless-mcp/**, Cargo.toml, Cargo.lock, harness/e2e/**, harness/shots/world.mjs, harness/shots/fixture/**, harness/pds.mjs, harness/tls-proxy.mjs, harness/package.json, harness/package-lock.json, .knotless/workflows/e2e.yml, .knotless/uses.lock]
cache: [target, /usr/local/cargo/registry, /usr/local/rustup, /opt/node, /opt/browsers, /opt/chromium-debs, /opt/atproto, web/node_modules, harness/node_modules]
env:
PLAYWRIGHT_BROWSERS_PATH: /opt/browsers
# The spaces alpha the private flows run over, as test.yml pins it: the
# two move together.
ATPROTO_COMMIT: 11c5d98cd50261c5e1201fedc5c6c05a7b8d1ccf
ALPHA_BUILD: "2"
steps:
- uses: chadtmiller.com/steps/node@1
with: {version: 24.22.1}
- uses: chadtmiller.com/steps/npm-ci@1
name: web
with: {dir: web}
- uses: chadtmiller.com/steps/npm-ci@1
name: harness
with: {dir: harness}
- name: chromium
run: |
export PATH=/opt/node/bin:$PATH
cd harness
# The browser, into /opt/browsers once for each Playwright version.
npx playwright install chromium
# The libraries it needs, which `install-deps` would fetch with apt on
# every run: their .debs are kept, fetched again only when this
# Playwright or this Debian asks for others, and installed offline.
pkgs=$(npx playwright install-deps --dry-run chromium | sed -n "s/.*--no-install-recommends \([^\"']*\).*/\1/p")
[ -n "$pkgs" ] || { echo "no packages in: $(npx playwright install-deps --dry-run chromium)"; exit 1; }
debs=/opt/chromium-debs
want="$(. /etc/os-release && echo "$VERSION_ID") $pkgs"
if [ "$(cat $debs/.want 2>/dev/null)" != "$want" ]; then
rm -rf "${debs:?}"/* && mkdir -p $debs/partial
apt-get update -qq
apt-get install -y -qq --no-install-recommends --download-only -o Dir::Cache::archives=$debs $pkgs
echo "$want" > $debs/.want
fi
dpkg -i $debs/*.deb > /dev/null
- name: spaces alpha
run: |
[ "$(cat /opt/atproto/.built 2>/dev/null)" = "$ATPROTO_COMMIT $ALPHA_BUILD" ] && exit 0
export PATH=/opt/node/bin:$PATH COREPACK_ENABLE_DOWNLOAD_PROMPT=0 COREPACK_HOME=/opt/atproto/.corepack
find /opt/atproto -mindepth 1 -delete
cd /opt/atproto
git init -q . && git fetch -q --depth 1 https://github.com/bluesky-social/atproto.git "$ATPROTO_COMMIT" && git checkout -q FETCH_HEAD
mkdir .bin && corepack enable --install-directory /opt/atproto/.bin
export PATH=/opt/atproto/.bin:$PATH
pnpm install --frozen-lockfile --ignore-scripts
pnpm run build:tooling --force
pnpm --filter '@atproto/dev-env^...' --filter @atproto/dev-env run --if-present prebuild
pnpm exec tsc --build packages/dev-env/tsconfig.build.json
# The PDS serves the OAuth sign-in pages from this bundle.
pnpm --filter @atproto/oauth-provider-ui run build:ui
echo "$ATPROTO_COMMIT $ALPHA_BUILD" > /opt/atproto/.built
- name: sqlite
run: |
# The one install script the PDS needs, which the install above
# skips: SQLite's native module, as better-sqlite3's own script builds
# it (a prebuilt one where there is one).
export PATH=/opt/node/bin:$PATH
cd "$(dirname "$(node -p "require.resolve('better-sqlite3/package.json', { paths: ['/opt/atproto/packages/pds'] })")")"
[ -f build/Release/better_sqlite3.node ] && exit 0
npm run install
- name: build
run: |
export PATH=/opt/node/bin:$PATH
cargo build --bins -p knotless -p knotless-serve
cd web && npm run build
- name: flows
run: |
export PATH=/opt/node/bin:$PATH
status=0
(cd harness && E2E=1 npx playwright test -c e2e/playwright.config.mjs) || status=$?
# Then the private ones, over a world of their own on the alpha.
(cd harness && KNOTLESS_DEV_ENV=/opt/atproto/packages/dev-env E2E=1 npx playwright test -c e2e/playwright.config.mjs) || status=$?
# The page each failed flow ended on, named for the flow.
if [ -n "${KNOTLESS_RUN_OUTPUTS:-}" ] && [ -d target/e2e ]; then
for shot in target/e2e/*/*/test-failed-*.png; do
[ -f "$shot" ] && cp "$shot" "$KNOTLESS_RUN_OUTPUTS/$(basename "$(dirname "$shot")").png"
done
fi
exit $status