Skip to content

fix(admin): namespace generated module assets #296

fix(admin): namespace generated module assets

fix(admin): namespace generated module assets #296

Workflow file for this run

name: CI
# Third-party actions are pinned to commit SHAs (with the human-readable
# version in a trailing comment) to defend against tag-takeover supply-chain
# attacks. Refresh SHAs deliberately; do not switch back to floating tags.
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
WIKI_ECON_BUILD_COMMIT: ${{ github.sha }}
jobs:
changes:
name: Detect changed areas
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
rust: ${{ steps.filter.outputs.rust }}
site: ${{ steps.filter.outputs.site }}
image: ${{ steps.filter.outputs.image }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 2
- name: Filter changed paths
id: filter
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
with:
filters: |
rust:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.cargo/**'
- 'vendor/**'
- 'deny.toml'
site:
- 'config/generated/metric-catalog.json'
- 'site/src/**'
- 'site/data-build/**'
- 'site/observablehq.config.js'
- 'site/package.json'
- 'site/site-footer.js'
- 'package.json'
- 'package-lock.json'
- 'scripts/site-source-bundle.cjs'
image:
- 'scripts/**'
- 'deploy/toolforge/**'
- 'config/**'
- 'package.json'
- 'package-lock.json'
- 'site/package.json'
- 'site/vendor/**'
- 'site/admin-auth.cjs'
- 'site/admin-server.cjs'
- 'site/freshness.cjs'
- 'Procfile'
- 'RustConfig'
- '.node-version'
- '.npmrc'
quality-node:
name: Quality (Node/shell/Python)
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Install pinned npm
run: npm install --global npm@11.12.1
- name: Install Node dependencies
run: npm ci
- name: Verify exact runtime closure
run: node scripts/verify-runtime.cjs
- name: Verify generated stack documentation
run: node scripts/generate-stack-reference.cjs --check
- name: Enforce compute-family algorithm versions
env:
COMPUTE_VERSION_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
run: node scripts/check-compute-versions.cjs
- name: Check shell script syntax
run: bash -n scripts/*.sh scripts/lib/*.sh site/data-build/*.sh deploy/cloud-vps/*.sh deploy/toolforge/*.sh
- name: Run shellcheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
with:
additional_files: scripts/lib/wiki_econ.sh
scandir: ./scripts ./site/data-build ./deploy/cloud-vps ./deploy/toolforge
- name: Check Node entrypoint syntax
run: |
node --check site/admin-auth.cjs
node --check site/admin-server.cjs
node --check site/freshness.cjs
node -e 'const p=require("./config/operations-slos.json"); if(p.schema_version !== 1) process.exit(1)'
node --check site/observablehq.config.js
node --check scripts/check-npm-advisories.cjs
node --check scripts/check-npm-licenses.cjs
node --check scripts/check-vendor-patches.cjs
node --check scripts/build-site-fixture.cjs
node --check scripts/deny-network.cjs
node --check scripts/check-freshness.cjs
node --check scripts/prepare-site-source.cjs
node --check scripts/publish-static-root.cjs
node --check scripts/release-provenance.cjs
node --check scripts/generate-sboms.cjs
node --check scripts/generate-stack-reference.cjs
node --check scripts/release-bundle.cjs
node --check scripts/verify-runtime.cjs
node --check scripts/verify-site-dependencies.cjs
node --check scripts/verify-site-reproducibility.cjs
node --check scripts/publish-browser-data.cjs
node --check scripts/browser-performance.cjs
node --check scripts/check-compute-versions.cjs
node --check scripts/site-source-bundle.cjs
- name: Run Node unit tests
run: |
node --test site/admin-auth.test.cjs
node --test site/admin-lifecycle.test.cjs
node --test site/admin-quality.test.cjs
node --test site/admin-operational-truth.test.cjs
node --test site/admin-page.test.cjs
node --test site/admin-server.test.cjs
node --test site/build-site.test.cjs
node --test site/mobile-layout.test.cjs
node --test site/robots.test.cjs
node --test site/data-build/manifest.test.cjs
node --test site/freshness.test.cjs
node --test deploy/toolforge/run-record.test.cjs
node --test deploy/toolforge/run-refresh.test.cjs
node --test deploy/toolforge/run-publish-ready.test.cjs
node --test deploy/toolforge/install-binary.test.cjs
node --test deploy/toolforge/download-release.test.cjs
node --test deploy/toolforge/download-site-source.test.cjs
node --test deploy/toolforge/install-site-source.test.cjs
node --test deploy/toolforge/run-publication-qualification.test.cjs
node --test deploy/toolforge/prune-releases.test.cjs
node --test deploy/toolforge/run-capacity-benchmark.test.cjs
node --test deploy/toolforge/run-qualify-wiki.test.cjs
node --test deploy/toolforge/rebuild-image.test.cjs
node --test deploy/toolforge/load-scheduled-jobs.test.cjs
node --test deploy/toolforge/run-fleet-worker.test.cjs
node --test deploy/toolforge/imported-backup.test.cjs
node --test deploy/toolforge/recovery-operations.test.cjs
node --test scripts/check-npm-advisories.test.cjs
node --test scripts/check-npm-licenses.test.cjs
node --test scripts/build-site-fixture.test.cjs
node --test scripts/check-freshness.test.cjs
node --test scripts/prepare-site-source.test.cjs
node --test scripts/publish-static-root.test.cjs
node --test scripts/release-provenance.test.cjs
node --test scripts/generate-sboms.test.cjs
node --test scripts/generate-stack-reference.test.cjs
node --test scripts/release-bundle.test.cjs
node --test scripts/qualify-capacity.test.cjs
node --test scripts/verify-site-dependencies.test.cjs
node --test scripts/verify-site-reproducibility.test.cjs
node --test scripts/publish-browser-data.test.cjs
node --test scripts/browser-performance.test.cjs
node --test scripts/check-compute-versions.test.cjs
node --test scripts/check-fleet-qualification.test.cjs
node --test scripts/site-source-bundle.test.cjs
node --test site/browser-cache.test.mjs
node --test site/activity-tiers.test.mjs
node --test site/wiki-scope.test.mjs
node --test scripts/wiki-lifecycle.test.cjs
node --test scripts/wiki-econ-runtime.test.cjs
node scripts/check-fleet-qualification.cjs
- name: Check helper CLI entrypoints
run: |
./scripts/build-site.sh --help
./scripts/refresh.sh --help
./scripts/setup.sh --help
- name: Check Python coverage helper
run: python3 -m py_compile scripts/check_lcov.py scripts/test_check_lcov.py
- name: Run Python tests
run: python3 -m unittest discover -s scripts -p 'test_*.py'
quality-rust:
name: Quality (Rust)
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # @stable as of pin
with:
toolchain: 1.98.0
components: rustfmt, clippy
- name: Cache cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Check formatting
run: cargo fmt --all -- --check
- name: Lint
run: cargo clippy --locked --all-targets --all-features -- -D warnings
- name: Verify generated metric catalog
run: cargo run --locked -- metric-catalog --check
# Note: the test suite runs in the `coverage` job under cargo-llvm-cov,
# which subsumes a plain `cargo test`. Running `cargo test` here too
# would double the wall time without adding signal.
- name: Check docs
run: cargo doc --locked --no-deps
site:
name: Build production site fixture
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Install pinned npm
run: npm install --global npm@11.12.1
- name: Clean-install workspace dependencies
run: npm ci
- name: Enforce npm advisory policy
run: node scripts/check-npm-advisories.cjs
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # @stable as of pin
with:
toolchain: 1.98.0
- name: Cache cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Generate deterministic site fixture
run: cargo run --locked -- --output-dir "$RUNNER_TEMP/wiki-econ-site-data" site-fixture
- name: Build twice offline and verify byte reproducibility
run: >-
node scripts/verify-site-reproducibility.cjs
--data-dir "$RUNNER_TEMP/wiki-econ-site-data"
--work-dir "$RUNNER_TEMP/wiki-econ-site-reproducibility"
- name: Generate deterministic browser scalability fixture
run: cargo run --locked -- --output-dir "$RUNNER_TEMP/wiki-econ-browser-data" browser-performance-fixture
- name: Build browser scalability fixture offline
run: >-
node scripts/build-site-fixture.cjs
--data-dir "$RUNNER_TEMP/wiki-econ-browser-data"
--dist-dir "$RUNNER_TEMP/wiki-econ-browser-dist"
- name: Enforce browser performance budgets
run: >-
node scripts/browser-performance.cjs
--dist-dir "$RUNNER_TEMP/wiki-econ-browser-dist"
--report "$RUNNER_TEMP/browser-performance.json"
- name: Retain browser performance evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: browser-performance
path: ${{ runner.temp }}/browser-performance.json
if-no-files-found: error
coverage:
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
LLVM_COV_OUTPUT_PATH: target/llvm-cov.info
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Install pinned npm
run: npm install --global npm@11.12.1
- name: Install Node dependencies
run: npm ci
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # @stable as of pin
with:
toolchain: 1.98.0
- name: Cache cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Install coverage tooling
uses: taiki-e/install-action@51cd0b8c0499559d9a4d75c0f5c67bec3a894ec8 # v2
with:
tool: cargo-llvm-cov
- name: Run coverage
run: cargo llvm-cov --locked --workspace --all-features --all-targets --lcov --output-path "$LLVM_COV_OUTPUT_PATH"
- name: Validate LCOV line coverage
run: python3 scripts/check_lcov.py "$LLVM_COV_OUTPUT_PATH"
- name: Upload coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: llvm-cov-lcov
path: ${{ env.LLVM_COV_OUTPUT_PATH }}
if-no-files-found: error
security-node:
name: Security (Node/Python)
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Install pinned npm
run: npm install --global npm@11.12.1
- name: Install REUSE compliance tooling
run: python3 -m pip install --disable-pip-version-check 'reuse[charset-normalizer]==6.2.0'
- name: REUSE lint
run: reuse --no-multiprocessing lint
- name: npm advisory policy
run: node scripts/check-npm-advisories.cjs
- name: npm license policy
run: node scripts/check-npm-licenses.cjs
- name: Vendor patch hygiene
run: scripts/check_vendor_patches.sh
security-rust:
name: Security (Rust)
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # @stable as of pin
with:
toolchain: 1.98.0
- name: Cache cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Install security tooling
uses: taiki-e/install-action@51cd0b8c0499559d9a4d75c0f5c67bec3a894ec8 # v2
with:
tool: cargo-deny,cargo-audit
- name: cargo-deny
run: cargo deny check advisories bans licenses sources
- name: cargo-audit
run: cargo audit -D warnings
toolforge-release:
name: Build Toolforge release artifact
if: |
always() && github.event_name == 'push' && github.ref == 'refs/heads/main' &&
needs.quality-node.result == 'success' &&
(needs.quality-rust.result == 'success' || needs.quality-rust.result == 'skipped') &&
needs.site.result == 'success' &&
(needs.coverage.result == 'success' || needs.coverage.result == 'skipped') &&
needs.security-node.result == 'success' &&
(needs.security-rust.result == 'success' || needs.security-rust.result == 'skipped')
needs:
- changes
- quality-node
- quality-rust
- site
- coverage
- security-node
- security-rust
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: read
id-token: write
attestations: write
concurrency:
group: toolforge-release-build
cancel-in-progress: false
steps:
- name: Check out repository history
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Install pinned npm
run: npm install --global npm@11.12.1
- name: Install exact Node dependency closure
run: npm ci
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # @stable as of pin
with:
toolchain: 1.98.0
- name: Cache release artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: ". -> target"
shared-key: toolforge-release
cache-on-failure: true
- name: Build release binary
run: cargo build --release --locked
- name: Verify release binary
run: |
file target/release/wiki-econ
file target/release/wiki-econ | grep -Eq 'ELF 64-bit.*x86-64'
ldd_output="$(ldd target/release/wiki-econ)"
printf '%s\n' "$ldd_output"
if grep -F 'not found' <<< "$ldd_output"; then
echo "Release binary has unresolved shared libraries" >&2
exit 1
fi
target/release/wiki-econ --help >/dev/null
mkdir -p "$RUNNER_TEMP/wiki-econ-release"
install -m 0755 target/release/wiki-econ "$RUNNER_TEMP/wiki-econ-release/wiki-econ"
- name: Build the attested browser artifact offline
run: |
target/release/wiki-econ --output-dir "$RUNNER_TEMP/wiki-econ-release-site-data" site-fixture
node scripts/build-site-fixture.cjs \
--data-dir "$RUNNER_TEMP/wiki-econ-release-site-data" \
--dist-dir "$RUNNER_TEMP/wiki-econ-release-browser"
- name: Generate release SBOMs and complete notices
run: |
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
cp THIRD_PARTY_NOTICES.md "$RUNNER_TEMP/wiki-econ-release/THIRD_PARTY_NOTICES.md"
node scripts/generate-sboms.cjs \
--binary "$RUNNER_TEMP/wiki-econ-release/wiki-econ" \
--browser-dist "$RUNNER_TEMP/wiki-econ-release-browser" \
--output-dir "$RUNNER_TEMP/wiki-econ-release"
- name: Record release dependency and platform provenance
run: |
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
node scripts/release-provenance.cjs \
--binary "$RUNNER_TEMP/wiki-econ-release/wiki-econ" \
--supply-chain-dir "$RUNNER_TEMP/wiki-econ-release" \
--output "$RUNNER_TEMP/wiki-econ-release/release-provenance.json"
- name: Seal deterministic release envelope
run: |
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
archive="target/release/wiki-econ-release-$GITHUB_SHA.tar.gz"
node scripts/release-bundle.cjs --write "$RUNNER_TEMP/wiki-econ-release" "$GITHUB_SHA"
tar --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --numeric-owner \
-czf "$archive" -C "$RUNNER_TEMP/wiki-econ-release" \
SHA256SUMS THIRD_PARTY_NOTICES.md release-provenance.json third-party-notices.json \
wiki-econ wiki-econ-browser-bundle.cdx.json wiki-econ-rust-binary.cdx.json \
wiki-econ-toolforge-site-image.cdx.json
sha256sum "$archive" | sed 's#target/release/##' > "$archive.sha256"
- name: Attest release envelope
id: attest
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: target/release/wiki-econ-release-${{ github.sha }}.tar.gz
- name: Upload release artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: wiki-econ-linux-x86_64-${{ github.sha }}
path: |
target/release/wiki-econ-release-${{ github.sha }}.tar.gz
target/release/wiki-econ-release-${{ github.sha }}.tar.gz.sha256
${{ steps.attest.outputs.bundle-path }}
if-no-files-found: error
retention-days: 30
site-source-release:
name: Build Toolforge site-source artifact
if: |
always() && github.event_name == 'push' && github.ref == 'refs/heads/main' &&
needs.quality-node.result == 'success' &&
needs.site.result == 'success' &&
needs.security-node.result == 'success'
needs:
- changes
- quality-node
- site
- security-node
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
id-token: write
attestations: write
concurrency:
group: toolforge-site-source-build
cancel-in-progress: false
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
package-manager-cache: false
- name: Prepare deterministic site-source envelope
run: |
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
bundle="$RUNNER_TEMP/wiki-econ-site-source"
archive="wiki-econ-site-source-$GITHUB_SHA.tar.gz"
node scripts/site-source-bundle.cjs --prepare "$GITHUB_WORKSPACE" "$bundle" "$GITHUB_SHA"
tar --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --numeric-owner \
-czf "$archive" -C "$bundle" .
sha256sum "$archive" > "$archive.sha256"
- name: Attest site-source envelope
id: attest
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: wiki-econ-site-source-${{ github.sha }}.tar.gz
- name: Upload site-source artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: wiki-econ-site-source-${{ github.sha }}
path: |
wiki-econ-site-source-${{ github.sha }}.tar.gz
wiki-econ-site-source-${{ github.sha }}.tar.gz.sha256
${{ steps.attest.outputs.bundle-path }}
if-no-files-found: error
retention-days: 30