Files
Fractured/tools/fractured-launcher-electron

Fractured Launcher (Electron)

Windows launcher with no extra console window, native Browse folder dialog, Gitea or GitHub release assets + GitHub repo file sync, realmlist, optional auth, Play, and auto-update (via electron-updater). This is the only supported client launcher in this repo.

Requirements

Run from source

cd tools/fractured-launcher-electron
npm install
npm start

On first run, launcher.json is created next to the app (dev: in this folder).

Where patches download from

  • Recommended (self-hosted Gitea): set gitea.base_url, gitea.owner, gitea.repo in launcher.json (see default-launcher.json). Players need GITEA_TOKEN (or the env name in gitea.token_env) if the Gitea repo is private — same trade-off as any private host (per-player token, SSO proxy, or a read-only deploy token you accept distributing).
  • Fallback: if gitea.base_url is empty, from_release uses the GitHub Releases API against github.owner / github.repo (defaults to this Fractured repo for non-release paths), with optional GITHUB_TOKEN for private assets.

Build Windows installers

npm install
npm run pack:win

Produces under dist/:

Artifact Purpose
Fractured-Launcher-${version}-Setup.exe (NSIS) Recommended for players — supports seamless auto-update and restart.
Fractured-Launcher-${version}-Windows-Portable.exe No installer; players replace the file manually. Auto-update is less reliable than NSIS.

Auto-update behaviour

  • Packaged builds only (npm run pack:win output). In npm start dev mode, update checks are skipped (button still explains that).
  • No implicit GitHub feed: the app does not guess package.jsonrepository anymore. Without configuration you get a clear “skipped” message instead of a 404 on a private repo.
  • Configured feeds (first match wins): update_feed_url / LAUNCHER_UPDATE_URL (generic latest.yml); or gitea block filled in + GITEA_TOKEN when the instance is private (resolves …/releases/download/{tag}/); or GITHUB_TOKEN + github.owner / github.repo for private GitHub releases only.
  • ~5 seconds after launch, then every 6 hours, the app checks when a feed is configured.
  • When a download finishes, a dialog offers Restart now (calls quitAndInstall) or Later.
  • Manual check: button Check launcher updates in the UI.

Where launcher updates are hosted

npm run publish:win runs electron-builder with --publish never — artifacts stay in dist/; CI uploads them to Gitea when you publish a GitHub release. For ad-hoc uploads, use scripts/upload-release-to-gitea.sh. For launcher auto-update, prefer:

  • Set update_feed_url (or LAUNCHER_UPDATE_URL) to a generic HTTPS base URL where latest.yml and the installer files are hosted (often the same Gitea release attachment URLs pattern your reverse proxy exposes), or
  • Keep publishing to a GitHub release only for latest.yml + installers if you accept that small metadata/binary channel there.

Private GitHub updater: set GH_TOKEN / GITHUB_TOKEN / github.token_env as documented in lib/auto-update.js behaviour.

Generic feed: optional Bearer token via the same token envs if your static host checks Authorization.

Publishing a new launcher version

  1. Bump version in package.json on main (or your release branch) and merge.
  2. Create a GitHub release (tag + attach patches / Wow.exe if needed) and click PublishSync release to Gitea builds Windows installers and mirrors everything to Gitea.
  3. Local check: npm run pack:win then scripts/upload-release-to-gitea.sh with the same GITEA_* env vars as CI if you need a manual upload.

Sync to Gitea (patches + launcher binaries)

CI workflow Sync release to Gitea (.github/workflows/gitea-release-sync.yml) runs on every published GitHub release on this repo:

  1. Triggers on release published on Dawnforger/Fractured (or workflow_dispatch with a tag).
  2. Builds the Electron app from that tag on Windows.
  3. Downloads all assets attached to that GitHub release (MPQs, patched Wow.exe, etc.).
  4. Merges with the built launcher artifacts and uploads everything to a Gitea release with the same tag (existing attachments on that Gitea release are replaced).

GitHub Actions secrets (repository → Settings → Secrets and variables → Actions):

Secret Example
GITEA_BASE_URL https://git.yourdomain.com (no trailing slash)
GITEA_TOKEN Gitea personal access token with permission to manage releases and attachments on the target repo
GITEA_OWNER Organization or username on Gitea
GITEA_REPO Repository name — must already have at least one commit (Gitea returns HTTP 422 “repo is empty” for zero-commit repos; push e.g. a README on main or set GITEA_TARGET_REF to your default branch)

Optional variable (Settings → Variables): GITEA_TARGET_REF — default branch/commitish used only when the workflow must create a new Gitea release and Gitea needs target_commitish (defaults to main in the upload script if unset).

Player launcher.json: set gitea so from_release files resolve on your instance:

"gitea": {
  "base_url": "https://git.yourdomain.com",
  "owner": "myorg",
  "repo": "fractured-patches",
  "release_tag": "latest",
  "token_env": "GITEA_TOKEN"
}

Manual upload: bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0 with the same env vars as CI.

Sync did not run / Gitea unchanged — checklist

  1. Git tag ≠ GitHub Release — Only Releases (published on the GitHub Releases page) trigger this workflow. If your teammate only git push --tags, create a Release from that tag and click Publish (or run Actions → Sync release to Gitea → Run workflow and enter the tag).
  2. Draft release — Must click Publish release; drafts do not mirror.
  3. Workflow on default branch — GitHub runs release workflows from the default branch (e.g. main). Ensure .github/workflows/gitea-release-sync.yml is merged there.
  4. Repo name guard — Jobs use if: github.repository == 'Dawnforger/Fractured'. Forks or renames must change that line or runs are skipped.
  5. SecretsGITEA_BASE_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO must be set under Settings → Secrets and variables → Actions. A failed “Upload to Gitea” step usually prints which is missing.
  6. Actions tab — Open the latest Sync release to Gitea run; a red build-electron (old tag without package-lock.json, etc.) or Upload to Gitea step shows the real error.
  7. HTTP 422 repo is empty — The Gitea repo has no commits yet. Push any initial commit (e.g. Add README in the Gitea web UI, or git push to main). Optionally set GITEA_TARGET_REF to match your real default branch if it is not main. From this repo you can run scripts/bootstrap-gitea-repo.sh (see script header for GITEA_* env or pass the HTTPS/SSH clone URL as the first argument).

Private Gitea token for players

Do not embed a shared admin PAT in a shipped launcher.json. Prefer read-only tokens scoped to one repo, short-lived tokens, or a small auth service that redirects to signed URLs.

Release asset names must match files[].source when from_release: true. Use release_tag: "latest" or a pinned tag matching both GitHub and Gitea.

Patch versions (same filenames, different bytes)

The launcher does not read Git commits. For turn-key updates when asset names stay fixed (patch-Z.MPQ, Wow-patched.exe, …):

  1. Ship patch-manifest.json next to those files on every release (Gitea/GitHub attachment). It lists a version label (any string you bump per release, e.g. v0.9.0-client) and a sha256 per files[].source name.
  2. With patch_manifest.enabled: true (default in default-launcher.json), Download updates first fetches the manifest from the same release channel. If the files already on disk match those checksums, the player sees “already match build … (nothing to download)” — no redundant downloads.
  3. After a real download, the launcher re-hashes installed files and compares to the manifest; mismatch → clear error. It also writes .fractured/patch-state.json under the WoW folder so the UI can show “Installed client files: …”.

If patch-manifest.json is missing on a release, the launcher falls back to always downloading all configured files (same as before).

Generate the manifest when you cut a release (paths are your local patch binaries):

cd /path/to/staging
node tools/fractured-launcher-electron/scripts/generate-patch-manifest.js v0.9.0-client patch-Z.MPQ Wow-patched.exe > patch-manifest.json

Attach patch-manifest.json together with the MPQ/exe to the GitHub release (CI sync copies it to Gitea with everything else).

CI

Workflow Fractured launcher CI (.github/workflows/fractured-launcher-ci.yml) runs on pushes/PRs under tools/fractured-launcher-electron/: Windows pack uses electron-builder … --publish never (not npm run pack:win, so tagged checkouts never require GH_TOKEN). Actions → Fractured launcher CI → Run workflow runs it manually.

Sync release to Gitea (.github/workflows/gitea-release-sync.yml) uses the same pack command. If you see GH_TOKEN / GitHubPublisher errors in logs, the job is almost certainly an old Re-run failed jobs — open Actions → Sync release to Gitea → Run workflow, enter the tag, and start a new run instead.

Config

Schema is defined by default-launcher.json (shipped in the app; first run copies to launcher.json beside the executable):

  • game_dir: WoW 3.3.5a root (contains Wow.exe).
  • update_feed_url: optional generic HTTPS base for launcher auto-update.
  • gitea: base_url, owner, repo, release_tag, token_env — when base_url is set (and owner/repo set), from_release downloads use the Gitea API.
  • github: used for non-release repo paths (from_release: false) and for GitHub from_release when gitea.base_url is empty.
  • patch_manifest: enabled, source (default patch-manifest.json), from_release — checksum-based skip + verify (see above).
  • files, realmlist, auth, launch.