Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/launcher-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: launcher

# Path-filtered: only runs when the launcher crate or its workflows change.
# The launcher is excluded from the main `ci` workflow (test.yml) because
# eframe brings in heavier system deps than the rest of the workspace and
# only matters on Windows. This workflow owns its full pipeline.

on:
push:
branches: [main]
paths:
- 'crates/launcher/**'
- '.github/workflows/launcher-*.yml'
pull_request:
paths:
- 'crates/launcher/**'
- '.github/workflows/launcher-*.yml'

concurrency:
group: launcher-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0

permissions:
contents: read

jobs:
fmt:
name: cargo fmt --check (launcher)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: cargo fmt
run: |
if ! cargo fmt -p sgw-launcher -- --check; then
echo "::error::Formatting differs from rustfmt. Run 'cargo fmt -p sgw-launcher' locally and commit the result."
exit 1
fi

clippy:
name: cargo clippy -D warnings (Windows)
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
with:
shared-key: launcher-windows
- name: cargo clippy
run: cargo clippy -p sgw-launcher --all-targets -- -D warnings

build:
name: cargo build (Windows)
runs-on: windows-latest
needs: clippy
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: launcher-windows
- name: cargo build
run: cargo build -p sgw-launcher --all-targets

test:
name: cargo nextest (Windows)
runs-on: windows-latest
needs: clippy
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: launcher-windows
- name: install nextest
uses: taiki-e/install-action@nextest
- name: cargo nextest run
run: cargo nextest run --profile=ci -p sgw-launcher
130 changes: 130 additions & 0 deletions .github/workflows/launcher-release-on-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: launcher-release-on-comment

# ChatOps trigger for launcher-release.yml. A maintainer comments
# `/release-launcher` on a merged PR and this workflow validates the
# comment and dispatches the launcher release workflow.
#
# Mirrors the structure of release-on-comment.yml (which handles the
# server container `/release` command). The two are intentionally
# separate so a server release and a launcher release can happen
# independently.

on:
issue_comment:
types: [created]

permissions: {}

concurrency:
group: launcher-release-comment-${{ github.event.issue.number }}
cancel-in-progress: false

jobs:
dispatch:
name: validate + dispatch launcher-release
if: |
github.event.issue.pull_request != null
&& startsWith(github.event.comment.body, '/release-launcher')
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
actions: write
contents: read
timeout-minutes: 10
env:
GH_REPO: ${{ github.repository }}
steps:
- name: validate command shape
id: cmd
env:
BODY: ${{ github.event.comment.body }}
run: |
set -euo pipefail
FIRST_LINE=$(printf '%s' "$BODY" | head -n1)
if printf '%s' "$FIRST_LINE" | grep -Eq '^/release-launcher([[:space:]]|$)'; then
echo "matched=true" >> "$GITHUB_OUTPUT"
else
echo "Comment starts with '/release-launcher' but isn't an exact command — skipping."
echo "matched=false" >> "$GITHUB_OUTPUT"
fi

- name: 👀 acknowledge receipt
if: steps.cmd.outputs.matched == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
COMMENT_ID: ${{ github.event.comment.id }}
run: |
gh api -X POST \
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content=eyes >/dev/null

- name: validate commenter has write access
if: steps.cmd.outputs.matched == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.event.comment.user.login }}
COMMENT_ID: ${{ github.event.comment.id }}
PR: ${{ github.event.issue.number }}
run: |
set -euo pipefail
PERM=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" --jq '.permission')
echo "actor=${ACTOR} permission=${PERM}"
case "$PERM" in
admin|maintain|write) echo "ok" ;;
*)
gh api -X POST \
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content=-1 >/dev/null
gh pr comment "$PR" \
--body "@${ACTOR} \`/release-launcher\` requires repo write permission. (You have \`${PERM}\`.)"
exit 1
;;
esac

- name: ensure PR is merged
if: steps.cmd.outputs.matched == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
COMMENT_ID: ${{ github.event.comment.id }}
PR: ${{ github.event.issue.number }}
run: |
set -euo pipefail
MERGED=$(gh api "repos/${REPO}/pulls/${PR}" --jq '.merged')
if [ "$MERGED" != "true" ]; then
gh api -X POST \
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content=-1 >/dev/null
gh pr comment "$PR" \
--body "Can't \`/release-launcher\` from an open PR. Merge first, then comment \`/release-launcher\` again — the launcher release workflow always builds from \`main\` HEAD, so it needs the merge to be in main."
exit 1
fi

- name: dispatch launcher-release.yml
if: steps.cmd.outputs.matched == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ github.event.issue.number }}
run: |
gh workflow run launcher-release.yml \
--ref main \
-f "triggering_pr=${PR}"

- name: 🚀 acknowledge dispatch
if: steps.cmd.outputs.matched == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
COMMENT_ID: ${{ github.event.comment.id }}
PR: ${{ github.event.issue.number }}
ACTOR: ${{ github.event.comment.user.login }}
SERVER_URL: ${{ github.server_url }}
run: |
gh api -X POST \
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content=rocket >/dev/null
gh pr comment "$PR" \
--body "🚀 Launcher release dispatched by @${ACTOR}. Watch [launcher-release runs](${SERVER_URL}/${REPO}/actions/workflows/launcher-release.yml)."
96 changes: 96 additions & 0 deletions .github/workflows/launcher-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: launcher-release

# Builds and publishes a GitHub Release containing the launcher .exe.
# Triggered either manually via the Actions UI (`workflow_dispatch`) or
# via the `/release-launcher` ChatOps command on a merged PR, which
# dispatches this workflow through `launcher-release-on-comment.yml`.
#
# Always builds whatever is at `main` HEAD — the triggering PR is just
# the cut-point marker, mirroring the policy of `release-container.yml`.

on:
workflow_dispatch:
inputs:
triggering_pr:
description: 'PR # that triggered this release (informational, optional)'
required: false

permissions: {}

concurrency:
group: launcher-release
cancel-in-progress: false

jobs:
release:
name: build + publish (Windows)
runs-on: windows-latest
permissions:
contents: write
timeout-minutes: 45
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
# Baked into the binary at compile time via option_env!. The release
# job is the only place this secret is referenced; PR / build jobs
# produce non-uploading binaries.
LAUNCHER_LOG_SAS_URL: ${{ secrets.LAUNCHER_LOG_SAS_URL }}
steps:
# Actions pinned to immutable commit SHAs (with version comments) per
# GitHub's secure-use guide. This job is privileged — it builds with a
# secret SAS baked in and creates GitHub Releases — so the supply-chain
# surface from any third-party action is unusually large here.
# `persist-credentials: false` so the GITHUB_TOKEN isn't left in the
# local git config for later steps.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: main
persist-credentials: false
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
shared-key: launcher-release
- name: cargo build --release
run: cargo build -p sgw-launcher --release
- name: stamp version
id: stamp
shell: bash
run: |
set -euo pipefail
SHORT_SHA="${GITHUB_SHA:0:7}"
DATE=$(date -u +%Y%m%d)
TAG="launcher-${DATE}-${SHORT_SHA}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
- name: stage artifact
shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
cp target/release/sgw-launcher.exe \
"artifacts/sgw-launcher-${{ steps.stamp.outputs.tag }}.exe"
- name: upload build artifact (workflow run)
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: sgw-launcher-${{ steps.stamp.outputs.tag }}
path: artifacts/sgw-launcher-${{ steps.stamp.outputs.tag }}.exe
retention-days: 30
- name: create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TRIGGERING_PR: ${{ github.event.inputs.triggering_pr }}
TAG: ${{ steps.stamp.outputs.tag }}
shell: bash
run: |
set -euo pipefail
BODY="Stargate Worlds launcher build from commit \`${GITHUB_SHA}\`."
if [ -n "${TRIGGERING_PR}" ]; then
BODY="${BODY}

Dispatched from PR #${TRIGGERING_PR}."
fi
gh release create "${TAG}" \
--title "Launcher ${TAG}" \
--notes "${BODY}" \
"artifacts/sgw-launcher-${TAG}.exe"
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,14 @@ cargo check -p cimmeria-services
# Single-crate test
cargo test -p cimmeria-services

# Full workspace check — skip the Tauri apps so the linker doesn't OOM
# Full workspace check — skip the GUI apps (Tauri editors and the egui
# launcher) so the linker doesn't OOM and Linux dev hosts don't need
# xkbcommon/xcb dev packages.
cargo check --workspace \
--exclude cimmeria-app \
--exclude cimmeria-content-editor \
--exclude cimmeria-scene-editor
--exclude cimmeria-scene-editor \
--exclude sgw-launcher

# Kill stale builds
pkill -f "cargo|rustc"
Expand Down
Loading
Loading