From 0a10bbef53b71d482d02c10eaed9779894604f00 Mon Sep 17 00:00:00 2001 From: Joao Dordio Date: Thu, 8 Oct 2026 14:23:07 +0100 Subject: [PATCH 1/3] chore: prepare release 3.2.1 and add prepare/publish workflows Bump package version and build info for the Android setRead crash fix and README min-version updates. Add GitHub Actions to open release PRs and publish to npm with provenance after master matches the target version. --- .github/workflows/prepare-release.yml | 109 ++++++++++++++++++++++++++ .github/workflows/publish-release.yml | 96 +++++++++++++++++++++++ CHANGELOG.md | 4 + package.json | 2 +- src/itblBuildInfo.ts | 2 +- 5 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/prepare-release.yml create mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 000000000..d1c831a1d --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,109 @@ +name: Prepare Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version number (e.g., 3.6.0)' + required: true + type: string + ticket: + description: 'Jira ticket number (e.g., 1234 for SDK-1234)' + required: true + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + prepare-release: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} + private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + ref: master + token: ${{ steps.app-token.outputs.token }} + + - name: Validate version format + run: | + if ! [[ "${{ github.event.inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then + echo "::error::Invalid version format. Use semantic versioning (e.g., 3.6.0 or 3.6.0-beta1)" + exit 1 + fi + + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Update Changelog + id: update_changelog + run: | + version="${{ github.event.inputs.version }}" + changelog_file="CHANGELOG.md" + + if grep -qE "^## $version\b" "$changelog_file"; then + echo "::error::CHANGELOG.md already has a section for $version" + exit 1 + fi + + # RN has no [Unreleased] section -- insert a new ## version header at the top + temp_file=$(mktemp) + { + echo "## $version" + echo "" + cat "$changelog_file" + } > "$temp_file" + mv "$temp_file" "$changelog_file" + + echo "See CHANGELOG.md for release notes." > "$RUNNER_TEMP/release-notes.md" + + - name: Bump package.json version + run: npm --no-git-tag-version --allow-same-version version "${{ github.event.inputs.version }}" + + - name: Regenerate build info + run: node scripts/autoCreatePackageInfo.js + + - name: Create Pull Request + uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 # v5 + with: + token: ${{ steps.app-token.outputs.token }} + title: "SDK-${{ github.event.inputs.ticket }}: Prepare for Release ${{ github.event.inputs.version }}" + body: | + # Prepare for Release ${{ github.event.inputs.version }} + + ## SDK Release Checklist + - [ ] CHANGELOG.md updated with release notes under the new version header + - [ ] Version bumped in package.json + - [ ] src/itblBuildInfo.ts regenerated + - [ ] README.md reviewed (if needed) + - [ ] All tests passing + - [ ] Documentation updated (if needed) + + #### 📚 Docs PR if applicable + > Add link: https://github.com/Iterable/iterable-docs/pull/XXXX + branch: "release/SDK-${{ github.event.inputs.ticket }}-${{ github.event.inputs.version }}" + commit-message: "[SDK-${{ github.event.inputs.ticket }}]: Prepare for release ${{ github.event.inputs.version }}" + labels: release + delete-branch: true + + - name: Create Draft GitHub Release + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + version="${{ github.event.inputs.version }}" + + if gh release view "$version" &>/dev/null; then + echo "Draft release $version already exists, skipping." + else + gh release create "$version" \ + --draft \ + --title "$version" \ + --notes-file "$RUNNER_TEMP/release-notes.md" + fi diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 000000000..5c588640b --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,96 @@ +name: Publish Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to publish (e.g., 3.6.0)' + required: true + type: string + +env: + VERSION: ${{ github.event.inputs.version }} + +permissions: + contents: write + id-token: write + +jobs: + publish-release: + runs-on: ubuntu-latest + environment: npm + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} + private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + ref: master + token: ${{ steps.app-token.outputs.token }} + + - name: Verify release is ready on master + run: | + pkg_version=$(node -p "require('./package.json').version") + build_info_ver=$(grep -oP "version:\s*'\K[^']+" src/itblBuildInfo.ts) + + if [ "$pkg_version" != "$VERSION" ]; then + echo "::error::package.json version is '$pkg_version', expected '$VERSION'. Merge the prepare-release PR first." + exit 1 + fi + + if [ "$build_info_ver" != "$VERSION" ]; then + echo "::error::src/itblBuildInfo.ts version is '$build_info_ver', expected '$VERSION'. Run yarn prepare and merge." + exit 1 + fi + + if ! grep -qE "^## $VERSION\b" CHANGELOG.md; then + echo "::error::CHANGELOG.md has no entry for $VERSION. Merge the prepare-release PR to master before running this workflow." + exit 1 + fi + + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'yarn' + registry-url: 'https://registry.npmjs.org' + + - name: Update npm for OIDC support + run: npm install -g npm@latest + + - run: yarn install --frozen-lockfile + + - run: yarn test --maxWorkers=2 + + - run: yarn prepare + + - name: Publish to npm + run: npm publish --provenance + + - name: Publish draft GitHub release and tag master + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh release edit "$VERSION" \ + --draft=false \ + --target "$(git rev-parse HEAD)" \ + --latest + + - name: Slack notification + run: | + release_url="https://github.com/${{ github.repository }}/releases/tag/$VERSION" + payload=$(jq -n \ + --arg text ":package: *React Native SDK ${VERSION}* has been released. <${release_url}|View release notes>." \ + '{"text": $text}') + curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" + + - name: Slack failure notification + if: failure() + run: | + run_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}" + payload=$(jq -n \ + --arg text ":alert: React Native SDK release ${VERSION} failed (attempt ${{ github.run_attempt }}). Run: ${run_url}" \ + '{"text": $text}') + curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index f244d4cc1..59277745f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ - Fixed an Android crash when marking an in-app message as read after that message was no longer in the native queue. - `setReadForMessage` logs and returns when the message id is missing, matching the iOS bridge. The Android bridge was passing that missing message into `IterableInAppManager.setRead`, which threw a `NullPointerException` and killed the app process. +### Updates + +- Documented minimum supported React Native (**0.77+**) and React (**18.3.1+**) versions in the README. + ## 3.2.0 ### Updates diff --git a/package.json b/package.json index 3ffca9aa5..cb865d3e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@iterable/react-native-sdk", - "version": "3.2.0", + "version": "3.2.1", "description": "Iterable SDK for React Native.", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", diff --git a/src/itblBuildInfo.ts b/src/itblBuildInfo.ts index cf113768f..f2cd6f12e 100644 --- a/src/itblBuildInfo.ts +++ b/src/itblBuildInfo.ts @@ -3,5 +3,5 @@ * It contains the version of the package */ export const buildInfo = { - version: '3.2.0', + version: '3.2.1', }; From f50b66d63d7181aabcdee68b5aa59f974b7b6a1d Mon Sep 17 00:00:00 2001 From: Joao Dordio Date: Fri, 9 Oct 2026 18:08:56 +0100 Subject: [PATCH 2/3] chore: drop prepare/publish release workflows from the 3.2.1 release and add README mapping row --- .github/workflows/prepare-release.yml | 109 -------------------------- .github/workflows/publish-release.yml | 96 ----------------------- README.md | 1 + 3 files changed, 1 insertion(+), 205 deletions(-) delete mode 100644 .github/workflows/prepare-release.yml delete mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml deleted file mode 100644 index d1c831a1d..000000000 --- a/.github/workflows/prepare-release.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Prepare Release - -on: - workflow_dispatch: - inputs: - version: - description: 'Version number (e.g., 3.6.0)' - required: true - type: string - ticket: - description: 'Jira ticket number (e.g., 1234 for SDK-1234)' - required: true - type: string - -permissions: - contents: write - pull-requests: write - -jobs: - prepare-release: - runs-on: ubuntu-latest - steps: - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} - private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} - - - uses: actions/checkout@v4 - with: - ref: master - token: ${{ steps.app-token.outputs.token }} - - - name: Validate version format - run: | - if ! [[ "${{ github.event.inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then - echo "::error::Invalid version format. Use semantic versioning (e.g., 3.6.0 or 3.6.0-beta1)" - exit 1 - fi - - - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Update Changelog - id: update_changelog - run: | - version="${{ github.event.inputs.version }}" - changelog_file="CHANGELOG.md" - - if grep -qE "^## $version\b" "$changelog_file"; then - echo "::error::CHANGELOG.md already has a section for $version" - exit 1 - fi - - # RN has no [Unreleased] section -- insert a new ## version header at the top - temp_file=$(mktemp) - { - echo "## $version" - echo "" - cat "$changelog_file" - } > "$temp_file" - mv "$temp_file" "$changelog_file" - - echo "See CHANGELOG.md for release notes." > "$RUNNER_TEMP/release-notes.md" - - - name: Bump package.json version - run: npm --no-git-tag-version --allow-same-version version "${{ github.event.inputs.version }}" - - - name: Regenerate build info - run: node scripts/autoCreatePackageInfo.js - - - name: Create Pull Request - uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 # v5 - with: - token: ${{ steps.app-token.outputs.token }} - title: "SDK-${{ github.event.inputs.ticket }}: Prepare for Release ${{ github.event.inputs.version }}" - body: | - # Prepare for Release ${{ github.event.inputs.version }} - - ## SDK Release Checklist - - [ ] CHANGELOG.md updated with release notes under the new version header - - [ ] Version bumped in package.json - - [ ] src/itblBuildInfo.ts regenerated - - [ ] README.md reviewed (if needed) - - [ ] All tests passing - - [ ] Documentation updated (if needed) - - #### 📚 Docs PR if applicable - > Add link: https://github.com/Iterable/iterable-docs/pull/XXXX - branch: "release/SDK-${{ github.event.inputs.ticket }}-${{ github.event.inputs.version }}" - commit-message: "[SDK-${{ github.event.inputs.ticket }}]: Prepare for release ${{ github.event.inputs.version }}" - labels: release - delete-branch: true - - - name: Create Draft GitHub Release - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - version="${{ github.event.inputs.version }}" - - if gh release view "$version" &>/dev/null; then - echo "Draft release $version already exists, skipping." - else - gh release create "$version" \ - --draft \ - --title "$version" \ - --notes-file "$RUNNER_TEMP/release-notes.md" - fi diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml deleted file mode 100644 index 5c588640b..000000000 --- a/.github/workflows/publish-release.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Publish Release - -on: - workflow_dispatch: - inputs: - version: - description: 'Version to publish (e.g., 3.6.0)' - required: true - type: string - -env: - VERSION: ${{ github.event.inputs.version }} - -permissions: - contents: write - id-token: write - -jobs: - publish-release: - runs-on: ubuntu-latest - environment: npm - steps: - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} - private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} - - - uses: actions/checkout@v4 - with: - ref: master - token: ${{ steps.app-token.outputs.token }} - - - name: Verify release is ready on master - run: | - pkg_version=$(node -p "require('./package.json').version") - build_info_ver=$(grep -oP "version:\s*'\K[^']+" src/itblBuildInfo.ts) - - if [ "$pkg_version" != "$VERSION" ]; then - echo "::error::package.json version is '$pkg_version', expected '$VERSION'. Merge the prepare-release PR first." - exit 1 - fi - - if [ "$build_info_ver" != "$VERSION" ]; then - echo "::error::src/itblBuildInfo.ts version is '$build_info_ver', expected '$VERSION'. Run yarn prepare and merge." - exit 1 - fi - - if ! grep -qE "^## $VERSION\b" CHANGELOG.md; then - echo "::error::CHANGELOG.md has no entry for $VERSION. Merge the prepare-release PR to master before running this workflow." - exit 1 - fi - - - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'yarn' - registry-url: 'https://registry.npmjs.org' - - - name: Update npm for OIDC support - run: npm install -g npm@latest - - - run: yarn install --frozen-lockfile - - - run: yarn test --maxWorkers=2 - - - run: yarn prepare - - - name: Publish to npm - run: npm publish --provenance - - - name: Publish draft GitHub release and tag master - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - gh release edit "$VERSION" \ - --draft=false \ - --target "$(git rev-parse HEAD)" \ - --latest - - - name: Slack notification - run: | - release_url="https://github.com/${{ github.repository }}/releases/tag/$VERSION" - payload=$(jq -n \ - --arg text ":package: *React Native SDK ${VERSION}* has been released. <${release_url}|View release notes>." \ - '{"text": $text}') - curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" - - - name: Slack failure notification - if: failure() - run: | - run_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}" - payload=$(jq -n \ - --arg text ":alert: React Native SDK release ${VERSION} failed (attempt ${{ github.run_attempt }}). Run: ${run_url}" \ - '{"text": $text}') - curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" diff --git a/README.md b/README.md index 2c402c7c6..9eb6e6207 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ For quick reference, the following table lists the versions of the [Android SDK] | RN SDK Version | Android SDK Version | iOS SDK Version | | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------- | +| [3.2.1](https://www.npmjs.com/package/@iterable/react-native-sdk/v/3.2.1) | [3.10.1](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.10.1) | [6.7.5](https://github.com/Iterable/swift-sdk/releases/tag/6.7.5) | [3.2.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/3.2.0) | [3.10.1](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.10.1) | [6.7.5](https://github.com/Iterable/swift-sdk/releases/tag/6.7.5) | [3.1.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/3.1.0) | [3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) | [6.6.7](https://github.com/Iterable/swift-sdk/releases/tag/6.6.7) | [3.0.1](https://www.npmjs.com/package/@iterable/react-native-sdk/v/3.0.1) | [3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) | [6.6.3](https://github.com/Iterable/swift-sdk/releases/tag/6.6.3) From 7d99695c96e595b3c0242c8ac8b582a96e324d86 Mon Sep 17 00:00:00 2001 From: Joao Dordio Date: Fri, 9 Oct 2026 18:52:53 +0100 Subject: [PATCH 3/3] docs: document showMessage null resolve and rejection in TSDoc and changelog The generated API reference only described the success URL. Document the Android null resolve on tap-outside and the rejection for an empty or unknown message id, and mention the empty-id case in the 3.2.1 changelog. --- CHANGELOG.md | 2 +- src/inApp/classes/IterableInAppManager.ts | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0825ef474..c0673d32c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Fixed the Android module failing to compile against its pinned native Android SDK 3.10.1 in 3.2.0. The bridge now implements `onEmbeddedMessagingSyncSucceeded` and `onEmbeddedMessagingSyncFailed` from `IterableEmbeddedUpdateHandler` as no-ops, matching the native defaults. - Fixed an Android crash in `Iterable.inAppManager.showMessage` (`IllegalStateException: Method addObserver must be called on the main thread`), present since 3.2.0. - The bridge now displays the message on the main thread, matching `setAutoDisplayPaused` and the iOS SDK. It was calling the native in-app displayer on the React Native native-modules thread, which crashes when the current activity is not a `FragmentActivity` (for example a Jetpack Compose `ComponentActivity`). - - The promise now rejects when the message id is no longer in the native queue instead of crashing with a `NullPointerException`. + - The promise now rejects when the message id is empty or no longer in the native queue instead of crashing with a `NullPointerException`. - Dismissing a message by tapping outside it now resolves the promise with `null` instead of crashing on `url.toString()`. ### Updates diff --git a/src/inApp/classes/IterableInAppManager.ts b/src/inApp/classes/IterableInAppManager.ts index ae34f80d1..675e01587 100644 --- a/src/inApp/classes/IterableInAppManager.ts +++ b/src/inApp/classes/IterableInAppManager.ts @@ -82,15 +82,23 @@ export class IterableInAppManager { * * @example * ```typescript - * Iterable.inAppManager.showMessage(message, false).then(url => { - * console.log("url: " + url) - * }); + * Iterable.inAppManager + * .showMessage(message, false) + * .then(url => { + * console.log("url: " + url) + * }) + * .catch(error => { + * console.log("Could not show message: " + error.message) + * }); * ``` * * @param message - The message to show (an {@link IterableInAppMessage} object) * @param consume - Whether or not the message should be consumed from the user's message queue after being shown. This should be defaulted to true. * * @returns A Promise that resolves to the URL of the button or link the user tapped to close the in-app message. + * On Android, it resolves to `null` when the user closes the message by tapping outside it, and rejects when the + * message id is empty or the message is not in the local queue. In other cases, such as when the message can't be + * displayed, the Promise may not settle. */ showMessage( message: IterableInAppMessage,