github.run_id is a global counter across the whole Gitea instance (the
same numbering as the 'task NNNN' IDs in the runner's own logs), not
what Gitea's web UI uses in its /actions/runs/<n> URL - that's the
per-repository run_number. The email's 'Ver execução' link pointed at
a run belonging to a different, unrelated repo/workflow entirely.
The <style>-in-head version rendered broken in the actual mail client
(no card, no colors, no spacing - the email client stripped the style
block, a common limitation of HTML email). Rewrote as a table-based
layout with every style attribute inline, which is the robust approach
for cross-client HTML email compatibility, and added the real Atomenta
logo (https://atomenta.com.br/assets/images/logo_2024.png, the same
asset served by the Atomenta app's own public/assets). Dropped the
dark-mode media query - not expressible with inline-only styles, and
not essential for an internal CI notification.
Reused the visual style from the existing Atomenta transactional email
templates (src/views/emails/pedifoods/admin_review_alert.html in the
Atomenta repo) - card container, colored status badge, label/value
details box, dark-mode support. Branding is generic Atomenta (header
and footer), since this notification is shared across all projects on
this CI - only the email Subject names which project it's about
('[PediFoods CI] ...'). Sent as a proper MIME text/html message instead
of the earlier plain-text body.
Sends an email via the domain's own SMTP (mail.loverde.com.br:587,
STARTTLS) as the last step of both test.yml and beta.yml, using
if: always() so it fires regardless of the job's outcome - subject/body
include job.status, branch, commit and a link to the run. Credentials
(SMTP_USER, SMTP_PASS, NOTIFY_EMAIL_TO) come from repo/org secrets, not
hardcoded here.
assemble was passing CURRENT_PROJECT_VERSION=$BUILD_NUMBER (github.run_
number-style CI counter) as an xcarg, silently replacing the project's
real, manually-managed version (e.g. '2026.09.11.0.0.1') with an
unrelated small integer - the just-uploaded TestFlight build showed up
as '0.0.1 (51)' instead of the intended version. Removed the override
and the now-unused BUILD_NUMBER env var in beta.yml; CI now always
builds with whatever CURRENT_PROJECT_VERSION is committed in the
project. Re-running CI against the same commit without bumping it first
will now get a clear 'duplicate build number' rejection from Apple
instead of silently uploading under the wrong version.
The file-based diagnostic dump never worked: PediFoodsUITests-Runner
runs as an app inside the simulator, sandboxed to that device's own
container filesystem. NSTemporaryDirectory() called from test code
resolves inside the simulator's data container, invisible to the host
shell's $TMPDIR a later CI step tried to cat - confirmed by the new
step printing 'No UI test diagnostic log was written.' every time.
Replaced with quickDiagnostics(_:), which builds a short live-state
summary (closed-store snackbar, spinning activity indicator, any open
alert, visible 'erro' text, first ~15 visible static texts) and
interpolates it directly into each of the three XCTAssertTrue failure
messages - the one channel already confirmed working end to end on
every run. message: is @autoclosure, so this costs nothing when the
assertion passes. Removed the dead file-write helper and the now-unused
CI step.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
Confirmed (pasted full CI log around a timeout, searched for the
print()'d marker string, found nothing) that plain print() from inside
a UI test never reaches fastlane's xcodebuild log output on this
runner - its formatter only relays lines matching its own known
patterns and drops everything else.
CartCheckoutFlowTests now writes app.debugDescription to
NSTemporaryDirectory()+'pedifoods_uitest_diag.log' on each of its three
failure points (shared dumpDiagnostics helper, appending), clearing any
stale copy in class setUp(). test.yml gained a step after 'Run tests
with coverage' (if: always()) that cats that file when present - TMPDIR
is stable for the whole CI job, unlike the per-run-hashed workspace
path that broke the original screenshot attempt.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
Shared macOS runner accumulates orphaned simulator entries (registry
present, data dir missing on disk) which fail run_tests with 'Unable
to boot device because it cannot be located on disk'. Clean up before
every test run instead of requiring manual simctl intervention.
- Remove bitrise.yml (no longer used, replaced by Gitea Actions on
a native macos-build:host runner - Docker VM-based macos-ctl/
macos-release runners are dead per earlier session)
- test.yml: push-triggered on any branch except develop/release/main,
runs fastlane tests (coverage) instead of raw xcodebuild
- beta.yml: PR-to-develop triggered, builds + uploads to TestFlight;
drops the dead wake/sleep-macos-vm jobs, uses the archive-API
checkout workaround (see go-gitea/gitea#21819), points at the real
ci-build.keychain-db instead of the old ci-signing.keychain-db name
- release.yml removed: release/main run nothing automated per the
agreed branch flow, promotion is manual
- Fastfile: add a tests lane (coverage) so the workflow just calls
fastlane, matching LC Wallet's convention
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.
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.
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.
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.