Skip to content

ci: test library consumption from packed tarballs - #603

Open
thiagobrez wants to merge 1 commit into
mainfrom
t3/test-library-consumption-ci
Open

thiagobrez wants to merge 1 commit into
mainfrom
t3/test-library-consumption-ci

Conversation

@thiagobrez

Copy link
Copy Markdown
Collaborator

PR Description

CI change. Consumers keep hitting bugs that CI can't catch because nothing in CI uses the library the way users do. The example app:

The Expo template and the config plugin never ran in CI either.

This follows react-native-arrangement-view's CI: pack the packages and install them into freshly created apps.

build-library now packs react-native-bottom-tabs, @bottom-tabs/react-navigation and @bottom-tabs/expo-template and uploads them as an artifact. It uses npm pack because changeset publish publishes with npm publish, so CI tests the same files that ship.

consumer-expo (iOS on macos-26, Android on ubuntu-latest) follows the Quick Start's create-expo-app --template @bottom-tabs/expo-template path:

  • Creates an app from the packed template and installs the packed library and React Navigation package.
  • Runs tsc, expo export, and expo prebuild, which runs the plugin from the tarball's app.plugin.js.
  • Checks that the plugin set the Material 3 theme.
  • Release builds use Xcode 26.6 and AGP 8, where the library applies kotlin-android itself.

consumer-cli (iOS on xcode-27, Android on ubuntu-latest):

  • Runs @react-native-community/cli init on the example's React Native version (currently 0.87.1, which comes with TypeScript 6), then installs the packed library.
  • Uses the standalone TabView from .github/consumer/App.tsx.
  • Runs tsc, a JS bundle, CocoaPods and Release builds.
  • Android switches to the Material 3 theme, as the Quick Start tells CLI users to, and turns on AGP 9's built-in Kotlin, which AGP 10 makes mandatory.
  • iOS uses Xcode 27, the only Xcode that compiles the #if compiler(>=6.4) iOS 27 code.

Two more changes taken from the reference workflow: yarn test now runs in the lint job (CI never ran it), and concurrency cancels superseded runs.

Things for reviewers:

  • xcode-27 is GitHub's public-preview Xcode 27 runner label. actionlint doesn't recognize it yet; react-native-arrangement-view already uses it.
  • The template is still on Expo SDK 54, so consumer-expo tests what template users get, not the latest SDK.
  • The existing build-ios job now overlaps these jobs and still never runs xcodebuild through CocoaPods. It could be dropped or fixed in a follow-up.

How to test?

Check the four consumer-* jobs on this PR.

Every step was run locally first:

  • consumer-expo: iOS Release with Xcode 26.6 and Android Release passed.
  • consumer-cli: iOS Release with Xcode 27.1 and Android Release with built-in Kotlin passed.
  • Putting back the kotlin-kapt line that fix(android): remove unused kotlin-kapt plugin for AGP 9 built-in Kotlin #600 removed makes consumer-cli (android) fail with The 'org.jetbrains.kotlin.kapt' plugin is not compatible with built-in Kotlin support, the error users reported.

Screenshots

N/A, CI only.

🤖 Generated with Claude Code

Pack the publishable packages with npm pack and install them into fresh
consumer apps, so CI resolves the library the way users do instead of
through workspace links and src/ aliases.

- consumer-expo: app from the packed Expo template, config plugin via
  prebuild, Release builds on Xcode 26 and AGP 8
- consumer-cli: React Native CLI app on the example's RN version,
  Release builds on Xcode 27 and AGP 9 with built-in Kotlin

Also run yarn test in the lint job and cancel superseded runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread .github/workflows/ci.yml
Comment on lines +213 to +307
name: consumer-expo (${{ matrix.platform }})
needs: build-library
runs-on: ${{ matrix.runner }}
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
include:
- platform: ios
runner: macos-26
- platform: android
runner: ubuntu-latest

env:
APP_DIR: ${{ github.workspace }}/../consumer

steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: .nvmrc

- name: Download packages
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: packages
path: ${{ runner.temp }}/packages

- name: Create app from the Expo template
run: |
npx --yes create-expo-app@latest "$APP_DIR" --template "$RUNNER_TEMP"/packages/bottom-tabs-expo-template-*.tgz --no-install

# The template pins the published versions; the tarballs replace them.
- name: Install packages from tarballs
working-directory: ${{ env.APP_DIR }}
run: |
npm install "$RUNNER_TEMP"/packages/react-native-bottom-tabs-*.tgz "$RUNNER_TEMP"/packages/bottom-tabs-react-navigation-*.tgz

- name: Typecheck
working-directory: ${{ env.APP_DIR }}
run: npx tsc --noEmit

- name: Bundle JavaScript
working-directory: ${{ env.APP_DIR }}
run: npx expo export --platform ${{ matrix.platform }}

- name: Install JDK
if: matrix.platform == 'android'
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
distribution: 'zulu'
java-version: '17'

- name: Setup Gradle
if: matrix.platform == 'android'
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0

- name: Finalize Android SDK
if: matrix.platform == 'android'
run: |
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"

# Runs the library's config plugin from the tarball's app.plugin.js.
- name: Prebuild
working-directory: ${{ env.APP_DIR }}
run: npx expo prebuild --platform ${{ matrix.platform }}

# BottomNavigationView crashes on launch without a Material theme.
- name: Check the config plugin set the Material 3 theme
if: matrix.platform == 'android'
working-directory: ${{ env.APP_DIR }}
run: |
grep 'parent="Theme.Material3.DayNight.NoActionBar"' android/app/src/main/res/values/styles.xml

- name: Build for iOS
if: matrix.platform == 'ios'
working-directory: ${{ env.APP_DIR }}
run: |
xcodebuild -workspace ios/consumer.xcworkspace -scheme consumer -configuration Release -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath ios/build ARCHS=arm64 CODE_SIGNING_ALLOWED=NO -quiet

- name: Build for Android
if: matrix.platform == 'android'
working-directory: ${{ env.APP_DIR }}/android
env:
JAVA_OPTS: '-XX:MaxHeapSize=6g'
run: |
./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a --no-daemon --console=plain

# Installs the packed library into a fresh React Native Community CLI app on
# the example's React Native version, as bare React Native users do.
consumer-cli:
Comment thread .github/workflows/ci.yml
Comment on lines +308 to +410
name: consumer-cli (${{ matrix.platform }})
needs: build-library
runs-on: ${{ matrix.runner }}
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
include:
# Only Xcode 27 compiles the `#if compiler(>=6.4)` iOS 27 SDK code;
# consumer-expo builds with Xcode 26.
- platform: ios
runner: xcode-27
- platform: android
runner: ubuntu-latest

env:
APP_DIR: ${{ github.workspace }}/../consumer

steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: .nvmrc

- name: Download packages
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: packages
path: ${{ runner.temp }}/packages

- name: Create app on the example's React Native version
run: |
version=$(node -p "require('./apps/example/package.json').dependencies['react-native'].replace(/^\D+/, '')")
npx --yes @react-native-community/cli@latest init Consumer --version "$version" --directory "$APP_DIR" --skip-install --skip-git-init

- name: Install library from tarball
working-directory: ${{ env.APP_DIR }}
run: |
cp "$GITHUB_WORKSPACE/.github/consumer/App.tsx" App.tsx
npm install "$RUNNER_TEMP"/packages/react-native-bottom-tabs-*.tgz

- name: Typecheck
working-directory: ${{ env.APP_DIR }}
run: npx tsc --noEmit

- name: Bundle JavaScript
working-directory: ${{ env.APP_DIR }}
run: |
npx react-native bundle --entry-file index.js --platform ${{ matrix.platform }} --dev false --bundle-output "$RUNNER_TEMP/index.bundle"

- name: Install pods
if: matrix.platform == 'ios'
working-directory: ${{ env.APP_DIR }}
run: |
bundle install
cd ios && bundle exec pod install

- name: Build for iOS
if: matrix.platform == 'ios'
working-directory: ${{ env.APP_DIR }}
run: |
xcodebuild -workspace ios/Consumer.xcworkspace -scheme Consumer -configuration Release -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath ios/build ARCHS=arm64 CODE_SIGNING_ALLOWED=NO -quiet

- name: Install JDK
if: matrix.platform == 'android'
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
distribution: 'zulu'
java-version: '17'

- name: Setup Gradle
if: matrix.platform == 'android'
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0

- name: Finalize Android SDK
if: matrix.platform == 'android'
run: |
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"

# Inherits the Material 3 theme as the Quick Start tells CLI users to, and
# turns on AGP 9's built-in Kotlin, which the template still opts out of
# and AGP 10 makes mandatory. consumer-expo (AGP 8) covers the library
# applying kotlin-android itself.
- name: Configure Android app
if: matrix.platform == 'android'
working-directory: ${{ env.APP_DIR }}/android
run: |
sed -i 's/Theme.AppCompat.DayNight.NoActionBar/Theme.Material3.DayNight.NoActionBar/' app/src/main/res/values/styles.xml
grep 'parent="Theme.Material3.DayNight.NoActionBar"' app/src/main/res/values/styles.xml
sed -i '/^android.builtInKotlin=false/d; /^android.newDsl=false/d' gradle.properties
sed -i '/apply plugin: "org.jetbrains.kotlin.android"/d' app/build.gradle

- name: Build for Android
if: matrix.platform == 'android'
working-directory: ${{ env.APP_DIR }}/android
env:
JAVA_OPTS: '-XX:MaxHeapSize=6g'
run: |
./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a --no-daemon --console=plain

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants