Skip to content

chore(dev): mix firecracker.install (#34) #147

chore(dev): mix firecracker.install (#34)

chore(dev): mix firecracker.install (#34) #147

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
# Cancel a previous in-progress run for the same ref when a new one starts.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
elixir:
name: Elixir (mix check)
runs-on: ubuntu-latest
env:
# setup-beam caches hex/rebar; MIX_ENV is overridden per-step where :test
# is needed (ecto + test). compile/format/credo/dialyzer run in :dev so the
# dev-only deps (dialyxir, credo) are available.
MIX_ENV: dev
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v7
- uses: erlef/setup-beam@v1
id: beam
with:
elixir-version: "1.20"
otp-version: "28"
- name: Cache deps and _build
uses: actions/cache@v6
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-
- name: Cache dialyzer PLTs
uses: actions/cache@v6
with:
path: priv/plts
key: ${{ runner.os }}-plt-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-plt-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-
- name: Install dependencies
run: mix deps.get
# protoc + the protoc-gen-elixir escript drive the `:grpc_gen` Mix compiler,
# which generates the (gitignored) gRPC bindings before the Elixir compiler.
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Install protoc-gen-elixir
run: mix escript.install hex protobuf 0.17.0 --force
- name: Compile (warnings as errors)
# --force matches the `mix check` alias: re-surfaces warnings even on a
# cached _build, where an unchanged module would otherwise be skipped.
run: mix compile --warnings-as-errors --force
- name: Check formatting
run: mix format --check-formatted
- name: Credo
run: mix credo --strict
- name: Create and migrate test DB
env:
MIX_ENV: test
# -r names the repo explicitly: ecto_repos lives in mix.exs application
# env (so it works when hyper is a dependency), which the ecto.* mix
# tasks don't discover here -- without -r they no-op and tests then hit
# a missing database.
run: |
mix ecto.create -r Hyper.Img.Db.Repo
mix ecto.migrate -r Hyper.Img.Db.Repo
- name: Test + coverage (warnings as errors)
env:
MIX_ENV: test
# coveralls.json wraps `mix test` (so --no-start / --warnings-as-errors
# still apply) and writes cover/excoveralls.json for the Codecov upload.
# --no-start: the supervision tree provisions a real Firecracker host
# under /srv/hyper, unavailable on a CI runner.
run: mix coveralls.json --no-start --warnings-as-errors
# !cancelled() (not always()) uploads results on test PASS or FAIL -- the
# point of Test Analytics is failure/flake history -- while still skipping
# if the workflow was cancelled. junit_formatter writes the XML at end of
# run regardless of pass/fail. report_type: test_results sends JUnit to
# Test Analytics via codecov-action (the standalone test-results-action is
# deprecated and pinned to Node 20).
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: _build/test/junit.xml
flags: elixir
report_type: test_results
# The artifact feeds the workflow_run publisher (test-results.yml), which
# runs EnricoMi with a write token -- so the GitHub Check works even on
# fork PRs, where this job's token is read-only.
- name: Upload Elixir test results artifact
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: elixir-test-results
path: _build/test/junit.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: cover/excoveralls.json
flags: elixir
fail_ci_if_error: true
- name: Dialyzer
run: mix dialyzer
rust:
name: Rust (suidhelper)
runs-on: ubuntu-latest
defaults:
run:
working-directory: native/suidhelper
steps:
- uses: actions/checkout@v7
# rustup reads native/suidhelper/rust-toolchain.toml on the first cargo
# call and installs the pinned nightly toolchain, components, and targets.
- name: Show toolchain
run: rustup show
- uses: Swatinem/rust-cache@v2
with:
workspaces: native/suidhelper
- name: rustfmt
run: cargo fmt --check
- name: clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Install cargo-llvm-cov and nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov,nextest
- name: test + coverage
# `nextest` runs the suite while llvm-cov collects coverage AND nextest
# writes target/nextest/ci/junit.xml (see .config/nextest.toml). One run,
# two artifacts: lcov.info for coverage, junit.xml for Test Analytics.
run: cargo llvm-cov nextest --profile ci --all-features --lcov --output-path lcov.info
# !cancelled() uploads on pass OR fail (see elixir job). The action runs at
# repo root, so the path is fully qualified -- working-directory only
# affects `run:` steps, not `uses:` actions. report_type: test_results
# sends JUnit via codecov-action (test-results-action is deprecated/Node 20).
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: native/suidhelper/target/nextest/ci/junit.xml
flags: rust
report_type: test_results
# Repo-relative path: upload-artifact is an action, so the rust job's
# `working-directory: native/suidhelper` default does NOT apply.
- name: Upload Rust test results artifact
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: rust-test-results
path: native/suidhelper/target/nextest/ci/junit.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
# working-directory only applies to `run:` steps, not actions.
files: native/suidhelper/lcov.info
flags: rust
fail_ci_if_error: true
suidhelper-privileged:
name: Rust suidhelper (privileged E2E)
runs-on: ubuntu-latest
defaults:
run:
working-directory: native/suidhelper
steps:
- uses: actions/checkout@v7
# rustup reads native/suidhelper/rust-toolchain.toml and installs the pinned
# nightly on the first cargo call.
- name: Show toolchain
run: rustup show
- uses: Swatinem/rust-cache@v2
with:
workspaces: native/suidhelper
- name: Install nextest
uses: taiki-e/install-action@v2
with:
tool: nextest
# Root-gated cases are tagged by name: every test that needs root ends in
# `_as_root` and self-skips when run unprivileged. We select them by that
# suffix (via sudo -E so the runner's cargo/rustup home and PATH are kept)
# so the root-only paths -- sys-test ok, fake-bin argv capture, mknod/chown
# jail build -- actually run. A new root test is picked up automatically by
# following the naming convention; no test-binary enumeration to maintain.
# The rest of the suite (owner-axis tests assume a non-root uid) stays in
# the non-root `rust` job above.
- name: Gated tests as root
run: |
sudo -E env "PATH=$PATH" \
cargo nextest run --features insecure_test_seams -E 'test(/_as_root$/)'
# Uploads the triggering event payload so the workflow_run publisher
# (test-results.yml) can map results back to the originating PR -- required
# for PR comments on fork PRs. Tiny job, always runs.
event_file:
name: Upload event file
runs-on: ubuntu-latest
steps:
- name: Upload
uses: actions/upload-artifact@v7
with:
name: Event File
path: ${{ github.event_path }}