diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index abbcc9b..9658d3d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -2,9 +2,12 @@ name: CI on: push: - branches: [main] + branches: ['**'] # not tags pull_request: +env: + NPM_REGISTRY: https://verdaccio.harvmaster.com/ + jobs: check: name: Node ${{ matrix.node }} @@ -28,3 +31,26 @@ jobs: run: npm run test:only - name: Build run: npm run build + + # Pushes only (not pull requests): main → `latest`, next → `next`, other branches → `dev-`. + # See scripts/publish.mjs. Needs the NPM_REGISTRY_TOKEN secret. + staging: + name: Publish to staging registry + needs: check + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - name: Configure registry + run: | + host="${NPM_REGISTRY#https://}" + printf 'registry=%s\n//%s:_authToken=${NPM_REGISTRY_TOKEN}\n' "$NPM_REGISTRY" "$host" > .npmrc + - name: Publish + run: node scripts/publish.mjs "${{ github.ref_name }}" + env: + NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }} diff --git a/package.json b/package.json index 8c502b3..c7518b9 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,15 @@ "import": "./dist/index.js" } }, + "publishConfig": { + "registry": "https://verdaccio.harvmaster.com/" + }, "files": [ "dist" ], "scripts": { "build": "tsc -p tsconfig.build.json", + "prepack": "npm run build", "typecheck": "tsc --noEmit", "test": "tsc --noEmit && vitest run", "test:watch": "vitest", diff --git a/scripts/publish.mjs b/scripts/publish.mjs new file mode 100644 index 0000000..73260c0 --- /dev/null +++ b/scripts/publish.mjs @@ -0,0 +1,38 @@ +// Publishes the package for a branch: `main` → `latest`, `next` → `next`, any other branch → +// `dev-`. Usage: node scripts/publish.mjs [--dry-run] +// +// `main` publishes the version in package.json as is, and skips if it is already published (bump +// the version to release). Other branches publish a prerelease, `-.`, +// so every push gets its own version. The registry and its token come from `.npmrc` / npm config. +import { execFileSync } from 'node:child_process' +import { readFileSync, writeFileSync } from 'node:fs' + +const [branch, ...flags] = process.argv.slice(2) +if (!branch) throw new Error('Usage: node scripts/publish.mjs [--dry-run]') +const dryRun = flags.includes('--dry-run') + +const npm = (...args) => execFileSync('npm', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] }).trim() + +const pkg = JSON.parse(readFileSync('package.json', 'utf8')) +const base = pkg.version.replace(/\+.*/, '') +const tag = branch === 'main' ? 'latest' : branch === 'next' ? 'next' : `dev-${branch.replace(/[^0-9A-Za-z-]/g, '-')}` + +if (tag === 'latest') { + let published = '' + try { + published = npm('view', `${pkg.name}@${base}`, 'version') + } catch { + // Not found (or the package doesn't exist yet): publish it. + } + if (published) { + console.log(`${pkg.name}@${base} is already published; bump the version in package.json to release.`) + process.exit(0) + } +} else { + const timestamp = new Date().toISOString().replace(/\D/g, '') + pkg.version = `${base}${base.includes('-') ? '.' : '-'}${tag}.${timestamp}` + writeFileSync('package.json', `${JSON.stringify(pkg, null, 2)}\n`) +} + +console.log(`Publishing ${pkg.name}@${pkg.version} with tag "${tag}"`) +execFileSync('npm', ['publish', '--tag', tag, ...(dryRun ? ['--dry-run'] : [])], { stdio: 'inherit' })