From 864dc66e8ece8aa4001005d6a1f97de7145a73b6 Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 14:03:47 -0300 Subject: [PATCH 1/6] fix: stop overriding CURRENT_PROJECT_VERSION with the CI run number 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. --- .gitea/workflows/beta.yml | 3 --- fastlane/Fastfile | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 5f52542..89cee88 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -9,9 +9,6 @@ jobs: build-and-upload: runs-on: macos-build - env: - BUILD_NUMBER: ${{ github.run_number }} - steps: - name: Checkout repository (workaround - Gitea git-upload-pack bug, see go-gitea/gitea#21819) run: | diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 183bbfe..a76df60 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -16,10 +16,20 @@ lane :assemble do |options| # Manual signing (team/identity/profile) for the "PediFoods" target is # baked directly into PediFoods.xcodeproj's own project settings - not # overridden here. - # agvtool needs VERSIONING_SYSTEM = apple-generic, which this project doesn't - # set, so it silently no-ops - pass CURRENT_PROJECT_VERSION directly instead. + # + # CURRENT_PROJECT_VERSION is likewise never overridden here - it's + # whatever is committed in the project (manually bumped, e.g. + # "2026.09.11.0.0.1"). A prior version of this lane overrode it with + # the CI job's own run number (BUILD_NUMBER = github.run_number-style + # counter) to guarantee a unique, ever-increasing build number per + # run - but that silently replaced the real, meaningful version with + # an unrelated small integer (a build uploaded as "51" instead of the + # intended "2026.09.11.0.0.1"). Trusting the committed value means + # re-running CI against the same commit without bumping + # CURRENT_PROJECT_VERSION first will make TestFlight/App Store reject + # the upload as a duplicate build number - a clear, safe failure, + # which is the correct behavior for a manually-versioned project. xcargs = "-skipPackagePluginValidation -skipMacroValidation" - xcargs += " CURRENT_PROJECT_VERSION=#{ENV['BUILD_NUMBER']}" if ENV["BUILD_NUMBER"] build_app( scheme: "PediFoods", From 55b65b874dccac87677a784e8314cb62cb5d92d7 Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 14:07:46 -0300 Subject: [PATCH 2/6] feat: automatic date-based build number (YYYY.MM.DD.0.0.) User wants build numbers to always follow this format, incrementing across multiple builds on the same calendar day and resetting to 1 on a new day - not a manually-edited fixed string, and not the CI run counter (which just uploaded a build as '51', unrelated to the real versioning scheme). Added next_build_version, which persists {date, build_number} to a fixed path under this CI user's home directory on the Mac mini runner (~/ci-build-version-state/pedifoods.json) - state has to live outside the per-run checkout, since every CI run gets a fresh, throwaway clone under .cache/act//. assemble now passes this as the CURRENT_PROJECT_VERSION xcarg instead of the project's committed value, so every build gets a fresh, correctly-formatted, ever-increasing version automatically. --- fastlane/Fastfile | 49 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index a76df60..aa363d4 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,8 +1,32 @@ # This file contains the fastlane.tools configuration for the PediFoods iOS app. # You can find the documentation at https://docs.fastlane.tools +require "date" +require "json" +require "fileutils" + default_platform(:ios) +# Build number scheme: "YYYY.MM.DD.0.0." - increments across +# builds made on the same calendar day, resets to 1 on a new day. +# Needs somewhere to remember "the last used, and for which day", +# and the per-run checkout under .cache/act// can't be +# that place - each CI run gets a fresh, throwaway clone, so anything +# written there is gone by the next run. State lives instead at a fixed +# path under this CI user's home directory on the Mac mini runner, +# which is the same machine/user across every run. +def next_build_version + state_path = File.expand_path("~/ci-build-version-state/pedifoods.json") + FileUtils.mkdir_p(File.dirname(state_path)) + + today = Date.today.strftime("%Y.%m.%d") + state = File.exist?(state_path) ? JSON.parse(File.read(state_path)) : {} + build_number = (state["date"] == today) ? state["build_number"].to_i + 1 : 1 + + File.write(state_path, JSON.generate({ "date" => today, "build_number" => build_number })) + "#{today}.0.0.#{build_number}" +end + lane :tests do run_tests( scheme: "PediFoods", @@ -17,19 +41,18 @@ lane :assemble do |options| # baked directly into PediFoods.xcodeproj's own project settings - not # overridden here. # - # CURRENT_PROJECT_VERSION is likewise never overridden here - it's - # whatever is committed in the project (manually bumped, e.g. - # "2026.09.11.0.0.1"). A prior version of this lane overrode it with - # the CI job's own run number (BUILD_NUMBER = github.run_number-style - # counter) to guarantee a unique, ever-increasing build number per - # run - but that silently replaced the real, meaningful version with - # an unrelated small integer (a build uploaded as "51" instead of the - # intended "2026.09.11.0.0.1"). Trusting the committed value means - # re-running CI against the same commit without bumping - # CURRENT_PROJECT_VERSION first will make TestFlight/App Store reject - # the upload as a duplicate build number - a clear, safe failure, - # which is the correct behavior for a manually-versioned project. - xcargs = "-skipPackagePluginValidation -skipMacroValidation" + # CURRENT_PROJECT_VERSION IS overridden here, with next_build_version + # (see top of file) rather than the project's own committed value. + # Earlier versions of this lane either used the committed value as-is + # (fine until you forget to bump it and Apple rejects a duplicate + # build number) or overrode it with the CI job's raw run-number + # counter (BUILD_NUMBER = github.run_number-style, which silently + # uploaded a build as "51" with no relation to the real version at + # all). next_build_version keeps the real date-based scheme and + # bumps only the last component automatically, so every push + # is guaranteed a fresh, correctly-formatted, ever-increasing build + # number with no manual edit needed before each run. + xcargs = "-skipPackagePluginValidation -skipMacroValidation CURRENT_PROJECT_VERSION=#{next_build_version}" build_app( scheme: "PediFoods", From d400e94d16cf54bd0268758973be64bbae26badf Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 14:36:50 -0300 Subject: [PATCH 3/6] feat(ci): email notification on success or failure 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. --- .gitea/workflows/beta.yml | 25 +++++++++++++++++++++++++ .gitea/workflows/test.yml | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 89cee88..9ebbf07 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -52,3 +52,28 @@ jobs: - name: Clean up API key if: always() run: rm -f fastlane/apikey.json + + - name: Email notification + if: always() + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + NOTIFY_EMAIL_TO: ${{ secrets.NOTIFY_EMAIL_TO }} + run: | + STATUS="${{ job.status }}" + SUBJECT="[PediFoods CI] ${STATUS} - ${{ github.workflow }}" + BODY="Job: ${{ github.workflow }} / ${{ github.job }} + Status: ${STATUS} + Branch: ${{ github.ref_name }} + Commit: ${{ github.sha }} + Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + printf 'From: PediFoods CI <%s>\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n' \ + "$SMTP_USER" "$NOTIFY_EMAIL_TO" "$SUBJECT" "$BODY" | \ + curl --silent --show-error \ + --url "smtp://mail.loverde.com.br:587" \ + --ssl-reqd \ + --mail-from "$SMTP_USER" \ + --mail-rcpt "$NOTIFY_EMAIL_TO" \ + --user "$SMTP_USER:$SMTP_PASS" \ + --upload-file - || true diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index f0e04d0..c6265da 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -24,3 +24,28 @@ jobs: - name: Run tests with coverage run: fastlane tests + + - name: Email notification + if: always() + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + NOTIFY_EMAIL_TO: ${{ secrets.NOTIFY_EMAIL_TO }} + run: | + STATUS="${{ job.status }}" + SUBJECT="[PediFoods CI] ${STATUS} - ${{ github.workflow }}" + BODY="Job: ${{ github.workflow }} / ${{ github.job }} + Status: ${STATUS} + Branch: ${{ github.ref_name }} + Commit: ${{ github.sha }} + Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + printf 'From: PediFoods CI <%s>\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n' \ + "$SMTP_USER" "$NOTIFY_EMAIL_TO" "$SUBJECT" "$BODY" | \ + curl --silent --show-error \ + --url "smtp://mail.loverde.com.br:587" \ + --ssl-reqd \ + --mail-from "$SMTP_USER" \ + --mail-rcpt "$NOTIFY_EMAIL_TO" \ + --user "$SMTP_USER:$SMTP_PASS" \ + --upload-file - || true From 0e575481cbcf0fc7b2493c37cdff46547cea48b1 Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 15:00:05 -0300 Subject: [PATCH 4/6] style(ci): send CI notification email as branded HTML, not plain text 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. --- .gitea/workflows/test.yml | 89 +++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index c6265da..1a6606b 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -33,16 +33,87 @@ jobs: NOTIFY_EMAIL_TO: ${{ secrets.NOTIFY_EMAIL_TO }} run: | STATUS="${{ job.status }}" - SUBJECT="[PediFoods CI] ${STATUS} - ${{ github.workflow }}" - BODY="Job: ${{ github.workflow }} / ${{ github.job }} - Status: ${STATUS} - Branch: ${{ github.ref_name }} - Commit: ${{ github.sha }} - Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + WORKFLOW="${{ github.workflow }}" + JOB="${{ github.job }}" + BRANCH="${{ github.ref_name }}" + COMMIT="${{ github.sha }}" + COMMIT_SHORT="${COMMIT:0:7}" + RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + YEAR="$(date +%Y)" - printf 'From: PediFoods CI <%s>\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n' \ - "$SMTP_USER" "$NOTIFY_EMAIL_TO" "$SUBJECT" "$BODY" | \ - curl --silent --show-error \ + if [ "$STATUS" = "success" ]; then + BADGE_BG="#dcfce7"; BADGE_FG="#166534"; BADGE_LABEL="SUCESSO"; TITLE="Pipeline concluído com sucesso" + else + BADGE_BG="#fee2e2"; BADGE_FG="#991b1b"; BADGE_LABEL="FALHA"; TITLE="Pipeline falhou" + fi + + SUBJECT="[PediFoods CI] ${BADGE_LABEL} - ${WORKFLOW}" + + HTML_BODY=$(cat < + + + + + ${SUBJECT} + + + +
+

🚀 Atomenta CI

+
+
${BADGE_LABEL}
+

${TITLE}

+
+
Workflow:${WORKFLOW}
+
Job:${JOB}
+
Branch:${BRANCH}
+
Commit:${COMMIT_SHORT}
+
+ +
+ +
+ + + HTMLEOF + ) + + { + printf 'From: Atomenta CI <%s>\r\n' "$SMTP_USER" + printf 'To: %s\r\n' "$NOTIFY_EMAIL_TO" + printf 'Subject: %s\r\n' "$SUBJECT" + printf 'MIME-Version: 1.0\r\n' + printf 'Content-Type: text/html; charset=UTF-8\r\n' + printf '\r\n' + printf '%s\r\n' "$HTML_BODY" + } | curl --silent --show-error \ --url "smtp://mail.loverde.com.br:587" \ --ssl-reqd \ --mail-from "$SMTP_USER" \ From fdf751760929c836627cc63753d10c3949ff7306 Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 15:00:43 -0300 Subject: [PATCH 5/6] style(ci): send CI notification email as branded HTML, not plain text Same as test.yml's Email notification step. --- .gitea/workflows/beta.yml | 89 +++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/beta.yml b/.gitea/workflows/beta.yml index 9ebbf07..a53d469 100644 --- a/.gitea/workflows/beta.yml +++ b/.gitea/workflows/beta.yml @@ -61,16 +61,87 @@ jobs: NOTIFY_EMAIL_TO: ${{ secrets.NOTIFY_EMAIL_TO }} run: | STATUS="${{ job.status }}" - SUBJECT="[PediFoods CI] ${STATUS} - ${{ github.workflow }}" - BODY="Job: ${{ github.workflow }} / ${{ github.job }} - Status: ${STATUS} - Branch: ${{ github.ref_name }} - Commit: ${{ github.sha }} - Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + WORKFLOW="${{ github.workflow }}" + JOB="${{ github.job }}" + BRANCH="${{ github.ref_name }}" + COMMIT="${{ github.sha }}" + COMMIT_SHORT="${COMMIT:0:7}" + RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + YEAR="$(date +%Y)" - printf 'From: PediFoods CI <%s>\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n' \ - "$SMTP_USER" "$NOTIFY_EMAIL_TO" "$SUBJECT" "$BODY" | \ - curl --silent --show-error \ + if [ "$STATUS" = "success" ]; then + BADGE_BG="#dcfce7"; BADGE_FG="#166534"; BADGE_LABEL="SUCESSO"; TITLE="Pipeline concluído com sucesso" + else + BADGE_BG="#fee2e2"; BADGE_FG="#991b1b"; BADGE_LABEL="FALHA"; TITLE="Pipeline falhou" + fi + + SUBJECT="[PediFoods CI] ${BADGE_LABEL} - ${WORKFLOW}" + + HTML_BODY=$(cat < + + + + + ${SUBJECT} + + + +
+

🚀 Atomenta CI

+
+
${BADGE_LABEL}
+

${TITLE}

+
+
Workflow:${WORKFLOW}
+
Job:${JOB}
+
Branch:${BRANCH}
+
Commit:${COMMIT_SHORT}
+
+ +
+ +
+ + + HTMLEOF + ) + + { + printf 'From: Atomenta CI <%s>\r\n' "$SMTP_USER" + printf 'To: %s\r\n' "$NOTIFY_EMAIL_TO" + printf 'Subject: %s\r\n' "$SUBJECT" + printf 'MIME-Version: 1.0\r\n' + printf 'Content-Type: text/html; charset=UTF-8\r\n' + printf '\r\n' + printf '%s\r\n' "$HTML_BODY" + } | curl --silent --show-error \ --url "smtp://mail.loverde.com.br:587" \ --ssl-reqd \ --mail-from "$SMTP_USER" \ From 06439b6b45a8118cc54a9782e89491d6c94ca1bf Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 15:17:52 -0300 Subject: [PATCH 6/6] fix(ci): use table layout with fully inline CSS for the notification email The - -
-

🚀 Atomenta CI

-
-
${BADGE_LABEL}
-

${TITLE}

-
-
Workflow:${WORKFLOW}
-
Job:${JOB}
-
Branch:${BRANCH}
-
Commit:${COMMIT_SHORT}
-
- -
- -
+ + + +
+ + + + +
+ Atomenta +
+
${BADGE_LABEL}
+

${TITLE}

+ + + + + +
Workflow:${WORKFLOW}
Job:${JOB}
Branch:${BRANCH}
Commit:${COMMIT_SHORT}
+ +
© ${YEAR} Atomenta
+
HTMLEOF diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 1a6606b..970cdee 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -56,50 +56,29 @@ jobs: ${SUBJECT} - - -
-

🚀 Atomenta CI

-
-
${BADGE_LABEL}
-

${TITLE}

-
-
Workflow:${WORKFLOW}
-
Job:${JOB}
-
Branch:${BRANCH}
-
Commit:${COMMIT_SHORT}
-
- -
- -
+ + + +
+ + + + +
+ Atomenta +
+
${BADGE_LABEL}
+

${TITLE}

+ + + + + +
Workflow:${WORKFLOW}
Job:${JOB}
Branch:${BRANCH}
Commit:${COMMIT_SHORT}
+ +
© ${YEAR} Atomenta
+
HTMLEOF