Files
Fractured/tools/fractured-launcher-electron
Docker Build 310bff4e06
Fractured launcher CI / electron-launcher-windows (push) Waiting to run
Fractured launcher CI / electron-launcher-linux (push) Waiting to run
ci: Gitea-first launcher release; VPS origin migration
- Gitea release workflow downloads existing Gitea attachments, merges CI-built
  launchers, uploads merged set (no GitHub release mirror).
- Add download-release-from-gitea.sh; clarify release-sync filters.
- VPS update: repoint github.com/HighSocietyRaiding/Fractured remote to Gitea
  before git pull unless skipped via env.
- Docs and bootstrap comment; distro workflow uses shared skip helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 13:22:23 -04:00
..

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 / Gitea release — attach launcher builds, 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 run Gitea release — attach launcher builds (see below). 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/publish the Gitea release for that tag (attach patches / Wow.exe, patch-manifest.json, etc.).
  3. Run GitHub Actions → Gitea release — attach launcher builds (or publish a GitHub release with the same tag if you still use that trigger) so CI builds Windows + Linux launchers and re-uploads all release files to Gitea (see workflow header).
  4. 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.

Gitea release + launcher (patches + binaries)

CI workflow Gitea release — attach launcher builds (.github/workflows/gitea-release-sync.yml) attaches CI-built launcher binaries to the Gitea release that already exists for the chosen tag:

  1. Triggers on release published on HighSocietyRaiding/Fractured (optional) or workflow_dispatch with a tag (the tag must match a Gitea release on GITEA_OWNER / GITEA_REPO).
  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 existing attachments from that Gitea release (MPQs, patched Wow.exe, etc.).
  4. Merges with the built launcher artifacts and replaces all attachments on the same Gitea release (the upload script clears prior attachments, then posts the merged set). Launcher installers already on Gitea (Fractured-Launcher*, case-insensitive) are not re-downloaded — the CI build from the default branch is the only source of launcher binaries. *.blockmap and builder-debug.yml are omitted from the merge and from Gitea uploads.

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 upload script must create a new Gitea release (no release for that tag yet) and Gitea needs target_commitish (defaults to main in the upload script if unset). Prefer creating the release on Gitea before CI so patches ship in the same run.

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.

Legacy “bridge” after changing baked-gitea-channel.js: Players still using the old Gitea URL only receive launcher updates from that host. Build Windows + Linux installers (e.g. download Fractured launcher CI artifacts, or run npm run pack:win / npm run pack:linux), put dist/ contents in one folder if needed, then:

export GITEA_BASE_URL=http://your-old-host:port   # legacy base, no trailing slash
export GITEA_TOKEN=... GITEA_OWNER=Dawnsorrow GITEA_REPO=Fractured-Distro
bash tools/fractured-launcher-electron/scripts/gitea-replace-launcher-only.sh \
  tools/fractured-launcher-electron/dist latest

That script deletes only Fractured-Launcher*, latest.yml (only if you supply a new latest.yml in dist/), latest-linux.yml (only if supplied), *.blockmap, and builder-debug.yml on the release, then uploads the new files — Wow.exe, MPQs, and patch-manifest.json are left alone. If you only built Linux locally, merge Windows CI dist/ files into the same folder first so latest.yml is not removed without a replacement. Use release tag latest if that is what release_tag points at.

Launcher attach did not run / Gitea unchanged — checklist

  1. Gitea release missing — The workflow downloads from Gitea first. Create/publish a release on GITEA_OWNER / GITEA_REPO with the same tag before running CI (or rely on upload script to create an empty release — you still need GITEA_TARGET_REF if the repo default branch is not main).
  2. GitHub-only tag — If you use the release published trigger on GitHub, the tag must still exist as a Gitea release with your patch assets. Otherwise use Actions → Gitea release — attach launcher builds → Run workflow after the Gitea release exists.
  3. Manual run: tag vs titleRun workflow must receive the git tag (e.g. v0.7.11-paragon-…), not the long human-readable release title (spaces break the tag).
  4. Draft release — On Gitea, publish the release; drafts may not be visible to the API the same way — use a published release.
  5. 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.
  6. Repo name guard — Jobs use if: github.repository == 'HighSocietyRaiding/Fractured'. Forks or renames must change that line or runs are skipped (GitHub Actions only; canonical clone is Gitea).
  7. 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.
  8. Actions tab — Open the latest Gitea release — attach launcher builds run; a red build-electron (old tag without package-lock.json, etc.), Merge Gitea release assets, or Upload to Gitea step shows the real error.
  9. 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).
  10. sync Wow.exe: fetch failed — Often HTTPS/TLS to Gitea; use http://… in lib/baked-gitea-channel.js if you only serve plain HTTP, or fix certs / NODE_EXTRA_CA_CERTS. Ensure Wow-patched.exe exists on the release (release_tag: latest vs pinned). Errors include the failing URL when possible.
  11. Wine + Windows portable — If the folder picker returns /home/..., the launcher maps it to Z:\home\... (Wines Unix root). Wow.exe is matched case-insensitively for Linux-backed folders. Re-save the WoW folder after upgrading if validation still fails.
  12. EBUSY / file locked on Windows — The client (or antivirus) may keep .MPQ files open. The launcher retries for a short window and downloads the new file before replacing the old one; if sync still fails, exit WoW (and any tool previewing that folder) and run Download updates again.
  13. .bak-* clutter — When Download updates finishes without error, the launcher removes matching *.bak-YYYYMMDD-HHmmss files from earlier runs (same pattern it uses when replacing files). Failed syncs do not delete backups.
  14. Gitea still shows an old launcher version — The workflow overlays tools/fractured-launcher-electron from the default branch, so package.json there defines the built version. Ensure launcher changes are merged to main before attaching to the release (or re-run Actions → Gitea release — attach launcher builds after merging). Fractured-Launcher* files already on Gitea are skipped when re-downloading so CI is the only launcher source.
  15. Migrating baked-gitea-channel.js to a new host — Publish gitea-replace-launcher-only.sh (see Manual upload above) on the old Gitea latest release so auto-update still works until clients move to the new URL.

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 your Gitea releases.

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 Gitea release (same files CI merges before upload).

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.

Gitea release — attach launcher builds (.github/workflows/gitea-release-sync.yml) uses the same pack commands. If you see stale behaviour after a workflow edit, start a new run (Actions → Gitea release — attach launcher builds → Run workflow) instead of Re-run failed jobs on an old run.

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: default []. Download updates resolves what to pull in order: (1) non-empty files if you set explicit sourcedest pairs; (2) else each key in patch-manifest.json on the release (recommended); (3) else release attachments except launcher artifacts (Fractured-Launcher*, *.blockmap, latest*.yml, .AppImage, patch-manifest.json): .MPQData/enUS/<name>.MPQ (extension forced to .MPQ caps for client compatibility), one .exelaunch.exe. Multiple .exe attachments require a manifest. Legacy Wow-patched.exe entries are removed when merging launcher.json.
  • realmlist, auth, launch (**exe**, args). Only Data/enUS/realmlist.wtf is written: any realmlist.paths entry that is not the enUS file is ignored (so enGB is never created). On Linux, Play never runs Wow.exe as a native process (that yields EACCES). Use launch.linux_wrapper (default ["wine"]) so the launcher runs e.g. wine /path/Wow.exeargs, or set launch.linux_steam_uri to a Steam URI (e.g. steam://rungameid/… for a non-Steam game shortcut — the number is shown in Steams shortcut properties). Optional linux_steam_binary defaults to steam; for Flatpak Steam use linux_steam_spawn: ["flatpak", "run", "com.valvesoftware.Steam"] (the URI is appended as the last argument). After a successful Download updates run, the launcher deletes prior *.bak-YYYYMMDD-HHmmss backup files it created under the WoW folder.