clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

build: execute native package acceptance

Dami committed b9c8330
Install or unpack each desktop package on its native runner, verify OS association metadata, launch it with a freshly built Sample Region Atlas, and require byte-exact intake into an isolated library.

Fix Windows GUI-installer exit handling and ad-hoc seal unsigned macOS preview bundles after assembly.
commit
b9c833060db74c2aa673e925aa878dc8a32ed82f
parent
0deaa6862cd1767f1816d55574da11125d00f73b
author
Dami <dami.etoile@gmail.com>
8 files changed, +215 -13
M.github/workflows/release.yml19++++++----
Mdocs/releasing.md11+++++-
Mdocs/testing.md9+++--
Mscripts/release/sign-macos.sh10+++++-
Ascripts/release/smoke-linux.sh55++++++++++++++++++++++++++++++
Ascripts/release/smoke-macos.sh53+++++++++++++++++++++++++++++
Ascripts/release/smoke-windows.ps153+++++++++++++++++++++++++++++
Mtests/release/test_packaging.py18++++++++++
M .github/workflows/release.yml +11 -8
@@ -232,6 +232,16 @@ jobs:
232232 shell: bash233233 run: ./scripts/release/package-desktop.sh linux-amd64 "${{ steps.metadata.outputs.version }}" Atlas dist234234 235+ - name: Smoke macOS package and native file intake236+ if: matrix.target == 'darwin-arm64'237+ shell: bash238+ run: ./scripts/release/smoke-macos.sh "dist/Atlas-${{ steps.metadata.outputs.version }}-darwin-arm64.zip" "${{ steps.metadata.outputs.version }}" "$GITHUB_WORKSPACE"239+240+ - name: Smoke Linux install, MIME registration, and native file intake241+ if: matrix.target == 'linux-amd64'242+ shell: bash243+ run: ./scripts/release/smoke-linux.sh "dist/Atlas-${{ steps.metadata.outputs.version }}-linux-amd64.deb" "${{ steps.metadata.outputs.version }}" "$GITHUB_WORKSPACE"244+235245 - name: Package Windows installer236246 if: matrix.target == 'windows-amd64'237247 shell: pwsh
@@ -248,14 +258,7 @@ jobs:
248258 - name: Smoke Windows install and .atlas association249259 if: matrix.target == 'windows-amd64'250260 shell: pwsh251- run: |252- $installer = "dist/Atlas-${{ steps.metadata.outputs.version }}-windows-amd64-setup.exe"253- & $installer /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /CURRENTUSER254- if ($LASTEXITCODE -ne 0) { throw "installer exited $LASTEXITCODE" }255- $association = (Get-Item "HKCU:\Software\Classes\.atlas").GetValue("")256- if ($association -ne "Atlas.Volume") { throw "unexpected .atlas association: $association" }257- $command = (Get-Item "HKCU:\Software\Classes\Atlas.Volume\shell\open\command").GetValue("")258- if ($command -notmatch 'Atlas\.exe.*%1') { throw "unexpected open command: $command" }261+ run: ./scripts/release/smoke-windows.ps1 -Installer "dist/Atlas-${{ steps.metadata.outputs.version }}-windows-amd64-setup.exe" -Version "${{ steps.metadata.outputs.version }}" -RepoRoot "${{ github.workspace }}"259262 260263 - uses: actions/upload-artifact@v4261264 with:
M docs/releasing.md +10 -1
M docs/testing.md +6 -3
M scripts/release/sign-macos.sh +9 -1
A scripts/release/smoke-linux.sh +55 -0
@@ -0,0 +1,55 @@
1+#!/usr/bin/env bash2+set -euo pipefail3+4+if [[ $# -ne 3 ]]; then5+ echo "usage: smoke-linux.sh <package.deb> <version> <repo-root>" >&26+ exit 27+fi8+9+package=$110+version=$211+root=$312+deb_version=${version#v}13+if [[ ! "$deb_version" =~ ^[0-9] ]]; then14+ deb_version="0~$deb_version"15+fi16+stage=$(mktemp -d)17+app_pid=""18+cleanup() {19+ if [[ -n "$app_pid" ]]; then kill "$app_pid" >/dev/null 2>&1 || true; fi20+ rm -rf "$stage"21+}22+trap cleanup EXIT23+24+sudo apt-get install -y "$package" xvfb dbus-x1125+dpkg-query -W -f='${Status}\n${Version}\n${Architecture}\n' atlas-desktop \26+ | grep -Fx 'install ok installed'27+dpkg-query -W -f='${Version}' atlas-desktop | grep -Fx "$deb_version"28+dpkg-query -W -f='${Architecture}' atlas-desktop | grep -Fx amd6429+test -x /usr/bin/Atlas30+test -f /usr/share/applications/dev.felinestatemachine.Atlas.desktop31+test -f /usr/share/mime/packages/dev.felinestatemachine.atlas.xml32+33+go run "$root/cmd/atlas" build -cache "$stage/cache" -bundles "$stage/source" \34+ "$root/examples/sample-region.atlas-project"35+artifact=$(find "$stage/source" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit)36+test -n "$artifact"37+[[ "$(xdg-mime query filetype "$artifact")" == application/vnd.felinestatemachine.atlas ]]38+gio mime application/vnd.felinestatemachine.atlas \39+ | grep -F 'dev.felinestatemachine.Atlas.desktop'40+41+mkdir -p "$stage/library" "$stage/data"42+ATLAS_BUNDLES_DIR="$stage/library" ATLAS_DATA_DIR="$stage/data" \43+ dbus-run-session -- xvfb-run -a /usr/bin/Atlas "$artifact" \44+ >"$stage/atlas.log" 2>&1 &45+app_pid=$!46+for _ in $(seq 1 60); do47+ if find "$stage/library" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit | grep -q .; then48+ break49+ fi50+ kill -0 "$app_pid" 2>/dev/null || { cat "$stage/atlas.log" >&2; exit 1; }51+ sleep 152+done53+installed=$(find "$stage/library" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit)54+test -n "$installed"55+cmp "$artifact" "$installed"
A scripts/release/smoke-macos.sh +53 -0
@@ -0,0 +1,53 @@
1+#!/usr/bin/env bash2+set -euo pipefail3+4+if [[ $# -ne 3 ]]; then5+ echo "usage: smoke-macos.sh <package.zip> <version> <repo-root>" >&26+ exit 27+fi8+9+package=$110+version=$211+root=$312+version_number=${version#v}13+mac_version=0.0.014+if [[ "$version_number" =~ ^[0-9]+(\.[0-9]+){0,2} ]]; then15+ mac_version=${BASH_REMATCH[0]}16+fi17+stage=$(mktemp -d)18+app_pid=""19+cleanup() {20+ if [[ -n "$app_pid" ]]; then kill "$app_pid" >/dev/null 2>&1 || true; fi21+ rm -rf "$stage"22+}23+trap cleanup EXIT24+25+ditto -x -k "$package" "$stage/unpacked"26+app="$stage/unpacked/Atlas.app"27+executable="$app/Contents/MacOS/Atlas"28+test -x "$executable"29+codesign --verify --deep --strict --verbose=2 "$app"30+[[ "$(plutil -extract CFBundleIdentifier raw "$app/Contents/Info.plist")" == dev.felinestatemachine.atlas ]]31+[[ "$(plutil -extract CFBundleShortVersionString raw "$app/Contents/Info.plist")" == "$mac_version" ]]32+plutil -extract UTExportedTypeDeclarations.0.UTTypeIdentifier raw "$app/Contents/Info.plist" \33+ | grep -Fx dev.felinestatemachine.atlas.volume34+"$executable" --version | grep -Fx "Atlas $version"35+36+go run "$root/cmd/atlas" build -cache "$stage/cache" -bundles "$stage/source" \37+ "$root/examples/sample-region.atlas-project"38+artifact=$(find "$stage/source" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit)39+test -n "$artifact"40+mkdir -p "$stage/library" "$stage/data"41+ATLAS_BUNDLES_DIR="$stage/library" ATLAS_DATA_DIR="$stage/data" \42+ "$executable" "$artifact" >"$stage/atlas.log" 2>&1 &43+app_pid=$!44+for _ in $(seq 1 60); do45+ if find "$stage/library" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit | grep -q .; then46+ break47+ fi48+ kill -0 "$app_pid" 2>/dev/null || { cat "$stage/atlas.log" >&2; exit 1; }49+ sleep 150+done51+installed=$(find "$stage/library" -maxdepth 1 -type f -name 'sample-region-*.atlas' -print -quit)52+test -n "$installed"53+cmp "$artifact" "$installed"
A scripts/release/smoke-windows.ps1 +53 -0
@@ -0,0 +1,53 @@
1+param(2+ [Parameter(Mandatory = $true)][string]$Installer,3+ [Parameter(Mandatory = $true)][string]$Version,4+ [Parameter(Mandatory = $true)][string]$RepoRoot5+)6+7+$ErrorActionPreference = "Stop"8+$stage = Join-Path $env:RUNNER_TEMP ("atlas-windows-smoke-" + [Guid]::NewGuid().ToString("N"))9+New-Item -ItemType Directory -Path $stage | Out-Null10+$app = $null11+12+try {13+ $install = Start-Process -FilePath $Installer -ArgumentList '/VERYSILENT', '/SUPPRESSMSGBOXES', '/NORESTART', '/CURRENTUSER' -Wait -PassThru14+ if ($install.ExitCode -ne 0) { throw "installer exited $($install.ExitCode)" }15+16+ $association = (Get-Item "HKCU:\Software\Classes\.atlas").GetValue("")17+ if ($association -ne "Atlas.Volume") { throw "unexpected .atlas association: $association" }18+ $contentType = (Get-Item "HKCU:\Software\Classes\.atlas").GetValue("Content Type")19+ if ($contentType -ne "application/vnd.felinestatemachine.atlas") { throw "unexpected content type: $contentType" }20+ $command = (Get-Item "HKCU:\Software\Classes\Atlas.Volume\shell\open\command").GetValue("")21+ if ($command -notmatch '^"([^"]+Atlas\.exe)" "%1"$') { throw "unexpected open command: $command" }22+ $executable = $Matches[1]23+ if (-not (Test-Path $executable -PathType Leaf)) { throw "installed executable missing: $executable" }24+25+ $cache = Join-Path $stage "cache"26+ $source = Join-Path $stage "source"27+ & go run "$RepoRoot/cmd/atlas" build -cache $cache -bundles $source "$RepoRoot/examples/sample-region.atlas-project"28+ if ($LASTEXITCODE -ne 0) { throw "Sample Region fixture build exited $LASTEXITCODE" }29+ $artifact = Get-ChildItem $source -Filter "sample-region-*.atlas" | Select-Object -First 130+ if ($null -eq $artifact) { throw "Sample Region fixture was not built" }31+32+ $library = Join-Path $stage "library"33+ $data = Join-Path $stage "data"34+ New-Item -ItemType Directory -Path $library, $data | Out-Null35+ $env:ATLAS_BUNDLES_DIR = $library36+ $env:ATLAS_DATA_DIR = $data37+ $app = Start-Process -FilePath $executable -ArgumentList $artifact.FullName -PassThru38+ $installed = $null39+ foreach ($attempt in 1..60) {40+ $installed = Get-ChildItem $library -Filter "sample-region-*.atlas" -ErrorAction SilentlyContinue | Select-Object -First 141+ if ($null -ne $installed) { break }42+ if ($app.HasExited) { throw "installed Atlas exited before file intake with $($app.ExitCode)" }43+ Start-Sleep -Seconds 144+ }45+ if ($null -eq $installed) { throw "installed Atlas did not intake Sample Region" }46+ if ((Get-FileHash $artifact.FullName -Algorithm SHA256).Hash -ne (Get-FileHash $installed.FullName -Algorithm SHA256).Hash) {47+ throw "installed Atlas changed the handed-off artifact"48+ }49+}50+finally {51+ if ($null -ne $app -and -not $app.HasExited) { Stop-Process -Id $app.Id -Force }52+ if (Test-Path $stage) { Remove-Item -Recurse -Force $stage }53+}
M tests/release/test_packaging.py +18 -0
@@ -62,6 +62,10 @@ class NativePackagingTest(unittest.TestCase):
6262 self.assertIn("package-desktop.sh", workflow)6363 self.assertIn("sign-windows.ps1", workflow)6464 self.assertIn("package-smoke.sh", workflow)65+ self.assertIn("smoke-macos.sh", workflow)66+ self.assertIn("smoke-linux.sh", workflow)67+ self.assertIn("smoke-windows.ps1", workflow)68+ self.assertNotIn("& $installer /VERYSILENT", workflow)6569 mac_package = (ROOT / "scripts/release/package-desktop.sh").read_text()6670 self.assertIn("sign-macos.sh", mac_package)6771 self.assertIn("stage=$(mktemp -d)", mac_package)
@@ -71,6 +75,20 @@ class NativePackagingTest(unittest.TestCase):
7175 self.assertIn('--keychain "$keychain"', mac_signer)7276 self.assertNotIn("security list-keychains", mac_signer)7377 self.assertIn('xcrun stapler validate "$app"', mac_signer)78+ self.assertIn('codesign --force --deep --sign - "$app"', mac_signer)79+80+ windows_smoke = (ROOT / "scripts/release/smoke-windows.ps1").read_text()81+ self.assertIn("Start-Process -FilePath $Installer", windows_smoke)82+ self.assertIn("$install.ExitCode", windows_smoke)83+ self.assertIn("application/vnd.felinestatemachine.atlas", windows_smoke)84+ linux_smoke = (ROOT / "scripts/release/smoke-linux.sh").read_text()85+ self.assertIn("dpkg-query", linux_smoke)86+ self.assertIn("xdg-mime query filetype", linux_smoke)87+ self.assertIn("ATLAS_BUNDLES_DIR", linux_smoke)88+ mac_smoke = (ROOT / "scripts/release/smoke-macos.sh").read_text()89+ self.assertIn("codesign --verify --deep --strict", mac_smoke)90+ self.assertIn("dev.felinestatemachine.atlas.volume", mac_smoke)91+ self.assertIn("ATLAS_BUNDLES_DIR", mac_smoke)7492 7593 def test_release_asset_allowlist_is_exact(self) -> None:7694 version = "v1.2.3"