Releasing
Contributor11 min readUpdated
What ships, setting the version, the release steps, patch releases and release candidates.
On this page
Releases are built on a maintainer's machine, not in CI: the data addons are
generated from the game client's tables and local sources that are not in the
repository. CI (.github/workflows/ci.yml) runs the test suite on every push
and pull request against a fresh clone, where the tests that need built data
skip with a reason.
What ships#
node tools/polestar.js pack writes three files to dist/:
| File | Contents |
|---|---|
Polestar-<version>.zip | Polestar (with LICENSE and a player README.txt), Polestar_Guides, always Polestar_Nav (Polestar's walking router, started from Shortest Path Forever by Cillian Berragan, and Polestar's walking maps with PACK.json, its own GPL-3.0-or-later LICENSE and CREDITS.md; nav check must pass), Polestar_QuestDB / Polestar_ItemDB when built, and always Polestar_Codex (with its LICENSE-DATA.txt) |
Polestar_Recorder-<version>.zip | Polestar_Recorder (optional, with LICENSE) |
SHA256SUMS | checksums of both zips, in the format sha256sum -c reads |
Addon folders sit at the top level of each zip, so a player (or an addon
manager) extracts straight into Interface/AddOns/. The zips are
reproducible: the same sources give byte-identical archives.
Community addons are packed by their authors with polestar pack in the packAn addon of guides for Polestar, built by the compiler from a folder with a pack.json (the folder is a pack too); Polestar's own is Polestar_Guides (pack id guides).
folder (<pack>/dist/<folder>-<version>.zip, version from its pack.json);
see docs/PACKS.md.
Version#
The version lives in package.json, in guides/pack.json (the manifest of
our guides' pack: compile writes its version into Polestar_Guides.toc)
and in the ## Version line of every addon TOC. One command sets them all:
node tools/polestar.js version # print it; exit 1 if guides/pack.json or a TOC disagrees
node tools/polestar.js version 0.2.0 # set package.json, guides/pack.json and every addon TOCData addons rebuilt later take the version from package.json. Versions
follow x.y.z; 0.x releases are betas.
What a release holds#
Feature releases are built from the full local data build of the steps below.
THIRD_PARTY.md ("Distributing Polestar") lists what
that puts in the zip: the community quest database facts in Polestar_Guides, Polestar_QuestDB
and Polestar_Codex, nothing from the loot-table addon, the rares database or DungeonJournal,
and never the zone crossings pack (pack leaves it out). The CodexLookup's database of items, mounts, pets, recipes, NPCs, rares, quests, zones, dungeons and treasures, with where each comes from (codex build, the Polestar_Codex addon). is part of
Polestar (Lookup, Dungeons and Rares read it): pack stops when
Polestar_Codex is not built, or was built with --local-extras, or names
one of those three among its sources.
Two kinds of release#
| Release | Version | Carries | Built from | Steps |
|---|---|---|---|---|
| Feature | 0.x.0 | everything on next: the addon, the tools, the data addons, guides | a full data build | Steps |
| Patch | 0.x.y | guideOne route, written as a .guide file and compiled to Lua; its id reads like forever/alliance/human/01-elwynn-forest. fixes for the latest release, nothing else | the last release's data addons, the guides recompiled | Patch releases |
A feature release may go out first as release candidates, 0.3.0-rc.1,
-rc.2 and so on, for testers: see Release candidates.
CONTRIBUTING.md ("Branches") tells contributors which branch a pull
request goes to.
After merging contributor PRs#
Contributors edit source files and run only the tests for what they changed.
They do not compile Guides.lua, regenerate the UI previews, sync locale
files or rebuild the textures. After merging a PR that touches any of those
sources, run:
node tools/polestar.js regenThis runs compile + i18n sync + preview + media in order. Pass
--no-<step> to skip one you know is unaffected. compile needs the
reference dataThe game facts in data/forever/*.json (quests, NPCs, objects, items, zones, map frames and more) that the compiler and the data builds read; built locally by data build. (data/forever); the other three do not.
Run regen once per merge or batch, then push the regenerated files on
next. You do not need to run it before every commit, only before pushing
a batch to next or starting the release steps below.
Steps#
These are the steps of a feature release. Replace 0.2.0 with the new
version throughout.
-
Start from a clean, up-to-date
nextwith the game client installed (the data build reads its tables and installed addons). Work and pull requests for the coming release collect onnext(the default branch, pushed as it goes);mainholds the latest release and moves only in stepOne stop in a guide: a title, optional conditions and tip, and at least one goal. 7. Releases come in batches, when the maintainer decides. If ahotfix/branch holds fixes that were never released, merge it intonextfirst, then delete it. -
Set the version and rebuild the data, the data addons and the guides:
shell node tools/polestar.js version 0.2.0 node tools/polestar.js data build node tools/polestar.js questdb build node tools/polestar.js itemdb build && node tools/polestar.js gear build node tools/polestar.js codex build # release-safe; never --local-extras here node tools/polestar.js lint node tools/polestar.js compile -
Run the whole suite with the data built (nothing skips for missing data now):
shell node --test "tests/**/*.test.js" -
Pack and check the archives:
shell rm -rf dist && node tools/polestar.js pack (cd dist && sha256sum -c SHA256SUMS) unzip -l dist/Polestar-0.2.0.zip # Polestar_Codex/ with its LICENSE-DATA.txt, and Polestar_Nav/ with its LICENSE and CREDITS.md, are listed node tools/polestar.js nav check # Polestar_Nav's walking maps match its PACK.json -
Smoke-test the zips as a player gets them: unpack both into an empty AddOns folder and load the addons from there under Lua 5.1, with the stub client the tests use (
POLESTAR_ADDON_DIRpoints the test loader at another addon folder):shell smoke=$(mktemp -d) unzip -q dist/Polestar-0.2.0.zip -d "$smoke/AddOns" unzip -q dist/Polestar_Recorder-0.2.0.zip -d "$smoke/AddOns" POLESTAR_ADDON_DIR="$smoke/AddOns" node --test tests/release.test.js rm -rf "$smoke"The test loads every addon in the folder in client order, logs in, and checks that the guides registered, a guide opened, the data addons loaded and every TOC carries the release version.
-
Then install the zip into the game itself (remove the old
Polestar*folders first), restart it fully and test what the release changes: play the first steps of a guide, and search, Dungeons and Rares when the Codex changed. This is a gate: nothing is tagged, pushed or uploaded (steps 7 to 10) until the maintainer has played the release on their own client and said it is good. If the servers are down, the release waits.When the gamepad support changed (
UI/Gamepad.lua,UI/GamepadGuide.lua,Bindings.xml), play it with a gamepad too:/console taintLog 1, then with "Browse the guide with the gamepad" on and again with the options open, walk into a fight and out of it (the windows give the gamepad back as it starts and take it again after), bind a button and a chord on the Key bindings tab, use the quest item from focus mode, and/reload. Then/console taintLog 0:Logs/taint.lognames no Polestar file and the chat shows noADDON_ACTION_BLOCKEDorADDON_ACTION_FORBIDDEN.Then once more with the game's gamepad interface on (Options, Gamepad, Enable Gamepad UI), with
taintLogat 0 (on this client it breaks every addon'shooksecurefuncpost-hooks, so a logged pass shows Polestar misbehaving where nothing is wrong):/polestaris unknown, the minimap button shows; open the options from it, move through them and close them with B, browse the guide, and let a route choice or a new character's welcome show by itself: it takes no button until the Browse the guide button is pressed. Open and close the world map, a quest giver's window, a vendor, the flight map and the talents with the gamepad. LB and RB + the D-pad keep targeting while no Polestar window holds the gamepad, and no "AddOn blocked" warning shows. -
Assemble the changelog from the fragments in
changes/(CONTRIBUTING.md, "Commits and pull requests"), then commit it with the version bump and the regenerated files that are tracked (git statuslists them;Polestar_QuestDBand the other local packs are ignored) onnext, bringmainup to it, tag and push both. The maintainer's pushes pass the branch rules (see Branch rules):shell node tools/polestar.js changelog build 0.2.0 # writes "## 0.2.0 (<today>)", deletes the fragments git add CHANGELOG.md changes package.json guides/pack.json addon/Polestar addon/Polestar_Guides addon/Polestar_ItemDB addon/Polestar_Nav addon/Polestar_Recorder git commit -m "Release: 0.2.0" git switch main && git merge --ff-only next && git switch next git tag -a v0.2.0 -m "Polestar 0.2.0" main git push origin main next v0.2.0 -
Write the release notes.
release notesdrafts all three from the version's CHANGELOG.md section intodist/(not committed), each paragraph and list item joined onto one line, since GitHub shows every line break of a release's notes:shell node tools/polestar.js release notes 0.2.0It writes
notes-0.2.0.md(GitHub),notes-curseforge-0.2.0.mdandnotes-discord-0.2.0.txt(posts of at most 2,000 characters). Each starts with a[[REPLACE ...]]line: write the opening paragraph there. The drafts carry every player and author entry word for word; cut them down to what changed for players first (guides, routes, features, fixes), then for guide authors, and add the special thanks by hand. Hours to 60 are the simulator's model estimates and are written as such, with the world they come from. -
Publish the release with the three files from step 4:
shell gh release create v0.2.0 --verify-tag --title "Polestar 0.2.0" \ --notes-file dist/notes-0.2.0.md --latest \ dist/Polestar-0.2.0.zip dist/Polestar_Recorder-0.2.0.zip dist/SHA256SUMSA release is a full release, marked Latest, from 0.2.1 on, so the repository's Releases box and
releases/latestpoint at it; the 0.x version and the notes say it is a beta. -
Upload
dist/Polestar-0.2.0.zipto CurseForge (Forever 1.60.1, release type Release: CurseForge's app lists a project only once it has one) and refresh the project description: the steps and settings are in docs/curseforge/README.md. -
Cut the next patch's branch from the new tag and push it, so backported guide fixes for this release have a base:
shell git switch -c hotfix/0.2.1 v0.2.0 && git push -u origin hotfix/0.2.1 && git switch next
Patch releases#
A patch ships guide fixes on top of the latest release without the full
data build. Its branch, hotfix/<version>, is cut from the release's tag
(step 11 above). Contributors never open pull requests there: every fix
goes to next, and the ones the patch should carry arrive as backport
pull requests (below). The Base branch check refuses a pull request into
a hotfix/ branch from anyone but the backport workflow and the
maintainers, and one that changes anything a patch may not carry. The
example is 0.2.5 after 0.2.4.
The patch is built with the release's own tools, which the branch has, and
run by release patch from a next checkout, which has the reference data
and the release tooling. Work in a worktree of the branch:
-
Check out the branch beside this one and run the patch build:
shell git fetch origin git worktree add ../polestar-0.2.5 origin/hotfix/0.2.5 -B hotfix/0.2.5 node tools/polestar.js release patch 0.2.5 --root ../polestar-0.2.5In the worktree it checks that the branch holds only what a patch may carry (
release scope: the guides, the compiled guides, their tests, the data corrections, the changelog and its fragments), puts back the data addons the last release shipped (from its GitHub release, or--zip <file>), sets the version, lints and compiles with that checkout's tools and this checkout'sdata/forever, then lists the compiled guides that differ from the release. It stops when:- the branch has a path a patch may not carry: that change belongs on
nextand in a feature release; tools/lib/data/corrections.jsonchanged: runnode tools/polestar.js data buildin the worktree, then again with--data-built;- a compiled guide changed though its source did not: this checkout's
data/foreverwas built from newer data than the release's; the same fix,data buildin the worktree and--data-built.
When the fixes bring a quest, NPC or object id the release did not have, rerun
codex buildin the worktree (the Codex reads our guides) and, once the release shipsPolestar_Names_<locale>,i18n names. - the branch has a path a patch may not carry: that change belongs on
-
In the worktree, run steps 3 to 6 above: the suite, pack, the zip smoke test and the in-game gate. In the game, play the steps the fixes changed. The suite reads the worktree's own
data/, which a new worktree lacks; unless you randata buildthere, link this checkout's for it (ln -s "$PWD/data/forever" ../polestar-0.2.5/data/foreverfrom thenextcheckout), so the tests that need data run instead of skipping. -
Assemble the patch's changelog, commit, bring
mainup to the branch, tag and push:shell cd ../polestar-0.2.5 node ../polestar/tools/polestar.js changelog build 0.2.5 --root . git add CHANGELOG.md changes package.json guides/pack.json addon/Polestar addon/Polestar_Guides addon/Polestar_ItemDB addon/Polestar_Nav addon/Polestar_Recorder git commit -m "Release: 0.2.5" git push origin hotfix/0.2.5 git fetch origin main && git push origin hotfix/0.2.5:main git tag -a v0.2.5 -m "Polestar 0.2.5" hotfix/0.2.5 && git push origin v0.2.5(
../polestarstands for thenextcheckout.) Pushing the branch tomainis a fast-forward: the branch was cut from the releasemainholds. -
Check that the issues the patch fixes are closed before publishing: their fixes merged into
nextwith "closes #N", so GitHub closed them then. Close any that are still open, as completed; the release workflow tells the Discord threads of issues closed by the time the release is published. -
Steps 8 to 10 above, with
release notes 0.2.5 --root .: the notes, the GitHub release, CurseForge. -
Publishing the release opens a pull request merging
mainintonext(.github/workflows/merge-back.yml). Merge it with a merge commit, never a squash. It can stop onaddon/Polestar_Guides/Guides.lua, which both branches regenerate: resolve it locally, takingnext's side, then runnode tools/polestar.js compileonnextand commit the result with the merge. The version files take0.2.5; the next feature release sets its own in step 2. Pull requests opened by a workflow start no checks: close and reopen it to run them.A release runs the workflows of its tagged commit. Patches cut from a release older than these workflows (every 0.2.x patch) run without them: run "Merge back" and "Discord release notes" by hand, from
next, with the patch's tag (Actions, the workflow, Run workflow). -
Remove the worktree, delete
hotfix/0.2.5on GitHub and cuthotfix/0.2.6fromv0.2.5, as in step 11.
Fixes merged into next that a patch should carry#
This is how a patch gets its fixes. Contributors open every guide fix
against next; one that belongs in the coming patch gets the backport
label, before or after the merge. .github/workflows/backport.yml then
opens a pull request cherry-picking it onto the one open hotfix/ branch
(it comments instead when there is none, or several). That pull request
goes through the same review and Base branch check as any other; close
and reopen it to start its checks. A fix that needs anything next has
and the release lacks is not a patch fix: leave the label off.
Branch rules#
GitHub's rulesets for the repository, under Settings, Rules:
| Branch | Rules |
|---|---|
next | changes by pull request only, with Tests and Base branch passing; no force push, no deletion |
hotfix/** | changes by pull request only, with Tests and Base branch passing; only maintainers create one; no force push |
main | only maintainers update it; no force push, no deletion |
Repository admins bypass all three, which the release steps rely on (the pushes of steps 7 and 11, and of patch step 3). Branches are deleted when their pull request merges.
Release candidates#
A feature release with a large change (a new engine, a new data build) can
go to testers first. Build it as in Steps with the version
0.3.0-rc.1 and do steps 1 to 6, then:
- leave the fragments in
changes/: only the final release runschangelog build.node tools/polestar.js changelog showprints what the candidate carries, for its notes; - tag
v0.3.0-rc.1onnext;maindoes not move for a candidate; - publish with
gh release create v0.3.0-rc.1 --verify-tag --prerelease(never--latest), soreleases/lateststays on the last release. The Discord workflow then pings the tester role in the testers channel instead of telling bug threads their fix is out (tools/discord-bot/README.md); - upload to CurseForge as release type Beta, which only players who opt in get.
Fixes go to next and out as -rc.2. When a candidate brings no new bugs,
set the version to 0.3.0 and release it with the full steps.