From 5afa5b7695e65d0f2988cc2cb5c1954c3b24e48f Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Sat, 3 Oct 2026 08:20:25 +0200 Subject: [PATCH 1/2] ci(winget): fail the fork sync loudly when the PAT lacks the workflow scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field failure 2026-10-03 (v0.6.43): microsoft/winget-pkgs had merged a .github/workflows change since the last release, and GitHub refuses to fast-forward a fork over such commits for a PAT without the `workflow` scope. The sync step was `continue-on-error` with a `||` warning, so the refusal scrolled past, the fork stayed 81 commits behind, and komac failed with the same misleading `CreateRef` permissions error as the August stale-fork incident — the submission looked like a credential problem again. The sync step now fails hard, naming the real cause: the workflow-scope refusal gets its own message with the in-place PAT fix and the manual sync command, any other sync failure says so, and a fork that is still behind upstream after the sync stops the job before komac runs. The weekly readiness probe reads the token's scopes directly (the refusal only reproduces while upstream has pending workflow changes, so the sync-then-branch probe alone can pass one week and fail the next) and treats a failed or incomplete sync as not ready. Docs and the rotation issue state the required scopes: `public_repo` + `workflow`. --- .github/workflows/winget-publish.yml | 53 +++++++++++++++---- .../workflows/winget-token-expiry-check.yml | 28 ++++++++-- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/.github/workflows/winget-publish.yml b/.github/workflows/winget-publish.yml index 1023c342b..3466bb62f 100644 --- a/.github/workflows/winget-publish.yml +++ b/.github/workflows/winget-publish.yml @@ -32,13 +32,23 @@ # # ── Required secret ────────────────────────────────────────────────── # `WINGET_TOKEN` — a **classic** Personal Access Token with the -# `public_repo` scope (fine-grained tokens that can fork + push to -# microsoft/winget-pkgs also work). The default `GITHUB_TOKEN` CANNOT +# `public_repo` AND `workflow` scopes. The default `GITHUB_TOKEN` CANNOT # be used: it has no permission to fork an external repo or push the # manifest branch. Create it under the maintainer account that owns the # winget-pkgs fork (the same account that hand-submitted PR # microsoft/winget-pkgs#378294 for v0.5.102), then add it at # Settings → Secrets and variables → Actions → New repository secret. +# +# Why `workflow` too (field failure 2026-10-03, v0.6.43): the fork sync +# below fast-forwards githubrobbi/winget-pkgs over whatever upstream +# merged since the last release. Whenever that span touches a file +# under `.github/workflows/` — which microsoft/winget-pkgs does every +# few weeks — GitHub rejects the fast-forward for a PAT without the +# `workflow` scope ("refusing to allow a Personal Access Token to +# create or update workflow"). The fork then stays stale and komac +# fails with the misleading `CreateRef` permissions error. A classic +# PAT's scopes can be edited in place at https://github.com/settings/tokens +# (no rotation needed). name: 📦 WinGet Publish @@ -122,13 +132,19 @@ jobs: # winget-pkgs moves constantly and nothing else ever pushes to the # fork. Syncing here makes the submission self-healing. # - # `continue-on-error`: a sync hiccup must not block the submission — - # if the fork happens to be current, komac proceeds fine regardless. - # The `||` fallback keeps the step green while still surfacing the - # reason in the log. + # Second field failure 2026-10-03 (v0.6.43): the sync itself was + # refused — upstream had merged a `.github/workflows/` change and + # GitHub will not let a PAT without the `workflow` scope fast-forward + # over it. The step was `continue-on-error` with a `||` warning, so + # the refusal scrolled past as a warning, the fork stayed 81 commits + # behind, and komac failed with the same misleading `CreateRef` + # message as in August. This step therefore FAILS HARD now, with + # the real cause, whenever the fork is not at upstream's HEAD after + # the sync: komac cannot succeed from a stale base, so letting it + # try only buries the diagnosis. A fork that was already current + # never reaches the sync call. - name: Sync winget-pkgs fork with upstream if: steps.token.outputs.present == 'true' - continue-on-error: true env: GH_TOKEN: ${{ secrets.WINGET_TOKEN }} FORK_USER: githubrobbi @@ -137,16 +153,31 @@ jobs: set -uo pipefail before="$(gh api "repos/${FORK_USER}/winget-pkgs/commits/master" --jq .sha 2>/dev/null || echo unknown)" upstream="$(gh api repos/microsoft/winget-pkgs/commits/master --jq .sha 2>/dev/null || echo unknown)" - if [[ "$before" == "$upstream" && "$before" != unknown ]]; then + if [[ "$upstream" == unknown ]]; then + echo "::error title=Fork sync failed::Cannot read microsoft/winget-pkgs master with WINGET_TOKEN." + exit 1 + fi + if [[ "$before" == "$upstream" ]]; then echo "Fork already in sync at ${before}." exit 0 fi echo "Fork ${before} behind upstream ${upstream} — fast-forwarding." - gh api -X POST "repos/${FORK_USER}/winget-pkgs/merge-upstream" \ - -f branch=master --jq '.message' \ - || echo "::warning title=Fork sync failed::Could not fast-forward ${FORK_USER}/winget-pkgs; komac may fail to create its manifest branch." + if ! sync_out="$(gh api -X POST "repos/${FORK_USER}/winget-pkgs/merge-upstream" -f branch=master 2>&1)"; then + echo "$sync_out" + if grep -q "refusing to allow a Personal Access Token to create or update workflow" <<<"$sync_out"; then + echo "::error title=WINGET_TOKEN lacks the workflow scope::Upstream winget-pkgs changed a .github/workflows file and GitHub refuses to fast-forward the fork with a PAT that lacks the \`workflow\` scope. Fix: edit the classic PAT at https://github.com/settings/tokens to add \`workflow\` (no rotation needed), or sync once by hand with a token that has it: gh api -X POST repos/${FORK_USER}/winget-pkgs/merge-upstream -f branch=master — then re-run this workflow for ${{ inputs.release-tag }}." + else + echo "::error title=Fork sync failed::Could not fast-forward ${FORK_USER}/winget-pkgs (see output above). komac cannot cut its manifest branch from a stale fork, so the submission stops here." + fi + exit 1 + fi + echo "$sync_out" | jq -r '.message' 2>/dev/null || echo "$sync_out" after="$(gh api "repos/${FORK_USER}/winget-pkgs/commits/master" --jq .sha 2>/dev/null || echo unknown)" echo "Fork now at ${after} (upstream ${upstream})." + if [[ "$after" != "$upstream" ]]; then + echo "::error title=Fork still behind upstream::${FORK_USER}/winget-pkgs is at ${after}, upstream is ${upstream}. komac would fail with a misleading CreateRef permissions error; sync the fork and re-run." + exit 1 + fi - name: Submit manifest to winget-pkgs if: steps.token.outputs.present == 'true' diff --git a/.github/workflows/winget-token-expiry-check.yml b/.github/workflows/winget-token-expiry-check.yml index 528e47162..a38016de9 100644 --- a/.github/workflows/winget-token-expiry-check.yml +++ b/.github/workflows/winget-token-expiry-check.yml @@ -4,7 +4,7 @@ # ───────────────────────────────────────────────────────────────────────────── # winget-token-expiry-check — weekly heads-up before WINGET_TOKEN dies. # -# `winget-publish.yml` needs a classic PAT (`WINGET_TOKEN`, `public_repo` +# `winget-publish.yml` needs a classic PAT (`WINGET_TOKEN`, `public_repo` + `workflow` # scope) to fork + push to microsoft/winget-pkgs. When that PAT expires # the release-triggered WinGet submission fails — and because it runs on # the `release: released` event (not the PR path), nobody sees the red X @@ -174,9 +174,27 @@ jobs: || fail "cannot read microsoft/winget-pkgs master" [[ -n "${upstream:-}" ]] || fail "upstream master SHA came back empty" + # Scope check (field failure 2026-10-03, v0.6.43): the fork sync + # is refused outright whenever upstream's pending commits touch + # `.github/workflows/` and the PAT lacks the `workflow` scope — + # a condition that comes and goes with upstream's activity, so + # the sync-then-branch probe below can pass one week and the + # release fail the next. Reading the scopes is deterministic. + scopes="$(gh api -i user 2>/dev/null | grep -i '^x-oauth-scopes:' | cut -d: -f2- | tr -d ' \r')" + if [[ -n "$scopes" ]] && ! grep -qw workflow <<<"$scopes"; then + fail "WINGET_TOKEN lacks the workflow scope (has: ${scopes}); the fork sync is refused whenever microsoft/winget-pkgs changes a workflow file. Edit the classic PAT at https://github.com/settings/tokens to add workflow." + fi + # Fast-forward the fork, mirroring what the publish workflow does. - gh api -X POST "repos/${FORK_USER}/winget-pkgs/merge-upstream" \ - -f branch=master >/dev/null 2>&1 || true + if ! sync_out="$(gh api -X POST "repos/${FORK_USER}/winget-pkgs/merge-upstream" -f branch=master 2>&1)"; then + if grep -q "refusing to allow a Personal Access Token to create or update workflow" <<<"$sync_out"; then + fail "fork sync refused: WINGET_TOKEN lacks the workflow scope and upstream has pending workflow changes" + fi + fail "fork sync failed: ${sync_out}" + fi + fork_head="$(gh api "repos/${FORK_USER}/winget-pkgs/commits/master" --jq .sha 2>/dev/null || true)" + [[ "$fork_head" == "$upstream" ]] \ + || fail "fork still behind after sync (fork ${fork_head:-?}, upstream ${upstream})" # The real test: create the kind of ref komac creates, then remove # it. A unique name keeps concurrent runs from colliding. @@ -277,11 +295,11 @@ jobs: `Check, in this order, before assuming the token is at fault:`, `1. Does the fork \`githubrobbi/winget-pkgs\` still exist and is it writable?`, `2. Is it far behind \`microsoft/winget-pkgs\`? (Publishing self-heals this, but a failing sync will not.)`, - `3. Only then suspect the token — and note the action requires a **classic** PAT; fine-grained PATs are unsupported and cannot open the cross-fork PR.`, + `3. Only then suspect the token — and note the action requires a **classic** PAT with the \`public_repo\` **and** \`workflow\` scopes; without \`workflow\` the fork sync is refused whenever upstream touched a workflow file, and fine-grained PATs are unsupported (they cannot open the cross-fork PR).`, ``, ] : []), `### How to rotate`, - `1. Create a new **classic** PAT at https://github.com/settings/tokens — scope \`public_repo\` only, ~1-year expiry.`, + `1. Create a new **classic** PAT at https://github.com/settings/tokens — scopes \`public_repo\` + \`workflow\`, ~1-year expiry.`, `2. \`gh secret set WINGET_TOKEN --repo ${owner}/${repo}\` and paste it.`, `3. Run the **🔑 WinGet Token Expiry Check** workflow manually (\`workflow_dispatch\`) to confirm healthy — it will auto-close this issue.`, ``, From c0f762eae092f47806b11e7ac978b7f127bc8505 Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Sat, 3 Oct 2026 08:26:09 +0200 Subject: [PATCH 2/2] chore: development v0.6.44 - comprehensive testing complete [auto-commit] --- CHANGELOG.md | 19 +++++++++++++- Cargo.lock | 52 +++++++++++++++++++------------------- Cargo.toml | 28 ++++++++++---------- crates/uffs-cli/Cargo.toml | 4 +-- 4 files changed, 60 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70a37deb7..12730d876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.44] - 2026-10-03 + +### Added + +- cli: make --benchmark measure output production; -v shows the rows + +### Changed + +- daemon: never force a tier change on memory pressure + +### Fixed + +- client: report the client's own deadline as Timeout, never as a warm-up retry +- daemon: surface search timeouts as errors and cancel the orphaned scan +- daemon: log the per-tick USN refresh at debug, not info + ## [0.6.43] - 2026-10-03 ### Added @@ -2880,7 +2896,8 @@ thin clients over a unified `uffsd` process. ### Fixed - Various MFT parsing edge cases -[Unreleased]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.43...HEAD +[Unreleased]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.44...HEAD +[0.6.44]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.43...v0.6.44 [0.6.43]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.42...v0.6.43 [0.6.42]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.41...v0.6.42 [0.6.41]: https://github.com/skyllc-ai/UltraFastFileSearch/compare/v0.6.40...v0.6.41 diff --git a/Cargo.lock b/Cargo.lock index 98e53a39b..d5f381c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4329,7 +4329,7 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uffs-bench" -version = "0.6.43" +version = "0.6.44" dependencies = [ "chrono", "clap", @@ -4346,7 +4346,7 @@ dependencies = [ [[package]] name = "uffs-broker" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "serde", @@ -4365,14 +4365,14 @@ dependencies = [ [[package]] name = "uffs-broker-protocol" -version = "0.6.43" +version = "0.6.44" dependencies = [ "thiserror 2.0.21", ] [[package]] name = "uffs-ci-pipeline" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "chrono", @@ -4391,7 +4391,7 @@ dependencies = [ [[package]] name = "uffs-cli" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "assert_cmd", @@ -4414,7 +4414,7 @@ dependencies = [ [[package]] name = "uffs-client" -version = "0.6.43" +version = "0.6.44" dependencies = [ "dirs-next", "libc", @@ -4434,7 +4434,7 @@ dependencies = [ [[package]] name = "uffs-core" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "bytemuck", @@ -4465,7 +4465,7 @@ dependencies = [ [[package]] name = "uffs-daemon" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "clap", @@ -4498,7 +4498,7 @@ dependencies = [ [[package]] name = "uffs-diag" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "chrono", @@ -4513,7 +4513,7 @@ dependencies = [ [[package]] name = "uffs-fetch" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "hex", @@ -4524,7 +4524,7 @@ dependencies = [ [[package]] name = "uffs-format" -version = "0.6.43" +version = "0.6.44" dependencies = [ "chrono", "itoa", @@ -4535,7 +4535,7 @@ dependencies = [ [[package]] name = "uffs-gen-hooks" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "clap", @@ -4547,7 +4547,7 @@ dependencies = [ [[package]] name = "uffs-gen-workflow" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "clap", @@ -4560,7 +4560,7 @@ dependencies = [ [[package]] name = "uffs-manifest-audit" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "clap", @@ -4572,7 +4572,7 @@ dependencies = [ [[package]] name = "uffs-mcp" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "axum", @@ -4596,7 +4596,7 @@ dependencies = [ [[package]] name = "uffs-mft" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "bitflags", @@ -4637,14 +4637,14 @@ dependencies = [ [[package]] name = "uffs-polars" -version = "0.6.43" +version = "0.6.44" dependencies = [ "polars", ] [[package]] name = "uffs-security" -version = "0.6.43" +version = "0.6.44" dependencies = [ "aes-gcm", "dirs-next", @@ -4659,22 +4659,22 @@ dependencies = [ [[package]] name = "uffs-statusfmt" -version = "0.6.43" +version = "0.6.44" [[package]] name = "uffs-text" -version = "0.6.43" +version = "0.6.44" dependencies = [ "bytemuck", ] [[package]] name = "uffs-time" -version = "0.6.43" +version = "0.6.44" [[package]] name = "uffs-update" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "dirs-next", @@ -4691,11 +4691,11 @@ dependencies = [ [[package]] name = "uffs-version" -version = "0.6.43" +version = "0.6.44" [[package]] name = "uffs-vss-requestor" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "cc", @@ -4707,7 +4707,7 @@ dependencies = [ [[package]] name = "uffs-watchdog" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "dirs-next", @@ -4716,7 +4716,7 @@ dependencies = [ [[package]] name = "uffs-winsvc" -version = "0.6.43" +version = "0.6.44" dependencies = [ "anyhow", "windows", diff --git a/Cargo.toml b/Cargo.toml index 19384d67a..617852806 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ members = [ # Workspace Package Metadata (inherited by all crates) # ───────────────────────────────────────────────────────────────────────────── [workspace.package] -version = "0.6.43" +version = "0.6.44" edition = "2024" # No `rust-version` claim: the workspace is structurally nightly-only. # `crates/uffs-polars` enables `polars/nightly` unconditionally, which @@ -135,36 +135,36 @@ 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.43" } -uffs-security = { path = "crates/uffs-security", version = "0.6.43" } -uffs-text = { path = "crates/uffs-text", version = "0.6.43" } -uffs-time = { path = "crates/uffs-time", version = "0.6.43" } -uffs-version = { path = "crates/uffs-version", version = "0.6.43" } -uffs-statusfmt = { path = "crates/uffs-statusfmt", version = "0.6.43" } -uffs-mft = { path = "crates/uffs-mft", version = "0.6.43" } -uffs-format = { path = "crates/uffs-format", version = "0.6.43" } -uffs-core = { path = "crates/uffs-core", version = "0.6.43" } -uffs-client = { path = "crates/uffs-client", version = "0.6.43" } +uffs-polars = { path = "crates/uffs-polars", version = "0.6.44" } +uffs-security = { path = "crates/uffs-security", version = "0.6.44" } +uffs-text = { path = "crates/uffs-text", version = "0.6.44" } +uffs-time = { path = "crates/uffs-time", version = "0.6.44" } +uffs-version = { path = "crates/uffs-version", version = "0.6.44" } +uffs-statusfmt = { path = "crates/uffs-statusfmt", version = "0.6.44" } +uffs-mft = { path = "crates/uffs-mft", version = "0.6.44" } +uffs-format = { path = "crates/uffs-format", version = "0.6.44" } +uffs-core = { path = "crates/uffs-core", version = "0.6.44" } +uffs-client = { path = "crates/uffs-client", version = "0.6.44" } # `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 # Layer-0 lib — cross-platform tests run on every CI lane. Added in # F5 (issue #205) so neither side duplicates `BROKER_PIPE_NAME` / # wire-format byte literals. -uffs-broker-protocol = { path = "crates/uffs-broker-protocol", version = "0.6.43" } +uffs-broker-protocol = { path = "crates/uffs-broker-protocol", version = "0.6.44" } # `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.43" } +uffs-winsvc = { path = "crates/uffs-winsvc", version = "0.6.44" } # `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.43" } +uffs-fetch = { path = "crates/uffs-fetch", version = "0.6.44" } # 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 diff --git a/crates/uffs-cli/Cargo.toml b/crates/uffs-cli/Cargo.toml index babad84e3..0e458b314 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.43", default-features = false } +uffs-client = { path = "../uffs-client", version = "0.6.44", 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.43", 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.43" } +uffs-format = { path = "../uffs-format", version = "0.6.44" } # Typed drive-letter newtype. Direct dep so the CLI command signatures # (`daemon_load`, `daemon_tiering`, etc.) name `DriveLetter` natively