chore(dev): mix firecracker.install (#34)
#147
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |