ci: Gitea-first launcher release; VPS origin migration
Fractured launcher CI / electron-launcher-windows (push) Waiting to run
Fractured launcher CI / electron-launcher-linux (push) Waiting to run

- 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>
This commit is contained in:
Docker Build
2026-05-13 13:22:23 -04:00
parent c3f679ab74
commit 310bff4e06
8 changed files with 159 additions and 73 deletions
+1 -1
View File
@@ -111,7 +111,7 @@ jobs:
for f in /tmp/from-main/*; do
if [ -f "$f" ]; then
bn=$(basename "$f")
if should_skip_merge_from_github "$bn"; then
if should_skip_existing_launcher_artifact "$bn"; then
echo "Skipping GitHub release asset (CI launcher or excluded): $bn"
continue
fi
+21 -35
View File
@@ -1,27 +1,27 @@
# Primary path for player-facing binaries: every *published* GitHub Release on this repo
# is mirrored to your self-hosted Gitea (same tag). No public GitHub distro repo.
# Player-facing binaries live on self-hosted Gitea releases (same tag as this run).
# This workflow builds the Electron launcher (Windows + Linux) and uploads it together with
# whatever attachments already exist on that Gitea release (patches, Wow exe, …). It does **not**
# copy from GitHub releases anymore — create/publish the release on Gitea first, then run CI.
#
# Triggers:
# - release: published / released → GitHub “Release” (not a raw git tag alone).
# - workflow_dispatch → Actions → this workflow → “Run workflow” (enter tag).
# - release: published / released → GitHub “Release” (optional hook; tag must match Gitea).
# - workflow_dispatch → Actions → this workflow → “Run workflow” (enter tag = Gitea release 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.
# YAML. After changing this file on default branch, start a *new* run via “Run workflow”.
#
# 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
# definition must exist on the repo DEFAULT branch (GitHub runs it from there).
# Important: the Gitea release for the chosen tag must already exist with non-launcher assets
# if you expect them on the final upload (CI rewrites all attachments on that release).
#
# Steps: Windows (NSIS+portable) + Linux (AppImage) in parallel, launcher from DEFAULT BRANCH
# overlay on tag checkout → merge with GitHub release assets → upload all to Gitea.
# overlay on tag checkout → download existing Gitea attachments → merge CI launcher → upload to Gitea.
#
# Secrets: GITEA_BASE_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO
# Optional variable: GITEA_TARGET_REF (see tools/fractured-launcher-electron/README.md)
#
# Job guard: edit `if:` if github.repository is not HighSocietyRaiding/Fractured.
name: Sync release to Gitea
name: Gitea release — attach launcher builds
on:
release:
@@ -29,7 +29,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Git tag only (e.g. v0.7.11-paragon-foo). NOT the release title — open the release and copy the tag next to the title.'
description: 'Gitea release tag (e.g. v0.7.11-paragon-foo). Must match an existing Gitea release on GITEA_OWNER/GITEA_REPO.'
required: true
type: string
@@ -64,7 +64,7 @@ jobs:
exit 1
fi
if printf '%s' "$TAG" | grep -q '[[:space:]]'; then
echo '::error::Tag contains whitespace — that is usually the **release title**, not the tag. On GitHub → Releases → open the release → copy the **tag** (short ref like v0.7.11-…), not the long title line.'
echo '::error::Tag contains whitespace — use the exact Gitea tag (e.g. v0.7.11-…), not the release title.'
exit 1
fi
fi
@@ -202,33 +202,19 @@ jobs:
name: electron-dist-linux
path: /tmp/electron-linux
- name: Merge GitHub release assets + Electron build
env:
GH_TOKEN: ${{ github.token }}
- name: Merge Gitea release assets + Electron build
run: |
set -euo pipefail
. tools/fractured-launcher-electron/scripts/release-sync-filters.sh
TAG="${{ needs.meta.outputs.tag }}"
mkdir -p combined
mkdir -p /tmp/from-main
if gh release download "$TAG" -R "${{ github.repository }}" -D /tmp/from-main 2>/tmp/dl.err; then
shopt -s nullglob
for f in /tmp/from-main/*; do
if [ -f "$f" ]; then
bn=$(basename "$f")
if should_skip_merge_from_github "$bn"; then
echo "Skipping GitHub release asset (CI launcher or excluded): $bn"
continue
fi
cp -f "$f" combined/
fi
done
echo "Merged assets from ${{ github.repository }} release $TAG"
else
echo "GitHub release download note (continuing with launcher only):"
cat /tmp/dl.err || true
fi
mkdir -p /tmp/from-gitea
bash tools/fractured-launcher-electron/scripts/download-release-from-gitea.sh /tmp/from-gitea "$TAG"
shopt -s nullglob
for f in /tmp/from-gitea/*; do
if [ -f "$f" ]; then
cp -f "$f" combined/
fi
done
for f in /tmp/electron-win/* /tmp/electron-linux/*; do
if [ -f "$f" ]; then
cp -f "$f" combined/
+29 -4
View File
@@ -22,9 +22,16 @@
# bash scripts/vps-update-server.sh --dry-run
# bash scripts/vps-update-server.sh --run-after 'custom command here'
#
# Canonical Fractured source (Gitea): https://git.hisora.dev/HighSocietyRaiding/Fractured.git
# If this clone still has github.com/HighSocietyRaiding/Fractured on the pull remote, the script
# rewrites that remote URL to Gitea before pulling (override with FRACTURED_GITEA_ORIGIN_URL;
# disable with FRACTURED_SKIP_ORIGIN_MIGRATION=1).
#
# Environment:
# FRACTURED_GIT_REMOTE — remote name (default: origin)
# FRACTURED_POST_UPDATE_CMD — shell command run after compile (used by bare --run-after)
# FRACTURED_GIT_REMOTE — remote name (default: origin)
# FRACTURED_GITEA_ORIGIN_URL — URL used when auto-migrating off GitHub (default: Gitea HTTPS above)
# FRACTURED_SKIP_ORIGIN_MIGRATION — set to 1 to never rewrite remote URLs
# FRACTURED_POST_UPDATE_CMD — shell command run after compile (used by bare --run-after)
set -euo pipefail
@@ -40,6 +47,7 @@ DO_RESTART=0
INSTALL_PREFIX=""
POST_UPDATE_CMD="${FRACTURED_POST_UPDATE_CMD:-}"
GIT_REMOTE="${FRACTURED_GIT_REMOTE:-origin}"
FRACTURED_GITEA_ORIGIN_URL="${FRACTURED_GITEA_ORIGIN_URL:-https://git.hisora.dev/HighSocietyRaiding/Fractured.git}"
usage() {
cat <<'EOF'
@@ -59,8 +67,10 @@ Options:
If CMD is omitted, uses FRACTURED_POST_UPDATE_CMD.
Environment:
FRACTURED_GIT_REMOTE Git remote (default: origin).
FRACTURED_POST_UPDATE_CMD Used with bare --run-after.
FRACTURED_GIT_REMOTE Git remote name (default: origin).
FRACTURED_GITEA_ORIGIN_URL Target URL when auto-migrating from GitHub Fractured remote.
FRACTURED_SKIP_ORIGIN_MIGRATION Set to 1 to skip GitHub → Gitea remote rewrite.
FRACTURED_POST_UPDATE_CMD Used with bare --run-after.
EOF
}
@@ -178,12 +188,27 @@ current_branch() {
git symbolic-ref -q --short HEAD || git rev-parse --short HEAD
}
# Old VPS clones may still have origin → github.com/HighSocietyRaiding/Fractured; repoint to Gitea.
ensure_fractured_origin_on_gitea() {
if [[ "${FRACTURED_SKIP_ORIGIN_MIGRATION:-0}" == "1" ]]; then
return 0
fi
local url
url="$(git remote get-url "$GIT_REMOTE" 2>/dev/null || true)"
[[ -z "$url" ]] && return 0
if [[ "$url" =~ github\.com[:/]HighSocietyRaiding/Fractured ]]; then
echo "==> $GIT_REMOTE still points at GitHub Fractured; switching to Gitea: $FRACTURED_GITEA_ORIGIN_URL"
run git remote set-url "$GIT_REMOTE" "$FRACTURED_GITEA_ORIGIN_URL"
fi
}
if [[ "$NO_PULL" -eq 0 ]]; then
ref="$(current_branch)"
if [[ "$ref" == "HEAD" ]]; then
echo "error: detached HEAD; checkout a branch or use --no-pull." >&2
exit 1
fi
ensure_fractured_origin_on_gitea
echo "==> git pull $GIT_REMOTE $ref"
run git pull "$GIT_REMOTE" "$ref"
else
+30 -28
View File
@@ -43,7 +43,7 @@ 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).
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.sh`** → **`dist/*.AppImage`**.
@@ -66,7 +66,7 @@ Produces **`dist/Fractured-Launcher-${version}-Linux-x86_64.AppImage`**. Same **
### 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:
**`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.
@@ -78,17 +78,18 @@ Produces **`dist/Fractured-Launcher-${version}-Linux-x86_64.AppImage`**. Same **
### 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 **Publish****Sync 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.
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.
## Sync to Gitea (patches + launcher binaries)
## Gitea release + launcher (patches + binaries)
CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml`) runs on **every published GitHub release** on this repo:
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`** (or **workflow_dispatch** with a 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 **all assets** attached to that **GitHub** release (MPQs, patched `Wow.exe`, etc.).
4. Merges with the built launcher artifacts and uploads to a **Gitea release** with the **same tag** (existing attachments on that Gitea release are replaced). **Launcher installers** attached on GitHub (**`Fractured-Launcher*`**, case-insensitive) are **not** merged — the **CI build from the default branch** is the only source of launcher binaries, so an old installer on the GitHub release cannot “stick” on Gitea next to a newer build. **`*.blockmap`** and **`builder-debug.yml`** are omitted from the merge and from Gitea uploads.
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):
@@ -99,7 +100,7 @@ CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml
| **`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).
**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`**:
@@ -126,28 +127,29 @@ bash tools/fractured-launcher-electron/scripts/gitea-replace-launcher-only.sh \
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.
### Sync did not run / Gitea unchanged — checklist
### Launcher attach 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 title****Run 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 == 'HighSocietyRaiding/Fractured'`. Forks or renames must change that line or runs are skipped (GitHub Actions only; canonical clone is Gitea).
6. **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.
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`** — 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.
10. **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.
11. **`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.
12. **`.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.
13. **Gitea still shows an old launcher version** — The sync 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 publishing the GitHub release (or re-run **Actions → Sync release to Gitea** after merging). Previously, **Fractured-Launcher\*** files **attached on the GitHub release** were merged too, which could leave **two** installer versions on Gitea; those assets are now skipped in favor of CI-only builds.
14. **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.
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 title****Run 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. **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.
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 both GitHub and Gitea.
**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)
@@ -166,13 +168,13 @@ 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).
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.
**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.
**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
@@ -36,7 +36,7 @@ git checkout -q -b "$BRANCH"
cat >README.md <<'EOF'
# Fractured release mirror
Release assets (launcher builds, patches, `patch-manifest.json`, etc.) are uploaded here by **GitHub Actions** (“Sync release to Gitea”) from the main Fractured repository.
Release assets (launcher builds, patches, `patch-manifest.json`, etc.) are uploaded here by **GitHub Actions** (**Gitea release — attach launcher builds**) from the main Fractured repository.
This initial commit exists because **Gitea requires at least one commit** in the repository before releases can be created.
EOF
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# Download attachments from an existing Gitea release (same tag as CI). Skips filenames that
# the launcher CI rebuilds (see release-sync-filters.sh) so Electron artifacts can replace them.
#
# Usage:
# export GITEA_BASE_URL=https://git.example.com
# export GITEA_TOKEN=gta_...
# export GITEA_OWNER=myorg
# export GITEA_REPO=Fractured
# ./download-release-from-gitea.sh /tmp/gitea-assets v1.0.0
#
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=release-sync-filters.sh
. "$SCRIPT_DIR/release-sync-filters.sh"
OUTDIR="${1:?first arg: output directory}"
TAG="${2:?second arg: release tag (e.g. v1.0.0)}"
: "${GITEA_BASE_URL:?Set GITEA_BASE_URL (no trailing slash required)}"
: "${GITEA_TOKEN:?Set GITEA_TOKEN}"
: "${GITEA_OWNER:?Set GITEA_OWNER}"
: "${GITEA_REPO:?Set GITEA_REPO}"
BASE="${GITEA_BASE_URL%/}"
API="$BASE/api/v1"
AUTH_JSON=(-H "Authorization: token ${GITEA_TOKEN}" -H "Accept: application/json")
mkdir -p "$OUTDIR"
TAG_ENC=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$TAG")
REL_JSON=$(mktemp)
trap 'rm -f "$REL_JSON"' EXIT
code=$(curl -sS -o "$REL_JSON" -w "%{http_code}" "${AUTH_JSON[@]}" \
"$API/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/tags/${TAG_ENC}")
if [ "$code" != "200" ]; then
echo "Gitea GET release by tag \"$TAG\" failed HTTP $code — create/publish that release on Gitea first, then run this workflow." >&2
cat "$REL_JSON" >&2
exit 1
fi
count=0
while IFS= read -r obj; do
[ -z "$obj" ] && continue
name=$(jq -r '.name // empty' <<<"$obj")
url=$(jq -r '(.browser_download_url // .download_url // empty)' <<<"$obj")
[ -z "$name" ] || [ "$name" = "null" ] && continue
[ -z "$url" ] || [ "$url" = "null" ] && continue
if should_skip_existing_launcher_artifact "$name"; then
echo "Skipping existing launcher artifact (CI replaces): $name"
continue
fi
if [[ "$url" != http://* && "$url" != https://* ]]; then
if [[ "$url" != /* ]]; then
echo "Unexpected attachment URL for $name: $url" >&2
exit 1
fi
url="${BASE}${url}"
fi
echo "Downloading $name"
curl -fsSL -H "Authorization: token ${GITEA_TOKEN}" -o "$OUTDIR/$name" "$url"
count=$((count + 1))
done < <(jq -c '(.attachments // .assets // [])[]' "$REL_JSON")
echo "Downloaded $count non-launcher attachment(s) from Gitea release $TAG."
@@ -1,9 +1,9 @@
#!/usr/bin/env bash
# Shared filters for GitHubGitea / distro release merges and Gitea uploads.
# Shared filters for release merges (GitHub/Gitea sources) and Gitea uploads.
# shellcheck shell=bash
# Skip when copying assets from `gh release download` into combined/: CI-built launcher is authoritative.
should_skip_merge_from_github() {
# Skip when copying existing release assets into combined/: CI-built launcher wins on collision.
should_skip_existing_launcher_artifact() {
local l
l=$(printf '%s' "${1##*/}" | tr '[:upper:]' '[:lower:]')
case "$l" in
@@ -14,6 +14,11 @@ should_skip_merge_from_github() {
return 1
}
# Legacy name (GitHub release download step); same rules as Gitea merge.
should_skip_merge_from_github() {
should_skip_existing_launcher_artifact "$@"
}
# Skip when POSTing attachments to Gitea (belt-and-suspenders if something slips into combined/).
should_skip_gitea_upload() {
local l
@@ -41,7 +41,7 @@ elif [ "$code" = "404" ]; then
body=$(jq -n \
--arg tag "$TAG" \
--arg name "Fractured $TAG" \
--arg body "Synced from GitHub Actions (Fractured)." \
--arg body "Fractured $TAG (CI)." \
--arg target "$TARGET" \
'{tag_name:$tag,name:$name,body:$body,draft:false,prerelease:false,target_commitish:$target}')
code=$(curl -sS -o "$REL_JSON" -w "%{http_code}" -X POST "${AUTH_H[@]}" \