diff --git a/.github/workflows/gitea-release-sync.yml b/.github/workflows/gitea-release-sync.yml index ef8af71..e7e77fa 100644 --- a/.github/workflows/gitea-release-sync.yml +++ b/.github/workflows/gitea-release-sync.yml @@ -3,7 +3,11 @@ # # Triggers: # - release: published / released → GitHub “Release” (not a raw git tag alone). -# - workflow_dispatch → re-run for a tag manually. +# - workflow_dispatch → Actions → this workflow → “Run workflow” (enter tag). +# +# Troubleshooting: “Re-run failed jobs” on an OLD run replays the *original* workflow +# YAML (e.g. still runs `npm run pack:win` without --publish never). After changing this +# file on default branch, start a *new* run via “Run workflow”, not Re-run on a pre-fix run. # # Important: pushing only a git tag does NOT run this — you must create/publish a # Release on github.com (Releases → Draft/new release → Publish). The workflow diff --git a/tools/fractured-launcher-electron/README.md b/tools/fractured-launcher-electron/README.md index 8f84e06..970627e 100644 --- a/tools/fractured-launcher-electron/README.md +++ b/tools/fractured-launcher-electron/README.md @@ -1,6 +1,6 @@ # Fractured Launcher (Electron) -Windows launcher with **no extra console window**, **native Browse folder** dialog, GitHub **release assets** + repo file sync, **realmlist**, optional **auth**, **Play**, and **auto-update** (via `electron-updater`). This is the **only** supported client launcher in this repo. +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 @@ -14,7 +14,12 @@ npm install npm start ``` -On first run, `launcher.json` is created next to the app (dev: in this folder). By default **`github.repo`** is **`Fractured-Distro`** (public release assets); no token is required for patches. Set **GITHUB_TOKEN** only if you override `github` to a **private** repo. +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 @@ -33,60 +38,110 @@ Produces under **`dist/`**: ## Auto-update behaviour - **Packaged** builds only (`npm run pack:win` output). In `npm start` dev mode, update checks are skipped (button still explains that). -- **~5 seconds** after launch, then **every 6 hours**, the app checks for a newer version. +- **No implicit GitHub feed:** the app does **not** guess `package.json` → `repository` 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 updates are hosted +### Where launcher updates are hosted -**`package.json`** → `build.publish` targets **`Dawnforger/Fractured-Distro`** (public). `electron-updater` reads **`latest.yml`** from the **latest** release there; players do not need a GitHub token for launcher updates. +**`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: -**Private GitHub** (if you change `build.publish` or `github` back to a private repo): set **`GH_TOKEN`** / **`GITHUB_TOKEN`** / **`github.token_env`** before starting the launcher so the updater can authenticate. +- 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. -**Public generic feed** (optional CDN): set **`update_feed_url`** or **`LAUNCHER_UPDATE_URL`** to a folder that hosts `latest.yml` + installers; optional Bearer token if the host requires it. +**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` (semver, e.g. `1.0.1`). -2. Create a **GitHub personal access token** with `repo` (or `public_repo` for public repos). -3. From this directory: +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 **Publish** — **Sync 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. -```bash -set GH_TOKEN=ghp_your_token_here -npm run publish:win +## 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 (create an empty repo on Gitea first) | + +**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: + +```json +"gitea": { + "base_url": "https://git.yourdomain.com", + "owner": "myorg", + "repo": "fractured-patches", + "release_tag": "latest", + "token_env": "GITEA_TOKEN" +} ``` -That builds NSIS + portable and **uploads** update metadata and installers to the configured GitHub repo’s **releases** (see [electron-builder publish](https://www.electron.build/configuration/publish)). +**Manual upload:** `bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0` with the same env vars as CI. -Players on an older NSIS install will pick up the next version automatically on the next check. +### Sync did not run / Gitea unchanged — checklist -## Public distro repo (patches + launcher binaries) +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. **Secrets** — **`GITEA_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. -Default **`default-launcher.json`** uses **`github.repo`: `Fractured-Distro`** so **`from_release`** assets (`patch-Z.MPQ`, `Wow-patched.exe`, …) download from **[Dawnforger/Fractured-Distro releases](https://github.com/Dawnforger/Fractured-Distro/releases)** — **no player token**. +### Private Gitea token for players -Publish assets there by: +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. -- **GitHub Actions** — workflow **Sync release to Fractured-Distro** (`.github/workflows/distro-release-sync.yml`): on **release published** on `Dawnforger/Fractured`, it **builds the Electron launcher** from that tag on Windows (`npm run pack:win`), **downloads every asset** attached to that release (patches, `Wow-patched.exe`, …), merges them (launcher files overwrite on duplicate names), and creates or updates the **same tag** on **`Fractured-Distro`**. Requires repository secret **`DISTRO_SYNC_TOKEN`**. **Manual:** Actions → run workflow with an existing tag. -- **Locally:** `scripts/publish-to-distro.sh` (see script header) if you need to upload without a full release cycle. +**Release asset names** must match **`files[].source`** when **`from_release`**: true. Use **`release_tag`**: `"latest"` or a pinned tag matching both GitHub and Gitea. -If your public repo slug is not `Fractured-Distro`, edit **`DISTRO_REPO`** in the workflow / script and **`github.repo`** in `launcher.json`. +## Patch versions (same filenames, different bytes) -### Private `github.repo` (optional) +The launcher does **not** read Git commits. For **turn-key** updates when asset names stay fixed (`patch-Z.MPQ`, `Wow-patched.exe`, …): -For a **private** release source, set `GITHUB_TOKEN` (or `github.token_env`) with read access — **do not** embed a shared PAT in shipped `launcher.json` for all players. +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: …”**. -**Release asset names** must match **`files[].source`** exactly when **`from_release`**: true. Use **`release_tag`: `"latest"`** for the newest release, or pin a tag. +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): + +```bash +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 **`npm run pack:win`** and **artifacts** (`*.exe`, `latest.yml`, blockmaps). **Actions → Fractured launcher CI → Run workflow** runs it manually. +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 (overrides default GitHub feed when set). -- **`github`**: `owner`, `repo`, `ref` (repo file paths), **`release_tag`** (`latest` or e.g. `v1.0.0`), **`token_env`** (env var name for a PAT when using private sources). -- **`files`**: `source`, `dest`, `backup`, **`from_release`** (asset name on GitHub release vs repo path). -- **`realmlist`**, **`auth`**, **`launch`**. +- **`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`**.