CI / policy (push) Successful in 6s
CI / validate (push) Successful in 53s
CI / build (push) Successful in 1m18s
CI / policy (pull_request) Successful in 9s
CI / security (push) Successful in 1m12s
CI / validate (pull_request) Successful in 1m18s
CI / benchmark (push) Successful in 3m56s
CI / test (push) Successful in 4m6s
CI / build (pull_request) Successful in 36s
CI / security (pull_request) Successful in 1m5s
CI / test (pull_request) Successful in 3m2s
CI / images (push) Successful in 1m15s
CI / required (push) Successful in 1s
CI / benchmark (pull_request) Successful in 7m18s
CI / images (pull_request) Successful in 1m50s
CI / required (pull_request) Successful in 1s
The first PR run landed on srvhome, about 1.9 times slower than nessar-desktop. The fixed 300-second timeout killed its protocol during the loaded year, although every completed scenario was within its relative budget. Raise the CI timeout to 600 seconds and the test timeout to 570 seconds so they only guard against hangs, and record the first CI-runner ratios. Refs #285 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
209 lines
8.2 KiB
YAML
209 lines
8.2 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
pull_request:
|
|
types: [opened, reopened, synchronize, edited]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
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.21.0
|
|
outputs:
|
|
slug: ${{ steps.policy.outputs.slug }}
|
|
tag: ${{ steps.policy.outputs.tag }}
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
- name: Policy regression tests
|
|
run: node scripts/ci-policy.test.mjs && node scripts/ci-image.test.mjs
|
|
- name: Validate event and commit
|
|
id: policy
|
|
run: node scripts/ci-policy.mjs
|
|
|
|
validate:
|
|
name: validate
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.0
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
- run: npm install --global bun@1.4.2
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun run lint
|
|
- run: bun run format:check
|
|
- run: bunx prettier --check 'scripts/ci-*.mjs' '.gitea/workflows/*.yml'
|
|
- run: bunx tsc --noEmit -p tsconfig.app.json
|
|
- run: bunx tsc --noEmit -p tsconfig.spec.json
|
|
|
|
test:
|
|
name: test
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.0
|
|
env:
|
|
NODE_OPTIONS: --max-old-space-size=2048
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
- run: npm install --global bun@1.4.2
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun scripts/generate-langs.js
|
|
- name: Functional coverage
|
|
run: bunx vitest run --coverage --maxWorkers=1 --exclude='src/app/benchmark/**'
|
|
- name: Coverage report
|
|
if: ${{ always() }}
|
|
uses: https://gitea.com/actions/upload-artifact@v3
|
|
with:
|
|
name: coverage
|
|
path: coverage/
|
|
retention-days: 7
|
|
if-no-files-found: error
|
|
|
|
benchmark:
|
|
name: benchmark
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.0
|
|
env:
|
|
NODE_OPTIONS: --max-old-space-size=2048
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
- run: npm install --global bun@1.4.2
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun scripts/generate-langs.js
|
|
- name: Benchmark protocol with relative gate
|
|
run: timeout 600s bunx vitest run src/app/benchmark/ --maxWorkers=1
|
|
|
|
build:
|
|
name: build
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.0
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
- run: npm install --global bun@1.4.2
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun run build -- --configuration production
|
|
- uses: https://gitea.com/actions/upload-artifact@v3
|
|
with:
|
|
name: dist
|
|
path: dist/
|
|
retention-days: 7
|
|
if-no-files-found: error
|
|
|
|
security:
|
|
name: security
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.0
|
|
steps:
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
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@v3
|
|
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.8.2
|
|
concurrency:
|
|
group: image-${{ needs.policy.outputs.tag != '' && 'stable' || needs.policy.outputs.slug }}
|
|
cancel-in-progress: false
|
|
services:
|
|
docker:
|
|
image: docker:29.8.2-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:
|
|
- name: Install checkout and policy runtimes
|
|
run: apk add --no-cache nodejs git
|
|
- uses: https://gitea.com/actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
- name: Build branch images or promote a release
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
run: node scripts/ci-image.mjs
|
|
|
|
required:
|
|
name: required
|
|
if: ${{ always() }}
|
|
needs: [policy, validate, test, benchmark, build, security, images]
|
|
runs-on: ubuntu-latest
|
|
container: node:24.21.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
|