The GitHub Actions Cache mechanism has been removed from vcpkg, so you can't use x-gha
in VCPKG_BINARY_SOURCES
as a way to re-use built products from one build to the next anymore. I was using x-gha
because the setup was
trivial for all platforms: just set a couple environment variables.
You can use NuGet packages to achieve the same purpose, although the setup is a little more complicated. Here's what I learned while setting this up for the first time for use with GitHub Actions.
In order for NuGet to interact with the packages associated with your github account, you will need a
way to authenticate NuGet with GitHub. Do this by creating a classic public access token
as described in the GitHub documentation with token name GH_PACKAGES_TOKEN
. The actual name isn't
important, so long as you use the same name in your workflow and repository. The token needs packages:write
permission in order to cache dependencies.
When you create the token, you will only be able to see it's value once at creation time. Keep the token value in a secure location and treat it like a password. After the token is created you will not be able to view it's value again, you will only be able to create a new value.
Each repository that is going to use NuGet packages for caching needs to be configued with a secret to provide the access token
to workflows. See Using secrets in GitHub Actions
in the GitHub documentation. Configure a secret named GH_PACKAGES_TOKEN
and paste in the value of the access token
you generated.
NuGet is a .NET application, so you need to be able to run .NET applications on the github runner. Even though the runner image documentation says that mono is installed by default on ubuntulatest, it gave me 'command not found' when I attempted to run mono.
On windows-latest
, nuget was already installed via Chocolatey.
On ubuntu-latest
, nuget was not installed. Vcpkg can fetch nuget with the command vcpkg fetch nuget
.
I use vcpkg as a submodule, so on initial checkout the vcpkg executable isn't available yet. It gets downloaded
during a bootstrap phase in my CMake build process. However, we need to invoke nuget first to configure package
locations before we invoke vcpkg via cmake. On ubuntu-latest
we can run the bootstrap script to get
vcpkg downloaded, so our prerequisite workflow step is as follows:
- name: Install Prerequisites Linux
if: ${{matrix.os == 'ubuntu-latest'}}
shell: bash
run: |
${{github.workspace}}/vcpkg/bootstrap-vcpkg.sh
sudo apt install mono-complete
I'm using ${{github.workspace}}/vcpkg
to locate the bootstrap shell script as I've got vcpkg checked out as a
submodule in the root of my project.
Unlike GitHub Actions Cache, which shows cached items in the Actions tab for your repository, NuGet packages are shown in the Packages tab for your account. To configure NuGet we need to add a package source and then configure the api key for that source. This is done by invoking nuget twice:
env:
FEED_URL: https://nuget.pkg.github.com/${{github.repository_owner}}/index.json
# ...
steps:
- name: Add NuGet sources Linux
if: ${{matrix.os == 'ubuntu-latest'}}
shell: bash
run: |
mono `${{github.workspace}}/vcpkg/vcpkg fetch nuget | tail -n 1` \
sources add \
-Source "${{env.FEED_URL}}" \
-StorePasswordInClearText \
-Name GitHubPackages \
-UserName "${{github.repository_owner}}" \
-Password "${{secrets.GH_PACKAGES_TOKEN}}"
mono `./vcpkg/vcpkg fetch nuget | tail -n 1` \
setapikey "${{secrets.GH_PACKAGES_TOKEN}}" \
-Source "${{env.FEED_URL}}"
- name: Add NuGet sources Windows
if: ${{matrix.os == 'ubuntu-latest'}}
shell: bash
run: |
nuget \
sources add \
-Source "${{env.FEED_URL}}" \
-StorePasswordInClearText \
-Name GitHubPackages \
-UserName "${{github.repository_owner}}" \
-Password "${{secrets.GH_PACKAGES_TOKEN}}"
nuget \
setapikey "${{secrets.GH_PACKAGES_TOKEN}}" \
-Source "${{env.FEED_URL}}"
When we use vcpkg to fetch nuget, the last line of the output from vcpkg is the local path where nuget was stored; hence the unusual shell trickery to invoke nuget:
mono `vcpkg fetch nuget | tail -1`
I'm using ${{github.repository_owner}}
to identify the user associated with the package cache to NuGet
and the GH_PACKAGES_TOKEN
secret mentioned above.
The above steps are identical, except for how they invoke nuget. (Did you realize you could
run steps on the windows-latest
runner using the bash
shell? I didn't until I worked this out.)
We can factor out this duplication by using a matrix strategy.
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
# Disabled until <curses.h> can be safely included without conflict in terminal.h
# os: [ ubuntu-latest, windows-latest, macos-latest ]
include:
- os: ubuntu-latest
nuget: mono `./vcpkg/vcpkg fetch nuget | tail -n 1`
- os: windows-latest
nuget: nuget
runs-on: ${{matrix.os}}
env:
FEED_URL: https://nuget.pkg.github.com/${{github.repository_owner}}/index.json
steps:
# ...
- name: Add NuGet sources
shell: bash
run: |
${{matrix.nuget}} \
sources add \
-Source "${{env.FEED_URL}}" \
-StorePasswordInClearText \
-Name GitHubPackages \
-UserName "${{github.repository_owner}}" \
-Password "${{secrets.GH_PACKAGES_TOKEN}}"
${{matrix.nuget}} \
setapikey "${{secrets.GH_PACKAGES_TOKEN}}" \
-Source "${{env.FEED_URL}}"
In the build matrix, I've set different values for the nuget parameter to the command
used to invoke nuget in the two environments. Note that I replaced ${{github.workspace}}
with .
for ubuntu because the matrix parameters are created before I've done a checkout
step and the checkout step is what sets github.workspace
. This is potentially fragile,
but the steps are always run in the checkout directory, so I don't think this is really a
problem.
Once NuGet has been configured and authenticated with GitHub, we can tell vcpkg to use the nuget binary caching method:
- name: CMake workflow
env:
VCPKG_BINARY_SOURCES: "clear;nuget,${{env.FEED_URL}},readwrite"
run: cmake --workflow --preset default
Setting the VCPKG_BINARY_SOURCES
environment variable configures vcpkg to use the NuGet feed that is authenticated against
our github account.
If everything has been configured correctly, your workflow runs should start publishing private packages to your github account that are reused between workflow runs.