Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
5c12ea2
Modtale Launcher
Villagers654 Jun 20, 2026
97fe6a7
Launcher github actions
Villagers654 Jun 20, 2026
f0c3e9b
Run launcher tests in actions
Villagers654 Jun 20, 2026
1706be9
Fix MacOS build
Villagers654 Jun 20, 2026
2fc55f9
Seed R2
Villagers654 Jun 20, 2026
c80b41f
Seed users properly
Villagers654 Jun 20, 2026
cf210c6
Attempt 2 at R2 seeding
Villagers654 Jun 20, 2026
c2a39d9
Add logging to DataSeeder
Villagers654 Jun 20, 2026
0222482
Semi-actions rewrite
Villagers654 Jun 20, 2026
f010bb8
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
eef9752
Attempt to rebuild
Villagers654 Jun 21, 2026
2e1e6e8
Build all Linux launcher packages in CI
Villagers654 Jun 21, 2026
3155c48
Attempt to rebuild
Villagers654 Jun 21, 2026
ddf5c73
Fix weird auth issue
Villagers654 Jun 21, 2026
1d5619c
Harden some banner related stuff
Villagers654 Jun 21, 2026
f22cfa9
Add log4j
Villagers654 Jun 21, 2026
9de05b1
Fix some seeding issues
Villagers654 Jun 21, 2026
deea96e
Handle linux files separately
Villagers654 Jun 21, 2026
a4901f2
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
84d982d
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
d544f84
Attempt to redeploy
Villagers654 Jun 21, 2026
f14a0f3
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
feabb49
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
af26500
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
3410f7b
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
6284fe2
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
a5157ac
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
e022a91
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 21, 2026
66cb527
Merge modpack-v2 into launcher and update dependencies
Villagers654 Jun 27, 2026
38791da
Hide the results per page dropdown on mobile
Villagers654 Jun 27, 2026
a87e4e4
Merge branch 'modpack-v2' into launcher
Villagers654 Jun 27, 2026
d610b9a
Fix native download modal game version default
Villagers654 Jun 27, 2026
cd63a03
Fix deployment issues
Villagers654 Jun 28, 2026
e99d2c2
Merge branch 'modpack-v2' into launcher
Villagers654 Jul 24, 2026
46c6dc2
Merge branch 'modpack-v2' into launcher
Villagers654 Aug 12, 2026
f76ef50
Harden launcher update selection
Villagers654 Aug 12, 2026
c1b7707
Merge branch 'main' into launcher
Villagers654 Sep 3, 2026
f785a4b
Merge branch 'develop' into launcher
Villagers654 Sep 3, 2026
be64237
Update launcher dependencies
Villagers654 Sep 3, 2026
e73723d
Merge branch 'develop' into launcher
Villagers654 Sep 3, 2026
48c2c79
Bring launcher pages up to web parity
Villagers654 Sep 3, 2026
c69adb7
Merge branch 'modpack-v2' into launcher
Villagers654 Sep 3, 2026
e9733c8
Merge modpack-v2 into launcher
Villagers654 Sep 3, 2026
3f9058f
Make launcher scrolling native and fluid
Villagers654 Sep 3, 2026
d7d0a0b
Make scroll regression tests headless-safe
Villagers654 Sep 3, 2026
f267dba
Port HytaleModding wiki to launcher
Villagers654 Sep 3, 2026
8276593
Align modpacks with Hytale's unified runtime
Villagers654 Sep 3, 2026
9e063b8
Make nyoCF the canonical CurseForge integration
Villagers654 Sep 3, 2026
fa5385e
Reconcile and manage CurseForge launcher installs
Villagers654 Sep 3, 2026
86f13b5
Bring CurseForge browsing and project views to parity
Villagers654 Sep 3, 2026
80526b5
Reduce launcher runtime overhead and improve diagnostics
Villagers654 Sep 3, 2026
b285a88
Update launcher from modpack-v2 after history cleanup
Villagers654 Sep 3, 2026
400b6ae
Add native browser fallbacks for launcher links
Villagers654 Sep 3, 2026
6d4353f
Make launcher toasts compact and content-aware
Villagers654 Sep 3, 2026
7bd82a7
Allow browsing projects without a Modtale account
Villagers654 Sep 3, 2026
7405e23
Simplify library action layout
Villagers654 Sep 3, 2026
99fb690
Refine library world layout
Villagers654 Sep 3, 2026
f25448b
Consolidate library world actions
Villagers654 Sep 3, 2026
a9e0687
Polish library action rail
Villagers654 Sep 3, 2026
b252e2b
Add contextual launcher tooltips
Villagers654 Sep 3, 2026
7a92959
Simplify launcher play control
Villagers654 Sep 3, 2026
a65563e
Remove version controls from play menu
Villagers654 Sep 3, 2026
5fb25f1
Expand modpack contents from header
Villagers654 Sep 3, 2026
4577c6a
Honor native cursor sizing on Linux
Villagers654 Sep 3, 2026
480b1f5
Make launcher CurseForge integration direct
Villagers654 Sep 3, 2026
fb04846
Align launcher scrollbar to window edge
Villagers654 Sep 3, 2026
69c1c5e
Extend launcher viewport to bottom edge
Villagers654 Sep 3, 2026
5b105ee
Match launcher banner scrolling to web
Villagers654 Sep 3, 2026
ab0e8aa
Match launcher scrolling to web
Villagers654 Sep 3, 2026
bd98413
Match web scrolling across display scales
Villagers654 Sep 3, 2026
a48658e
Match launcher typography to web
Villagers654 Sep 3, 2026
1442486
Make Modtale accounts optional in launcher
Villagers654 Sep 3, 2026
b06bd7a
Keep project layout stable without banners
Villagers654 Sep 3, 2026
4272c84
Build web localization foundation
Villagers654 Sep 3, 2026
2148e0a
Merge localization foundation into launcher
Villagers654 Sep 3, 2026
347f662
Add native launcher localization foundation
Villagers654 Sep 3, 2026
d70e671
Make status history persistence resilient
Villagers654 Sep 3, 2026
23a9df1
Simplify status page chrome
Villagers654 Sep 3, 2026
c0032e6
Remove status last-check tile
Villagers654 Sep 3, 2026
a01ee49
Unify service status and availability history
Villagers654 Sep 3, 2026
faf4318
Align launcher content and auto-hide scrollbars
Villagers654 Sep 3, 2026
40b3931
Separate project card banners from content
Villagers654 Sep 3, 2026
1ae4bbf
Scope included mods hover to inner card
Villagers654 Sep 3, 2026
6e883ef
Restore library action group divider
Villagers654 Sep 3, 2026
c452782
Route CurseForge modpacks through launcher
Villagers654 Sep 3, 2026
a1aca86
Use CurseForge galleries for project banners
Villagers654 Sep 3, 2026
c0a0286
Add CurseForge browse category pill
Villagers654 Sep 3, 2026
fb064f2
Simplify download version labels
Villagers654 Sep 3, 2026
4af964f
Maintain a single Discord status mirror
Villagers654 Sep 4, 2026
db6c846
Fix status mirror service startup
Villagers654 Sep 4, 2026
7edbad0
Remove emoji from Discord status mirror
Villagers654 Sep 4, 2026
3e7e3c2
Keep status colors in availability bars
Villagers654 Sep 4, 2026
213cf65
Use a plain marker for missing status history
Villagers654 Sep 4, 2026
23225bd
Refine Play catalog edge treatment
Villagers654 Sep 4, 2026
3598614
Align Browse results with navbar bounds
Villagers654 Sep 4, 2026
d2ceaa8
Keep project statistics fully readable
Villagers654 Sep 4, 2026
8904e06
Treat CurseForge Early Access as oldest
Villagers654 Sep 4, 2026
0bfa0f5
Bring launcher Markdown rendering to web parity
Villagers654 Sep 4, 2026
c10cddc
Add CurseForge discussions and pinned comments
Villagers654 Sep 4, 2026
ec55f1d
Stabilize creator banner layout
Villagers654 Sep 4, 2026
6d4035f
Unify project card byline typography
Villagers654 Sep 4, 2026
c61fef5
Add pinned project comments
Villagers654 Sep 4, 2026
caa02ec
Merge develop into launcher
Villagers654 Sep 4, 2026
b8314eb
Keep Browse within navbar bounds
Villagers654 Sep 4, 2026
16c56a2
Import CurseForge comments read-only
Villagers654 Sep 4, 2026
f2bd01d
Load CurseForge comments directly in launcher
Villagers654 Sep 4, 2026
c99a150
Avoid conflicting status history index creation
Villagers654 Sep 4, 2026
9bcb3d0
Validate BetterMap comments in launcher
Villagers654 Sep 4, 2026
9ca4bf8
Replace live project test with stable coverage
Villagers654 Sep 4, 2026
93e8781
Show CurseForge pinned comments first
Villagers654 Sep 4, 2026
ccde787
Keep launcher browse content horizontally anchored
Villagers654 Sep 4, 2026
0f08dc0
Replace provider dropdown with direct switch
Villagers654 Sep 4, 2026
cf8bff1
Bound browse content to navbar edges
Villagers654 Sep 6, 2026
e7ff927
Constrain browse workspace to navbar bounds
Villagers654 Sep 7, 2026
b54cb0c
Limit CurseForge banner enrichment to page results
Villagers654 Sep 7, 2026
362b98f
Avoid CurseForge metadata fetches during browse
Villagers654 Sep 7, 2026
58169e0
Reject invalid API credentials without falling back to browser sessions
Villagers654 Sep 7, 2026
979a322
Limit credentialed CORS to trusted frontend origins
Villagers654 Sep 7, 2026
1e47819
Run frontend tests without modifying installed Vitest files
Villagers654 Sep 7, 2026
6138066
Save launcher JSON through a shared atomic file writer
Villagers654 Sep 7, 2026
79daee3
Handle cookie and OAuth error text without unsafe decoding
Villagers654 Sep 7, 2026
f5ff7c5
Remove browse category spacing gap
Villagers654 Sep 7, 2026
ac6261f
Load CurseForge browse banners asynchronously
Villagers654 Sep 7, 2026
55b2e85
Reject modpack override traversal and symbolic-link destinations
Villagers654 Sep 7, 2026
e4ba3dc
Validate all mock fixtures before replacing template collections
Villagers654 Sep 7, 2026
bad9884
Hide favorites on CurseForge mod pages
Villagers654 Sep 7, 2026
d196e0b
Keep PR test coverage and validate repository scripts in CI
Villagers654 Sep 7, 2026
d2b68cd
Preserve text around CurseForge description images
Villagers654 Sep 7, 2026
488aacf
Protect session mutations with CSRF tokens in every environment
Villagers654 Sep 7, 2026
0decf11
Close download responses and discard incomplete transfers
Villagers654 Sep 7, 2026
68ab600
Fix CurseForge HTML heading styling
Villagers654 Sep 7, 2026
c9c91d0
Remove public signing defaults and hide internal server error details
Villagers654 Sep 7, 2026
bd685e1
Consume download tokens atomically and tolerate null status snapshots
Villagers654 Sep 7, 2026
dca1c0e
Restrict and bound social preview image fetches
Villagers654 Sep 7, 2026
1d57a2e
Bound project prefetch memory, concurrency, and cache age
Villagers654 Sep 7, 2026
8e899d9
Disable legacy MFA-bypassing login and preserve stable account identity
Villagers654 Sep 7, 2026
bfe6faf
Render CurseForge descriptions with full HTML structure
Villagers654 Sep 7, 2026
7ecfcf9
Align security assertions and give browser import tests bounded start…
Villagers654 Sep 7, 2026
2a1a6b7
Share scroll locks across dialogs and preserve existing page styles
Villagers654 Sep 7, 2026
b00f39d
Restore sessions without readable cookies and tighten host checks
Villagers654 Sep 7, 2026
bc71a2e
Document audit findings, validation, and current contributor workflows
Villagers654 Sep 7, 2026
8e5812c
Merge codebase audit improvements
Villagers654 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions .github/scripts/detect-component-changes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ changed_files="$(git diff --name-only --no-renames "$base_sha" "$head_sha")"

frontend=false
backend=false
launcher=false
launcher_build=false

while IFS= read -r path; do
[[ -z "$path" ]] && continue
Expand All @@ -91,13 +93,27 @@ while IFS= read -r path; do
backend/*)
backend=true
;;
launcher/*)
launcher=true
launcher_build=true
;;
.github/workflows/tests.yml|.github/scripts/detect-component-changes.sh|.github/scripts/should-run-tests-workflow.sh)
frontend=true
backend=true
launcher=true
;;
.github/workflows/launcher-release.yml)
launcher=true
;;
esac
done <<< "$changed_files"

{
echo "Change detection range: $range_label"
echo "Frontend changed: $frontend"
echo "Backend changed: $backend"
echo "Launcher changed: $launcher"
echo "Launcher build needed: $launcher_build"
echo "Changed files:"
if [[ -n "$changed_files" ]]; then
printf '%s\n' "$changed_files"
Expand All @@ -109,4 +125,6 @@ done <<< "$changed_files"
{
echo "frontend=$frontend"
echo "backend=$backend"
echo "launcher=$launcher"
echo "launcher_build=$launcher_build"
} >> "${GITHUB_OUTPUT:-/dev/stdout}"
23 changes: 2 additions & 21 deletions .github/scripts/should-run-tests-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ event_name="${GITHUB_EVENT_NAME:-}"
repo="${GITHUB_REPOSITORY:-}"
repo_owner="${GITHUB_REPOSITORY_OWNER:-${repo%%/*}}"
ref_name="${GITHUB_REF_NAME:-}"
pr_action="${PR_ACTION:-}"
pr_head_repo="${PR_HEAD_REPO:-}"
pr_head_sha="${PR_HEAD_SHA:-}"
workflow_file="${WORKFLOW_FILE:-tests.yml}"

should_run=true
reason="This workflow run owns the work."
Expand All @@ -24,14 +20,8 @@ open_pr_count_for_branch() {
--jq 'length'
}

covering_push_run_for_pr_head() {
gh api --method GET "repos/$repo/actions/workflows/$workflow_file/runs" \
-f event=push \
-f head_sha="$pr_head_sha" \
--jq '.workflow_runs[] | select((.status != "completed") or (.conclusion != "cancelled" and .conclusion != "skipped")) | .html_url' |
head -n 1
}

# PR runs always own their tests. A queued push may itself skip because a PR
# exists, so its presence cannot prove that the commit has test coverage.
if [[ "$event_name" == "push" ]]; then
if gh_available && [[ -n "$repo" && -n "$repo_owner" && -n "$ref_name" ]]; then
if open_pr_count="$(open_pr_count_for_branch)"; then
Expand All @@ -45,15 +35,6 @@ if [[ "$event_name" == "push" ]]; then
else
echo "::warning::GitHub CLI or token unavailable; running tests to avoid missing coverage."
fi
elif [[ "$event_name" == pull_request* ]]; then
if [[ "$pr_action" == "opened" || "$pr_action" == "reopened" ]]; then
if gh_available && [[ "$pr_head_repo" == "$repo" && -n "$pr_head_sha" ]]; then
if push_run_url="$(covering_push_run_for_pr_head)" && [[ -n "$push_run_url" ]]; then
should_run=false
reason="Skipping PR workflow because an existing push run already covers this commit: $push_run_url"
fi
fi
fi
fi

echo "$reason"
Expand Down
81 changes: 81 additions & 0 deletions .github/tests/workflow-scripts.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { afterEach, beforeEach, test } from 'node:test';

const scripts = fileURLToPath(new URL('../scripts/', import.meta.url));
let directory;
beforeEach(() => {
directory = fs.mkdtempSync(path.join(os.tmpdir(), 'modtale-workflow-'));
});
afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));

function run(script, overrides = {}) {
const output = path.join(directory, 'output');
fs.writeFileSync(output, '');
const result = spawnSync('bash', [path.join(scripts, script)], {
cwd: directory,
encoding: 'utf8',
env: {
...process.env,
GITHUB_OUTPUT: output,
GITHUB_REPOSITORY: 'Modtale/modtale',
GITHUB_REPOSITORY_OWNER: 'Modtale',
GITHUB_REF_NAME: 'audit',
GH_TOKEN: 'test-token',
...overrides,
},
});
assert.equal(result.status, 0, result.stdout + result.stderr);
return fs.readFileSync(output, 'utf8');
}

test('PR creation and synchronization always retain their own test coverage', () => {
for (const action of ['opened', 'reopened', 'synchronize']) {
const output = run('should-run-tests-workflow.sh', {
GITHUB_EVENT_NAME: 'pull_request', PR_ACTION: action,
});
assert.match(output, /^should_run=true$/m);
}
});

test('push skips only when the GitHub API confirms an open PR', () => {
const bin = path.join(directory, 'bin');
fs.mkdirSync(bin);
const gh = path.join(bin, 'gh');
fs.writeFileSync(gh, '#!/bin/sh\nprintf "1\\n"\n', { mode: 0o755 });
const env = { GITHUB_EVENT_NAME: 'push', PATH: `${bin}${path.delimiter}${process.env.PATH}` };
assert.match(run('should-run-tests-workflow.sh', env), /^should_run=false$/m);
fs.writeFileSync(gh, '#!/bin/sh\nexit 1\n', { mode: 0o755 });
assert.match(run('should-run-tests-workflow.sh', env), /^should_run=true$/m);
});

test('test-workflow changes select all components without requesting launcher packaging', () => {
const git = (...args) => {
const result = spawnSync('git', args, { cwd: directory, encoding: 'utf8' });
assert.equal(result.status, 0, result.stderr);
return result.stdout.trim();
};
git('init', '--quiet');
git('config', 'user.name', 'Audit Test');
git('config', 'user.email', 'audit@example.test');
git('config', 'commit.gpgsign', 'false');
fs.writeFileSync(path.join(directory, 'README.md'), 'test');
git('add', '.');
git('commit', '--quiet', '-m', 'Initial fixture');
const base = git('rev-parse', 'HEAD');
fs.mkdirSync(path.join(directory, '.github/workflows'), { recursive: true });
fs.writeFileSync(path.join(directory, '.github/workflows/tests.yml'), 'name: tests');
git('add', '.');
git('commit', '--quiet', '-m', 'Change test workflow');
const output = run('detect-component-changes.sh', {
GITHUB_EVENT_NAME: 'push', GITHUB_SHA: git('rev-parse', 'HEAD'), PUSH_BEFORE_SHA: base,
});
for (const component of ['frontend', 'backend', 'launcher']) {
assert.match(output, new RegExp(`^${component}=true$`, 'm'));
}
assert.match(output, /^launcher_build=false$/m);
});
177 changes: 174 additions & 3 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ jobs:
permissions:
contents: 'read'
id-token: 'write'
outputs:
launcher_build: ${{ steps.filter.outputs.launcher_build }}
launcher_site_base_url: ${{ steps.launcher_metadata.outputs.site_base_url }}
launcher_api_base_url: ${{ steps.launcher_metadata.outputs.api_base_url }}
launcher_version: ${{ steps.launcher_metadata.outputs.version }}
launcher_artifact_prefix: ${{ steps.launcher_metadata.outputs.artifact_prefix }}

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -491,7 +497,8 @@ jobs:
ARGS+=("--update-secrets" "WIKI_API_KEY=WIKI_API_KEY:latest")
ARGS+=("--update-secrets" "DISCORD_WEBHOOK_URL=DISCORD_WEBHOOK_URL:latest")
ARGS+=("--update-secrets" "ADMIN_DISCORD_WEBHOOK_URL=ADMIN_DISCORD_WEBHOOK_URL:latest")
ARGS+=("--update-secrets" "STATUS_DISCORD_WEBHOOK_URL=STATUS_DISCORD_WEBHOOK_URL:latest")
# Only the detached status service owns the persistent #status message.
ARGS+=("--remove-secrets" "STATUS_DISCORD_WEBHOOK_URL")
elif [ "${{ env.ENV_TYPE }}" = "dev" ]; then
ARGS+=("--cpu" "1")
ARGS+=("--min-instances" "0")
Expand All @@ -515,7 +522,7 @@ jobs:
ARGS+=("--update-secrets" "BLUESKY_CLIENT_ID=BLUESKY_CLIENT_ID:latest")
ARGS+=("--update-secrets" "WARDEN_API_KEY=${{ env.WARDEN_SECRET_NAME }}:latest")
ARGS+=("--update-secrets" "WIKI_API_KEY=WIKI_API_KEY:latest")
ARGS+=("--remove-env-vars" "STATUS_DISCORD_WEBHOOK_URL")
ARGS+=("--remove-secrets" "STATUS_DISCORD_WEBHOOK_URL")
else
: "${RUNTIME_SERVICE_ACCOUNT:?Set environment variable GCP_BRANCH_PREVIEW_RUNTIME_SERVICE_ACCOUNT in the branch-preview GitHub environment.}"
# Preview services are usually idle. Request-based CPU billing avoids
Expand All @@ -535,7 +542,8 @@ jobs:
ARGS+=("--update-secrets" "APP_SEEDING_SOURCE_R2_ACCESS_KEY=${{ env.SEEDING_SOURCE_R2_ACCESS_KEY_SECRET_NAME }}:latest")
ARGS+=("--update-secrets" "APP_SEEDING_SOURCE_R2_SECRET_KEY=${{ env.SEEDING_SOURCE_R2_SECRET_KEY_SECRET_NAME }}:latest")
ARGS+=("--update-secrets" "APP_SEEDING_SOURCE_R2_ENDPOINT=${{ env.SEEDING_SOURCE_R2_ENDPOINT_SECRET_NAME }}:latest")
ARGS+=("--remove-env-vars" "SMTP_HOST,SMTP_PORT,SMTP_USERNAME,SMTP_PASSWORD,PRE_AUTH_SECRET,GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,GITLAB_CLIENT_ID,GITLAB_CLIENT_SECRET,DISCORD_CLIENT_ID,DISCORD_CLIENT_SECRET,TWITTER_CLIENT_ID,TWITTER_CLIENT_SECRET,BLUESKY_CLIENT_ID,BLUESKY_CLIENT_SECRET,WARDEN_URL,WARDEN_API_KEY,WIKI_API_KEY,WEBHOOK_URL,HYTALEMODDING_KEY,DISCORD_WEBHOOK_URL,ADMIN_DISCORD_WEBHOOK_URL,STATUS_DISCORD_WEBHOOK_URL")
ARGS+=("--remove-env-vars" "SMTP_HOST,SMTP_PORT,SMTP_USERNAME,SMTP_PASSWORD,PRE_AUTH_SECRET,GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,GITLAB_CLIENT_ID,GITLAB_CLIENT_SECRET,DISCORD_CLIENT_ID,DISCORD_CLIENT_SECRET,TWITTER_CLIENT_ID,TWITTER_CLIENT_SECRET,BLUESKY_CLIENT_ID,BLUESKY_CLIENT_SECRET,WARDEN_URL,WARDEN_API_KEY,WIKI_API_KEY,WEBHOOK_URL,HYTALEMODDING_KEY,DISCORD_WEBHOOK_URL,ADMIN_DISCORD_WEBHOOK_URL")
ARGS+=("--remove-secrets" "STATUS_DISCORD_WEBHOOK_URL")
fi

gcloud run deploy ${{ env.BACKEND_SERVICE }} "${ARGS[@]}" --format="value(status.url)" > backend_url.txt
Expand Down Expand Up @@ -719,6 +727,21 @@ jobs:
fi
fi

- name: Export launcher build metadata
id: launcher_metadata
run: |
short_sha="${GITHUB_SHA:0:7}"
suffix="${TAG:-preview}-$short_sha"
suffix="$(printf '%s' "$suffix" | sed 's/[^A-Za-z0-9.-]/-/g' | sed 's/--*/-/g' | cut -c 1-50 | sed 's/^[.-]*//;s/[.-]*$//')"
if [ -z "$suffix" ]; then
suffix="preview-$short_sha"
fi

echo "site_base_url=$FINAL_FRONTEND_URL" >> "$GITHUB_OUTPUT"
echo "api_base_url=$API_URL" >> "$GITHUB_OUTPUT"
echo "version=0.1.0-$suffix" >> "$GITHUB_OUTPUT"
echo "artifact_prefix=launcher-$suffix" >> "$GITHUB_OUTPUT"

- name: Notify Admin Bot of Backend Production Deploy
if: env.ENV_TYPE == 'prod' && (steps.update_backend_self_awareness.outputs.revision != '' || steps.deploy_backend.outputs.revision != '')
env:
Expand Down Expand Up @@ -833,6 +856,154 @@ jobs:
echo "---" >> $GITHUB_STEP_SUMMARY
echo "*View deployment details in [Google Cloud Console](https://console.cloud.google.com/run?project=${{ env.PROJECT_ID }}).* " >> $GITHUB_STEP_SUMMARY

package-launcher:
name: Package Staging Launcher (${{ matrix.name }})
needs: deploy
if: github.repository == 'Modtale/modtale' && needs.deploy.outputs.launcher_build == 'true'
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- name: Linux Packages
os: ubuntu-latest
artifact: linux
- name: Windows Installer
os: windows-latest
artifact: windows
- name: macOS DMG
os: macos-latest
artifact: macos
defaults:
run:
working-directory: launcher
shell: bash
env:
LAUNCHER_SITE_BASE_URL: ${{ needs.deploy.outputs.launcher_site_base_url }}
LAUNCHER_API_BASE_URL: ${{ needs.deploy.outputs.launcher_api_base_url }}
LAUNCHER_VERSION: ${{ needs.deploy.outputs.launcher_version }}

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Validate launcher target URLs
run: |
: "${LAUNCHER_SITE_BASE_URL:?Launcher site URL was not produced by the deploy job.}"
: "${LAUNCHER_API_BASE_URL:?Launcher API URL was not produced by the deploy job.}"
: "${LAUNCHER_VERSION:?Launcher version was not produced by the deploy job.}"

echo "Launcher site URL: $LAUNCHER_SITE_BASE_URL"
echo "Launcher API URL: $LAUNCHER_API_BASE_URL"

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

- name: Set up Linux packaging tooling
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
binutils \
flatpak \
rpm \
tar \
xz-utils \
zstd
sudo apt-get install -y libfuse2 || sudo apt-get install -y libfuse2t64 || true
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
sudo flatpak install -y flathub org.freedesktop.Platform//25.08 org.freedesktop.Sdk//25.08
curl -L \
-o "$RUNNER_TEMP/appimagetool" \
https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x "$RUNNER_TEMP/appimagetool"
echo "APPIMAGETOOL=$RUNNER_TEMP/appimagetool" >> "$GITHUB_ENV"
echo "APPIMAGE_EXTRACT_AND_RUN=1" >> "$GITHUB_ENV"

- name: Set up Windows installer tooling
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install wixtoolset -y --no-progress
$wix = Get-ChildItem "C:\Program Files (x86)" -Directory -Filter "WiX Toolset*" |
Sort-Object Name -Descending |
Select-Object -First 1
if ($null -eq $wix) {
throw "WiX Toolset was not installed."
}
"$($wix.FullName)\bin" | Out-File -FilePath $env:GITHUB_PATH -Append

- name: Ensure Gradle wrapper is executable
run: chmod +x gradlew

- name: Build staging launcher package
run: |
package_task="packageAll"
if [ "$RUNNER_OS" = "Linux" ]; then
package_task="packageLinuxAll"
fi

./gradlew clean "$package_task" \
-PlauncherVersion="$LAUNCHER_VERSION" \
-PmodtaleSiteBaseUrl="$LAUNCHER_SITE_BASE_URL" \
-PmodtaleApiBaseUrl="$LAUNCHER_API_BASE_URL"

- name: Upload Linux AppImage package
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-linux-appimage
path: launcher/build/distributions/*.AppImage
if-no-files-found: error

- name: Upload Linux Debian package
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-linux-deb
path: launcher/build/distributions/*.deb
if-no-files-found: error

- name: Upload Linux RPM package
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-linux-rpm
path: launcher/build/distributions/*.rpm
if-no-files-found: error

- name: Upload Linux Flatpak package
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-linux-flatpak
path: launcher/build/distributions/*.flatpak
if-no-files-found: error

- name: Upload Linux pacman package
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-linux-pacman
path: launcher/build/distributions/*.pkg.tar.zst
if-no-files-found: error

- name: Upload staging launcher package
if: runner.os != 'Linux'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.deploy.outputs.launcher_artifact_prefix }}-${{ matrix.artifact }}
path: launcher/build/distributions/*
if-no-files-found: error

cleanup-branch-preview:
name: Clean Up Deleted Branch Preview
if: github.repository == 'Modtale/modtale' && github.event.deleted == true && startsWith(github.ref, 'refs/heads/') && github.ref_name != 'main' && github.ref_name != 'develop'
Expand Down
Loading
Loading