name: CI on: workflow_dispatch: pull_request: branches: [ "main" ] push: branches: - "**" tags: [ "v*" ] permissions: contents: read pull-requests: read concurrency: group: ci-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: prek: name: Lint, format & test runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 # Toolchain version + components come from rust-toolchain.toml. rustflags is # cleared so plain builds don't fail on warnings; the clippy hook still does. - uses: actions-rust-lang/setup-rust-toolchain@v1 with: rustflags: '' - name: Install SQLite3 run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev # just drives the prek recipes; mise provides the pinned prek (mise.toml). - uses: taiki-e/install-action@v2 with: tool: just - uses: jdx/mise-action@v4 # Run the same prek hooks devs run locally; .pre-commit-config.yaml is the # source of truth. Tests are a separate step for visible timing. - name: Lint & format (prek hooks, excluding tests) run: just prek-run-on-all --skip test-unit - name: Unit tests run: just test # The prek job never builds a container image, so a bump of the Dockerfile's # base image reaches main unvalidated and fails later, in Publish, after # ghcr.io/etkecc/baibot:latest has already been attempted. These two jobs close # that gap: decide whether a Dockerfile changed, and if so build the image the # way Publish does - but without pushing anything. # # The build is gated rather than unconditional because it is a full Rust # release build; running it on every push would turn a ~1 minute pipeline into # a ~10 minute one for changes that cannot affect the image. docker-gate: name: Decide whether the image needs building runs-on: ubuntu-latest outputs: build: ${{ steps.decide.outputs.build }} steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Look for Dockerfile changes against main id: decide run: | if [ "${{ github.event_name }}" = 'workflow_dispatch' ]; then echo 'Forced via workflow_dispatch.' echo 'build=true' >> "$GITHUB_OUTPUT" exit 0 fi # Publish builds and pushes from main, so a main-side build here would # be redundant. This gate exists for branches, before they merge. if [ "${{ github.ref_name }}" = 'main' ]; then echo 'On main; Publish covers this.' echo 'build=false' >> "$GITHUB_OUTPUT" exit 0 fi git fetch --no-tags origin main if git diff --name-only origin/main HEAD -- Dockerfile | grep -q .; then echo 'A Dockerfile changed; the image will be built.' echo 'build=true' >> "$GITHUB_OUTPUT" else echo 'No Dockerfile changed.' echo 'build=false' >> "$GITHUB_OUTPUT" fi docker-build: name: Build the container image (without publishing it) needs: docker-gate if: needs.docker-gate.outputs.build == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 # No build cache on purpose: a bump of the base image is exactly the case # where a cold build is the honest test. - name: Build uses: docker/build-push-action@v7 with: push: false