Skip to content

CI

CI #92

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
schedule:
# Daily 01:00 UTC — catches advisories that drop between PRs.
- cron: '0 1 * * *'
concurrency:
# One live run per ref. A fresh push to the same PR / branch cancels
# the in-flight run so we don't pay for stale CI.
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
rust:
name: Rust checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The rust-toolchain.toml at the repo root pins the channel and the
# required components (clippy, rustfmt). The action reads it, so no
# explicit `toolchain: stable` here.
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: Format (rustfmt --check)
run: cargo fmt --all -- --check
- name: Unit tests (lib)
run: cargo test --lib --locked
- name: Clippy gate
# Mirrors the canonical acceptance command pinned in
# devflow-docs/backlog.md (BL-P2-063). Keeping `--lib --tests`
# means test-site regressions can't hide behind the main gate.
# `--locked` matches the test/build steps so a drifted Cargo.lock
# can't silently change the dep tree clippy inspects.
run: cargo clippy --lib --tests --locked -- -D warnings
- name: Binary compile
# The lib-only tests miss `src/main.rs`; compile the bin
# explicitly so a stale main.rs can't merge.
run: cargo build --bin nexttui --locked
audit:
name: Dependency audit (cargo audit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Ownership: BL-P2-080 and BL-P2-081 share this job. Whichever PR merges
# first is the job owner — the other PR only needs `needs: devstack-integration`.
# The `if: false` placeholder below is removed once an actual devstack image
# digest is pinned (see BL-P2-081 activation checklist in backlog.md).
devstack-integration:
name: Devstack integration gate (BL-P2-080 FR-8)
runs-on: ubuntu-latest
# Dedicated concurrency group so a fresh push to the same branch/PR
# cancels the in-flight devstack run without touching the fast `rust` job.
concurrency:
group: devstack-integration-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# ------------------------------------------------------------------
# Step: start devstack container
# ------------------------------------------------------------------
# PLACEHOLDER: The devstack docker image digest has NOT been verified
# in this environment. This step is disabled (`if: false`) until the
# actual image digest is confirmed by the devstack infra owner
# (BL-P2-081 coordination or manual pinning).
#
# When the digest is known, replace the placeholder tag and remove the
# `if: false` guard. Everything else (healthcheck, log upload, test
# invocation) is already wired and will activate automatically.
#
# TODO: pin actual devstack image — BL-P2-080 CI gate
# ------------------------------------------------------------------
- name: Start devstack container (PLACEHOLDER — currently disabled)
id: devstack-setup
if: false # Disabled until image digest is confirmed (BL-P2-081)
run: |
docker run -d \
--name devstack \
-p 5000:5000 \
-p 5001:5001 \
opendevstack/devstack:placeholder
# TODO: replace `placeholder` with a pinned tag or digest, e.g.:
# opendevstack/devstack@sha256:<verified-digest>
# Floating `latest` is intentionally forbidden (supply-chain safety).
# ------------------------------------------------------------------
# Step: wait for Keystone endpoint to become healthy
# ------------------------------------------------------------------
# This step is also gated on the container being up. It runs only
# when `devstack-setup` ran (i.e., when `if: false` is lifted above).
- name: Wait for Keystone endpoint (localhost:5000)
id: devstack-health
if: steps.devstack-setup.outcome == 'success'
run: |
echo "Waiting for Keystone at localhost:5000 (max 120s)…"
deadline=$(($(date +%s) + 120))
until nc -z localhost 5000 2>/dev/null; do
if [ "$(date +%s)" -ge "$deadline" ]; then
echo "ERROR: Keystone did not become ready within 120s"
exit 1
fi
sleep 2
done
echo "Keystone is up."
# ------------------------------------------------------------------
# Step: run devstack integration tests
# ------------------------------------------------------------------
# Requires DEVSTACK_URL and DEVSTACK_TOKEN injected from GHA secrets
# (set up as repo/env secrets when the container is activated).
- name: Run devstack integration tests
id: devstack-test
if: steps.devstack-health.outcome == 'success'
env:
DEVSTACK_URL: ${{ vars.DEVSTACK_URL }}
DEVSTACK_TOKEN: ${{ secrets.DEVSTACK_TOKEN }}
run: |
cargo test \
--test devstack_directory \
--features devstack-integration \
-- --nocapture
# ------------------------------------------------------------------
# Step: collect docker logs on any failure
# ------------------------------------------------------------------
- name: Collect devstack container logs on failure
if: failure()
run: |
docker logs devstack > devstack.log 2>&1 || echo "(container not running)" > devstack.log
- name: Upload devstack logs as artifact
if: failure()
uses: actions/upload-artifact@v4
with:
name: devstack-logs-${{ github.run_id }}
path: devstack.log
retention-days: 7