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
81 changes: 71 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ jobs:
# published tarball or on pkg.go.dev, where it cannot be corrected in place.
- run: npm run version:check

release-pins:
name: Release-path pins (checker + self-test)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# The hook that runs this locally is bypassable with --no-verify, so the
# authoritative run is here.
- run: sh scripts/check-release-pins.sh
# And the checker itself is checked: a scanner that matches nothing also
# exits 0, so the fixtures prove each rule still fires.
- run: sh scripts/check-release-pins.sh --self-test

node-sdk:
name: Node SDK (build + test, Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
Expand Down Expand Up @@ -169,33 +181,48 @@ jobs:
named test/export.test.ts 2 '^strict: .+$'

mcp:
name: MCP server (build + test)
name: MCP server (build + test, Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# The server publishes engines.node >=22, so the floor is exercised here
# for the same reason it is on the SDK job above: a suite that only ever
# runs on the newest runtime tests everything except what it claims.
node-version: [22, 24]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build -w packages/sdk
- run: npm run build -w packages/mcp
- run: npm test -w packages/mcp

python-sdk:
name: Python SDK (lint + type + test)
name: Python SDK (lint + type + test, Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Both ends of what the package claims on PyPI, and the version in the
# middle that the rest of CI runs on: 3.10 is the requires-python floor,
# 3.14 the newest classifier, and a suite exercised only at one point in
# that span tests neither promise.
python-version: ["3.10", "3.12", "3.14"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
python-version: ${{ matrix.python-version }}
- name: Install
working-directory: packages/sdk-py
run: python -m pip install --upgrade pip && pip install -e ".[dev]"
run: python -m pip install pip==26.2.1 && pip install -e ".[dev]"
- name: Ruff
working-directory: packages/sdk-py
run: ruff check . && ruff format --check .
Expand All @@ -205,6 +232,13 @@ jobs:
- name: Pytest
working-directory: packages/sdk-py
run: pytest -q
# The PyPI upload runs twine check --strict and refuses what it cannot
# render, after the tag exists. The same check on every PR, on a
# distribution built here, so metadata never reaches a tag unrendered.
- name: twine check --strict (the publish action's own gate)
run: |
. scripts/lib/twine-check.sh
twine_check python3 packages/sdk-py "$RUNNER_TEMP/twine-gate"

go-sdk:
name: Go SDK (fmt + vet + test)
Expand All @@ -215,7 +249,7 @@ jobs:
submodules: recursive
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.23"
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
- name: gofmt
working-directory: packages/sdk-go
Expand All @@ -230,7 +264,8 @@ jobs:
- name: goreleaser check
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
version: "~> v2"
# The same goreleaser the release job installs (scripts/release-pins.env).
version: "v2.18.0"
workdir: packages/sdk-go
args: check

Expand All @@ -246,7 +281,7 @@ jobs:
node-version: 24
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.23"
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
Expand All @@ -267,7 +302,7 @@ jobs:
node-version: 24
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.23"
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
Expand All @@ -276,7 +311,7 @@ jobs:
- run: npm run build -w packages/sdk
- name: Install the Python SDK
working-directory: packages/sdk-py
run: python -m pip install --upgrade pip && pip install -e ".[dev]"
run: python -m pip install pip==26.2.1 && pip install -e ".[dev]"
# Compiled here, where the network still exists, so nothing inside the
# namespace needs to reach a module proxy to build what it runs.
- name: Compile the Go fixture tests
Expand Down Expand Up @@ -323,6 +358,32 @@ jobs:
- run: npm ci
- run: npm run build -w leji-site

ui-smoke:
name: UI smoke (viewer, export, and site in a browser)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- run: npm ci
# Both builds are inputs to the suite: it runs the CLI out of packages/sdk/dist
# and previews the site out of packages/site/dist.
- run: npm run build -w packages/sdk
- run: npm run build -w packages/site
- run: npm run browsers -w packages/e2e
- run: npm run e2e -w packages/e2e
# Traces and screenshots are retained only for failures, so a red job is
# inspectable without a rerun and a green one uploads nothing.
- name: Playwright report and traces
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ui-smoke-report
path: |
packages/e2e/report/
packages/e2e/test-results/

dogfood:
name: Dogfood (repo conforms to its own spec)
runs-on: ubuntu-latest
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/dependency-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Dependency audit

# Advisories against dependencies that have not changed: the pins are exact, so
# nothing here moves on its own, and a monthly run is the only trigger that fires
# when no one has committed anything. The pull-request, release/* and rc/* triggers
# cover the other direction, a dependency surface that did change. Policy and failure
# thresholds: docs/decisions/0008-dependency-pinning-and-refresh.md.
# Actions and scanners are pinned, like everything else on the release path.

on:
schedule:
- cron: "0 6 1 * *"
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/dependency-audit.yml"
- "package.json"
- "package-lock.json"
- "packages/*/package.json"
- "packages/sdk-py/pyproject.toml"
- "packages/sdk-go/go.mod"
push:
# release/* and rc/*: the audit runs against the exact bytes a release is cut
# from, on the branch the candidate is prepared on and on the one it is proven
# on.
branches: ["release/*", "rc/*"]

permissions:
contents: read

jobs:
npm-audit:
name: npm (root lockfile, all workspaces)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
# The scanner is npm itself, so it is pinned to the same version the
# release workflow publishes with, not to whatever the Node release bundles.
- run: npm install -g npm@12.0.2 && npm --version
- run: npm ci
# Every workspace resolves through the root lockfile, so one audit covers
# all of them. High or above fails the run.
- run: npm audit --audit-level=high

pip-audit:
name: Python (resolved dependency export, Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# The same span the Python SDK is tested across, for the same reason: a
# dependency closure can be interpreter-conditioned, so an audit at one
# point in the span says nothing about the ends. 3.10 is the
# requires-python floor, 3.14 the newest classifier, 3.12 the version the
# rest of CI runs on.
python-version: ["3.10", "3.12", "3.14"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
# The audit boundary is the SDK's resolved runtime closure, not the runner's
# environment: install the package, export exactly what that resolved to,
# and audit the export. The SDK itself is excluded, being what is audited
# rather than a dependency of it, and pip-audit is installed only after the
# export so that its own dependencies stay out of the boundary.
- name: Resolve the SDK's dependencies
run: |
python -m venv .venv
.venv/bin/pip install -e ./packages/sdk-py
.venv/bin/pip freeze --exclude-editable > resolved.txt
cat resolved.txt
- name: Audit the export
run: |
.venv/bin/pip install pip-audit==2.10.1
.venv/bin/pip-audit --strict -r resolved.txt

govulncheck:
name: Go (module and standard library)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
# govulncheck reports the vulnerabilities this code can actually reach, so
# a finding here is a call path, not an inventory entry.
- name: govulncheck
working-directory: packages/sdk-go
run: go run golang.org/x/vuln/cmd/govulncheck@v1.7.0 ./...
2 changes: 1 addition & 1 deletion .github/workflows/release-finalize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
release_tag:
description: "Tag the draft release is attached to (the goreleaser/Go tag)"
required: true
default: "packages/sdk-go/v1.4.0"
default: "packages/sdk-go/v1.4.1"

permissions:
contents: write # publish the release
Expand Down
32 changes: 23 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
python-version: "3.12"
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.23"
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
- run: npm ci
- name: Build artifacts, cold-install each, assert CLI battery + cross-SDK parity
Expand All @@ -63,7 +63,7 @@ jobs:
node-version: 24
registry-url: "https://registry.npmjs.org"
# OIDC trusted publishing needs npm >= 11.5.1 (Node 24 may ship older).
- run: npm install -g npm@latest && npm --version
- run: npm install -g npm@12.0.2 && npm --version
- run: npm ci
- name: Verify tag matches package version
run: |
Expand Down Expand Up @@ -98,9 +98,11 @@ jobs:
PKG="$(node -p "require('./packages/sdk/jsr.json').version")"
test "$TAG" = "$PKG" || { echo "tag packages/sdk/v$TAG != jsr.json version $PKG"; exit 1; }
# JSR publishes the TS source (per jsr.json), not the npm dist tarball.
# The jsr CLI is pinned like every other tool on this path; the version
# mirrors JSR_VERSION in scripts/release-pins.env.
- name: Publish to JSR
working-directory: packages/sdk
run: npx jsr publish
run: npx jsr@0.14.3 publish

# --- packages/create-leji/v* : npm `create-leji` (first-class, own tag) ---

Expand All @@ -118,7 +120,7 @@ jobs:
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
- run: npm install -g npm@latest && npm --version
- run: npm install -g npm@12.0.2 && npm --version
- run: npm ci
- name: Verify tag matches package version
run: |
Expand Down Expand Up @@ -146,7 +148,7 @@ jobs:
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
- run: npm install -g npm@latest && npm --version
- run: npm install -g npm@12.0.2 && npm --version
- run: npm ci
- name: Verify tag matches package version
run: |
Expand Down Expand Up @@ -185,10 +187,20 @@ jobs:
test "$TAG" = "$PKG" || { echo "tag packages/sdk-py/v$TAG != pyproject version $PKG"; exit 1; }
- name: Test
working-directory: packages/sdk-py
run: python -m pip install --upgrade pip && pip install -e ".[dev]" && pytest -q
run: python -m pip install pip==26.2.1 && pip install -e ".[dev]" && pytest -q
- name: Build sdist + wheel
working-directory: packages/sdk-py
run: python -m pip install --upgrade build && python -m build
run: python -m pip install build==1.5.0 && python -m build
# The last gate before the irreversible act, on the exact bytes about to be
# uploaded: the publish action rejects a distribution twine cannot render,
# and it rejects it after the tag exists. Same function as the smoke and
# the pre-push hook, same twine the action below bundles.
- name: twine check --strict on the built distribution
run: |
. scripts/lib/twine-check.sh
twine_check python3 packages/sdk-py "$RUNNER_TEMP/twine-gate" packages/sdk-py/dist
# SHA, version comment, and the twine the gate above installs all come from
# scripts/release-pins.env; moving this pin means moving that file.
- name: Publish
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
Expand All @@ -209,7 +221,7 @@ jobs:
fetch-depth: 0 # full history so goreleaser sees tags
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.23"
go-version: "1.27.0"
cache-dependency-path: packages/sdk-go/go.sum
- name: Verify tag matches SDK version
run: |
Expand All @@ -227,7 +239,9 @@ jobs:
- name: Build cross-platform binaries (goreleaser, no publish)
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
version: "~> v2"
# The action is pinned by SHA; this input pins the goreleaser it
# installs, and mirrors GORELEASER_VERSION in scripts/release-pins.env.
version: "v2.18.0"
workdir: packages/sdk-go
args: release --clean --skip=publish
env:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ dist/
# Leji generated viewer (leji viewer / view output)
.leji/

# UI smoke output (Playwright report, traces and screenshots)
packages/e2e/report/
packages/e2e/test-results/
packages/e2e/.work/

# Tests & coverage
coverage/
coverage.out
Expand Down
Loading