Skip to content
Latchkey

Release workflow (middyjs/middy)

The Release workflow from middyjs/middy, explained and optimized by Latchkey.

C

CI health: C - fair

Point runs-on at Latchkey and get caching, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: middyjs/middy.github/workflows/release.ymlLicense MITView source

What it does

This is the Release workflow from the middyjs/middy repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

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

The workflow

workflow (.yml)
name: Release

on:
  pull_request:
    types: [closed]
    branches: [main]
    paths:
      - 'package.json'

env:
  NODE_VERSION: 24.x

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  build:
    name: Build
    if: ${{ github.event.pull_request.merged }}
    runs-on: ubuntu-latest

    permissions:
      contents: read
      id-token: write # actions/attest-build-provenance
      attestations: write # actions/attest-build-provenance
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit
          disable-telemetry: true
      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Set env
        run: |
          tag=$(npm pkg get version | xargs)
          echo "tag=${tag}" >> "$GITHUB_ENV"
          echo "prerelease=$([ "${tag##*-*}" ] && echo false || echo true)" >> "$GITHUB_ENV"
      - name: Install dependencies
        run: |
          npm ci --ignore-scripts
      - name: Verify dependency signatures
        run: |
          npm audit signatures
      - name: Build
        run: |
          npm run build --if-present
      - name: Pack
        id: pack
        run: |
          mapfile -t args < <(npm query '.workspace:not([private])' | jq -r '.[] | "--workspace", .location')
          {
            echo "packages<<EOF"
            npm pack "${args[@]}" --json
            echo EOF
          } >> "$GITHUB_OUTPUT"
      - name: Build Attestations
        uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
        with:
          subject-path: |
            **/*.tgz
      - name: Upload artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{ env.tag }}
          path: |
            **/package.json
            **/*.tgz
    outputs:
      tag: ${{ env.tag }}
      prerelease: ${{ env.prerelease }}

  release:
    name: Release
    needs: build
    runs-on: ubuntu-latest

    permissions:
      contents: write # softprops/action-gh-release
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit
          disable-telemetry: true
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # zizmor: ignore[cache-poisoning] no cache configured (no `cache:` input); reconsider if caching is added
        with:
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Download artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ needs.build.outputs.tag }}
      - name: Release
        # Replaces actions/create-release & actions/upload-release-asset (deprecated)
        uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.0 # zizmor: ignore[superfluous-actions] prefer maintained action over inline `gh release` script
        with:
          draft: true
          prerelease: ${{ needs.build.outputs.prerelease }}
          tag_name: ${{ needs.build.outputs.tag }}
          generate_release_notes: true
          # Disabled, covered by GitHub Attestations & npm
          #files: |
          #  **/*.tgz
    outputs:
      tag: ${{ needs.build.outputs.tag }}
      prerelease: ${{ needs.build.outputs.prerelease }}

  publish:
    name: Publish
    needs: release
    runs-on: ubuntu-latest

    environment: npm-publish

    permissions:
      contents: read
      id-token: write # npm publish
      attestations: read # gh attestation verify
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: block
          disable-telemetry: true
          allowed-endpoints: >
            registry.npmjs.org:443
            fulcio.sigstore.dev:443
            rekor.sigstore.dev:443
            tuf-repo-cdn.sigstore.dev:443
            github.com:443
            api.github.com:443
            tmaproduction.blob.core.windows.net:443
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Download artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ needs.release.outputs.tag }}
      - name: Verify build provenance attestation
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          find . -name '*.tgz' -print -exec gh attestation verify {} --repo ${{ github.repository }} \;
      - name: npm publish (next)
        if: ${{ needs.release.outputs.prerelease == 'true' }}
        run: |
          find . -name '*.tgz' -exec npm publish --tag next --provenance --access public {} \;
      - name: npm publish (latest)
        if: ${{ needs.release.outputs.prerelease == 'false' }}
        run: |
          find . -name '*.tgz' -exec npm publish --tag latest --provenance --access public {} \;

The same workflow, on Latchkey

Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.

name: Release
 
on:
  pull_request:
    types: [closed]
    branches: [main]
    paths:
      - 'package.json'
 
env:
  NODE_VERSION: 24.x
 
permissions:
  contents: read
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false
 
jobs:
  build:
    timeout-minutes: 30
    name: Build
    if: ${{ github.event.pull_request.merged }}
    runs-on: latchkey-small
 
    permissions:
      contents: read
      id-token: write # actions/attest-build-provenance
      attestations: write # actions/attest-build-provenance
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit
          disable-telemetry: true
      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          cache: 'npm'
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Set env
        run: |
          tag=$(npm pkg get version | xargs)
          echo "tag=${tag}" >> "$GITHUB_ENV"
          echo "prerelease=$([ "${tag##*-*}" ] && echo false || echo true)" >> "$GITHUB_ENV"
      - name: Install dependencies
        run: |
          npm ci --ignore-scripts
      - name: Verify dependency signatures
        run: |
          npm audit signatures
      - name: Build
        run: |
          npm run build --if-present
      - name: Pack
        id: pack
        run: |
          mapfile -t args < <(npm query '.workspace:not([private])' | jq -r '.[] | "--workspace", .location')
          {
            echo "packages<<EOF"
            npm pack "${args[@]}" --json
            echo EOF
          } >> "$GITHUB_OUTPUT"
      - name: Build Attestations
        uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
        with:
          subject-path: |
            **/*.tgz
      - name: Upload artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{ env.tag }}
          path: |
            **/package.json
            **/*.tgz
    outputs:
      tag: ${{ env.tag }}
      prerelease: ${{ env.prerelease }}
 
  release:
    timeout-minutes: 30
    name: Release
    needs: build
    runs-on: latchkey-small
 
    permissions:
      contents: write # softprops/action-gh-release
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit
          disable-telemetry: true
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # zizmor: ignore[cache-poisoning] no cache configured (no `cache:` input); reconsider if caching is added
        with:
          cache: 'npm'
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Download artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ needs.build.outputs.tag }}
      - name: Release
        # Replaces actions/create-release & actions/upload-release-asset (deprecated)
        uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.0 # zizmor: ignore[superfluous-actions] prefer maintained action over inline `gh release` script
        with:
          draft: true
          prerelease: ${{ needs.build.outputs.prerelease }}
          tag_name: ${{ needs.build.outputs.tag }}
          generate_release_notes: true
          # Disabled, covered by GitHub Attestations & npm
          #files: |
          #  **/*.tgz
    outputs:
      tag: ${{ needs.build.outputs.tag }}
      prerelease: ${{ needs.build.outputs.prerelease }}
 
  publish:
    timeout-minutes: 30
    name: Publish
    needs: release
    runs-on: latchkey-small
 
    environment: npm-publish
 
    permissions:
      contents: read
      id-token: write # npm publish
      attestations: read # gh attestation verify
    steps:
      - name: Harden runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: block
          disable-telemetry: true
          allowed-endpoints: >
            registry.npmjs.org:443
            fulcio.sigstore.dev:443
            rekor.sigstore.dev:443
            tuf-repo-cdn.sigstore.dev:443
            github.com:443
            api.github.com:443
            tmaproduction.blob.core.windows.net:443
      - name: Setup Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          cache: 'npm'
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - name: Download artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ needs.release.outputs.tag }}
      - name: Verify build provenance attestation
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          find . -name '*.tgz' -print -exec gh attestation verify {} --repo ${{ github.repository }} \;
      - name: npm publish (next)
        if: ${{ needs.release.outputs.prerelease == 'true' }}
        run: |
          find . -name '*.tgz' -exec npm publish --tag next --provenance --access public {} \;
      - name: npm publish (latest)
        if: ${{ needs.release.outputs.prerelease == 'false' }}
        run: |
          find . -name '*.tgz' -exec npm publish --tag latest --provenance --access public {} \;
 

What changed

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 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow