From dda7bbcb1708f93f31737af827b4b79ed494b837 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 11:35:58 -0300 Subject: [PATCH 01/18] [macos-runner] Add Gitea Actions workflows for self-hosted macOS CI Replaces bitrise.yml with equivalent test/beta/release pipelines running on the self-hosted macos-release runner (act_runner inside a dockur/macos VM). Each workflow wakes the VM over SSH before the job and stops it after, since the VM can't be left running 24/7 without starving the VPS's other services. --- .gitea/workflows/beta.yml | 79 +++++++++++++++++++++++++++++++++ .gitea/workflows/release.yml | 86 ++++++++++++++++++++++++++++++++++++ .gitea/workflows/test.yml | 54 ++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 .gitea/workflows/beta.yml create mode 100644 .gitea/workflows/release.yml create mode 100644 .gitea/workflows/test.yml diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml new file mode 100644 index 0000000..4abfd63 --- /dev/null +++ b/.gitea/workflows/beta.yml @@ -0,0 +1,79 @@ +name: Beta (TestFlight) + +on: + push: + branches: + - develop + +jobs: + wake-macos-vm: + runs-on: docker + container: + image: alpine:3.20 + steps: + - name: Install ssh client + run: apk add --no-cache openssh-client + - name: Start macOS VM container + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + + build-and-upload: + needs: wake-macos-vm + runs-on: macos-release + + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }} + DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set build number + run: | + cd Darwin + agvtool new-version -all ${{ gitea.run_number }} + + - name: Write App Store Connect API key + run: | + cat > Darwin/fastlane/apikey.json < ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..4e7fa6a --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,86 @@ +name: Release (App Store Connect) + +on: + push: + branches: + - main + +jobs: + wake-macos-vm: + runs-on: docker + container: + image: alpine:3.20 + steps: + - name: Install ssh client + run: apk add --no-cache openssh-client + - name: Start macOS VM container + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + + build-archive-deliver: + needs: wake-macos-vm + runs-on: macos-release + + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }} + DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Verify ASC secrets + run: | + if [ -z "${{ secrets.ASC_KEY_ID }}" ]; then + echo "ERROR: ASC_KEY_ID is empty" + exit 1 + fi + + - name: Set build number + run: | + cd Darwin + agvtool new-version -all ${{ gitea.run_number }} + + - name: Write App Store Connect API key + run: | + cat > Darwin/fastlane/apikey.json < ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml new file mode 100644 index 0000000..56f2cfc --- /dev/null +++ b/.gitea/workflows/test.yml @@ -0,0 +1,54 @@ +name: Test (feature branches) + +on: + pull_request: + branches: + - "feature/*" + +jobs: + wake-macos-vm: + runs-on: docker + container: + image: alpine:3.20 + steps: + - name: Install ssh client + run: apk add --no-cache openssh-client + - name: Start macOS VM container + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + + test: + needs: wake-macos-vm + runs-on: macos-release + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run unit tests + run: | + xcodebuild test \ + -project Darwin/PediFoods.xcodeproj \ + -scheme "PediFoods App" \ + -destination "platform=iOS Simulator,name=iPhone 16,OS=latest" + + sleep-macos-vm: + needs: test + if: always() + runs-on: docker + container: + image: alpine:3.20 + steps: + - name: Install ssh client + run: apk add --no-cache openssh-client + - name: Stop macOS VM container + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ + ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop From cef21aa1608a887296b164b3da81c7919692d0d4 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 11:46:04 -0300 Subject: [PATCH 02/18] [macos-runner] Fix wake/sleep jobs to use an actually-registered runner label 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. --- .gitea/workflows/beta.yml | 4 ++-- .gitea/workflows/release.yml | 4 ++-- .gitea/workflows/test.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 4abfd63..9d300e8 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -7,7 +7,7 @@ on: jobs: wake-macos-vm: - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: @@ -64,7 +64,7 @@ jobs: sleep-macos-vm: needs: build-and-upload if: always() - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 4e7fa6a..d48ec63 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: wake-macos-vm: - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: @@ -71,7 +71,7 @@ jobs: sleep-macos-vm: needs: build-archive-deliver if: always() - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 56f2cfc..1ee4c6e 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -7,7 +7,7 @@ on: jobs: wake-macos-vm: - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: @@ -39,7 +39,7 @@ jobs: sleep-macos-vm: needs: test if: always() - runs-on: docker + runs-on: pedifoods container: image: alpine:3.20 steps: From 8af8c7cb5baa8c9769ba90d691162fc675190c4d Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 11:57:09 -0300 Subject: [PATCH 03/18] [macos-runner] Replace SSH-based VM wake/sleep with a dedicated runner container 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. --- .gitea/workflows/beta.yml | 26 ++++---------------------- .gitea/workflows/release.yml | 26 ++++---------------------- .gitea/workflows/test.yml | 26 ++++---------------------- 3 files changed, 12 insertions(+), 66 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 9d300e8..3d6284c 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -7,19 +7,10 @@ on: jobs: wake-macos-vm: - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Start macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + run: docker start macos build-and-upload: needs: wake-macos-vm @@ -64,16 +55,7 @@ jobs: sleep-macos-vm: needs: build-and-upload if: always() - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Stop macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop + run: docker stop macos diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index d48ec63..fac245f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -7,19 +7,10 @@ on: jobs: wake-macos-vm: - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Start macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + run: docker start macos build-archive-deliver: needs: wake-macos-vm @@ -71,16 +62,7 @@ jobs: sleep-macos-vm: needs: build-archive-deliver if: always() - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Stop macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop + run: docker stop macos diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 1ee4c6e..63b6471 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -7,19 +7,10 @@ on: jobs: wake-macos-vm: - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Start macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} start + run: docker start macos test: needs: wake-macos-vm @@ -39,16 +30,7 @@ jobs: sleep-macos-vm: needs: test if: always() - runs-on: pedifoods - container: - image: alpine:3.20 + runs-on: macos-ctl steps: - - name: Install ssh client - run: apk add --no-cache openssh-client - name: Stop macOS VM container - run: | - mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 \ - ${{ secrets.VPS_SSH_USER }}@${{ secrets.VPS_SSH_HOST }} stop + run: docker stop macos From d9c2035807ea67301d75cf263c46b30b6951a118 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 17:48:47 -0300 Subject: [PATCH 04/18] [macos-runner] Replace actions/checkout@v4 with a plain git clone step 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. --- .gitea/workflows/beta.yml | 6 +++++- .gitea/workflows/release.yml | 6 +++++- .gitea/workflows/test.yml | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 3d6284c..067aacc 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -23,7 +23,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + run: | + REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" + AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" + git clone "$AUTH_URL" . + git checkout "${{ gitea.sha }}" - name: Set build number run: | diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index fac245f..3a3e743 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -23,7 +23,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + run: | + REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" + AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" + git clone "$AUTH_URL" . + git checkout "${{ gitea.sha }}" - name: Verify ASC secrets run: | diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 63b6471..23c40ca 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -18,7 +18,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + run: | + REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" + AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" + git clone "$AUTH_URL" . + git checkout "${{ gitea.sha }}" - name: Run unit tests run: | From 54728cdc719e78e2ff9a5f61c61dda0a93234b3b Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 18:21:54 -0300 Subject: [PATCH 05/18] [macos-runner] Unlock login keychain explicitly before fastlane signing steps 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. --- .gitea/workflows/beta.yml | 3 +++ .gitea/workflows/release.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 067aacc..0c466b4 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -47,6 +47,9 @@ jobs: } EOF + - name: Unlock keychain + run: security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + - name: fastlane beta run: | cd Darwin diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 3a3e743..f9d9a76 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -54,6 +54,9 @@ jobs: } EOF + - name: Unlock keychain + run: security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + - name: fastlane release run: | cd Darwin From d84fcc6ac46ce7377787eeba48d9c4861f607a2c Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 18:30:10 -0300 Subject: [PATCH 06/18] [macos-runner] Force login keychain into the job session's search list 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. --- .gitea/workflows/beta.yml | 6 +++++- .gitea/workflows/release.yml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 0c466b4..e921d0b 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -48,7 +48,11 @@ jobs: EOF - name: Unlock keychain - run: security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + run: | + security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + security list-keychains -d user -s ~/Library/Keychains/login.keychain-db + security default-keychain -d user -s ~/Library/Keychains/login.keychain-db + security find-identity -v -p codesigning - name: fastlane beta run: | diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index f9d9a76..ef11a3f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -55,7 +55,11 @@ jobs: EOF - name: Unlock keychain - run: security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + run: | + security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db + security list-keychains -d user -s ~/Library/Keychains/login.keychain-db + security default-keychain -d user -s ~/Library/Keychains/login.keychain-db + security find-identity -v -p codesigning - name: fastlane release run: | From a0f18644d75a562e2b2d593091215adb2fd00bbe Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 18:40:51 -0300 Subject: [PATCH 07/18] [macos-runner] Use a dedicated CI keychain instead of the login keychain 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. --- .gitea/workflows/beta.yml | 6 +++--- .gitea/workflows/release.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index e921d0b..851fe3c 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -49,9 +49,9 @@ jobs: - name: Unlock keychain run: | - security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db - security list-keychains -d user -s ~/Library/Keychains/login.keychain-db - security default-keychain -d user -s ~/Library/Keychains/login.keychain-db + security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/ci-signing.keychain-db + security list-keychains -d user -s ~/Library/Keychains/ci-signing.keychain-db ~/Library/Keychains/login.keychain-db + security default-keychain -d user -s ~/Library/Keychains/ci-signing.keychain-db security find-identity -v -p codesigning - name: fastlane beta diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index ef11a3f..1611495 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -56,9 +56,9 @@ jobs: - name: Unlock keychain run: | - security unlock-keychain -p "${{ secrets.MAC_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db - security list-keychains -d user -s ~/Library/Keychains/login.keychain-db - security default-keychain -d user -s ~/Library/Keychains/login.keychain-db + security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/ci-signing.keychain-db + security list-keychains -d user -s ~/Library/Keychains/ci-signing.keychain-db ~/Library/Keychains/login.keychain-db + security default-keychain -d user -s ~/Library/Keychains/ci-signing.keychain-db security find-identity -v -p codesigning - name: fastlane release From 247e59b5f1830496bbb8da77e7c9c3fa33ac8fa7 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 18:44:29 -0300 Subject: [PATCH 08/18] [macos-runner] Merge keychain unlock and fastlane into a single step 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. --- .gitea/workflows/beta.yml | 5 +---- .gitea/workflows/release.yml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 851fe3c..d4ba822 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -47,15 +47,12 @@ jobs: } EOF - - name: Unlock keychain + - name: Unlock keychain and run fastlane beta run: | security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/ci-signing.keychain-db security list-keychains -d user -s ~/Library/Keychains/ci-signing.keychain-db ~/Library/Keychains/login.keychain-db security default-keychain -d user -s ~/Library/Keychains/ci-signing.keychain-db security find-identity -v -p codesigning - - - name: fastlane beta - run: | cd Darwin fastlane beta diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 1611495..bcf9b05 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -54,15 +54,12 @@ jobs: } EOF - - name: Unlock keychain + - name: Unlock keychain and run fastlane release run: | security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/ci-signing.keychain-db security list-keychains -d user -s ~/Library/Keychains/ci-signing.keychain-db ~/Library/Keychains/login.keychain-db security default-keychain -d user -s ~/Library/Keychains/ci-signing.keychain-db security find-identity -v -p codesigning - - - name: fastlane release - run: | cd Darwin fastlane release From 303886b103fb861a987cde9b59189f34c7d48c06 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 30 Jul 2026 18:48:01 -0300 Subject: [PATCH 09/18] [macos-runner] Add diagnostics to the keychain step to find the real cause 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. --- .gitea/workflows/release.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index bcf9b05..9a672be 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -56,9 +56,20 @@ jobs: - name: Unlock keychain and run fastlane release run: | - security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" ~/Library/Keychains/ci-signing.keychain-db - security list-keychains -d user -s ~/Library/Keychains/ci-signing.keychain-db ~/Library/Keychains/login.keychain-db - security default-keychain -d user -s ~/Library/Keychains/ci-signing.keychain-db + echo "whoami: $(whoami)" + echo "HOME: $HOME" + echo "expanded keychain path: $HOME/Library/Keychains/ci-signing.keychain-db" + ls -la "$HOME/Library/Keychains/" || echo "cannot list Keychains dir" + security unlock-keychain -p "${{ secrets.CI_KEYCHAIN_PASSWORD }}" "$HOME/Library/Keychains/ci-signing.keychain-db" + echo "--- keychain info ---" + security show-keychain-info "$HOME/Library/Keychains/ci-signing.keychain-db" || true + security list-keychains -d user -s "$HOME/Library/Keychains/ci-signing.keychain-db" "$HOME/Library/Keychains/login.keychain-db" + security default-keychain -d user -s "$HOME/Library/Keychains/ci-signing.keychain-db" + echo "--- current search list ---" + security list-keychains + echo "--- dump-keychain identity count ---" + security dump-keychain "$HOME/Library/Keychains/ci-signing.keychain-db" | grep -c "class.*0x80001000" || true + echo "--- find-identity ---" security find-identity -v -p codesigning cd Darwin fastlane release From 47cf46e007b0ee40a52e1903fe283b8a367c7e2f Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 09:32:10 -0300 Subject: [PATCH 10/18] [macos-runner] Guard checkout against hanging on prompts or a stalled clone 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. --- .gitea/workflows/beta.yml | 3 ++- .gitea/workflows/release.yml | 3 ++- .gitea/workflows/test.yml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index d4ba822..2cc5240 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -24,9 +24,10 @@ jobs: steps: - name: Checkout run: | + export GIT_TERMINAL_PROMPT=0 REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git clone "$AUTH_URL" . + git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Set build number diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 9a672be..e5ac71a 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -24,9 +24,10 @@ jobs: steps: - name: Checkout run: | + export GIT_TERMINAL_PROMPT=0 REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git clone "$AUTH_URL" . + git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Verify ASC secrets diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 23c40ca..8bda2ae 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -19,9 +19,10 @@ jobs: steps: - name: Checkout run: | + export GIT_TERMINAL_PROMPT=0 REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git clone "$AUTH_URL" . + git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Run unit tests From 3e82e6e1fece44ee8b5356a78adbc868d1360171 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 09:38:29 -0300 Subject: [PATCH 11/18] [macos-runner] Force manual code signing in the assemble lane's xcargs 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. --- Darwin/fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile index cfc6f8f..fc38d78 100644 --- a/Darwin/fastlane/Fastfile +++ b/Darwin/fastlane/Fastfile @@ -12,7 +12,7 @@ lane :assemble do |options| sdk: "iphoneos", export_method: ENV["BITRISE_DISTRIBUTION_METHOD"] || "app-store", xcconfig: "fastlane/AppStore.xcconfig", - xcargs: "-skipPackagePluginValidation -skipMacroValidation", + xcargs: "-skipPackagePluginValidation -skipMacroValidation CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\"Apple Distribution\" PROVISIONING_PROFILE_SPECIFIER=\"com.br.pedifoods.app AppStore\"", derived_data_path: "../.build/Darwin/DerivedData", output_directory: "../.build/fastlane/Darwin", skip_archive: ENV["FASTLANE_SKIP_ARCHIVE"] == "YES", From d21ee2e7a6f61706a86a1a3e75548f8126b4a77a Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 10:05:32 -0300 Subject: [PATCH 12/18] [macos-runner] Pass DEVELOPMENT_TEAM into xcargs for SPM package targets 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. --- Darwin/fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile index fc38d78..9da47f1 100644 --- a/Darwin/fastlane/Fastfile +++ b/Darwin/fastlane/Fastfile @@ -12,7 +12,7 @@ lane :assemble do |options| sdk: "iphoneos", export_method: ENV["BITRISE_DISTRIBUTION_METHOD"] || "app-store", xcconfig: "fastlane/AppStore.xcconfig", - xcargs: "-skipPackagePluginValidation -skipMacroValidation CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\"Apple Distribution\" PROVISIONING_PROFILE_SPECIFIER=\"com.br.pedifoods.app AppStore\"", + xcargs: "-skipPackagePluginValidation -skipMacroValidation CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\"Apple Distribution\" PROVISIONING_PROFILE_SPECIFIER=\"com.br.pedifoods.app AppStore\" DEVELOPMENT_TEAM=#{ENV['DEVELOPMENT_TEAM']}", derived_data_path: "../.build/Darwin/DerivedData", output_directory: "../.build/fastlane/Darwin", skip_archive: ENV["FASTLANE_SKIP_ARCHIVE"] == "YES", From cfa054a593757259bc5076b0405856590323e09a Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 10:20:33 -0300 Subject: [PATCH 13/18] [macos-runner] Scope manual signing to the app target only 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. --- Darwin/fastlane/Fastfile | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile index 9da47f1..656ae00 100644 --- a/Darwin/fastlane/Fastfile +++ b/Darwin/fastlane/Fastfile @@ -7,12 +7,28 @@ default_platform(:ios) lane :assemble do |options| # only build the iOS side of the app ENV["SKIP_ZERO"] = "true" + + # Scope manual signing to just the app target - the SPM package's own + # generated targets (PediFoods, pedi-foods_PediFoods) explicitly don't + # support provisioning profiles and must stay on Automatic, so a blanket + # xcargs override breaks them. + if ENV["DEVELOPMENT_TEAM"] + update_code_signing_settings( + use_automatic_signing: false, + path: "PediFoods.xcodeproj", + code_sign_identity: "Apple Distribution", + profile_name: "com.br.pedifoods.app AppStore", + team_id: ENV["DEVELOPMENT_TEAM"], + targets: ["PediFoods App"] + ) + end + build_app( scheme: "PediFoods App", sdk: "iphoneos", export_method: ENV["BITRISE_DISTRIBUTION_METHOD"] || "app-store", xcconfig: "fastlane/AppStore.xcconfig", - xcargs: "-skipPackagePluginValidation -skipMacroValidation CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\"Apple Distribution\" PROVISIONING_PROFILE_SPECIFIER=\"com.br.pedifoods.app AppStore\" DEVELOPMENT_TEAM=#{ENV['DEVELOPMENT_TEAM']}", + xcargs: "-skipPackagePluginValidation -skipMacroValidation", derived_data_path: "../.build/Darwin/DerivedData", output_directory: "../.build/fastlane/Darwin", skip_archive: ENV["FASTLANE_SKIP_ARCHIVE"] == "YES", From 28f145f39299e09ff1dfc30340ff1ff000657f95 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 10:41:36 -0300 Subject: [PATCH 14/18] [macos-runner] Disable credential helper and askpass for checkout clone 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. --- .gitea/workflows/beta.yml | 4 +++- .gitea/workflows/release.yml | 4 +++- .gitea/workflows/test.yml | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 2cc5240..82a4deb 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -25,9 +25,11 @@ jobs: - name: Checkout run: | export GIT_TERMINAL_PROMPT=0 + export GIT_ASKPASS=/bin/false + export SSH_ASKPASS=/bin/false REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . + git -c credential.helper= -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Set build number diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index e5ac71a..4517deb 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -25,9 +25,11 @@ jobs: - name: Checkout run: | export GIT_TERMINAL_PROMPT=0 + export GIT_ASKPASS=/bin/false + export SSH_ASKPASS=/bin/false REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . + git -c credential.helper= -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Verify ASC secrets diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 8bda2ae..9e1f4e1 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -20,9 +20,11 @@ jobs: - name: Checkout run: | export GIT_TERMINAL_PROMPT=0 + export GIT_ASKPASS=/bin/false + export SSH_ASKPASS=/bin/false REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" AUTH_URL="$(echo "$REPO_URL" | sed "s#https://#https://x-access-token:${{ gitea.token }}@#")" - git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . + git -c credential.helper= -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - name: Run unit tests From 7445561e5cc1b3d3dc3fe5fe06f42cc7a77e9440 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 10:54:04 -0300 Subject: [PATCH 15/18] [macos-runner] Revert update_code_signing_settings - unusable on this project format 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. --- Darwin/fastlane/Fastfile | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile index 656ae00..17b1135 100644 --- a/Darwin/fastlane/Fastfile +++ b/Darwin/fastlane/Fastfile @@ -8,21 +8,15 @@ lane :assemble do |options| # only build the iOS side of the app ENV["SKIP_ZERO"] = "true" - # Scope manual signing to just the app target - the SPM package's own - # generated targets (PediFoods, pedi-foods_PediFoods) explicitly don't - # support provisioning profiles and must stay on Automatic, so a blanket - # xcargs override breaks them. - if ENV["DEVELOPMENT_TEAM"] - update_code_signing_settings( - use_automatic_signing: false, - path: "PediFoods.xcodeproj", - code_sign_identity: "Apple Distribution", - profile_name: "com.br.pedifoods.app AppStore", - team_id: ENV["DEVELOPMENT_TEAM"], - targets: ["PediFoods App"] - ) - end - + # Manual signing (team/identity/profile) for the "PediFoods App" target is + # baked directly into PediFoods.xcodeproj's own project settings - not + # overridden here. A command-line xcargs/update_code_signing_settings + # override applies to the entire build graph, including the SPM package's + # own ephemeral targets (PediFoods, pedi-foods_PediFoods) which explicitly + # reject provisioning profiles and must stay Automatic; there's no way to + # scope a global override to just one target, and update_code_signing_settings + # itself can't parse this project's newer .pbxproj format anyway (gem + # limitation, not fixable by updating the gem). build_app( scheme: "PediFoods App", sdk: "iphoneos", From 57a4ad30862f030826a6c451813c6cb589911081 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 11:12:59 -0300 Subject: [PATCH 16/18] [macos-runner] Set manual signing on the app target's Release configuration 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. --- Darwin/PediFoods.xcodeproj/project.pbxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Darwin/PediFoods.xcodeproj/project.pbxproj b/Darwin/PediFoods.xcodeproj/project.pbxproj index 7b8fcbe..9f94c21 100644 --- a/Darwin/PediFoods.xcodeproj/project.pbxproj +++ b/Darwin/PediFoods.xcodeproj/project.pbxproj @@ -195,6 +195,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 496EB72F2A6AE4DE00C1253B /* PediFoods.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Distribution"; + CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = K4E5BZMM4V; ENABLE_PREVIEWS = YES; INFOPLIST_KEY_CFBundleDisplayName = "Pedi Foods"; @@ -202,6 +204,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + PROVISIONING_PROFILE_SPECIFIER = "com.br.pedifoods.app AppStore"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; TARGETED_DEVICE_FAMILY = 1; From 007227f0ea322f6254e69e72e2f28fc70a645fc1 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Fri, 31 Jul 2026 11:36:50 -0300 Subject: [PATCH 17/18] [macos-runner] Fix build number bump - agvtool was silently no-oping 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. --- .gitea/workflows/beta.yml | 6 +----- .gitea/workflows/release.yml | 6 +----- Darwin/fastlane/Fastfile | 7 ++++++- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 82a4deb..d406ee9 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -20,6 +20,7 @@ jobs: APPLE_ID: ${{ secrets.APPLE_ID }} ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }} DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} + BUILD_NUMBER: ${{ gitea.run_number }} steps: - name: Checkout @@ -32,11 +33,6 @@ jobs: git -c credential.helper= -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=30 clone "$AUTH_URL" . git checkout "${{ gitea.sha }}" - - name: Set build number - run: | - cd Darwin - agvtool new-version -all ${{ gitea.run_number }} - - name: Write App Store Connect API key run: | cat > Darwin/fastlane/apikey.json < Darwin/fastlane/apikey.json < Date: Fri, 31 Jul 2026 11:52:24 -0300 Subject: [PATCH 18/18] [macos-runner] Don't auto-submit for review after upload 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. --- Darwin/fastlane/Fastfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile index 8a8b099..6fda24a 100644 --- a/Darwin/fastlane/Fastfile +++ b/Darwin/fastlane/Fastfile @@ -63,7 +63,8 @@ lane :release do |options| upload_to_app_store( api_key_path: "fastlane/apikey.json", app_rating_config_path: "fastlane/metadata/rating.json", - release_notes: { default: "Fixes and improvements." } + release_notes: { default: "Fixes and improvements." }, + submit_for_review: false ) end