Replace deprecated altool (removed in Xcode 14) with xcodebuild
-exportArchive using App Store Connect API authentication.
Changes:
- upload_testflight.sh: Use xcodebuild with destination=upload
and API key authentication instead of altool
- ios_testflight.yml: Pass xcarchive path and use new API key secrets
Required new GitHub secrets:
- APP_STORE_CONNECT_API_KEY_ID
- APP_STORE_CONNECT_API_ISSUER_ID
- APP_STORE_CONNECT_API_KEY (contents of .p8 file)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1. Speed up check_build_deps.sh:
- Cache expensive `bazel query deps(...)` results
- Reuse cached deps for all three checks instead of running
separate bazel query commands
- Use grep filtering on cached results instead of bazel intersect
2. Run lint job in parallel with test job:
- Split bazel_test.yml into separate lint and test jobs
- Jobs run concurrently, reducing total CI time
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Bazel 8 changed module extension repository naming from `~~` and `~` to
`++` and `+`. Update workflows to find crane dynamically instead of
using hardcoded paths that break with Bazel version changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Eagle and admin server use different S3 buckets:
- Eagle: eagle0 bucket (game data) - uses DO_SPACES_* secrets
- Admin: eagle0-assets bucket (What's New) - uses ACCESS_KEY_ID/SECRET_KEY secrets
The previous change (#5847) broke Eagle by giving it credentials that only
have access to eagle0-assets. This fix:
- Reverts Eagle to use DO_SPACES_* secrets (eagle0 bucket access)
- Adds ADMIN_S3_* env vars for admin server (eagle0-assets bucket access)
- Admin container receives ADMIN_S3_* values as DO_SPACES_* env vars
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The docker_build workflow was using DO_SPACES_ACCESS_KEY/SECRET_KEY
secrets which don't have write access. Use ACCESS_KEY_ID/SECRET_KEY
instead, which are the same secrets that work for CI builds.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The existing cleanup only removed dangling images. Now also removes
images older than 24h to prevent disk space exhaustion while keeping
recent images available for rollback.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add stale PR refs cleanup to iOS CI workflows
Self-hosted runners persist .git between runs. When a PR is updated,
old local refs may point to commits that were never fetched, causing
"Could not scan for Git LFS files" errors.
Applied to:
- ios_addressables_build.yml
- ios_testflight.yml
(Same fix already present in mac_build.yml and unity_build.yml)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fetch LFS after checkout to avoid stale ref issues
Move LFS fetching out of actions/checkout and into a separate step.
This ensures our stale PR refs cleanup runs before any LFS operations,
avoiding "Could not scan for Git LFS files" errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix LFS fetch order in mac_build and unity_build workflows
Apply the same fix as ios_* workflows: set lfs: false in checkout
and fetch LFS files manually afterward to avoid stale ref issues.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Notify connected clients when a new version is available after CI deploy.
Flow:
1. CI deploys new build and waits 60s for CDN cache
2. CI calls admin server HTTP endpoint with shared secret
3. Admin server calls Eagle via gRPC
4. Eagle broadcasts to all connected lobby users
Components:
- Proto: ClientUpdateAvailable message, NotifyClientUpdate RPC
- Eagle: notifyClientUpdate() broadcasts to lobby users
- Admin server: /notify-update HTTP endpoint with secret auth
- CI: Notify steps in mac_build.yml and unity_build.yml
- Docker: NOTIFY_SECRET env var passed to admin container
Client-side handling will be added in a follow-up PR.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add git corruption detection to Unity CI workflows
Self-hosted runners can accumulate corrupted git state over time.
Add a pre-checkout step that runs git fsck and nukes the repo if
corruption is detected, allowing a fresh clone.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI git corruption: check target ref objects instead of full fsck
Self-hosted runners can have stale refs (e.g., refs/remotes/pull/*/merge)
pointing to commits whose objects weren't fetched when the PR was updated.
Running git fsck is too broad and slow. Instead, check if the specific
target ref's objects are complete before checkout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI: prune stale PR refs instead of checking all objects
Self-hosted runners persist .git between runs. When a PR is updated,
actions/checkout doesn't prune old local refs like refs/remotes/pull/*/merge.
These stale refs may point to commits whose objects were never fetched,
causing "missing object" errors during checkout.
Fix by explicitly removing PR refs before checkout. This is faster and
more targeted than validating object completeness.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The warmup binary was being copied from a hardcoded bazel-bin path that
doesn't work reliably with cross-compilation and remote caching. When the
binary was cached but not materialized locally, the cp command would fail.
Fix by:
- Create a warmup_tar pkg_tar target that packages the warmup binary
- Use tar extraction instead of cp, which forces Bazel to materialize
the output file before the command runs
- Rename the binary from warmup_linux_amd64 to warmup in the tar
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add eagle0.net as server name alias
Allows accessing the site via eagle0.net in addition to prod.eagle0.net.
Note: After deploying, run certbot to add eagle0.net to the SSL certificate:
certbot --nginx -d prod.eagle0.net -d eagle0.net
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add admin container image verification in deploy workflow
The admin container was running a stale image after the entrypoint was
changed from /app/admin_server to /app/admin_server_linux_amd64. This
happened because:
1. crane pull + docker load doesn't update the :latest tag locally
2. docker-compose fallback to :latest used the old cached image
3. --force-recreate recreated the container but with the old image
Fixes:
- Tag pulled admin image as :latest after docker load (ensures fallback
uses correct image)
- Add image digest verification after deploy (fails early if wrong
image is running)
- Add admin startup verification in deploy-blue-green.sh (catches
container crashes with helpful logs)
This follows the same pattern used in auth_build.yml for auth service
verification.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Automate Unity version sync and CI installation
1. UnityVersionSync.cs: Editor script that automatically updates
ci/unity_version.sh when the project is opened in a newer Unity version.
This runs on project load via [InitializeOnLoad].
2. ensure_unity_installed.sh: CI script that checks if the required Unity
version is installed and attempts to install it via Unity Hub CLI if not.
Added to all Unity build workflows.
3. Updated workflow paths to trigger on ci/unity_version.sh changes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add platform-specific Unity modules and concurrent install safety
- Pass platform argument (mac/windows/ios) to ensure_unity_installed.sh
- Add lock file mechanism to prevent concurrent Unity installations
- Install only needed modules per platform (mac-il2cpp, windows-mono, ios)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Read Unity version from ProjectVersion.txt instead of manual file
- Remove ci/unity_version.sh (manually maintained)
- Remove Assets/Editor/UnityVersionSync.cs (auto-sync script)
- Update ensure_unity_installed.sh to parse ProjectVersion.txt directly
- Unity automatically maintains ProjectVersion.txt, no manual step needed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update all build scripts to read Unity version from ProjectVersion.txt
- build_windows.sh
- build_unity_ios.sh
- build_ios_addressables.sh
- build_mac.sh
- ensure_unity_installed.sh (fix comment)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add weekly Bazel cache cleanup workflow
Removes Bazel output bases not accessed in 7 days to prevent disk
space accumulation on self-hosted runners. Keeps 'cache' and 'install'
directories. Remote cache means minimal perf impact from clearing
local output bases.
Runs weekly on Sunday at 00:00 UTC, can also be triggered manually.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use bazel clean instead of find-based cleanup
The previous approach looked for output bases not accessed in 7 days,
but since the runner is used regularly, those directories always have
recent access times and would never be cleaned up.
Using `bazel clean` weekly clears the local output base, freeing disk
space. The next build will be slightly slower but will pull from remote
cache.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
All four workflows that use EAGLE0_BUILD_DIR were creating build
directories at /tmp/eagle0-{run_id} but never cleaning them up,
causing disk space accumulation on self-hosted runners.
Added `rm -rf "$EAGLE0_BUILD_DIR"` cleanup step with `if: always()`
to ensure cleanup runs even if the build fails.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1. Don't upload IPA artifact after TestFlight upload - it's redundant
and takes 150MB per build. Only keep artifact when skipping upload
(for debugging), with 1-day retention.
2. Fix "broken pipe" error in artifact storage check by writing to temp
file instead of piping through sort | head (which causes SIGPIPE).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Mac codesign by isolating build keychain during signing
The --keychain flag alone doesn't prevent codesign from finding matching
identities in other keychains on the search list. Fix by temporarily
setting ONLY the build keychain as the search list during signing, then
restoring the original list on exit.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Include System.keychain for Apple root certificates
The previous fix broke certificate chain verification because we removed
the login keychain but also lost access to Apple's root certificates.
Include System.keychain (which has Apple roots) but not login.keychain
(which has the duplicate signing identity).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use certificate SHA-1 hash instead of name to avoid ambiguity
Instead of manipulating the keychain search list (which breaks certificate
chain verification), extract the SHA-1 hash of the specific certificate
from the build keychain and use that as the signing identity. SHA-1 hashes
are unambiguous and codesign will use the exact certificate specified.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix certificate hash extraction - get first valid identity
The previous grep for SIGNING_IDENTITY failed because GitHub Actions
masks the value. Instead, get the first valid codesigning identity from
the keychain (there should only be one since we just imported it).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use full keychain path for find-identity
security find-identity requires the full path to the keychain file,
not just the keychain name. Resolve the full path from list-keychains
output before querying for identities.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add debug output to certificate import and remove set-keychain-settings
Add debug output to see what identities are available after import.
Also remove the set-keychain-settings line which may be causing issues
(the -u flag locks keychain on sleep).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Search all keychains for identity hash
The build keychain import isn't creating a recognizable codesigning
identity, but the certificate exists in login.keychain. Search all
keychains and use the hash of the first valid identity - hashes are
unique and unambiguous regardless of which keychain contains them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The Mac build was failing with errSecInternalComponent because the newly
created keychain wasn't added to the search list. Without this step,
codesign cannot find the certificate.
Changes:
- Add keychain to search list with security list-keychains
- Add -T /usr/bin/security to import command for completeness
- Set 1-hour keychain timeout for signing large app bundles
This matches the keychain setup used in the iOS TestFlight workflow.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When multiple runners on the same machine try to sign simultaneously,
they were conflicting on the shared keychain names (build.keychain,
ios-build.keychain). This caused errSecInternalComponent errors.
Changes:
- mac_build.yml: Use build-${run_id}.keychain
- ios_testflight.yml: Use ios-build-${run_id}.keychain
- codesign_mac_app.sh: Support KEYCHAIN_NAME env var with fallback
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add EAGLE0_BUILD_DIR environment variable to workflows, defaulting to
/tmp/eagle0-${github.run_id}. This allows multiple runners with the
unity-mac label to run builds in parallel without path conflicts.
Changes:
- Workflows set EAGLE0_BUILD_DIR based on run ID
- Build scripts use EAGLE0_BUILD_DIR with fallback to /tmp/eagle0
- Library cache remains shared (beneficial for build speed)
To add a second parallel runner:
1. Set up a new runner with the same 'unity-mac' label
2. GitHub Actions will automatically distribute jobs to available runners
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The cleanup job needs this permission to delete intermediate
xcode-project artifacts. Without it, the gh api DELETE call
fails with HTTP 403.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Reuse the same credentials already used for Mac notarization instead
of requiring a separate App Store Connect API key.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- New workflow_dispatch-only workflow for iOS TestFlight builds
- build_unity_ios.sh: Builds Unity iOS player (generates Xcode project)
- archive_ios.sh: Archives and exports IPA using xcodebuild
- upload_testflight.sh: Uploads IPA to TestFlight via App Store Connect API
Required secrets:
- IOS_CERTIFICATE: Apple Distribution certificate (.p12, base64)
- IOS_CERTIFICATE_PWD: Certificate password
- IOS_PROVISIONING_PROFILE: App Store provisioning profile (base64)
- APP_STORE_CONNECT_API_KEY: JSON with key_id, issuer_id, and key
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Only trigger on changes to:
- AddressableAssetsData config
- Music assets (the addressable content)
- BuildScript.cs
- Build scripts
Previously triggered on any Unity file change, which was too broad.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add iOS addressables build workflow
Creates a separate workflow to build and upload iOS addressables to CDN.
This is needed for iOS clients to load remote assets.
- Adds build_ios_addressables.sh script
- Adds ios_addressables_build.yml workflow
- Uploads to https://assets.eagle0.net/addressables/iOS/
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix iOS addressables build: add proto build step
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Usage:
eagle-logs # Tail logs (follow mode)
eagle-logs -n 100 # Show last 100 lines and follow
eagle-logs --no-follow -n 50 # Last 50 lines without following
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Remove sudo symlink creation (deploy user lacks passwordless sudo)
- Simplify eagle-exec to read from /opt/eagle0/.active-instance file
- Update deploy-blue-green.sh to write active instance to file
- Keep fallback to checking running containers if file doesn't exist
Users can add their own alias:
alias eagle-exec='/opt/eagle0/scripts/eagle-exec.sh'
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a helper script that automatically runs docker exec against the
active Eagle instance (blue or green), determined by checking nginx
config.
Usage:
eagle-exec printenv GEMINI_API_KEY
eagle-exec jcmd 1 VM.flags
eagle-exec sh
The script is deployed to /opt/eagle0/scripts/ and symlinked to
/usr/local/bin/eagle-exec for easy access.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add ANTHROPIC_API_KEY to docker-compose environment
Required for Claude LLM provider to work in production.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add ANTHROPIC_API_KEY to deployment workflow
Pass the secret to the deploy job and export it for docker-compose.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The installer is built by installer_build.yml, not the Docker workflow.
Adding exclusion prevents unnecessary Docker builds when only installer
files change.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Fix genrule variable syntax for manifest public key
Genrules use Make variable syntax $(VAR), not curly brace syntax {VAR}.
The curly brace syntax only works in x_defs for go_binary rules.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use --action_env to pass MANIFEST_PUBLIC_KEY to genrule
Bazel genrules don't support workspace status variable substitution
in cmd strings. Instead, use --action_env to pass the environment
variable into the sandbox, where it can be referenced as a regular
shell variable ($$MANIFEST_PUBLIC_KEY in the genrule cmd).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Handle missing MANIFEST_PUBLIC_KEY env var in genrule
When building via 'bazel test //src/main/go/...', the genrule runs
without --action_env=MANIFEST_PUBLIC_KEY set. Use bash default value
syntax ${VAR:-} to default to empty string when the env var isn't set,
allowing the build to succeed with an empty public key.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Simplify public key handling - let empty var expand naturally
Bazel doesn't support ${VAR:-} syntax in genrule cmd. Instead, just
use $MANIFEST_PUBLIC_KEY directly - if not set, it expands to empty
string which the installer handles gracefully.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add manual tag to exclude webview installer from wildcard builds
The webview installer genrule requires --action_env=MANIFEST_PUBLIC_KEY
to be set. Adding tags = ["manual"] excludes it from wildcard patterns
like "bazel test //src/main/go/..." so it won't fail in the test workflow.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Rename WebView installer output from EagleInstallerWebView.exe to Eagle0.exe
- Update CI workflow to build and deploy Eagle0.exe
- Update all references in authservice (download URLs, batch scripts, HTML)
- Update installer_build_handler default key and version content
- Update help text and versioninfo.json metadata
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove continue-on-error from "Run tests" step so GitHub UI expands
the actual failing step instead of "Fail if tests failed".
The log collection and artifact upload steps use `if: always()` so
they still run after test failures.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove .NET installer build and old manifest support. The Go installer is
now the only version built and deployed. Both the installer and Unity
builds now update only the v2 manifest at installer/v2/eagle0_manifest.txt.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Two issues fixed:
1. Unity build workflow now updates BOTH manifests (old and v2)
- The v2 manifest was missing all game file hashes because only the
installer workflow was updating it
- Now unity_build.yml calls manifest_manager with both "unity3d" and
"unity3d-v2" to update both manifests
2. Go installer now verifies game exists before claiming "up to date"
- Previously, if manifests matched but files didn't exist, it would
claim success and try to launch a non-existent game
- Now checks if eagle0.exe exists; if not, forces redownload
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add console UI with progress bar to Go installer
Adds a polished console-based UI to the Go installer with:
- Box-drawn banner header
- Status messages with ► prefix
- Success messages with ✓ prefix
- Error messages with ✗ prefix
- Warning messages with ⚠ prefix
- Progress bar with █ (filled) and ░ (empty) characters
- File count display during downloads
- Elapsed time on completion
The UI uses pure Go (no external dependencies) so the binary
size remains at 5.4MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add application icon to Go installer
- Copy eagle0.ico from .NET installer
- Add versioninfo.json for goversioninfo configuration
- Generate resource_windows.syso with embedded icon and version info
- Update BUILD.bazel to include the .syso resource in Windows build
Binary size increased from 5.4MB to 5.5MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Zone.Identifier removal in .NET installer
File.Exists() doesn't work with Windows Alternate Data Streams (ADS) -
it always returns false. Changed to unconditionally attempt deletion
and ignore errors if the ADS doesn't exist.
This fixes the self-update mechanism where SmartScreen was blocking
the new installer because Zone.Identifier was never actually removed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Two-stage manifest for clean installer upgrade path
Old .NET installers have a bug where Zone.Identifier removal fails,
blocking SmartScreen from allowing the Go installer to run. This change
creates a two-stage upgrade path:
1. OLD manifest (installer/eagle0_manifest.txt) -> points to .NET installer
- Old .NET installers download new .NET installer (same format, works)
2. NEW manifest (installer/v2/eagle0_manifest.txt) -> points to Go installer
- New .NET installer checks v2 manifest, downloads Go installer
- Go installer also checks v2 manifest for future updates
Changes:
- .NET installer now checks installer/v2/ manifest path
- Go installer now checks installer/v2/ manifest path
- manifest_manager supports "installer-v2" section for v2 manifest
- CI workflow updates both manifests appropriately
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The CI runner doesn't have s3cmd installed. Modified installer_build_handler
to accept an optional second argument for the S3 key, allowing it to upload
the Go installer to installer/EagleInstallerMini.exe.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove Zone.Identifier to fix Windows SmartScreen blocking
When the installer downloads a new version and launches it programmatically,
Windows SmartScreen blocks the execution because the file has a "Mark of the
Web" (Zone.Identifier alternate data stream) indicating it was downloaded
from the internet.
Remove the Zone.Identifier after download to allow the new installer to
launch without being blocked.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zone.Identifier fix to .NET installer, deploy both versions
- Add Zone.Identifier removal to .NET installer's download logic
- Update CI to build both .NET and Go installers
- Deploy .NET installer to installer/EagleInstaller.exe
- Deploy Go installer to installer/EagleInstallerMini.exe
- Manifest points to EagleInstallerMini.exe (Go version)
Flow for existing users:
1. Old .NET installer → auto-updates to fixed .NET installer
2. Fixed .NET installer → downloads EagleInstallerMini.exe, strips Zone.Identifier, updates to Go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Rewrite Windows installer in Go (73MB -> 5.4MB)
Rewrites the Eagle0 Windows installer from C#/.NET to Go, reducing the
binary size from 73MB to 5.4MB (93% reduction).
The Go implementation provides the same functionality:
- Downloads and verifies game files via manifest
- Ed25519 signature verification for manifest
- SHA256 verification for all downloaded files
- Parallel downloads (8 concurrent slots)
- Self-update mechanism for installer updates
- Console-based progress output
- Invitation code support
Changes:
- Add Go installer source code (main.go, updater.go, config.go)
- Add Bazel BUILD file with Windows cross-compilation target
- Update CI workflow to build Go binary instead of .NET
- Add MANIFEST_PUBLIC_KEY to workspace status for build-time injection
The installer is cross-compiled for Windows from Mac using Bazel.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI permission error when copying installer output
Remove any existing installer-output directory before copying
to avoid permission denied errors from previous runs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add upload_addressables.sh script for Addressables bundles
- Add upload step to Mac and Windows build workflows
- Update mac_build_handler to use eagle0-assets bucket
- Update unity3d_windows_build_handler to use eagle0-assets bucket
- Update installer_build_handler to use eagle0-assets bucket
- Update manifest_manager to use eagle0-assets bucket
All uploads now go to eagle0-assets bucket, accessible via assets.eagle0.net.
Old eagle0-windows bucket can be deleted after DNS TTL expires (~1 week).
Addressables uploaded to:
- https://assets.eagle0.net/addressables/StandaloneOSX/
- https://assets.eagle0.net/addressables/StandaloneWindows64/
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Use run-specific artifact names (signed-mac-app-{run_id}, notarized-mac-app-{run_id})
to prevent race conditions where a previous run's cleanup job might delete
artifacts from a concurrent run.
Root cause: When PR #5465 merged, the previous run (which used the old workflow
that deleted ALL artifacts by name) ran its cleanup job between when the new
run uploaded its artifact (06:13:20) and when it tried to download it (06:18:27).
Fix: Include the GitHub run ID in artifact names so each run's artifacts are
uniquely named and cleanup jobs can only delete their own artifacts.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Eagle server build to CI
Add eagle_build.yml workflow that builds the Eagle server when Scala
or Eagle proto files change. This ensures build failures in the Eagle
server are caught during PR review, similar to how shardok_build.yml
works for the C++ Shardok server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix unused dependencies and simplify GameAdminServiceImpl
- Remove unused json4s and settings_loader deps from :service target
- Simplify GameAdminServiceImpl since GamesManager now returns
game_admin proto types directly (no conversion needed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Read version from the built app's Info.plist instead of calculating
from git commit count at deploy time. This ensures the appcast version
always matches what's actually in the app bundle.
Previously, the deploy step used `git rev-list --count HEAD` which could
differ from the version baked into the Unity build if commits were added
between the build and deploy steps.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add styled DMG with arrow pointing to Applications
- Add create-dmg as a bazel dependency for creating styled DMG installers
- Add background image with arrow pointing from app to Applications
- Update mac_build_handler to use create-dmg for styled DMG creation
- Update CI workflow to use the new DMG creation process
The DMG now shows a visual arrow guiding users to drag the app
to the Applications folder.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add --skip-jenkins flag for headless CI environment
The AppleScript that positions icons times out in CI environments
without a GUI session. Using --skip-jenkins skips the Finder styling
while still including the background image and Applications link.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Switch from create-dmg to dmgbuild for CI compatibility
dmgbuild generates .DS_Store files programmatically without needing
AppleScript or Finder access, making it work in headless CI environments.
- Remove create-dmg bazel dependency (relied on AppleScript)
- Use dmgbuild Python tool instead (pip install dmgbuild)
- Generates proper icon positions and background without GUI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix DMG styling: lighter background, adjust icon positions
- Use light gray background so file/folder names are readable
- Move arrow up to align with icon centers
- Move Applications link further right (500 -> 520) to clear the arrow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix arrow: move left, connect head to shaft properly
- Arrow now starts at x=200 (closer to app icon)
- Arrow ends at x=460 (further from Applications at x=520)
- Head and shaft now overlap for proper connection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Runs every 6 hours to check total artifact storage. Fails if storage
exceeds 500 MB and lists the largest artifacts for debugging.
Steady-state should be ~50 MB (logs and test results). This acts as
a safety net in case cleanup jobs fail to run properly.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The cleanup step was deleting ALL signed-mac-app artifacts across the
entire repo, not just the current run's. When two main builds ran
concurrently, one build's cleanup would delete the other's artifacts.
Fix: Change cleanup to only delete the current run's artifacts using
the runs/$run_id/artifacts API endpoint instead of deleting all
artifacts matching the name.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a step to the bazel_test workflow that runs check_build_deps.sh
in strict mode. This enforces architectural boundaries:
- src/main should not depend on src/test
- library/ proto dependencies should not exceed baseline
Runs before tests so failures are caught early.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add retention-days to all GitHub Actions artifacts
- Use retention-days: 1 for artifacts deployed to external storage
(sysroot, installer, mac app builds)
- Use retention-days: 3 for debug logs and test results
Prevents artifact storage quota from being exceeded.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete Mac build artifacts after successful deploy
Automatically deletes signed-mac-app and notarized-mac-app artifacts
after deployment completes. These ~250MB artifacts are only needed to
pass the app between workflow jobs; once deployed, they're redundant.
This prevents artifact storage from accumulating even if retention-days
doesn't expire them quickly enough.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete ALL old build artifacts after deploy (not just current run)
Both Mac and Windows installer workflows now delete ALL artifacts
with their respective names after successful deployment:
- Mac: signed-mac-app, notarized-mac-app
- Windows: eagle-installer
This ensures no artifact buildup even from failed/stale runs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Increase retention for test/build logs to 7 days
These are small files useful for debugging, so keep them longer:
- test.json (~100KB each)
- editor_win.log / editor_mac.log (~60KB each)
- failed-test-logs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use 5-day retention to match repository maximum
The repository has a 5-day maximum retention policy configured.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete old Mac builds from DigitalOcean when pruning appcast
The appcast keeps the last 10 versions, but the old DMG files were never
deleted from S3. Now when items are removed from the appcast, the
corresponding DMG files are also deleted from DigitalOcean Spaces.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add cleanup job that runs even when build fails
Previously, artifact cleanup only happened in the deploy job, which
doesn't run if earlier jobs fail. This left behind large artifacts
from failed builds, eventually hitting the storage quota.
The new cleanup job runs on ubuntu-latest with `if: always()` so it
executes regardless of whether build-and-sign, wait-notarization,
or deploy succeeded or failed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>