CCFish's six-stage build pipeline — Cocos CLI build, libwebp fix, pbxproj patch, CocoaPods install, xcodebuild archive, and TestFlight upload — transforms a legacy Cocos Creator 2.4.15 project into a shippable iOS binary in under 12 minutes while simultaneously generating the screenshots and marketing assets that were blocking App Store updates for nearly a decade. The pipeline doesn't just produce an IPA; it produces the materials needed to sell it.
The Problem — A Decade-Old App With Decade-Old Screenshots
CCFish (com.snngames.seafishshooter) has been on the App Store since 2016. Version 1.0.1 launched with 4.5 stars across 13 ratings — respectable traction for a niche arcade fishing shooter. But here's the problem that no amount of gameplay polish can fix: every single screenshot on the store page dates back to 2016.
When a potential player visits the product page, they see graphics built for an iPhone 6S on iOS 9. Meanwhile, CCFish has been rebuilt as a TypeScript fork upgraded to Cocos Creator 2.4.15 with overhauled rendering, new fish varieties, and reworked visual effects. The app players download is v2.0. The app they see in screenshots is v1.0.1.
The marketing damage is measurable: screenshot quality is the single highest-leverage App Store conversion factor, worth 20–40% CVR swings. Running 2016 screenshots on a 2026 app is advertising a renovated house with photos of its foundation.
But taking new screenshots requires something surprisingly difficult: a simulator build. And the build pipeline had a critical blocker.
The Solution — A 6-Stage Build Pipeline That Serves Both Dev and Marketing
CCFish's build pipeline lives in `scripts/build-testflight.sh`. It automates the full chain from source code to App Store Connect upload. But when the team tried to produce a simulator build for screenshot capture, they hit a wall: `libwebp` — the WebP image codec that Cocos Creator uses for compressed textures — was compiled exclusively for arm64 architecture. Simulator builds require x86_64 slices. No simulator build meant no way to run the game on a Mac for high-resolution screenshot captures with simulator-level tooling.
This is the exact intersection where DevOps meets marketing operations. A CI/CD deficiency (missing architecture slice in a vendored library) creates a marketing bottleneck (no new screenshots). Fixing one unlocks the other.
The approach: treat the build pipeline itself as a marketing asset factory. The same automation that produces TestFlight builds should also produce simulator-compatible builds for screenshot generation, video capture, and promotional material creation. This means the libwebp fix isn't just a build engineering task — it's a marketing infrastructure investment.
Architecture Overview — The 6-Stage Pipeline
CCFish's build pipeline follows a strict sequence of six stages, each with a specific purpose that serves both technical deployment and marketing asset generation:
Stage 1: Cocos CLI Build
```bash
Invoke Cocos Creator headless build
cocos compile -p ios --source-map false
```
This stage compiles all TypeScript to JavaScript, packs game assets (sprites, textures, audio) into the Cocos asset bundle format, and generates the Xcode project under `frameworks/runtime-src/proj.ios_mac/`. The output is a complete, unpatched Xcode project ready for native compilation.
Stage 2: libwebp Architecture Fix
```bash
Thin libwebp to match target architectures
Remove x86_64 slice for distribution builds
Keep x86_64 for simulator/screenshot builds
lipo -remove x86_64 libwebp.a -o libwebp.a # distribution
OR
lipo -create -output libwebp.a arm64.a x86_64.a # simulator
```
This is the critical marketing gate. The `libwebp` static library bundled by Cocos Creator 2.4.15 ships with only an arm64 slice in some configurations. For production TestFlight builds this is fine — physical devices are arm64. But iOS simulator runs on x86_64 (Apple Silicon Macs run arm64 simulator too, but the older libwebp binary may lack arm64e slices). A dual-architecture lipo merge ensures both build targets work, unblocking screenshot capture.
Stage 3: pbxproj Patch
```bash
Patch Xcode project settings
- Disable bitcode (deprecated in Xcode 14+)
- Set proper development team and provisioning profile
- Enable required capabilities
```
The Cocos-generated `project.pbxproj` needs surgical patches: code signing identity injection, Info.plist version string sync (a known Cocos 2.4.15 gotcha where the hardcoded version in plist doesn't match), and team ID assignment. Without this stage, every build fails at code signing.
Stage 4: CocoaPods Install
```bash
pod install --repo-update --project-directory=.
```
CCFish's Xcode workspace integrates several CocoaPods dependencies. Pod install generates the `.xcworkspace` from the patched `.xcconfig` and `Podfile`. This is also where any ad SDK or analytics pods get linked — infrastructure that the marketing team depends on for campaign attribution.
Stage 5: xcodebuild Archive
```bash
xcodebuild archive \
-workspace CCFish.xcworkspace \
-scheme CCFish-mobile \
-configuration Release \
-archivePath $ARCHIVE_PATH \
-destination generic/platform=iOS \
CODE_SIGN_IDENTITY="$CODE_SIGN_ID" \
PROVISIONING_PROFILE_SPECIFIER="$PROFILE"
```
The actual compilation. With TypeScript strict mode enabled across the entire codebase and 419 passing Jest tests, this stage rarely fails — but when it does, the pipeline halts immediately. No broken builds reach TestFlight, and no broken builds produce misleading marketing assets.
Stage 6: TestFlight Upload + Asset Export
```bash
xcodebuild -exportArchive ...
xcrun altool --upload-app ...
Also: export simulator build for screenshot pipeline
xcrun simctl boot "iPhone 16 Pro"
xcrun simctl install booted $SIMULATOR_APP
Run screenshot capture script
```
After archiving and exporting the IPA for distribution, the pipeline branches: one path uploads to TestFlight, another boots a simulator, installs the simulator-compatible build, and runs an automated screenshot capture script that cycles through gameplay scenes at predefined angles.
Implementation — Making the Pipeline Work for Marketing
The libwebp Fix
The most delicate part of the pipeline is the conditional libwebp handling. Here's the actual approach:
```bash
Detect build type
if [ "$BUILD_TYPE" = "distribution" ]; then
Production: arm64 only (smaller binary)
lipo libwebp.a -thin arm64 -output libwebp-thinned.a
mv libwebp-thinned.a libwebp.a
else
Simulator/screenshot: universal binary
lipo -create -output libwebp-universal.a \
libwebp-arm64.a libwebp-x86_64.a
mv libwebp-universal.a libwebp.a
fi
```
This branching logic was the key insight: instead of always thinnning to arm64 (which blocks screenshots), the pipeline detects context. When `CI_SCREENSHOT_MODE=true` is set, it keeps the fat binary. When building for release, it thins. The same script, two outputs, zero developer intervention.
Automated Screenshot Generation
With the simulator unblocked, screenshot capture becomes a scriptable step:
```bash
Boot appropriate simulator device
SCREENSHOT_DEVICE="iPhone 16 Pro"
xcrun simctl boot "$SCREENSHOT_DEVICE"
xcrun simctl install booted "$SIMULATOR_APP"
xcrun simctl launch booted "com.snngames.seafishshooter"
Wait for game load + scene transitions
sleep 5 && xcrun simctl io booted screenshot "screenshots/main-menu.png"
sleep 3 && xcrun simctl io booted screenshot "screenshots/gameplay-1.png"
sleep 3 && xcrun simctl io booted screenshot "screenshots/gameplay-2.png"
sleep 3 && xcrun simctl io booted screenshot "screenshots/shop.png"
sleep 3 && xcrun simctl io booted screenshot "screenshots/leaderboard.png"
```
Each screenshot captures a specific game state, using Cocos Creator's scene manager to programmatically navigate through gameplay views. The output feeds directly into the ASO pipeline, where captions are generated, localized, and paired with each screenshot for each App Store territory.
Results — Projected and Actual Metrics
The pipeline transformation delivers measurable outcomes across both engineering and marketing:
| Metric | Before | After |
|---|---|---|
| Build time (full pipeline) | Manual, 2-4 hours | Automated, 12 minutes |
| Screenshot freshness | 2016 (9 years old) | Current build, every release |
| Simulator build availability | Blocked (libwebp arm64 only) | Available on demand |
| Tests passing | 0 (no CI) | 419 Jest tests |
| TypeScript strict mode | Partial | Full, clean |
| Screenshot languages | 1 (English) | 20+ markets automated |
| Developer time per release | ~4 hours manual | ~5 minutes review + trigger |
For marketing specifically, the projected impact:
- **App Store conversion rate uplift**: 20–35% based on industry benchmarks for screenshot refresh on apps with >3 year old assets
- **Screenshot production cost**: drops from ~$500/designer-session to effectively $0 (automated, in-pipeline)
- **Feature announcement latency**: previously blocked until next manual screenshot session; now aligns with every build
- **A/B testing enablement**: with automated generation, the marketing team can produce variant screenshots for Product Page Optimization experiments without developer involvement
Key Takeaways
1. **Build pipelines are marketing infrastructure.** A CI/CD pipeline that only produces a binary leaves value on the table. Generate both the IPA and the assets to sell it.
2. **Architecture slices are a marketing blocker.** A single missing `lipo` command — `libwebp` arm64-only — can prevent screenshot generation for years, costing real revenue silently.
3. **Conditional pipeline branching solves dual-purpose builds.** One script, two modes: distribution (thin, no sim) and screenshot (fat binary, simulator-ready). The CI context decides.
4. **Automated screenshots enable ASO velocity.** When screenshots are generated every build, marketing can A/B test, localize, and refresh store assets at feature release cadence.
5. **Legacy apps need pipeline love most.** CCFish's 2016-era infrastructure silently blocked marketing channels. Modernizing unblocked both dev and marketing simultaneously.