diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..561262f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,43 @@ +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +# +# The cooldown lets a compromised publish be noticed before we propose it. It +# applies to version updates only -- security updates ignore it. +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single pull request + cooldown: + default-days: 7 + schedule: + interval: weekly + day: "thursday" + time: "08:00" + timezone: "America/New_York" + - package-ecosystem: cargo + directory: / + cooldown: + default-days: 7 + schedule: + interval: weekly + day: "thursday" + time: "08:00" + timezone: "America/New_York" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch"] + - package-ecosystem: npm + directory: /ui + cooldown: + default-days: 7 + schedule: + interval: weekly + day: "thursday" + time: "08:00" + timezone: "America/New_York" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch"] diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 557c2f0..748028f 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -21,13 +21,17 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - - uses: actions/checkout@v5 - - uses: taiki-e/install-action@mdbook + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false + - uses: taiki-e/install-action@decb84fb327e5b809630c914eb4d1ca67cf59966 # v2.85.14 + with: + tool: mdbook - name: Build run: mdbook build docs - - uses: actions/configure-pages@v5 - - uses: actions/upload-pages-artifact@v3 + - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 + - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 with: path: docs/book - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 725b42e..fd9e95b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,20 +9,31 @@ env: CARGO_TERM_COLOR: always RUSTC_WRAPPER: sccache +# Default to read-only; jobs that need more grant it explicitly. +permissions: + contents: read + jobs: ci: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 with: + toolchain: stable components: clippy, rustfmt - - uses: taiki-e/install-action@cargo-make - - uses: taiki-e/install-action@nextest + - uses: taiki-e/install-action@decb84fb327e5b809630c914eb4d1ca67cf59966 # v2.85.14 + with: + tool: cargo-make + - uses: taiki-e/install-action@decb84fb327e5b809630c914eb4d1ca67cf59966 # v2.85.14 + with: + tool: nextest - - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - name: Install iperf3 run: sudo apt-get update && sudo apt-get install -y iperf3 @@ -31,37 +42,43 @@ jobs: run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Build - run: cargo build --workspace --all-targets + run: cargo build --locked --workspace --all-targets - name: Test - run: cargo nextest run --workspace --profile ci + run: cargo nextest run --locked --workspace --profile ci - name: Clippy - run: cargo clippy --workspace --tests -- -D warnings + run: cargo clippy --locked --workspace --tests -- -D warnings - name: Format run: cargo make format-check + - name: Lockfile must not have moved + # cargo make takes no --locked; assert the lockfile instead. + run: git diff --exit-code Cargo.lock macos-check: runs-on: [self-hosted, macOS, arm64] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 with: + toolchain: stable components: clippy - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: "20" - - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - name: Build patchbay-vm - run: cargo build -p patchbay-vm + run: cargo build --locked -p patchbay-vm - name: Clippy patchbay-vm - run: cargo clippy -p patchbay-vm -- -D warnings + run: cargo clippy --locked -p patchbay-vm -- -D warnings - name: Container backend smoke test run: | @@ -75,7 +92,7 @@ jobs: exit 0 fi rustup target add aarch64-unknown-linux-musl - cargo build --release -p patchbay-vm -p patchbay-runner --bin patchbay --target aarch64-unknown-linux-musl + cargo build --locked --release -p patchbay-vm -p patchbay-runner --bin patchbay --target aarch64-unknown-linux-musl ./target/release/patchbay-vm --backend container run \ --patchbay-version "path:target/aarch64-unknown-linux-musl/release/patchbay" \ ./patchbay-cli/tests/fixtures/iperf-1to1-public.toml @@ -84,13 +101,17 @@ jobs: e2e: runs-on: [self-hosted, linux, x64] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 + with: + toolchain: stable - - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: "20" cache: npm @@ -115,7 +136,7 @@ jobs: run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true - name: Build Rust (bins + test targets) - run: cargo build --workspace --all-targets + run: cargo build --locked --workspace --all-targets - name: UI E2E working-directory: ui @@ -131,8 +152,10 @@ jobs: env: RUSTC_WRAPPER: "" steps: - - uses: actions/checkout@v6 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false + - uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1 with: arguments: --workspace --all-features command: check diff --git a/.github/workflows/release-rolling.yml b/.github/workflows/release-rolling.yml index 435d714..c3fdae4 100644 --- a/.github/workflows/release-rolling.yml +++ b/.github/workflows/release-rolling.yml @@ -4,8 +4,9 @@ on: push: branches: [main] +# Default to read-only; only the publish job needs write. permissions: - contents: write + contents: read env: CARGO_TERM_COLOR: always @@ -15,22 +16,25 @@ jobs: name: Build Linux Musl runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 with: + toolchain: stable targets: x86_64-unknown-linux-musl - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - name: Install musl toolchain run: sudo apt-get update && sudo apt-get install -y musl-tools - name: Build patchbay (linux musl) - run: cargo build -p patchbay-cli --all-features --release --target x86_64-unknown-linux-musl + run: cargo build --locked -p patchbay-cli --all-features --release --target x86_64-unknown-linux-musl - name: Build patchbay-serve (linux musl) - run: cargo build -p patchbay-server --release --target x86_64-unknown-linux-musl + run: cargo build --locked -p patchbay-server --release --target x86_64-unknown-linux-musl - name: Package linux artifacts run: | @@ -40,7 +44,7 @@ jobs: tar -C dist -czf dist/patchbay-x86_64-unknown-linux-musl.tar.gz patchbay-x86_64-unknown-linux-musl tar -C dist -czf dist/patchbay-serve-x86_64-unknown-linux-musl.tar.gz patchbay-serve-x86_64-unknown-linux-musl - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-linux-musl path: dist/*.tar.gz @@ -55,41 +59,52 @@ jobs: - runner: macos-14 target: aarch64-apple-darwin steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 with: + toolchain: stable targets: ${{ matrix.target }} - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - name: Build patchbay (${{ matrix.target }}) - run: cargo build -p patchbay-cli --all-features --release --target ${{ matrix.target }} + env: + RUST_TARGET: ${{ matrix.target }} + run: cargo build --locked -p patchbay-cli --all-features --release --target "$RUST_TARGET" - name: Package macOS artifact (${{ matrix.target }}) + env: + RUST_TARGET: ${{ matrix.target }} run: | mkdir -p dist - cp target/${{ matrix.target }}/release/patchbay dist/patchbay-${{ matrix.target }} - tar -C dist -czf dist/patchbay-${{ matrix.target }}.tar.gz patchbay-${{ matrix.target }} + cp "target/$RUST_TARGET/release/patchbay" "dist/patchbay-$RUST_TARGET" + tar -C dist -czf "dist/patchbay-$RUST_TARGET.tar.gz" "patchbay-$RUST_TARGET" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-macos-${{ matrix.target }} path: dist/*.tar.gz publish-rolling: name: Publish Rolling Release + permissions: + contents: write # moves the rolling tag and publishes the release runs-on: ubuntu-latest needs: - build-linux-musl - build-macos steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: + # stays on: this job moves the rolling tag with a bare `git push` + persist-credentials: true fetch-depth: 0 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: path: release-artifacts @@ -98,15 +113,21 @@ jobs: git tag -f rolling "$GITHUB_SHA" git push -f origin refs/tags/rolling + # `gh` ships on the runner, so this job needs no third-party action + # despite being the only one holding contents: write. - name: Publish rolling release - uses: softprops/action-gh-release@v2 - with: - tag_name: rolling - name: Rolling build - prerelease: true - make_latest: false - files: | - release-artifacts/**/*.tar.gz - body: | - Automated rolling release from commit ${{ github.sha }}. - This release is updated on every push to main. + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + notes="Automated rolling release from commit ${GITHUB_SHA}. + This release is updated on every push to main." + if gh release view rolling >/dev/null 2>&1; then + gh release edit rolling \ + --title "Rolling build" --prerelease --latest=false --notes "$notes" + else + gh release create rolling \ + --title "Rolling build" --prerelease --latest=false --notes "$notes" + fi + find release-artifacts -type f -name '*.tar.gz' -print0 \ + | xargs -0 gh release upload rolling --clobber diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml new file mode 100644 index 0000000..34b0a60 --- /dev/null +++ b/.github/workflows/zizmor.yaml @@ -0,0 +1,54 @@ +# Static analysis of our own workflows. The tree is clean, so this gates on +# every finding, not just High. +name: Workflow Lint + +on: + pull_request: + paths: ['.github/workflows/**', '.github/dependabot.yml', '.pinact.yaml'] + push: + branches: [main] + paths: ['.github/workflows/**', '.github/dependabot.yml', '.pinact.yaml'] + workflow_dispatch: + +permissions: + contents: read + +jobs: + zizmor: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false + - uses: taiki-e/install-action@decb84fb327e5b809630c914eb4d1ca67cf59966 # v2.85.14 + with: + tool: zizmor + # whole repo, not just workflows/ -- dependabot.yml is audited too + - run: zizmor --offline . + + pinact: + # Fails if a pinned SHA drifts from its version comment, or is younger + # than the cooldown in .pinact.yaml. + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + persist-credentials: false + # Release tarball + checksum instead of pinact-action, which wants + # contents:write to push commits. This job only reads. + - name: Install pinact + env: + PINACT_VERSION: "4.1.1" + run: | + set -euo pipefail + base="https://github.com/suzuki-shunsuke/pinact/releases/download/v${PINACT_VERSION}" + curl -fsSL -O "$base/pinact_linux_amd64.tar.gz" + curl -fsSL -O "$base/pinact_${PINACT_VERSION}_checksums.txt" + grep ' pinact_linux_amd64.tar.gz$' "pinact_${PINACT_VERSION}_checksums.txt" | sha256sum -c - + tar xzf pinact_linux_amd64.tar.gz pinact + install -m755 pinact /usr/local/bin/pinact + - run: pinact run --check --verify-comment --verify-min-age + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.pinact.yaml b/.pinact.yaml new file mode 100644 index 0000000..3456fbf --- /dev/null +++ b/.pinact.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/pinact/refs/heads/main/json-schema/pinact.json +version: 3 + +min_age: + value: 7 # days, matches the dependabot cooldown diff --git a/Cargo.lock b/Cargo.lock index 82ccd9b..8f85b20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,9 +384,9 @@ checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cpufeatures", @@ -756,7 +756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -989,9 +989,9 @@ checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "h2" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -2204,7 +2204,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2430,7 +2430,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2489,7 +2489,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2925,7 +2925,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3569,7 +3569,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/deny.toml b/deny.toml index 7ef1364..1391937 100644 --- a/deny.toml +++ b/deny.toml @@ -20,6 +20,7 @@ allow = [ [advisories] +yanked = "deny" ignore = [ "RUSTSEC-2024-0436", # paste -> netlink-packet-core ]