Skip to content

Plugin breaks EAS Build and CNG workflow - reads files during config introspection before they exist #56

@alex-vance

Description

@alex-vance

Summary

Voltra’s config plugin breaks the standard Continuous Native Generation (CNG) workflow used by EAS Build. The plugin attempts to read the Live Activity extension’s Info.plist during config introspection, but this file doesn’t exist yet because prebuild hasn’t run.

This affects anyone following Expo’s recommended practice of gitignoring the ios/ directory and letting EAS Build generate it in the cloud.

The Problem

When running eas build (or the fingerprint-based continuous deployment action), EAS CLI runs config introspection locally before uploading your project:

Failed to read the app config from the project using "npx expo config" command: 
npx expo config --json --type introspect exited with non-zero code: 1

During introspection, Voltra’s plugin calls readFileSync on ios/{AppName}LiveActivity/Info.plist:

[ios.infoPlist]: withIosInfoPlistBaseMod: ENOENT: no such file or directory, 
open '/home/runner/work/.../ios/PUMPDLiveActivity/Info.plist'

Timeline of what happens:

  1. Developer runs eas build (no ios/ folder exists - this is correct for CNG)
  2. EAS CLI runs npx expo config --type introspect locally to read entitlements for credential generation
  3. Voltra’s withInfoPlist mod tries to read ios/{App}LiveActivity/Info.plist
  4. Build fails - the file won’t exist until prebuild runs on EAS’s macOS builders

Why This Violates Expo Plugin Guidelines

From Expo’s config plugin documentation:

“Generate, move, and delete new files in dangerous mods only. Failing to do so will break introspection.

“Introspection only supports a subset of modifiers… Introspection only works on safe modifiers (static files like JSON, XML, plist, properties)”

The issue is in plugin/build/features/ios/plist/index.js - configureMainAppPlist uses withInfoPlist and reads a file that’s created by generateWidgetExtensionFiles (which uses withDangerousMod). The withInfoPlist mod runs during introspection, but withDangerousMod does not.

Current Workaround

Run npx expo prebuild --platform ios before eas build. However, this:

  • Defeats the purpose of CNG (generating native code locally)
  • Requires committing the extension folder or using .easignore tricks
  • Adds complexity to CI pipelines

Suggested Fixes

Option 1: Guard the file read

const { existsSync, readFileSync } = require('fs');

if (existsSync(filePath)) {
  const content = plist.parse(readFileSync(filePath, 'utf8'));
  // ... rest of logic
} else {
  // Skip during introspection, will run properly during actual prebuild
  return config;
}

Option 2: Follow the expo-apple-targets pattern

Keep extension source files (including Info.plist) in a /targets folder outside ios/, then symlink them during prebuild. This way the source files exist in the repo and can be read during introspection, without breaking CNG for the main app.

Option 3: Move file reads to withDangerousMod

Ensure all file reads happen in withDangerousMod callbacks that only execute during actual prebuild, not introspection.

Environment

  • voltra: 1.1.2
  • expo: SDK 52
  • eas-cli: 16.32.0
  • CI: GitHub Actions (Ubuntu) with expo/expo-github-action/continuous-deploy-fingerprint

Reproduction

  1. Create an Expo project with Voltra configured
  2. Add ios/ to .gitignore (standard CNG setup)
  3. Push to GitHub and run eas build --platform ios
  4. Build fails during config introspection

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions