Skip to content

Commit 7937e19

Browse files
authored
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.
1 parent 8bd6575 commit 7937e19

3 files changed

Lines changed: 78 additions & 50 deletions

File tree

.github/workflows/build_LoopFollow.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ jobs:
131131
if: steps.workflow-permission.outputs.has_permission != 'true'
132132
run: |
133133
echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY
134-
echo "You have not yet configured the scheduled sync and build for LoopFollow's browser build." >> $GITHUB_STEP_SUMMARY
135-
echo "Synchronizing your fork of <code>LoopFollow</code> with the upstream repository <code>loopandlearn/LoopFollow</code> will be skipped." >> $GITHUB_STEP_SUMMARY
136-
echo "If you want to enable automatic builds and updates for your LoopFollow, please follow the instructions \
137-
under the following path <code>LoopFollow/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
134+
echo "You have not yet configured the scheduled sync and build for ${{ github.event.repository.name }}'s browser build." >> $GITHUB_STEP_SUMMARY
135+
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
136+
echo "If you want to enable automatic builds and updates for your ${{ github.event.repository.name }}, please follow the instructions \
137+
under the following path <code>${{ github.event.repository.name }}/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
138138
139139
# Set a logic flag if this is the second instance of this day-of-week in this month
140140
- name: Check if this is the second time this day-of-week happens this month
@@ -185,7 +185,7 @@ jobs:
185185
(vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
186186
(vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )
187187
188-
# Builds LoopFollow
188+
# Builds the app
189189
build:
190190
name: Build
191191
needs: [check_certs, check_status]
@@ -227,7 +227,7 @@ jobs:
227227
- name: Sync clock
228228
run: sudo sntp -sS time.windows.com
229229

230-
# Build signed LoopFollow IPA file
230+
# Build signed IPA file
231231
- name: Fastlane Build & Archive
232232
run: bundle exec fastlane build_LoopFollow
233233
env:

fastlane/Fastfile

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@ DEVICE_NAME = ENV["DEVICE_NAME"]
2323
DEVICE_ID = ENV["DEVICE_ID"]
2424
ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120"
2525

26+
# Reads the per-app suffix from LoopFollowDisplayNameConfig.xcconfig so the same
27+
# Fastfile produces the correct bundle identifiers in every sister repository
28+
# (LoopFollow, LoopFollow_Second, LoopFollow_Third) without per-repo edits.
29+
def loopfollow_app_suffix
30+
root = GITHUB_WORKSPACE || File.expand_path("..", __dir__)
31+
path = File.join(root, "LoopFollowDisplayNameConfig.xcconfig")
32+
return "" unless File.exist?(path)
33+
34+
File.foreach(path) do |line|
35+
next if line.strip.start_with?("//")
36+
if (match = line.match(/^\s*app_suffix\s*=\s*(.*)$/))
37+
return match[1].strip
38+
end
39+
end
40+
""
41+
end
42+
43+
APP_SUFFIX = loopfollow_app_suffix
44+
APP_IDENTIFIER = "com.#{TEAMID}.LoopFollow#{APP_SUFFIX}"
45+
LA_IDENTIFIER = "#{APP_IDENTIFIER}.LoopFollowLAExtension"
46+
2647
platform :ios do
2748
desc "Build Loop Follow"
2849
lane :build_LoopFollow do
@@ -40,7 +61,7 @@ platform :ios do
4061
)
4162

4263
previous_build_number = latest_testflight_build_number(
43-
app_identifier: "com.#{TEAMID}.LoopFollow",
64+
app_identifier: APP_IDENTIFIER,
4465
api_key: api_key,
4566
)
4667

@@ -55,8 +76,8 @@ platform :ios do
5576
type: "appstore",
5677
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
5778
app_identifier: [
58-
"com.#{TEAMID}.LoopFollow",
59-
"com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"
79+
APP_IDENTIFIER,
80+
LA_IDENTIFIER
6081
]
6182
)
6283

@@ -66,14 +87,14 @@ platform :ios do
6687

6788
update_code_signing_settings(
6889
path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",
69-
profile_name: mapping["com.#{TEAMID}.LoopFollow"],
90+
profile_name: mapping[APP_IDENTIFIER],
7091
code_sign_identity: "iPhone Distribution",
7192
targets: ["LoopFollow"]
7293
)
7394

7495
update_code_signing_settings(
75-
path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",
76-
profile_name: mapping["com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"],
96+
path: "#{GITHUB_WORKSPACE}/LoopFollow.xcodeproj",
97+
profile_name: mapping[LA_IDENTIFIER],
7798
code_sign_identity: "iPhone Distribution",
7899
targets: ["LoopFollowLAExtensionExtension"]
79100
)
@@ -87,10 +108,10 @@ platform :ios do
87108
buildlog_path: 'buildlog',
88109
export_options: {
89110
provisioningProfiles: {
90-
"com.#{TEAMID}.LoopFollow" => mapping["com.#{TEAMID}.LoopFollow"],
91-
"com.#{TEAMID}.LoopFollow.LoopFollowLAExtension" => mapping["com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"]
111+
APP_IDENTIFIER => mapping[APP_IDENTIFIER],
112+
LA_IDENTIFIER => mapping[LA_IDENTIFIER]
92113
}
93-
}
114+
}
94115
)
95116

96117
copy_artifacts(
@@ -138,12 +159,12 @@ platform :ios do
138159
end
139160
end
140161

141-
configure_bundle_id("LoopFollow", "com.#{TEAMID}.LoopFollow", [
162+
configure_bundle_id("LoopFollow", APP_IDENTIFIER, [
142163
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS,
143164
Spaceship::ConnectAPI::BundleIdCapability::Type::PUSH_NOTIFICATIONS
144165
])
145-
146-
configure_bundle_id("LoopFollow Live Activity Extension", "com.#{TEAMID}.LoopFollow.LoopFollowLAExtension", [
166+
167+
configure_bundle_id("LoopFollow Live Activity Extension", LA_IDENTIFIER, [
147168
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS
148169
])
149170

@@ -166,8 +187,8 @@ platform :ios do
166187
verbose: true,
167188
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
168189
app_identifier: [
169-
"com.#{TEAMID}.LoopFollow",
170-
"com.#{TEAMID}.LoopFollow.LoopFollowLAExtension"
190+
APP_IDENTIFIER,
191+
LA_IDENTIFIER
171192
]
172193
)
173194
end

release.sh

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ VERSION_FILE="Config.xcconfig"
1414
MARKETING_KEY="LOOP_FOLLOW_MARKETING_VERSION"
1515
DEV_BRANCH="dev"
1616
MAIN_BRANCH="main"
17-
PATCH_DIR="../${APP_NAME}_update_patches"
1817
# ---------------------------------------
1918

2019
# --- functions here ---
@@ -26,38 +25,49 @@ queue_push() { push_cmds+=("git -C \"$(pwd)\" $*"); echo "+ [queued] (in $(pwd))
2625

2726
update_follower () {
2827
local DIR="$1"
28+
local build_minute="$2" # staggered Sunday-build minute (see calls below)
29+
local suffix=".${DIR#${APP_NAME}_}" # LoopFollow_Second -> .Second
30+
local display="$DIR" # LoopFollow_Second
31+
local upstream="loopandlearn/${DIR}" # loopandlearn/LoopFollow_Second
32+
2933
echo; echo "🔄 Updating $DIR"
3034
cd "$DIR"
3135

32-
echo; echo "If there are custom changes needed to the patch, make the change before continuing"
33-
pause
34-
3536
# 1 · Make sure we’re on a clean, up-to-date main
3637
echo_run git switch "$MAIN_BRANCH"
3738
echo_run git fetch
3839
echo_run git pull
3940

40-
# 2 · Apply the patch
41-
if ! git apply --whitespace=nowarn "$PATCH_FILE"; then
42-
echo "‼️ Some changes could not be applied, so no changes were made."
43-
echo "The command used was: git apply --whitespace=nowarn $PATCH_FILE"
44-
echo; echo "Use a different terminal to fix and apply the patch before continuing"
45-
pause
41+
# 2 · Full mirror of the release tree from the primary repo.
42+
# Every tracked file (including the overlay files) is synced; only git
43+
# metadata and local/build dirs are protected. --delete makes the tree an
44+
# exact mirror, auto-correcting any drift.
45+
echo_run rsync -a --delete \
46+
--exclude='.git/' \
47+
--exclude='.claude/' \
48+
--exclude='build/' \
49+
"$PRIMARY_ABS_PATH"/ ./
50+
51+
# 3 · Re-apply this instance's overlay on top of the mirror
52+
perl -i -pe "s|^app_suffix\s*=.*|app_suffix = ${suffix}|" LoopFollowDisplayNameConfig.xcconfig
53+
perl -i -pe "s|^display_name\s*=.*|display_name = ${display}|" LoopFollowDisplayNameConfig.xcconfig
54+
perl -i -pe "s|^(\s*)UPSTREAM_REPO:.*|\${1}UPSTREAM_REPO: ${upstream}|" .github/workflows/build_LoopFollow.yml
55+
perl -i -pe "s|^(\s*)- cron:.*|\${1}- cron: \"${build_minute} 10 * * 0\" # Sunday at UTC 10:${build_minute}|" .github/workflows/build_LoopFollow.yml
56+
57+
# 4 · Rename the synced workspace to this instance's name
58+
rm -rf "${DIR}.xcworkspace"
59+
if [ -d "${APP_NAME}.xcworkspace" ]; then
60+
mv "${APP_NAME}.xcworkspace" "${DIR}.xcworkspace"
4661
fi
4762

48-
# 3 · Pause if any conflict markers remain
49-
if git ls-files -u | grep -q .; then
50-
echo "⚠️ Conflicts detected."
51-
echo " If Fastfile or build_LoopFollow.yml were modified, these are expected."
52-
echo " Open your merge tool, resolve, then press Enter."
53-
pause
63+
# 5 · Single commit capturing the mirror + overlay
64+
git add -A
65+
if git diff --cached --quiet; then
66+
echo "$DIR already up to date — nothing to commit."
67+
else
68+
echo_run git commit -m "transfer v${new_ver} updates from LF to ${DIR}"
5469
fi
5570

56-
# 4 · Single commit capturing all staged changes
57-
git add -u
58-
git add $(git ls-files --others --exclude-standard) 2>/dev/null || true
59-
git commit -m "transfer v${new_ver} updates from LF to ${DIR}"
60-
6171
echo_run git status
6272
echo "💻 Build & test $DIR now."; pause # build & test checkpoint
6373
queue_push push origin "$MAIN_BRANCH"
@@ -124,16 +134,13 @@ echo_run git commit -m "update version to ${new_ver}" "$VERSION_FILE"
124134
echo "💻 Build & test release branch now."; pause
125135
queue_push push origin "$RELEASE_BRANCH"
126136

127-
# --- create a patch from main..release branch (includes the bump) -----
128-
mkdir -p "$PATCH_DIR"
129-
PATCH_FILE="${PATCH_DIR}/LF_diff_${old_ver}_to_${new_ver}.patch"
130-
131-
git diff -M --binary "$MAIN_BRANCH" "$RELEASE_BRANCH" \
132-
> "$PATCH_FILE"
133-
137+
# --- mirror the release tree into the sister repos ----
138+
# Second arg = each app's scheduled browser-build minute (Sundays at 10:xx UTC),
139+
# staggered from the main app (:17) so a user who forked all three apps doesn't
140+
# trigger three simultaneous builds.
134141
cd ..
135-
update_follower "$SECOND_DIR"
136-
update_follower "$THIRD_DIR"
142+
update_follower "$SECOND_DIR" "27"
143+
update_follower "$THIRD_DIR" "40"
137144

138145
# ---------- GitHub Actions Test ---------
139146
echo;

0 commit comments

Comments
 (0)