Files
wehub-resource-sync 3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

196 lines
8.2 KiB
YAML

name: Release
on:
push:
# Only REL-prefixed marker tags create a public release. Version
# tags themselves (v0.4.0, v0.4.0rc1) trigger the four packaging
# workflows (.deb / RPM / OBS / AUR) via their `v*.*.*` globs but
# NOT this Release workflow — that's deliberate. kernalix7 found
# that rapid-iteration tags between v0.1.9 and v0.2.2.1 shipped
# unverified releases that broke users' working installs
# (2026-04-29). Releasing now requires pushing a SECOND tag pointed
# at the same commit as the version tag, namespaced with REL- so
# the version scheme itself stays clean (PEP 440 / SemVer):
#
# git tag v0.4.0 # version, triggers packaging
# git tag REL-v0.4.0 # marker, triggers this workflow
# git push origin v0.4.0 REL-v0.4.0
#
# The previous `*RTM*` glob was retired 2026-05-05 alongside the
# PEP 440 versioning move (current versions are `0.4.0` /
# `0.4.0rc1`, no RTM suffix anywhere). Manual workflow_dispatch
# still works for emergency one-offs.
tags:
- "REL-*"
workflow_dispatch:
permissions:
contents: write
# id-token: write # reserved for future PyPI trusted publisher
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: pip install ruff
- name: Run ruff check
run: ruff check src/ tests/
- name: Run ruff format check
run: ruff format --check src/ tests/
- name: Install project
run: pip install -e ".[dev]"
- name: Run tests
run: pytest tests/ -v
# Skip tag/pyproject version parity check on manual dispatch
- name: Verify tag matches pyproject version
if: github.event_name == 'push'
run: |
# Tag layout: REL-v<pep440-version>. Strip the REL- marker
# prefix and the leading 'v' to get pyproject.toml's version
# field (e.g., REL-v0.4.0rc1 -> 0.4.0rc1, REL-v0.4.0 -> 0.4.0).
tag="${GITHUB_REF_NAME#REL-}"
tag="${tag#v}"
pyver=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
if [ "$tag" != "$pyver" ]; then
echo "Tag version ($tag) does not match pyproject.toml version ($pyver)" >&2
exit 1
fi
build:
needs: validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: python -m pip install build twine
- name: Build distribution
run: python -m build
- name: Check distribution metadata
run: python -m twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Extract version from REL tag
id: version
run: |
# REL-v0.4.0 -> 0.4.0; REL-v0.4.0rc1 -> 0.4.0rc1. Used by all
# subsequent steps so the public Release page reads
# "v0.4.0" / "0.4.0" instead of the internal "REL-v0.4.0"
# marker tag name.
tag="${GITHUB_REF_NAME#REL-}"
tag="${tag#v}"
echo "value=$tag" >> "$GITHUB_OUTPUT"
- name: Extract changelog section
id: changelog
run: |
# REL-v0.4.0 -> 0.4.0; REL-v0.4.0rc1 -> 0.4.0rc1.
tag="${GITHUB_REF_NAME#REL-}"
tag="${tag#v}"
# Determine the previous published version so we can include
# any intermediate unreleased sections in the notes (sections
# the user wrote in CHANGELOG but never tagged & released).
# On 2026-05-05, v0.3.1 was such an unreleased section: it
# collected Mic92's PRs #62-#65 but no v0.3.1 tag was ever
# pushed; v0.4.0rc1's release page would have lost those
# contributor credits if it only matched ## [0.4.0].
# Strategy: find the most recent CHANGELOG section that
# corresponds to a published GitHub Release (i.e., has its own
# tag). Capture from the current section back to (but not
# including) that already-published one.
prev=""
while IFS= read -r section; do
sver="${section#\#\# \[}"
sver="${sver%%\] *}"
# Skip the in-flight version itself and the unreleased header.
[ "$sver" = "$tag" ] && continue
[ "$sver" = "Unreleased" ] && continue
# First section we hit that has a corresponding tag is the
# previous published release. CHANGELOG is in newest-first
# order so this is correct on the first match.
if git rev-parse --verify "v${sver}^{commit}" >/dev/null 2>&1; then
prev="$sver"
break
fi
done < <(grep -E '^## \[' CHANGELOG.md)
notes=$(awk -v ver="$tag" -v prev="$prev" '
$0 ~ "^## \\[" ver "\\]" { flag=1 }
prev != "" && $0 ~ "^## \\[" prev "\\]" { exit }
prev == "" && flag && /^## \[/ && $0 !~ "^## \\[" ver "\\]" { exit }
flag { print }
' CHANGELOG.md)
# PEP 440 prereleases (0.4.0rc1) usually share notes with the
# final (0.4.0) section in CHANGELOG. Fall back to the base
# version section if the prerelease-specific one is missing.
if [ -z "$(echo "$notes" | tr -d '[:space:]')" ]; then
base=$(echo "$tag" | sed -E 's/(rc|alpha|beta)[0-9]*$//')
if [ "$base" != "$tag" ]; then
notes=$(awk -v ver="$base" -v prev="$prev" '
$0 ~ "^## \\[" ver "\\]" { flag=1 }
prev != "" && $0 ~ "^## \\[" prev "\\]" { exit }
prev == "" && flag && /^## \[/ && $0 !~ "^## \\[" ver "\\]" { exit }
flag { print }
' CHANGELOG.md)
fi
fi
if [ -z "$(echo "$notes" | tr -d '[:space:]')" ]; then
notes="See CHANGELOG.md for details."
fi
{
echo "notes<<CHANGELOG_EOF"
echo "$notes"
echo "CHANGELOG_EOF"
} >> "$GITHUB_OUTPUT"
- name: Detect prerelease
id: prerelease
run: |
# Match both PEP 440 ("0.4.0rc1", no separator) and SemVer-
# hyphen ("0.4.0-rc1") prerelease forms after the REL- prefix
# is stripped.
tag="${GITHUB_REF_NAME#REL-}"
tag="${tag#v}"
if echo "$tag" | grep -qE -- '(rc|beta|alpha)[0-9]*'; then
echo "value=true" >> "$GITHUB_OUTPUT"
else
echo "value=false" >> "$GITHUB_OUTPUT"
fi
- name: Create release
uses: softprops/action-gh-release@v2
with:
# tag_name + name override the default github.ref_name
# (REL-v0.4.0 marker) with the clean version tag (v0.4.0) so
# the public release surface is namespace-free. The marker
# tag still exists in git as the trigger record.
tag_name: v${{ steps.version.outputs.value }}
name: v${{ steps.version.outputs.value }}
prerelease: ${{ steps.prerelease.outputs.value }}
# The hand-written CHANGELOG section is the entire release body —
# including its "### Contributors" subsection, which credits the
# issue reporters and external PR authors for the release (the repo's
# house convention; see CONTRIBUTING.md). generate_release_notes is
# deliberately NOT enabled: on this maintainer-authored repo it dumps
# a ~120-line "What's Changed" list that credits the owner on nearly
# every PR and buries the real contributor credits behind it. This
# step is the SOLE writer of the release body: the packaging
# workflows (.deb / RPM / OBS / AppImage) only attach assets, never
# touch the notes, so the release isn't edited/rewritten repeatedly.
body: ${{ steps.changelog.outputs.notes }}
files: |
dist/*.tar.gz
dist/*.whl