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
42 changes: 35 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@ name: Publish
# (alpha, beta, rc), so `npm install @versionone/agility-sdk` never picks it
# up; only a plain version moves `latest`.
#
# Staged publishing: the workflow runs `npm stage publish`, which places the
# version in the registry where nobody can install it yet. A maintainer then
# approves it with 2FA, and only then does it go out:
# npm stage approve <stage-id> (or Approve on the package's npmjs page)
# The stage ID is printed in the run's summary. `npm stage reject <stage-id>`
# discards it instead. A trusted-publisher token can stage but never approve,
# so CI alone can never put a version on npm.
#
# Authentication is npm trusted publishing (OIDC): on the package's npmjs
# settings, link organization versionone, repository VersionOne.SDK.JavaScript,
# workflow publish.yml and environment npm; no token is stored anywhere.
# (1.0.0-alpha.0 was published by hand, as a package must exist before it
# can be linked.) An NPM_TOKEN secret, if set, is used as a fallback.
# workflow publish.yml and environment npm, and allow `npm stage publish`
# (npm recommends leaving plain `npm publish` disallowed). No token is stored.
# An NPM_TOKEN secret, if set, is used as a fallback; it, too, can only stage.

on:
push:
Expand All @@ -38,8 +46,8 @@ jobs:
with:
node-version: 24
registry-url: https://registry.npmjs.org
# Trusted publishing needs npm 11.5.1 or later.
- run: npm install -g npm@^11.5.1
# `npm stage` needs npm 11.15.0 or later.
- run: npm install -g npm@^11.15.0
- run: npm ci
- name: The tag names the version in package.json
run: |
Expand All @@ -58,7 +66,27 @@ jobs:
esac
if [ -z "${tag}" ]; then tag=next; fi
echo "DIST_TAG=${tag}" >> "$GITHUB_ENV"
echo "Publishing ${VERSION} with dist-tag ${tag}"
- run: npm publish --provenance --access public --tag "${DIST_TAG}"
echo "Staging ${VERSION} with dist-tag ${tag}"
- name: Stage the release
run: |
npm stage publish --provenance --access public --tag "${DIST_TAG}" | tee stage.out
stage_id="$(sed -n 's/.*(staged with id \([^)]*\)).*/\1/p' stage.out | tail -n 1)"
{
echo "## Staged @versionone/agility-sdk@${VERSION} (dist-tag ${DIST_TAG})"
echo
echo "Nothing is published until a maintainer approves it with 2FA:"
echo
echo '```sh'
if [ -n "${stage_id}" ]; then
echo "npm stage view ${stage_id} # check what was staged"
echo "npm stage approve ${stage_id} # publish it"
echo "npm stage reject ${stage_id} # discard it"
else
echo "npm stage list @versionone/agility-sdk # find the stage ID"
fi
echo '```'
echo
echo "Or approve it on https://www.npmjs.com/package/@versionone/agility-sdk"
} >> "$GITHUB_STEP_SUMMARY"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,33 @@ prerelease (`1.0.0-alpha.0`, `-beta.N`, `-rc.N`) goes out on its own
dist-tag (`alpha`, `beta`, `rc`), so `latest` is never a prerelease.

```bash
npm version 1.0.0-alpha.1 --no-git-tag-version # bump, commit, merge
git tag v1.0.0-alpha.1 && git push origin v1.0.0-alpha.1
npm version 1.0.0-alpha.2 --no-git-tag-version # bump, commit, merge
git tag v1.0.0-alpha.2 && git push origin v1.0.0-alpha.2
```

The workflow does not publish directly: it runs `npm stage publish`, which
puts the version in the registry where nobody can install it yet, and prints
its stage ID in the run's summary. A maintainer then approves it with 2FA,
which is what publishes it:

```bash
npm stage view <stage-id> # what was staged
npm stage approve <stage-id> # publish it (asks for 2FA)
npm stage reject <stage-id> # discard it
```

or approves it on the package's page on npmjs. `npm stage` needs npm
11.15.0 or later.

Authentication is npm trusted publishing: on npmjs, the package's settings
link this repository, `publish.yml` and the GitHub environment `npm`, and no
token is stored. The `npm` environment (repository settings) requires an
approval, so a pushed tag waits for a maintainer before it publishes. A package
must exist before it can be linked, so the very first version is published
once by hand by an `@versionone` org member (`npm publish --access public
--tag alpha` from a clean checkout of the tagged commit), then linked.
link this repository, `publish.yml` and the GitHub environment `npm`, with
the `npm stage publish` permission (leave plain `npm publish` off, as npm
recommends), and no token is stored. A trusted-publisher link expires; an
expired one must be deleted and created again, and until then the workflow
fails with `ENEEDAUTH`. The `npm` environment (repository settings) requires
an approval, so a pushed tag waits for a maintainer before it stages. A
package must exist before it can be linked, so the very first version was
published once by hand by an `@versionone` org member.

## Before PR

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@versionone/agility-sdk",
"version": "1.0.0-alpha.1",
"version": "1.0.0-alpha.2",
"description": "TypeScript SDK for the Digital.ai Agility (VersionOne) API: queries, batches, schema-translated rows, guarded writes, attachments",
"keywords": [
"agility",
Expand Down
Loading