aviso-server 0.7.1

Notification service for data-driven workflows with live and replay APIs.
name: CI

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

concurrency:
  group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: "1"

jobs:
  # ── Rust: fmt, clippy, build, test, lockfiles ───────────────────
  rust:
    name: Rust
    # Trusted path only: self-hosted runners must never run fork code.
    # Fork PRs skip every trusted job; ci-pass then stays red so they
    # cannot merge. A maintainer lands the work on a repo branch to gate it.
    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
    runs-on: [self-hosted, Linux, platform-builder-docker-xl, platform-builder-Ubuntu-22.04]
    container:
      image: eccr.ecmwf.int/aviso/server-ci:0.1.0
      credentials:
        username: ${{ secrets.ECMWF_DOCKER_REGISTRY_USERNAME }}
        password: ${{ secrets.ECMWF_DOCKER_REGISTRY_ACCESS_TOKEN }}
    env:
      RUSTC_WRAPPER: sccache
      CARGO_INCREMENTAL: "0"
      SCCACHE_BUCKET: aviso-server-ci-cache
      SCCACHE_ENDPOINT: "https://object-store.os-api.cci1.ecmwf.int"
      SCCACHE_REGION: us-east-1
      SCCACHE_S3_USE_SSL: "true"
      SCCACHE_S3_KEY_PREFIX: aviso-server/
      SCCACHE_BASEDIRS: ${{ github.workspace }}
      SCCACHE_IGNORE_SERVER_IO_ERROR: "1"
      AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
      TMPDIR: ${{ github.workspace }}/.tmp
      # Tests bind 127.0.0.1 only and must never traverse a proxy.
      NO_PROXY: "127.0.0.1,localhost"
    steps:
      - uses: actions/checkout@v5
      - run: mkdir -p "$TMPDIR"

      - name: Format check (aviso-server)
        run: cargo fmt -- --check

      - name: Format check (aviso-validators subcrate)
        run: cargo fmt --manifest-path aviso-validators/Cargo.toml -- --check

      - name: Format check (aviso-ecpds subcrate)
        run: cargo fmt --manifest-path aviso-ecpds/Cargo.toml -- --check

      - name: Clippy (default features)
        run: cargo clippy --locked --all-targets -- -D warnings

      - name: Clippy (--features ecpds)
        run: cargo clippy --locked --features ecpds --all-targets -- -D warnings

      - name: Clippy (aviso-validators subcrate)
        run: cargo clippy --locked --manifest-path aviso-validators/Cargo.toml --all-targets -- -D warnings

      - name: Clippy (aviso-ecpds subcrate)
        run: cargo clippy --locked --manifest-path aviso-ecpds/Cargo.toml --all-targets -- -D warnings

      - name: Build (default + ecpds)
        run: |
          cargo build --locked --all-targets
          cargo build --locked --features ecpds --all-targets

      - name: Test (default features)
        run: |
          unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY all_proxy
          cargo test --locked -- --test-threads=1

      - name: Test (--features ecpds)
        run: |
          unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY all_proxy
          cargo test --locked --features ecpds -- --test-threads=1

      - name: Test (aviso-validators subcrate)
        run: |
          unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY all_proxy
          cargo test --locked --manifest-path aviso-validators/Cargo.toml -- --test-threads=1

      - name: Test (aviso-ecpds subcrate)
        run: |
          unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY all_proxy
          cargo test --locked --manifest-path aviso-ecpds/Cargo.toml -- --test-threads=1

      - name: Lockfiles unchanged
        run: |
          git config --global --add safe.directory "$GITHUB_WORKSPACE"
          git diff --exit-code Cargo.lock aviso-validators/Cargo.lock aviso-ecpds/Cargo.lock

      - name: sccache stats
        if: always()
        run: sccache --show-adv-stats

      - name: Cleanup
        if: always()
        run: rm -rf "$TMPDIR" target aviso-validators/target aviso-ecpds/target

  # ── Guard: the consumed image tag must track .github/ci/VERSION ──
  # ci-image.yml builds the image from VERSION, but this workflow and
  # docs-sites.yaml pin the tag literally. Fail loud if they drift so a
  # VERSION bump cannot silently keep running on the old image.
  image-pin:
    name: image pin
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: CI image tag matches .github/ci/VERSION
        run: |
          version="$(tr -d '[:space:]' < .github/ci/VERSION)"
          expected="image: eccr.ecmwf.int/aviso/server-ci:${version}"
          rc=0
          for wf in .github/workflows/ci.yaml .github/workflows/docs-sites.yaml; do
            if ! grep -qF "$expected" "$wf"; then
              echo "::error file=$wf::expected '$expected' (from .github/ci/VERSION) not found"
              rc=1
            fi
          done
          exit "$rc"

  # ── Aggregate gate: the single required status check ────────────
  ci-pass:
    name: ci-pass
    if: always()
    needs: [rust, image-pin]
    runs-on: ubuntu-latest
    steps:
      - name: Verify every required job succeeded
        run: |
          results='${{ join(needs.*.result, ',') }}'
          echo "needs results: $results"
          case ",$results," in
            *,failure,*|*,cancelled,*|*,skipped,*)
              echo "::error::a required job did not succeed (fork PRs skip the trusted jobs by design)"
              exit 1
              ;;
          esac
          echo "all required jobs succeeded"