After #302, the development machine's protocol takes about 41 seconds instead of 232. The budget multipliers now add about 1.5 times headroom to the new ratios (0.1 for typed obligations, which leaves room for runner jitter), and the hang guard drops from 600 to 240 seconds, with a 210-second test timeout: about three times the expected run on srvhome, which is about 1.9 times slower than nessar-desktop. Part of #286 and #302. The gate still needs a passing CI benchmark on two runners. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
238 lines
9.4 KiB
YAML
238 lines
9.4 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
pull_request:
|
|
types: [opened, reopened, synchronize, edited]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Gitea's checkout and upload-artifact actions are JavaScript actions that the runner
|
|
# executes with the job container's own `node`, so those containers keep Node. Project
|
|
# commands never use it: the jobs that run them install the pinned Bun with
|
|
# scripts/install-bun.sh and run `run:` steps under a shell whose PATH has Bun but no Node.
|
|
|
|
concurrency:
|
|
group: ci-${{ github.event_name }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
|
|
jobs:
|
|
policy:
|
|
name: policy
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
defaults:
|
|
run:
|
|
shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}
|
|
outputs:
|
|
slug: ${{ steps.policy.outputs.slug }}
|
|
tag: ${{ steps.policy.outputs.tag }}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- name: Policy regression tests
|
|
run: bun scripts/ci-policy.test.mjs && bun scripts/ci-image.test.mjs
|
|
- name: Validate event and commit
|
|
id: policy
|
|
run: bun scripts/ci-policy.mjs
|
|
|
|
validate:
|
|
name: validate
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
defaults:
|
|
run:
|
|
shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun run lint
|
|
- run: bun run format:check
|
|
- run: bun run typecheck
|
|
|
|
test:
|
|
name: test
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
defaults:
|
|
run:
|
|
shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun scripts/generate-langs.js
|
|
- name: Functional coverage
|
|
run: bun test --config=bunfig.coverage.toml --isolate --path-ignore-patterns='**/benchmark/**'
|
|
- name: Coverage report
|
|
if: ${{ always() }}
|
|
run: bun scripts/coverage-reports.mjs
|
|
- name: Upload coverage
|
|
if: ${{ always() }}
|
|
uses: https://gitea.com/actions/upload-artifact@v7
|
|
with:
|
|
name: coverage
|
|
path: coverage/
|
|
retention-days: 7
|
|
if-no-files-found: error
|
|
|
|
benchmark:
|
|
name: benchmark
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: node:24.22.0
|
|
options: --memory=3g
|
|
defaults:
|
|
run:
|
|
shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun scripts/generate-langs.js
|
|
- name: Benchmark protocol with relative gate
|
|
run: timeout 240s bun test src/app/benchmark --timeout=240000
|
|
|
|
build:
|
|
name: build
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
defaults:
|
|
run:
|
|
shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun run build -- --configuration production
|
|
- uses: https://gitea.com/actions/upload-artifact@v7
|
|
with:
|
|
name: dist
|
|
path: dist/
|
|
retention-days: 7
|
|
if-no-files-found: error
|
|
|
|
security:
|
|
name: security
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install scanners
|
|
run: |
|
|
apt-get update
|
|
apt-get install --yes --no-install-recommends python3-venv
|
|
python3 -m venv /tmp/semgrep
|
|
/tmp/semgrep/bin/pip install semgrep==1.180.0
|
|
case "$(uname -m)" in
|
|
x86_64) trivy_arch=64bit ;;
|
|
aarch64) trivy_arch=ARM64 ;;
|
|
*) echo 'Unsupported Trivy architecture' >&2; exit 1 ;;
|
|
esac
|
|
archive="trivy_0.75.0_Linux-${trivy_arch}.tar.gz"
|
|
curl --fail --location --silent --show-error "https://github.com/aquasecurity/trivy/releases/download/v0.75.0/${archive}" --output "/tmp/${archive}"
|
|
curl --fail --location --silent --show-error https://github.com/aquasecurity/trivy/releases/download/v0.75.0/trivy_0.75.0_checksums.txt --output /tmp/trivy-checksums.txt
|
|
(cd /tmp && sha256sum --check --ignore-missing trivy-checksums.txt)
|
|
tar -xzf "/tmp/${archive}" -C /usr/local/bin trivy
|
|
- name: JavaScript and TypeScript SAST
|
|
run: /tmp/semgrep/bin/semgrep scan --config p/javascript --config p/typescript --metrics off --sarif --output semgrep-results.sarif src scripts
|
|
- name: Dependency and secret scan
|
|
if: ${{ !cancelled() }}
|
|
run: trivy fs --db-repository public.ecr.aws/aquasecurity/trivy-db:2 --format sarif --output trivy-results.sarif --skip-dirs node_modules,dist,coverage .
|
|
- name: Security reports
|
|
if: ${{ always() }}
|
|
uses: https://gitea.com/actions/upload-artifact@v7
|
|
with:
|
|
name: security
|
|
path: '*-results.sarif'
|
|
retention-days: 7
|
|
if-no-files-found: error
|
|
|
|
images:
|
|
name: images
|
|
needs: [policy, validate, test, benchmark, build, security]
|
|
if: >-
|
|
success() &&
|
|
(github.event_name == 'push' ||
|
|
(github.event_name == 'pull_request' &&
|
|
github.event.pull_request.head.repo.full_name == github.repository))
|
|
runs-on: ubuntu-latest
|
|
container: docker:29.9.1
|
|
concurrency:
|
|
group: image-${{ needs.policy.outputs.tag != '' && 'stable' || needs.policy.outputs.slug }}
|
|
cancel-in-progress: false
|
|
services:
|
|
docker:
|
|
image: docker:29.9.1-dind
|
|
env:
|
|
DOCKER_TLS_CERTDIR: ''
|
|
options: >-
|
|
--privileged
|
|
--health-cmd "docker info"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
env:
|
|
DOCKER_HOST: tcp://docker:2375
|
|
DOCKER_TLS_CERTDIR: ''
|
|
defaults:
|
|
run:
|
|
shell: sh
|
|
steps:
|
|
# Alpine's nodejs package is for the checkout action only (it shares /usr/bin
|
|
# with git); Bun needs libgcc and libstdc++ and the installer needs curl.
|
|
- name: Install checkout runtime and tools
|
|
run: apk add --no-cache nodejs git curl libgcc libstdc++
|
|
- uses: https://gitea.com/actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
- name: Install Bun
|
|
run: sh scripts/install-bun.sh
|
|
- name: Build branch images or promote a release
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
run: /opt/bun/bin/bun scripts/ci-image.mjs
|
|
|
|
required:
|
|
name: required
|
|
if: ${{ always() }}
|
|
needs: [policy, validate, test, benchmark, build, security, images]
|
|
runs-on: ubuntu-latest
|
|
container: node:24.22.0
|
|
env:
|
|
CHECK_RESULTS: ${{ needs.policy.result }} ${{ needs.validate.result }} ${{ needs.test.result }} ${{ needs.benchmark.result }} ${{ needs.build.result }} ${{ needs.security.result }}
|
|
IMAGE_RESULT: ${{ needs.images.result }}
|
|
IMAGE_EXPECTED: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
|
|
steps:
|
|
- name: Require successful validation and expected publication
|
|
run: |
|
|
test "$CHECK_RESULTS" = 'success success success success success success' || exit 1
|
|
if [ "$IMAGE_EXPECTED" = 'true' ]; then
|
|
test "$IMAGE_RESULT" = 'success'
|
|
else
|
|
test "$IMAGE_RESULT" = 'skipped'
|
|
fi
|