Przeglądaj źródła

Parameterize bundle ID and build workflow for identical sister-repo code (#690)

* Derive Fastlane bundle identifiers from app_suffix

Read app_suffix from LoopFollowDisplayNameConfig.xcconfig in the Fastfile and
build the app and Live Activity extension identifiers from it, replacing the
hardcoded com.<team>.LoopFollow strings. The same Fastfile now produces the
correct bundle identifiers in LoopFollow, LoopFollow_Second and
LoopFollow_Third without per-repo edits.

Update release.sh so the conflict notice no longer lists the Fastfile, which is
now identical across the sister repositories.

* Make swiftformat and build workflow identical across sister repos

Accept any LoopFollow* directory in Scripts/swiftformat.sh instead of only the
exact 'LoopFollow' folder, so formatting runs consistently in LoopFollow,
LoopFollow_Second and LoopFollow_Third.

Drive the variant name in build_LoopFollow.yml's not-configured summary from
${{ github.event.repository.name }} / ${{ env.UPSTREAM_REPO }} and drop the
variant name from two comments, so the workflow no longer hardcodes 'LoopFollow'
in cosmetic text. The only intentional per-repo differences left are the cron
minute and UPSTREAM_REPO.

* Keep swiftformat folder guard intact

The exact-'LoopFollow' folder check is intentional: it prevents swiftformat from
reformatting source files when the tree is checked out as a sister repo
(LoopFollow_Second / LoopFollow_Third), which would create spurious diffs. The
script stays identical across all three repos and skips by folder name at
runtime.

* Sync sister repos by mirroring instead of patching

Replace the git diff / git apply patch flow in release.sh's update_follower
with an rsync --delete mirror of the release tree into each sister repo,
followed by re-applying that instance's overlay (app_suffix, display_name,
UPSTREAM_REPO, cron) and renaming the workspace. The per-instance values are
derived from the sister directory name; only the staggered cron minute is
passed in.

Mirroring makes each sister an exact copy of the release tree except for the
overlay, so transfers no longer hit patch-context conflicts and any accidental
drift in a sister is corrected automatically. Only .git, local/build dirs and
the local team override (LoopFollowConfigOverride.xcconfig) are protected from
the sync.

* Trim release.sh sync excludes to existing dirs only

Drop the DerivedData and LoopFollowConfigOverride.xcconfig rsync excludes — they
don't exist in any of the repos, so there is nothing to protect. The mirror now
only shields .git, .claude and build.

* Preserve each sister's cron minute instead of hardcoding it

Capture the sister's existing scheduled-build cron line before mirroring and
restore it afterwards, rather than passing hardcoded minutes (27/40) into
update_follower. Each repo keeps its own staggered build time with no magic
numbers in the call sites; if a repo has no cron line the mirrored default is
left in place.

* Define sister build minute in the release script, not the sister repo

Pass each app's staggered Sunday-build minute into update_follower and write it
during the overlay, instead of capturing it from the sister repo. All per-app
data lives in the main repo's release script; the sister owns nothing and is a
pure product of the mirror plus the overlay. A comment at the call site explains
the staggered minutes.
Jonas Björkert 1 tydzień temu
rodzic
commit
7937e19070
3 zmienionych plików z 78 dodań i 50 usunięć
  1. 6 6
      .github/workflows/build_LoopFollow.yml
  2. 35 14
      fastlane/Fastfile
  3. 37 30
      release.sh

+ 6 - 6
.github/workflows/build_LoopFollow.yml

@@ -131,10 +131,10 @@ jobs:
         if: steps.workflow-permission.outputs.has_permission != 'true'
         run: |
           echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY
-          echo "You have not yet configured the scheduled sync and build for LoopFollow's browser build." >> $GITHUB_STEP_SUMMARY
-          echo "Synchronizing your fork of <code>LoopFollow</code> with the upstream repository <code>loopandlearn/LoopFollow</code> will be skipped." >> $GITHUB_STEP_SUMMARY
-          echo "If you want to enable automatic builds and updates for your LoopFollow, please follow the instructions \
-              under the following path <code>LoopFollow/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
+          echo "You have not yet configured the scheduled sync and build for ${{ github.event.repository.name }}'s browser build." >> $GITHUB_STEP_SUMMARY
+          echo "Synchronizing your fork of <code>${{ github.event.repository.name }}</code> with the upstream repository <code>${{ env.UPSTREAM_REPO }}</code> will be skipped." >> $GITHUB_STEP_SUMMARY
+          echo "If you want to enable automatic builds and updates for your ${{ github.event.repository.name }}, please follow the instructions \
+              under the following path <code>${{ github.event.repository.name }}/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
 
       # Set a logic flag if this is the second instance of this day-of-week in this month
       - name: Check if this is the second time this day-of-week happens this month
@@ -185,7 +185,7 @@ jobs:
           (vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
           (vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )
 
-  # Builds LoopFollow
+  # Builds the app
   build:
     name: Build
     needs: [check_certs, check_status]
@@ -227,7 +227,7 @@ jobs:
       - name: Sync clock
         run: sudo sntp -sS time.windows.com
 
-      # Build signed LoopFollow IPA file
+      # Build signed IPA file
       - name: Fastlane Build & Archive
         run: bundle exec fastlane build_LoopFollow
         env:

+ 35 - 14
fastlane/Fastfile

@@ -23,6 +23,27 @@ DEVICE_NAME = ENV["DEVICE_NAME"]
 DEVICE_ID = ENV["DEVICE_ID"]
 ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120"
 
+# Reads the per-app suffix from LoopFollowDisplayNameConfig.xcconfig so the same
+# Fastfile produces the correct bundle identifiers in every sister repository
+# (LoopFollow, LoopFollow_Second, LoopFollow_Third) without per-repo edits.
+def loopfollow_app_suffix
+  root = GITHUB_WORKSPACE || File.expand_path("..", __dir__)
+  path = File.join(root, "LoopFollowDisplayNameConfig.xcconfig")
+  return "" unless File.exist?(path)
+
+  File.foreach(path) do |line|
+    next if line.strip.start_with?("//")
+    if (match = line.match(/^\s*app_suffix\s*=\s*(.*)$/))
+      return match[1].strip
+    end
+  end
+  ""
+end
+
+APP_SUFFIX = loopfollow_app_suffix
+APP_IDENTIFIER = "com.#{TEAMID}.LoopFollow#{APP_SUFFIX}"
+LA_IDENTIFIER = "#{APP_IDENTIFIER}.LoopFollowLAExtension"
+
 platform :ios do
   desc "Build Loop Follow"
   lane :build_LoopFollow do
@@ -40,7 +61,7 @@ platform :ios do
     )
 
     previous_build_number = latest_testflight_build_number(
-      app_identifier: "com.#{TEAMID}.LoopFollow",
+      app_identifier: APP_IDENTIFIER,
       api_key: api_key,
     )
 
@@ -55,8 +76,8 @@ platform :ios do
       type: "appstore",
       git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
       app_identifier: [
-        "com.#{TEAMID}.LoopFollow",
-        "com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"
+        APP_IDENTIFIER,
+        LA_IDENTIFIER
       ]
     )
 
@@ -66,14 +87,14 @@ platform :ios do
 
     update_code_signing_settings(
       path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",
-      profile_name: mapping["com.#{TEAMID}.LoopFollow"],
+      profile_name: mapping[APP_IDENTIFIER],
       code_sign_identity: "iPhone Distribution",
       targets: ["LoopFollow"]
     )
 
     update_code_signing_settings(
-      path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",    
-      profile_name: mapping["com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"],
+      path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",
+      profile_name: mapping[LA_IDENTIFIER],
       code_sign_identity: "iPhone Distribution",
       targets: ["LoopFollowLAExtensionExtension"]
     )
@@ -87,10 +108,10 @@ platform :ios do
       buildlog_path: 'buildlog',
       export_options: {
         provisioningProfiles: {
-          "com.#{TEAMID}.LoopFollow" => mapping["com.#{TEAMID}.LoopFollow"],
-          "com.#{TEAMID}.LoopFollow.LoopFollowLAExtension" => mapping["com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"]
+          APP_IDENTIFIER => mapping[APP_IDENTIFIER],
+          LA_IDENTIFIER => mapping[LA_IDENTIFIER]
         }
-      }	
+      }
     )
 
     copy_artifacts(
@@ -138,12 +159,12 @@ platform :ios do
       end
     end
 
-    configure_bundle_id("LoopFollow", "com.#{TEAMID}.LoopFollow", [
+    configure_bundle_id("LoopFollow", APP_IDENTIFIER, [
       Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS,
       Spaceship::ConnectAPI::BundleIdCapability::Type::PUSH_NOTIFICATIONS
     ])
-    
-    configure_bundle_id("LoopFollow Live Activity Extension", "com.#{TEAMID}.LoopFollow.LoopFollowLAExtension", [
+
+    configure_bundle_id("LoopFollow Live Activity Extension", LA_IDENTIFIER, [
       Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS
     ])
 
@@ -166,8 +187,8 @@ platform :ios do
       verbose: true,
       git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
       app_identifier: [
-        "com.#{TEAMID}.LoopFollow",
-	"com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"
+        APP_IDENTIFIER,
+        LA_IDENTIFIER
       ]
     )
   end

+ 37 - 30
release.sh

@@ -14,7 +14,6 @@ VERSION_FILE="Config.xcconfig"
 MARKETING_KEY="LOOP_FOLLOW_MARKETING_VERSION"
 DEV_BRANCH="dev"
 MAIN_BRANCH="main"
-PATCH_DIR="../${APP_NAME}_update_patches"
 # ---------------------------------------
 
 # --- functions here ---
@@ -26,38 +25,49 @@ queue_push() { push_cmds+=("git -C \"$(pwd)\" $*"); echo "+ [queued] (in $(pwd))
 
 update_follower () {
   local DIR="$1"
+  local build_minute="$2"                     # staggered Sunday-build minute (see calls below)
+  local suffix=".${DIR#${APP_NAME}_}"        # LoopFollow_Second  -> .Second
+  local display="$DIR"                        # LoopFollow_Second
+  local upstream="loopandlearn/${DIR}"        # loopandlearn/LoopFollow_Second
+
   echo; echo "🔄  Updating $DIR …"
   cd "$DIR"
 
-  echo; echo "If there are custom changes needed to the patch, make the change before continuing"
-  pause
-
   # 1 · Make sure we’re on a clean, up-to-date main
   echo_run git switch "$MAIN_BRANCH"
   echo_run git fetch
   echo_run git pull
 
-  # 2 · Apply the patch
-  if ! git apply --whitespace=nowarn "$PATCH_FILE"; then
-    echo "‼️  Some changes could not be applied, so no changes were made."
-    echo "The command used was: git apply --whitespace=nowarn $PATCH_FILE"
-    echo; echo "Use a different terminal to fix and apply the patch before continuing"
-    pause
+  # 2 · Full mirror of the release tree from the primary repo.
+  #     Every tracked file (including the overlay files) is synced; only git
+  #     metadata and local/build dirs are protected. --delete makes the tree an
+  #     exact mirror, auto-correcting any drift.
+  echo_run rsync -a --delete \
+    --exclude='.git/' \
+    --exclude='.claude/' \
+    --exclude='build/' \
+    "$PRIMARY_ABS_PATH"/ ./
+
+  # 3 · Re-apply this instance's overlay on top of the mirror
+  perl -i -pe "s|^app_suffix\s*=.*|app_suffix = ${suffix}|"      LoopFollowDisplayNameConfig.xcconfig
+  perl -i -pe "s|^display_name\s*=.*|display_name = ${display}|" LoopFollowDisplayNameConfig.xcconfig
+  perl -i -pe "s|^(\s*)UPSTREAM_REPO:.*|\${1}UPSTREAM_REPO: ${upstream}|" .github/workflows/build_LoopFollow.yml
+  perl -i -pe "s|^(\s*)- cron:.*|\${1}- cron: \"${build_minute} 10 * * 0\" # Sunday at UTC 10:${build_minute}|" .github/workflows/build_LoopFollow.yml
+
+  # 4 · Rename the synced workspace to this instance's name
+  rm -rf "${DIR}.xcworkspace"
+  if [ -d "${APP_NAME}.xcworkspace" ]; then
+    mv "${APP_NAME}.xcworkspace" "${DIR}.xcworkspace"
   fi
 
-  # 3 · Pause if any conflict markers remain
-  if git ls-files -u | grep -q .; then
-    echo "⚠️  Conflicts detected."
-    echo "    If Fastfile or build_LoopFollow.yml were modified, these are expected."
-    echo "    Open your merge tool, resolve, then press Enter."
-    pause
+  # 5 · Single commit capturing the mirror + overlay
+  git add -A
+  if git diff --cached --quiet; then
+    echo "✓  $DIR already up to date — nothing to commit."
+  else
+    echo_run git commit -m "transfer v${new_ver} updates from LF to ${DIR}"
   fi
 
-  # 4 · Single commit capturing all staged changes
-  git add -u
-  git add $(git ls-files --others --exclude-standard) 2>/dev/null || true
-  git commit -m "transfer v${new_ver} updates from LF to ${DIR}"
-
   echo_run git status
   echo "💻  Build & test $DIR now."; pause  # build & test checkpoint
   queue_push push origin "$MAIN_BRANCH"
@@ -124,16 +134,13 @@ echo_run git commit -m "update version to ${new_ver}" "$VERSION_FILE"
 echo "💻  Build & test release branch now."; pause
 queue_push push origin "$RELEASE_BRANCH"
 
-# --- create a patch from main..release branch (includes the bump) -----
-mkdir -p "$PATCH_DIR"
-PATCH_FILE="${PATCH_DIR}/LF_diff_${old_ver}_to_${new_ver}.patch"
-
-git diff -M --binary "$MAIN_BRANCH" "$RELEASE_BRANCH"  \
-  > "$PATCH_FILE"
-
+# --- mirror the release tree into the sister repos ----
+# Second arg = each app's scheduled browser-build minute (Sundays at 10:xx UTC),
+# staggered from the main app (:17) so a user who forked all three apps doesn't
+# trigger three simultaneous builds.
 cd ..
-update_follower "$SECOND_DIR"
-update_follower "$THIRD_DIR"
+update_follower "$SECOND_DIR" "27"
+update_follower "$THIRD_DIR"  "40"
 
 # ---------- GitHub Actions Test ---------
 echo;