Commit and release workflow
This is the canonical checklist for Shirowwww/Achievement-Watcher-Next. Other
project documents link here instead of duplicating the release process.
On this page
- Commit rules
- Release preparation
- Documentation accuracy check
- Occasional: re-verifying the comparison table against competing projects
- Clean validation
- Build and artifact checks
- Linux AppImage
- Push, CI and GitHub release
- Identifiers that must not change
- The repository rename
- Auto-update proof
- Validation log
- Final handoff
Commit rules
- Start with
git status --short --branchand preserve unrelated changes. - Split work by concern. Code, tests and the matching
CHANGELOG.mdentry may stay together; unrelated docs, dependency refreshes and CI fixes get separate commits. - Stage explicit paths, inspect
git diff --cached, then commit. - Use short English Conventional Commit subjects, for example:
fix: sync dependency lockfilefeat: add controller UI navigationdocs: clarify notification setupchore: update runtime and dependencies
- Do not add generated-by or unrelated co-authoring attribution to commits or public-facing text.
- Never rewrite already-pushed history unless the user explicitly requests it.
Release preparation
Use a new SemVer version. A published version is immutable: never replace its installer to force an update, because clients only update to a higher version.
Update the same version in all of these places:
app/package.jsonapp/package-lock.json(root package entries)watchdog/package.jsonwatchdog/package-lock.json(root package entries)CHANGELOG.md: move relevantUnreleasedentries under the dated versionRELEASE_NOTES.md: title, highlights and installer filename
The README release badge is dynamic and does not require a manual version edit.
Do not hand-edit generated app/dist/latest.yml or latest-linux.yml; the builds create them.
A release is one version for both systems: the Windows installer and the Linux AppImage are built from the same commit and published together.
Regenerate the styled site changelog and release discovery files after dating the changelog entry:
node tools/site/changelog.js
node tools/site/discovery.js
Commit docs/changelog.md, docs/releases.xml and docs/sitemap.xml with the release changes. The
site workflow rejects them when they drift from CHANGELOG.md or the public guide inventory.
Documentation accuracy check
Do this for every release, not only ones that “feel” doc-worthy - stale docs accumulate silently between releases otherwise:
README.md“What it does” list: add a phrase for any genuinely new capability this release ships (a new source, a new tool, a reliability/data-safety change). Bug fixes belong inCHANGELOG.md, not this list. If nothing list-worthy shipped, leave it alone.docs/comparison.md: only touch a row if this release changed the behavior it describes. Do not re-verify the whole table on every release - that is a separate, occasional audit (see below), not a per-release step.- Stale absolute paths: if this release changed a user-data path, filename or directory (the
3.5.3
%APPDATA%\Achievement Watcher 3.0split from the legacy%APPDATA%\Achievement Watcheris the precedent), grep the whole repo for the old form - public docs,.github/ISSUE_TEMPLATE/, and the localCLAUDE.md/AGENTS.mdnotes all drift independently and have shipped stale paths before. - Broken doc links: confirm every
[text](path#anchor)added or touched this release actually resolves (a moved/renamed file or a renamed heading breaks the anchor silently). A quick script that resolves every relative link in every tracked.mdfile against the filesystem is cheap insurance - see git history around 2026-08-05 for one.
Occasional: re-verifying the comparison table against competing projects
Do this only when asked, or when a competing project’s fork clearly changed - not every release.
When you do it: check the competitor’s actual source (main.js/preload.js/renderer HTML,
fetched via gh api repos/<owner>/<repo>/contents/<path> -H "Accept: application/vnd.github.raw"),
not just their README. A README is a marketing summary and routinely under-describes real
features. A missing README mention is not evidence of a missing feature - grep the code before
downgrading a ✅ to a ❌.
Clean validation
From the repository root in PowerShell:
git diff --check
Push-Location app
npm ci
npm test
node ../tools/audit.js
Pop-Location
Push-Location watchdog
npm ci
npm test
node ../tools/audit.js
Pop-Location
tools/audit.js is npm audit --omit=dev --audit-level=high with a short list of advisories
accepted by id, each with its reason; an accepted one blocks again once its package has a fix.
If npm ci skipped install scripts, npm run build stops on a missing registry-js addon
(3.12.0 shipped without it and scanned libraries through reg.exe). Run
npm rebuild registry-js --foreground-scripts in app/ and build again.
The app suite includes locale completeness. If a native optional dependency is unavailable on the current machine, record the exact limitation; do not silently skip a failed check.
Before release, confirm the four package and lockfile root versions match:
node -e "for (const p of ['app/package.json','app/package-lock.json','watchdog/package.json','watchdog/package-lock.json']) console.log(p, require('./' + p).version)"
Build and artifact checks
Build on Windows from app/:
Push-Location app
npm run build
Pop-Location
Expected files:
app/dist/Achievement.Watcher.Setup.<version>.exeapp/dist/Achievement.Watcher.Setup.<version>.exe.blockmapapp/dist/Achievement.Watcher.Portable.<version>.zipapp/dist/latest.yml
Check that latest.yml names the exact installer and version. Verify its SHA-512
against the built installer:
$version = (Get-Content app/package.json | ConvertFrom-Json).version
$installer = "app/dist/Achievement.Watcher.Setup.$version.exe"
$expected = [Convert]::ToBase64String(
[Security.Cryptography.SHA512]::HashData([IO.File]::ReadAllBytes($installer))
)
Get-Content app/dist/latest.yml
$expected
The documentation points readers at a VirusTotal report for the current installer, keyed on its SHA-256. Print the new one and update every place that names it, so the link does not keep pointing at the previous release:
Get-FileHash "app/dist/Achievement.Watcher.Setup.$version.exe" -Algorithm SHA256
Upload the installer to VirusTotal once so the report exists: looking a hash up never creates one,
so a link to a file nobody submitted is a dead link. Then replace the previous hash everywhere it
appears. It is in README.md, docs/faq.md, docs/troubleshooting.md, docs/index.html and the
nine docs/assets/i18n/*.json files, which repeat the site sentence in each language. A plain search
for the old hash across the repository finds them all; the version number beside it only changes in
the first three.
Also smoke-test the packaged runtime and the affected feature path. For runtime inspection, temporarily use Electron as Node and always remove the variable:
$env:ELECTRON_RUN_AS_NODE = '1'
& 'app/dist/win-unpacked/Achievement Watcher.exe' -e "console.log(process.versions)"
Remove-Item Env:ELECTRON_RUN_AS_NODE
The build runs npm prune --omit=dev in watchdog/. Restore its development
dependencies afterward with npm install from watchdog/.
Linux AppImage
The AppImage is built on Linux, from the same commit as the installer. Either run the manual
Linux build workflow (.github/workflows/build-linux.yml) on that commit and download its
linux-appimage artifact, or build on a Linux machine:
(cd watchdog && npm ci --ignore-scripts)
(cd app && npm ci --ignore-scripts && npx patch-package && npm run build:linux)
Expected files:
app/dist/Achievement.Watcher.<version>.AppImageapp/dist/latest-linux.yml
build/build-linux.js fails the build unless the AppImage is executable, its embedded
app-update.yml points at Shirowwww/Achievement-Watcher-Next, and latest-linux.yml carries this
version and the AppImage’s SHA-512. Then start the AppImage once on Linux and check the affected
feature path. It needs FUSE 2 (libfuse2, libfuse2t64 on Ubuntu 24.04).
The AppImage is not code-signed: an update is trusted through HTTPS and the SHA-512 in
latest-linux.yml.
Push, CI and GitHub release
- Ensure commits are atomic and the worktree contains no accidental files.
- Push
maintooriginand wait for.github/workflows/test.ymlto pass, the Linux job included. - Create the GitHub release only after CI succeeds, uploading the Windows updater assets, the
portable archive, and the AppImage with
latest-linux.yml:
$version = (Get-Content app/package.json | ConvertFrom-Json).version
$target = git rev-parse HEAD
gh release create "v$version" `
"app/dist/Achievement.Watcher.Setup.$version.exe" `
"app/dist/Achievement.Watcher.Setup.$version.exe.blockmap" `
"app/dist/Achievement.Watcher.Portable.$version.zip" `
"app/dist/latest.yml" `
"app/dist/Achievement.Watcher.$version.AppImage" `
"app/dist/latest-linux.yml" `
--repo Shirowwww/Achievement-Watcher-Next `
--target $target `
--title "Achievement Watcher Next $version" `
--notes-file RELEASE_NOTES.md
- Verify the release page exposes the installer, portable ZIP, blockmap,
latest.yml, the AppImage andlatest-linux.yml, and that both public manifests are downloadable. Once an AppImage has been published, every later release must carrylatest-linux.yml: without it, each installed AppImage reports an error on every update check. - Refresh the version the website prints under its download buttons, and commit it:
node tools/site/release-data.js
git add docs/data/release.json
git commit -m "docs: publish <version> on the site"
The file is read from the site’s own origin rather than from the GitHub API, so a visitor never spends an unauthenticated rate limit to see a version number. Missing or stale, the pages fall back to their own wording; they never show a wrong version.
Identifiers that must not change
The product was renamed to Achievement Watcher Next in 3.9.0, but several identifiers deliberately
kept their historical value because an existing install is keyed on them. Changing any of these
breaks upgrades rather than merely renaming something, and test/core/branding.test.js pins them:
| Identifier | Value | Why it stays |
|---|---|---|
appId (AppUserModelID) |
io.github.shirowwww.achievement.watcher |
Windows matches toasts and taskbar pins against it; it also names the autostart registry value, which the uninstaller removes by that name |
executableName |
Achievement Watcher |
Fixes the .exe name, install directory and uninstaller filename; the autostart registry value stores that full path, and the Watchdog spawns the app by it |
app.setName() |
Achievement Watcher |
Names the main log file |
| Installer artifact | Achievement.Watcher.Setup.<version>.exe |
Referenced by latest.yml; published releases are immutable |
updaterCacheDirName |
achievement-watcher-updater |
Existing partially downloaded updates live there |
Linux executableName |
achievement-watcher |
The Watchdog starts the app by that name on Linux |
| AppImage artifact | Achievement.Watcher.<version>.AppImage |
Referenced by latest-linux.yml and named by the site |
| Legacy data folders | Achievement Watcher 3.0, Achievement Watcher |
Import sources for the one-way migration; never renamed or deleted |
The repository rename
The repository was renamed Achievement-Watcher-3.0 → Achievement-Watcher-Next for 3.9.0. Clients
released before 3.9.0 shipped an app-update.yml naming the old repository, so they reach new
releases only through GitHub’s permanent rename redirect. That redirect was verified end to end
against electron-updater’s own HTTP stack - the releases.atom feed, the latest.yml channel file
and the installer asset all follow the 301 transparently.
Never create a new repository at Shirowwww/Achievement-Watcher-3.0. Doing so takes over the old
path, kills the redirect and permanently strands every client older than 3.9.0. Releases must also
stay on this repository: moving them to a different owner or repo breaks the same path.
Auto-update proof
An updater check is only meaningful from an installed lower version to the newly published higher version.
- Keep the previous stable installer available and install/run that version.
- Publish the new higher version and its matching
latest.yml, installer and blockmap. - Launch the previous installed version normally (not
npm start). - Confirm logs show the GitHub feed check, the new version download and the
restart prompt. electron-updater’s own log names a differential download
(“Download block maps”); a line “Cannot download differentially, fallback
to full download” is not a failure, but note it.
[updater] update signer acceptedmust appear before the install: an unsigned or unpinned installer is refused (see INSTALLER_AND_UPDATES.md). - Accept restart, then confirm the running app reports the new version.
On Linux the same proof starts from the previous AppImage, run as a file the user owns. After
“Update now”, electron-updater replaces it with Achievement.Watcher.<version>.AppImage and
restarts it; the log then reports the new version, and the autostart entry, when enabled, points at
the new file. There is no signer line on Linux: the SHA-512 in latest-linux.yml is the check.
Do not claim auto-update success from source mode, an unpacked build, or a same-version asset replacement.
Validation log
- 3.5.2 (2026-08-04): maintainer confirmed the installed-app auto-update from 3.5.1 to 3.5.2 (feed check, download, restart prompt and reported version).
Final handoff
- Confirm
git status --short --branchis clean and synchronized. - Report tests, build output, CI result, release URL and updater result separately.
- If the user asked to relaunch the application, relaunch the installed build only after validation is complete.