Files
Fractured/tools/fractured-launcher-electron/README.md
T
Docker Build c1f7eaa153 fix(launcher): clearer fetch errors for Gitea TLS/DNS (fetch failed)
- fetchOrThrow wraps global fetch with TLS/DNS/refused hints + URL (sanitized).
- Use in gitea-release, github paths; fetchToFile already benefits.
- README checklist for sync Wow.exe fetch failed; version 1.0.5.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 21:46:48 -05:00

14 KiB
Raw Blame History

Fractured Launcher (Electron)

Windows and Linux (AppImage) 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: dev — next to the app in this folder; Windows packaged — beside the .exe; Linux AppImage / macOS packaged — under Electron app.getPath('userData') (typically under ~/.config/, folder name from the app; AppImage mount is read-only so config cannot live beside the binary).

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.

Build Linux AppImage

cd tools/fractured-launcher-electron
npm install
npm run pack:linux

Produces dist/Fractured-Launcher-${version}-Linux-x86_64.AppImage. Same lib/baked-gitea-channel.js and default-launcher.json as Windows; run on Linux (or use Fractured launcher CI / Sync release to Gitea, which upload this file to Gitea with the Windows installers).

Quick local test (avoids tag snapshot / CI):

  • Linux: from repo root, bash tools/fractured-launcher-electron/scripts/manual-pack-linux.shdist/*.AppImage.
  • Windows: on a Windows machine, cd tools/fractured-launcher-electron, npm ci, npm run pack:windist/*.exe.

Hardcoded Gitea channel (non-token)

lib/baked-gitea-channel.js exports base_url, owner, repo, release_tag. Set those strings once in the repo (same values you use for CI upload — not secret). At runtime config-store merges them into gitea.* so launcher.json does not need those fields; GITEA_TOKEN (or gitea.token_env) is still only for private Gitea. Leave a field '' in the baked file to fall back to default-launcher.json / user launcher.json for that key.

npm run pack:win is plain electron-builder — no inject step, no extra JSON beside the app.

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 + Linux launcher artifacts and mirrors everything to Gitea.
  3. Local check: npm run pack:win (on Windows) or npm run pack:linux / scripts/manual-pack-linux.sh, 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 Windows (NSIS + portable) and Linux (AppImage) in parallel, each using tools/fractured-launcher-electron from the default branch (overlaid onto the tag checkout), so older release tags never ship a launcher missing new lib/*.js files.
  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: packaged builds should already include gitea.base_url / owner / repo from the bake step above. Players only need to set GITEA_TOKEN (or your token_env) if the Gitea repo is private. To point at another instance, edit gitea in launcher.json:

"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. Manual run: tag vs titleRun workflow must receive the git tag (e.g. v0.7.11-paragon-…), copied from the release pages tag badge. Pasting the release title (long line with spaces/parentheses) breaks git fetch with invalid refspec.
  3. Draft release — Must click Publish release; drafts do not mirror.
  4. 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.
  5. Repo name guard — Jobs use if: github.repository == 'Dawnforger/Fractured'. Forks or renames must change that line or runs are skipped.
  6. 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.
  7. 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.
  8. 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).
  9. sync Wow.exe: fetch failed (Linux/AppImage especially) — Usually TLS (self-signed Gitea cert) or cannot reach the host. Use a trusted certificate on Gitea, or put your CA in the system trust store, or set NODE_EXTRA_CA_CERTS=/path/to/ca.pem when launching the AppImage if you must use self-signed HTTPS. If the mirror is LAN-only HTTP, set base_url in lib/baked-gitea-channel.js to http://… (only if acceptable). Ensure Wow-patched.exe exists on the same Gitea release you configured (release_tag: latest vs pinned tag). Newer builds show the failing URL and TLS hints in the error text.

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 (e.g. Wow-patched.exe — add more files entries for any extra MPQs you ship):

  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 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 (npm run pack:win) and Linux (npm run pack:linux) jobs, each electron-builder … --publish never. Actions → Fractured launcher CI → Run workflow runs it manually.

Sync release to Gitea (.github/workflows/gitea-release-sync.yml) uses the same pack commands. 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 Windows exe, or under userData on Linux/macOS packaged builds):

  • game_dir: WoW 3.3.5a root (contains Wow.exe).
  • update_feed_url: optional generic HTTPS base for launcher auto-update.
  • launcher_updates_from_github: default false. Only when true will a GITHUB_TOKEN (or github.token_env) enable electron-updaters GitHub provider against github.owner / github.repo. Leave false when launcher binaries and latest.yml live on Gitea (use gitea + token instead) so a stray GitHub token does not produce “No published versions on GitHub”.
  • gitea: base_url, owner, repo, release_tag, token_env — when base_url is set (and owner/repo set), from_release downloads and (with token if needed) the generic updater feed use Gitea. Required for players if your CI mirrors patches/launchers to Gitea only.
  • 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.