ci(github): replace release-please with justfile (#1840)

This commit is contained in:
Oleg Shparber
2026-05-05 02:21:30 +03:00
committed by GitHub
parent 9d2ec8f6de
commit 866dc9cd99
9 changed files with 215 additions and 104 deletions
-30
View File
@@ -1,30 +0,0 @@
name: Release Please
on:
push:
branches:
- main
permissions: {}
jobs:
release-please:
name: Release Please
if: github.repository == 'zealdocs/zeal'
runs-on: ubuntu-latest
steps:
- name: Generate App Token
id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.CI_APP_ID }}
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- uses: googleapis/release-please-action@v5
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
+3
View File
@@ -41,3 +41,6 @@ CMakeUserPresets.json
# Claude Code # Claude Code
.claude/settings.local.json .claude/settings.local.json
# DotENV
.env
-3
View File
@@ -1,3 +0,0 @@
{
".": "0.8.1"
}
+4 -3
View File
@@ -10,16 +10,17 @@ set(CMAKE_ERROR_DEPRECATED TRUE)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# VERSION is the next development version, bumped automatically after each release. # VERSION is the next development version, bumped automatically after each release.
# ZEAL_RELEASE_VERSION is the last released version, updated by release-please. # ZEAL_RELEASE_VERSION is the last released version, updated by `just release-prepare`.
# When both match, the build is treated as a release; otherwise it's a dev build. # When both match, the build is treated as a release; otherwise it's a dev build.
# The `# x-version` marker on each version line acts as a sed anchor for the bump.
project(Zeal project(Zeal
VERSION 0.8.2 # x-release-please-version VERSION 0.8.2 # x-version
DESCRIPTION "A simple documentation browser." DESCRIPTION "A simple documentation browser."
HOMEPAGE_URL "https://zealdocs.org" HOMEPAGE_URL "https://zealdocs.org"
LANGUAGES CXX LANGUAGES CXX
) )
set(ZEAL_RELEASE_VERSION "0.8.1") # x-release-please-version set(ZEAL_RELEASE_VERSION "0.8.1") # x-version
include(ZealHelpers) include(ZealHelpers)
+2 -2
View File
@@ -24,6 +24,7 @@ path = [
"**/*.cmake", "**/*.cmake",
"**/CMakeLists.txt", "**/CMakeLists.txt",
"CMakePresets.json", "CMakePresets.json",
"justfile",
"pkg/**/*", "pkg/**/*",
"vcpkg.json", "vcpkg.json",
@@ -36,9 +37,8 @@ path = [
".gitattributes", ".gitattributes",
".github/**/*", ".github/**/*",
".gitignore", ".gitignore",
".release-please-manifest.json",
"CHANGELOG.md", "CHANGELOG.md",
"release-please-config.json", "tools/cliff.toml",
] ]
SPDX-FileCopyrightText = "Oleg Shparber, et al. <https://zealdocs.org>" SPDX-FileCopyrightText = "Oleg Shparber, et al. <https://zealdocs.org>"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
+69
View File
@@ -0,0 +1,69 @@
# Zeal dev and release recipes.
# Uses CMake presets defined in CMakePresets.json.
# Run `just` (no args) to see the recipe list.
set shell := ["bash", "-cu"]
set windows-shell := ["sh", "-cu"]
[private]
default:
@just --list
# Configure a build directory using a preset.
[group('dev')]
configure preset="dev":
cmake --preset {{preset}}
# Build using a preset.
[group('dev')]
build preset="dev":
cmake --build --preset {{preset}}
# Configure, build, and run the test suite.
[group('dev')]
test:
cmake --preset testing
cmake --build --preset testing
ctest --preset testing
# Apply clang-format across the tree (use -Fix to apply, -Staged for staged files only).
[group('dev')]
format *args:
pwsh tools/run-clang-format.ps1 {{args}}
# Run clang-tidy (use -Fix to apply, -Staged for staged files only, -Check <name> for a single check).
[group('dev')]
lint *args: configure
pwsh tools/run-clang-tidy.ps1 {{args}}
# Remove all build directories.
[group('dev')]
clean:
rm -rf build
# Generate release notes for the given version, insert the corresponding
# <release> entry into the appdata, and bump CMakeLists.txt versions.
# Review the resulting changes with `git diff` before running `just release-push`.
# Maintainer use only.
[group('release')]
release-prepare version:
bash tools/release-prepare.sh {{version}}
# Commit appdata + version bump, push, create the draft release with the
# generated notes, then tag and push the tag (which triggers build CI).
# The actual GitHub Publish click still happens manually in the UI.
# `remote` controls which remote (and matching GitHub repo) receives the push;
# default is `origin` (works for fork testing). For an upstream release, run
# `just release-push 0.8.2 upstream` (or whatever you've named the upstream remote).
# Maintainer use only.
[group('release')]
release-push version remote="origin":
git add assets/freedesktop/org.zealdocs.zeal.appdata.xml.in CMakeLists.txt
git commit -m "chore: release v{{version}}"
git push {{remote}} HEAD
gh release create v{{version}} --draft --notes-file build/release-notes.md \
--repo "$(git remote get-url {{remote}} | sed -E 's|^.*github\.com[:/]||; s|\.git$||')"
git tag v{{version}}
git push {{remote}} v{{version}}
@echo "Pushed v{{version}} with draft release. CI will upload artifacts."
@echo "When CI is done, open the draft on GitHub and click Publish."
-29
View File
@@ -1,29 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "go",
"skip-changelog": true,
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"draft": true,
"force-tag-creation": true,
"include-v-in-tag": true,
"pull-request-title-pattern": "chore: release v${version}",
"changelog-sections": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "perf", "section": "Performance"},
{"type": "docs", "section": "Documentation"},
{"type": "build", "section": "Build System"},
{"type": "ci", "section": "CI/CD"},
{"type": "chore", "hidden": true},
{"type": "refactor", "hidden": true},
{"type": "style", "hidden": true},
{"type": "test", "hidden": true}
],
"extra-files": [
"CMakeLists.txt"
],
"packages": {
".": {}
}
}
@@ -1,40 +1,26 @@
# git-cliff ~ configuration file # git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration # https://git-cliff.org/docs/configuration
# Set via GITHUB_REPO and GITHUB_TOKEN environment variables. [remote.github]
#[remote.github] owner = "zealdocs"
#owner = "zealdocs" repo = "zeal"
#repo = "zeal" # Token is read from GITHUB_TOKEN env var (gh's auth) and is needed only to
#token = "" # enrich commits with PR titles and contributor info.
[changelog] [changelog]
# Template docs: https://keats.github.io/tera/docs/. # Template docs: https://keats.github.io/tera/docs/.
body = """\ body = """\
<!-- TODO: Update milestone link, review commits. -->
{% if version and previous.version %}
[Full Changelog]({{ self::diff_url() }}) | [Resolved Issues]({{ self::remote_url() }}/milestone/TBD?closed=1)
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %} {% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim }} ### {{ group | striptags | trim }}
{% for commit in commits %} {% for commit in commits %}
{% if commit.github.pr_title -%}
{%- set commit_message = commit.github.pr_title -%}
{%- else -%}
{%- set commit_message = commit.message -%}
{%- endif -%}
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\ - {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\
{{ commit.message | split(pat="\n") | first | trim }} \ {{ commit.message | split(pat="\n") | first | trim }} \
({{ self::commit_link(id=commit.id) }})\ ({{ self::commit_link(id=commit.id) }})\
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
--- {%- if github and github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
{%- if github -%}\ #### New Contributors
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
{% raw %}\n{% endraw -%}
#### New Contributors
{%- endif %}
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} {% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
- @{{ contributor.username }} made their first contribution - @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %} in \ {%- if contributor.pr_number %} in \
@@ -42,6 +28,11 @@ body = """\
{%- endif %} {%- endif %}
{%- endfor -%} {%- endfor -%}
{%- endif -%} {%- endif -%}
{%- if version and previous.version %}
---
[Full Changelog]({{ self::diff_url() }}){% if extra and extra.milestone %} | [Resolved Issues]({{ self::remote_url() }}/milestone/{{ extra.milestone }}?closed=1){% endif %}
{% endif %}\
{%- macro remote_url() -%} {%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
@@ -61,39 +52,37 @@ body = """\
""" """
# Remove the leading and trailing whitespace from the template. # Remove the leading and trailing whitespace from the template.
trim = true trim = true
# Template for the changelog footer.
footer = """
"""
# Postprocessors.
postprocessors = []
[git] [git]
# Parse the commits based on https://www.conventionalcommits.org. # Parse the commits based on https://www.conventionalcommits.org.
conventional_commits = true conventional_commits = true
# Filter out the commits that are not conventional. # Filter out the commits that are not conventional.
filter_unconventional = true filter_unconventional = true
# Process each line of a commit as an individual commit.
split_commits = false
# Regex for preprocessing the commit messages. # Regex for preprocessing the commit messages.
commit_preprocessors = [ commit_preprocessors = [
# Remove issue numbers from commits. # Remove issue numbers from commits.
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
] ]
# Regex for parsing and grouping commits. # Regex for parsing and grouping commits.
# The `<!-- N -->` prefix on each group name is a sort hack: cliff orders groups
# alphabetically, the HTML comments establish the desired numeric order, and the
# `striptags` filter in the body template strips them before display.
commit_parsers = [ commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->Features" }, { message = "^feat", group = "<!-- 0 -->Features" },
{ message = "^fix", group = "<!-- 1 -->Bug Fixes" }, { message = "^fix", group = "<!-- 1 -->Bug Fixes" },
{ message = "^perf", group = "<!-- 2 -->Performance" }, { message = "^perf", group = "<!-- 2 -->Performance" },
{ message = "^doc", group = "<!-- 3 -->Documentation" }, { message = "^docs", group = "<!-- 3 -->Documentation" },
{ message = "^build", group = "<!-- 4 -->Build System" }, { message = "^build", group = "<!-- 4 -->Build System" },
{ message = "^ci", group = "<!-- 5 -->CI/CD" }, # Skipped groups (kept in git history but excluded from release notes):
# Skipped groups: { message = "^ci", skip = true },
{ message = "^chore", skip = true }, { message = "^chore", skip = true },
{ message = "^refactor", skip = true }, { message = "^refactor", skip = true },
{ message = "^style", skip = true },
{ message = "^test", skip = true },
{ message = "^revert", skip = true },
] ]
# Filter out the commits that are not matched by commit parsers. # Drop commits whose type doesn't match any parser above (typos, weird types).
filter_commits = false filter_commits = true
# Sort the tags topologically. # Always include breaking-change commits (`feat!:`, `fix!:`, etc.) even if a
topo_order = false # skip rule above would otherwise drop them.
# Sort the commits inside sections by oldest/newest order. protect_breaking_commits = true
sort_commits = "oldest"
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
#
# release-prepare.sh - Generate release notes and stage version bumps locally.
#
# SPDX-FileCopyrightText: Oleg Shparber, et al. <https://zealdocs.org>
# SPDX-License-Identifier: MIT
#
# Generates release notes via git-cliff and AppStream description bullets via
# Claude, inserts the new <release> entry into the appdata, and bumps the
# version markers in CMakeLists.txt. Review the result with `git diff` before
# running `just release-push <version>`.
#
# Usage: tools/release-prepare.sh <version>
#
set -euo pipefail
trap 'echo "ERROR: failed at line ${LINENO}: ${BASH_COMMAND}" >&2' ERR
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <version>" >&2
exit 2
fi
VERSION="$1"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid version: '${VERSION}'. Expected major.minor.patch (e.g. 0.9.0)." >&2
exit 2
fi
# Canonical upstream repo for milestone lookups, regardless of which fork the
# maintainer is currently working on. Cliff itself is hardcoded to the same
# repo via [remote.github] in tools/cliff.toml.
CANONICAL_REPO="zealdocs/zeal"
# Lowercase to avoid clobbering the Windows env var of the same name (APPDATA
# is what gh uses to find its config). Local script vars stay lowercase.
appdata="assets/freedesktop/org.zealdocs.zeal.appdata.xml.in"
cmakelists="CMakeLists.txt"
mkdir -p build
if grep -qF "version=\"${VERSION}\"" "$appdata"; then
echo "${appdata} already has a <release> for ${VERSION}; aborting." >&2
echo "Run 'git restore .' to redo." >&2
exit 1
fi
milestone=$(gh api "repos/${CANONICAL_REPO}/milestones?state=all" \
--jq ".[] | select(.title == \"${VERSION}\") | .number" || true)
if [ -n "$milestone" ]; then
echo "Linking upstream milestone #${milestone} (\"${VERSION}\")"
# cliff's --context flag in 2.x is a debug print, not a way to set template
# vars. The documented mechanism for custom template data is the `extra`
# field on release/commit objects: dump cliff's context, jq-inject the
# milestone into the release's `extra`, render via --from-context.
git cliff --tag "v${VERSION}" --unreleased --config tools/cliff.toml --context \
> build/cliff-context.json
jq ".[0].extra.milestone = ${milestone}" build/cliff-context.json \
> build/cliff-context.with-milestone.json
git cliff --config tools/cliff.toml --from-context build/cliff-context.with-milestone.json \
> build/release-notes.md
else
echo "No upstream milestone titled \"${VERSION}\"; skipping milestone link."
git cliff --tag "v${VERSION}" --unreleased --config tools/cliff.toml > build/release-notes.md
fi
claude -p "Read this git-cliff release changelog and summarize the \
user-facing changes as up to 10 <li>...</li> bullets for an AppStream \
<description>. AppStream is read by Linux software centers, so skip \
Windows-only and macOS-only changes. Each bullet should be concrete: the \
specific problem fixed or capability added, in terms a non-developer \
recognizes. Use past tense and start each bullet with one of: Added, \
Changed, Removed, Deprecated, Fixed. Order bullets by category in that \
sequence (Added first, Fixed last). No trailing period on bullets. Skip \
internal refactors, build/CI/dependency commits, and changes a user \
wouldn't notice. Avoid common AI cliches (em-dashes, 'delve into', \
'leverage', 'robust', 'ensure', 'seamless', 'comprehensive'). Output \
bullets only, no wrapping <ul>." \
< build/release-notes.md \
> build/appdata-bullets.txt
today=$(date -u +%Y-%m-%d)
indented=$(sed 's/^[[:space:]]*//; s/^/ /' build/appdata-bullets.txt)
cat > build/release-block.xml <<EOF
<release date="${today}" version="${VERSION}" type="stable">
<description>
<ul>
${indented}
</ul>
</description>
<url>https://github.com/${CANONICAL_REPO}/releases/tag/v${VERSION}</url>
</release>
EOF
sed -i "/<releases>@ZEAL_APPSTREAM_DEV_RELEASE@/r build/release-block.xml" "$appdata"
sed -i "s/ VERSION [0-9.]\+ # x-version/ VERSION ${VERSION} # x-version/" "$cmakelists"
sed -i "s/set(ZEAL_RELEASE_VERSION \"[0-9.]\+\") # x-version/set(ZEAL_RELEASE_VERSION \"${VERSION}\") # x-version/" "$cmakelists"
echo "---"
echo "Applied:"
printf ' %-55s %s\n' \
"${appdata}" "new <release> entry for v${VERSION}" \
"${cmakelists}" "VERSION and ZEAL_RELEASE_VERSION bumped to ${VERSION}" \
"build/release-notes.md" "used by 'just release-push' as --notes-file"
echo
echo "Review with: git diff"
echo "Then run: just release-push ${VERSION}"
echo
echo "To redo: git restore . && just release-prepare ${VERSION}"