Skip to content
Latchkey

Publish to NPM workflow (less/less.js)

The Publish to NPM workflow from less/less.js, explained and optimized by Latchkey.

C

CI health: C - fair

Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: less/less.js.github/workflows/publish.ymlLicense Apache-2.0View source

What it does

This is the Publish to NPM workflow from the less/less.js repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Publish to NPM

on:
  # Publish when a release PR is merged:
  #   master branch: "chore: release vX.Y.Z"       PR β†’ publishes latest
  #   alpha branch:  "chore: alpha release vX.Y.Z"  PR β†’ publishes alpha
  # Both release PRs are created automatically by create-release-pr.yml.
  pull_request:
    types: [closed]
    branches:
      - master
      - alpha

permissions:
  id-token: write  # Required for OIDC trusted publishing
  contents: write  # Required for creating releases and pushing tags

jobs:
  publish:
    name: Publish to NPM
    runs-on: ubuntu-latest
    # Only run when a release PR with the expected title is merged into master
    # or alpha.  Any other PR close (or merge without the right title) is
    # silently skipped.
    if: |
      github.repository == 'less/less.js' &&
      github.event.pull_request.merged == true &&
      (
        (github.event.pull_request.base.ref == 'master' &&
         startsWith(github.event.pull_request.title, 'chore: release v')) ||
        (github.event.pull_request.base.ref == 'alpha' &&
         startsWith(github.event.pull_request.title, 'chore: alpha release v'))
      )

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
          # Check out the base branch (master or alpha) post-merge so the
          # version bump from the release PR is already present.
          ref: ${{ github.event.pull_request.base.ref }}

      - name: Install pnpm
        uses: pnpm/action-setup@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 'lts/*'
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile --ignore-scripts

      - name: Run node tests (ESM + CJS)
        run: pnpm run test:node

      - name: Build
        run: |
          cd packages/less
          pnpm run build

      - name: Configure Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Determine branch and tag type
        id: branch-info
        run: |
          # Always a pull_request event; base.ref is master or alpha.
          BRANCH="${{ github.event.pull_request.base.ref }}"
          echo "branch=$BRANCH" >> $GITHUB_OUTPUT
          if [ "$BRANCH" = "alpha" ]; then
            echo "is_alpha=true" >> $GITHUB_OUTPUT
            echo "npm_tag=alpha" >> $GITHUB_OUTPUT
            echo "release_type=prerelease" >> $GITHUB_OUTPUT
          else
            echo "is_alpha=false" >> $GITHUB_OUTPUT
            echo "npm_tag=latest" >> $GITHUB_OUTPUT
            echo "release_type=release" >> $GITHUB_OUTPUT
          fi

      - name: Validate alpha branch requirements
        if: steps.branch-info.outputs.is_alpha == 'true'
        run: |
          # Fetch master branch
          git fetch origin master:master || true
          
          # Check 1: Alpha branch must not be behind master
          echo "πŸ” Checking if alpha branch is up to date with master..."
          MASTER_COMMITS=$(git rev-list --count alpha..master 2>/dev/null || echo "0")
          
          if [ "$MASTER_COMMITS" -gt 0 ]; then
            echo "❌ ERROR: Alpha branch is behind master by $MASTER_COMMITS commit(s)"
            echo "   Alpha branch must include all commits from master before publishing"
            exit 1
          fi
          echo "βœ… Alpha branch is up to date with master"
          
          # Check 2: Get current version and validate it contains 'alpha'
          CURRENT_VERSION=$(node -p "require('./packages/less/package.json').version")
          echo "πŸ“¦ Current version: $CURRENT_VERSION"
          
          if [[ ! "$CURRENT_VERSION" =~ -alpha\. ]]; then
            echo "❌ ERROR: Alpha branch version must contain '-alpha.'"
            echo "   Current version: $CURRENT_VERSION"
            echo "   Expected format: X.Y.Z-alpha.N"
            exit 1
          fi
          echo "βœ… Version contains 'alpha' suffix"
          
          # Check 3: Alpha base version must be >= master version
          echo "πŸ” Comparing alpha base version with master version..."
          MASTER_VERSION=$(git show master:packages/less/package.json 2>/dev/null | node -p "try { JSON.parse(require('fs').readFileSync(0, 'utf-8')).version } catch(e) { '0.0.0' }" || echo "0.0.0")
          
          if [ "$MASTER_VERSION" = "0.0.0" ]; then
            echo "⚠️  Could not determine master version, skipping comparison"
          else
            echo "πŸ“¦ Master version: $MASTER_VERSION"
            
            # Extract base version (remove -alpha.X suffix)
            ALPHA_BASE=$(echo "$CURRENT_VERSION" | sed 's/-alpha\.[0-9]*$//')
            echo "πŸ“¦ Alpha base version: $ALPHA_BASE"
            
            # Compare versions using semver from root workspace
            COMPARE_RESULT=$(node -e "
              const semver = require('semver');
              const alphaBase = process.argv[1];
              const master = process.argv[2];
              if (semver.lt(alphaBase, master)) {
                console.log('ERROR');
              } else {
                console.log('OK');
              }
            " "$ALPHA_BASE" "$MASTER_VERSION" 2>/dev/null || echo "ERROR")
            
            if [ "$COMPARE_RESULT" = "ERROR" ]; then
              echo "❌ ERROR: Alpha base version ($ALPHA_BASE) is lower than master version ($MASTER_VERSION)"
              echo "   According to semver, alpha base version must be >= master version"
              exit 1
            fi
            echo "βœ… Alpha base version is >= master version"
          fi

      - name: Ensure npm 11.5.1 or later for trusted publishing
        run: npm install -g npm@latest

      - name: Bump version and publish
        id: publish
        env:
          # Pass the resolved base branch name (master or alpha) so that
          # bump-and-publish.js knows which branch it is publishing for.
          GITHUB_REF_NAME: ${{ steps.branch-info.outputs.branch }}
        run: |
          pnpm run publish
          
          # Extract version from package.json
          VERSION=$(node -p "require('./packages/less/package.json').version")
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "tag=v$VERSION" >> $GITHUB_OUTPUT

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          TAG="${{ steps.publish.outputs.tag }}"
          VERSION="${{ steps.publish.outputs.version }}"
          IS_ALPHA="${{ steps.branch-info.outputs.is_alpha }}"

          if [ "$IS_ALPHA" = "true" ]; then
            TITLE="Alpha Release $TAG"
            PRERELEASE="--prerelease"
            BODY="## Alpha Release

          This is an alpha release from the alpha branch.

          ## Installation

          \`\`\`bash
          npm install less@${VERSION} --tag alpha
          \`\`\`

          Or:

          \`\`\`bash
          npm install less@alpha
          \`\`\`"
          else
            TITLE="Release $TAG"
            PRERELEASE=""
            BODY="## Changes

          See [CHANGELOG.md](https://github.com/less/less.js/blob/master/CHANGELOG.md) for details.

          ## Installation

          \`\`\`bash
          npm install less@${VERSION}
          \`\`\`"
          fi

          if gh release view "$TAG" &>/dev/null; then
            echo "Release $TAG already exists, uploading assets to existing release"
            gh release upload "$TAG" packages/less/dist/less.js packages/less/dist/less.min.js --clobber
          else
            gh release create "$TAG" \
              --title "$TITLE" \
              $PRERELEASE \
              --notes "$BODY" \
              packages/less/dist/less.js \
              packages/less/dist/less.min.js
          fi

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Publish to NPM
 
on:
  # Publish when a release PR is merged:
  #   master branch: "chore: release vX.Y.Z"       PR β†’ publishes latest
  #   alpha branch:  "chore: alpha release vX.Y.Z"  PR β†’ publishes alpha
  # Both release PRs are created automatically by create-release-pr.yml.
  pull_request:
    types: [closed]
    branches:
      - master
      - alpha
 
permissions:
  id-token: write  # Required for OIDC trusted publishing
  contents: write  # Required for creating releases and pushing tags
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  publish:
    timeout-minutes: 30
    name: Publish to NPM
    runs-on: latchkey-small
    # Only run when a release PR with the expected title is merged into master
    # or alpha.  Any other PR close (or merge without the right title) is
    # silently skipped.
    if: |
      github.repository == 'less/less.js' &&
      github.event.pull_request.merged == true &&
      (
        (github.event.pull_request.base.ref == 'master' &&
         startsWith(github.event.pull_request.title, 'chore: release v')) ||
        (github.event.pull_request.base.ref == 'alpha' &&
         startsWith(github.event.pull_request.title, 'chore: alpha release v'))
      )
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
          # Check out the base branch (master or alpha) post-merge so the
          # version bump from the release PR is already present.
          ref: ${{ github.event.pull_request.base.ref }}
 
      - name: Install pnpm
        uses: pnpm/action-setup@v4
 
      - uses: actions/setup-node@v4
        with:
          node-version: 'lts/*'
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'
 
      - name: Install dependencies
        run: pnpm install --frozen-lockfile --ignore-scripts
 
      - name: Run node tests (ESM + CJS)
        run: pnpm run test:node
 
      - name: Build
        run: |
          cd packages/less
          pnpm run build
 
      - name: Configure Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
 
      - name: Determine branch and tag type
        id: branch-info
        run: |
          # Always a pull_request event; base.ref is master or alpha.
          BRANCH="${{ github.event.pull_request.base.ref }}"
          echo "branch=$BRANCH" >> $GITHUB_OUTPUT
          if [ "$BRANCH" = "alpha" ]; then
            echo "is_alpha=true" >> $GITHUB_OUTPUT
            echo "npm_tag=alpha" >> $GITHUB_OUTPUT
            echo "release_type=prerelease" >> $GITHUB_OUTPUT
          else
            echo "is_alpha=false" >> $GITHUB_OUTPUT
            echo "npm_tag=latest" >> $GITHUB_OUTPUT
            echo "release_type=release" >> $GITHUB_OUTPUT
          fi
 
      - name: Validate alpha branch requirements
        if: steps.branch-info.outputs.is_alpha == 'true'
        run: |
          # Fetch master branch
          git fetch origin master:master || true
          
          # Check 1: Alpha branch must not be behind master
          echo "πŸ” Checking if alpha branch is up to date with master..."
          MASTER_COMMITS=$(git rev-list --count alpha..master 2>/dev/null || echo "0")
          
          if [ "$MASTER_COMMITS" -gt 0 ]; then
            echo "❌ ERROR: Alpha branch is behind master by $MASTER_COMMITS commit(s)"
            echo "   Alpha branch must include all commits from master before publishing"
            exit 1
          fi
          echo "βœ… Alpha branch is up to date with master"
          
          # Check 2: Get current version and validate it contains 'alpha'
          CURRENT_VERSION=$(node -p "require('./packages/less/package.json').version")
          echo "πŸ“¦ Current version: $CURRENT_VERSION"
          
          if [[ ! "$CURRENT_VERSION" =~ -alpha\. ]]; then
            echo "❌ ERROR: Alpha branch version must contain '-alpha.'"
            echo "   Current version: $CURRENT_VERSION"
            echo "   Expected format: X.Y.Z-alpha.N"
            exit 1
          fi
          echo "βœ… Version contains 'alpha' suffix"
          
          # Check 3: Alpha base version must be >= master version
          echo "πŸ” Comparing alpha base version with master version..."
          MASTER_VERSION=$(git show master:packages/less/package.json 2>/dev/null | node -p "try { JSON.parse(require('fs').readFileSync(0, 'utf-8')).version } catch(e) { '0.0.0' }" || echo "0.0.0")
          
          if [ "$MASTER_VERSION" = "0.0.0" ]; then
            echo "⚠️  Could not determine master version, skipping comparison"
          else
            echo "πŸ“¦ Master version: $MASTER_VERSION"
            
            # Extract base version (remove -alpha.X suffix)
            ALPHA_BASE=$(echo "$CURRENT_VERSION" | sed 's/-alpha\.[0-9]*$//')
            echo "πŸ“¦ Alpha base version: $ALPHA_BASE"
            
            # Compare versions using semver from root workspace
            COMPARE_RESULT=$(node -e "
              const semver = require('semver');
              const alphaBase = process.argv[1];
              const master = process.argv[2];
              if (semver.lt(alphaBase, master)) {
                console.log('ERROR');
              } else {
                console.log('OK');
              }
            " "$ALPHA_BASE" "$MASTER_VERSION" 2>/dev/null || echo "ERROR")
            
            if [ "$COMPARE_RESULT" = "ERROR" ]; then
              echo "❌ ERROR: Alpha base version ($ALPHA_BASE) is lower than master version ($MASTER_VERSION)"
              echo "   According to semver, alpha base version must be >= master version"
              exit 1
            fi
            echo "βœ… Alpha base version is >= master version"
          fi
 
      - name: Ensure npm 11.5.1 or later for trusted publishing
        run: npm install -g npm@latest
 
      - name: Bump version and publish
        id: publish
        env:
          # Pass the resolved base branch name (master or alpha) so that
          # bump-and-publish.js knows which branch it is publishing for.
          GITHUB_REF_NAME: ${{ steps.branch-info.outputs.branch }}
        run: |
          pnpm run publish
          
          # Extract version from package.json
          VERSION=$(node -p "require('./packages/less/package.json').version")
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "tag=v$VERSION" >> $GITHUB_OUTPUT
 
      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          TAG="${{ steps.publish.outputs.tag }}"
          VERSION="${{ steps.publish.outputs.version }}"
          IS_ALPHA="${{ steps.branch-info.outputs.is_alpha }}"
 
          if [ "$IS_ALPHA" = "true" ]; then
            TITLE="Alpha Release $TAG"
            PRERELEASE="--prerelease"
            BODY="## Alpha Release
 
          This is an alpha release from the alpha branch.
 
          ## Installation
 
          \`\`\`bash
          npm install less@${VERSION} --tag alpha
          \`\`\`
 
          Or:
 
          \`\`\`bash
          npm install less@alpha
          \`\`\`"
          else
            TITLE="Release $TAG"
            PRERELEASE=""
            BODY="## Changes
 
          See [CHANGELOG.md](https://github.com/less/less.js/blob/master/CHANGELOG.md) for details.
 
          ## Installation
 
          \`\`\`bash
          npm install less@${VERSION}
          \`\`\`"
          fi
 
          if gh release view "$TAG" &>/dev/null; then
            echo "Release $TAG already exists, uploading assets to existing release"
            gh release upload "$TAG" packages/less/dist/less.js packages/less/dist/less.min.js --clobber
          else
            gh release create "$TAG" \
              --title "$TITLE" \
              $PRERELEASE \
              --notes "$BODY" \
              packages/less/dist/less.js \
              packages/less/dist/less.min.js
          fi
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow