From a8e1fd36b6a1efce196d0d100017eb08d1abc73a Mon Sep 17 00:00:00 2001 From: bootc-dev Bot Date: Tue, 3 Mar 2026 15:12:41 +0000 Subject: [PATCH] Sync common files from infra repository Synchronized from bootc-dev/infra@61b769aee4ec165dcbfd993aeaa10a7fae2cb629. Signed-off-by: bootc-dev Bot --- .bootc-dev-infra-commit.txt | 2 +- .devcontainer/devcontainer.json | 14 +- .github/actions/bootc-ubuntu-setup/action.yml | 27 +-- .github/actions/setup-rust/action.yml | 20 ++ .github/workflows/openssf-scorecard-gate.yml | 28 +++ .github/workflows/rebase.yml | 45 +++++ AGENTS.md | 12 ++ REVIEW.md | 173 ++++++++++++++++++ 8 files changed, 294 insertions(+), 27 deletions(-) create mode 100644 .github/actions/setup-rust/action.yml create mode 100644 .github/workflows/openssf-scorecard-gate.yml create mode 100644 .github/workflows/rebase.yml create mode 100644 REVIEW.md diff --git a/.bootc-dev-infra-commit.txt b/.bootc-dev-infra-commit.txt index aaaf938a..ff1331ff 100644 --- a/.bootc-dev-infra-commit.txt +++ b/.bootc-dev-infra-commit.txt @@ -1 +1 @@ -b23aa64010d014befa5adc5bc54363b6fb60a3e4 +61b769aee4ec165dcbfd993aeaa10a7fae2cb629 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 26e62a2d..678171be 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,9 +13,17 @@ }, "features": {}, "runArgs": [ - // Because we want to be able to run podman and also use e.g. /dev/kvm - // among other things - "--privileged" + // In general we hope that the outer environment has set up + // a user namespace to keep this secure. + "--cap-add=all", + // Required for mounting /proc in nested user namespace + "--security-opt", "label=disable", + // Allows access to /proc paths needed for nested containers + "--security-opt", "unmask=/proc/*", + // Device access for nested containers and VMs + "--device", "/dev/net/tun", + // I always want KVM + "--device", "/dev/kvm" ], "postCreateCommand": { // Our init script diff --git a/.github/actions/bootc-ubuntu-setup/action.yml b/.github/actions/bootc-ubuntu-setup/action.yml index 8353a6e4..783c5050 100644 --- a/.github/actions/bootc-ubuntu-setup/action.yml +++ b/.github/actions/bootc-ubuntu-setup/action.yml @@ -47,22 +47,7 @@ runs: IDV=$(. /usr/lib/os-release && echo ${ID}-${VERSION_ID}) test "${IDV}" = "ubuntu-24.04" # plucky is the next release - # Ubuntu uses different mirrors for different architectures: - # - amd64: azure.archive.ubuntu.com/ubuntu (on Azure runners) - # - arm64: ports.ubuntu.com/ubuntu-ports - case "$(arch)" in - x86_64) - mirror="http://azure.archive.ubuntu.com/ubuntu" - ;; - aarch64) - mirror="http://ports.ubuntu.com/ubuntu-ports" - ;; - *) - echo "Unsupported architecture: $(arch)" >&2 - exit 1 - ;; - esac - echo "deb ${mirror} plucky universe main" | sudo tee /etc/apt/sources.list.d/plucky.list + echo 'deb http://azure.archive.ubuntu.com/ubuntu plucky universe main' | sudo tee /etc/apt/sources.list.d/plucky.list /bin/time -f '%E %C' sudo apt update # skopeo is currently older in plucky for some reason hence --allow-downgrades /bin/time -f '%E %C' sudo apt install -y --allow-downgrades crun/plucky podman/plucky skopeo/plucky just @@ -73,13 +58,8 @@ runs: set -xeuo pipefail echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules - # Only trigger if /dev/kvm exists (may not be available on arm64 runners) - if [ -e /dev/kvm ]; then - sudo udevadm trigger --name-match=kvm - ls -l /dev/kvm - else - echo "Note: /dev/kvm not available on this runner" - fi + sudo udevadm trigger --name-match=kvm + ls -l /dev/kvm # Used by a few workflows, but generally useful - name: Set architecture variable id: set_arch @@ -91,6 +71,7 @@ runs: shell: bash run: | set -xeuo pipefail + # renovate: datasource=github-releases depName=bootc-dev/bcvk export BCVK_VERSION=0.10.0 # see https://github.com/bootc-dev/bcvk/issues/176 /bin/time -f '%E %C' sudo apt install -y libkrb5-dev pkg-config libvirt-dev genisoimage qemu-utils qemu-kvm virtiofsd libvirt-daemon-system python3-virt-firmware diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml new file mode 100644 index 00000000..f2f5e067 --- /dev/null +++ b/.github/actions/setup-rust/action.yml @@ -0,0 +1,20 @@ +name: 'Setup Rust' +description: 'Install Rust toolchain with caching and nextest' +runs: + using: 'composite' + steps: + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install nextest + uses: taiki-e/install-action@v2 + with: + tool: nextest + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-all-crates: true + # Only generate caches on push to git main + save-if: ${{ github.ref == 'refs/heads/main' }} + # Suppress actually using the cache for builds running from + # git main so that we avoid incremental compilation bugs + lookup-only: ${{ github.ref == 'refs/heads/main' }} diff --git a/.github/workflows/openssf-scorecard-gate.yml b/.github/workflows/openssf-scorecard-gate.yml new file mode 100644 index 00000000..830564a2 --- /dev/null +++ b/.github/workflows/openssf-scorecard-gate.yml @@ -0,0 +1,28 @@ +# Gate PRs on OpenSSF Scorecard regressions. +# +# See also: https://github.com/ossf/scorecard/issues/1270 +name: OpenSSF Scorecard + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + scorecard: + name: Scorecard + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Check for regressions + uses: bootc-dev/actions/openssf-scorecard@main + with: + base-sha: ${{ github.event.pull_request.base.sha }} + head-sha: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml new file mode 100644 index 00000000..68f329a4 --- /dev/null +++ b/.github/workflows/rebase.yml @@ -0,0 +1,45 @@ +name: Automatic Rebase +on: + pull_request: + types: [labeled] + +permissions: + contents: read + +jobs: + rebase: + name: Rebase + if: github.event.label.name == 'needs-rebase' + runs-on: ubuntu-latest + steps: + - name: Generate Actions Token + id: token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + + - name: Checkout + uses: actions/checkout@v6 + with: + token: ${{ steps.token.outputs.token }} + fetch-depth: 0 + + - name: Automatic Rebase + uses: peter-evans/rebase@v4 + with: + token: ${{ steps.token.outputs.token }} + + - name: Remove needs-rebase label + if: always() + uses: actions/github-script@v8 + with: + github-token: ${{ steps.token.outputs.token }} + script: | + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + name: 'needs-rebase' + }); diff --git a/AGENTS.md b/AGENTS.md index 6f7982c7..98ff2478 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,18 @@ When generating substantial amounts of code, you SHOULD include an `Assisted-by: TOOLNAME (MODELNAME)`. For example, `Assisted-by: Goose (Sonnet 4.5)`. +## Code guidelines + +The [REVIEW.md](REVIEW.md) file describes expectations around +testing, code quality, commit organization, etc. If you're +creating a change, it is strongly encouraged after each +commit and especially when you think a task is complete +to spawn a subagent to perform a review using guidelines (alongside +looking for any other issues). + +If you are performing a review of other's code, the same +principles apply. + ## Follow other guidelines Look at the project README.md and look for guidelines diff --git a/REVIEW.md b/REVIEW.md new file mode 100644 index 00000000..80e6f4d8 --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,173 @@ +# Code Review Guidelines + +These guidelines are derived from analysis of code reviews across the bootc-dev +organization (October–December 2024). They represent the collective expectations +and standards that have emerged from real review feedback. + +## Testing + +Tests are expected for all non-trivial changes - unit and integration by default. + +If there's something that's difficult to write a test for at the current time, +please do at least state if it was tested manually. + +### Choosing the Right Test Type + +Unit tests are appropriate for parsing logic, data transformations, and +self-contained functions. Use integration tests for anything that involves +running containers or VMs. + +Default to table-driven tests instead of having a separate unit test per +case. Especially LLMs like to generate the latter, but it can become +too verbose. Context windows matter to both humans and LLMs reading the +code later (this applies outside of unit tests too of course, but it's +easy to generate a *lot* of code for unit tests unnecessarily). + +### Separating Parsing from I/O + +A recurring theme is structuring code for testability. Split parsers from data +reading: have the parser accept a `&str`, then have a separate function that +reads from disk and calls the parser. This makes unit testing straightforward +without filesystem dependencies. + +### Test Assertions + +Make assertions strict and specific. Don't just verify that code "didn't crash"— +check that outputs match expected values. When adding new commands or output +formats, tests should verify the actual content, not just that something was +produced. + +## Code Quality + +### Parsing Structured Data + +Never parse structured data formats (JSON, YAML, XML) with text tools like `grep` +or `sed`. + +### Shell Scripts + +Try to avoid having shell script longer than 50 lines. This commonly occurs +in build system and tests. For the build system, usually there's higher +level ways to structure things (Justfile e.g.) and several of our projects +use the `cargo xtask` pattern to put arbitrary "glue" code in Rust using +the `xshell` crate to keep it easy to run external commands. + +### Constants and Magic Values + +Extract magic numbers into named constants. Any literal number that isn't +immediately obvious—buffer sizes, queue lengths, retry counts, timeouts—should +be a constant with a descriptive name. The same applies to magic strings: +deduplicate repeated paths, configuration keys, and other string literals. + +When values aren't self-explanatory, add a comment explaining why that specific +value was chosen. + +### Don't ignore (swallow) errors + +Avoid the `if let Ok(v) = ... { }` in Rust, or `foo 2>/dev/null || true` +pattern in shell script by default. Most errors should be propagated by +default. If not, it's usually appropriate to at least log error messages +at a `tracing::debug!` or equivalent level. + +Handle edge cases explicitly: missing data, malformed input, offline systems. +Error messages should provide clear context for diagnosis. + +### Code Organization + +Separate concerns: I/O operations, parsing logic, and business logic belong in +different functions. Structure code so core logic can be unit tested without +external dependencies. + +It can be OK to duplicate a bit of code in a slightly different form twice, +but having it happen in 3 places asks for deduplication. + +## Commits and Pull Requests + +### Commit Organization + +Break changes into logical, atomic commits. Reviewers appreciate being able to +follow your reasoning: "Especially grateful for breaking it up into individual +commits so I can more easily follow your train of thought." + +Preparatory refactoring should be separate from behavioral changes. Each commit +should tell a clear story and be reviewable independently. Commit messages should +explain the "why" not just the "what," and use imperative mood ("Add feature" +not "Added feature"). + +### PR Descriptions + +PRs should link to the issues they address using `Closes:` or `Fixes:` with +full URLs. One reviewer noted: "I edited this issue just now to have +`Closes: ` but let's try to be sure we're doing that kind of thing in +general in the future." + +Document known limitations and caveats explicitly. When approaches have tradeoffs +or don't fully solve a problem, say so. For complex investigations, use collapsible +`
` sections to include debugging notes without cluttering the main +description. + +Think about broader implications: "But we'll have this problem across all repos +right?" Consider how your change affects the wider ecosystem. + +### Keeping PRs Current + +Keep PRs rebased on main. When CI failures are fixed in other PRs, rebase to +pick up the fixes. Reference the fixing PR when noting that a rebase is needed. + +### Before Merge + +Self-review your diff before requesting review. Catch obvious issues yourself +rather than burning reviewer cycles. + +Do not add `Signed-off-by` lines automatically—these require explicit human +action after review. If code was AI-assisted, include an `Assisted-by:` trailer +indicating the tool and model used. + + +## Architecture and Design + +### Workarounds vs Proper Fixes + +When implementing a workaround, document where the proper fix belongs and link +to relevant upstream issues. Invest time investigating proper fixes before +settling on workarounds. + +### Cross-Project Considerations + +Prefer pushing fixes upstream when the root cause is in a dependency. Reduce +scope where possible; don't reimplement functionality that belongs elsewhere. + +When multiple systems interact (like Renovate and custom sync tooling), be +explicit about which system owns what and how they coordinate. + +### Avoiding Regressions + +Verify that new code paths handle all cases the old code handled. When rewriting +functionality, ensure equivalent coverage exists. + +### Review Requirements + +When multiple contributors co-author a PR, bring in an independent reviewer. + +## Rust-Specific Guidance + +Prefer rustix over `libc`. All `unsafe` code must be very carefully +justified. + +### Dependencies + +New dependencies should be justified. Glance at existing reverse dependencies +on crates.io to see if a crate is widely used. Consider alternatives: "I'm +curious if you did any comparative analysis at all with alternatives?" + +Prefer well-maintained crates with active communities. Consider `cargo deny` +policies when adding dependencies. + +### API Design + +When adding new commands or options, think about machine-readable output early. +JSON is generally preferred for that. + +Keep helper functions in appropriate modules. Move command output formatting +close to the CLI layer, keeping core logic functions focused on their primary +purpose.