Skip to content

feat(ci): automated npm publishing via trusted publishing#228

Merged
kherembourg merged 3 commits into
mainfrom
feat/ci-publish
Mar 17, 2026
Merged

feat(ci): automated npm publishing via trusted publishing#228
kherembourg merged 3 commits into
mainfrom
feat/ci-publish

Conversation

@kherembourg
Copy link
Copy Markdown
Contributor

Summary

  • Add publish.yml workflow triggered on GitHub release creation
  • Publishes all 5 npm packages with OIDC provenance (no secrets, no expiration)
  • CI runs as prerequisite before publish via workflow_call
  • Version verification ensures package.json versions match the release tag

Changes

  • .github/workflows/publish.yml — new workflow for automated npm publish
  • .github/workflows/ci.yml — replace push trigger with workflow_call to enable reuse
  • packages/*/package.json — fix repository.url fields for npm provenance (must point to repo root with directory field)

npm Trusted Publisher setup required

Configure on npmjs.com for each package (Access → Trusted Publishers → GitHub Actions):

Package URL
react-native-purchasely npmjs.com/package/react-native-purchasely/access
@purchasely/react-native-purchasely-google npmjs.com/package/@purchasely/react-native-purchasely-google/access
@purchasely/react-native-purchasely-amazon npmjs.com/package/@purchasely/react-native-purchasely-amazon/access
@purchasely/react-native-purchasely-huawei npmjs.com/package/@purchasely/react-native-purchasely-huawei/access
@purchasely/react-native-purchasely-android-player npmjs.com/package/@purchasely/react-native-purchasely-android-player/access

Settings: repo = Purchasely/Purchasely-ReactNative, workflow = publish.yml

Release process

  1. Merge version PR into main
  2. gh release create 5.7.2 --target main --title "5.7.2" --notes "..."
  3. Publish is automatic (OIDC, no secret, no expiration)
  4. Verify: npm view react-native-purchasely version

Test plan

  • YAML validation passes
  • Tests pass (139/139)
  • CI: lint, test, build-android, build-ios
  • Configure trusted publishers on npmjs.com before first release

🤖 Generated with Claude Code

kherembourg and others added 3 commits March 17, 2026 08:56
- iOS SDK 5.7.1 → 5.7.2
- Android SDK 5.7.1 → 5.7.3
- Update all package versions, tests, and documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous resolution `>=3.1.3` resolved to minimatch 10.x (ESM-only),
which broke babel-plugin-istanbul/test-exclude in Jest coverage runs.
Pin to `~3.1.3` (resolves to 3.1.5) which includes the security fix
while remaining CommonJS-compatible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add publish.yml workflow triggered on GitHub release
- Publishes all 5 packages with OIDC provenance (no secrets needed)
- Includes version verification against release tag
- Modify ci.yml to support workflow_call for reuse from publish.yml
- Fix repository.url in package.json files for npm provenance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 17, 2026

Greptile Summary

This PR introduces automated npm publishing via OIDC trusted publishing (no long-lived secrets) triggered on GitHub release creation, alongside a version bump from 5.7.1 to 5.7.2 across all five packages. The CI workflow is refactored to support workflow_call so publish can depend on it as a prerequisite, and repository fields across all package manifests are fixed to the proper object format with a directory field required for npm provenance.

Key findings:

  • Missing directory field in packages/purchasely/package.json — the main package's repository object was updated but the directory: "packages/purchasely" field was omitted. Every other package in this PR has it. This will cause npm provenance to fail to identify the correct subdirectory for the most important package in the monorepo.
  • Non-idempotent publish steps — none of the five npm publish steps use --if-not-exists. A transient failure after a partial publish will make the workflow un-re-runnable for the already-published packages, requiring manual version management.
  • npm@latest is unpinnednpm install -g npm@latest introduces non-determinism across runs and silently ignores the >= 11.5.1 version requirement stated in the step name. A pinned major (e.g. npm@11) is safer.
  • CI no longer runs on direct pushes to main — removing the push trigger means hotfixes or bot commits merged directly to main bypass CI validation.
  • minimatch resolution downgraded from >=3.1.3 to ~3.1.3 — this drops the resolved version from 10.2.4 to 3.1.5. Likely done for tooling API compatibility, but future security patches in newer minimatch versions will not be applied to the monorepo.

Confidence Score: 3/5

  • Safe to merge after fixing the missing directory field in the main package and adding --if-not-exists to publish steps.
  • Two concrete bugs block a smooth first publish: the main package is missing its directory field for provenance, and the non-idempotent publish steps cannot be safely retried after a partial failure. The CI trigger change also silently reduces coverage on main. These issues won't cause a broken release today but will cause operational pain on the first publish attempt.
  • packages/purchasely/package.json (missing directory field) and .github/workflows/publish.yml (non-idempotent steps, unpinned npm version)

Important Files Changed

Filename Overview
.github/workflows/publish.yml New OIDC-based npm publish workflow triggered on GitHub release. Has two issues: publish steps are not idempotent (missing --if-not-exists), and npm@latest is unpinned.
.github/workflows/ci.yml Replaced push trigger with workflow_call to allow reuse from publish.yml. As a side effect, CI no longer runs automatically on direct commits to main.
packages/purchasely/package.json Version bumped to 5.7.2 and repository URL updated, but the directory field is missing from the repository object — unlike all other packages in this PR. This will break npm provenance for the main package.
packages/amazon/package.json Version bumped to 5.7.2, repository field correctly converted to object with git+https URL and directory field for npm provenance.
packages/google/package.json Version bumped to 5.7.2, repository URL fixed to git+https format with directory field for provenance.
packages/huawei/package.json Version bumped to 5.7.2, repository field converted from bare string to correct object format with directory field.
packages/android-player/package.json Version bumped to 5.7.2, repository URL fixed to git+https format with directory field for provenance.
package.json minimatch resolution changed from >=3.1.3 to ~3.1.3, downgrading the resolved version from 10.2.4 to 3.1.5. Likely intentional for compatibility with tooling that uses the v3 API, but means security patches in newer minor/major versions will be missed.
packages/purchasely/src/index.ts Version constant purchaselyVersion bumped from 5.7.1 to 5.7.2 in line with the package.json change.
packages/purchasely/react-native-purchasely.podspec iOS Purchasely SDK dependency bumped from 5.7.1 to 5.7.2, consistent with the version bump.

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub
    participant CI as ci.yml (reusable)
    participant Pub as publish.yml
    participant npm as npmjs.com

    Dev->>GH: gh release create 5.7.2
    GH->>Pub: release.published event
    Pub->>CI: workflow_call (prerequisite)
    CI-->>Pub: lint / test / build-android / build-ios pass
    Pub->>Pub: Setup Node.js + npm (OIDC)
    Pub->>Pub: yarn install + yarn all:prepare
    Pub->>Pub: Verify all package.json versions == tag
    Pub->>npm: npm publish --provenance (react-native-purchasely)
    Pub->>npm: npm publish --provenance (@purchasely/google)
    Pub->>npm: npm publish --provenance (@purchasely/amazon)
    Pub->>npm: npm publish --provenance (@purchasely/huawei)
    Pub->>npm: npm publish --provenance (@purchasely/android-player)
    npm-->>Dev: 5 packages live on npm registry
Loading

Comments Outside Diff (2)

  1. packages/purchasely/package.json, line 52-55 (link)

    P2 Missing directory field for npm provenance

    The repository object for react-native-purchasely (the main package) is missing the directory field that all other packages in this PR now include. The PR description explicitly states the fix must "point to repo root with directory field", yet this package — the most important one in the monorepo — does not have it.

    Without directory, npm provenance will not correctly identify packages/purchasely as the package subdirectory within the monorepo.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/purchasely/package.json
    Line: 52-55
    
    Comment:
    **Missing `directory` field for npm provenance**
    
    The `repository` object for `react-native-purchasely` (the main package) is missing the `directory` field that all other packages in this PR now include. The PR description explicitly states the fix must "point to repo root with `directory` field", yet this package — the most important one in the monorepo — does not have it.
    
    Without `directory`, npm provenance will not correctly identify `packages/purchasely` as the package subdirectory within the monorepo.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Cursor Fix in Codex

  2. .github/workflows/ci.yml, line 1-9 (link)

    P2 CI no longer runs on direct pushes to main

    The push trigger has been dropped entirely. CI now only runs via workflow_call (from publish.yml) and on pull requests targeting main. Any direct commit to main — a hotfix, an automated bot commit, a dependency-update merge — will not trigger CI automatically.

    If the intent is to ensure CI always validates the main branch state after a merge, consider re-adding the push trigger alongside workflow_call:

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: .github/workflows/ci.yml
    Line: 1-9
    
    Comment:
    **CI no longer runs on direct pushes to `main`**
    
    The `push` trigger has been dropped entirely. CI now only runs via `workflow_call` (from `publish.yml`) and on pull requests targeting `main`. Any direct commit to `main` — a hotfix, an automated bot commit, a dependency-update merge — will not trigger CI automatically.
    
    If the intent is to ensure CI always validates the `main` branch state after a merge, consider re-adding the `push` trigger alongside `workflow_call`:
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Cursor Fix in Codex

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/purchasely/package.json
Line: 52-55

Comment:
**Missing `directory` field for npm provenance**

The `repository` object for `react-native-purchasely` (the main package) is missing the `directory` field that all other packages in this PR now include. The PR description explicitly states the fix must "point to repo root with `directory` field", yet this package — the most important one in the monorepo — does not have it.

Without `directory`, npm provenance will not correctly identify `packages/purchasely` as the package subdirectory within the monorepo.

```suggestion
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Purchasely/Purchasely-ReactNative.git",
    "directory": "packages/purchasely"
  },
```

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

---

This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 56-74

Comment:
**Publish steps are not idempotent — workflow cannot be safely retried**

If the workflow fails mid-way (e.g., packages 1–2 publish successfully but package 3 fails due to a transient network error), re-running the workflow will immediately fail on the already-published packages because npm rejects publishing an existing version. This creates an unrecoverable partial-publish state that requires manual intervention.

Adding `--if-not-exists` makes each step idempotent: npm skips re-publishing an already-published version and exits 0, so a re-run of the workflow will safely publish only the remaining packages.

```suggestion
      - name: Publish react-native-purchasely
        working-directory: packages/purchasely
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-google
        working-directory: packages/google
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-amazon
        working-directory: packages/amazon
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-huawei
        working-directory: packages/huawei
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-android-player
        working-directory: packages/android-player
        run: npm publish --access public --provenance --if-not-exists
```

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

---

This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 35-36

Comment:
**Unpinned `npm@latest` introduces non-determinism**

`npm install -g npm@latest` installs whatever the current latest npm release is at the time of each workflow run. This means:
- The published artifact may vary between runs depending on which npm version is active.
- A future npm major release could silently change publish behavior or break the workflow.
- It conflicts with the step's own comment that specifies `>= 11.5.1` — the actual installed version is never verified.

Pin to an explicit version (and update it deliberately via Dependabot or a renovate rule):

```suggestion
      - name: Ensure npm >= 11.5.1 (required for trusted publishing)
        run: npm install -g npm@11
```

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

---

This is a comment left during a code review.
Path: .github/workflows/ci.yml
Line: 1-9

Comment:
**CI no longer runs on direct pushes to `main`**

The `push` trigger has been dropped entirely. CI now only runs via `workflow_call` (from `publish.yml`) and on pull requests targeting `main`. Any direct commit to `main` — a hotfix, an automated bot commit, a dependency-update merge — will not trigger CI automatically.

If the intent is to ensure CI always validates the `main` branch state after a merge, consider re-adding the `push` trigger alongside `workflow_call`:

```suggestion
on:
  workflow_call:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
```

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

Last reviewed commit: 75114e9

Comment on lines +56 to +74
- name: Publish react-native-purchasely
working-directory: packages/purchasely
run: npm publish --access public --provenance

- name: Publish @purchasely/react-native-purchasely-google
working-directory: packages/google
run: npm publish --access public --provenance

- name: Publish @purchasely/react-native-purchasely-amazon
working-directory: packages/amazon
run: npm publish --access public --provenance

- name: Publish @purchasely/react-native-purchasely-huawei
working-directory: packages/huawei
run: npm publish --access public --provenance

- name: Publish @purchasely/react-native-purchasely-android-player
working-directory: packages/android-player
run: npm publish --access public --provenance
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 Publish steps are not idempotent — workflow cannot be safely retried

If the workflow fails mid-way (e.g., packages 1–2 publish successfully but package 3 fails due to a transient network error), re-running the workflow will immediately fail on the already-published packages because npm rejects publishing an existing version. This creates an unrecoverable partial-publish state that requires manual intervention.

Adding --if-not-exists makes each step idempotent: npm skips re-publishing an already-published version and exits 0, so a re-run of the workflow will safely publish only the remaining packages.

Suggested change
- name: Publish react-native-purchasely
working-directory: packages/purchasely
run: npm publish --access public --provenance
- name: Publish @purchasely/react-native-purchasely-google
working-directory: packages/google
run: npm publish --access public --provenance
- name: Publish @purchasely/react-native-purchasely-amazon
working-directory: packages/amazon
run: npm publish --access public --provenance
- name: Publish @purchasely/react-native-purchasely-huawei
working-directory: packages/huawei
run: npm publish --access public --provenance
- name: Publish @purchasely/react-native-purchasely-android-player
working-directory: packages/android-player
run: npm publish --access public --provenance
- name: Publish react-native-purchasely
working-directory: packages/purchasely
run: npm publish --access public --provenance --if-not-exists
- name: Publish @purchasely/react-native-purchasely-google
working-directory: packages/google
run: npm publish --access public --provenance --if-not-exists
- name: Publish @purchasely/react-native-purchasely-amazon
working-directory: packages/amazon
run: npm publish --access public --provenance --if-not-exists
- name: Publish @purchasely/react-native-purchasely-huawei
working-directory: packages/huawei
run: npm publish --access public --provenance --if-not-exists
- name: Publish @purchasely/react-native-purchasely-android-player
working-directory: packages/android-player
run: npm publish --access public --provenance --if-not-exists
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 56-74

Comment:
**Publish steps are not idempotent — workflow cannot be safely retried**

If the workflow fails mid-way (e.g., packages 1–2 publish successfully but package 3 fails due to a transient network error), re-running the workflow will immediately fail on the already-published packages because npm rejects publishing an existing version. This creates an unrecoverable partial-publish state that requires manual intervention.

Adding `--if-not-exists` makes each step idempotent: npm skips re-publishing an already-published version and exits 0, so a re-run of the workflow will safely publish only the remaining packages.

```suggestion
      - name: Publish react-native-purchasely
        working-directory: packages/purchasely
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-google
        working-directory: packages/google
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-amazon
        working-directory: packages/amazon
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-huawei
        working-directory: packages/huawei
        run: npm publish --access public --provenance --if-not-exists

      - name: Publish @purchasely/react-native-purchasely-android-player
        working-directory: packages/android-player
        run: npm publish --access public --provenance --if-not-exists
```

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

Fix in Claude Code Fix in Cursor Fix in Codex

Comment on lines +35 to +36
- name: Ensure npm >= 11.5.1 (required for trusted publishing)
run: npm install -g npm@latest
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 Unpinned npm@latest introduces non-determinism

npm install -g npm@latest installs whatever the current latest npm release is at the time of each workflow run. This means:

  • The published artifact may vary between runs depending on which npm version is active.
  • A future npm major release could silently change publish behavior or break the workflow.
  • It conflicts with the step's own comment that specifies >= 11.5.1 — the actual installed version is never verified.

Pin to an explicit version (and update it deliberately via Dependabot or a renovate rule):

Suggested change
- name: Ensure npm >= 11.5.1 (required for trusted publishing)
run: npm install -g npm@latest
- name: Ensure npm >= 11.5.1 (required for trusted publishing)
run: npm install -g npm@11
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 35-36

Comment:
**Unpinned `npm@latest` introduces non-determinism**

`npm install -g npm@latest` installs whatever the current latest npm release is at the time of each workflow run. This means:
- The published artifact may vary between runs depending on which npm version is active.
- A future npm major release could silently change publish behavior or break the workflow.
- It conflicts with the step's own comment that specifies `>= 11.5.1` — the actual installed version is never verified.

Pin to an explicit version (and update it deliberately via Dependabot or a renovate rule):

```suggestion
      - name: Ensure npm >= 11.5.1 (required for trusted publishing)
        run: npm install -g npm@11
```

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

Fix in Claude Code Fix in Cursor Fix in Codex

@kherembourg kherembourg merged commit 715b8a4 into main Mar 17, 2026
5 checks passed
@kherembourg kherembourg deleted the feat/ci-publish branch March 17, 2026 10:33
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.

1 participant