diff --git a/.github/workflows/cargo-vet-refresh.yml b/.github/workflows/cargo-vet-refresh.yml index 6ab67c51b..afa25b2ed 100644 --- a/.github/workflows/cargo-vet-refresh.yml +++ b/.github/workflows/cargo-vet-refresh.yml @@ -8,8 +8,10 @@ # # Runs `cargo vet regenerate imports` weekly to fetch the latest # upstream audit records (Mozilla, Google, Bytecode Alliance, -# ISRG, Zcash). If `supply-chain/imports.lock` changed, opens a -# PR with the updated lockfile for maintainer review + merge. +# ISRG, Zcash). If `supply-chain/` changed, it REPORTS the change +# (job summary with the diff, and the refreshed files as an +# artifact) for the maintainer to apply locally; it opens no PR and +# pushes no branch. # # Why this is necessary: # @@ -20,43 +22,26 @@ # CI — the maintainer would end up pulling imports manually on # every Dependabot PR. # -# Scheduling a weekly refresh amortises that cost to ~5 min of -# PR-review once per week: the maintainer skims the diff (which -# is usually just added audit records, never modifications) and -# merges. +# Scheduling a weekly refresh amortises that cost to one look a +# week: the maintainer skims the reported diff (usually just added +# audit records, never modifications) and applies it on the next +# working branch. # -# Why we keep it a PR instead of pushing to `main` directly: +# Why a report and not a PR: # -# Branch protection on `main` requires reviewer sign-off (the -# `main-protection` ruleset's `PULL_REQUEST` rule). Direct -# pushes by automation would bypass that review surface. -# Routing the refresh through a PR keeps the maintainer's -# usual review + squash-merge flow intact. -# -# The matching `REQUIRED_SIGNATURES` rule used to make the -# intermediate bot commit on the PR branch unsignable without -# storing a GPG private key as a secret, which forced the -# maintainer to amend + force-push every refresh just to satisfy -# branch protection. Resolved by `sign-commits: true` on the -# `peter-evans/create-pull-request` step below (Phase -# cargo-vet-refresh-signed-commits, refs #272 force-push). -# See that step's comment for the mechanism. -# -# Operational note — CI won't auto-run on this PR: -# -# GitHub suppresses workflow runs for PRs opened by the default -# GITHUB_TOKEN (supply-chain defence against recursive automation -# loops). To trigger the Tier 1 checks, the maintainer needs to -# either close-and-reopen the PR, or push an empty commit. This -# is a known GitHub limitation; documented in the PR body we -# open so the reviewer sees it. +# Opening a pull request is the maintainer's call, never the CI's: +# every PR is a paid CI round, and a PR opened by GITHUB_TOKEN runs +# no workflows until someone closes and reopens it or pushes to it +# (#643 sat that way). The run ends with the diff in its summary; +# the maintainer applies it with +# `cargo vet regenerate imports && cargo vet prune` on the working +# branch, where the commit hooks vet it like any other change. The +# job therefore needs read access only. # # Cost: # -# ~30 seconds per week for the workflow itself (cargo-vet + -# curl). ~5 min per week for the maintainer to review + merge -# the PR (if there's a diff; often there isn't, in which case -# the workflow is a no-op). +# ~1 minute per week for the workflow itself (cargo-vet + curl); +# often there is no diff, and the run is a no-op. name: "🔄 cargo-vet: weekly import refresh" @@ -64,7 +49,7 @@ on: schedule: # Mondays at 08:00 UTC — gives Mozilla/Google/BA the full # weekend-plus to land their own weekly audit updates before - # we fetch them. Chose Monday morning so the PR is fresh for + # we fetch them. Chose Monday morning so the report is fresh for # the start-of-week review. - cron: "0 8 * * 1" @@ -73,12 +58,9 @@ on: # upstream audits before re-checking). workflow_dispatch: -# Minimum permissions needed: -# contents: write — push the refresh branch -# pull-requests: write — open / update the refresh PR +# Read-only: the run reports, it writes nothing to the repository. permissions: - contents: write - pull-requests: write + contents: read # Avoid overlapping runs; latest wins. Unlikely to matter at # weekly cadence but defensive for manual dispatch races. @@ -121,7 +103,7 @@ jobs: prefix-key: "vet-refresh" - name: Install cargo-vet - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-vet @@ -142,8 +124,8 @@ jobs: # Sanity check: the refreshed config + imports.lock must # still pass `cargo vet check`. If it doesn't, something # went very wrong upstream (e.g. an import URL started - # serving a malformed audits.toml) and we should NOT open - # a PR that would break CI on merge. + # serving a malformed audits.toml) and the report must say + # so instead of suggesting a change that would break CI. run: cargo vet check --locked - name: Detect changes @@ -151,97 +133,46 @@ jobs: run: | if git diff --quiet supply-chain/; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "No changes to supply-chain/ — nothing to open a PR for." + echo "No changes to supply-chain/ — nothing to report." else echo "changed=true" >> "$GITHUB_OUTPUT" - echo "supply-chain/ changed — will open a PR." + echo "supply-chain/ changed — reporting the diff." echo "" echo "=== diff summary ===" git diff --stat supply-chain/ fi - - name: Open refresh PR + - name: Report the refresh + if: steps.diff.outputs.changed == 'true' + # No PR, no branch (see the header): the diff goes to the job + # summary and the refreshed files to an artifact, and the + # maintainer applies the same commands locally. + run: | + { + echo "## 🔄 cargo-vet imports changed upstream" + echo "" + echo "Apply on the working branch (the commit hooks vet it):" + echo "" + echo '```bash' + echo "cargo vet regenerate imports && cargo vet prune && cargo vet check --locked" + echo '```' + echo "" + echo "### diff" + echo "" + echo '```diff' + git diff --stat supply-chain/ + echo "" + git diff supply-chain/ | head -400 + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + + - name: Upload the refreshed supply-chain if: steps.diff.outputs.changed == 'true' - # peter-evans/create-pull-request handles: - # • branch creation (reuses if the same branch is still open) - # • commit authoring (GitHub Actions bot) - # • PR open OR update (idempotent across weekly runs) - # - # `sign-commits: true` switches the action from `git` to the - # GitHub REST API (`PUT /repos/{owner}/{repo}/contents/{path}`) - # for commit creation. REST-API commits authored by - # `GITHUB_TOKEN` are auto-signed by GitHub's `web-flow` key, so - # the resulting bot commit satisfies the `main-protection` - # ruleset's `REQUIRED_SIGNATURES` rule without us ever provisioning - # a private GPG key as a secret. See `action.yml` of - # `peter-evans/create-pull-request@v7` for the input docs - # ("Sign commits as `github-actions[bot]` when using `GITHUB_TOKEN`"). - # - # Without this flag the action commits via `git` with the bot's - # email but no signature, producing an `unsigned` commit that - # branch protection rejects on merge (manifested as PR #272 - # being `BLOCKED` until the maintainer rebased + force-pushed a - # signed copy by hand). - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: "chore/cargo-vet-refresh" - base: main - delete-branch: true - sign-commits: true - commit-message: | - chore(security): refresh cargo-vet imports - - Scheduled weekly refresh of supply-chain/imports.lock - from upstream audit sources (Mozilla, Google, Bytecode - Alliance, ISRG, Zcash). - - See .github/workflows/cargo-vet-refresh.yml for - rationale and docs/architecture/security/ - supply-chain-posture.md for the full supply-chain - posture. - title: "chore(security): weekly cargo-vet import refresh" - body: | - ## Summary - - Weekly refresh of `supply-chain/imports.lock` from upstream - audit sources (Mozilla, Google, Bytecode Alliance, ISRG, - Zcash). Opened automatically by - `.github/workflows/cargo-vet-refresh.yml`. - - ## ⚠️ Before merging - - GitHub **suppresses CI runs** on PRs opened by the - default `GITHUB_TOKEN` (anti-recursion safety). To - trigger the Tier 1 checks: - - * **close and reopen this PR**, or - * push an empty commit: - ```bash - git fetch origin chore/cargo-vet-refresh - git checkout chore/cargo-vet-refresh - git commit --allow-empty -S -m "chore: trigger ci" - git push - ``` - - ## What changed - - Diff is entirely within `supply-chain/` — either added - audit records or pruned exemptions that are now covered - by upstream audits. - - ## Why review is still needed - - Even though the content comes from trusted upstreams, - the merge commit itself must be signed + reviewed per - branch protection. Skim the diff for anything - unusual (e.g. a removed audit record that's been - replaced by an exemption — would indicate an upstream - has *retracted* an audit, which warrants investigation). - labels: | - security - dependencies - automated + name: supply-chain-refresh + path: supply-chain/ + retention-days: 14 - name: No-op summary if: steps.diff.outputs.changed == 'false' diff --git a/.github/workflows/ci-failure-notify.yml b/.github/workflows/ci-failure-notify.yml index 916c8cfe4..cffdc6088 100644 --- a/.github/workflows/ci-failure-notify.yml +++ b/.github/workflows/ci-failure-notify.yml @@ -2,14 +2,14 @@ # SPDX-License-Identifier: MPL-2.0 # ───────────────────────────────────────────────────────────────────────────── -# ci-failure-notify — file a tracking issue when a watched workflow +# ci-failure-notify - file a tracking issue when a watched workflow # concludes in failure AFTER `auto-rerun-transient.yml` has had its # chance to retry transient flakes. # # Why this lives outside `pr-fast.yml` / `tier-2.yml`: # # The previous in-workflow `notify-failure` jobs fired synchronously -# as soon as any gate job failed — BEFORE the cross-workflow +# as soon as any gate job failed - BEFORE the cross-workflow # `auto-rerun-transient` listener could observe the failure and # dispatch a retry. Transient failures (HTTP 429, network blips, # runner-setup faults) thus generated false-positive `ci-failure-*` @@ -38,27 +38,20 @@ # # Coverage: # -# * "PR Fast CI" → label `ci-failure-pr-fast` -# * "🌙 UFFS Tier 2 Nightly CI" → label `ci-failure-tier-2` -# * "🔍 CodeQL (Rust SAST)" is intentionally NOT covered — it uses -# `continue-on-error: true` (advisory job) and its transient -# failures are absorbed by `auto-rerun-transient.yml` without -# ever surfacing as `conclusion == 'failure'`. -# -# Release pipeline (`release.yml`) keeps its own in-workflow -# notify-failure job because: -# -# 1. It's not in `auto-rerun-transient.yml`'s watched list (release -# failures are not transient-retryable by design — every step -# mutates published state). -# 2. Release failures need immediate human attention; a 60s delay -# is unwanted. +# * every workflow listed under `workflows:` below; the `known` +# table gives each its label and title, and a workflow missing +# from the table still files an issue (label from its name). +# * each genuine failure also mails failed@nios.net (the +# `Mail the failure` step, NOTIFY_SMTP_USER / NOTIFY_SMTP_PASSWORD). +# * not watched: this notifier, auto-rerun, commitlint, the +# dependabot reviews, preview artifacts and the release +# auto-trigger (PR-scoped or advisory). # # Permissions: # -# * `actions: read` — required to call GET /repos/.../actions/runs/. -# * `contents: read` — implicit (issue body links to commits). -# * `issues: write` — required to create / comment on / update issues. +# * `actions: read` - required to call GET /repos/.../actions/runs/. +# * `contents: read` - implicit (issue body links to commits). +# * `issues: write` - required to create / comment on / update issues. name: 🔔 CI Failure Notify @@ -66,13 +59,21 @@ on: workflow_run: workflows: - "PR Fast CI" - - "🌙 UFFS Tier 2 Nightly CI" + - "🔄 cargo-vet: weekly import refresh" + - "🔍 CodeQL (Rust SAST)" + - "📦 crates.io dry-run + semver-checks" - "🐤 Nightly Canary" + - "🔥 Release cache warm" + - "🚀 Release-plz" + - "🚀 UFFS Release Pipeline" + - "🌙 UFFS Tier 2 Nightly CI" + - "📦 WinGet Publish" + - "🔑 WinGet Token Expiry Check" types: [completed] # Default to ZERO permissions; the single job below grants only what # it needs. Matches the least-privilege pattern used in every other -# UFFS workflow. +# workflow here. permissions: {} # Serialise per watched run so two near-simultaneous workflow_run @@ -134,7 +135,7 @@ jobs: status="$(printf '%s' "$run_json" | jq -r '.status')" conclusion="$(printf '%s' "$run_json" | jq -r '.conclusion // "null"')" - printf 'Run %s — attempt=%s status=%s conclusion=%s\n' \ + printf 'Run %s - attempt=%s status=%s conclusion=%s\n' \ "${RUN_ID}" "${attempt}" "${status}" "${conclusion}" { @@ -177,8 +178,9 @@ jobs: echo "notify=true" >> "$GITHUB_OUTPUT" - name: File or update tracking issue + id: issue if: steps.decide.outputs.notify == 'true' - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: WORKFLOW_NAME: ${{ github.event.workflow_run.name }} HEAD_SHA: ${{ github.event.workflow_run.head_sha }} @@ -189,51 +191,45 @@ jobs: ATTEMPT: ${{ steps.status.outputs.attempt }} with: script: | - // Map workflow-run name → per-workflow label + title prefix. - // The mapping mirrors the labels previously emitted by each - // workflow's in-line `notify-failure` job before the move - // to this centralised, race-safe workflow. + // Map workflow-run name -> per-workflow label + title. A + // workflow missing from this table still files an issue + // (label from its name): a failure is never dropped. const wf = process.env.WORKFLOW_NAME; const sha = process.env.HEAD_SHA; const shortSha = sha.substring(0, 7); const attempt = process.env.ATTEMPT; - let label, title, extraBody; - if (wf === 'PR Fast CI') { - label = 'ci-failure-pr-fast'; - title = `🔴 CI Failure: PR Fast CI — ${shortSha}`; - extraBody = []; - } else if (wf === '🌙 UFFS Tier 2 Nightly CI') { - label = 'ci-failure-tier-2'; - title = `🔴 CI Failure: Tier 2 Nightly CI — ${shortSha}`; - extraBody = [ - '', - '📧 Notification sent to: cifail@uffs.io', - ]; - } else if (wf === '🐤 Nightly Canary') { - // Early-warning probe against the LATEST floating nightly - // (NOT the pinned toolchain). A failure here means an - // upcoming nightly will break the workspace — triage - // before the next `just toolchain-sync` pin bump, not - // during it. This is advisory: the canary is never a - // required merge check, so this issue tracks future risk, - // not a broken main. - label = 'ci-failure-nightly-canary'; - title = `🐤 Nightly Canary: upcoming-toolchain regression — ${shortSha}`; - extraBody = [ + const known = { + 'PR Fast CI': ['ci-failure-pr-fast', '🔴 CI Failure: PR Fast CI'], + '🔄 cargo-vet: weekly import refresh': ['ci-failure-cargo-vet-weekly-import-refresh', '🔴 CI Failure: cargo-vet: weekly import refresh'], + '🔍 CodeQL (Rust SAST)': ['ci-failure-codeql-rust-sast', '🔴 CI Failure: CodeQL (Rust SAST)'], + '📦 crates.io dry-run + semver-checks': ['ci-failure-crates-io-dry-run-semver-checks', '🔴 CI Failure: crates.io dry-run + semver-checks'], + '🐤 Nightly Canary': ['ci-failure-nightly-canary', '🐤 Nightly Canary: upcoming-toolchain regression'], + '🔥 Release cache warm': ['ci-failure-release-cache-warm', '🔴 CI Failure: Release cache warm'], + '🚀 Release-plz': ['ci-failure-release-plz', '🔴 CI Failure: Release-plz'], + '🚀 UFFS Release Pipeline': ['ci-failure-release', '🔴 Release Failure'], + '🌙 UFFS Tier 2 Nightly CI': ['ci-failure-tier-2', '🔴 CI Failure: Tier 2 Nightly CI'], + '📦 WinGet Publish': ['ci-failure-winget-publish', '🔴 CI Failure: WinGet Publish'], + '🔑 WinGet Token Expiry Check': ['ci-failure-winget-token-expiry-check', '🔴 CI Failure: WinGet Token Expiry Check'], + }; + const slug = wf.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 40); + const [label, titlePrefix] = known[wf] || [`ci-failure-${slug}`, `🔴 CI Failure: ${wf}`]; + const title = `${titlePrefix} - ${shortSha}`; + const extraBody = []; + if (wf === '🐤 Nightly Canary') { + extraBody.push( '', '⚠️ This is an **early-warning** signal from the floating-nightly canary, ' + 'not a broken pin. `main` still builds on the pinned toolchain. ' + 'Investigate before the next pin bump; if it is an upstream/dep ' + - 'regression, the fix is usually a dependency bump (cf. the ethnum ' + - '1.5.2→1.5.3 episode), not a toolchain downgrade.', - '', - '📧 Notification sent to: cifail@uffs.io', - ]; - } else { - core.warning(`Unknown workflow name: ${wf}; not notifying.`); - return; + 'regression, the fix is usually a dependency bump, not a toolchain downgrade.', + ); } + // The mail: the issue is assigned to githubrobbi, and GitHub + // mails the assignee on the new issue and on every comment + // a repeat failure adds (the account's notification address). + const mailTo = `${context.repo.repo.toLowerCase()}failed@nios.net`; + extraBody.push('', `📧 Mailed to ${mailTo} (the next step), and GitHub mails the assignee on this issue and every repeat-failure comment.`); const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.RUN_ID}`; const commitUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${sha}`; @@ -281,8 +277,10 @@ jobs: body: body, }); core.info(`Updated existing issue #${existing.data[0].number}`); + core.setOutput('url', existing.data[0].html_url); + core.setOutput('title', `${existing.data[0].title} (repeat: ${shortSha})`); } else { - await github.rest.issues.create({ + const created = await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: title, @@ -291,4 +289,54 @@ jobs: assignees: ['githubrobbi'], }); core.info('Created new failure issue'); + core.setOutput('url', created.data.html_url); + core.setOutput('title', title); } + + # The mail: failed@nios.net (the owner's convention, one + # address per repository). nios.net's DMARC admits only the IONOS + # relay as a sender, so the mail goes FROM the Gmail account that + # owns the app password TO the nios.net address. Plain curl SMTP, + # no third-party action. Without the two secrets the step says so + # loudly (a warning on the run) instead of failing the notifier: + # the issue above is already filed. + - name: Mail the failure + # Also when filing the issue failed: the mail is the signal + # that must not be lost (ISSUE_URL is then empty). + if: ${{ !cancelled() && steps.decide.outputs.notify == 'true' }} + env: + SMTP_USER: ${{ secrets.NOTIFY_SMTP_USER }} + SMTP_PASSWORD: ${{ secrets.NOTIFY_SMTP_PASSWORD }} + REPO: ${{ github.event.repository.name }} + WORKFLOW_NAME: ${{ github.event.workflow_run.name }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + RUN_URL: ${{ github.event.workflow_run.html_url }} + ISSUE_URL: ${{ steps.issue.outputs.url }} + ISSUE_TITLE: ${{ steps.issue.outputs.title }} + shell: bash + run: | + set -euo pipefail + if [[ -z "${SMTP_USER}" || -z "${SMTP_PASSWORD}" ]]; then + echo "::warning::NOTIFY_SMTP_USER / NOTIFY_SMTP_PASSWORD are not set; no failure mail was sent (the issue is filed)" + exit 0 + fi + to="$(printf '%s' "${REPO}" | tr '[:upper:]' '[:lower:]')failed@nios.net" + subject="[${REPO}] ${ISSUE_TITLE:-${WORKFLOW_NAME} failed}" + mail="$(mktemp)" + { + printf 'From: %s CI <%s>\r\n' "${REPO}" "${SMTP_USER}" + printf 'To: %s\r\n' "${to}" + printf 'Subject: %s\r\n' "${subject}" + printf 'Date: %s\r\n' "$(date -R)" + printf 'MIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n' + printf '%s failed on %s (%s).\r\n\r\n' "${WORKFLOW_NAME}" "${HEAD_BRANCH}" "${HEAD_SHA:0:7}" + printf 'Run: %s\r\n' "${RUN_URL}" + printf 'Issue: %s\r\n' "${ISSUE_URL}" + } > "${mail}" + curl --silent --show-error --fail --ssl-reqd \ + --url 'smtp://smtp.gmail.com:587' \ + --user "${SMTP_USER}:${SMTP_PASSWORD}" \ + --mail-from "${SMTP_USER}" --mail-rcpt "${to}" \ + --upload-file "${mail}" + echo "mailed ${to}" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bef10a1d0..c24374b00 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -138,7 +138,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Initialize CodeQL - uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 + uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3 with: languages: ${{ matrix.language }} # Rust support (public preview) currently only accepts @@ -153,6 +153,6 @@ jobs: # queries: security-and-quality - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 + uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 7b5dd253a..a37b52685 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -51,9 +51,12 @@ # # What this does NOT do: # -# • Validate individual commit messages on the feature branch. UFFS -# uses squash-merge exclusively, so the PR title (which becomes the -# squash subject) is the only commit message that lands on `main`. +# • Validate individual commit messages on the feature branch. PRs +# land as merge commits (the queue's method is MERGE), so the +# branch's commits reach `main` one by one; their subjects are +# checked locally by the commit-msg hook and the pre-push +# `check_commit_subjects.sh` gate. The PR title is the subject of +# the merge itself and the release-notes line for the PR. # • Validate the PR description / body. Only the title is parsed for # release-plz's version inference. # @@ -79,7 +82,7 @@ on: # merge queue requires every required status check to also report on # the `merge_group` event, but that event carries no PR title. We # recover the PR from the queue ref (`gh-readonly-queue//pr-- - # `) and re-lint its title (the squash subject that will land). + # `) and re-lint its title (the subject of the PR that will land). # Without this, a title-required check would deadlock the queue. merge_group: @@ -114,9 +117,9 @@ jobs: steps: - name: Validate PR title against Conventional Commits env: - # `pull_request.title` is the canonical squash-merge subject. - # We deliberately do NOT inspect commits on the branch — those - # get squashed away on merge and are noise for release-plz. + # `pull_request.title` names the PR in the merge commit and + # the release notes. The branch's own commits are linted + # locally (commit-msg hook + pre-push subject gate), not here. # (Empty on `merge_group`; that path derives the title below.) TITLE: ${{ github.event.pull_request.title }} PR_NUMBER: ${{ github.event.pull_request.number }} @@ -146,8 +149,8 @@ jobs: # ── Merge-queue path (merge_group event) ─────────────────── # The merge_group event has no PR payload, so recover the PR - # number from the queue ref and re-lint its title (the squash - # subject that will land on main). No sticky comment here — + # number from the queue ref and re-lint its title (the PR + # that will land on main). No sticky comment here — # that already happened at PR-open time on the pull_request # run; the queue only needs a pass/fail gate. The queue is # configured for single-PR groups (max_entries_to_{build, diff --git a/.github/workflows/crates-io-dry-run.yml b/.github/workflows/crates-io-dry-run.yml index 7057184b1..bb7a52d7d 100644 --- a/.github/workflows/crates-io-dry-run.yml +++ b/.github/workflows/crates-io-dry-run.yml @@ -198,7 +198,7 @@ jobs: # `cargo-deny`, `cargo-vet`, `cargo-machete`, `cargo-udeps`). # Bundled with the rust-cache so the per-week binary fetch # piggybacks on the workflow's existing cache key. - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-semver-checks diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index a8db0cf6d..d764fac0c 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -160,7 +160,7 @@ jobs: # rejected ("the merge strategy for main is set by the merge queue") and # leaves auto-merge armed but NOT enrolled in the queue, so the PR sits # CLEAN-but-stuck (cf. #494). `--auto` alone lets the queue own the - # squash and enrolls correctly. This step also fires on `synchronize`, + # method (MERGE: a merge commit) and enrolls correctly. This step also fires on `synchronize`, # so it now re-arms (self-heals enrollment) after an Update-branch. if: steps.gate.outputs.eligible == 'true' env: diff --git a/.github/workflows/pr-fast.yml b/.github/workflows/pr-fast.yml index 04424a1c0..52d780769 100644 --- a/.github/workflows/pr-fast.yml +++ b/.github/workflows/pr-fast.yml @@ -381,7 +381,7 @@ jobs: - name: Install cargo-vet if: needs.classify.outputs.dep == 'true' - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-vet @@ -552,7 +552,7 @@ jobs: with: shared-key: pr-fast-test-build save-if: 'false' - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: nextest @@ -593,7 +593,7 @@ jobs: with: shared-key: pr-fast-security - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-deny,cargo-vet,cargo-machete @@ -689,7 +689,7 @@ jobs: # Exercise the same suite natively on Windows in the merge queue so the # class is caught before merge. Mirrors the ubuntu `tests` job's # invocation; `#[ignore]`d live-MFT tests still skip. - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: nextest - run: cargo nextest run --workspace --all-features --lib --tests --profile ci --locked diff --git a/.github/workflows/preview-artifacts.yml b/.github/workflows/preview-artifacts.yml index 12b3bc933..5c554f35a 100644 --- a/.github/workflows/preview-artifacts.yml +++ b/.github/workflows/preview-artifacts.yml @@ -112,7 +112,7 @@ jobs: timeout-minutes: 32 steps: - name: Poll check-runs for PR Fast CI / required - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const sha = '${{ needs.gate.outputs.sha }}'; @@ -170,7 +170,7 @@ jobs: - run: rustup show - run: rustup target add x86_64-pc-windows-msvc - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-xwin @@ -213,7 +213,7 @@ jobs: done ls -la dist/windows/ - - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: windows-preview-${{ needs.gate.outputs.sha }} path: dist/windows/ @@ -261,7 +261,7 @@ jobs: - run: rustup show - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: nextest @@ -286,7 +286,7 @@ jobs: --target x86_64-pc-windows-msvc \ --archive-file "uffs-tests-${{ needs.gate.outputs.sha }}.tar.zst" - - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: nextest-archive-${{ needs.gate.outputs.sha }} path: uffs-tests-${{ needs.gate.outputs.sha }}.tar.zst @@ -310,7 +310,7 @@ jobs: with: ref: ${{ needs.gate.outputs.sha }} - - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + - uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: nextest @@ -389,7 +389,7 @@ jobs: cat manifest.json echo "::endgroup::" - - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: manifest-${{ needs.gate.outputs.sha }} path: manifest.json diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index c73ac4f5e..aabdc8975 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -372,10 +372,10 @@ jobs: shared-key: release-plz-active-pr - name: Run release-plz release-pr - # Pinned to commit SHA of the v0.5.128 tag (resolved 2026-05-08) + # Pinned to the commit SHA of its release tag (named in the comment) # for supply-chain hygiene. Bump alongside other action pins # via the standard Dependabot or manual-refresh process. - uses: release-plz/action@7ef5e4d4b0420630cfcaeb182c01e13651d4c577 # v0.5.145 + uses: release-plz/action@66b324fd6c72d05d2a08568492ecdd62bfb8a62b # v0.5.148 with: command: release-pr config: release-plz.toml @@ -431,7 +431,7 @@ jobs: # job runs on every push but only does work when the PR # actually merged. id: release-plz - uses: release-plz/action@7ef5e4d4b0420630cfcaeb182c01e13651d4c577 # v0.5.145 + uses: release-plz/action@66b324fd6c72d05d2a08568492ecdd62bfb8a62b # v0.5.148 with: command: release config: release-plz.toml @@ -522,8 +522,14 @@ jobs: # is already the tag. ref: ${{ inputs.publish_tag || github.ref }} - - name: Install Rust (nightly toolchain) - uses: dtolnay/rust-toolchain@nightly + - name: Install pinned nightly (from rust-toolchain.toml) + # The pin of the checked-out release tag, never a floating + # nightly: `cargo publish` verifies by building the package, and + # a nightly the release was never built with can fail on a new + # lint or regression (the canary's job, not the publish's). + # `rustup show` reads `rust-toolchain.toml` and installs exactly + # that channel, like every other job in this workflow. + run: rustup show - name: Cache cargo dependencies uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6b9f7b122..3193f6cbf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -868,7 +868,7 @@ jobs: rm -rf release-staging demo - name: Upload release artifacts - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: ${{ matrix.artifact-name }} path: release-artifacts/ @@ -988,7 +988,7 @@ jobs: # # and prove the SBOM came from this exact workflow run. - name: Install cargo-cyclonedx - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-cyclonedx @@ -1292,7 +1292,7 @@ jobs: # No `target_commitish`: the tag was just created at the built # commit by the step above, so the release simply references the # existing tag — sidestepping the commit-SHA-target 403 entirely. - uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3 + uses: softprops/action-gh-release@8a0cd651e5b2365bd640854a3fa5dfc70e4875f3 # v3.0.4 with: tag_name: ${{ needs.release-preparation.outputs.tag }} name: ${{ needs.release-preparation.outputs.release-name }} @@ -1423,7 +1423,7 @@ jobs: issues: write steps: - name: Create failure issue - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | // Per-workflow label so Release / Tier 1 / Tier 2 failures @@ -1443,7 +1443,7 @@ jobs: ``, `⚠️ **This is a release pipeline failure — immediate attention required.**`, ``, - `📧 Notification sent to: releasefail@uffs.io`, + `📧 Failure mail: the CI Failure Notify workflow files the issue and mails ultrafastfilesearchfailed@nios.net`, ].join('\n'); const existing = await github.rest.issues.listForRepo({ diff --git a/.github/workflows/tier-2.yml b/.github/workflows/tier-2.yml index 19d0a2e11..665182436 100644 --- a/.github/workflows/tier-2.yml +++ b/.github/workflows/tier-2.yml @@ -87,7 +87,7 @@ jobs: cache-on-failure: 'true' - name: Install tools - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-llvm-cov,cargo-nextest @@ -192,7 +192,7 @@ jobs: cache-on-failure: 'true' - name: Install cargo-udeps - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-udeps @@ -267,7 +267,7 @@ jobs: cache-on-failure: 'true' - name: Install cargo-hack - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-hack @@ -408,7 +408,7 @@ jobs: cache-on-failure: 'true' - name: Install cargo-careful - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-careful @@ -532,7 +532,7 @@ jobs: cache-on-failure: 'true' - name: Install cargo-mutants - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-mutants @@ -561,7 +561,7 @@ jobs: # per-mutant diff, log, and overall summary in # `outcomes.json`. if: always() - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: cargo-mutants-output-${{ github.run_id }} path: mutants.out/ @@ -685,7 +685,7 @@ jobs: cache-on-failure: 'true' - name: Install cargo-fuzz - uses: taiki-e/install-action@dfc84ffb7254b048a0a843316ff5a2714dcdc7bd # v2 + uses: taiki-e/install-action@cfdb7ed4ccc7c7dce37759eae89dc5674b469b39 # v2.90.3 with: tool: cargo-fuzz @@ -732,7 +732,7 @@ jobs: # promote to a hard gate we'll switch this to # `actions/cache` so each run builds on the last. if: always() - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: cargo-fuzz-output-${{ github.run_id }} path: | diff --git a/.github/workflows/winget-token-expiry-check.yml b/.github/workflows/winget-token-expiry-check.yml index a38016de9..c6530acd5 100644 --- a/.github/workflows/winget-token-expiry-check.yml +++ b/.github/workflows/winget-token-expiry-check.yml @@ -211,7 +211,7 @@ jobs: fi - name: Open / update / close the expiry tracking issue - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: STATUS: ${{ steps.probe.outputs.status }} EXPIRY: ${{ steps.probe.outputs.expiry }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 075d241ac..dcbfc8631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.47] - 2026-11-10 + +### Fixed + +- lints: clear what the newest nightly clippy flags (the floating-nightly canary, #641) +- deps: zstd 0.13.4 / zstd-safe 7.4.0 for polars' copy, so RUSTSEC-2026-0391, -0398 and -0403 no longer need an ignore +- release: the crates.io publish job builds with the pinned toolchain, not a floating nightly +- merge: the ship pipeline merges main into the release branch and enrolls it in the merge queue without a squash flag; merge commits are signature-checked +- just: three recipe messages no longer run the names they quote in backticks +- citation: CITATION.cff names the current release, and `just ship` keeps it in sync + +### Changed + +- toolchain: nightly-2026-10-29 -> nightly-2026-11-10 +- deps: every dependency at its latest release, each new version vetted (reqwest held at 0.12, see Cargo.toml) +- ci: the weekly cargo-vet refresh reports the new imports instead of opening a PR; failed workflows file an issue and send mail +- ci: every GitHub Action pinned to its latest release SHA + ## [0.6.46] - 2026-10-29 ### Fixed diff --git a/CITATION.cff b/CITATION.cff index fd3e3975a..b83428b4c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -34,11 +34,10 @@ license: MPL-2.0 license-url: "https://github.com/skyllc-ai/UltraFastFileSearch/blob/main/LICENSE" # ── Version ───────────────────────────────────────────────────────────────── -# Keep this in sync with [workspace.package].version in Cargo.toml. -# The release pipeline (release-plz / just ship) should bump this automatically -# once Pattern 5 in build/update_all_versions.rs is extended to cover CITATION.cff. -version: "0.5.120" -date-released: "2026-06-09" +# Kept in sync with [workspace.package].version in Cargo.toml: `just ship` +# rewrites `version` and `date-released` in the release commit. +version: "0.6.47" +date-released: "2026-11-10" # ── Classification ─────────────────────────────────────────────────────────── type: software diff --git a/Cargo.lock b/Cargo.lock index 13d36da14..cccca4005 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" +checksum = "fc0fc36d7440d015b45468ba08a21e80e056ebbd014c611c6298bb7503e04bd1" dependencies = [ "cipher", "cpubits", @@ -254,7 +254,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -374,9 +374,9 @@ dependencies = [ [[package]] name = "bit-vec" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269" +checksum = "72503af1e72be411d74ca45cd6b309878fcb35354e252fc2a3a8bda3a97082a0" dependencies = [ "borsh", "serde", @@ -434,7 +434,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -498,7 +498,7 @@ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -527,9 +527,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" +checksum = "4ff2a8126fff0f8c839080d619675a585e61720bb12d2baceae45769fd66f155" dependencies = [ "find-msvc-tools", "jobserver", @@ -656,7 +656,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -683,7 +683,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -735,9 +735,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +checksum = "be95d4a884358a21a199689323c29e9256b4a1b315e22f183d155422041d3a9a" [[package]] name = "constant_time_eq" @@ -784,9 +784,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055" +checksum = "2b929dd2a135771f40afabd1073f85eed59c800b7c61901e5c254270497dad64" dependencies = [ "cfg-if", ] @@ -920,9 +920,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2376ffd6f96d8a9db59875b4b6b0237d6900b5baf64279714e05493ce64828a" +checksum = "261af4dc7a06ea8a527a8717d8cbba3417530d6ea14e1edb3de6b67806be474f" dependencies = [ "cmov", ] @@ -953,7 +953,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -964,7 +964,7 @@ checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -1043,7 +1043,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -1086,7 +1086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1141,9 +1141,9 @@ checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" [[package]] name = "flate2" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +checksum = "626d4928326b646e70f65ed36fc7cf9f206bdb68a0019ee7c6b4709da4e084c4" dependencies = [ "crc32fast", "miniz_oxide", @@ -1252,7 +1252,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -1751,9 +1751,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5cf4372d022092674a83b8febf5865f7a573cc0c5baec5e49c879b6a20978f" +checksum = "3a580c13f0ffc335ef42dd3f6b1a41b726051c5e1656d9a3aa6fddb3c5dfbbf9" dependencies = [ "hybrid-array", ] @@ -1790,9 +1790,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "3bd1574e617a9a6c1f8f8181bd7e841e1f20e4277607ef28945765188d6284d4" [[package]] name = "jobserver" @@ -1806,9 +1806,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.106" +version = "0.3.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a" dependencies = [ "cfg-if", "futures-util", @@ -1892,9 +1892,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" [[package]] name = "lru-slab" @@ -1962,9 +1962,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "d5848f5a9e8117cf94f2f90faac24a87ff81600ff8628c1e3deaab572e76bfa1" [[package]] name = "miniz_oxide" @@ -2316,7 +2316,7 @@ dependencies = [ "streaming-iterator", "strum_macros", "version_check", - "zstd 0.13.3", + "zstd 0.13.4", ] [[package]] @@ -2705,7 +2705,7 @@ dependencies = [ "simdutf8", "snap", "streaming-decompression", - "zstd 0.13.3", + "zstd 0.13.4", ] [[package]] @@ -3102,7 +3102,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3267,7 +3267,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3358,9 +3358,9 @@ dependencies = [ [[package]] name = "rmcp" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8be15815186873ea063bf005011d16c356804973489e40c39116ae3a2878d1f" +checksum = "d24a7e92f4bcaa36b97fa93c4fa7ca8f3e669eced949345b3462dba333c7f909" dependencies = [ "async-trait", "base64 0.23.1", @@ -3379,6 +3379,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "sha2", "sse-stream", "thiserror 2.0.21", "tokio", @@ -3391,15 +3392,15 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0748e054a5f5598b3213cf72099ae6b2745ba4d996c59ccf455150a7dbb839" +checksum = "b3140f24a766f80f9ec1607bde85680959a69b83d46fa9ce87dadab7e67ab029" dependencies = [ "darling", "proc-macro2", "quote", "serde_json", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3437,7 +3438,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3468,9 +3469,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +checksum = "102f35f7de7b06d8c61f1cff6592f5253e0dea431c74867aad5dfd70f44663fb" dependencies = [ "web-time", "zeroize", @@ -3552,7 +3553,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3611,7 +3612,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3622,7 +3623,7 @@ checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3653,9 +3654,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" +checksum = "9c187e0a369471c550af028cba56d4f4fa2a489ad28151ae95a4d340a2a8b782" dependencies = [ "serde_core", ] @@ -3711,9 +3712,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b29ffa27d3d59a049bb328258de100f8012848d1b08d444e08bbc02ac468435e" +checksum = "49f7447afd772ca79ad17ae0a3972a6b0df0cb4b80990fd82707593e9e012a74" dependencies = [ "libc", "signal-hook-registry", @@ -3779,9 +3780,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.16.4" +version = "1.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" +checksum = "149309498a5f7334b12ddae0cad8aba81ce785fe91755068867e017cf98fee46" [[package]] name = "snap" @@ -3923,9 +3924,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +checksum = "4ae9ccbea3a7b0b09443036490c8f34a8b1b760fd345e5c0dae0aac718faea79" dependencies = [ "proc-macro2", "quote", @@ -3949,7 +3950,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -3973,10 +3974,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4032,7 +4033,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -4125,7 +4126,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -4165,9 +4166,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.8+spec-1.1.0" +version = "1.2.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d" +checksum = "d5755c51863cdac711f1956baf8eb9588e060755f7d3c4c979e70ea1e1f32cd7" dependencies = [ "indexmap", "serde_core", @@ -4180,18 +4181,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" +checksum = "173f25c883cd2176a2d8212155638c0a60d484a9d84546d270866f40f56b6e49" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.17+spec-1.1.0" +version = "0.25.19+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" +checksum = "dd43688c4858d7264ae1cf9a9f874a67c3feb74a883b1cd7bebd6a4ea8307218" dependencies = [ "indexmap", "toml_datetime", @@ -4210,9 +4211,9 @@ dependencies = [ [[package]] name = "toml_writer" -version = "1.1.3+spec-1.1.0" +version = "1.2.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770" +checksum = "009dbb7700f11c55ef83aebbe4c0e9cf1f1ac0844bd8d6f00dc38f302026e13a" [[package]] name = "tower" @@ -4350,7 +4351,7 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uffs-bench" -version = "0.6.46" +version = "0.6.47" dependencies = [ "chrono", "clap", @@ -4367,7 +4368,7 @@ dependencies = [ [[package]] name = "uffs-broker" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "serde", @@ -4386,14 +4387,14 @@ dependencies = [ [[package]] name = "uffs-broker-protocol" -version = "0.6.46" +version = "0.6.47" dependencies = [ "thiserror 2.0.21", ] [[package]] name = "uffs-ci-pipeline" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "chrono", @@ -4412,7 +4413,7 @@ dependencies = [ [[package]] name = "uffs-cli" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "assert_cmd", @@ -4435,7 +4436,7 @@ dependencies = [ [[package]] name = "uffs-client" -version = "0.6.46" +version = "0.6.47" dependencies = [ "dirs-next", "libc", @@ -4455,7 +4456,7 @@ dependencies = [ [[package]] name = "uffs-core" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "bytemuck", @@ -4486,7 +4487,7 @@ dependencies = [ [[package]] name = "uffs-daemon" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "clap", @@ -4520,7 +4521,7 @@ dependencies = [ [[package]] name = "uffs-diag" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "chrono", @@ -4535,7 +4536,7 @@ dependencies = [ [[package]] name = "uffs-fetch" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "hex", @@ -4546,7 +4547,7 @@ dependencies = [ [[package]] name = "uffs-format" -version = "0.6.46" +version = "0.6.47" dependencies = [ "chrono", "itoa", @@ -4557,7 +4558,7 @@ dependencies = [ [[package]] name = "uffs-gen-hooks" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "clap", @@ -4569,7 +4570,7 @@ dependencies = [ [[package]] name = "uffs-gen-workflow" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "clap", @@ -4582,7 +4583,7 @@ dependencies = [ [[package]] name = "uffs-manifest-audit" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "clap", @@ -4594,7 +4595,7 @@ dependencies = [ [[package]] name = "uffs-mcp" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "axum", @@ -4618,7 +4619,7 @@ dependencies = [ [[package]] name = "uffs-mft" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "bitflags", @@ -4659,14 +4660,14 @@ dependencies = [ [[package]] name = "uffs-polars" -version = "0.6.46" +version = "0.6.47" dependencies = [ "polars", ] [[package]] name = "uffs-security" -version = "0.6.46" +version = "0.6.47" dependencies = [ "aes-gcm", "dirs-next", @@ -4681,22 +4682,22 @@ dependencies = [ [[package]] name = "uffs-statusfmt" -version = "0.6.46" +version = "0.6.47" [[package]] name = "uffs-text" -version = "0.6.46" +version = "0.6.47" dependencies = [ "bytemuck", ] [[package]] name = "uffs-time" -version = "0.6.46" +version = "0.6.47" [[package]] name = "uffs-update" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "dirs-next", @@ -4713,11 +4714,11 @@ dependencies = [ [[package]] name = "uffs-version" -version = "0.6.46" +version = "0.6.47" [[package]] name = "uffs-vss-requestor" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "cc", @@ -4730,7 +4731,7 @@ dependencies = [ [[package]] name = "uffs-watchdog" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "dirs-next", @@ -4739,7 +4740,7 @@ dependencies = [ [[package]] name = "uffs-winsvc" -version = "0.6.46" +version = "0.6.47" dependencies = [ "anyhow", "windows", @@ -4835,9 +4836,12 @@ dependencies = [ [[package]] name = "utf8_iter" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "af3bd9d3e571c26fcce9d033b87ea5ab061b8d2ffe9b7a5675d810d68f03edf0" +dependencies = [ + "utf_types", +] [[package]] name = "utf8parse" @@ -4845,11 +4849,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utf_types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3b425286980a367278453f3b6d28cdcb134b574e5a35824a6ff5eebad50bb6" + [[package]] name = "uuid" -version = "1.29.0" +version = "1.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" +checksum = "bed99139f7dac9b5cfe5f17be4d66dc7079a34dee1d74bcc77217119240e267c" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4932,12 +4942,11 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.129" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c" dependencies = [ "cfg-if", - "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", @@ -4945,9 +4954,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.79" +version = "0.4.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +checksum = "5e14e0327cb5368ad60ccb22d04a9827a693c95a926d8d125e9fa39e0baec501" dependencies = [ "js-sys", "tokio", @@ -4956,9 +4965,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.129" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4966,22 +4975,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.129" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.129" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e" dependencies = [ "unicode-ident", ] @@ -5001,9 +5010,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.106" +version = "0.3.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +checksum = "d7d179596ff5ec0f6de4db64d54011bde6289cc49603d3a6e4ce3f329207ab88" dependencies = [ "js-sys", "wasm-bindgen", @@ -5050,7 +5059,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5253,9 +5262,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +checksum = "8c3b93199df0ab874cdf0c6a94f0a0dc3423fe41f1cafb62b8bd7b1deb277aef" dependencies = [ "memchr", ] @@ -5307,7 +5316,7 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", "synstructure", ] @@ -5348,7 +5357,7 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", "synstructure", ] @@ -5388,7 +5397,7 @@ checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.9", + "syn 3.0.10", ] [[package]] @@ -5405,11 +5414,11 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zstd" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +checksum = "cdb02c3132d251ab1606e9fc913a7da6c6ed08cc263df15edf082e1dc497d157" dependencies = [ - "zstd-safe 7.3.0", + "zstd-safe 7.4.0", ] [[package]] @@ -5423,9 +5432,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.3.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" +checksum = "1c8ea04668f5de8d8d51051714ba560c9536519cadcdb8829bba4fe2ad159b03" dependencies = [ "zstd-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 8a5837ec3..9545a41da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ members = [ # Workspace Package Metadata (inherited by all crates) # ───────────────────────────────────────────────────────────────────────────── [workspace.package] -version = "0.6.46" +version = "0.6.47" edition = "2024" # No `rust-version` claim: the workspace is structurally nightly-only. # `crates/uffs-polars` enables `polars/nightly` unconditionally, which @@ -134,16 +134,16 @@ publish = false # proposed-plan output for 12 days because `release-plz update` # failed at `cargo package` with this very error. See # `release-automation-baseline.md` §10 for the diagnostic trail. -uffs-polars = { path = "crates/uffs-polars", version = "0.6.46" } -uffs-security = { path = "crates/uffs-security", version = "0.6.46" } -uffs-text = { path = "crates/uffs-text", version = "0.6.46" } -uffs-time = { path = "crates/uffs-time", version = "0.6.46" } -uffs-version = { path = "crates/uffs-version", version = "0.6.46" } -uffs-statusfmt = { path = "crates/uffs-statusfmt", version = "0.6.46" } -uffs-mft = { path = "crates/uffs-mft", version = "0.6.46" } -uffs-format = { path = "crates/uffs-format", version = "0.6.46" } -uffs-core = { path = "crates/uffs-core", version = "0.6.46" } -uffs-client = { path = "crates/uffs-client", version = "0.6.46" } +uffs-polars = { path = "crates/uffs-polars", version = "0.6.47" } +uffs-security = { path = "crates/uffs-security", version = "0.6.47" } +uffs-text = { path = "crates/uffs-text", version = "0.6.47" } +uffs-time = { path = "crates/uffs-time", version = "0.6.47" } +uffs-version = { path = "crates/uffs-version", version = "0.6.47" } +uffs-statusfmt = { path = "crates/uffs-statusfmt", version = "0.6.47" } +uffs-mft = { path = "crates/uffs-mft", version = "0.6.47" } +uffs-format = { path = "crates/uffs-format", version = "0.6.47" } +uffs-core = { path = "crates/uffs-core", version = "0.6.47" } +uffs-client = { path = "crates/uffs-client", version = "0.6.47" } # `uffs-broker-protocol` carries the wire-protocol types shared between # `uffs-broker` (the elevated handle vendor, Windows-only binary) and # `uffs-daemon::broker_client` (the handle consumer). Pure-logic @@ -152,20 +152,20 @@ uffs-client = { path = "crates/uffs-client", version = "0.6.46" } # wire-format byte literals. Also holds the VSS `HRESULT` retry table # (`vss_hresult`) shared by `uffs-vss-requestor` and uffs-products' # Content Coordinator. -uffs-broker-protocol = { path = "crates/uffs-broker-protocol", version = "0.6.46" } +uffs-broker-protocol = { path = "crates/uffs-broker-protocol", version = "0.6.47" } # `uffs-winsvc` — native Windows service control (SCM query/start/stop) + # the non-connecting broker-pipe readiness probe. Layer-0 leaf: its only # dependency is the `windows` crate (windows-target), with non-Windows # stubs so cross-platform consumers (uffs-update, uffs-cli) compile. # Single source of truth for the `sc`/SCM mechanics previously duplicated # across uffs-broker, uffs-update, and uffs-cli. -uffs-winsvc = { path = "crates/uffs-winsvc", version = "0.6.46" } +uffs-winsvc = { path = "crates/uffs-winsvc", version = "0.6.47" } # `uffs-fetch` — hardened release-asset transport (blocking reqwest + # rustls with retry/timeout/byte-cap, plus `SHA256SUMS` verification), # extracted from `uffs-update` as a small public lib so external products # can reuse it. Cross-platform pure-logic leaf; keeps the HTTP/TLS stack # out of the lean `uffs` CLI exactly as before. -uffs-fetch = { path = "crates/uffs-fetch", version = "0.6.46" } +uffs-fetch = { path = "crates/uffs-fetch", version = "0.6.47" } # NOTE: no `uffs-broker` workspace dependency alias on purpose — # `uffs-broker` is a binary-only crate (the only `[lib]` it carries is # this protocol module's now-extracted sibling); no other workspace @@ -246,12 +246,12 @@ serde_json = "1.0.154" # tier overrides + adaptive-TTL knobs in `uffs-daemon::config`. # Pinned to v1.1.2 to match the supply-chain exemption already on # the transitive copy pulled by other deps; no new vet entry needed. -toml = "1.1.8" +toml = "1.2.1" # ───── Data Structures ───── bitflags = "2.13.2" bytemuck = { version = "1.25.2", features = ["derive"] } -smallvec = "1.16.4" +smallvec = "1.16.5" zerocopy = { version = "0.8.63", features = ["derive"] } # ───── Error Handling ───── @@ -259,7 +259,7 @@ thiserror = "2.0.21" anyhow = "1.0.104" # ───── MCP (Model Context Protocol) ───── -rmcp = { version = "3.8.0", features = ["server", "transport-io", "macros"] } +rmcp = { version = "3.9.0", features = ["server", "transport-io", "macros"] } schemars = "1.2.3" # ───── HTTP / Tower (for MCP Streamable HTTP gateway) ───── @@ -316,7 +316,7 @@ strsim = "0.11.1" rustc-hash = "2.1.3" # ───── Formatting / Encoding ───── -itoa = "1.0.18" +itoa = "1.0.19" sha2 = "0.11.1" hex = "0.4.3" @@ -324,7 +324,7 @@ hex = "0.4.3" # Blocking HTTP with rustls + the system trust store. `rustls-tls-native-roots` # matches the existing transitive reqwest config, so no new crate enters the lock. # -# DELIBERATELY HELD AT 0.12 (re-verified 2026-10-28 against 0.13.5 and polars +# DELIBERATELY HELD AT 0.12 (re-verified 2026-11-10 against 0.13.5 and polars # 0.55.2, the latest of both; everything # else in this workspace is at latest). 0.13 still drops # `rustls-tls-native-roots` and routes system trust through @@ -364,7 +364,7 @@ hostname = "0.4.2" num_cpus = "1.17.0" # Random unique identifiers. Only the `v4` (random) feature is enabled # workspace-wide; consumers that need v1/v5/v7 etc. re-add the feature. -uuid = { version = "1.29.0", features = ["v4"] } +uuid = { version = "1.29.2", features = ["v4"] } # ───── Security / Crypto ───── aes-gcm = "0.11.1" diff --git a/SECURITY.md b/SECURITY.md index 7f7e2929f..938e8e928 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -83,8 +83,9 @@ rationale, and operational playbook. (Mozilla, Google, Bytecode Alliance, ISRG, Zcash), a local audit in `supply-chain/audits.toml`, or a grandfathered exemption in `supply-chain/config.toml`. The - `.github/workflows/cargo-vet-refresh.yml` workflow refreshes - upstream imports weekly via PR. + `.github/workflows/cargo-vet-refresh.yml` workflow checks the + upstream imports weekly and reports the diff; the maintainer + applies it on a working branch. - **Structural audit** — `just geiger` produces an on-demand `unsafe` / `build.rs` / proc-macro footprint report for the resolved dep tree (run monthly; compare against baseline). diff --git a/crates/uffs-cli/Cargo.toml b/crates/uffs-cli/Cargo.toml index 37f1125fc..5e5143b33 100644 --- a/crates/uffs-cli/Cargo.toml +++ b/crates/uffs-cli/Cargo.toml @@ -70,7 +70,7 @@ path = "src/main.rs" # by the release bump) is required for `cargo package` validation — see # root `Cargo.toml`'s [workspace.dependencies] note for the full # rationale (R6 of `release-automation-plan.md`). -uffs-client = { path = "../uffs-client", version = "0.6.46", default-features = false } +uffs-client = { path = "../uffs-client", version = "0.6.47", default-features = false } # Canonical CSV / parity / legacy-footer writer. Direct dep (not a # re-export chain through `uffs-client`) so the CLI and the daemon @@ -79,7 +79,7 @@ uffs-client = { path = "../uffs-client", version = "0.6.46", default-features = # by the release bump) is required for `cargo package` validation — see # root `Cargo.toml`'s [workspace.dependencies] note for the full # rationale (R6 of `release-automation-plan.md`). -uffs-format = { path = "../uffs-format", version = "0.6.46" } +uffs-format = { path = "../uffs-format", version = "0.6.47" } # Typed drive-letter newtype. Direct dep so the CLI command signatures # (`daemon_load`, `daemon_tiering`, etc.) name `DriveLetter` natively diff --git a/crates/uffs-client/src/protocol/cli_args.rs b/crates/uffs-client/src/protocol/cli_args.rs index afdeb47f2..a502d7561 100644 --- a/crates/uffs-client/src/protocol/cli_args.rs +++ b/crates/uffs-client/src/protocol/cli_args.rs @@ -400,10 +400,6 @@ impl RawCliArgs { /// Convert raw CLI values into a fully-populated [`SearchParams`], /// performing all sugar expansion. #[expect(clippy::too_many_lines, reason = "sugar expansion for 60+ flags")] - #[expect( - clippy::cognitive_complexity, - reason = "three composable sugar rewrites (drive-prefix, ext-glob, regex-ext) plus filter normalisation; splitting further would fragment the parse pipeline" - )] fn into_search_params(mut self) -> Result { // ── Pattern sugar: --begins-with / --ends-with / --contains ─ let raw_pattern = self @@ -486,13 +482,8 @@ impl RawCliArgs { .map_or_default(crate::format::parse_month_spec); // ── Smart case ───────────────────────────────────────────── - let case_sensitive = if self.case { - true - } else if self.smart_case { - pattern.chars().any(char::is_uppercase) - } else { - false - }; + let case_sensitive = + self.case || (self.smart_case && pattern.chars().any(char::is_uppercase)); // ── Ext-pattern sugar: `*.` → pattern="*" + ext= ── // diff --git a/crates/uffs-diag/src/bin/compare_scan_parity.rs b/crates/uffs-diag/src/bin/compare_scan_parity.rs index c51064411..2edaee992 100644 --- a/crates/uffs-diag/src/bin/compare_scan_parity.rs +++ b/crates/uffs-diag/src/bin/compare_scan_parity.rs @@ -48,7 +48,6 @@ )] #![expect( clippy::str_to_string, - clippy::string_slice, reason = "string operations on normalized ASCII paths are safe" )] #![expect( @@ -167,13 +166,11 @@ fn add_normalized_paths(df: &DataFrame) -> Result { Ok(result) } -/// Check if path is an Alternate Data Stream +/// Check if path is an Alternate Data Stream: a `:` after the drive +/// prefix (`C:`). `get` instead of slicing, so a multi-byte character at +/// byte 2 reads as "no stream" rather than panicking. fn is_ads_path(path: &str) -> bool { - if path.len() > 2 { - path[2..].contains(':') - } else { - false - } + path.get(2..).is_some_and(|rest| rest.contains(':')) } /// Extract drive letter from path diff --git a/crates/uffs-mft/src/parse/forensic/base.rs b/crates/uffs-mft/src/parse/forensic/base.rs index c82a37d7c..0e84a7d57 100644 --- a/crates/uffs-mft/src/parse/forensic/base.rs +++ b/crates/uffs-mft/src/parse/forensic/base.rs @@ -550,20 +550,19 @@ pub(super) fn parse_base_record( // parity) (dir_index_size, dir_index_allocated) } else { - streams.iter().find(|s| s.name.is_empty()).map_or_else( - || { - // No default $DATA stream — use reparse_size for - // junctions/symlinks (resident ValueLength from - // the $REPARSE_POINT attribute) - if reparse_tag != 0 { - (reparse_size, 0) // Reparse point data is resident, - // allocated=0 - } else { - (0, 0) - } - }, - |s| (s.size, s.allocated_size), - ) + // No default $DATA stream: a junction/symlink reports its reparse + // data size (the resident ValueLength of $REPARSE_POINT, so + // allocated is 0); anything else is empty. Two integers, so the + // fallback is computed eagerly (clippy::unnecessary_lazy_evaluations). + let no_default_stream = if reparse_tag == 0 { + (0, 0) + } else { + (reparse_size, 0) + }; + streams + .iter() + .find(|s| s.name.is_empty()) + .map_or(no_default_stream, |s| (s.size, s.allocated_size)) }; ParseResult::Base(ParsedRecord { diff --git a/crates/uffs-mft/src/parse/full.rs b/crates/uffs-mft/src/parse/full.rs index 731ef0a03..408740c97 100644 --- a/crates/uffs-mft/src/parse/full.rs +++ b/crates/uffs-mft/src/parse/full.rs @@ -660,20 +660,19 @@ pub fn parse_record_full(data: &[u8], frs: u64) -> ParseResult { // parity) (dir_index_size, dir_index_allocated) } else { - streams.iter().find(|s| s.name.is_empty()).map_or_else( - || { - // No default $DATA stream — use reparse_size for - // junctions/symlinks (resident ValueLength from - // the $REPARSE_POINT attribute) - if reparse_tag != 0 { - (reparse_size, 0) // Reparse point data is resident, - // allocated=0 - } else { - (0, 0) - } - }, - |s| (s.size, s.allocated_size), - ) + // No default $DATA stream: a junction/symlink reports its reparse + // data size (the resident ValueLength of $REPARSE_POINT, so + // allocated is 0); anything else is empty. Two integers, so the + // fallback is computed eagerly (clippy::unnecessary_lazy_evaluations). + let no_default_stream = if reparse_tag == 0 { + (0, 0) + } else { + (reparse_size, 0) + }; + streams + .iter() + .find(|s| s.name.is_empty()) + .map_or(no_default_stream, |s| (s.size, s.allocated_size)) }; ParseResult::Base(ParsedRecord { diff --git a/crates/uffs-vss-requestor/Cargo.toml b/crates/uffs-vss-requestor/Cargo.toml index 322633367..111b7129a 100644 --- a/crates/uffs-vss-requestor/Cargo.toml +++ b/crates/uffs-vss-requestor/Cargo.toml @@ -75,7 +75,7 @@ uffs-version = { workspace = true, features = ["build"] } # for this internal helper — it's spawned by the Broker, never invoked # directly by a user in normal operation, so it skips the `winresource` # step `uffs-broker` uses. -cc = "1.10.0" +cc = "1.11.1" [lints] workspace = true diff --git a/deny.toml b/deny.toml index 41f6976bb..90fc52e87 100644 --- a/deny.toml +++ b/deny.toml @@ -43,27 +43,6 @@ ignore = [ # polars-io adopting object_store 0.14. Remove together with the ignore above. # See: https://rustsec.org/advisories/RUSTSEC-2026-0194 "RUSTSEC-2026-0194", - # zstd 0.13.3 / zstd-safe 7.3.0, held by polars-arrow 0.55.2 (the newest polars) - # for its parquet support. UFFS's own zstd (every cache file) is already on the - # fixed 0.14.3 / 8.2.0 line; only polars' private copy is affected. - # * RUSTSEC-2026-0398 (zstd-safe `Seekable::decompress`): not compiled in; the - # `seekable` feature is off for this copy. - # * RUSTSEC-2026-0403 (uninitialized memory on a failed legacy-frame - # decompress): reachable only when UFFS reads a parquet file - # (`uffs-mft inspect`, the parquet query path, the diag tools), and only for - # a crafted file carrying legacy zstd frames. - # * RUSTSEC-2026-0391 (state of an abandoned stream leaking into the next - # stream on a reused context): reachable only through a parquet read or - # write that fails part-way and then reuses the context. - # Upstream fix merged 2026-10-27: pola-rs/polars#30316 bumps zstd to 0.14.4; it is - # not in a release yet (checked 2026-10-28). Remove these three once a polars - # release containing #30316 is adopted. - # See: https://rustsec.org/advisories/RUSTSEC-2026-0391 - "RUSTSEC-2026-0391", - # See: https://rustsec.org/advisories/RUSTSEC-2026-0398 - "RUSTSEC-2026-0398", - # See: https://rustsec.org/advisories/RUSTSEC-2026-0403 - "RUSTSEC-2026-0403", ] [licenses] diff --git a/docs/architecture/dev-flow.md b/docs/architecture/dev-flow.md index 70d6a618d..3dc634cf9 100644 --- a/docs/architecture/dev-flow.md +++ b/docs/architecture/dev-flow.md @@ -135,7 +135,7 @@ and `.github/workflows/tier-2.yml` (all as of commit `185ed8825`). udeps, Windows native) T4 weekly cron ─▶ cargo-vet-refresh.yml - (opens import-bump PR) + (reports the import diff; no PR) ``` ### 3.2 Gate matrix (full) diff --git a/docs/architecture/security/supply-chain-posture.md b/docs/architecture/security/supply-chain-posture.md index 01a5d6aac..6751fbf61 100644 --- a/docs/architecture/security/supply-chain-posture.md +++ b/docs/architecture/security/supply-chain-posture.md @@ -115,7 +115,7 @@ control lands or a deferred item is promoted. | SemVer compliance | `cargo-semver-checks check-release` | For every publishable crate, diff the current rustdoc JSON against the latest version on crates.io. Catches SemVer-breaking API changes (removed pub items, signature changes, new required generic params, etc.) shipped under a non-major version bump. Informational pre-R8 (no crates.io baseline yet); becomes the canonical correctness check once the first publish lands | Weekly | **Yes** (advisory pre-R8, then GATE) — `crates-io-dry-run.yml` (Tier R3-05, landed 2026-05-12) | | MSRV verification | `cargo +1.91 check --locked` (`--all-features` + `--no-default-features`) | Independently verifies the workspace builds on the claimed MSRV (`Cargo.toml::rust-version = "1.91"`) against the **stable** 1.91 toolchain. `rust-toolchain.toml` pins nightly so every other gate runs there; this is the only gate that exercises the contract surface downstream consumers see. Catches MSRV regressions that `clippy::incompatible_msrv` misses (new trait impls, deref coercion changes, new `const fn` qualifications, inference improvements). Hard gate \u2014 a failure means the rust-version claim is lying to crates.io consumers | Weekly | **Yes** (hard gate) \u2014 `tier-2.yml::msrv` (Tier R3-06, landed 2026-05-12) | | Adversarial-input fuzzing | `cargo-fuzz` (libfuzzer-sys) | Runs libfuzzer-sys harnesses against the workspace's primary untrusted-byte attack surface: `uffs_mft::parse::{parse_record,parse_record_full,parse_record_zero_alloc,apply_fixup}`. Every NTFS MFT record we parse came from an external filesystem image — fuzz tests robustness to malformed bytes (truncated records, cyclic FRS references, integer overflows in metadata fields, slice-bounds violations in the fixup application). Complements Miri / cargo-careful (UB on *known* inputs) and cargo-mutants (test-suite quality on *known* inputs) by generating *unknown* adversarial inputs. Time-bounded (`-max_total_time=900` per target, 30 min total). Artifacts (`crashes/` + `corpus/`) uploaded for 90 days for offline triage and seed carry-over | Weekly | **Yes** (advisory; informational baseline rollout) — `tier-2.yml::fuzz` (Tier R3-07, landed 2026-05-12) | -| Import refresh | `cargo-vet-refresh.yml` | Weekly `cargo vet regenerate imports` → PR | Mondays 08:00 UTC | GitHub schedules | +| Import refresh | `cargo-vet-refresh.yml` | Weekly `cargo vet regenerate imports` → job-summary report (no PR) | Mondays 08:00 UTC | GitHub schedules | | Structural audit | `cargo-geiger` via `just geiger` | unsafe / build.rs / proc-macro footprint | On-demand (monthly) | No | | Semantic SAST | `codeql.yml` (Rust, public preview) | Dataflow-based bug patterns (path / SQL / regex injection, crypto misuse, unvalidated redirects) | PR + Tuesdays 06:30 UTC | Informational (not a required gate yet) | | Windows regression check | `pr-fast.yml::windows-lint` job | `cargo clippy --workspace --all-targets --all-features --locked --no-deps -- -D warnings` on `windows-latest` | **Every PR** (was weekly Tier 2 pre-PR-#138; now PR-time) | GitHub-required-check | @@ -470,7 +470,9 @@ Vetting Succeeded (121 fully audited, 2 partially audited, 387 exempted) - Dependabot PR where the bump isn't covered: **~2 min** to run `cargo vet regenerate exemptions && git commit --amend` locally and push. Weekly refresh minimises how often this happens. -- Weekly refresh PR review: **~5 min** to skim the diff + merge. +- Weekly refresh report: **~5 min** to skim the reported diff and apply + it on the next working branch (`cargo vet regenerate imports && + cargo vet prune`). ### Cargo.lock crate count (2026-04-22) diff --git a/just/analysis.just b/just/analysis.just index 00671312c..82ad18154 100644 --- a/just/analysis.just +++ b/just/analysis.just @@ -185,7 +185,7 @@ pgo: fi if [[ ! -x "$INSTRUMENTED" ]]; then printf "\033[1;31m❌ instrumented binary missing under %s/release/\033[0m\n" "$TARGET_DIR" - printf "\033[0;34m Set CARGO_TARGET_DIR or check .cargo/config.toml `build.target-dir`.\033[0m\n" + printf "\033[0;34m Set CARGO_TARGET_DIR or check .cargo/config.toml 'build.target-dir'.\033[0m\n" exit 1 fi printf "\033[0;32m ✓ instrumented binary: %s\033[0m\n" "$INSTRUMENTED" @@ -214,7 +214,7 @@ pgo: printf "\033[0;32m ✓ merged profile: %s\033[0m\n" "$MERGED" RUSTFLAGS="-Cprofile-use=${MERGED}" cargo build --release -p uffs-cli printf "\033[0;32m✅ PGO build complete — %s/release/uffs now uses the merged profile\033[0m\n" "$TARGET_DIR" - printf "\033[0;34m Run `just use-local` to install, then re-bench against the pre-PGO baseline.\033[0m\n" + printf "\033[0;34m Run 'just use-local' to install, then re-bench against the pre-PGO baseline.\033[0m\n" # Windows variant of the PGO pipeline. Uses PowerShell because # `shared.just` sets `windows-shell := powershell.exe` — the bash diff --git a/just/shared.just b/just/shared.just index 1dfca9396..cd7c42380 100644 --- a/just/shared.just +++ b/just/shared.just @@ -614,6 +614,21 @@ toolchain-sync: sccache --stop-server >/dev/null 2>&1 || true fi + # A new nightly's rustfmt can re-wrap code the old one accepted (the + # 2026-10-14 -> 2026-10-29 bump re-wrapped comments in twenty files), + # and the pre-push fmt gate then refuses the very push that carries + # the pin. Reformat with the new pin now, so the bump and its + # formatting land in the same change instead of surfacing at pre-push. + printf "\033[0;34m Formatting with the new pin's rustfmt (cargo fmt --all)...\033[0m\n" + if ! cargo fmt --all; then + printf "\033[0;31m❌ cargo fmt failed under %s - fix it before committing the pin.\033[0m\n" "$CANDIDATE" + exit 1 + fi + if ! git diff --quiet -- '*.rs'; then + printf "\033[0;33m ⚠️ rustfmt re-wrapped files under the new pin - commit them with the bump:\033[0m\n" + git diff --stat -- '*.rs' | tail -n 1 | sed 's/^/ /' + fi + printf "\033[0;32m✅ Toolchain synced to %s\033[0m\n" "$CANDIDATE" rustc --version cargo --version diff --git a/just/workflow.just b/just/workflow.just index b27d23305..4ffcbfad0 100644 --- a/just/workflow.just +++ b/just/workflow.just @@ -43,7 +43,7 @@ phase1-test: # Phase 2: commit and push (version bump retired — release-plz handles it). phase2-ship: @printf "\033[0;34m🚀 PHASE 2: Explicit Ship Lane\033[0m\n" - @printf "\033[1;33mThis lane performs commit and push. Version bumping now handled by release-plz on `main`.\033[0m\n" + @printf "\033[1;33mThis lane performs commit and push. Version bumping now handled by release-plz on main.\033[0m\n" @echo "========================================================" cargo run -q --release --target-dir target/ci-bootstrap -p uffs-ci-pipeline -- phase2 @@ -113,11 +113,13 @@ go-justfile: # Merge a PR through main's merge queue (the queue-correct incantation). # -# `main` uses a GitHub merge queue, so `gh pr merge --squash` is -# silently rejected ("the merge strategy for main is set by the merge -# queue") and does NOT enroll the PR. This wrapper uses the right form — -# `gh pr merge --auto` with NO method flag (the queue owns the -# squash) — so the PR enters the queue and merges once its checks pass. +# `main` uses a GitHub merge queue, so `gh pr merge --squash` (or +# any method flag) is rejected ("the merge strategy for main is set by +# the merge queue") and does NOT enroll the PR. This wrapper uses the +# right form — `gh pr merge --auto` with NO method flag — so the PR +# enters the queue and merges once its checks pass. The queue's method +# is MERGE (`main-protection`): the PR lands as a merge commit with the +# branch's signed commits kept one by one. Never squash, never rebase. merge PR: @printf "\033[0;34m🔀 Enrolling PR #{{ PR }} into main's merge queue...\033[0m\n" gh pr merge {{ PR }} --auto @@ -129,7 +131,9 @@ merge PR: # If one slipped through unsigned (e.g. committed with # `-c commit.gpgsign=false`), the `commit-signatures` pre-push gate blocks # the push. This rebases the branch in place, re-signing each commit with -# your configured GPG key, then force-pushes with lease. `--amend` leaves +# your configured GPG key, then force-pushes with lease. `--rebase-merges` +# keeps every merge the branch carries (a plain rebase would flatten them, +# and PRs land as merge commits with their history kept). `--amend` leaves # the staged set empty so the per-commit pre-commit hook no-ops; the heavy # gates run once on the final push. sign-branch: @@ -143,7 +147,7 @@ sign-branch: base=$(git merge-base origin/main HEAD) n=$(git rev-list --count "$base"..HEAD) printf "\033[0;34m🔏 Re-signing %s commit(s) on '%s' (base %s)...\033[0m\n" "$n" "$branch" "${base:0:10}" - git rebase --exec 'git commit --amend --no-edit -S' "$base" + git rebase --rebase-merges --exec 'git commit --amend --no-edit -S' "$base" printf "\033[0;34m⬆️ Force-pushing (with lease)...\033[0m\n" git push --force-with-lease printf "\033[0;32m✅ Branch re-signed and pushed — every commit now carries a signature.\033[0m\n" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f8f933b80..16bd480cd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -54,7 +54,7 @@ # Run `just toolchain-sync` to re-attempt a channel bump; the CI # pipeline auto-refreshes on `ship --fresh` unless `--skip-toolchain-sync` # is passed. -channel = "nightly-2026-10-29" +channel = "nightly-2026-11-10" # Specify components that should always be available components = [ diff --git a/scripts/ci-pipeline/src/changelog.rs b/scripts/ci-pipeline/src/changelog.rs index cd820cc8c..561f66741 100644 --- a/scripts/ci-pipeline/src/changelog.rs +++ b/scripts/ci-pipeline/src/changelog.rs @@ -173,8 +173,9 @@ pub(crate) fn generate_commit_draft() -> Result> { /// Commit subjects (one per line) since the most recent `v*` tag — i.e. the /// work that a pending release would ship. Falls back to the whole history when -/// no release tag exists yet. Merge commits are excluded (main squash-merges, -/// so each PR is one Conventional-Commit subject). +/// no release tag exists yet. Merge commits are excluded: a PR lands as a merge +/// commit whose own subject is `Merge pull request ...`, while the branch's +/// commits it brings in carry the Conventional-Commit subjects. /// /// # Errors /// diff --git a/scripts/ci-pipeline/src/git_ops.rs b/scripts/ci-pipeline/src/git_ops.rs index cfc6b0121..86d64e4c6 100644 --- a/scripts/ci-pipeline/src/git_ops.rs +++ b/scripts/ci-pipeline/src/git_ops.rs @@ -10,12 +10,12 @@ //! //! The entry points are [`git_commit`] (create the `chore: development //! vX.Y.Z ...` signed commit) and [`git_push`] (push the release -//! branch + open the release PR with auto-merge queued). Each of the -//! push sub-steps (`ensure_on_release_branch`, `rebase_onto_upstream`, -//! `push_release_branch`, `find_existing_release_pr`, `open_release_pr`, -//! `enable_auto_merge`, `return_to_base_branch`) is its own small helper -//! so a failure points at a named function in the backtrace and -//! refactors stay surgical. +//! branch + open the release PR and enroll it in `main`'s merge +//! queue). Each of the push sub-steps (`ensure_on_release_branch`, +//! `merge_upstream`, `push_release_branch`, `find_existing_release_pr`, +//! `open_release_pr`, `enable_auto_merge`, `return_to_base_branch`) is +//! its own small helper so a failure points at a named function in the +//! backtrace and refactors stay surgical. //! //! The auto-commit is created on the `release/vX.Y.Z` branch (see //! [`git_commit`] / `switch_to_release_branch`), never on `main`, so a @@ -47,8 +47,8 @@ pub(crate) const BASE_BRANCH: &str = "main"; /// branch already exists. /// /// Why this matters: committing on `main` left local `main` permanently -/// 1-ahead of `origin/main`, and after the PR squash-merged the local -/// commit's SHA diverged from GitHub's squashed commit, forcing a +/// 1-ahead of `origin/main`, and once the PR merged the local commit +/// diverged from what GitHub recorded on `main`, forcing a /// `git reset --hard origin/main` after every ship. Committing on the /// release branch keeps local `main` exactly at `origin/main` /// throughout, so a plain `git pull --ff-only` syncs it post-merge. @@ -174,24 +174,24 @@ async fn ensure_on_release_branch(ctx: &PipelineContext, release_branch: &str) - .await } -/// Rebase the currently-checked-out release branch onto -/// `origin/` so the auto-commit lands on top of any -/// intervening mainline changes before the PR is opened. +/// Merge `origin/` into the currently-checked-out release +/// branch so the PR is based on current mainline before it opens. /// -/// (Previously this rebased the *current* branch onto its own upstream -/// — valid when the ship committed on `main`. Now that the commit -/// lives on `release/vX.Y.Z`, we rebase onto `origin/main` so the PR is -/// based on current mainline.) +/// A MERGE, never a rebase: the branch's commits are signed, and a PR +/// lands on `main` as a merge commit with those commits kept one by +/// one. A rebase would rewrite (and drop the signatures of) every +/// commit and flatten any merge already on the branch. When the branch +/// already contains `origin/main` this is a no-op. /// /// # Errors /// -/// Propagates any failure from the wrapped `git pull --rebase` +/// Propagates any failure from the wrapped `git pull --no-rebase` /// subprocess (network, merge conflicts, ...). -async fn rebase_onto_upstream(ctx: &PipelineContext) -> Result<()> { +async fn merge_upstream(ctx: &PipelineContext) -> Result<()> { execute_command( - "Git pull rebase", + "Git pull merge", "git", - &["pull", "origin", BASE_BRANCH, "--rebase"], + &["pull", "--no-rebase", "--no-edit", "origin", BASE_BRANCH], ctx, ) .await @@ -246,8 +246,8 @@ fn find_existing_release_pr(release_branch: &str) -> Result> { } /// Open a new release PR via `gh pr create`, with the branch- -/// protection-compatible title + body that explains the auto-merge -/// squash strategy. +/// protection-compatible title + body that explains how it lands (the +/// merge queue, a merge commit) and what the merge triggers. /// /// # Errors /// @@ -262,26 +262,22 @@ async fn open_release_pr( let pr_body = format!( "## Summary\n\n\ `just ship` Phase 2 auto-commit for **v{version}** — the \ - `[workspace.package].version` bump in `Cargo.toml`. This PR \ - routes that commit through branch-protection rules. Once it \ - merges to `{base_branch}`, run `just release-tag` to cut the \ - signed `v{version}` tag, which fires `release.yml` and builds \ - the cross-platform binaries + GitHub Release v{version}. \ - (No auto-tag on merge — the tag step is manual on-demand, \ - Path B.)\n\n\ - ## Auto-merge\n\n\ - `--auto --squash` is queued — GitHub will merge as soon as the \ - required status checks pass. Squash is required because \ - `main-protection` mandates signed commits, and GitHub's \ - rebase-auto-merge cannot sign the rebased commit; the \ - squash-merge commit is signed by GitHub's own key, which \ - satisfies `required_signatures: true`. The original author's \ - signed commit remains verifiable in the PR branch history.\n\n\ + `[workspace.package].version` bump in `Cargo.toml`.\n\n\ + ## Merge\n\n\ + The PR is enrolled in `{base_branch}`'s merge queue \ + (`gh pr merge --auto`, the same call as `just merge `). \ + The queue merges it once the required checks pass, with a \ + merge commit: the branch's signed commits land one by one, \ + and GitHub signs the merge commit it creates, so \ + `required_signatures: true` holds. Never squash, never \ + rebase.\n\n\ ## After merge\n\n\ - The auto-commit lived only on `{release_branch}`, so local \ - `{base_branch}` never drifted — sync it with a plain \ - `git pull --ff-only origin {base_branch}` (no `reset --hard` \ - needed)." + `release-auto-trigger.yml` sees the merged `{release_branch}` \ + and dispatches `release.yml`, which builds the binaries and \ + publishes GitHub Release v{version} (`just release-tag` is the \ + manual fallback). The auto-commit lived only on \ + `{release_branch}`, so local `{base_branch}` never drifted — \ + sync it with `git pull --ff-only origin {base_branch}`." ); println!("📬 Opening release PR"); @@ -305,34 +301,27 @@ async fn open_release_pr( .await } -/// Enable GitHub auto-merge with the squash strategy for the release -/// PR. -/// -/// Squash is mandatory on this repo because: -/// -/// 1. `main-protection` requires `required_signatures: true` (every commit on -/// main must be signed). -/// 2. GitHub's rebase-auto-merge cannot sign the rebased commit; it fails -/// with `GraphQL: Base branch requires signed commits. Rebase merges -/// cannot be automatically signed by GitHub` (observed on PR #36, the -/// first real `just ship` for v0.5.69). -/// 3. GitHub signs the squash-merge commit with its own key, which satisfies -/// `required_signatures: true` on main. -/// -/// Trust trade-off: the author's GPG signature is lost on the commit -/// that lands on main (it becomes a GitHub-signed squash). The -/// original signed commit remains verifiable in the PR branch history, -/// and every prior merged PR on this repo uses the same pattern. +/// Enroll the release PR in `main`'s merge queue (`gh pr merge --auto` +/// with NO method flag, exactly what `just merge ` runs). +/// +/// The queue owns the merge method, and `main-protection` sets it to +/// MERGE: the release commit, signed by its author, lands on `main` +/// unchanged, and GitHub signs the merge commit it creates, so +/// `required_signatures: true` holds for both. Passing `--squash` (the +/// old call) is refused by a queue-governed branch and squashes the +/// history this project keeps; rebase cannot be signed by GitHub at all. +/// The queue only merges once the required checks pass on the merge +/// group, so enrolling early never lands an unchecked PR. /// /// # Errors /// /// Propagates any failure from the wrapped `gh pr merge` subprocess. async fn enable_auto_merge(ctx: &PipelineContext, release_branch: &str) -> Result<()> { - println!("⚡ Ensuring auto-merge is enabled (squash strategy)"); + println!("⚡ Enrolling the release PR in the merge queue (merge commit)"); execute_command( "Enable auto-merge", "gh", - &["pr", "merge", release_branch, "--auto", "--squash"], + &["pr", "merge", release_branch, "--auto"], ctx, ) .await @@ -343,7 +332,7 @@ async fn enable_auto_merge(ctx: &PipelineContext, release_branch: &str) -> Resul /// directly to `main`. /// /// Thin orchestrator — each sub-step lives in its own helper -/// ([`ensure_on_release_branch`], [`rebase_onto_upstream`], +/// ([`ensure_on_release_branch`], [`merge_upstream`], /// [`push_release_branch`], [`find_existing_release_pr`], /// [`open_release_pr`], [`enable_auto_merge`], [`return_to_base_branch`]) /// so the control flow stays readable and individual failures map 1:1 @@ -375,9 +364,9 @@ pub(crate) async fn git_push(ctx: &PipelineContext) -> Result<()> { // auto-commit rather than resetting it to a commit-less HEAD). ensure_on_release_branch(ctx, &release_branch).await?; - // Rebase the release branch onto current mainline before opening the - // PR (keeps the PR based on the latest `origin/main`). - rebase_onto_upstream(ctx).await?; + // Merge current mainline into the release branch before opening the + // PR (keeps the PR based on the latest `origin/main`; never a rebase). + merge_upstream(ctx).await?; push_release_branch(ctx, &release_branch).await?; @@ -398,7 +387,7 @@ pub(crate) async fn git_push(ctx: &PipelineContext) -> Result<()> { // Leave the developer back on `main`, untouched and exactly at // `origin/main`. Because the commit only ever lived on the release - // branch, local `main` never drifted — after the PR squash-merges, + // branch, local `main` never drifted — after the PR merges, // a plain `git pull --ff-only` syncs it (no `reset --hard` needed). return_to_base_branch(ctx).await?; diff --git a/scripts/ci-pipeline/src/ship.rs b/scripts/ci-pipeline/src/ship.rs index 2780ce7e7..86ba56f61 100644 --- a/scripts/ci-pipeline/src/ship.rs +++ b/scripts/ci-pipeline/src/ship.rs @@ -40,7 +40,7 @@ use crate::exec::{ execute_step_with_tracking, }; use crate::git_ops::{count_unpushed_commits, git_commit, git_push}; -use crate::version::{bump_workspace_version, get_current_version}; +use crate::version::{bump_workspace_version, get_current_version, sync_citation_file}; use crate::workflow::{ ALL_STEPS, STEP_CLEAN_ARTIFACTS, STEP_COVERAGE_TESTS, STEP_FORMAT_CHECK, STEP_FORMAT_CODE, STEP_GIT_COMMIT, STEP_GIT_PUSH, STEP_PARALLEL_VALIDATION, STEP_TOOLCHAIN_SYNC, WorkflowPhase, @@ -544,6 +544,11 @@ pub(crate) async fn run_enhanced_phase2( // gated by `version_incremented`, and the roll itself is a no-op on an // already-rolled file. roll_changelog_file(&new_version).context("Failed to roll CHANGELOG [Unreleased]")?; + // CITATION.cff names the release too (same commit, same date as the + // changelog roll). + let release_date = chrono::Local::now().format("%Y-%m-%d").to_string(); + sync_citation_file(&new_version, &release_date) + .context("Failed to sync CITATION.cff to the new version")?; state.version_incremented = true; state.current_version = new_version; state.save()?; diff --git a/scripts/ci-pipeline/src/version.rs b/scripts/ci-pipeline/src/version.rs index 64168e498..4b2524795 100644 --- a/scripts/ci-pipeline/src/version.rs +++ b/scripts/ci-pipeline/src/version.rs @@ -70,6 +70,63 @@ pub(crate) fn bump_workspace_version(level: &str) -> Result<()> { Ok(()) } +/// Set `version:` and `date-released:` in `CITATION.cff` to the release +/// being cut, so the citation metadata names the version it ships with. +/// +/// The file was meant to be bumped by the R5-retired version tooling and was +/// left at 0.5.120 for forty releases; this keeps it in the release commit. +/// Only the two top-level keys are rewritten; the rest of the file (comments +/// included) is kept byte for byte. A missing `CITATION.cff` is not an error. +/// +/// # Errors +/// +/// Returns an error if the file exists but cannot be read or written, or if +/// it lacks a top-level `version:` key. +pub(crate) fn sync_citation_file(version: &str, date: &str) -> Result<()> { + let path = std::path::Path::new("CITATION.cff"); + if !path.exists() { + return Ok(()); + } + let content = std::fs::read_to_string(path).context("Failed to read CITATION.cff")?; + let updated = sync_citation(&content, version, date)?; + if updated != content { + std::fs::write(path, updated).context("Failed to write CITATION.cff")?; + } + Ok(()) +} + +/// Rewrite the top-level `version:` and `date-released:` lines of a +/// `CITATION.cff` text (see [`sync_citation_file`]). +/// +/// # Errors +/// +/// Returns an error if `content` has no top-level `version:` key. +fn sync_citation(content: &str, version: &str, date: &str) -> Result { + let mut found_version = false; + let mut out = String::with_capacity(content.len()); + for line in content.split_inclusive('\n') { + let ending = if line.ends_with('\n') { "\n" } else { "" }; + if line.starts_with("version:") { + found_version = true; + out.push_str("version: \""); + out.push_str(version); + out.push('"'); + out.push_str(ending); + } else if line.starts_with("date-released:") { + out.push_str("date-released: \""); + out.push_str(date); + out.push('"'); + out.push_str(ending); + } else { + out.push_str(line); + } + } + if !found_version { + bail!("CITATION.cff has no top-level `version:` key"); + } + Ok(out) +} + /// Parse `content` (the text of a workspace root `Cargo.toml`) and /// extract the `version = "..."` entry from the `[workspace.package]` /// table specifically — ignores any unrelated `version = ...` lines in @@ -101,3 +158,23 @@ pub(crate) fn extract_version_from_cargo_toml(content: &str) -> Result { } bail!("Version extraction failed - no version found in [workspace.package]") } + +#[cfg(test)] +mod tests { + use super::sync_citation; + + #[test] + fn citation_version_and_date_are_rewritten_and_the_rest_kept() { + let before = "cff-version: 1.2.0\n# Keep in sync.\nversion: \"0.5.120\"\ndate-released: \"2026-06-09\"\ntype: software\n"; + let after = sync_citation(before, "0.6.47", "2026-11-10").unwrap(); + assert_eq!( + after, + "cff-version: 1.2.0\n# Keep in sync.\nversion: \"0.6.47\"\ndate-released: \"2026-11-10\"\ntype: software\n" + ); + } + + #[test] + fn citation_without_version_is_an_error() { + let _missing = sync_citation("cff-version: 1.2.0\n", "0.6.47", "2026-11-10").unwrap_err(); + } +} diff --git a/scripts/ci/check_commit_signatures.sh b/scripts/ci/check_commit_signatures.sh index 0c8e31532..462b39028 100755 --- a/scripts/ci/check_commit_signatures.sh +++ b/scripts/ci/check_commit_signatures.sh @@ -17,9 +17,9 @@ # same `{{COMMIT_RANGES}}` wiring from the pre-push hook). # # **Mode**: -# range RANGE -- check every non-merge commit in the git revision -# range RANGE (e.g. `origin/main..HEAD`). Empty / -# merges-only ranges silently succeed. +# range RANGE -- check every commit, merges included, in the git +# revision range RANGE (e.g. `origin/main..HEAD`). +# An empty range silently succeeds. # # **What counts as signed** — `git log --format=%G?` status codes: # G good signature -> OK @@ -57,7 +57,7 @@ usage() { cat <&2 Usage: $(basename "$0") range RANGE - range RANGE Verify every non-merge commit in RANGE is signed + range RANGE Verify every commit (merges included) in RANGE is signed (e.g. 'origin/main..HEAD'). Example: @@ -76,7 +76,7 @@ ${C_YELLOW}Fix (self-heal):${C_RESET} re-sign the whole branch in place, then re ${C_CYAN}just sign-branch${C_RESET} (equivalent to: - ${C_CYAN}git rebase --exec 'git commit --amend --no-edit -S' \$(git merge-base origin/main HEAD)${C_RESET} + ${C_CYAN}git rebase --rebase-merges --exec 'git commit --amend --no-edit -S' \$(git merge-base origin/main HEAD)${C_RESET} ${C_CYAN}git push --force-with-lease${C_RESET}) ${C_YELLOW}Root cause to avoid:${C_RESET} never commit with ${C_CYAN}-c commit.gpgsign=false${C_RESET}. @@ -96,9 +96,10 @@ case "$mode" in printf '%s[FAIL]%s not in a git repository\n' "$C_RED" "$C_RESET" >&2 exit 1 fi - # `%H%G?` one line per non-merge commit. Merge commits are - # skipped (`main` requires linear history, so they never reach the - # queue; a local merge on a feature branch would be flattened). + # `%H%G?` one line per commit, merges included: PRs land on + # `main` as merge commits through the queue, so a merge made on a + # branch reaches `main` as-is and must carry a signature like any + # other commit (the ruleset rejects it otherwise). fail=0 count=0 while IFS=$'\t' read -r oid sig; do @@ -114,7 +115,7 @@ case "$mode" in "$(git log -1 --format='%s' "$oid" 2>/dev/null)" >&2 ;; esac - done < <(git log --no-merges --format='%H%x09%G?' "$arg" 2>/dev/null || true) + done < <(git log --format='%H%x09%G?' "$arg" 2>/dev/null || true) if (( fail )); then print_help_block diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 96b1265e4..d0d918c5d 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -79,6 +79,12 @@ notes = """ Reviewed the full diff. It is dominated by a new src/engine/simd.rs adding AVX2 and NEON kernels behind a new default-on simd-unsafe feature, which is the crate's only unsafe and is confined to that module (forbid(unsafe_code) still applies with the feature off). I checked each kernel's bounds arithmetic — the loop guards require a full vector of readable input and reserve exactly the bytes stored, and complete_quads_len validates output capacity before any SIMD write — and verified the encode/decode lookup tables accept and reject precisely the bytes the scalar decoder does for both the standard and URL-safe alphabets; a differential harness of 624,480 cases on aarch64, covering round-trips plus corrupted and truncated inputs, found the SIMD engines byte- and error-identical to GeneralPurpose. The remainder is a mechanical refactor threading a simd_prefix closure through the scalar paths, the DecodeError::InvalidLastSymbol struct-variant change, and additive presets. No build script and no network or filesystem access; the preset engines remain scalar. """ +[[audits.bit-vec]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-run" +delta = "0.10.1 -> 0.10.2" +notes = "Reviewed the delta: BitVec::process gains a tail flag that masks the last block for nand/nor/xnor instead of filling unused bits with ones and fixing them afterwards; split_off no longer pushes one block more than the split-off length needs; blocks_mut and fix_last_block_with_ones removed; new tests for both fixes. Safe code only, no unsafe, no build script, no I/O or dependency change." + [[audits.bitflags]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -243,6 +249,12 @@ criteria = "safe-to-deploy" delta = "0.16.4 -> 0.16.6" notes = "Delta reviewed for the 2026-09-27 dependency refresh (local diff of the crates.io sources; library, macro and build-script lines read in full, tests and fixtures skimmed and grepped for process, net, fs, env, unsafe and include use). ANSI stripper handles OSC and DCS sequences on char boundaries, ASCII width fast path, truncate_str cuts on char boundaries (fixes a multibyte panic); the new terminal log fixture is read only by a cfg(test) test. Mirrored from the docenta repo supply-chain audit performed by the same auditor/owner." +[[audits.const-oid]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "0.10.2 -> 0.10.3" +notes = "Reviewed the delta: the arc decoder accumulates base-128 digits with checked_mul/checked_add and returns ArcTooBig on overflow, replacing a byte-count heuristic that let a five-byte arc above u32::MAX truncate silently (regression test added); new RFC 6211 OID constants and database entries; doc links. No unsafe, no build script, no I/O or dependency change." + [[audits.cpufeatures]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -267,6 +279,12 @@ criteria = "safe-to-deploy" delta = "1.5.2 -> 1.5.3" notes = "Refactor of the hasher state. The specialized (SIMD) State becomes a zero-sized token whose existence proves CPU-feature detection succeeded; the running CRC moves to Hasher as a plain u32, and update() returns the new value instead of mutating. The unsafe calls into calculate/calculate_avx2/calculate_avx512 and the aarch64 path are unchanged and still reachable only through that token. Short inputs in the pclmulqdq path now fall back to baseline::update_slow (an existing safe function). Adds total_len() and tests. New dependabot/CI/release workflow files are not part of the build. No build.rs, process, network, filesystem or environment access." +[[audits.crc32fast]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "1.5.3 -> 1.5.5" +notes = "Reviewed the delta: deny(unsafe_code) crate-wide with allows only on the SIMD modules; new aarch64 PMULL folding kernel gated on runtime aes detection and input >= 64 bytes, every vector load slices a[..16] first so a miscount panics rather than reads out of bounds; pclmulqdq loads slice a[..N] first; env read only under cfg(test); no build script change. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.crossbeam-channel]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -443,6 +461,12 @@ criteria = "safe-to-deploy" delta = "2.4.1 -> 2.5.0" notes = 'Source changes under src/ are doc-comment wording only ("ranges" -> "range"); no logic, no unsafe, no build.rs (build = false). The version bump exists to move the optional wasm-only getrandom dependency and dev-dependencies from 0.3.4 to 0.4 (rand 0.9 -> 0.10 for benches); no new dependencies otherwise. Bench harness rewritten to adapt wyhash to the rand 0.10 TryRng trait, dev-only. Lockfile churn reflects the getrandom/rand major bumps. No fs/net/process/env surface exists or was added.' +[[audits.flate2]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "1.1.10 -> 1.1.11" +notes = "Reviewed the delta: zlib-rs backend gains decompress_uninit/compress_uninit forwarding to zlib-rs safe APIs with the written count from total_out as before; reset fixes for C, miniz and zlib-rs backends; zio Writer finished flag, UnexpectedEof on truncation, buf_pos cursor with a no-progress guard; unsafe site count per file unchanged; no build script. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.fs4]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -688,6 +712,12 @@ criteria = "safe-to-deploy" version = "1.0.18" notes = "Full audit (488 LOC, no_std, zero deps). No fs/net/process/env capability; no build.rs. All 13 unsafe sites reviewed: digit-pair table reads are get_unchecked(pair*2+{0,1}) with pair<100 from divmod100 into a 200-byte ASCII table (in-bounds by construction); buffer writes are checked-index MaybeUninit writes covering exactly [offset, len); slice_buffer_to_str's from_utf8_unchecked is sound because only ASCII table bytes and b'0'+digit are ever written; Buffer::format's pointer cast goes from the largest buffer ([MaybeUninit; i128 MAX_STR_LEN]) to a smaller same-alignment array, and the unreachable_unchecked is an optimizer hint upheld because write() slices within the buffer. Publisher verified on crates.io: dtolnay." +[[audits.itoa]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "1.0.18 -> 1.0.19" +notes = "Reviewed the full delta. The digit writers replace bounds-checked buf[offset] stores with get_unchecked_mut; offsets start at the buffer length (MAX_STR_LEN) and decrease by exactly the digit groups written, and the signed path starts one byte in to reserve the minus sign, so every unchecked index stays in bounds. No build script, I/O, process or network changes. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.jobserver]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -812,6 +842,12 @@ criteria = "safe-to-deploy" delta = "0.4.33 -> 0.4.34" notes = "Delta review: full diffstat, all risk-pattern additions (unsafe/process/net/fs/build), dependency and build-script changes scanned; nothing outside the crate domain, no new deps beyond same-family path bumps, no build scripts. Mirrored from the docenta and uffs-products repo supply-chain audits performed by the same auditor/owner." +[[audits.log]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "0.4.34 -> 0.4.35" +notes = "Reviewed the delta: only the html_root_url doc attribute dropped in src/lib.rs; the rest is CI, release-plz config, CHANGELOG, README and version metadata. No build script, unsafe or new dependencies. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.lru-slab]] who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -1365,6 +1401,12 @@ criteria = "safe-to-deploy" delta = "3.5.1 -> 3.8.0" notes = "Three minor releases (3.6.0 to 3.8.0). Non-test unsafe: none (the only new unsafe is an fcntl(F_GETFL) in a Linux test). New stdio_pipes transport reopens Linux FIFO stdin/stdout through /proc/self/fd with O_NONBLOCK so the original descriptors keep their flags; falls back to blocking stdio otherwise. AsyncRwTransport bounds incoming lines (16 MiB default). Model deserialization rejects JSON-RPC messages carrying both result and error and reads integral float ids. Client cancels abandoned requests via a drop guard and answers pings during initialize. OAuth (auth feature, not enabled in this workspace): SSRF hardening via a DNS resolver that blocks link-local, metadata and reserved addresses and allows private or loopback only when the base URL names one; reads the standard proxy env vars only to exempt configured proxies; bounded redirects. Streamable-HTTP fixes (feature not enabled here). New Linux-only libc dependency. No build.rs, no process spawning, no new outbound endpoints." +[[audits.rmcp]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "3.8.0 -> 3.9.0" +notes = "Reviewed the delta: SEP-2640 skill data model (types, SHA-256 digest checks, manifest URI validation, no I/O), HTTP client response bodies bounded (16 MiB JSON, 64 KiB errors), stricter Accept and Mcp-Param parsing, cancellation-safe stdio protocol-error reply, structured OAuth discovery errors, graceful_shutdown sends SIGTERM through process-wrap's safe signal API (libc only for the constant) before killing a child. No build script, no new unsafe; Command/fs only in cfg(test) and tests/. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.rmcp-macros]] who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -1465,6 +1507,12 @@ criteria = "safe-to-deploy" delta = "3.5.1 -> 3.8.0" notes = "Version bump only, plus the LICENSE file now shipped in the crate. No code change." +[[audits.rmcp-macros]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "3.8.0 -> 3.9.0" +notes = "Reviewed the delta: CHANGELOG and version metadata only; no code change. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.rustc-hash]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -1553,6 +1601,12 @@ criteria = "safe-to-deploy" delta = "1.14.1 -> 1.15.1" notes = "Small delta with no unsafe code and no new runtime dependencies; only criterion is added as a dev-dependency plus a dns_name bench target (the bench file is not even included in the published package). New public API is the const constructor FipsStatus::certified, a trivial enum builder. The TLS-relevant change is a rewrite of the const DNS-name validate function in src/server_name.rs from a per-character state machine to a per-label loop; the two implementations were compared case by case and they enforce the same rules (253-byte total max, 1-63 byte labels, alphanumeric/underscore with interior hyphens only, no empty labels, all-numeric final label rejected, trailing dot accepted), with empty input now rejected by an explicit up-front check rather than the old final-state check — no loosening observed. A matching unit test for FipsStatus Debug formatting is added. Nothing suspicious observed; capability surface is unchanged." +[[audits.rustls-pki-types]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "1.15.1 -> 1.16.0" +notes = "Reviewed the delta: DnsName strips one trailing dot on parse and gains into_owned; validation now also rejects a final label that is a hex number (0x...) or a leading-zero number, like the WHATWG ends-in-a-number rule; base64 helper renamed to u8_is_zero with a corrected doc (body unchanged); PEM error text names the real 256 MB limit; doc links. One new unsafe: strip_trailing_dot calls str::from_utf8_unchecked on the input minus a final ASCII '.', which stays valid UTF-8. No build script, I/O or dependency change." + [[audits.rustls-webpki]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -1722,6 +1776,12 @@ criteria = "safe-to-deploy" delta = "1.16.3 -> 1.16.4" notes = "Version bump only (plus the crate's own dev Cargo.lock). No source change." +[[audits.smallvec]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "1.16.4 -> 1.16.5" +notes = "Reviewed the delta: README and comment re-wrapping plus version metadata only; no code, unsafe or dependency change. Same review as recorded in skyllc-ai/docenta, re-read against this diff." + [[audits.snap]] who = "Robert M1 <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -2333,6 +2393,12 @@ criteria = "safe-to-deploy" delta = "0.6.7 -> 0.6.8" notes = "Delta reviewed for the 2026-09-27 dependency refresh (local diff of the crates.io sources, read in full unless noted). Style refactors, trace macro fixes and Deflate::new_with_config validated by deflate::init; no new unsafe. Mirrored from the docenta repo supply-chain audit performed by the same auditor/owner." +[[audits.zstd]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "0.13.3 -> 0.13.4" +notes = "Reviewed the delta: backport of the 0.14 fixes to the 0.13 line. Encoders and decoders built with_context reset the session first, so a reused context starts a new frame; from_continuous checks the sample-size sum for overflow; the auto-finish wrappers skip finishing after a writer panicked; set_pledged_src_size added; zstd-safe requirement 7.1.0 -> 7.4.0. The crate's license changes from MIT to BSD-3-Clause (allowed by deny.toml). No unsafe added, no build script change, no new I/O." + [[audits.zstd]] who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" @@ -2357,6 +2423,12 @@ criteria = "safe-to-deploy" delta = "7.2.4 -> 7.3.0" notes = "Delta reviewed for the 2026-09-07 dependency refresh (local diff of the registry sources, 6 files). Relicensing MIT OR Apache-2.0 -> BSD-3-Clause (LICENSE files rewritten; the deny gate checks it); the only source change marks the three experimental block-API methods CCtx::compress_block, DCtx::decompress_block and DCtx::insert_block as unsafe fn with Safety docs, bodies unchanged. No new unsafe blocks or FFI calls, build.rs unchanged, no dependency or feature change. Mirrored from the docenta repo supply-chain audit performed by the same auditor/owner." +[[audits.zstd-safe]] +who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" +criteria = "safe-to-deploy" +delta = "7.3.0 -> 7.4.0" +notes = "Reviewed the delta, focused on the FFI wrappers: the backport of the RUSTSEC-2026-0391/0398/0403 fixes. CCtx/DCtx remember an error that may leave the C context undefined (Poison) and refuse further stream calls until a reset; one-shot calls reset the session afterwards; In/OutBuffer wrappers discard the position zstd reports on a failed call, so no unwritten bytes are marked initialized; InBuffer::wrap panics on pos > len; OutBuffer::around_pos zero-fills initialized..pos after the existing pos <= capacity check; multithreaded frames are tracked so dictionaries and the job table are not freed under running workers; DCtx::try_clone refuses contexts owning a dictionary (ZSTD_copyDCtx is a memcpy) and resets the copy; StableInBuffer(true) is refused; seekable decompress bounds-checked. New unsafe sites are FFI calls with the same contracts plus the bounds-checked zero-fill. zstd-sys requirement 2.0.15 -> 2.0.17; no build script change." + [[audits.zstd-safe]] who = "Robert Nio <50460704+githubrobbi@users.noreply.github.com>" criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 5d7c9334e..58e072177 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -344,22 +344,6 @@ criteria = "safe-to-deploy" version = "0.13.1" criteria = "safe-to-deploy" -[[exemptions.futures]] -version = "0.3.32" -criteria = "safe-to-deploy" - -[[exemptions.futures-channel]] -version = "0.3.32" -criteria = "safe-to-deploy" - -[[exemptions.futures-executor]] -version = "0.3.32" -criteria = "safe-to-deploy" - -[[exemptions.futures-io]] -version = "0.3.32" -criteria = "safe-to-deploy" - [[exemptions.futures-task]] version = "0.3.32" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index bed2c56f8..964d654e2 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -7,8 +7,8 @@ when = "2026-06-17" trusted-publisher = "github:RustCrypto/traits" [[publisher.aes]] -version = "0.9.3" -when = "2026-08-28" +version = "0.9.4" +when = "2026-11-02" trusted-publisher = "github:RustCrypto/block-ciphers" [[publisher.anyhow]] @@ -53,8 +53,8 @@ user-login = "Darksonn" user-name = "Alice Ryhl" [[publisher.cc]] -version = "1.10.0" -when = "2026-10-23" +version = "1.11.1" +when = "2026-11-06" trusted-publisher = "github:rust-lang/cc-rs" [[publisher.cfg-if]] @@ -138,8 +138,8 @@ when = "2026-05-20" trusted-publisher = "github:RustCrypto/block-modes" [[publisher.ctutils]] -version = "0.4.7" -when = "2026-10-29" +version = "0.4.8" +when = "2026-11-02" trusted-publisher = "github:RustCrypto/utils" [[publisher.digest]] @@ -307,13 +307,13 @@ user-login = "cuviper" user-name = "Josh Stone" [[publisher.inout]] -version = "0.2.5" -when = "2026-10-19" +version = "0.2.6" +when = "2026-11-07" trusted-publisher = "github:RustCrypto/utils" [[publisher.js-sys]] -version = "0.3.106" -when = "2026-09-25" +version = "0.3.107" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.jsonpath_lib_polars_vendor]] @@ -343,8 +343,8 @@ user-login = "BurntSushi" user-name = "Andrew Gallant" [[publisher.mime]] -version = "0.3.17" -when = "2023-03-20" +version = "0.3.18" +when = "2026-11-05" user-id = 359 user-login = "seanmonstar" user-name = "Sean McArthur" @@ -635,8 +635,8 @@ user-login = "dtolnay" user-name = "David Tolnay" [[publisher.serde_spanned]] -version = "1.1.2" -when = "2026-10-08" +version = "1.1.3" +when = "2026-11-06" user-id = 6743 user-login = "epage" user-name = "Ed Page" @@ -647,8 +647,8 @@ when = "2026-10-12" trusted-publisher = "github:RustCrypto/hashes" [[publisher.signal-hook]] -version = "0.4.5" -when = "2026-10-04" +version = "0.4.6" +when = "2026-10-31" user-id = 5906 user-login = "vorner" user-name = "Michal 'vorner' Vaner" @@ -661,8 +661,8 @@ user-login = "dtolnay" user-name = "David Tolnay" [[publisher.syn]] -version = "3.0.9" -when = "2026-10-12" +version = "3.0.10" +when = "2026-11-09" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" @@ -703,22 +703,22 @@ user-login = "Darksonn" user-name = "Alice Ryhl" [[publisher.toml]] -version = "1.1.8+spec-1.1.0" -when = "2026-10-09" +version = "1.2.1+spec-1.1.0" +when = "2026-11-06" user-id = 6743 user-login = "epage" user-name = "Ed Page" [[publisher.toml_datetime]] -version = "1.1.2+spec-1.1.0" -when = "2026-10-08" +version = "1.1.3+spec-1.1.0" +when = "2026-11-06" user-id = 6743 user-login = "epage" user-name = "Ed Page" [[publisher.toml_edit]] -version = "0.25.17+spec-1.1.0" -when = "2026-10-09" +version = "0.25.19+spec-1.1.0" +when = "2026-11-06" user-id = 6743 user-login = "epage" user-name = "Ed Page" @@ -731,8 +731,8 @@ user-login = "epage" user-name = "Ed Page" [[publisher.toml_writer]] -version = "1.1.3+spec-1.1.0" -when = "2026-10-08" +version = "1.2.0+spec-1.1.0" +when = "2026-11-04" user-id = 6743 user-login = "epage" user-name = "Ed Page" @@ -792,15 +792,22 @@ user-login = "Manishearth" user-name = "Manish Goregaokar" [[publisher.utf8_iter]] -version = "1.0.4" -when = "2023-12-01" +version = "1.1.0" +when = "2026-10-29" +user-id = 4484 +user-login = "hsivonen" +user-name = "Henri Sivonen" + +[[publisher.utf_types]] +version = "1.0.0" +when = "2026-10-29" user-id = 4484 user-login = "hsivonen" user-name = "Henri Sivonen" [[publisher.uuid]] -version = "1.29.0" -when = "2026-10-23" +version = "1.29.2" +when = "2026-11-09" user-id = 3204 user-login = "KodrAus" user-name = "Ashley Mannix" @@ -813,38 +820,38 @@ user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen]] -version = "0.2.129" -when = "2026-09-25" +version = "0.2.130" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.wasm-bindgen-futures]] -version = "0.4.79" -when = "2026-09-25" +version = "0.4.80" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.wasm-bindgen-macro]] -version = "0.2.129" -when = "2026-09-25" +version = "0.2.130" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.wasm-bindgen-macro-support]] -version = "0.2.129" -when = "2026-09-25" +version = "0.2.130" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.wasm-bindgen-shared]] -version = "0.2.129" -when = "2026-09-25" +version = "0.2.130" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.web-sys]] -version = "0.3.106" -when = "2026-09-25" +version = "0.3.107" +when = "2026-10-30" trusted-publisher = "github:wasm-bindgen/wasm-bindgen" [[publisher.winnow]] -version = "1.0.4" -when = "2026-07-13" +version = "1.0.5" +when = "2026-11-05" user-id = 6743 user-login = "epage" user-name = "Ed Page" @@ -1004,6 +1011,26 @@ who = "Dan Gohman " criteria = "safe-to-deploy" delta = "0.3.9 -> 0.3.10" +[[audits.bytecode-alliance.audits.futures]] +who = "Joel Dice " +criteria = "safe-to-deploy" +version = "0.3.31" + +[[audits.bytecode-alliance.audits.futures]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.31 -> 0.3.34" + +[[audits.bytecode-alliance.audits.futures-channel]] +who = "Joel Dice " +criteria = "safe-to-deploy" +version = "0.3.31" + +[[audits.bytecode-alliance.audits.futures-channel]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.31 -> 0.3.34" + [[audits.bytecode-alliance.audits.futures-core]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -1015,6 +1042,26 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "0.3.28 -> 0.3.31" +[[audits.bytecode-alliance.audits.futures-executor]] +who = "Joel Dice " +criteria = "safe-to-deploy" +version = "0.3.31" + +[[audits.bytecode-alliance.audits.futures-executor]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.31 -> 0.3.34" + +[[audits.bytecode-alliance.audits.futures-io]] +who = "Joel Dice " +criteria = "safe-to-deploy" +version = "0.3.31" + +[[audits.bytecode-alliance.audits.futures-io]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.3.31 -> 0.3.34" + [[audits.bytecode-alliance.audits.futures-macro]] who = "Joel Dice " criteria = "safe-to-deploy" @@ -1834,12 +1881,21 @@ notes = "All code written or reviewed by Manish" aggregated-from = "https://raw.githubusercontent.com/mozilla-firefox/firefox/main/supply-chain/audits.toml" [[audits.mozilla.wildcard-audits.utf8_iter]] -who = "Makoto Kato " +who = "Henri Sivonen " criteria = "safe-to-deploy" user-id = 4484 # Henri Sivonen (hsivonen) start = "2022-04-19" -end = "2024-06-16" -notes = "Maintained by Henri Sivonen who works at Mozilla." +end = "2027-10-30" +notes = "I wrote this crate." +aggregated-from = "https://raw.githubusercontent.com/mozilla-firefox/firefox/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.utf_types]] +who = "Henri Sivonen " +criteria = "safe-to-deploy" +user-id = 4484 # Henri Sivonen (hsivonen) +start = "2026-09-24" +end = "2027-10-30" +notes = "I wrote this crate." aggregated-from = "https://raw.githubusercontent.com/mozilla-firefox/firefox/main/supply-chain/audits.toml" [[audits.mozilla.audits.adler2]]