Expo CI/CD: 11 Failures Fixed with GitHub Actions

Midnight. MacBook fan whirring like a jet engine. Eleventh Expo build failure in a row. Then—bliss. Testers' phones light up with my CRM app, zero bucks spent.

11 Brutal Fails Before My Expo CI/CD Pipeline Finally Delivered: A VibeCoder's Odyssey — theAIcatchup

Key Takeaways

  • 11 failures forged a zero-cost Expo CI/CD beast—persistence wins.
  • Self-hosted GitHub runners on Mac unlock unlimited iOS deploys.
  • Pin deps, nuke caches, unlock keychains: VibeCoder deployment gospel.

Sweat beading on my forehead, that 13-inch M5 MacBook Air humming like a spaceship pre-launch—another Expo build explodes in flames.

That’s the scene. Me, a VibeCoder, not typing code but dreaming it into existence via Claude AI, chasing the holy grail: a CI/CD pipeline that shoves my React Native CRM app straight onto therapists’ iPhones. Arc, my mobile beast for bodywork pros—28 files, 27 screens, 85% MVP done with Supabase humming in the backend. Code? Claude spits it out in minutes. But that last mile to testers? Pure hell.

Why VibeCoders Bleed on Deployment, Not Syntax

Look. AI’s the ultimate platform shift—like electricity flipping factories from steam. Suddenly, anyone’s a builder. I describe vibes; Claude codes screens. Energy surges. But push to main? Crickets. Or crashes.

TestFlight queues drag like molasses. Expo Go chokes on native modules. EAS cloud? Paywall for solos like me feels like robbery.

Enter the hero stack: Expo + GitHub Actions on a self-hosted runner (my Mac) + DeployGate. Push. Wait 10 minutes. Testers grab .ipa at a magic URL. Zero dollars. Unlimited.

But glory? Buried under 11 failures. Each a gut punch teaching VibeCoders why CI/CD’s the new superpower.

For a VibeCoder, this “last mile” is the biggest bottleneck — not writing code, but delivering what you’ve written.

Damn right. That’s the fire.

Push one: Boom. “Embed Pods Frameworks” crash at 3:54 AM.

Two workflows duked it out on main—one for DeployGate, one TestFlight. Mac’s Xcode choked, resources tapped out.

Fix? Banish TestFlight yaml to workflows-disabled purgatory. Simple mkdir, mv. Clean.

But wait—next push uploads 15MB binary blobs. .ipa and certs snuck into git. Rookie sin.

.gitignore to the rescue: build-*.ipa, AppleWWDRCAG3.cer. Purge history. Lesson screams: artifacts ain’t repo pets.

Is Self-Hosted GitHub Runner a MacBook Savior or Trap?

Failure four: Pods crash redux. Sneaky react-native bump from 0.83.2 to .4, plus react-native-worklets peer dep ambush from reanimated.

How’d I sleuth? Checkout last good commit’s package.json, npm ci, local eas build. Success!

Pin deps. Add the ghost dep. Stability reigns.

Five: Local builds fly, CI flops. Stale node_modules, Xcode DerivedData ghosts haunting /tmp.

Now here’s my unique twist—no article spells this—but these fails echo the Wright Brothers’ Kitty Hawk grind. Over 1,000 glider crashes before powered flight. VibeCoding’s the glider age: AI prototypes soar in sims, but real skies (deploys) demand brutal iteration. Prediction? In five years, AI will auto-pipe CI/CD too, but today’s pioneers like us forge the wings.

Workflow hack: Cache nuke step.

- name: Clean caches
  run: |
    rm -rf ~/Library/Developer/Xcode/DerivedData/Arc-* 
    rm -rf /tmp/eas-build-local-nodejs
    rm -rf /tmp/eas-cli-nodejs

Six: EAS snubs local creds, phones home. EAS_CREDENTIALS_SOURCE=local ignored by eas-cli 18.5.0.

Patch: eas.json decree.

"preview": {
  "distribution": "internal",
  "credentialsSource": "local"
}

Seven: credentials.json ghosts—gitignored, naturally. Checkout skips it.

Workflow copies from ~/client-crm/. Boom, present.

Eight—final boss tease: Code signs perfect, then “Embed Pods” at unlock. LaunchAgent runner can’t touch GUI keychain.

Unlock ritual:

- name: Unlock keychain
  run: security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db

GitHub Secrets holds the password. Sacred.

Attempt 12: Green across the board.

✔ Using local iOS credentials (credentials.json) ✔ Archive Succeeded ✔ Successfully exported and signed the ipa file ✔ Uploading to DeployGate… ✔ Distribution page updated

Git push to tester swipe: 10 minutes flat.

The Bulletproof Workflow That Ate 11 Corpses

Here’s the survivor, battle-scarred:

name: Build & Deploy to DeployGate
on:
  push:
    branches: [main]
  workflow_dispatch:
jobs:
  build-and-deploy:
    runs-on: self-hosted
    steps:
    - uses: actions/checkout@v4
    - name: Clean caches
      run: |
        rm -rf ~/Library/Developer/Xcode/DerivedData/Arc-*
        rm -rf /tmp/eas-build-local-nodejs
        rm -rf /tmp/eas-cli-nodejs
    - name: Install dependencies
      run: npm ci
    - name: Copy credentials
      run: |
        cp ~/client-crm/credentials.json ./credentials.json
        cp -r ~/client-crm/credentials ./credentials
    - name: Unlock keychain
      run: security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" ~/Library/Keychains/login.keychain-db
    - name: Build .ipa
      run: eas build --platform ios --profile preview --local
    # ... (DeployGate upload steps implied)

Adapt it. Your Mac’s the runner—install via GitHub docs. DeployGate? Free tier dev distributions, fixed URLs for testers. No App Store dance.

Energy restored. Arc lives on phones. Therapists log clients mid-massage.

But here’s the wonder: AI didn’t just code Arc. It vibe-fueled the pipeline debug—Claude dissected errors faster than Stack Overflow spelunking.

Platform shift complete. VibeCoders deploy like gods.

Short para punch: Scale it.

Long explore: Self-hosted shines for solos—your hardware, your rules. Cloud? Latency, costs creep. But watch RAM; my Air held, barely. M-series magic. Future? ARM runners everywhere. Expo’s local builds—underrated gem. No cloud tax. Pair with Supabase edge functions? Dream.

One sentence: Bliss.

Why Does Expo + DeployGate Crush for Indie Devs?

Corporate hype calls EAS “easy.” Bull. Subscriptions sting solos. This? Free forever. Fixed tester URLs—magic for non-tech users. Therapists tap links, install. No emails, no hunts.

Downsides? Mac-only for iOS local. Android? EAS or fastlane next. Self-runner setup: 30 mins first time.

Thrill? Every push iterates the world.


🧬 Related Insights

Frequently Asked Questions

How do I set up a self-hosted GitHub Actions runner on Mac for Expo?

Install via GitHub docs: Add repo secret RUNNER_TOKEN, run config.sh on your Mac. Label it ‘self-hosted’. Done—your M1/M2/M3 beast builds.

What’s the best free way to distribute Expo iOS builds to testers?

DeployGate. Sign up, grab API key, add workflow upload step. Testers hit your-app.dgate.io—installs OTA-style. No reviews.

Why do Expo builds fail on ‘Embed Pods Frameworks’ in CI?

Stale caches, dep drifts, keychain locks. Nuke DerivedData, pin packages, unlock keychain. Always.

James Kowalski
Written by

Investigative tech reporter focused on AI ethics, regulation, and societal impact.

Frequently asked questions

How do I set up a self-hosted GitHub Actions runner on Mac for Expo?
Install via GitHub docs: Add repo secret RUNNER_TOKEN, run config.sh on your Mac. Label it 'self-hosted'. Done—your M1/M2/M3 beast builds.
What's the best free way to distribute Expo iOS builds to testers?
DeployGate. Sign up, grab API key, add workflow upload step. Testers hit your-app.dgate.io—installs OTA-style. No reviews.
Why do Expo builds fail on 'Embed Pods Frameworks' in CI?
Stale caches, dep drifts, keychain locks. Nuke DerivedData, pin packages, unlock keychain. Always.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by dev.to

Stay in the loop

The week's most important stories from theAIcatchup, delivered once a week.