mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
iOS: give CI builds the RetroAchievements client identity
Nightly and MoonStore builds identify themselves to RetroAchievements as stock PCSX2, so the server allows softcore only. Hardcore has worked on locally built IPAs and nowhere else. Nothing was broken. pcsx2/Host.cpp reads the client version from ra_ua_secret.h behind __has_include and falls back to a stock agent when the macro is absent. That header is gitignored, so it has never existed on a runner. CI now writes it from the IOS_RA_UA_VERSION repository secret, the way the Android job already writes its keystores from NIGHTLY_RELEASE_KS_B64. A missing secret warns and continues, because fork pull requests never receive secrets and a nightly that fails to publish is worse than one without hardcore. A malformed version fails the build instead: RetroAchievements refuses a version it cannot order, and a refused agent is indistinguishable from an unknown one on the client. Only the nightly and pushes to master embed it. Pull request artifacts keep the stock agent, so a test build is not one more public copy of an identity that needs a release to revoke. A second step reads the finished binary and fails unless the exact version is in it, trailing space included, since a stale 1.2.3 is a prefix of a current 1.2.345. Both iOS jobs are continue-on-error, so that failure does not stop the workflow, but it does skip the upload that follows it, and the publish step treats a missing IPA as absent rather than fatal. A softcore build is not shipped. None of this makes the version private. The compiler bakes the finished agent into the binary as a plain literal, so anyone holding a build can read it out. What the gitignored header prevents is a fork inheriting a live identity straight from source.
This commit is contained in:
@@ -169,6 +169,19 @@ jobs:
|
|||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode.app
|
run: sudo xcode-select -switch /Applications/Xcode.app
|
||||||
|
|
||||||
|
# RetroAchievements grants hardcore on the client name and version in the HTTP user
|
||||||
|
# agent, and pcsx2/Host.cpp reads that version from a gitignored header. Only builds
|
||||||
|
# that reach players get it: a pull request artifact would be one more public copy of
|
||||||
|
# an identity that cannot be revoked without a release. Those keep the stock PCSX2
|
||||||
|
# agent and stay softcore, which is what a test build should be.
|
||||||
|
- name: Embed the RetroAchievements client identity
|
||||||
|
if: >-
|
||||||
|
github.repository == 'ARMSX2/ARMSX2' &&
|
||||||
|
github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
IOS_RA_UA_VERSION: ${{ secrets.IOS_RA_UA_VERSION }}
|
||||||
|
run: bash .github/workflows/scripts/common/write-ra-ua-secret.sh
|
||||||
|
|
||||||
- name: Configure (CMake, Xcode generator, real device SDK, no signing)
|
- name: Configure (CMake, Xcode generator, real device SDK, no signing)
|
||||||
working-directory: platforms/ios/app/src/main/cpp
|
working-directory: platforms/ios/app/src/main/cpp
|
||||||
run: >
|
run: >
|
||||||
@@ -209,6 +222,21 @@ jobs:
|
|||||||
echo "IPA_NAME=$IPA_NAME" >> $GITHUB_ENV
|
echo "IPA_NAME=$IPA_NAME" >> $GITHUB_ENV
|
||||||
ls -lh "$IPA_NAME"
|
ls -lh "$IPA_NAME"
|
||||||
|
|
||||||
|
# Nothing on the client reports a rejected agent, so a master build that lost the
|
||||||
|
# identity looks healthy until a player loses an unlock. Fail here instead. Carries
|
||||||
|
# the same guard as the write step, or every pull request would fail this check.
|
||||||
|
- name: Verify the client identity reached the binary
|
||||||
|
if: >-
|
||||||
|
github.repository == 'ARMSX2/ARMSX2' &&
|
||||||
|
github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
working-directory: platforms/ios/app/src/main/cpp
|
||||||
|
env:
|
||||||
|
IOS_RA_UA_VERSION: ${{ secrets.IOS_RA_UA_VERSION }}
|
||||||
|
run: |
|
||||||
|
APP_PATH=$(find build -name "ARMSX2iOS.app" -type d | head -1)
|
||||||
|
bash "$GITHUB_WORKSPACE/.github/workflows/scripts/common/verify-ra-ua-agent.sh" \
|
||||||
|
"$APP_PATH/ARMSX2iOS"
|
||||||
|
|
||||||
- name: Upload .ipa
|
- name: Upload .ipa
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -293,9 +293,29 @@ jobs:
|
|||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v7
|
||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode.app
|
run: sudo xcode-select -switch /Applications/Xcode.app
|
||||||
|
|
||||||
|
# RetroAchievements grants hardcore on the client name and version in the HTTP user
|
||||||
|
# agent, and pcsx2/Host.cpp reads that version from a gitignored header. Without this
|
||||||
|
# step the nightly identifies as stock PCSX2 and every unlock is softcore. Add the
|
||||||
|
# repo secret IOS_RA_UA_VERSION (the bare version, no leading v) to turn it on; the
|
||||||
|
# script warns and continues when it is absent.
|
||||||
|
- name: Embed the RetroAchievements client identity
|
||||||
|
env:
|
||||||
|
IOS_RA_UA_VERSION: ${{ secrets.IOS_RA_UA_VERSION }}
|
||||||
|
run: bash .github/workflows/scripts/common/write-ra-ua-secret.sh
|
||||||
|
|
||||||
- name: Build unsigned IPA
|
- name: Build unsigned IPA
|
||||||
run: ./platforms/ios/scripts/build-ios-ipa.sh
|
run: ./platforms/ios/scripts/build-ios-ipa.sh
|
||||||
|
|
||||||
|
# Nothing on the client reports a rejected agent, so a nightly that lost the identity
|
||||||
|
# looks healthy until a player loses an unlock. Fail here instead.
|
||||||
|
- name: Verify the client identity reached the binary
|
||||||
|
env:
|
||||||
|
IOS_RA_UA_VERSION: ${{ secrets.IOS_RA_UA_VERSION }}
|
||||||
|
run: >
|
||||||
|
bash .github/workflows/scripts/common/verify-ra-ua-agent.sh
|
||||||
|
platforms/ios/build-ios-xcode/Release-iphoneos/ARMSX2iOS.app/ARMSX2iOS
|
||||||
|
|
||||||
- name: Upload IPA
|
- name: Upload IPA
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Fail the build if the RetroAchievements client identity did not reach the binary.
|
||||||
|
#
|
||||||
|
# A reordered step, a stale build directory or a wrong path each produce a working IPA that
|
||||||
|
# reports itself as stock PCSX2. Nothing on the client says so, and the first sign is a
|
||||||
|
# player losing a hardcore unlock. Matching the exact version rather than the name also
|
||||||
|
# catches a build that compiled an older header.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BINARY="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$BINARY" ]]; then
|
||||||
|
echo "::error::usage: verify-ra-ua-agent.sh <path to the built binary>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$BINARY" ]]; then
|
||||||
|
echo "::error::$BINARY does not exist, so the build did not produce what this check reads."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="$(printf '%s' "${IOS_RA_UA_VERSION:-}" | tr -d '[:space:]')"
|
||||||
|
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
echo "no secret was supplied, so this build identifies as stock PCSX2. Nothing to verify."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The trailing space comes from Host.cpp's format string and is load bearing: a stale 1.2.3
|
||||||
|
# is a prefix of a current 1.2.345, so an unanchored match passes the build this exists to
|
||||||
|
# catch. grep drains the stream instead of using -q, which exits on the first match and
|
||||||
|
# SIGPIPEs strings, leaving pipefail to read a hit as a failed pipeline. Output is discarded
|
||||||
|
# so the version never reaches the log.
|
||||||
|
if strings -a "$BINARY" | grep -F "ARMSX2-iOS/v$VERSION " >/dev/null; then
|
||||||
|
echo "$BINARY carries the RetroAchievements client identity"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::error::$BINARY does not carry the expected client identity, so this build would be softcore only."
|
||||||
|
exit 1
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Write the RetroAchievements client identity that pcsx2/Host.cpp expects.
|
||||||
|
#
|
||||||
|
# Host.cpp includes "ra_ua_secret.h" behind __has_include and falls back to a stock PCSX2
|
||||||
|
# agent when the macro is absent. RetroAchievements grants hardcore on the leading
|
||||||
|
# Name/Version token of that agent, so a build without this header is softcore only. The
|
||||||
|
# header is gitignored, which is why a runner has to write it.
|
||||||
|
#
|
||||||
|
# Holding the version in a repository secret stops a fork or a code-lift from inheriting a
|
||||||
|
# live ARMSX2 identity. It hides nothing from anyone holding a build: the compiler bakes
|
||||||
|
# the finished agent into the binary as a plain literal.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HEADER="${GITHUB_WORKSPACE:-$PWD}/pcsx2/ra_ua_secret.h"
|
||||||
|
|
||||||
|
# Whitespace pasted into the secret field would end up inside an HTTP header.
|
||||||
|
VERSION="$(printf '%s' "${IOS_RA_UA_VERSION:-}" | tr -d '[:space:]')"
|
||||||
|
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
echo "::warning::IOS_RA_UA_VERSION is unset, so this build identifies as stock PCSX2 and RetroAchievements will allow softcore only."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A refused agent behaves exactly like an unknown one on the client, so catch a version
|
||||||
|
# RetroAchievements cannot order here rather than at a player's first hardcore unlock.
|
||||||
|
if [[ ! "$VERSION" =~ ^[0-9]+([.-][0-9]+)*$ ]]; then
|
||||||
|
echo "::error::IOS_RA_UA_VERSION is not a numeric dotted version. RetroAchievements cannot order it and will treat the client as unknown."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "${HEADER%/*}" ]]; then
|
||||||
|
echo "::error::${HEADER%/*} does not exist. Run this from the repository checkout."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Host.cpp's include is quoted, so one file beside it serves every platform building pcsx2/.
|
||||||
|
printf '#pragma once\n#define ARMSX2_IOS_RA_UA_VERSION "%s"\n' "$VERSION" > "$HEADER"
|
||||||
|
|
||||||
|
echo "wrote $HEADER"
|
||||||
Reference in New Issue
Block a user