Skip to content

@nativescript/tailwind -> npm #3

@nativescript/tailwind -> npm

@nativescript/tailwind -> npm #3

Workflow file for this run

name: '@nativescript/tailwind -> npm'
# npm's trusted publisher for @nativescript/tailwind names this file (npm_release.yml)
# and the npm-publish environment; renaming either breaks OIDC publishing.
on:
push:
tags:
# Releases are tagged with the bare version (4.0.9); a v-prefixed tag is accepted too.
- '[0-9]*'
- 'v[0-9]*'
workflow_dispatch:
inputs:
version:
description: 'Exact version to release (e.g. 4.2.0-rc.0). Overrides release-type/preid. Set it to the current package.json version to publish without bumping.'
required: false
type: string
default: ''
release-type:
description: "Version bump when 'version' is empty"
required: false
type: choice
options:
- patch
- minor
- major
- prerelease
default: patch
preid:
description: 'Prerelease identifier (release-type=prerelease only; also becomes the npm dist-tag, e.g. next | rc)'
required: false
type: string
default: next
dry-run:
description: 'Bump and pack locally without pushing, publishing, or creating a GitHub release'
required: false
type: boolean
default: false
concurrency:
# Avoid overlapping publishes on the same ref
group: npm-release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write # push the release commit and tag, create the GitHub release
id-token: write # required for npm provenance / trusted publishing (OIDC)
jobs:
release:
name: Publish (gated by environment)
# Tags pushed by this workflow do not re-trigger it, but guard anyway.
if: ${{ github.actor != 'github-actions[bot]' }}
runs-on: ubuntu-latest
environment:
name: ${{ (github.event_name == 'workflow_dispatch' && inputs.dry-run) && 'npm-publish-dry-run' || 'npm-publish' }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: Checkout repository (full history for tags and release notes)
uses: actions/checkout@v7.0.1
with:
fetch-depth: 0
# The checkout is pinned to the SHA resolved at dispatch time, and main may have moved
# while the run waited for environment approval; the release commit must land on the tip.
- name: Sync to latest branch tip (dispatch)
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
git fetch origin "$GITHUB_REF_NAME"
git reset --hard "origin/$GITHUB_REF_NAME"
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Update npm (required for OIDC trusted publishing)
run: |
npm install -g npm@^11.5.1
npm --version
# The published package resolves its dependency ranges fresh, so test against that too.
- name: Install dependencies
run: npm install --no-package-lock --no-audit --no-fund
- name: Test
run: node --test test/
# Tag push: publish package.json's version, which the tag must match (4.0.10, or v4.0.10).
# Dispatch: release `version`, or bump by release-type/preid; a version equal to package.json's
# publishes without bumping (e.g. to retry a failed publish).
# The npm dist-tag follows the version: a prerelease label becomes the tag
# (4.1.0-rc.0 -> rc, numeric labels -> next), stable -> latest.
- name: Resolve release context
id: ctx
shell: bash
env:
INPUT_VERSION: ${{ inputs.version }}
INPUT_RELEASE_TYPE: ${{ inputs['release-type'] }}
INPUT_PREID: ${{ inputs.preid }}
INPUT_DRY_RUN: ${{ inputs.dry-run }}
run: |
set -euo pipefail
pkg_version=$(node -p "require('./package.json').version")
semver() { npx --yes semver@7.7.2 "$@"; }
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
if [[ -n "${INPUT_VERSION}" ]]; then
version=$(semver "${INPUT_VERSION#v}") || { echo "Invalid version '${INPUT_VERSION}'." >&2; exit 1; }
elif [[ "${INPUT_RELEASE_TYPE}" == "prerelease" ]]; then
version=$(semver -i prerelease --preid "${INPUT_PREID}" "$pkg_version")
else
version=$(semver -i "${INPUT_RELEASE_TYPE}" "$pkg_version")
fi
tag="$version"
dry_run="${INPUT_DRY_RUN}"
else
version="${GITHUB_REF_NAME#v}"
if [[ "$version" != "$pkg_version" ]]; then
echo "Tag ${GITHUB_REF_NAME} does not match package.json version ${pkg_version}." >&2
exit 1
fi
tag="${GITHUB_REF_NAME}"
dry_run="false"
fi
bump="false"
if [[ "$version" != "$pkg_version" ]]; then
bump="true"
if [[ "${GITHUB_REF}" != "refs/heads/main" ]]; then
echo "Version bumps are released from main; dispatch this workflow on main." >&2
exit 1
fi
if git rev-parse -q --verify "refs/tags/${version}" >/dev/null; then
echo "Tag ${version} already exists." >&2
exit 1
fi
fi
if [[ "$version" == *-* ]]; then
prerelease="${version#*-}"
dist_tag="${prerelease%%.*}"
if [[ "$dist_tag" =~ ^[0-9]+$ ]]; then
dist_tag="next"
fi
else
dist_tag="latest"
fi
{
echo "version=${version}"
echo "tag=${tag}"
echo "bump=${bump}"
echo "dist_tag=${dist_tag}"
echo "dry_run=${dry_run}"
} >> "$GITHUB_OUTPUT"
echo "Releasing @nativescript/tailwind@${version} with dist-tag ${dist_tag} (bump: ${bump}, dry-run: ${dry_run})"
- name: Bump version and changelog (dispatch)
if: ${{ steps.ctx.outputs.bump == 'true' }}
shell: bash
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.ctx.outputs.version }}
run: |
set -euo pipefail
npm version "$VERSION" --no-git-tag-version
node .github/scripts/release-notes.mjs generate "$VERSION" --write
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -am "release: @nativescript/tailwind ${VERSION}"
git tag "$VERSION"
git show --stat HEAD
- name: Release notes
shell: bash
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.ctx.outputs.version }}
TAG: ${{ steps.ctx.outputs.tag }}
run: |
set -euo pipefail
if ! node .github/scripts/release-notes.mjs extract "$VERSION" > "$RUNNER_TEMP/release-notes.md"; then
to="$TAG"
git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null || to=HEAD
node .github/scripts/release-notes.mjs generate "$VERSION" --to "$to" > "$RUNNER_TEMP/release-notes.md"
fi
cat "$RUNNER_TEMP/release-notes.md"
- name: Package contents
run: npm pack --dry-run
# --atomic: if main moved and the push is rejected, the tag is rejected with it,
# so a failed run leaves nothing behind and a plain re-dispatch retries.
- name: Push release commit and tag (dispatch)
if: ${{ steps.ctx.outputs.bump == 'true' && steps.ctx.outputs.dry_run != 'true' }}
run: git push --atomic origin "HEAD:${GITHUB_REF_NAME}" "refs/tags/${{ steps.ctx.outputs.version }}"
# A dispatch publishing the current version may still need its tag for the GitHub release.
- name: Push missing tag (dispatch, no bump)
if: ${{ github.event_name == 'workflow_dispatch' && steps.ctx.outputs.bump != 'true' && steps.ctx.outputs.dry_run != 'true' }}
env:
VERSION: ${{ steps.ctx.outputs.version }}
run: |
if ! git rev-parse -q --verify "refs/tags/${VERSION}" >/dev/null; then
git tag "$VERSION"
git push origin "refs/tags/${VERSION}"
fi
# PUBLISH: OIDC trusted publishing (default). Avoid any lingering token auth.
- name: npm publish (OIDC)
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
shell: bash
env:
NPM_CONFIG_PROVENANCE: true
NODE_AUTH_TOKEN: ""
run: |
set -euo pipefail
unset NODE_AUTH_TOKEN
rm -f ~/.npmrc || true
if [[ -n "${NPM_CONFIG_USERCONFIG:-}" ]]; then
rm -f "$NPM_CONFIG_USERCONFIG" || true
fi
dry_run_arg=()
if [[ "${{ steps.ctx.outputs.dry_run }}" == "true" ]]; then
dry_run_arg=(--dry-run)
fi
npm publish --access public --tag "${{ steps.ctx.outputs.dist_tag }}" "${dry_run_arg[@]}"
# PUBLISH: token fallback (only when explicitly enabled via the USE_NPM_TOKEN repo/environment variable).
- name: npm publish (token)
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
shell: bash
env:
NPM_CONFIG_PROVENANCE: true
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
run: |
set -euo pipefail
dry_run_arg=()
if [[ "${{ steps.ctx.outputs.dry_run }}" == "true" ]]; then
dry_run_arg=(--dry-run)
fi
npm publish --access public --tag "${{ steps.ctx.outputs.dist_tag }}" "${dry_run_arg[@]}"
- name: GitHub release
if: ${{ steps.ctx.outputs.dry_run != 'true' }}
shell: bash
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.ctx.outputs.tag }}
DIST_TAG: ${{ steps.ctx.outputs.dist_tag }}
run: |
set -euo pipefail
release_args=(--latest)
if [[ "$DIST_TAG" != "latest" ]]; then
release_args=(--prerelease --latest=false)
fi
if gh release view "$TAG" >/dev/null 2>&1; then
gh release edit "$TAG" --notes-file "$RUNNER_TEMP/release-notes.md"
else
gh release create "$TAG" --verify-tag --title "$TAG" --notes-file "$RUNNER_TEMP/release-notes.md" "${release_args[@]}"
fi