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.21.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.21.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.21.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.21.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 600s bun test src/app/benchmark --timeout=600000 build: name: build runs-on: ubuntu-latest container: node:24.21.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.21.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.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