From f4f2384fd2c2bb0072a387f8dd2501239205e6a0 Mon Sep 17 00:00:00 2001 From: Han Lin Date: Fri, 30 Oct 2026 23:33:11 +0800 Subject: [PATCH] ci: regenerate THIRD-PARTY-NOTICES.txt on Dependabot pull requests Dependabot cannot run the script, so bumping a crate linked into the executables failed the notices check, which also skipped clippy, the tests and the MSI. The new workflow commits the regenerated file to the pull request branch and starts CI on that commit with workflow_dispatch, because a GITHUB_TOKEN push does not start CI by itself. No secrets are needed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 1 + .github/workflows/dependabot-notices.yml | 79 ++++++++++++++++++++++++ CONTRIBUTING.md | 2 +- docs/DESIGN.md | 2 +- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/dependabot-notices.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa3182b..3ea3ffc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,7 @@ jobs: - name: cargo fmt run: cargo fmt --all -- --check + # dependabot-notices.yml fixes this on Dependabot pull requests. - name: THIRD-PARTY-NOTICES.txt up to date run: ./scripts/third-party-notices.ps1 -Check diff --git a/.github/workflows/dependabot-notices.yml b/.github/workflows/dependabot-notices.yml new file mode 100644 index 0000000..8139111 --- /dev/null +++ b/.github/workflows/dependabot-notices.yml @@ -0,0 +1,79 @@ +# Keeps THIRD-PARTY-NOTICES.txt in step with Cargo.lock on Dependabot pull requests. +# +# Dependabot cannot run scripts/third-party-notices.ps1, so a bump of a crate linked +# into the executables fails that check in ci.yml, which also skips clippy, the tests +# and the MSI. This workflow regenerates the file, commits it to the pull request +# branch and starts CI on the new commit. Nothing here merges anything. +# +# - Runs triggered by Dependabot start with a read-only token; the permissions key +# below raises it, and no secrets are needed. +# - A push made with GITHUB_TOKEN does not start CI on its own (its pull_request run +# waits for approval), but a workflow_dispatch event always does, and the checks +# land on the same commit. +# - "[dependabot skip]" in the commit message lets Dependabot keep rebasing the branch. +# A rebase drops the commit and this workflow adds it again. +# +# Run it by hand with a branch name for a branch it missed. +# +# Pinning follows release.yml: GitHub-owned actions use major tags, third-party actions +# that run next to the write token use commit SHAs. + +name: Dependabot notices + +on: + pull_request: + paths: + - Cargo.lock + workflow_dispatch: + inputs: + branch: + description: Branch to regenerate the file on + required: true + type: string + +permissions: + contents: read + +defaults: + run: + shell: pwsh + +jobs: + regenerate: + name: Regenerate THIRD-PARTY-NOTICES.txt + # This job can write to the repository, so a pull request only reaches it when + # Dependabot opened it from a branch of this repository. + if: >- + github.event_name == 'workflow_dispatch' || + (github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.head.repo.full_name == github.repository) + runs-on: windows-latest + permissions: + actions: write + contents: write + env: + BRANCH: ${{ inputs.branch || github.head_ref }} + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ env.BRANCH }} + + - uses: dtolnay/rust-toolchain@89b12181fb390509a0842a86cc55eeb8eb928c1d # stable + + - name: Regenerate the file + run: ./scripts/third-party-notices.ps1 + + - name: Commit it and start CI + env: + GH_TOKEN: ${{ github.token }} + run: | + if (-not (git status --porcelain -- THIRD-PARTY-NOTICES.txt)) { + Write-Host 'THIRD-PARTY-NOTICES.txt is already up to date.' + exit 0 + } + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add THIRD-PARTY-NOTICES.txt + git commit -m 'chore: regenerate THIRD-PARTY-NOTICES.txt [dependabot skip]' + git push origin "HEAD:$env:BRANCH" + gh workflow run ci.yml --ref $env:BRANCH diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d69cbbe..3a83dcb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,7 +89,7 @@ Expectations: - **Lints:** clippy (including the `pedantic` group configured in `Cargo.toml`) must be clean with `-D warnings`. If you need to silence a lint, use `#[expect(..., reason = "...")]` (it warns once the lint no longer fires) and keep it as narrow as possible. - **Unsafe code:** `unsafe` is allowed only where the Win32 and COM calls live: `src/audio.rs`, `src/console.rs`, `src/gui/`, the known-folder fallback in `src/config.rs`, `src/timing.rs` and the enumeration helper in `tests/real_device.rs`. Every `unsafe` block needs a `// SAFETY:` comment stating the invariant it relies on. - **Tests:** new logic comes with unit tests. Keep pure logic (toggle decision, config parsing, CLI parsing, encoding helpers) separate from COM so it can be tested on CI runners, which have no audio devices. -- **Dependencies:** the startup-time budget and the zero-runtime-dependency goal matter. Discuss any new crate in an issue before adding it, and regenerate `THIRD-PARTY-NOTICES.txt` (`pwsh scripts/third-party-notices.ps1`) whenever `Cargo.lock` changes. +- **Dependencies:** the startup-time budget and the zero-runtime-dependency goal matter. Discuss any new crate in an issue before adding it, and regenerate `THIRD-PARTY-NOTICES.txt` (`pwsh scripts/third-party-notices.ps1`) whenever `Cargo.lock` changes. The `dependabot-notices.yml` workflow does this on Dependabot pull requests. ### Real-device tests diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 8097258..412bd3c 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -184,4 +184,4 @@ Decided during implementation and review; the sections above keep the original t - §10: `assets/app.manifest` carries the version by hand; a unit test fails when it differs from Cargo.toml. `scripts/` holds `e2e.ps1` and `third-party-notices.ps1` (writes `THIRD-PARTY-NOTICES.txt` from `Cargo.lock`; CI runs it with `-Check`); there is no `build-all.ps1` (`cargo build --release` builds both exes, `installer/build-msi.ps1` the MSI). - §11: `unsafe` also appears in `config.rs` (the known-folder fallback), `timing.rs` (`GetProcessTimes`) and the capture-enumeration helper in `tests/real_device.rs`, each with a `// SAFETY:` comment. The round-trip integration test runs only when `TOGGLE_AUDIO_TEST_DEVICES=;` is set, so `cargo test -- --ignored` never leaves a default changed; one of those tests (`policy_config_reasserts_the_current_defaults`) does call `SetDefaultEndpoint`, with each role's current default, through both interfaces. `tests/cli.rs` runs both binaries (usage errors, `--help`, `--version`, `toggle` without a usable configuration, redirected output bytes) without audio devices, so it runs in CI, and kills a run that does not exit within 30 s. - §13: the installer UI is `WixUI_FeatureTree` rather than `WixUI_InstallDir`, so "Add to PATH" (feature `PathEnv`) can be switched off in the Custom Setup page, which also has the Browse button for the folder; `ARPNOMODIFY` is not set, so Installed apps keeps Modify for changing that choice later. `LICENSE` is installed as `LICENSE.txt`, next to `THIRD-PARTY-NOTICES.txt` (the licenses of the statically linked crates, also in the zip), and `README.md` is installed when it exists at build time. `installer/License.rtf` is generated by `installer/make-license-rtf.ps1`; `build-msi.ps1` fails when it is stale, rejects pre-release versions (MSI versions have no pre-release field), fails when either exe imports the dynamic C runtime (`VCRUNTIME140.dll`, `api-ms-win-crt-*.dll`), runs cargo from the repository root so `.cargo/config.toml` (+crt-static) applies, reads ProductVersion and UpgradeCode back from the package and writes a `.sha256` file. Build, install and winget details: `docs/packaging.md`. -- §14: the checksum file is `SHA256SUMS.txt`. `ci.yml` also runs `cargo check` on the minimum Rust version (1.85, `rust-version` in Cargo.toml) and checks that `THIRD-PARTY-NOTICES.txt` matches `Cargo.lock`. `release.yml` rejects pre-release versions, which the MSI cannot represent. It also carries SignPath Foundation code signing, off until the SignPath secret and variables exist: one request with the MSI built from the unsigned exes, deep-signed by SignPath (exes inside, then the MSI), the signed exes extracted with `msiexec /a` for the zip, signatures verified before hashing, and a `workflow_dispatch` dry run that publishes nothing (`docs/signing.md`, `installer/signpath/`). +- §14: the checksum file is `SHA256SUMS.txt`. `ci.yml` also runs `cargo check` on the minimum Rust version (1.85, `rust-version` in Cargo.toml) and checks that `THIRD-PARTY-NOTICES.txt` matches `Cargo.lock`; `dependabot-notices.yml` regenerates that file on Dependabot pull requests and starts CI on the new commit. `release.yml` rejects pre-release versions, which the MSI cannot represent. It also carries SignPath Foundation code signing, off until the SignPath secret and variables exist: one request with the MSI built from the unsigned exes, deep-signed by SignPath (exes inside, then the MSI), the signed exes extracted with `msiexec /a` for the zip, signatures verified before hashing, and a `workflow_dispatch` dry run that publishes nothing (`docs/signing.md`, `installer/signpath/`).