From ab8cbf160b8e564f5594bc94d38662da73d8e627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Tue, 14 Apr 2026 16:48:36 +0200 Subject: [PATCH] chore(ci): set up automated NPM publishing via workflow_dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the workflow from apify-eslint-config: on manual dispatch, bump the version, regenerate CHANGELOG.md with git-cliff, publish to NPM, tag the commit, and create a GitHub release. Drop the ad-hoc publish.sh script and the prepublishOnly guard it enforced — the workflow is now the single source of truth for releases. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/publish_to_npm.yaml | 113 ++++++++++++++++++++++++++ cliff.toml | 61 ++++++++++++++ package.json | 3 +- publish.sh | 39 --------- 4 files changed, 175 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/publish_to_npm.yaml create mode 100644 cliff.toml delete mode 100755 publish.sh diff --git a/.github/workflows/publish_to_npm.yaml b/.github/workflows/publish_to_npm.yaml new file mode 100644 index 0000000..e253d80 --- /dev/null +++ b/.github/workflows/publish_to_npm.yaml @@ -0,0 +1,113 @@ +name: Publish to NPM + +# Bumps the package version, generates changelog, creates a git tag, publishes to NPM, and creates a GitHub release. + +on: + workflow_dispatch: + inputs: + bump: + description: 'Version bump' + required: true + type: choice + default: 'patch' + options: + - 'major' + - 'minor' + - 'patch' + - 'none' # Use 'none' to just publish the current version without bumping, useful if version is set manually. + +permissions: + contents: write + id-token: write + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - name: Check if branch is `main` + if: github.ref != 'refs/heads/main' + run: | + echo "This workflow can only be run on the main branch." + exit 1 + + - name: Checkout repository + uses: actions/checkout@v6 + with: + token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} + fetch-depth: 0 # Fetch all history for tags + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Bump version + if: ${{ inputs.bump != 'none' }} + run: npm version ${{ inputs.bump }} --no-git-tag-version + + - name: Get current version + id: get_version + run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + + # We need to create a commit and tag for the new version so that git-cliff can + # get the current version to generate the changelog. + # This commit will be ignored by git-cliff because of its title. + - name: Commit version bump + run: | + # Add a newline to CHANGELOG.md so that there is always a change to commit, even if the version was bumped manually before. + # CHANGELOG.md will be regenerated later with git-cliff, so this is a noop. + echo "" >> CHANGELOG.md + git config user.name "Apify Release Bot" + git config user.email "noreply@apify.com" + git add . + git commit -m "chore(release): Release new version [skip ci]" + git tag "v${{ steps.get_version.outputs.VERSION }}" + + - name: Generate full changelog + id: git-cliff + uses: orhun/git-cliff-action@v4 + env: + OUTPUT: CHANGELOG.md + + - name: Amend commit with updated changelog + run: | + git add CHANGELOG.md + git commit --amend --no-edit + # Move the tag to point to the amended commit + git tag -f "v${{ steps.get_version.outputs.VERSION }}" + + - name: Publish to NPM + run: npm publish --access public + + - name: Push changes + run: git push origin $(git rev-parse --abbrev-ref HEAD) --tags + + # Generate release notes only from the current version + - name: Generate changelog for release notes + id: git-cliff-release-notes + uses: orhun/git-cliff-action@v4 + with: + args: --current --strip all + + - name: Format release notes + id: format-release-notes + env: + # Pass input as environment variable to prevent injection issues in the script + UNFORMATTED_RELEASE_NOTES: ${{ steps.git-cliff-release-notes.outputs.content }} + run: | + # Git cliff outputs the version at the top, we don't need that in the release notes. + # We can simply skip the first two lines and then trim any leading/trailing whitespace. + FORMATTED_RELEASE_NOTES=$(echo "${UNFORMATTED_RELEASE_NOTES}" | tail -n +3 | sed '/^$/d') + { + echo "formatted_release_notes<> $GITHUB_OUTPUT + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + tag_name: "v${{ steps.get_version.outputs.VERSION }}" + name: "v${{ steps.get_version.outputs.VERSION }}" + body: ${{ steps.format-release-notes.outputs.formatted_release_notes }} diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..b4d0a9f --- /dev/null +++ b/cliff.toml @@ -0,0 +1,61 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +# A Tera template to be rendered as the changelog's header. +# See https://keats.github.io/tera/docs/#introduction +header = """ +# Changelog +""" +# A Tera template to be rendered for each release in the changelog. +# See https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ +## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %} +## Unreleased +{% endif %} +{% if version == "v0.0.1" %}\ +Initial release. +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %}\ +### {{ group | upper_first }} +{% for commit in commits %}- {{ commit.message | upper_first }} +{% endfor %} +{% endfor %} +""" +# A Tera template to be rendered as the changelog's footer. +# See https://keats.github.io/tera/docs/#introduction +footer = """""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = false +# Remove HTML comments +postprocessors = [ + { pattern = "", replace = "" } +] +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = true +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = true +# Split commits on newlines, treating each line as an individual commit. +split_commits = false +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^(docs|chore\\(docs\\))", skip = true }, + { message = "^(ci|chore\\(ci\\))", skip = true }, + { message = "^chore", group = "Chores" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = true +# Order releases topologically instead of chronologically. +topo_order = false +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "oldest" diff --git a/package.json b/package.json index d80a916..6c9d278 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ ".": "./tsconfig.json" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "prepublishOnly": "test $RUNNING_FROM_SCRIPT || (echo \"You must use publish.sh instead of 'npm publish' directly!\"; exit 1)" + "test": "echo \"Error: no test specified\" && exit 1" }, "files": [ "tsconfig.json" diff --git a/publish.sh b/publish.sh deleted file mode 100755 index 85ead5a..0000000 --- a/publish.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -set -e - -# Notes: -# - Use only "main" branch -# - New release gets always published as LATEST - -RED='\033[0;31m' -NC='\033[0m' # No Color - -PACKAGE_VERSION=`node -pe "require('./package.json').version"` -BRANCH=`git status | grep 'On branch' | cut -d ' ' -f 3` -BRANCH_UP_TO_DATE=`git status | grep 'nothing to commit' | tr -s \n ' '`; -GIT_TAG="v${PACKAGE_VERSION}" - -if [ -z "${BRANCH_UP_TO_DATE}" ]; then - printf "${RED}You have uncommitted changes!${NC}\n" - exit 1 -fi - -echo "Pushing to git ..." -git push - -# Main gets published as LATEST if that version doesn't exists yet and retagged as LATEST otherwise. -if [ "${BRANCH}" = "main" ]; then - echo "Publishing version ${PACKAGE_VERSION} with tag \"latest\" ..." - RUNNING_FROM_SCRIPT=1 npm publish --tag latest --access public - - echo "Tagging git commit with ${GIT_TAG} ..." - git tag ${GIT_TAG} - git push origin ${GIT_TAG} - echo "Git tag: ${GIT_TAG} created." -else - printf "${RED}@apify/tsconfig package uses only a main branch which gets published with a latest NPM tag!${NC}\n" - exit 1 -fi - -echo "Done."