Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,18 +504,32 @@ When adding new features:

## CI/CD Pipeline

GitHub Actions workflow (`.github/workflows/ci.yml`):
### Workflows

### CI Jobs
| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `.github/workflows/ci.yml` | `pull_request`, `merge_group`, `workflow_call` | Lint, test, build Android & iOS |
| `.github/workflows/publish.yml` | `release` (published) | Run CI then publish all 5 packages to npm |

### CI Jobs (ci.yml)

1. **lint** (ubuntu-latest) - TypeScript + ESLint checks, type checking
2. **test** (ubuntu-latest) - TypeScript/Jest unit tests with coverage
3. **build-library** (ubuntu-latest) - Build TypeScript package with Builder Bob
4. **build-android** (ubuntu-latest) - Build Android example app with Gradle caching
5. **build-ios** (macos-latest) - Build iOS example app with CocoaPods caching
3. **build-android** (ubuntu-latest) - Build Android example app with Gradle caching
4. **build-ios** (macos-latest) - Build iOS example app with CocoaPods caching

**Note:** Native tests (Android JUnit and iOS XCTest) are not included in CI as they require React Native dependencies from the example app context. Run these tests locally during development.

### Publish (publish.yml)

Triggered automatically when a GitHub release is created. Uses npm Trusted Publishing (OIDC) — no secrets or tokens needed.

1. Calls `ci.yml` via `workflow_call` (full CI runs first)
2. Verifies all `package.json` versions match the release tag
3. Publishes all 5 packages with `--provenance`

**Release tags must be bare versions** (e.g., `5.7.2`), not prefixed with `v`.

### CI Commands Reference

```yaml
Expand Down
158 changes: 86 additions & 72 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Release Process for Purchasely React Native SDK

This document describes the step-by-step process for releasing a new version of the Purchasely React Native SDK.
This document describes the step-by-step process for releasing a new version of the Purchasely React Native SDK. Publishing to npm is **automated via CI** — creating a GitHub release triggers the publish workflow.

## Prerequisites

- Node.js v20+ (see `.nvmrc`)
- Yarn 3.6.1+
- macOS with Xcode 15+ (for iOS builds)
- Android Studio with SDK 24+ (for Android builds)
- npm publishing access to `react-native-purchasely` and `@purchasely/*` packages
- `gh` CLI authenticated with push access to the repository
- npm Trusted Publishers configured (see [npm Setup](#npm-trusted-publisher-setup) — one-time only)

## Version Update Steps

Expand All @@ -33,32 +32,15 @@ s.dependency "Purchasely", '{IOS_VERSION}'

#### Android SDK Version

Update the Purchasely Android SDK version in:
Update the Purchasely Android SDK version in all 5 build.gradle files:

**`packages/purchasely/android/build.gradle`** (line ~143):
```groovy
api 'io.purchasely:core:{ANDROID_VERSION}'
```

**`packages/google/android/build.gradle`** (line ~133):
```groovy
implementation 'io.purchasely:google-play:{ANDROID_VERSION}'
```

**`packages/amazon/android/build.gradle`** (line ~131):
```groovy
implementation 'io.purchasely:amazon:{ANDROID_VERSION}'
```

**`packages/huawei/android/build.gradle`** (line ~135):
```groovy
implementation 'io.purchasely:huawei-services:{ANDROID_VERSION}'
```

**`packages/android-player/android/build.gradle`** (line ~141):
```groovy
implementation 'io.purchasely:player:{ANDROID_VERSION}'
```
| File | Dependency |
|------|-----------|
| `packages/purchasely/android/build.gradle` | `api 'io.purchasely:core:{ANDROID_VERSION}'` |
| `packages/google/android/build.gradle` | `implementation 'io.purchasely:google-play:{ANDROID_VERSION}'` |
| `packages/amazon/android/build.gradle` | `implementation 'io.purchasely:amazon:{ANDROID_VERSION}'` |
| `packages/huawei/android/build.gradle` | `implementation 'io.purchasely:huawei-services:{ANDROID_VERSION}'` |
| `packages/android-player/android/build.gradle` | `implementation 'io.purchasely:player:{ANDROID_VERSION}'` |

### 3. Run the Prepare Script

Expand All @@ -82,43 +64,26 @@ Add a new row to **`VERSIONS.md`**:

### 5. Update Test Files

Update the SDK version expectations in test files:
Replace all occurrences of the old version string with `{VERSION}` in:

**`packages/purchasely/src/__tests__/index.test.ts`** (~lines 185, 206):
```typescript
'{VERSION}' // Update version string in start() tests
```
- **`packages/purchasely/src/__tests__/index.test.ts`** (~lines 185, 206)
- **`packages/purchasely/src/__tests__/types.test.ts`** (~lines 331, 342)

**`packages/purchasely/src/__tests__/types.test.ts`** (~lines 331, 342):
```typescript
sdk_version: '{VERSION}',
// ...
expect(event.properties.sdk_version).toBe('{VERSION}')
```
### 6. Update Documentation

Update version references in **`CLAUDE.md`**:
- Properties table (Current Version, Native iOS SDK, Native Android SDK)
- Native Dependencies section (iOS SDK version, Android core version)
- Version Compatibility table

### 6. Update the Yarn Lock File
### 7. Update the Yarn Lock File

```bash
yarn install
```

This ensures `yarn.lock` reflects any dependency changes.

### 7. Verify iOS Builds Locally

```bash
cd example/ios
rm -rf Pods Podfile.lock
pod install --repo-update
cd ../..
```

Build the example app to verify compilation:
```bash
cd example/ios
xcodebuild -workspace example.xcworkspace -scheme example -configuration Debug -destination 'generic/platform=iOS' build
```

### 8. Run Tests

```bash
Expand All @@ -132,39 +97,67 @@ yarn typecheck
```bash
git add .
git commit -m "chore: Bump package versions to {VERSION}"
git push origin version/{VERSION}
git push -u origin version/{VERSION}
```

### 10. Create Pull Request

Create a PR targeting `main` and wait for all CI checks to pass:
```bash
gh pr create --base main --title "Version {VERSION}" --body "..."
```

Wait for all CI checks to pass:
- lint
- test
- build-android
- build-ios

### 11. Merge and Tag
### 11. Merge the PR

After CI passes and PR is approved:
```bash
gh pr merge --merge
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 gh pr merge strategy may conflict with branch protection rules

The --merge flag creates a merge commit. If the repository's branch protection rules require squash merges (a common setting for a clean main history), this command will fail. Consider documenting the expected merge strategy or using --squash if that is the project convention.

Suggested change
gh pr merge --merge
gh pr merge --squash
Prompt To Fix With AI
This is a comment left during a code review.
Path: RELEASE.md
Line: 119

Comment:
**`gh pr merge` strategy may conflict with branch protection rules**

The `--merge` flag creates a merge commit. If the repository's branch protection rules require squash merges (a common setting for a clean `main` history), this command will fail. Consider documenting the expected merge strategy or using `--squash` if that is the project convention.

```suggestion
gh pr merge --squash
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Cursor Fix in Codex

```

### 12. Create GitHub Release (triggers automated npm publish)

After PR approval and merge:
```bash
git checkout main
git pull origin main
git tag v{VERSION}
git push origin v{VERSION}
gh release create {VERSION} --target main --title "{VERSION}" --notes "## React Native SDK {VERSION}

### Native SDK updates
- **iOS SDK:** {OLD_IOS} → {IOS_VERSION}
- **Android SDK:** {OLD_ANDROID} → {ANDROID_VERSION}
"
```

### 12. Publish to npm
This automatically triggers `.github/workflows/publish.yml` which:
1. Runs the full CI pipeline (lint, test, build-android, build-ios)
2. Verifies all package.json versions match the release tag
3. Publishes all 5 packages to npm with OIDC provenance

### 13. Verify Publication

```bash
./publish.sh {VERSION} true
npm view react-native-purchasely version
npm view @purchasely/react-native-purchasely-google version
npm view @purchasely/react-native-purchasely-amazon version
npm view @purchasely/react-native-purchasely-huawei version
npm view @purchasely/react-native-purchasely-android-player version
```

This publishes all packages:
- `react-native-purchasely`
- `@purchasely/react-native-purchasely-google`
- `@purchasely/react-native-purchasely-huawei`
- `@purchasely/react-native-purchasely-amazon`
- `@purchasely/react-native-purchasely-android-player`
All should return `{VERSION}`.

## Published Packages

| Package | npm |
|---------|-----|
| `react-native-purchasely` | [npmjs.com](https://www.npmjs.com/package/react-native-purchasely) |
| `@purchasely/react-native-purchasely-google` | [npmjs.com](https://www.npmjs.com/package/@purchasely/react-native-purchasely-google) |
| `@purchasely/react-native-purchasely-amazon` | [npmjs.com](https://www.npmjs.com/package/@purchasely/react-native-purchasely-amazon) |
| `@purchasely/react-native-purchasely-huawei` | [npmjs.com](https://www.npmjs.com/package/@purchasely/react-native-purchasely-huawei) |
| `@purchasely/react-native-purchasely-android-player` | [npmjs.com](https://www.npmjs.com/package/@purchasely/react-native-purchasely-android-player) |

## Files Changed During Version Update

Expand All @@ -182,12 +175,23 @@ This publishes all packages:
| `packages/amazon/android/build.gradle` | Android SDK version (if updated) |
| `packages/huawei/android/build.gradle` | Android SDK version (if updated) |
| `packages/android-player/android/build.gradle` | Android SDK version (if updated) |
| `example/ios/Podfile` | iOS SDK version (if updated) |
| `VERSIONS.md` | Version mapping table |
| `CLAUDE.md` | Version references in properties and docs |
| `packages/purchasely/src/__tests__/index.test.ts` | Test version expectations |
| `packages/purchasely/src/__tests__/types.test.ts` | Test version expectations |
| `yarn.lock` | Dependency lock file |

## npm Trusted Publisher Setup

> One-time setup per package. Already configured — only needed if adding a new package.

For each package, go to `https://www.npmjs.com/package/<PACKAGE_NAME>/access`:
1. Section **Trusted Publishers** → **GitHub Actions**
2. Repository owner: `Purchasely`
3. Repository name: `Purchasely-ReactNative`
4. Workflow filename: `publish.yml`
5. Environment: *(leave empty)*
Comment on lines +191 to +193
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Repository name casing may not match actual GitHub repo

The npm Trusted Publisher setup instructs users to enter Purchasely-ReactNative as the repository name, and the troubleshooting section also refers to Purchasely/Purchasely-ReactNative. However, the repository URL used throughout the PR metadata and in OIDC tokens issued by GitHub Actions is purchasely/purchasely-reactnative (all lowercase).

npm's Trusted Publisher verification uses the OIDC token's repository claim, which GitHub sets from the actual repository path. If the case doesn't match exactly what was configured on npmjs.com, every publish attempt will fail with the "forbidden" or OIDC error described in the troubleshooting section below.

Please verify the exact case of the repository name as it appears on github.com and ensure the npm Trusted Publisher configuration (and the troubleshooting note at line 223) uses the correct casing.

Prompt To Fix With AI
This is a comment left during a code review.
Path: RELEASE.md
Line: 191-193

Comment:
**Repository name casing may not match actual GitHub repo**

The npm Trusted Publisher setup instructs users to enter `Purchasely-ReactNative` as the repository name, and the troubleshooting section also refers to `Purchasely/Purchasely-ReactNative`. However, the repository URL used throughout the PR metadata and in OIDC tokens issued by GitHub Actions is `purchasely/purchasely-reactnative` (all lowercase).

npm's Trusted Publisher verification uses the OIDC token's `repository` claim, which GitHub sets from the actual repository path. If the case doesn't match exactly what was configured on npmjs.com, every publish attempt will fail with the "forbidden" or OIDC error described in the troubleshooting section below.

Please verify the exact case of the repository name as it appears on `github.com` and ensure the npm Trusted Publisher configuration (and the troubleshooting note at line 223) uses the correct casing.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex


## Common Issues

### CI Fails with "lockfile would have been modified"
Expand All @@ -212,11 +216,21 @@ pod install --repo-update

Update the hardcoded version strings in the test files (see Step 5).

### Publish Fails with "forbidden" or OIDC error

- Verify Trusted Publisher is configured on npmjs.com for the failing package
- Ensure the workflow filename matches exactly: `publish.yml`
- Ensure the repository name matches: `Purchasely/Purchasely-ReactNative`

### Publish Fails with Version Mismatch

The release tag must match exactly the version in all `package.json` files. Release tags should be bare versions (e.g., `5.7.2`), not prefixed with `v`.

## Recommendations

1. **Always update tests** when bumping versions to avoid CI failures
2. **Run full test suite locally** before pushing to catch issues early
3. **Verify iOS builds** with `pod install --repo-update` before pushing
3. **Update CLAUDE.md** alongside version bumps to keep docs in sync
4. **Update VERSIONS.md** to maintain the version history for documentation
5. **Use semantic versioning**:
- MAJOR: Breaking API changes
Expand Down
Loading