From 9ba5b8b99bdfbb82c748ab44a04863825040868d Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 10 Jun 2026 17:44:10 +0200 Subject: [PATCH] ci: add workflow to test building local libraries --- .github/workflows/build-local-libraries.yml | 216 ++++++++++++++++++ .github/workflows/build-templates.yml | 3 + .../create-react-native-library/package.json | 2 +- .../create-react-native-library/src/index.ts | 6 +- .../create-react-native-library/src/prompt.ts | 5 + .../src/template.ts | 35 ++- yarn.lock | 10 +- 7 files changed, 265 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/build-local-libraries.yml diff --git a/.github/workflows/build-local-libraries.yml b/.github/workflows/build-local-libraries.yml new file mode 100644 index 000000000..2029785f5 --- /dev/null +++ b/.github/workflows/build-local-libraries.yml @@ -0,0 +1,216 @@ +name: Build local library +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + push: + branches: + - main + - next + pull_request: + branches: + - main + - next + merge_group: + types: + - checks_requested + +permissions: + contents: read + +jobs: + build: + env: + XCODE_VERSION: 26 + RCT_REMOVE_LEGACY_ARCH: 1 + RCT_USE_RN_DEP: 1 + RCT_USE_PREBUILT_RNCORE: 1 + REACT_NATIVE_VERSION: 0.85.0 + EXPO_SDK_VERSION: 55 + APP_NAME: LocalLibraryApp + LIBRARY_NAME: TestLibrary + LIBRARY_DIR: modules/TestLibrary + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-15 + app: + - community-cli + - expo + type: + - name: turbo-module + language: kotlin-objc + - name: nitro-module + language: kotlin-swift + + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.app }}-${{ matrix.type.name }}-${{ matrix.type.language }} + cancel-in-progress: true + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build package + run: | + yarn workspace create-react-native-library prepare + + - name: Create app + shell: bash + run: | + if [[ "${{ matrix.app }}" == "community-cli" ]]; then + npx @react-native-community/cli init "$APP_NAME" \ + --directory "$APP_NAME" \ + --version "$REACT_NATIVE_VERSION" \ + --skip-install \ + --skip-git-init \ + --pm npm \ + --package-name com.locallibraryapp + else + npx create-expo-app@latest "$APP_NAME" \ + --no-install \ + --template "blank@sdk-$EXPO_SDK_VERSION" + fi + + - name: Create local library + working-directory: ${{ env.APP_NAME }} + shell: bash + run: | + ../packages/create-react-native-library/bin/create-react-native-library "$LIBRARY_NAME" \ + --local \ + --directory "$LIBRARY_DIR" \ + --slug @bob/react-native-test \ + --description test \ + --type ${{ matrix.type.name }} \ + --languages ${{ matrix.type.language }} + + - name: Restore dependencies of app + id: app-npm-cache + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + ${{ env.APP_NAME }}/node_modules + ${{ env.APP_NAME }}/package-lock.json + key: ${{ runner.os }}-local-library-npm-${{ matrix.app }}-${{ matrix.type.name }}-${{ matrix.type.language }}-${{ hashFiles(format('{0}/package.json', env.APP_NAME), format('{0}/modules/**/package.json', env.APP_NAME)) }} + restore-keys: | + ${{ runner.os }}-local-library-npm-${{ matrix.app }}-${{ matrix.type.name }}-${{ matrix.type.language }}- + ${{ runner.os }}-local-library-npm-${{ matrix.app }}- + + - name: Install dependencies of app + if: steps.app-npm-cache.outputs.cache-hit != 'true' + working-directory: ${{ env.APP_NAME }} + run: | + npm install --no-audit --no-fund + + - name: Cache dependencies of app + if: steps.app-npm-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + ${{ env.APP_NAME }}/node_modules + ${{ env.APP_NAME }}/package-lock.json + key: ${{ steps.app-npm-cache.outputs.cache-primary-key }} + + - name: Generate nitrogen code + if: matrix.type.name == 'nitro-module' + working-directory: ${{ env.APP_NAME }}/${{ env.LIBRARY_DIR }} + shell: bash + run: | + nitro_version=$(node -p "require('../../package.json').dependencies['react-native-nitro-modules']") + npm exec --yes --package "nitrogen@$nitro_version" -- nitrogen + + - name: Install JDK + if: runner.os == 'Linux' + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + distribution: 'zulu' + java-version: '17' + + - name: Finalize Android SDK + if: runner.os == 'Linux' + run: | + /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" + + - name: Prebuild Expo app (Android) + if: runner.os == 'Linux' && matrix.app == 'expo' + working-directory: ${{ env.APP_NAME }} + run: | + npx expo prebuild --platform android --non-interactive + + - name: Cache Gradle + if: runner.os == 'Linux' + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + ~/.gradle/wrapper + ~/.gradle/caches + key: ${{ runner.os }}-local-library-gradle-${{ matrix.app }}-${{ hashFiles(format('{0}/android/gradle/wrapper/gradle-wrapper.properties', env.APP_NAME)) }} + restore-keys: | + ${{ runner.os }}-local-library-gradle-${{ matrix.app }}- + ${{ runner.os }}-local-library-gradle- + + - name: Build app (Android) + if: runner.os == 'Linux' + working-directory: ${{ env.APP_NAME }} + shell: bash + env: + JAVA_OPTS: "-XX:MaxHeapSize=6g" + run: | + if [[ "${{ matrix.app }}" == "community-cli" ]]; then + npx react-native build-android --extra-params "--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a" + else + cd android + ./gradlew assembleDebug -DtestBuildType=debug -Dorg.gradle.jvmargs=-Xmx4g -PreactNativeArchitectures=arm64-v8a --no-daemon --console=plain + fi + + - name: Use appropriate Xcode version + if: runner.os == 'macOS' + uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 + with: + xcode-version: ${{ env.XCODE_VERSION }} + + - name: Prebuild Expo app (iOS) + if: runner.os == 'macOS' && matrix.app == 'expo' + working-directory: ${{ env.APP_NAME }} + run: | + npx expo prebuild --platform ios --non-interactive + + - name: Install cocoapods + if: runner.os == 'macOS' + working-directory: ${{ env.APP_NAME }} + shell: bash + run: | + if [[ -f Gemfile ]]; then + bundle install + bundle exec pod repo update --verbose + bundle exec pod install --project-directory=ios + else + pod repo update --verbose + pod install --project-directory=ios + fi + + - name: Build app (iOS) + if: runner.os == 'macOS' + working-directory: ${{ env.APP_NAME }} + shell: bash + run: | + if [[ "${{ matrix.app }}" == "community-cli" ]]; then + npx react-native build-ios --mode Debug + else + xcodebuild ONLY_ACTIVE_ARCH=YES \ + -workspace "ios/$APP_NAME.xcworkspace" \ + -UseNewBuildSystem=YES \ + -scheme "$APP_NAME" \ + -configuration Debug \ + -sdk iphonesimulator \ + -derivedDataPath ios/build \ + -quiet + fi diff --git a/.github/workflows/build-templates.yml b/.github/workflows/build-templates.yml index adcfb6a4c..905d2de7d 100644 --- a/.github/workflows/build-templates.yml +++ b/.github/workflows/build-templates.yml @@ -15,6 +15,9 @@ on: types: - checks_requested +permissions: + contents: read + jobs: build: env: diff --git a/packages/create-react-native-library/package.json b/packages/create-react-native-library/package.json index ff3beaa2a..537569961 100644 --- a/packages/create-react-native-library/package.json +++ b/packages/create-react-native-library/package.json @@ -51,7 +51,7 @@ "github-username": "^9.0.0", "kleur": "^4.1.5", "ora": "^9.3.0", - "pigment": "^0.4.4", + "pigment": "^0.4.5", "typescript": "^6.0.3", "validate-npm-package-name": "^7.0.2" }, diff --git a/packages/create-react-native-library/src/index.ts b/packages/create-react-native-library/src/index.ts index 729c8a504..f35e271fe 100644 --- a/packages/create-react-native-library/src/index.ts +++ b/packages/create-react-native-library/src/index.ts @@ -48,6 +48,10 @@ async function create() { console.log(''); // Empty new line after prompts + if (answers.directory == null) { + throw new Error('Missing required option: --directory'); + } + const bobVersion = await bobVersionPromise; const nitroModulesVersion = answers.type === 'nitro-module' || answers.type === 'nitro-view' @@ -101,7 +105,7 @@ async function create() { spinner.text = 'Configuring tools'; await configureTools({ - tools: answers.tools, + tools: config.tools, config, root: folder, packageJson: rootPackageJson, diff --git a/packages/create-react-native-library/src/prompt.ts b/packages/create-react-native-library/src/prompt.ts index ec1a0d93f..1dee17b57 100644 --- a/packages/create-react-native-library/src/prompt.ts +++ b/packages/create-react-native-library/src/prompt.ts @@ -172,6 +172,7 @@ export const prompt = create(['[name]'], { return answers.name; }, validate: validateDirectory, + required: true, skip: () => { const answers = prompt.read(); @@ -261,6 +262,7 @@ export const prompt = create(['[name]'], { message: 'What is the name of the package author?', default: async () => getGitConfig('user.name'), validate: (input) => Boolean(input) || 'Cannot be empty', + required: true, skip: (): boolean => prompt.read().local === true, }, authorEmail: { @@ -270,6 +272,7 @@ export const prompt = create(['[name]'], { default: async () => getGitConfig('user.email'), validate: (input) => /^\S+@\S+$/.test(input) || 'Must be a valid email address', + required: true, skip: (): boolean => prompt.read().local === true, }, authorUrl: { @@ -296,6 +299,7 @@ export const prompt = create(['[name]'], { return undefined; }, validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL', + required: true, skip: (): boolean => prompt.read().local === true, }, repoUrl: { @@ -318,6 +322,7 @@ export const prompt = create(['[name]'], { return undefined; }, validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL', + required: true, skip: (): boolean => prompt.read().local === true, }, type: { diff --git a/packages/create-react-native-library/src/template.ts b/packages/create-react-native-library/src/template.ts index ea7fffb2d..bad2ba094 100644 --- a/packages/create-react-native-library/src/template.ts +++ b/packages/create-react-native-library/src/template.ts @@ -151,6 +151,31 @@ export function generateTemplateConfiguration({ }): TemplateConfiguration { const { slug, languages, type } = answers; + let { authorName, authorEmail, authorUrl, repoUrl } = answers; + + if (answers.local) { + authorName = ''; + authorEmail = ''; + authorUrl = ''; + repoUrl = ''; + } else { + if (authorName == null) { + throw new Error('Missing required option: --author-name'); + } + + if (authorEmail == null) { + throw new Error('Missing required option: --author-email'); + } + + if (authorUrl == null) { + throw new Error('Missing required option: --author-url'); + } + + if (repoUrl == null) { + throw new Error('Missing required option: --repo-url'); + } + } + const project = slug.replace(/^(react-native-|@[^/]+\/)/, ''); let namespace: string | undefined; @@ -190,13 +215,13 @@ export function generateTemplateConfiguration({ moduleConfig: getModuleConfig(type), }, author: { - name: answers.authorName, - email: answers.authorEmail, - url: answers.authorUrl, + name: authorName, + email: authorEmail, + url: authorUrl, }, - repo: answers.repoUrl, + repo: repoUrl, example: answers.example, - tools: answers.tools, + tools: answers.tools ?? [], year: new Date().getFullYear(), }; } diff --git a/yarn.lock b/yarn.lock index 4b5a1e703..bd7314688 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5347,7 +5347,7 @@ __metadata: github-username: "npm:^9.0.0" kleur: "npm:^4.1.5" ora: "npm:^9.3.0" - pigment: "npm:^0.4.4" + pigment: "npm:^0.4.5" typescript: "npm:^6.0.3" validate-npm-package-name: "npm:^7.0.2" bin: @@ -10105,13 +10105,13 @@ __metadata: languageName: node linkType: hard -"pigment@npm:^0.4.4": - version: 0.4.4 - resolution: "pigment@npm:0.4.4" +"pigment@npm:^0.4.5": + version: 0.4.5 + resolution: "pigment@npm:0.4.5" dependencies: ansi-escapes: "npm:^7.2.0" wrap-ansi: "npm:^9.0.2" - checksum: 10c0/d2663d4e44603ed4bdab7b1c7c73c0c8a42390599c7f7f880dc91b2cb00f252667e9e22606ca7c56db6bd36e4a6067e68141afd17d1e7d03867da8866bde5bb0 + checksum: 10c0/696ba9feee6f22d59de62a37e31ced40845dc061c78175b18805eb9550215685e5fd6612e355025742152d8bc093b56ef258044e5dae7265d781cd2b353b3aac languageName: node linkType: hard