name: Release on: push: tags: - "v*.*.*" workflow_dispatch: inputs: publish: description: "Publish to npm. Leave unchecked to run release verification only." required: true default: false type: boolean npm_dist_tag: description: "npm dist-tag to publish under when publish is enabled." required: false default: latest type: string permissions: contents: read id-token: write concurrency: group: release-${{ github.workflow }} cancel-in-progress: false env: # npm trusted publishing requires Node 22.14.0+ and npm 11.5.1+. NODE_VERSION: "24" PACKAGE_NAME: "skvlt" NPM_DIST_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.npm_dist_tag || 'latest' }} jobs: publish: name: Verify and publish runs-on: ubuntu-latest timeout-minutes: 20 environment: name: npm steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f with: node-version: ${{ env.NODE_VERSION }} registry-url: https://registry.npmjs.org - name: Show npm runtime shell: pwsh run: | "Node: $(node --version)" "npm: $(npm --version)" - name: Setup Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 - name: Install dependencies run: bun install --frozen-lockfile - name: Verify tag matches package version if: github.event_name == 'push' shell: pwsh run: | $tagVersion = "${{ github.ref_name }}".Substring(1) $packageVersion = (node -p "require('./package.json').version").Trim() if ($tagVersion -ne $packageVersion) { throw "Tag version '$tagVersion' did not match package version '$packageVersion'." } - name: Run release checks run: bun run check - name: Pack npm tarball id: pack shell: pwsh run: | $tarball = (npm pack | Select-Object -Last 1).Trim() if (-not $tarball) { throw "npm pack did not produce a tarball filename." } "tarball=$tarball" >> $env:GITHUB_OUTPUT - name: Smoke test installed CLI shell: pwsh run: | $prefix = Join-Path $PWD ".tmp-release-smoke" $tarballPath = Join-Path $PWD "${{ steps.pack.outputs.tarball }}" $expectedVersion = (node -p "require('./package.json').version").Trim() npm install --prefix $prefix $tarballPath $binPath = Join-Path $prefix "node_modules/.bin/skvlt" & $binPath --help $actualVersion = (& $binPath --version | Out-String).Trim() if ($actualVersion -ne $expectedVersion) { throw "Installed CLI version '$actualVersion' did not match package version '$expectedVersion'." } - name: Check whether this npm version is already published id: npm_version if: github.event_name == 'push' || inputs.publish == true shell: bash run: | package_version="$(node -p "require('./package.json').version")" published_version="" exact_version_published="false" if published_version="$(npm view "${PACKAGE_NAME}@${package_version}" version 2>/dev/null)"; then published_version="${published_version//$'\n'/}" else published_version="" fi if [[ "$published_version" == "$package_version" ]]; then exact_version_published="true" echo "Version '$package_version' is already published to npm. Skipping publish." else echo "Version '$package_version' is not published yet. Proceeding to publish." fi echo "package_version=$package_version" >> "$GITHUB_OUTPUT" echo "exact_version_published=$exact_version_published" >> "$GITHUB_OUTPUT" - name: Publish to npm if: (github.event_name == 'push' || inputs.publish == true) && steps.npm_version.outputs.exact_version_published != 'true' # Trusted publishing uses GitHub OIDC and does not require NPM_TOKEN. # Configure npm package settings with this workflow as a trusted publisher. shell: pwsh run: | $output = & npm publish --access public --tag "${{ env.NPM_DIST_TAG }}" 2>&1 | Out-String $exitCode = $LASTEXITCODE $output.TrimEnd() if ($exitCode -eq 0) { exit 0 } if ($output -match "Cannot publish over previously published version") { "Version is immutable on npm already. Treating publish as complete." exit 0 } throw "npm publish failed with exit code $exitCode." - name: Write workflow summary if: always() shell: pwsh run: | $packageVersion = (node -p "require('./package.json').version").Trim() $mode = if ("${{ github.event_name }}" -eq "push") { "tag publish" } elseif ("${{ inputs.publish }}" -eq "true") { "manual publish" } else { "manual verification" } @" ## Release summary - Mode: $mode - Package: $env:PACKAGE_NAME - Version: $packageVersion - Dist-tag: $env:NPM_DIST_TAG - Ref: ${{ github.ref }} - Publish skipped: ${{ steps.npm_version.outputs.exact_version_published == 'true' }} "@ >> $env:GITHUB_STEP_SUMMARY