Skip to content

build overlay server seperately #697

build overlay server seperately

build overlay server seperately #697

Workflow file for this run

name: Build Electron App
on:
push:
branches:
- main
workflow_dispatch:
concurrency:
group: release-exe-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
prepare-release:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
should_build_any: ${{ steps.version_check.outputs.should_build_any }}
should_create_release: ${{ steps.version_check.outputs.should_create_release }}
should_build_windows: ${{ steps.version_check.outputs.should_build_windows }}
should_build_linux: ${{ steps.version_check.outputs.should_build_linux }}
should_build_mac_arm64: ${{ steps.version_check.outputs.should_build_mac_arm64 }}
PACKAGE_VERSION: ${{ steps.version_check.outputs.PACKAGE_VERSION }}
release_body: ${{ steps.release_body.outputs.release_body }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
- name: Check package.json version against existing release
id: version_check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
echo "Fetching version from package.json..."
PACKAGE_VERSION=$(jq -r .version < ./package.json)
if [ -z "$PACKAGE_VERSION" ] || [ "$PACKAGE_VERSION" = "null" ]; then
echo "Error: Could not extract version from package.json"
exit 1
fi
echo "Package Version: $PACKAGE_VERSION"
echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
RELEASE_URL="https://api.github.com/repos/${{ github.repository }}/releases/tags/v$PACKAGE_VERSION"
RESPONSE_FILE=$(mktemp)
echo "Checking if release tag v$PACKAGE_VERSION exists..."
RESPONSE_CODE=$(curl -sS -o "$RESPONSE_FILE" -w "%{http_code}" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"$RELEASE_URL")
has_asset() {
jq -e --arg name "$1" '.assets[]? | select(.name == $name)' "$RESPONSE_FILE" > /dev/null
}
if [ "$RESPONSE_CODE" = "200" ]; then
echo "Release tag v$PACKAGE_VERSION exists. Checking which assets are missing..."
WINDOWS_SETUP="GameSentenceMiner-Setup-$PACKAGE_VERSION.exe"
WINDOWS_BLOCKMAP="$WINDOWS_SETUP.blockmap"
WINDOWS_UNPACKED_ZIP="GameSentenceMiner-$PACKAGE_VERSION-win-unpacked.zip"
LINUX_APPIMAGE="GameSentenceMiner-$PACKAGE_VERSION.AppImage"
MAC_ARM64_DMG="GameSentenceMiner-$PACKAGE_VERSION-arm64.dmg"
MAC_ARM64_BLOCKMAP="$MAC_ARM64_DMG.blockmap"
SHOULD_BUILD_WINDOWS=false
SHOULD_BUILD_LINUX=false
SHOULD_BUILD_MAC_ARM64=false
if ! has_asset "$WINDOWS_SETUP" || ! has_asset "$WINDOWS_BLOCKMAP" || ! has_asset "$WINDOWS_UNPACKED_ZIP" || ! has_asset "latest.yml"; then
SHOULD_BUILD_WINDOWS=true
fi
if ! has_asset "$LINUX_APPIMAGE" || ! has_asset "latest-linux.yml"; then
SHOULD_BUILD_LINUX=true
fi
if ! has_asset "$MAC_ARM64_DMG" || ! has_asset "$MAC_ARM64_BLOCKMAP" || ! has_asset "latest-mac.yml"; then
SHOULD_BUILD_MAC_ARM64=true
fi
SHOULD_BUILD_ANY=false
if [ "$SHOULD_BUILD_WINDOWS" = "true" ] || [ "$SHOULD_BUILD_LINUX" = "true" ] || [ "$SHOULD_BUILD_MAC_ARM64" = "true" ]; then
SHOULD_BUILD_ANY=true
fi
echo "should_create_release=false" >> "$GITHUB_OUTPUT"
echo "should_build_any=$SHOULD_BUILD_ANY" >> "$GITHUB_OUTPUT"
echo "should_build_windows=$SHOULD_BUILD_WINDOWS" >> "$GITHUB_OUTPUT"
echo "should_build_linux=$SHOULD_BUILD_LINUX" >> "$GITHUB_OUTPUT"
echo "should_build_mac_arm64=$SHOULD_BUILD_MAC_ARM64" >> "$GITHUB_OUTPUT"
elif [ "$RESPONSE_CODE" = "404" ]; then
echo "Release tag v$PACKAGE_VERSION does not exist. Proceeding with build and release."
echo "should_create_release=true" >> "$GITHUB_OUTPUT"
echo "should_build_any=true" >> "$GITHUB_OUTPUT"
echo "should_build_windows=true" >> "$GITHUB_OUTPUT"
echo "should_build_linux=true" >> "$GITHUB_OUTPUT"
echo "should_build_mac_arm64=true" >> "$GITHUB_OUTPUT"
else
echo "Error: Unexpected response while checking releases: $RESPONSE_CODE"
exit 1
fi
- name: Render release body
id: release_body
run: |
{
echo "release_body<<EOF"
node scripts/render-release-body.mjs --mode stable --version "${{ steps.version_check.outputs.PACKAGE_VERSION }}" --repo "${{ github.repository }}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
create-release:
runs-on: ubuntu-latest
needs:
- prepare-release
if: needs.prepare-release.outputs.should_create_release == 'true'
permissions:
contents: write
steps:
- name: Create release shell
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
target_commitish: ${{ github.sha }}
name: Release v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
body: ${{ needs.prepare-release.outputs.release_body }}
draft: false
prerelease: false
append_body: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-windows-unsigned:
runs-on: windows-latest
needs:
- prepare-release
- create-release
if: always() && needs.prepare-release.outputs.should_build_windows == 'true' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped')
permissions:
contents: read
id-token: write
actions: read
outputs:
windows_installer_artifact_id: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}
windows_unpacked_artifact_id: ${{ steps.upload-unsigned-windows-unpacked.outputs.artifact-id }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
cache: npm
cache-dependency-path: |
package-lock.json
GSM_Overlay/package-lock.json
- name: Cache Electron binaries
uses: actions/cache@v4
with:
path: |
~\AppData\Local\electron\Cache
~\AppData\Local\electron-builder\Cache
key: ${{ runner.os }}-electron-cache-${{ hashFiles('package-lock.json', 'GSM_Overlay/package-lock.json') }}
restore-keys: |
${{ runner.os }}-electron-cache-
- name: Fetch prebuilt overlay server (Windows)
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
New-Item -ItemType Directory -Force -Path GSM_Overlay/input_server/bin/win32 | Out-Null
gh release download overlay-server --repo ${{ github.repository }} --pattern gsm_overlay_server-win32.exe --output GSM_Overlay/input_server/bin/win32/gsm_overlay_server.exe --clobber
- name: Convert Handlebar files to LF line endings
run: |
Get-ChildItem -Path ./GSM_Overlay/yomitan/data/templates -Recurse -Include *.handlebars | ForEach-Object {
$content = (Get-Content $_.FullName -Raw) -replace "`r`n", "`n"
Set-Content $_.FullName $content -NoNewline
}
- name: Build Overlay
working-directory: ./GSM_Overlay
run: |
npm ci --prefer-offline --no-audit --fund=false
npm run package
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --fund=false
- name: Build Electron app
run: npm run app:dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload unsigned Windows installer for SignPath
id: upload-unsigned-windows
uses: actions/upload-artifact@v4
with:
name: signpath-windows-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: dist/*Setup*.exe
if-no-files-found: error
- name: Upload unsigned unpacked Windows build for SignPath
id: upload-unsigned-windows-unpacked
uses: actions/upload-artifact@v4
with:
name: signpath-windows-unpacked-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: dist/win-unpacked
if-no-files-found: error
sign-windows-installer:
runs-on: ubuntu-latest
needs:
- prepare-release
- build-windows-unsigned
if: always() && needs.build-windows-unsigned.result == 'success'
permissions:
contents: read
id-token: write
actions: read
steps:
- name: Submit Windows installer signing request
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd
with:
api-token: ${{ secrets.SIGNPATH_APITOKEN }}
organization-id: c677c167-9f3e-4577-b967-5d7730bf742d
project-slug: GameSentenceMiner
signing-policy-slug: release-signing
github-artifact-id: ${{ needs.build-windows-unsigned.outputs.windows_installer_artifact_id }}
wait-for-completion: true
output-artifact-directory: signed_windows
- name: Upload signed Windows installer
uses: actions/upload-artifact@v4
with:
name: signed-windows-installer-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: signed_windows
if-no-files-found: error
sign-windows-unpacked:
runs-on: ubuntu-latest
needs:
- prepare-release
- build-windows-unsigned
if: always() && needs.build-windows-unsigned.result == 'success'
permissions:
contents: read
id-token: write
actions: read
steps:
- name: Submit unpacked Windows signing request
id: sign-windows-unpacked
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd
with:
api-token: ${{ secrets.SIGNPATH_APITOKEN }}
organization-id: c677c167-9f3e-4577-b967-5d7730bf742d
project-slug: GameSentenceMiner
signing-policy-slug: release-signing
artifact-configuration-slug: Unpacked_GSM
github-artifact-id: ${{ needs.build-windows-unsigned.outputs.windows_unpacked_artifact_id }}
wait-for-completion: true
- name: Download signed unpacked Windows build
shell: pwsh
env:
SIGNPATH_APITOKEN: ${{ secrets.SIGNPATH_APITOKEN }}
run: |
$packageVersion = "${{ needs.prepare-release.outputs.PACKAGE_VERSION }}"
if (-not $packageVersion) {
throw "Package version is not available."
}
if (-not $env:SIGNPATH_APITOKEN) {
throw "SIGNPATH_APITOKEN secret is not available."
}
$distRoot = "dist"
New-Item -ItemType Directory -Force -Path $distRoot | Out-Null
$zipPath = Join-Path (Resolve-Path $distRoot).Path "GameSentenceMiner-$packageVersion-win-unpacked.zip"
if (Test-Path $zipPath) {
Remove-Item -LiteralPath $zipPath -Force
}
$headers = @{
Authorization = "Bearer $env:SIGNPATH_APITOKEN"
}
Invoke-WebRequest -Uri "${{ steps.sign-windows-unpacked.outputs.signed-artifact-download-url }}" -Headers $headers -OutFile $zipPath
if (-not (Test-Path $zipPath)) {
throw "Failed to download signed unpacked Windows zip."
}
- name: Upload signed unpacked Windows build
uses: actions/upload-artifact@v4
with:
name: signed-windows-unpacked-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: dist/GameSentenceMiner-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}-win-unpacked.zip
if-no-files-found: error
publish-windows-release:
runs-on: windows-latest
needs:
- prepare-release
- sign-windows-installer
- sign-windows-unpacked
if: always() && needs.sign-windows-installer.result == 'success' && needs.sign-windows-unpacked.result == 'success'
permissions:
contents: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies for release metadata
run: npm ci --prefer-offline --no-audit --fund=false
- name: Download signed Windows installer
uses: actions/download-artifact@v4
with:
name: signed-windows-installer-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: signed_windows
- name: Download signed unpacked Windows build
uses: actions/download-artifact@v4
with:
name: signed-windows-unpacked-${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
path: dist
- name: Stage signed installer and recreate update metadata
shell: pwsh
run: |
$packageVersion = "${{ needs.prepare-release.outputs.PACKAGE_VERSION }}"
if (-not $packageVersion) {
throw "Package version is not available."
}
$distRoot = "dist"
New-Item -ItemType Directory -Force -Path $distRoot | Out-Null
$distRoot = (Resolve-Path $distRoot).Path
$artifactRoot = (Resolve-Path "signed_windows").Path
$expectedInstallerName = "GameSentenceMiner-Setup-$packageVersion.exe"
$distExePath = Join-Path $distRoot $expectedInstallerName
$signedExe = Get-ChildItem -Path $artifactRoot -Recurse -File -Filter $expectedInstallerName | Select-Object -First 1
if (-not $signedExe) {
$signedExe = Get-ChildItem -Path $artifactRoot -Recurse -File -Filter "*Setup*.exe" | Select-Object -First 1
}
if (-not $signedExe) {
throw "No signed installer found in signed_windows."
}
if ($signedExe.Name -ne $expectedInstallerName) {
throw "Signed installer name '$($signedExe.Name)' does not match expected '$expectedInstallerName'."
}
Copy-Item -Path $signedExe.FullName -Destination $distExePath -Force
$appBuilder = node -e "process.stdout.write(require('app-builder-bin').appBuilderPath)"
if (-not $appBuilder) {
throw "Unable to resolve app-builder path."
}
$blockmapOutput = "$distExePath.blockmap"
$blockmapJson = & $appBuilder blockmap --input "$distExePath" --output "$blockmapOutput"
if ($LASTEXITCODE -ne 0) {
throw "app-builder failed to generate blockmap."
}
$blockmapInfo = $blockmapJson | ConvertFrom-Json
$releaseDate = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
$latestYml = @(
"version: $packageVersion",
"files:",
" - url: $expectedInstallerName",
" sha512: $($blockmapInfo.sha512)",
" size: $($blockmapInfo.size)",
"path: $expectedInstallerName",
"sha512: $($blockmapInfo.sha512)",
"releaseDate: '$releaseDate'"
) -join "`n"
Set-Content -Path (Join-Path $distRoot "latest.yml") -Value $latestYml -Encoding utf8NoBOM
- name: Upload Windows release assets
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
target_commitish: ${{ github.sha }}
name: Release v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
body: ${{ needs.prepare-release.outputs.release_body }}
draft: false
prerelease: false
fail_on_unmatched_files: true
overwrite_files: true
append_body: false
files: |
dist/*Setup*.exe
dist/GameSentenceMiner-*-win-unpacked.zip
dist/latest.yml
dist/*.blockmap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-linux:
runs-on: ubuntu-latest
needs:
- prepare-release
- create-release
if: always() && needs.prepare-release.outputs.should_build_linux == 'true' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped')
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
cache: npm
cache-dependency-path: |
package-lock.json
GSM_Overlay/package-lock.json
- name: Cache Electron binaries
uses: actions/cache@v4
with:
path: |
~/.cache/electron
~/.cache/electron-builder
key: ${{ runner.os }}-electron-cache-${{ hashFiles('package-lock.json', 'GSM_Overlay/package-lock.json') }}
restore-keys: |
${{ runner.os }}-electron-cache-
- name: Fetch prebuilt overlay server (Linux)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p GSM_Overlay/input_server/bin/linux
gh release download overlay-server --repo ${{ github.repository }} --pattern gsm_overlay_server-linux --output GSM_Overlay/input_server/bin/linux/gsm_overlay_server --clobber
chmod +x GSM_Overlay/input_server/bin/linux/gsm_overlay_server
- name: Build Overlay
working-directory: ./GSM_Overlay
run: |
npm ci --prefer-offline --no-audit --fund=false
npm run package
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --fund=false
- name: Build Electron app for Linux
run: npm run app:dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Linux release assets
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
target_commitish: ${{ github.sha }}
name: Release v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
body: ${{ needs.prepare-release.outputs.release_body }}
draft: false
prerelease: false
fail_on_unmatched_files: true
overwrite_files: true
append_body: false
files: |
dist/*.AppImage
dist/latest-linux.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-mac-arm64:
runs-on: macos-15
needs:
- prepare-release
- create-release
if: always() && needs.prepare-release.outputs.should_build_mac_arm64 == 'true' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped')
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
cache: npm
cache-dependency-path: |
package-lock.json
GSM_Overlay/package-lock.json
- name: Cache Electron binaries
uses: actions/cache@v4
with:
path: |
~/.cache/electron
~/.cache/electron-builder
key: ${{ runner.os }}-electron-cache-${{ hashFiles('package-lock.json', 'GSM_Overlay/package-lock.json') }}
restore-keys: |
${{ runner.os }}-electron-cache-
- name: Fetch prebuilt overlay server (macOS)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p GSM_Overlay/input_server/bin/darwin
gh release download overlay-server --repo ${{ github.repository }} --pattern gsm_overlay_server-darwin --output GSM_Overlay/input_server/bin/darwin/gsm_overlay_server --clobber
chmod +x GSM_Overlay/input_server/bin/darwin/gsm_overlay_server
- name: Build Overlay
working-directory: ./GSM_Overlay
run: |
npm ci --prefer-offline --no-audit --fund=false
npm run package
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --fund=false
- name: Build Electron app for macOS arm64
run: npm run app:dist -- --mac --arm64 -c.mac.artifactName='${productName}-${version}-${arch}.${ext}'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Copy arm64 update manifest
run: cp dist/latest-mac.yml dist/latest-mac-arm64.yml
- name: Upload macOS arm64 release assets
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
target_commitish: ${{ github.sha }}
name: Release v${{ needs.prepare-release.outputs.PACKAGE_VERSION }}
body: ${{ needs.prepare-release.outputs.release_body }}
draft: false
prerelease: false
fail_on_unmatched_files: true
overwrite_files: true
append_body: false
files: |
dist/*-arm64.dmg
dist/latest-mac.yml
dist/latest-mac-arm64.yml
dist/*-arm64.dmg.blockmap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
report-skipped-build:
runs-on: ubuntu-latest
needs:
- prepare-release
if: needs.prepare-release.outputs.should_build_any == 'false'
permissions:
contents: read
steps:
- name: Report skipped build
run: |
echo "Build and upload skipped because the release already exists and all expected assets are present."