1+ name : ProStore iOS Simulator Build
2+
3+ on :
4+ workflow_dispatch :
5+
6+ permissions :
7+ contents : write
8+
9+ jobs :
10+ build :
11+ runs-on : macos-15
12+ strategy :
13+ matrix :
14+ target : [device, simulator]
15+
16+ steps :
17+ - name : Checkout Repo
18+ uses : actions/checkout@v4
19+ with :
20+ fetch-depth : 0
21+ persist-credentials : true
22+
23+ - name : Show Xcode Version
24+ run : |
25+ echo "=== xcodebuild -version ==="
26+ xcodebuild -version || true
27+ echo "=== sw_vers ==="
28+ sw_vers || true
29+
30+ - name : Install Build Tools
31+ run : |
32+ # jq
33+ if ! command -v jq >/dev/null 2>&1; then
34+ echo "jq not found — attempting to install via brew"
35+ if command -v brew >/dev/null 2>&1; then
36+ brew install jq || true
37+ fi
38+ fi
39+
40+ # yq
41+ if ! command -v yq >/dev/null 2>&1; then
42+ echo "yq not found — attempting to install via brew"
43+ if command -v brew >/dev/null 2>&1; then
44+ brew install yq || true
45+ fi
46+ fi
47+
48+ # gh (GitHub CLI)
49+ if ! command -v gh >/dev/null 2>&1; then
50+ echo "gh not found — attempting to install via brew"
51+ if command -v brew >/dev/null 2>&1; then
52+ brew install gh || true
53+ fi
54+ fi
55+
56+ # xcodegen
57+ if ! command -v xcodegen >/dev/null 2>&1; then
58+ echo "xcodegen not found — attempting to install via brew"
59+ if command -v brew >/dev/null 2>&1; then
60+ brew install xcodegen || true
61+ fi
62+ fi
63+
64+ # Fallback: official gh installer script (works across platforms)
65+ if ! command -v gh >/dev/null 2>&1; then
66+ echo "gh still not found — attempting official installer script"
67+ curl -fsSL https://cli.github.com/install.sh | sh || true
68+ fi
69+
70+ echo "Tool versions (if installed):"
71+ echo "jq: $(jq --version 2>/dev/null || echo 'not installed')"
72+ echo "yq: $(yq --version 2>/dev/null || echo 'not installed')"
73+ echo "gh: $(gh --version 2>/dev/null || echo 'not installed')"
74+ echo "xcodegen: $(xcodegen --version 2>/dev/null || echo 'not installed')"
75+ shell : bash
76+
77+ - name : Check Project Files
78+ run : |
79+ echo "Workspace files:"
80+ ls -la || true
81+ echo "project.yml (first 200 lines):"
82+ sed -n '1,200p' project.yml || true
83+
84+ - name : Generate Xcode Project
85+ run : |
86+ set -e
87+ xcodegen generate --spec project.yml
88+ echo "Generated project at: $(pwd)/prostore.xcodeproj"
89+ echo "project.pbxproj header (first 60 lines):"
90+ sed -n '1,60p' prostore.xcodeproj/project.pbxproj || true
91+
92+ - name : Resolve Swift Packages
93+ run : |
94+ set -e
95+ echo "Resolving Swift package dependencies for prostore..."
96+ xcodebuild -resolvePackageDependencies -project prostore.xcodeproj -scheme prostore -configuration Release
97+
98+ # ===== DEVICE build branch (matrix.target == device) =====
99+ - name : Build Device (archive + package unsigned .ipa)
100+ if : matrix.target == 'device'
101+ id : build_device
102+ run : |
103+ set -e
104+ ARCHIVE_PATH="$PWD/build/prostore.xcarchive"
105+ mkdir -p build
106+
107+ # Try to read cached objectVersion from file
108+ CACHE_FILE=".object_version_cache"
109+ CACHED_VERSION=""
110+ candidates=(77 70 63 60 56 55)
111+
112+ if [ -f "$CACHE_FILE" ]; then
113+ CACHED_VERSION=$(cat "$CACHE_FILE" | tr -d '\n')
114+ echo "Found cached objectVersion: $CACHED_VERSION"
115+
116+ # Test the cached version first
117+ echo "----- Testing cached objectVersion = $CACHED_VERSION -----"
118+ /usr/bin/perl -0777 -pe "s/objectVersion = \\d+;/objectVersion = $CACHED_VERSION;/" -i.bak prostore.xcodeproj/project.pbxproj || true
119+
120+ set +e
121+ xcodebuild clean archive \
122+ -project prostore.xcodeproj \
123+ -scheme prostore \
124+ -archivePath "$ARCHIVE_PATH" \
125+ -sdk iphoneos \
126+ -configuration Release \
127+ CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO
128+ rc=$?
129+ set -e
130+
131+ if [ $rc -eq 0 ]; then
132+ echo "✅ Cached objectVersion $CACHED_VERSION works!"
133+ echo "object_version=$CACHED_VERSION" >> $GITHUB_OUTPUT
134+ echo "$CACHED_VERSION" > "$CACHE_FILE"
135+ else
136+ echo "❌ Cached objectVersion $CACHED_VERSION failed, trying other candidates..."
137+ fi
138+ fi
139+
140+ # If no cache or cached version failed, try all candidates
141+ if [ -z "$CACHED_VERSION" ] || [ ! -f "$ARCHIVE_PATH" ]; then
142+ build_ok=0
143+ for v in "${candidates[@]}"; do
144+ echo "----- Attempting build with objectVersion = $v -----"
145+ /usr/bin/perl -0777 -pe "s/objectVersion = \\d+;/objectVersion = $v;/" -i.bak prostore.xcodeproj/project.pbxproj || true
146+ echo "Applied objectVersion $v. Header preview:"
147+ sed -n '1,20p' prostore.xcodeproj/project.pbxproj || true
148+ echo "Running xcodebuild archive..."
149+ set +e
150+ xcodebuild clean archive \
151+ -project prostore.xcodeproj \
152+ -scheme prostore \
153+ -archivePath "$ARCHIVE_PATH" \
154+ -sdk iphoneos \
155+ -configuration Release \
156+ CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO
157+ rc=$?
158+ set -e
159+ if [ $rc -eq 0 ]; then
160+ echo "✅ xcodebuild succeeded with objectVersion = $v"
161+ echo "object_version=$v" >> $GITHUB_OUTPUT
162+ echo "$v" > "$CACHE_FILE"
163+ echo "Cached successful objectVersion: $v"
164+ build_ok=1
165+ break
166+ else
167+ echo "❌ xcodebuild failed (exit $rc). Trying next objectVersion..."
168+ fi
169+ done
170+
171+ if [ "${build_ok:-0}" -ne 1 ] && [ ! -d "$ARCHIVE_PATH" ]; then
172+ echo "ERROR: All objectVersion attempts failed. Dumping debug info:"
173+ echo "===== project.pbxproj header ====="
174+ sed -n '1,200p' prostore.xcodeproj/project.pbxproj || true
175+ echo "===== build directory listing ====="
176+ ls -la build || true
177+ exit 74
178+ fi
179+ fi
180+
181+ # Package Device .ipa (unsigned)
182+ APP_PATH="$ARCHIVE_PATH/Products/Applications/prostore.app"
183+ OUTPUT_IPA="build/com.prostoreios.prostore-unsigned-ios.ipa"
184+ echo "Device app path: $APP_PATH"
185+ if [ ! -d "$APP_PATH" ]; then
186+ echo "ERROR: App not found at expected path. Attempting fallback packaging from archive contents..."
187+ ls -la "$ARCHIVE_PATH" || true
188+ # attempt to find .app anywhere in archive
189+ FOUND_APP=$(find "$ARCHIVE_PATH" -name "*.app" -type d | head -n1 || true)
190+ if [ -n "$FOUND_APP" ]; then
191+ APP_PATH="$FOUND_APP"
192+ else
193+ echo "No .app found — failing device package step."
194+ exit 1
195+ fi
196+ fi
197+
198+ mkdir -p build/Payload
199+ rm -rf build/Payload/* || true
200+ cp -R "$APP_PATH" build/Payload/
201+ (cd build && zip -r "$(basename "$OUTPUT_IPA")" Payload) || exit 1
202+ echo "Created device ipa: $OUTPUT_IPA"
203+ ls -la "$OUTPUT_IPA" || true
204+
205+ - name : Set device artifact path output
206+ if : matrix.target == 'device'
207+ id : set_device_art
208+ run : |
209+ IPA="build/com.prostoreios.prostore-unsigned-ios.ipa"
210+ if [ -f "$IPA" ]; then
211+ echo "ipa_path=$IPA" >> $GITHUB_OUTPUT
212+ else
213+ echo "ipa_path=" >> $GITHUB_OUTPUT
214+ fi
215+
216+ - name : Upload Device IPA artifact
217+ if : matrix.target == 'device' && steps.set_device_art.outputs.ipa_path != ''
218+ uses : actions/upload-artifact@v4
219+ with :
220+ name : com.prostoreios.prostore-unsigned-ios.ipa
221+ path : ${{ steps.set_device_art.outputs.ipa_path }}
222+
223+ # ===== SIMULATOR build branch (matrix.target == simulator) =====
224+ - name : Build simulator (iphonesimulator) and package simulator .ipa (if possible)
225+ if : matrix.target == 'simulator'
226+ id : build_sim
227+ run : |
228+ set -e
229+ echo "Starting simulator build (won't fail the entire job if it fails)."
230+ SIM_DERIVED="$PWD/build/sim_derived"
231+ rm -rf "$SIM_DERIVED"
232+ set +e
233+ xcodebuild clean build \
234+ -project prostore.xcodeproj \
235+ -scheme prostore \
236+ -configuration Release \
237+ -sdk iphonesimulator \
238+ -derivedDataPath "$SIM_DERIVED" \
239+ CODE_SIGNING_ALLOWED=NO CODE_SIGN_IDENTITY=""
240+ rc=$?
241+ set -e
242+ if [ $rc -ne 0 ]; then
243+ echo "Warning: simulator build failed (exit $rc). Will attempt to recover packaging if a simulator .app exists."
244+ fi
245+
246+ POSSIBLE_APPS=(
247+ "$SIM_DERIVED/Build/Products/Release-iphonesimulator/prostore.app"
248+ "$SIM_DERIVED/Build/Products/Debug-iphonesimulator/prostore.app"
249+ "$PWD/build/Build/Products/Release-iphonesimulator/prostore.app"
250+ "$PWD/build/Release-iphonesimulator/prostore.app"
251+ )
252+
253+ FOUND_APP=""
254+ for p in "${POSSIBLE_APPS[@]}"; do
255+ if [ -d "$p" ]; then
256+ FOUND_APP="$p"
257+ break
258+ fi
259+ done
260+
261+ if [ -z "$FOUND_APP" ]; then
262+ echo "Simulator .app not found in expected locations. Listing $SIM_DERIVED contents for debug:"
263+ ls -la "$SIM_DERIVED" || true
264+ echo "Skipping simulator .ipa packaging."
265+ exit 0
266+ fi
267+
268+ echo "Found simulator .app at: $FOUND_APP"
269+ SIM_IPA="build/com.prostoreios.prostore-unsigned-iossimulator.ipa"
270+ mkdir -p build/Payload-sim
271+ rm -rf build/Payload-sim/* || true
272+ cp -R "$FOUND_APP" build/Payload-sim/
273+ (cd build && zip -r "$(basename "$SIM_IPA")" Payload-sim) || exit 1
274+ echo "Created simulator ipa: $SIM_IPA"
275+ ls -la "$SIM_IPA" || true
276+
277+ - name : Set simulator artifact path output
278+ if : matrix.target == 'simulator'
279+ id : set_sim_art
280+ run : |
281+ IPA="build/com.prostoreios.prostore-unsigned-iossimulator.ipa"
282+ if [ -f "$IPA" ]; then
283+ echo "ipa_path=$IPA" >> $GITHUB_OUTPUT
284+ else
285+ echo "ipa_path=" >> $GITHUB_OUTPUT
286+ fi
287+
288+ - name : Upload Simulator IPA artifact
289+ if : matrix.target == 'simulator' && steps.set_sim_art.outputs.ipa_path != ''
290+ uses : actions/upload-artifact@v4
291+ with :
292+ name : com.prostoreios.prostore-unsigned-iossimulator.ipa
293+ path : ${{ steps.set_sim_art.outputs.ipa_path }}
0 commit comments