Skip to content

Divsion 12 loading screen #1318

Divsion 12 loading screen

Divsion 12 loading screen #1318

Workflow file for this run

name: Build
on:
push:
branches:
- "main"
- "dev"
pull_request:
branches: ["main", "dev"]
permissions:
actions: read
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
XMAKE_VERSION: "3.1.1"
# Bump this when a dependency cache must be invalidated manually.
XMAKE_CACHE_SCHEMA: "v4"
# Narrow protobuf-only compiler-cache experiment.
PROTOBUF_SCCACHE_VERSION: "v0.18.0"
PROTOBUF_SCCACHE_SCHEMA: "v1"
STFC_PROTOBUF_SCCACHE: "1"
# Keep sccache local; actions/cache owns persistence explicitly.
SCCACHE_GHA_ENABLED: "false"
# setup-xmake normally exports this when its integrated build cache is on.
# Keep XMake's compiler cache enabled while managing persistence ourselves.
XMAKE_ACTION_BUILD_CACHE: "true"
jobs:
build-win:
runs-on: windows-2025-vs2026
outputs:
run_attempt: ${{ steps.build_identity.outputs.run_attempt }}
permissions:
actions: read
contents: read
env:
STFC_MSVC_SCCACHE: "1"
WINDOWS_SCCACHE_SCHEMA: "v1"
SCCACHE_DIR: ${{ github.workspace }}/build/.windows-sccache
SCCACHE_CACHE_SIZE: "2G"
steps:
- name: Capture build identity
id: build_identity
shell: pwsh
run: |
"run_attempt=${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 1
submodules: false
show-progress: false
- name: Stamp commit hash
run: python scripts/stamp-version-hash.py "${{ github.sha }}"
- name: Restore XMake
id: restore_xmake
uses: actions/cache/restore@v6
with:
path: ${{ runner.tool_cache }}/xmake
key: xmake-tool-${{ runner.os }}-${{ runner.arch }}-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}
- name: Setup XMake
uses: xmake-io/github-action-setup-xmake@v1.2.5
with:
xmake-version: ${{ env.XMAKE_VERSION }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.11
with:
version: ${{ env.PROTOBUF_SCCACHE_VERSION }}
- name: Identify Windows runner image
id: windows_toolchain
shell: pwsh
run: |
if (-not $env:ImageVersion) { throw "GitHub runner ImageVersion is unavailable" }
"image_version=$env:ImageVersion" >> $env:GITHUB_OUTPUT
- name: Set Windows compiler cache identity
shell: pwsh
run: |
"SCCACHE_C_CUSTOM_CACHE_BUSTER=stfc-windows-${{ env.WINDOWS_SCCACHE_SCHEMA }}-xmake-${{ env.XMAKE_VERSION }}-${{ steps.windows_toolchain.outputs.image_version }}" >> $env:GITHUB_ENV
- name: Locate XMake caches
id: xmake_cache_paths
shell: pwsh
run: |
$packageDir = xmake l -c 'import("core.package.package"); print(package.installdir())'
$packageDir = $packageDir.Trim()
if (-not $packageDir) {
throw "XMake returned an empty package directory"
}
"package_dir=$packageDir" >> $env:GITHUB_OUTPUT
- name: Restore XMake packages
id: restore_packages
uses: actions/cache/restore@v6
with:
path: ${{ steps.xmake_cache_paths.outputs.package_dir }}
key: windows-x64-release-xmake-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}-${{ hashFiles('xmake/dependencies/common.lua', 'xmake/dependencies/windows.lua', 'xmake-requires.lock', 'xmake-packages/**') }}
- name: Restore XMake local packages
id: restore_local_packages
uses: actions/cache/restore@v6
with:
path: build/.packages
key: windows-x64-release-xmake-local-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}-${{ hashFiles('xmake/dependencies/common.lua', 'xmake/dependencies/windows.lua', 'xmake-requires.lock', 'xmake-packages/packages/s/spud/**') }}
- name: Restore Windows compiler cache
id: restore_windows_sccache
uses: actions/cache/restore@v6
with:
path: build/.windows-sccache
key: windows-x64-release-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.WINDOWS_SCCACHE_SCHEMA }}-${{ steps.windows_toolchain.outputs.image_version }}-${{ github.sha }}
restore-keys: |
windows-x64-release-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.WINDOWS_SCCACHE_SCHEMA }}-${{ steps.windows_toolchain.outputs.image_version }}-
windows-x64-release-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.WINDOWS_SCCACHE_SCHEMA }}-
- name: Configure
id: configure_windows
timeout-minutes: 30
shell: pwsh
run: |
$commit = (git rev-parse HEAD).Trim()
if ($commit -notmatch '^[0-9a-f]{40}$') {
throw "Unable to resolve the checked-out commit for DLL provenance."
}
$sourceState = "git:$commit"
$buildInvocation = "github:${{ github.run_id }}:${{ github.run_attempt }}:windows"
$identity = "stfc-identity-v1;distribution=netniv.stfc-community-mod;source=$sourceState;" +
"base=$commit;build=$buildInvocation;mode=release;channel=ci"
"STFC_EXPECTED_IDENTITY=$identity" >> $env:GITHUB_ENV
$xmakeArgs = @(
'f', '-m', 'release', '-y', '-v',
"--stfc_source_state_id=$sourceState",
"--stfc_base_commit=$commit",
"--stfc_build_invocation_id=$buildInvocation",
'--stfc_build_channel=ci'
)
xmake @xmakeArgs
- name: Verify dependency lock
shell: pwsh
run: |
$lockFile = "xmake-requires.lock"
git ls-files --error-unmatch -- $lockFile *> $null
if ($LASTEXITCODE -ne 0) {
Write-Output "::warning file=${lockFile}::Dependency lock is not tracked. Run 'xmake l scripts/update-requires-lock.lua' and commit the result."
exit 0
}
git diff --quiet HEAD -- $lockFile
$diffExitCode = $LASTEXITCODE
if ($diffExitCode -eq 1) {
Write-Output "::warning file=${lockFile}::Dependency lock changed during configure. Regenerate and commit it."
git diff HEAD -- $lockFile
} elseif ($diffExitCode -ne 0) {
Write-Output "::warning file=${lockFile}::Git could not verify the dependency lock (exit code $diffExitCode)."
}
exit 0
- name: Upload configure diagnostics
if: ${{ failure() && steps.configure_windows.outcome == 'failure' }}
uses: actions/upload-artifact@v7.0.1
with:
name: xmake-configure-diagnostics-windows
path: ~/.xmake/cache/packages/**/installdir.failed/logs/install.txt
if-no-files-found: warn
include-hidden-files: true
retention-days: 3
- name: Reset compiler cache stats
shell: pwsh
run: sccache --zero-stats
- name: Test IL2CPP runtime helpers
run: |
xmake build -y il2cpp-runtime-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
xmake run il2cpp-runtime-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Test IL2CPP class lookup
run: |
xmake build -y il2cpp-class-lookup-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
xmake run il2cpp-class-lookup-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Build
run: xmake build -y stfc-community-mod
- name: Test x64 trampoline relocation
shell: pwsh
run: |
xmake build -y spud-relocation-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
xmake run spud-relocation-tests
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Report compiler cache
shell: pwsh
run: sccache --show-stats
- name: Package
shell: pwsh
run: |
Move-Item `
"build/windows/x64/release/stfc-community-mod.dll" `
"version.dll"
- name: Test checked IL2CPP helpers
run: |
xmake build -y il2cpp-checked-tests
xmake run il2cpp-checked-tests
- name: Verify DLL provenance
shell: pwsh
run: |
$dll = (Resolve-Path -LiteralPath 'version.dll').Path
$comments = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($dll).Comments
if (-not [string]::Equals($comments, $env:STFC_EXPECTED_IDENTITY, [System.StringComparison]::Ordinal)) {
throw "DLL provenance mismatch. Expected '$env:STFC_EXPECTED_IDENTITY'; found '$comments'."
}
Write-Host "Verified DLL provenance: $comments"
- name: Upload
uses: actions/upload-artifact@v7.0.1
with:
name: stfc-community-mod
path: version.dll
compression-level: 0
if-no-files-found: error
- name: Save XMake packages
if: ${{ success() && !cancelled() && steps.restore_packages.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: ${{ steps.xmake_cache_paths.outputs.package_dir }}
key: ${{ steps.restore_packages.outputs.cache-primary-key }}
- name: Save XMake local packages
if: ${{ success() && !cancelled() && steps.restore_local_packages.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.packages
key: ${{ steps.restore_local_packages.outputs.cache-primary-key }}
- name: Save Windows compiler cache
if: ${{ success() && !cancelled() && steps.restore_windows_sccache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.windows-sccache
key: ${{ steps.restore_windows_sccache.outputs.cache-primary-key }}
- name: Save XMake
if: ${{ success() && !cancelled() && steps.restore_xmake.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: ${{ runner.tool_cache }}/xmake
key: ${{ steps.restore_xmake.outputs.cache-primary-key }}
attest-win:
name: attest-win
needs: build-win
if: >-
github.event_name == 'push' &&
github.repository == 'netniV/stfc-mod' &&
github.ref_protected &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')
runs-on: windows-2025-vs2026
permissions:
actions: read
contents: read
id-token: write
attestations: write
steps:
- name: Download Windows DLL
uses: actions/download-artifact@v8.0.1
with:
name: stfc-community-mod
path: .artifacts/windows
- name: Verify DLL provenance
shell: pwsh
env:
STFC_SOURCE_COMMIT: ${{ github.sha }}
STFC_RUN_ID: ${{ github.run_id }}
STFC_BUILD_RUN_ATTEMPT: ${{ needs.build-win.outputs.run_attempt }}
run: |
$dll = (Resolve-Path -LiteralPath '.artifacts/windows/version.dll').Path
$commit = $env:STFC_SOURCE_COMMIT
$runId = $env:STFC_RUN_ID
$buildAttempt = $env:STFC_BUILD_RUN_ATTEMPT
if ($commit -notmatch '^[0-9a-f]{40}$') {
throw "Unable to resolve the workflow commit for DLL provenance."
}
if ($runId -notmatch '^[1-9][0-9]*$') {
throw "Unable to resolve the workflow run ID for DLL provenance."
}
if ($buildAttempt -notmatch '^[1-9][0-9]*$') {
throw "The producing build did not provide a valid run attempt."
}
$expected = "stfc-identity-v1;distribution=netniv.stfc-community-mod;source=git:$commit;" +
"base=$commit;build=github:${runId}:${buildAttempt}:windows;mode=release;channel=ci"
$comments = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($dll).Comments
if (-not [string]::Equals($comments, $expected, [System.StringComparison]::Ordinal)) {
throw "DLL provenance mismatch. Expected '$expected'; found '$comments'."
}
Write-Host "Verified DLL provenance: $comments"
- name: Attest Windows DLL
uses: actions/attest@v4.2.1
with:
subject-path: .artifacts/windows/version.dll
build-mac:
name: build-mac (${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
runner: macos-26
artifact: macos-arm64-build
- arch: x86_64
runner: macos-26-intel
artifact: macos-x86_64-build
runs-on: ${{ matrix.runner }}
env:
MACOSX_DEPLOYMENT_TARGET: "14.6"
# Swift PCM files embed their absolute module-cache path. Invalidate the
# cache when the repository checkout path changes (for example, on rename).
SWIFT_MODULE_CACHE_SCHEMA: "v2"
SCCACHE_DIR: ${{ github.workspace }}/build/.protobuf-sccache
SCCACHE_CACHE_SIZE: "1G"
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 1
submodules: "recursive"
show-progress: false
- name: Stamp commit hash
run: python3 scripts/stamp-version-hash.py "${{ github.sha }}"
- name: Restore XMake
id: restore_xmake
uses: actions/cache/restore@v6
with:
path: ${{ runner.tool_cache }}/xmake
key: xmake-tool-${{ runner.os }}-${{ runner.arch }}-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}
- name: Setup XMake
uses: xmake-io/github-action-setup-xmake@v1.2.5
with:
xmake-version: ${{ env.XMAKE_VERSION }}
- name: Setup protobuf sccache
uses: mozilla-actions/sccache-action@v0.0.11
with:
version: ${{ env.PROTOBUF_SCCACHE_VERSION }}
- name: Set protobuf compiler cache identity
shell: bash
run: |
echo "SCCACHE_C_CUSTOM_CACHE_BUSTER=stfc-protobuf-${{ env.PROTOBUF_SCCACHE_SCHEMA }}-xmake-${{ env.XMAKE_VERSION }}-macos-${{ matrix.arch }}-release-${{ env.MACOSX_DEPLOYMENT_TARGET }}-${{ hashFiles('mods/src/prime/proto/*.proto', 'mods/xmake.lua', 'xmake.lua', 'xmake-requires.lock', 'xmake/dependencies/common.lua', 'xmake/dependencies/macos.lua', 'xmake/rules/protobuf_sccache.lua') }}" >> "$GITHUB_ENV"
- name: Locate XMake caches
id: xmake_cache_paths
shell: bash
run: |
set -euo pipefail
PACKAGE_DIR="$(xmake l -c 'import("core.package.package"); print(package.installdir())')"
test -n "$PACKAGE_DIR"
PLZMA_SHA="$(git -C macos-launcher/deps/PLzmaSDK rev-parse HEAD)"
test -n "$PLZMA_SHA"
echo "package_dir=$PACKAGE_DIR" >> "$GITHUB_OUTPUT"
echo "plzma_sha=$PLZMA_SHA" >> "$GITHUB_OUTPUT"
- name: Identify Swift toolchain
id: swift_toolchain
shell: bash
run: |
set -euo pipefail
{
xcodebuild -version
xcrun swiftc --version
xcrun --sdk macosx --show-sdk-version
} | tee "$RUNNER_TEMP/swift-toolchain.txt"
TOOLCHAIN_HASH="$(shasum -a 256 "$RUNNER_TEMP/swift-toolchain.txt" | awk '{print $1}')"
test -n "$TOOLCHAIN_HASH"
echo "hash=$TOOLCHAIN_HASH" >> "$GITHUB_OUTPUT"
- name: Restore XMake packages
id: restore_packages
uses: actions/cache/restore@v6
with:
path: ${{ steps.xmake_cache_paths.outputs.package_dir }}
key: macos-${{ matrix.arch }}-release-xmake-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}-${{ env.MACOSX_DEPLOYMENT_TARGET }}-${{ steps.xmake_cache_paths.outputs.plzma_sha }}-${{ hashFiles('xmake/dependencies/common.lua', 'xmake/dependencies/macos.lua', 'xmake-requires.lock', 'xmake-packages/**') }}
- name: Restore XMake local packages
id: restore_local_packages
uses: actions/cache/restore@v6
with:
path: build/.packages
key: macos-${{ matrix.arch }}-release-xmake-local-${{ env.XMAKE_VERSION }}-${{ env.XMAKE_CACHE_SCHEMA }}-${{ env.MACOSX_DEPLOYMENT_TARGET }}-${{ steps.xmake_cache_paths.outputs.plzma_sha }}-${{ hashFiles('xmake/dependencies/common.lua', 'xmake/dependencies/macos.lua', 'xmake-requires.lock', 'xmake-packages/packages/s/spud/**', 'xmake-packages/packages/p/PLzmaSDK/**') }}
- name: Restore Swift module cache
id: restore_swift_modules
uses: actions/cache/restore@v6
with:
path: build/.swift-module-cache
key: macos-${{ matrix.arch }}-release-swift-modules-${{ env.SWIFT_MODULE_CACHE_SCHEMA }}-${{ env.MACOSX_DEPLOYMENT_TARGET }}-${{ steps.swift_toolchain.outputs.hash }}-${{ steps.xmake_cache_paths.outputs.plzma_sha }}-${{ hashFiles('macos-launcher/xmake.lua', 'macos-launcher/src/module.modulemap', 'xmake/dependencies/macos.lua', 'xmake-requires.lock', 'xmake-packages/packages/p/PLzmaSDK/**') }}
- name: Restore XMake compiler cache
id: restore_build
uses: actions/cache/restore@v6
with:
path: build/.build_cache
key: macos-${{ matrix.arch }}-release-${{ env.XMAKE_CACHE_SCHEMA }}-${{ github.sha }}
restore-keys: |
macos-${{ matrix.arch }}-release-${{ env.XMAKE_CACHE_SCHEMA }}-
- name: Restore protobuf compiler cache
id: restore_protobuf_sccache
uses: actions/cache/restore@v6
with:
path: build/.protobuf-sccache
key: macos-${{ matrix.arch }}-release-protobuf-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.PROTOBUF_SCCACHE_SCHEMA }}-${{ steps.swift_toolchain.outputs.hash }}-${{ github.sha }}
restore-keys: |
macos-${{ matrix.arch }}-release-protobuf-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.PROTOBUF_SCCACHE_SCHEMA }}-${{ steps.swift_toolchain.outputs.hash }}-
macos-${{ matrix.arch }}-release-protobuf-sccache-${{ env.PROTOBUF_SCCACHE_VERSION }}-${{ env.PROTOBUF_SCCACHE_SCHEMA }}-
- name: Configure
id: configure
timeout-minutes: 30
run: >
xmake f
-p macosx
-a "${{ matrix.arch }}"
-m release
-y
-v
--target_minver="$MACOSX_DEPLOYMENT_TARGET"
- name: Verify dependency lock
shell: bash
run: |
LOCK_FILE="xmake-requires.lock"
if ! git ls-files --error-unmatch "$LOCK_FILE" >/dev/null 2>&1; then
echo "::warning file=$LOCK_FILE::Dependency lock is not tracked. Run 'xmake l scripts/update-requires-lock.lua' and commit the result."
elif ! git diff --quiet HEAD -- "$LOCK_FILE"; then
echo "::warning file=$LOCK_FILE::Dependency lock changed during configure. Regenerate and commit it."
git diff HEAD -- "$LOCK_FILE"
fi
- name: Upload configure diagnostics
if: ${{ failure() && steps.configure.outcome == 'failure' }}
uses: actions/upload-artifact@v7.0.1
with:
name: xmake-configure-diagnostics-macos-${{ matrix.arch }}
path: ~/.xmake/cache/packages/**/installdir.failed/logs/install.txt
if-no-files-found: warn
include-hidden-files: true
retention-days: 3
- name: Reset protobuf compiler cache stats
shell: bash
run: sccache --zero-stats
- name: Test IL2CPP runtime helpers
run: |
xmake build -y il2cpp-runtime-tests
xmake run il2cpp-runtime-tests
- name: Test IL2CPP class lookup
run: |
xmake build -y il2cpp-class-lookup-tests
xmake run il2cpp-class-lookup-tests
- name: Build
# -D keeps the existing XMake compiler-cache diagnostics too.
run: xmake -y -D
- name: Test x64 trampoline relocation
if: matrix.arch == 'x86_64'
shell: bash
run: |
xmake build -y spud-relocation-tests
xmake run spud-relocation-tests
- name: Report protobuf compiler cache
shell: bash
run: sccache --show-stats
- name: Report Swift module cache
shell: bash
run: |
set -euo pipefail
if [[ -d build/.swift-module-cache ]]; then
du -sh build/.swift-module-cache
echo "files=$(find build/.swift-module-cache -type f | wc -l | tr -d ' ')"
else
echo "::warning::Swift module cache directory was not created"
fi
- name: Test checked IL2CPP helpers
run: |
xmake build -y il2cpp-checked-tests
xmake run il2cpp-checked-tests
- name: Verify portable macOS linkage
shell: bash
run: |
set -euo pipefail
RELEASE="build/macosx/${{ matrix.arch }}/release"
check_linkage() {
local binary="$1"
local deps
echo
echo "==> $binary"
deps="$(otool -L "$binary")"
printf '%s\n' "$deps"
if printf '%s\n' "$deps" | grep -E \
'/opt/homebrew/|/usr/local/(opt|Cellar)/|(^|/)libssl(\.[0-9]+)*\.dylib|(^|/)libcrypto(\.[0-9]+)*\.dylib|(^|/)libcurl(\.[0-9]+)*\.dylib'
then
echo "::error file=$binary::Forbidden dynamic dependency detected"
return 1
fi
}
check_linkage "$RELEASE/macOSLauncher"
check_linkage "$RELEASE/stfc-community-mod-loader"
check_linkage "$RELEASE/libstfc-community-mod.dylib"
- name: Finalize launcher bundle
if: ${{ matrix.arch == 'arm64' }}
shell: bash
run: |
set -euo pipefail
APP="build/macosx/arm64/release/macOSLauncher.app"
test -f build/macosx/arm64/release/macOSLauncher
test -f "$APP/Contents/Resources/Assets.car"
test -f macos-launcher/src/Info.plist
cp macos-launcher/src/Info.plist \
"$APP/Contents/Info.plist"
test -f "$APP/Contents/Info.plist"
# Only transfer the files needed by package-mac instead of the
# complete XMake release directory.
- name: Package intermediate build
shell: bash
run: |
set -euo pipefail
RELEASE="build/macosx/${{ matrix.arch }}/release"
ARCHIVE="macos-${{ matrix.arch }}-build.tar"
test -f "$RELEASE/macOSLauncher"
test -f "$RELEASE/stfc-community-mod-loader"
test -f "$RELEASE/libstfc-community-mod.dylib"
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
test -d "$RELEASE/macOSLauncher.app"
tar -cf "$ARCHIVE" \
-C "$RELEASE" \
macOSLauncher \
stfc-community-mod-loader \
libstfc-community-mod.dylib \
macOSLauncher.app
else
tar -cf "$ARCHIVE" \
-C "$RELEASE" \
macOSLauncher \
stfc-community-mod-loader \
libstfc-community-mod.dylib
fi
- name: Upload intermediate build
uses: actions/upload-artifact@v7.0.1
with:
name: ${{ matrix.artifact }}
path: macos-${{ matrix.arch }}-build.tar
compression-level: 0
retention-days: 1
if-no-files-found: error
- name: Save XMake packages
if: ${{ success() && !cancelled() && steps.restore_packages.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: ${{ steps.xmake_cache_paths.outputs.package_dir }}
key: ${{ steps.restore_packages.outputs.cache-primary-key }}
- name: Save XMake local packages
if: ${{ success() && !cancelled() && steps.restore_local_packages.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.packages
key: ${{ steps.restore_local_packages.outputs.cache-primary-key }}
- name: Save Swift module cache
if: ${{ success() && !cancelled() && steps.restore_swift_modules.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.swift-module-cache
key: ${{ steps.restore_swift_modules.outputs.cache-primary-key }}
- name: Save protobuf compiler cache
if: ${{ success() && !cancelled() && steps.restore_protobuf_sccache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.protobuf-sccache
key: ${{ steps.restore_protobuf_sccache.outputs.cache-primary-key }}
- name: Save XMake compiler cache
if: ${{ success() && !cancelled() && steps.restore_build.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: build/.build_cache
key: ${{ steps.restore_build.outputs.cache-primary-key }}
- name: Save XMake
if: ${{ success() && !cancelled() && steps.restore_xmake.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: ${{ runner.tool_cache }}/xmake
key: ${{ steps.restore_xmake.outputs.cache-primary-key }}
package-mac:
name: package-mac
needs: build-mac
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 1
submodules: false
show-progress: false
- name: Download ARM64 build
uses: actions/download-artifact@v8.0.1
with:
name: macos-arm64-build
path: .artifacts/arm64
- name: Download x86_64 build
uses: actions/download-artifact@v8.0.1
with:
name: macos-x86_64-build
path: .artifacts/x86_64
- name: Restore architecture builds
shell: bash
run: |
set -euo pipefail
mkdir -p build/macosx/arm64/release
mkdir -p build/macosx/x86_64/release
tar -xf .artifacts/arm64/macos-arm64-build.tar \
-C build/macosx/arm64/release
tar -xf .artifacts/x86_64/macos-x86_64-build.tar \
-C build/macosx/x86_64/release
test -f build/macosx/arm64/release/macOSLauncher
test -f build/macosx/x86_64/release/macOSLauncher
test -f build/macosx/arm64/release/stfc-community-mod-loader
test -f build/macosx/x86_64/release/stfc-community-mod-loader
test -f build/macosx/arm64/release/libstfc-community-mod.dylib
test -f build/macosx/x86_64/release/libstfc-community-mod.dylib
test -f \
build/macosx/arm64/release/macOSLauncher.app/Contents/Resources/Assets.car
test -f \
build/macosx/arm64/release/macOSLauncher.app/Contents/Info.plist
- name: Create universal App Bundle
shell: bash
run: |
set -euo pipefail
APP="build/macosx/arm64/release/macOSLauncher.app"
lipo -create \
build/macosx/arm64/release/macOSLauncher \
build/macosx/x86_64/release/macOSLauncher \
-output "$APP/Contents/MacOS/macOSLauncher"
lipo -create \
build/macosx/arm64/release/stfc-community-mod-loader \
build/macosx/x86_64/release/stfc-community-mod-loader \
-output "$APP/Contents/stfc-community-mod-loader"
lipo -create \
build/macosx/arm64/release/libstfc-community-mod.dylib \
build/macosx/x86_64/release/libstfc-community-mod.dylib \
-output "$APP/Contents/libstfc-community-mod.dylib"
cp assets/launcher.icns \
"$APP/Contents/Resources/"
mv \
build/macosx/arm64/release/macOSLauncher.app \
"build/macosx/arm64/release/STFC Community Mod.app"
FINAL_APP="build/macosx/arm64/release/STFC Community Mod.app"
test -f "$FINAL_APP/Contents/Info.plist"
test -f "$FINAL_APP/Contents/Resources/Assets.car"
test -f "$FINAL_APP/Contents/Resources/launcher.icns"
test -f "$FINAL_APP/Contents/MacOS/macOSLauncher"
test -f "$FINAL_APP/Contents/stfc-community-mod-loader"
test -f "$FINAL_APP/Contents/libstfc-community-mod.dylib"
- name: Verify universal binaries
shell: bash
run: |
set -euo pipefail
APP="build/macosx/arm64/release/STFC Community Mod.app"
verify_universal() {
local file="$1"
local archs
archs="$(lipo -archs "$file")"
echo "$file: $archs"
[[ " $archs " == *" arm64 "* ]]
[[ " $archs " == *" x86_64 "* ]]
}
verify_universal \
"$APP/Contents/MacOS/macOSLauncher"
verify_universal \
"$APP/Contents/stfc-community-mod-loader"
verify_universal \
"$APP/Contents/libstfc-community-mod.dylib"
- name: Verify universal app linkage
shell: bash
run: |
set -euo pipefail
APP="build/macosx/arm64/release/STFC Community Mod.app"
check_linkage() {
local binary="$1"
local deps
echo
echo "==> $binary"
deps="$(otool -L "$binary")"
printf '%s\n' "$deps"
if printf '%s\n' "$deps" | grep -E \
'/opt/homebrew/|/usr/local/(opt|Cellar)/|(^|/)libssl(\.[0-9]+)*\.dylib|(^|/)libcrypto(\.[0-9]+)*\.dylib|(^|/)libcurl(\.[0-9]+)*\.dylib'
then
echo "::error file=$binary::Forbidden dynamic dependency detected"
return 1
fi
}
check_linkage "$APP/Contents/MacOS/macOSLauncher"
check_linkage "$APP/Contents/stfc-community-mod-loader"
check_linkage "$APP/Contents/libstfc-community-mod.dylib"
- name: Sign App Bundle
shell: bash
run: |
set -euo pipefail
codesign \
--force \
--verify \
--verbose \
--deep \
--sign "-" \
"build/macosx/arm64/release/STFC Community Mod.app"
- name: Install packaging tools
shell: bash
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
run: |
set -euo pipefail
# zstd is currently preinstalled on the macOS 26 image.
# Keep the fallback so the workflow remains robust if the
# runner image changes later.
command -v zstd >/dev/null 2>&1 || brew install zstd
command -v create-dmg >/dev/null 2>&1 || brew install create-dmg
- name: Package macOS mod archive
shell: bash
run: |
set -euo pipefail
mkdir -p stfc-community-mod-macos-universal
tar -cf - \
-C "build/macosx/arm64/release/STFC Community Mod.app/Contents" \
libstfc-community-mod.dylib \
| zstd -15 -T0 \
-o stfc-community-mod-macos-universal/stfc-community-mod-macos-universal.tar.zst
shasum -a 256 \
stfc-community-mod-macos-universal/stfc-community-mod-macos-universal.tar.zst \
| awk '{print $1}' \
> stfc-community-mod-macos-universal/stfc-community-mod-macos-universal.tar.zst.sha256
- name: Create installer
shell: bash
run: |
set -euo pipefail
APP="build/macosx/arm64/release/STFC Community Mod.app"
DMG_SOURCE="build/macosx/arm64/release/dmg-source"
mkdir -p "$DMG_SOURCE"
mv "$APP" "$DMG_SOURCE/"
create_dmg_options=(
--filesystem APFS
--format ULFO
--volname "STFC Community Mod Installer"
--volicon assets/launcher.icns
--no-internet-enable
--background assets/mac_installer_background.png
--window-pos 200 120
--window-size 800 400
--icon-size 100
--icon "STFC Community Mod.app" 200 190
--app-drop-link 600 185
--applescript-sleep-duration 1
)
create-dmg "${create_dmg_options[@]}" \
stfc-community-mod-installer.dmg \
"$DMG_SOURCE/"
- name: Upload installer
uses: actions/upload-artifact@v7.0.1
with:
name: stfc-community-mod-installer
path: stfc-community-mod-installer.dmg
compression-level: 0
if-no-files-found: error
- name: Upload macOS mod archive
uses: actions/upload-artifact@v7.0.1
with:
name: stfc-community-mod-macos-universal
path: stfc-community-mod-macos-universal
compression-level: 0
if-no-files-found: error