Releasing
Pushing a version tag such as v0.2.0 to the StoneForge repository runs the Release workflow. It:
- checks the tag matches
VersioninDirectory.Build.props, - builds and packages StoneForge with
build\Package.ps1, - runs
StoneForge.TestsandStoneForge.Patcher.Tests, - publishes a GitHub release named
StoneForge <version>withStoneForge-<version>.zipattached. The release notes come from that version's section ofCHANGELOG.md.
A version with a suffix, such as 0.2.0-beta.1, is published as a pre-release.
Making a release
- Set
<Version>inDirectory.Build.propsto the new version. - Add a section to
CHANGELOG.mdheaded## <version>, for example## 0.2.0 — Skill trees. Everything under it, up to the next##heading, becomes the release notes. - Merge those changes to
main. Tag the merged commit on
mainand push the tag:git fetch origin git tag v0.2.0 origin/main git push origin v0.2.0The workflow runs from the tagged commit, so a tag on a commit from before the workflow existed starts nothing.
Follow the run under the repository's Actions tab. It stays Queued until the release runner is online; a queued job waits up to 24 hours. If the run fails, fix the problem and use Re-run jobs. If the fix needed a new commit, merge it and move the tag to it:
git fetch origin
git tag -f v0.2.0 origin/main
git push origin :refs/tags/v0.2.0
git push origin v0.2.0
Builds of main: main-latest
Every push to main is also packaged, on the same runner, as a rolling pre-release: main-latest, with StoneForge-main.zip (the same zip a release makes, for main as it is now). It's replaced on every push and isn't a release: players install versioned releases.
It's for mods developed against StoneForge's newest API ("stoneforge": "latest" in their mod.json): their CI can download StoneForge-main.zip and check their pushes compile against its StoneForge.API.dll, which GitHub's own runners can't generate without Stoneshard. The main-latest tag is moved to a commit only once its zip is up, so a mod's CI that finds the tag on main's head knows the zip is that commit's.
The Main build workflow does this; it needs the release runner online, as releases do.
The release runner
StoneForge.API is generated from Stoneshard's own game data, and that data can't be put on GitHub's hosted runners. Releases are therefore built on a self-hosted runner: a Windows PC of yours that GitHub sends the release job to. The job only runs while the runner is online.
What the machine needs
- Stoneshard from Steam, with StoneForge installed, on either branch. On the native branch, build once on the VM modbranch first: the API is generated from the VM build's data, kept in
%LOCALAPPDATA%\StoneForge\GameData(see Building and testing). The integration tests need StoneForge's preserved unpatched data,dotnet\data_base.win, and a release fails if it's missing. They run only against VM data, though: on the native branch they skip, so a runner on the VM modbranch tests more. - Visual Studio with MSBuild and the C++ tools for toolset
v145. - The .NET 10 SDK.
- Git.
These are the same tools as a normal development build; see Building and testing.
Registering the runner
- On GitHub, open the StoneForge repository's Settings → Actions → Runners and choose New self-hosted runner, then Windows and x64.
- Follow the commands GitHub shows to download the runner and run
config.cmd. When it asks:- Runner group: press Enter for Default. A group must already exist, so don't type a new name.
- Runner name: press Enter for the default, or name it.
- Additional labels: type
stoneshard.self-hosted,WindowsandX64are added automatically. The workflow only runs on a runner withself-hosted,windowsandstoneshard. - Run as a service: answer N unless you've read the service note below.
Let PowerShell run the workflow's scripts for your account. This doesn't need an administrator window:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUserStart the runner with
.\run.cmdfrom its folder and keep the window open. When it printsListening for Jobs, it picks up any queued release. After setup, the runner's page under Settings → Actions → Runners shows the labelsself-hosted,Windows,X64andstoneshard.
A runner installed as a service runs as a different Windows account. It needs the machine-wide script policy, which needs an administrator; from a normal PowerShell you can elevate just this command:
Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile -Command Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force'
That account also can't see your user's Steam settings. Unless Stoneshard is in Steam's default folder (C:\Program Files (x86)\Steam), set these as system environment variables, then restart the service:
STONESHARD_DIR: the game folder, for exampleD:\SteamLibrary\steamapps\common\Stoneshard.STONEFORGE_TEST_DATA: the unpatched data, for exampleD:\SteamLibrary\steamapps\common\Stoneshard\dotnet\data_base.win.
Running run.cmd from your own account avoids this.
Keeping the runner safe
A self-hosted runner runs workflow code on your PC. On a public repository, a pull request from a fork can change workflow files, including making them run on your runner.
- In Settings → Actions → General → Fork pull request workflows, require approval for all external contributors. Never approve a run for a pull request that changes
.github/workflows/unless you've read the change. - Only the Release workflow targets the
stoneshardlabel. Keep other workflows on GitHub's hosted runners. - Use the runner machine for building, not for anything that holds secrets you couldn't afford to lose.