Skip to content

Commit 15560fd

Browse files
authored
chore(ci): Rewrite publish workflow (#34)
This rewrites the publishing workflow to: - use OIDC to publish to NPM - bump the package version based on the workflow input - automatically generate the changelog - commit the version bump and changelog - generate a GitHub release One problem is that since the OIDC publishing to NPM allows only one publishing workflow, we lose the option to publish beta versions, but since this package is mostly stable, it's not really necessary. I've tested everything but the actual NPM publishing, I don't want to generate unnecessary package versions.
1 parent d9c347e commit 15560fd

File tree

4 files changed

+177
-211
lines changed

4 files changed

+177
-211
lines changed

.github/workflows/pre_release.yaml

Lines changed: 0 additions & 92 deletions
This file was deleted.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Publish to NPM
2+
3+
# Bumps the package version, generates changelog, creates a git tag, publishes to NPM, and creates a GitHub release.
4+
5+
on:
6+
workflow_dispatch:
7+
inputs:
8+
bump:
9+
description: 'Version bump'
10+
required: true
11+
type: choice
12+
default: 'patch'
13+
options:
14+
- 'major'
15+
- 'minor'
16+
- 'patch'
17+
- 'none' # Use 'none' to just publish the current version without bumping, useful if version is set manually.
18+
19+
permissions:
20+
contents: write
21+
id-token: write
22+
23+
jobs:
24+
publish:
25+
name: Publish
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Check if branch is `master`
29+
if: github.ref != 'refs/heads/master'
30+
run: |
31+
echo "This workflow can only be run on the master branch."
32+
exit 1
33+
34+
- name: Checkout repository
35+
uses: actions/checkout@v6
36+
with:
37+
token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
38+
fetch-depth: 0 # Fetch all history for tags
39+
40+
- name: Setup Node.js
41+
uses: actions/setup-node@v6
42+
with:
43+
node-version: 24
44+
45+
- name: Install dependencies
46+
run: npm install
47+
48+
- name: Bump version
49+
if: ${{ inputs.bump != 'none' }}
50+
run: npm version ${{ inputs.bump }} --no-git-tag-version
51+
52+
- name: Get current version
53+
id: get_version
54+
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
55+
56+
# We need to create a commit and tag for the new version so that git-cliff can
57+
# get the current version to generate the changelog.
58+
# This commit will be ignored by git-cliff because of its title.
59+
- name: Commit version bump
60+
run: |
61+
# Add a newline to CHANGELOG.md so that there is always a change to commit, even if the version was bumped manually before.
62+
# CHANGELOG.md will be regenerated later with git-cliff, so this is a noop.
63+
echo "" >> CHANGELOG.md
64+
git config user.name "Apify Release Bot"
65+
git config user.email "noreply@apify.com"
66+
git add .
67+
git commit -m "chore(release): Release new version [skip ci]"
68+
git tag "v${{ steps.get_version.outputs.VERSION }}"
69+
70+
- name: Generate full changelog
71+
id: git-cliff
72+
uses: orhun/git-cliff-action@v4
73+
env:
74+
OUTPUT: CHANGELOG.md
75+
76+
- name: Amend commit with updated changelog
77+
run: |
78+
git add CHANGELOG.md
79+
git commit --amend --no-edit
80+
# Move the tag to point to the amended commit
81+
git tag -f "v${{ steps.get_version.outputs.VERSION }}"
82+
83+
- name: Publish to NPM
84+
run: npm publish
85+
86+
- name: Push changes
87+
run: git push origin $(git rev-parse --abbrev-ref HEAD) --tags
88+
89+
# Generate release notes only from the current version
90+
- name: Generate changelog for release notes
91+
id: git-cliff-release-notes
92+
uses: orhun/git-cliff-action@v4
93+
with:
94+
args: --current --strip all
95+
96+
- name: Format release notes
97+
id: format-release-notes
98+
env:
99+
# Pass input as environment variable to prevent injection issues in the script
100+
UNFORMATTED_RELEASE_NOTES: ${{ steps.git-cliff-release-notes.outputs.content }}
101+
run: |
102+
# Git cliff outputs the version at the top, we don't need that in the release notes.
103+
# We can simply skip the first two lines and then trim any leading/trailing whitespace.
104+
FORMATTED_RELEASE_NOTES=$(echo "${UNFORMATTED_RELEASE_NOTES}" | tail -n +3 | sed '/^$/d')
105+
{
106+
echo "formatted_release_notes<<EOF"
107+
echo "${FORMATTED_RELEASE_NOTES}"
108+
echo "EOF"
109+
} >> $GITHUB_OUTPUT
110+
111+
- name: Create release
112+
uses: softprops/action-gh-release@v2
113+
with:
114+
tag_name: "v${{ steps.get_version.outputs.VERSION }}"
115+
name: "v${{ steps.get_version.outputs.VERSION }}"
116+
body: ${{ steps.format-release-notes.outputs.formatted_release_notes }}

.github/workflows/release.yaml

Lines changed: 0 additions & 119 deletions
This file was deleted.

cliff.toml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# git-cliff ~ configuration file
2+
# https://git-cliff.org/docs/configuration
3+
4+
[changelog]
5+
# A Tera template to be rendered as the changelog's header.
6+
# See https://keats.github.io/tera/docs/#introduction
7+
header = """
8+
# Changelog
9+
"""
10+
# A Tera template to be rendered for each release in the changelog.
11+
# See https://keats.github.io/tera/docs/#introduction
12+
body = """
13+
{% if version %}\
14+
## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }}
15+
{% else %}
16+
## Unreleased
17+
{% endif %}
18+
{% if version == "v0.0.1" %}\
19+
Initial release.
20+
{% endif %}\
21+
{% for group, commits in commits | group_by(attribute="group") %}\
22+
### {{ group | upper_first }}
23+
{% for commit in commits %}- {{ commit.message | upper_first }}
24+
{% endfor %}
25+
{% endfor %}
26+
"""
27+
# A Tera template to be rendered as the changelog's footer.
28+
# See https://keats.github.io/tera/docs/#introduction
29+
footer = """"""
30+
# Remove leading and trailing whitespaces from the changelog's body.
31+
trim = false
32+
# Remove HTML comments
33+
postprocessors = [
34+
{ pattern = "<!--.*?-->", replace = "" }
35+
]
36+
[git]
37+
# Parse commits according to the conventional commits specification.
38+
# See https://www.conventionalcommits.org
39+
conventional_commits = true
40+
# Exclude commits that do not match the conventional commits specification.
41+
filter_unconventional = true
42+
# Split commits on newlines, treating each line as an individual commit.
43+
split_commits = false
44+
# An array of regex based parsers for extracting data from the commit message.
45+
# Assigns commits to groups.
46+
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
47+
commit_parsers = [
48+
{ message = "^feat", group = "<!-- 0 -->Features" },
49+
{ message = "^fix", group = "<!-- 1 -->Bug Fixes" },
50+
{ message = "^chore\\(release\\)", skip = true },
51+
{ message = "^(docs|chore\\(docs\\))", skip = true },
52+
{ message = "^(ci|chore\\(ci\\))", skip = true },
53+
{ message = "^chore", group = "<!-- 2 -->Chores" },
54+
]
55+
# Exclude commits that are not matched by any commit parser.
56+
filter_commits = true
57+
# Order releases topologically instead of chronologically.
58+
topo_order = false
59+
# Order of commits in each group/release within the changelog.
60+
# Allowed values: newest, oldest
61+
sort_commits = "oldest"

0 commit comments

Comments
 (0)