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
101 changes: 101 additions & 0 deletions .github/npm-trusted-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# npm Trusted Publishing (OIDC)

Publish from GitHub Actions without a long-lived `NPM_TOKEN`.
npm mints a short-lived credential per workflow run via OIDC.

Official docs: https://docs.npmjs.com/trusted-publishers/

## Prerequisites

- You must be an **owner** (or have settings access) on each package below.
- Workflow that publishes: `.github/workflows/release.yaml` (filename is `release.yaml`).
- GitHub repo: `effect-app/libs` (public, GitHub-hosted runners).

## Packages to configure

Configure **each** publishable package (one trusted publisher per package):

| Package | npm settings URL |
| --- | --- |
| `effect-app` | https://www.npmjs.com/package/effect-app/access |
| `@effect-app/infra` | https://www.npmjs.com/package/@effect-app/infra/access |
| `@effect-app/vue` | https://www.npmjs.com/package/@effect-app/vue/access |
| `@effect-app/vue-components` | https://www.npmjs.com/package/@effect-app/vue-components/access |
| `@effect-app/cli` | https://www.npmjs.com/package/@effect-app/cli/access |
| `@effect-app/eslint-codegen-model` | https://www.npmjs.com/package/@effect-app/eslint-codegen-model/access |
| `@effect-app/eslint-shared-config` | https://www.npmjs.com/package/@effect-app/eslint-shared-config/access |

If the UI shows **Settings** / **Trusted Publisher** instead of `/access`, use that page for the same package.

## Exact steps on npmjs.com (per package)

Repeat for every package in the table:

1. Open the package page → **Settings** (or the access URL above).
2. Find **Trusted Publisher**.
3. Under **Select your publisher**, choose **GitHub Actions**.
4. Fill in exactly:
- **Organization or user:** `effect-app`
- **Repository:** `libs`
- **Workflow filename:** `release.yaml`
(filename only, including `.yaml` — not `.yml`, not a path)
- **Environment name:** leave **empty** (unless you later add a GitHub Environment to the job)
- **Allowed actions:** select **`npm publish`** (required)
5. Save.

npm does **not** validate the config on save — mismatches only show up at publish time (often as `E404`).

## Order of operations (important)

1. **Configure trusted publishers** for all packages above (npm side).
2. **Merge** the PR with package `repository` fields + this doc + `.github/release.oidc.yaml`.
3. **Apply the workflow** (maintainer, local credentials with `workflows` permission):

```bash
cp .github/release.oidc.yaml .github/workflows/release.yaml
git add .github/workflows/release.yaml && git commit -m "ci: enable npm OIDC trusted publishing" && git push
```

4. On push to `main`, the Changesets workflow publishes any unpublished versions (e.g. `4.0.0-beta.299`).
5. Confirm success in the Actions log: look for
`No NPM_TOKEN found, but OIDC is available - using npm trusted publishing`.
6. After a successful OIDC publish, optionally harden each package:
- **Publishing access** → **Require two-factor authentication and disallow tokens**
- Revoke the old GitHub secret `NPM_TOKEN` and any automation tokens on npm.

Do **not** set `NPM_TOKEN` on the publish step once OIDC is intended — `changesets/action` will use the long-lived token instead of OIDC when that env var is present.

## GitHub workflow (apply once)

The intended Changesets workflow lives at **`.github/release.oidc.yaml`**.

A GitHub App without the `workflows` permission cannot update files under
`.github/workflows/`. A maintainer must apply it once:

```bash
cp .github/release.oidc.yaml .github/workflows/release.yaml
git add .github/workflows/release.yaml
git commit -m "ci: enable npm OIDC trusted publishing in Changesets workflow"
git push
```

That workflow includes:

- `permissions.id-token: write`
- npm CLI upgraded to latest (≥ `11.5.1` required for OIDC)
- Node `24.14`
- **No** `NPM_TOKEN` on the publish step
- Package `repository.url` points at `https://github.com/effect-app/libs.git` (set in package.json)

## Troubleshooting

| Symptom | Likely cause |
| --- | --- |
| `E404` / “could not be found or you do not have permission” | Trusted publisher mismatch (org/repo/workflow filename), or OIDC not used because `NPM_TOKEN` is still set |
| `ENEEDAUTH` / unable to authenticate | Missing `id-token: write`, old npm CLI, or self-hosted runner (not supported) |
| Only some packages publish | Trusted publisher missing on those packages |

## Optional hardening

- GitHub **Environment** (e.g. `npm`) on the `version` job with required reviewers; if used, put the same environment name in every package’s trusted publisher config.
- Keep a **read-only** granular npm token only if install needs private packages (not required for this public monorepo today).
79 changes: 79 additions & 0 deletions .github/release.oidc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Intended contents of .github/workflows/release.yaml for npm Trusted Publishing (OIDC).
# Apply with: cp .github/release.oidc.yaml .github/workflows/release.yaml
# (Bot cannot push workflow file updates; a maintainer must apply this once.)
name: Changesets
on:
push:
branches:
- main
# id-token: write enables npm Trusted Publishing (OIDC).
# Do not pass NPM_TOKEN on the publish step — changesets/action prefers a
# long-lived token when set and will skip OIDC.
# See .github/npm-trusted-publishing.md for npmjs.com setup.
permissions:
contents: write
pull-requests: write
id-token: write
env:
CI: true
PNPM_CACHE_FOLDER: .pnpm-store
jobs:
ci:
uses: ./.github/workflows/ci.yml

version:
needs: [ci]
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- name: checkout code repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 24.14
registry-url: https://registry.npmjs.org

# Trusted publishing requires npm CLI >= 11.5.1
- name: Ensure npm supports OIDC trusted publishing
run: npm install -g npm@latest && npm --version

- uses: pnpm/action-setup@v3
name: Install pnpm
id: pnpm-install
with:
run_install: false

- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: install packages
run: pnpm install --frozen-lockfile
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: create and publish versions
uses: changesets/action@v1
with:
version: pnpm ci:version
commit: "chore: update versions"
title: "chore: update versions"
publish: pnpm ci:publish
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 changes: 13 additions & 5 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Intended contents of .github/workflows/release.yaml for npm Trusted Publishing (OIDC).
# Apply with: cp .github/release.oidc.yaml .github/workflows/release.yaml
# (Bot cannot push workflow file updates; a maintainer must apply this once.)
name: Changesets
on:
push:
branches:
- main
# id-token: write enables npm Trusted Publishing (OIDC).
# Do not pass NPM_TOKEN on the publish step — changesets/action prefers a
# long-lived token when set and will skip OIDC.
# See .github/npm-trusted-publishing.md for npmjs.com setup.
permissions:
contents: write
pull-requests: write
id-token: write
env:
CI: true
PNPM_CACHE_FOLDER: .pnpm-store
Expand All @@ -27,6 +35,11 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 24.14
registry-url: https://registry.npmjs.org

# Trusted publishing requires npm CLI >= 11.5.1
- name: Ensure npm supports OIDC trusted publishing
run: npm install -g npm@latest && npm --version

- uses: pnpm/action-setup@v3
name: Install pnpm
Expand All @@ -53,10 +66,6 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

# - name: setup pnpm config
# run: pnpm config set store-dir $PNPM_CACHE_FOLDER

- name: create and publish versions
uses: changesets/action@v1
Expand All @@ -68,4 +77,3 @@ jobs:
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
5 changes: 5 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"version": "2.1.0-beta.37",
"license": "MIT",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/cli"
},
"bin": {
"effect-app": "./bin.js",
"effa": "./bin.js",
Expand Down
5 changes: 5 additions & 0 deletions packages/effect-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"version": "4.0.0-beta.299",
"license": "MIT",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/effect-app"
},
"dependencies": {
"@tsconfig/strictest": "^2.0.8",
"date-fns": "^4.4.0",
Expand Down
5 changes: 5 additions & 0 deletions packages/eslint-codegen-model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"sideEffects": false,
"type": "module",
"version": "2.0.0-beta.27",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/eslint-codegen-model"
},
"bin": {
"effect-app-codegen": "./dist/cli.js"
},
Expand Down
5 changes: 5 additions & 0 deletions packages/eslint-shared-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"license": "MIT",
"private": false,
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/eslint-shared-config"
},
"sideEffects": false,
"scripts": {
"watch": "pnpm build -w",
Expand Down
5 changes: 5 additions & 0 deletions packages/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"version": "4.0.0-beta.299",
"license": "MIT",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/infra"
},
"dependencies": {
"@faker-js/faker": "^8.4.1",
"effect-app": "workspace:*",
Expand Down
5 changes: 5 additions & 0 deletions packages/vue-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"name": "@effect-app/vue-components",
"version": "4.0.0-beta.299",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/vue-components"
},
"scripts": {
"check": "vue-tsc",
"build": "pnpm build:run",
Expand Down
7 changes: 6 additions & 1 deletion packages/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"version": "4.0.0-beta.299",
"license": "MIT",
"type": "module",
"homepage": "https://github.com/effect-ts-app/libs/tree/main/packages/vue",
"repository": {
"type": "git",
"url": "https://github.com/effect-app/libs.git",
"directory": "packages/vue"
},
"homepage": "https://github.com/effect-app/libs/tree/main/packages/vue",
"dependencies": {
"@formatjs/intl": "^4.1.12",
"@tanstack/vue-query": "5.96.2",
Expand Down
Loading