diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 196ba7e..7e5fc39 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,14 +2,14 @@ name: Release on: push: - tags: - - "v*" + branches: + - main permissions: contents: read concurrency: - group: release-${{ github.ref_name }} + group: release cancel-in-progress: false jobs: @@ -18,30 +18,11 @@ jobs: timeout-minutes: 20 steps: - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - name: Verify tag is on main - run: | - git fetch origin main:refs/remotes/origin/main - git merge-base --is-ancestor "$GITHUB_SHA" origin/main - uses: actions/setup-node@v5 with: node-version: 24 registry-url: https://registry.npmjs.org - run: npm ci - - name: Verify package version matches tag - run: | - node --input-type=module <<'EOF' - import { readFileSync } from "node:fs"; - - const tag = process.env.GITHUB_REF_NAME; - const version = tag?.startsWith("v") ? tag.slice(1) : undefined; - const packageJson = JSON.parse(readFileSync("package.json", "utf8")); - - if (!version || packageJson.version !== version) { - throw new Error(`Tag ${tag} does not match ${packageJson.name}@${packageJson.version}`); - } - EOF - run: npm run check - run: npm run typecheck - run: npm run build @@ -49,12 +30,33 @@ jobs: run: npm pack --pack-destination "$RUNNER_TEMP" - name: Test packaged import run: | + eve_version=$(node -p 'require("./package.json").devDependencies.eve') smoke_directory=$(mktemp -d) cd "$smoke_directory" npm init -y >/dev/null - npm install "$RUNNER_TEMP"/supermemory-eve-*.tgz eve@0.31.3 + npm install "$RUNNER_TEMP"/supermemory-eve-*.tgz "eve@$eve_version" node --input-type=module --eval 'import("@supermemory/eve").then((module) => { if (typeof module.default !== "function") process.exit(1); })' + - name: Check whether version is published + id: publication + run: | + node --input-type=module <<'EOF' + import { appendFileSync, readFileSync } from "node:fs"; + + const { name, version } = JSON.parse(readFileSync("package.json", "utf8")); + const response = await fetch( + `https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, + { signal: AbortSignal.timeout(30_000) }, + ); + + if (!response.ok && response.status !== 404) { + throw new Error(`npm registry returned ${response.status} for ${name}@${version}`); + } + + appendFileSync(process.env.GITHUB_OUTPUT, `publish=${response.status === 404}\n`); + console.log(response.status === 404 ? `Publishing ${name}@${version}` : `${name}@${version} is already published; skipping`); + EOF - name: Publish to npm + if: steps.publication.outputs.publish == 'true' env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | diff --git a/README.md b/README.md index 5b5ecd5..ef0fd0c 100644 --- a/README.md +++ b/README.md @@ -188,3 +188,10 @@ npm run typecheck npm run build npm pack --dry-run ``` + +### Publishing + +Pushes to `main` run the release workflow. After checks and a packaged-import smoke test pass, +the workflow publishes the version in `package.json` if it is not already on npm. Bump the +package version and update `package-lock.json` to ship a new release; no Git tag is required. +Publishing requires the repository's `NPM_TOKEN` Actions secret.