Skip to content

ci(vcpkg): Repair the vcpkg CI path and move the binary cache to GitHub Packages - #2542

Open
bobtista wants to merge 6 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/ci/vcpkg-nuget-binary-cache
Open

ci(vcpkg): Repair the vcpkg CI path and move the binary cache to GitHub Packages#2542
bobtista wants to merge 6 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/ci/vcpkg-nuget-binary-cache

Conversation

@bobtista

@bobtista bobtista commented Apr 6, 2026

Copy link
Copy Markdown

The vcpkg builds were failing on a dead download link. ffmpeg needs an MSYS2 runtime package, and MSYS2 deletes old versions once a newer one supersedes them. We were still asking for the deleted one because builtin-baseline had not moved since March 2025, and the baseline pins the vcpkg tree that lists those package versions.

Now the binary cache is a NuGet feed on GitHub Packages keyed by vcpkg's own ABI hashes, so there is no hand-written cache key to invalidate and no save race. Compiler tracking is back on, the baseline is the 2026.07.29 release, both paths are in the filter, and vcpkg-lock.json is removed since nothing reads it and it is not vcpkg's lockfile format. Fork pull requests are set to read, since their token cannot upload.

I warmed the feed with a workflow_dispatch on a temporary branch, so the checks here already restore from it: 10 packages in under 6 seconds and vcpkg builds in 6 to 9 minutes, against 26 cold.

Follow-up: #3184 closes #3175 by making ffmpeg an opt-in vcpkg feature, so default cold builds no longer spend most of their time building a dependency that is not linked.

Todo:

  • Cold and warm workflow_dispatch runs on a fork: cold builds in about 26 minutes, warm restores 10 packages in 3.5s and builds in about 7min
  • Confirm fork pull requests restore from the feed, measured on this PR after warming

@greptile-apps

greptile-apps Bot commented Apr 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the fragile file-based vcpkg binary cache (actions/cache + files backend) with a NuGet feed hosted on GitHub Packages, eliminating the GHA cache-key/ABI-hash synchronization problem that caused full rebuilds whenever the two systems drifted. It also re-enables the three win32-vcpkg* build matrix entries that had been commented out.

  • build-toolchain.yml: Removes cache-key computation, actions/cache/restore, actions/cache/save, and the local cache-directory setup; replaces them with a single NuGet source authentication step (nuget sources add + setapikey using GITHUB_TOKEN) that runs only for vcpkg presets. VCPKG_BINARY_SOURCES is now set to the GitHub Packages NuGet feed at job level.
  • ci.yml: Adds packages: write to the top-level permissions block and uncomments the win32-vcpkg, win32-vcpkg-profile, and win32-vcpkg-debug matrix entries in both the build-generals and build-generalsmd-win32 jobs.

Confidence Score: 5/5

Safe to merge — a well-scoped CI infrastructure change with no application code affected.

The change is a clean swap of one caching backend for another, following the canonical vcpkg + GitHub Packages NuGet pattern. The NuGet authentication step correctly fetches the nuget CLI bundled with vcpkg, gates both the setup and credential steps on contains(inputs.preset, 'vcpkg'), and uses the ephemeral GITHUB_TOKEN (masked in logs). The packages: write permission is correctly added to both the caller (ci.yml) and callee (build-toolchain.yml) workflows, and the previously-commented-out vcpkg matrix entries are re-enabled in lockstep with the new cache infrastructure.

No files require special attention; both workflow files are straightforward and internally consistent.

Important Files Changed

Filename Overview
.github/workflows/build-toolchain.yml Replaces file-based vcpkg binary cache with NuGet/GitHub Packages; removes cache-key computation, restore, and save steps; adds conditional NuGet source authentication; no logical issues found.
.github/workflows/ci.yml Adds packages:write permission and re-enables the three previously-commented-out win32-vcpkg matrix entries in both build-generals and build-generalsmd-win32 jobs.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CI as ci.yml (matrix job)
    participant BT as build-toolchain.yml
    participant vcpkg as vcpkg.exe
    participant NuGet as GitHub Packages NuGet
    participant CMake as CMake Build

    CI->>BT: workflow_call (preset, game, tools, extras)
    BT->>BT: "Set env VCPKG_BINARY_SOURCES=nuget feed"

    alt contains(preset, 'vcpkg')
        BT->>vcpkg: lukka/run-vcpkg (runVcpkgInstall: false)
        vcpkg-->>BT: sets VCPKG_ROOT
        BT->>vcpkg: fetch nuget → returns nuget.exe path
        BT->>NuGet: nuget sources add (GitHubPackages, GITHUB_TOKEN)
        BT->>NuGet: nuget setapikey (GITHUB_TOKEN)
        BT->>BT: Set VCPKG_OVERLAY_TRIPLETS, VCPKG_INSTALL_OPTIONS
    end

    BT->>CMake: cmake --preset (configure)
    CMake->>vcpkg: install dependencies
    vcpkg->>NuGet: check binary cache (ABI hash as package id)
    alt Cache hit
        NuGet-->>vcpkg: restore package (fast)
    else Cache miss
        vcpkg->>vcpkg: build from source
        vcpkg->>NuGet: upload package (push)
    end
    CMake->>CMake: cmake --build
    BT-->>CI: artifacts
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CI as ci.yml (matrix job)
    participant BT as build-toolchain.yml
    participant vcpkg as vcpkg.exe
    participant NuGet as GitHub Packages NuGet
    participant CMake as CMake Build

    CI->>BT: workflow_call (preset, game, tools, extras)
    BT->>BT: "Set env VCPKG_BINARY_SOURCES=nuget feed"

    alt contains(preset, 'vcpkg')
        BT->>vcpkg: lukka/run-vcpkg (runVcpkgInstall: false)
        vcpkg-->>BT: sets VCPKG_ROOT
        BT->>vcpkg: fetch nuget → returns nuget.exe path
        BT->>NuGet: nuget sources add (GitHubPackages, GITHUB_TOKEN)
        BT->>NuGet: nuget setapikey (GITHUB_TOKEN)
        BT->>BT: Set VCPKG_OVERLAY_TRIPLETS, VCPKG_INSTALL_OPTIONS
    end

    BT->>CMake: cmake --preset (configure)
    CMake->>vcpkg: install dependencies
    vcpkg->>NuGet: check binary cache (ABI hash as package id)
    alt Cache hit
        NuGet-->>vcpkg: restore package (fast)
    else Cache miss
        vcpkg->>vcpkg: build from source
        vcpkg->>NuGet: upload package (push)
    end
    CMake->>CMake: cmake --build
    BT-->>CI: artifacts
Loading

Reviews (4): Last reviewed commit: "ci: Re-enable win32 vcpkg presets" | Re-trigger Greptile

@bobtista

bobtista commented Apr 6, 2026

Copy link
Copy Markdown
Author

Greptile Summary

This PR replaces the fragile actions/cache + files vcpkg binary cache with a GitHub Packages NuGet feed, eliminating the GHA cache-key / vcpkg ABI-hash synchronization doom loop described in the PR description. The structural change is sound and fork-tested.

  • build-toolchain.yml: VCPKG_BINARY_SOURCES now points at the NuGet feed; a new step fetches vcpkg's bundled NuGet binary and registers the GitHub Packages source with GITHUB_TOKEN credentials; the old cache-key computation, actions/cache/restore, directory setup, and actions/cache/save steps are all removed.
  • ci.yml: packages: write added so the GITHUB_TOKEN issued to calling jobs has sufficient scope to push packages to GitHub Packages (required because GitHub Actions intersects caller and callee permissions for reusable workflows).
  • Minor: nuget sources add is not idempotent — a step retry or self-hosted runner reuse would cause a "Source already exists" failure. Replacing with an existence check + update fallback would make the step more resilient.

Confidence Score: 4/5

Safe to merge; the NuGet-based caching approach is sound and fork-tested, with only a minor idempotency edge case on step retries.

The structural approach is correct and well-understood (vcpkg + GitHub Packages NuGet is a documented pattern). packages:write is correctly added to both caller and callee workflows. The only concern is nuget sources add failing on retry or self-hosted runners, which is low-probability on ephemeral GitHub-hosted runners. The first post-merge run will be a cold build but subsequent runs should be fast.

.github/workflows/build-toolchain.yml — specifically the NuGet auth step around lines 119-128 for the idempotency concern.

Important Files Changed

Filename Overview
.github/workflows/build-toolchain.yml Replaces files-based vcpkg binary cache with GitHub Packages NuGet feed; adds packages:write permission, NuGet auth step via vcpkg-bundled nuget binary, and removes old cache key/restore/save steps — minor idempotency concern on nuget sources add
.github/workflows/ci.yml Adds packages:write permission to the caller workflow so the GITHUB_TOKEN can write to GitHub Packages when delegated through the reusable build-toolchain.yml

Sequence Diagram

sequenceDiagram
    participant GHA as GitHub Actions
    participant vRun as lukka/run-vcpkg
    participant vcpkg as vcpkg.exe
    participant NuGet as NuGet CLI
    participant GPkg as GitHub Packages

    GHA->>vRun: Setup vcpkg (doNotCache:true)
    vRun-->>GHA: VCPKG_ROOT set
    GHA->>vcpkg: fetch nuget
    vcpkg-->>GHA: path/to/nuget.exe
    GHA->>NuGet: sources add GitHubPackages (GITHUB_TOKEN)
    NuGet-->>GHA: source registered
    GHA->>NuGet: setapikey (GITHUB_TOKEN)
    NuGet-->>GHA: API key stored
    GHA->>vcpkg: cmake --preset (triggers vcpkg install)
    vcpkg->>GPkg: GET package@{abi-hash}
    alt cache hit
        GPkg-->>vcpkg: package restored (e.g. 28.8ms for ffmpeg)
    else cache miss
        GPkg-->>vcpkg: 404 Not Found
        vcpkg->>vcpkg: build from source
        vcpkg->>GPkg: PUT package@{abi-hash}
    end
Loading

Loading
Prompt To Fix All With AI

This is a comment left during a code review.
Path: .github/workflows/build-toolchain.yml
Line: 120-127

Comment:
**`nuget sources add` is not idempotent**

If this step is retried (e.g., via GitHub Actions "Re-run failed jobs") or runs on a self-hosted runner where the `GitHubPackages` source was previously registered, `nuget sources add` will exit with "Source already exists" and fail the step. For ephemeral GitHub-hosted runners this is fine, but wrapping it in an existence check would make the step resilient to retries:

$src = "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
$exists = (& $nuget sources list) -match "GitHubPackages"
if ($exists) {
& $nuget sources update -Name GitHubPackages -Source $src -StorePasswordInClearText
-UserName "${{ github.repository_owner }}" -Password "${{ secrets.GITHUB_TOKEN }}" } else { & $nuget sources add -Name GitHubPackages -Source $src
-StorePasswordInClearText -UserName "${{ github.repository_owner }}"
-Password "${{ secrets.GITHUB_TOKEN }}"
}
& $nuget setapikey "${{ secrets.GITHUB_TOKEN }}" -Source $src


How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "ci(vcpkg): Switch binary cache from loca..." | Re-trigger Greptile

We use ephemeral GitHub-hosted runners, do we need to worry about this? Every job starts with a clean NuGet config

@xezon

xezon commented Jun 26, 2026

Copy link
Copy Markdown

What is now the strategy with vcpkg? We still have it disabled because it caused long CI build times every now and then.

@bobtista

Copy link
Copy Markdown
Author

What is now the strategy with vcpkg? We still have it disabled because it caused long CI build times every now and then.

The strategy is to move the dependency build output into the GitHub Packages NuGet binary cache instead of continuing with handmade caching tweaks trying to use actions/cache as a big shared folder cache. We should have used nuget from the beginning - Microsoft’s vcpkg docs specifically recommend the NuGet provider for this use case.

I tested this on my fork. The first GenCI run still took about 24 minutes for the vcpkg jobs, which I think is expected because it had to build/populate the cache. Then I ran the same workflow again on the same commit, and the vcpkg jobs dropped to about 5.5-8 minutes. The CMake configure step was only around 30-45 seconds on the second run, so ffmpeg was restored from the cache instead of rebuilt.

The workflow is still red on my fork because the replay checks need R2 secrets I don’t have set up, but all six win32-vcpkg build variants passed on both runs.

@bobtista
bobtista force-pushed the bobtista/ci/vcpkg-nuget-binary-cache branch from 68cdb82 to 8e918ef Compare July 19, 2026 13:28
@bobtista

Copy link
Copy Markdown
Author

Rebased onto main and updated the PR, updated the description here too - this is ready for review and merge IMHO

@bobtista bobtista added the Build Anything related to building, compiling label Jul 19, 2026
@bobtista
bobtista force-pushed the bobtista/ci/vcpkg-nuget-binary-cache branch from 8e918ef to 553b91e Compare August 19, 2026 20:02
The baseline had not moved since March 2025. MSYS2 mirrors purge superseded
packages, so the msys2-runtime pinned by that vcpkg tree now 404s on every
mirror and any cold build of ffmpeg fails. The 2026.07.29 tree pins a runtime
that is still published.
Disabling compiler tracking removed the compiler from the ABI hash, so a
runner image update could restore binaries built by a different MSVC. It was
a workaround for hand-written cache keys invalidating on every image update.
The NuGet binary cache addresses that directly, keyed by vcpkg's own ABI hash,
so the workaround is no longer needed and a compiler bump can rebuild once.
A change touching only vcpkg.json or triplets produced no build jobs, so a
baseline or triplet edit could land without ever being compiled.
The file is not vcpkg's lockfile format and nothing reads it. Dependency
versions are resolved from builtin-baseline and overrides in vcpkg.json, so
the checked-in file only suggested a version pinning that was not in effect.
@bobtista
bobtista force-pushed the bobtista/ci/vcpkg-nuget-binary-cache branch from 553b91e to 4a2d650 Compare August 19, 2026 21:10
@bobtista bobtista changed the title ci(vcpkg): Switch binary cache from local files to GitHub Packages NuGet feed ci(vcpkg): Repair the vcpkg CI path and move the binary cache to GitHub Packages Aug 19, 2026
@bobtista
bobtista force-pushed the bobtista/ci/vcpkg-nuget-binary-cache branch from f7fcd60 to 4a2d650 Compare August 20, 2026 14:09
@bobtista

Copy link
Copy Markdown
Author

Found the reason the downloads were failing - a retired link 😆 This can be merged now to turn vcpkg back on, the caching works, and I added a follow up PR that gates ffmpeg so it only gets built when it's being used. That's the majority of the build time on cold builds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build Anything related to building, compiling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Only build ffmpeg when FFmpeg support is enabled

2 participants