com.apple.developer.devicecheck.appattest-environment was hardcoded to
"development" for every build, including the App Store/TestFlight
distribution build. Apple's App Attest servers validate this claim
against how the app was actually signed/distributed, so a "development"
claim on a real distribution build fails - guest session handshake
never gets past the challenge step, no store/city data ever loads.
Parameterized per configuration: development for Debug, production for
Release, via an APP_ATTEST_ENVIRONMENT build setting.
App Store Connect flagged a validation warning: code references a
location API (guest store locator) but Info.plist has no
NSLocationWhenInUseUsageDescription, which would cause an App Review
rejection if left unaddressed. Added via INFOPLIST_KEY_* build setting
since this target generates its Info.plist from build settings rather
than a static file.
Binary upload itself succeeded - the only failure was
upload_to_app_store's default auto-submission colliding with an
existing in-progress review submission. CI should deliver the build;
submitting for review stays a deliberate manual step in App Store
Connect.
Upload rejected with "bundle version must be higher than previously
uploaded version: 1" - agvtool new-version requires VERSIONING_SYSTEM =
apple-generic, which this project never sets, so it did nothing every
run despite reporting success. Pass CURRENT_PROJECT_VERSION directly
via xcargs instead, parameterized from the job's run number.
Baked directly into the project instead of overriding at build time -
no tool can programmatically edit this project's .pbxproj (xcodeproj
gem can't parse its format), and a command-line xcargs override applies
to the whole build graph, breaking the SPM package's own targets which
must stay on Automatic. Debug config left untouched so local Xcode
development still uses automatic signing.
The xcodeproj gem can't parse PediFoods.xcodeproj's .pbxproj (newer
Xcode format than any released gem version supports), so the runtime
override always fails with a misleading "very old project file" error.
Signing config for the app target needs to live in the checked-in
project settings instead (set once via Xcode's GUI), since no
command-line override can be scoped to a single target without also
breaking the SPM package's own ephemeral targets.
17-minute hang on git clone, far past the http.lowSpeedLimit abort
threshold, isn't explained by a data-transfer stall. Now that the VM has
a real GUI session (auto-login), git-credential-osxkeychain could be
popping a GUI dialog nobody's there to dismiss, bypassing
GIT_TERMINAL_PROMPT. Disable the credential helper and force askpass to
fail immediately instead of prompting.
Blanket xcargs (CODE_SIGN_STYLE=Manual etc.) applied to every target in
the build, including the SPM package's own generated targets (PediFoods,
pedi-foods_PediFoods) which explicitly reject provisioning profiles and
need to stay Automatic. Use update_code_signing_settings scoped to just
"PediFoods App" instead, guarded behind DEVELOPMENT_TEAM being set so
Bitrise's existing automatic-signing path is untouched.
Main app target signed fine after the manual signing override, but the
SPM-generated pedi-foods_PediFoods target still failed with "requires a
development team" - it needs DEVELOPMENT_TEAM directly since profile
specifiers only map to the app's own bundle ID. Already available as a
job env var, just wasn't being passed into xcodebuild's build settings.
xcodebuild ignored sigh's downloaded provisioning profile because the
Xcode project's signing style is Automatic, which needs an interactive
Apple ID session unavailable in headless CI. Override at build time via
xcargs instead of changing the checked-in project signing settings.
git clone froze for 9+ minutes on one run with no clear cause. Set
GIT_TERMINAL_PROMPT=0 so it fails fast instead of hanging if credential
auth ever goes wrong, and abort via http.lowSpeedLimit/lowSpeedTime if
the transfer genuinely stalls instead of just being slow.
Identity is visible via the same commands over interactive SSH but not
from this job's own process, even within a single merged step - adding
whoami/HOME/path/keychain-info printouts to see what's actually
different about this execution context before guessing further.
Identity was visible with find-identity inside the unlock step itself
but still invisible to fastlane in the next step - each run: block
likely spawns a distinct process/session on this host executor, so the
unlock doesn't survive across steps even though keychain search-list
membership does. Run unlock and fastlane in the same shell invocation
to remove that boundary entirely.
A Gitea Actions job runs in a different macOS security session than an
interactive SSH login - login.keychain-db's unlock state and search-list
membership don't reliably carry over across that boundary, so the
identity was invisible to the job even after successful unlock. Point
the workflow's unlock step at a dedicated ci-signing.keychain-db instead,
created independent of any login session.
Unlocking alone wasn't enough - the launchd session's default keychain
search list apparently doesn't include the login keychain by default,
so sigh/fastlane still found zero identities even after a successful
unlock. Explicitly set it as both the search list and default keychain,
and print find-identity in the step itself to verify before fastlane runs.
act_runner's launchd daemon now runs as loverde_vm_mac (UserName set),
but its background session doesn't inherit the unlock state from an
interactive SSH session - the login keychain is still locked when
fastlane/sigh looks for signing identities, so none are found. Unlock it
explicitly at the start of each signing job instead.
actions/checkout@v4 is a JS action, and act_runner's host-executor JS
action path hits a known upstream caching bug on this runner (nested
hostexecutor/.cache/act path never gets the downloaded action, causing
MODULE_NOT_FOUND on every run). Introducing a config.yaml to work around
it (workdir_parent) triggers a separate act_runner bug requiring a Docker
socket that doesn't exist on this Mac. Side-stepping both by doing the
checkout as a plain shell git clone, which the host executor runs fine.
Drop the SSH + restricted-key design entirely. wake/sleep now runs on a
new macos-ctl runner (its own Docker container on the VPS, Docker socket
mounted) which runs docker start/stop macos directly on the sibling
container - no SSH indirection, and no borrowing of another project's
runner (pedifoods-web) to do it.
runs-on: docker matched no registered runner (labels are macos-release,
atomenta, pedifoods, lc-wallet-web). Use pedifoods, this repo's own
existing always-on runner, to run the wake/sleep SSH steps.