Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
## Changelog

- [ ] Added `changes/<pr-number>.<type>.md`
- [ ] This change is internal-only; explain why a maintainer should apply the
`skip-changelog` label:
- [ ] This is dependency maintenance, or it is internal-only and the PR
explains why a maintainer should apply `skip-changelog`:
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ jobs:
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GH_TOKEN: ${{ github.token }}
PR_ACTOR: ${{ github.actor }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
changed_files="$(git diff --name-only "$BASE_SHA" HEAD)"
Expand All @@ -44,9 +43,11 @@ jobs:
run_container=false
fi

if [[ "$PR_ACTOR" == "dependabot[bot]" ]] ||
labels="$(
gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/labels" \
--jq '.[].name' | grep -Fxq 'skip-changelog'; then
--jq '.[].name'
)"
if grep -Eq '^(dependencies|skip-changelog)$' <<< "$labels"; then
require_changelog=false
else
require_changelog=true
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/release-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ jobs:
.release-smoke/bin/python -m pip check
.release-smoke/bin/vidxp --version

- name: Upload GitHub release assets
- name: Publish GitHub release
if: steps.release.outputs.released == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: semantic-release publish --tag "${{ steps.release.outputs.tag }}"
run: |
semantic-release publish --tag "${{ steps.release.outputs.tag }}"
gh release edit "${{ steps.release.outputs.tag }}" \
--notes-file .release-notes.md

- name: Preserve distribution
if: steps.release.outputs.released == 'true'
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/release-to-test-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
- name: Determine and tag prerelease version
id: release
env:
BUILD_RELEASE_NOTES: "1"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: semantic-release version --as-prerelease --prerelease-token b --no-changelog

Expand All @@ -60,11 +61,14 @@ jobs:
.release-smoke/bin/python -m pip check
.release-smoke/bin/vidxp --version

- name: Upload GitHub release assets
- name: Publish GitHub release
if: steps.release.outputs.released == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: semantic-release publish --tag "${{ steps.release.outputs.tag }}"
run: |
semantic-release publish --tag "${{ steps.release.outputs.tag }}"
gh release edit "${{ steps.release.outputs.tag }}" \
--notes-file .release-notes.md

- name: Preserve distribution
if: steps.release.outputs.released == 'true'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/video.mp4
/dist/
/build/
/.release-notes.md
.build
/MANIFEST
/.pytest_cache/
Expand Down
10 changes: 6 additions & 4 deletions changes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ changes/123.feature.md
Add named repositories for selecting shared index locations and devices.
```

Purely internal changes may omit a fragment only when a maintainer applies the
`skip-changelog` label and the pull request explains why.
Dependency updates marked `dependencies` do not require a fragment. Other
internal changes may omit one when a maintainer applies `skip-changelog` and
the pull request explains why.

Towncrier collects and removes fragments when a stable release is created.
Do not edit `CHANGELOG.md` directly.
Towncrier renders the pending fragments for prerelease notes, then collects and
removes them when a stable release is created. Do not edit `CHANGELOG.md`
directly.
15 changes: 8 additions & 7 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,14 @@ The fragment should be one sentence written for users. Do not include a heading,
version number, commit message, or implementation details. See
[`changes/README.md`](../changes/README.md) for an example.

Internal-only maintenance does not need an empty fragment. Explain the reason in
the pull request and ask a maintainer to apply the `skip-changelog` label. CI
requires either a fragment or that label.

Towncrier collects the pending fragments into `CHANGELOG.md` and removes them
when a stable release is made. Do not edit the changelog or package version in a
feature pull request.
Dependency updates carrying GitHub's `dependencies` label do not require a
fragment. For other internal-only maintenance, explain the reason in the pull
request and ask a maintainer to apply `skip-changelog`. CI requires a fragment
unless one of those labels is present.

Towncrier renders pending fragments as prerelease notes, then collects them into
`CHANGELOG.md` and removes them when a stable release is made. Do not edit the
changelog or package version in a feature pull request.

## Questions

Expand Down
14 changes: 8 additions & 6 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ calculate the next version, creates a `b` prerelease tag and GitHub prerelease,
builds the distributions, and publishes them to TestPyPI. Commits that do not
require a semantic version bump do not publish a package.

Pending Towncrier fragments remain in `changes/` during prereleases.
Towncrier renders the pending fragments into the GitHub prerelease body without
consuming them. The fragments remain in `changes/` for the stable release.

## Stable releases

Expand All @@ -22,11 +23,12 @@ Pending Towncrier fragments remain in `changes/` during prereleases.
5. Confirm the new tag, GitHub release, PyPI package, and emptied pending
fragment set.

The workflow only runs its validation and release jobs for a `main` dispatch.
Both jobs use the same immutable dispatch revision. Python Semantic Release is
the only version authority. During its build step, Towncrier receives that
calculated version, updates `CHANGELOG.md`, and removes the released fragments
before the release commit and tag are created.
The workflow only runs from a `main` dispatch, and every publication job uses
the release commit created from that immutable revision. Python Semantic
Release is the only version authority. Towncrier is the only release-note
renderer. During the stable build it receives the calculated version, renders
the GitHub release body, updates `CHANGELOG.md` with the same section, and
removes the released fragments before the release commit and tag are created.

Release and CI tools are declared once in `utils/build-requirements.txt`. Do not
duplicate their versions in workflow files.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ select = ["E4", "E7", "E9", "F"]
version_toml = ["pyproject.toml:project.version"]
tag_format = "v{version}"
build_command = "bash ./utils/build_package.sh"
build_command_env = ["BUILD_CHANGELOG"]
build_command_env = ["BUILD_CHANGELOG", "BUILD_RELEASE_NOTES"]
assets = ["CHANGELOG.md"]
commit_parser = "conventional"
major_on_zero = true
Expand Down
25 changes: 19 additions & 6 deletions utils/build_package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ README="README.md"
README_BAK="$README.bak"
BUILD_DIR="build"
DIST_DIR="dist"
RELEASE_NOTES=".release-notes.md"

restore_readme() {
if [[ -f "$README_BAK" ]]; then
Expand All @@ -15,14 +16,29 @@ restore_readme() {

trap restore_readme EXIT

if [[ "${BUILD_CHANGELOG:-0}" == "1" ]]; then
echo "🧹 Removing stale package artifacts..."
rm -rf -- "$BUILD_DIR" "$DIST_DIR" "$RELEASE_NOTES"

if [[ "${BUILD_CHANGELOG:-0}" == "1" || "${BUILD_RELEASE_NOTES:-0}" == "1" ]]; then
if [[ -z "${NEW_VERSION:-}" ]]; then
echo "NEW_VERSION is required when BUILD_CHANGELOG=1" >&2
echo "NEW_VERSION is required when rendering release notes" >&2
exit 1
fi

RELEASE_DATE="$(date -u +%Y-%m-%d)"
echo "📰 Rendering release notes for v${NEW_VERSION}..."
towncrier build \
--draft \
--version "$NEW_VERSION" \
--date "$RELEASE_DATE" > "$RELEASE_NOTES"
fi

if [[ "${BUILD_CHANGELOG:-0}" == "1" ]]; then
echo "📰 Building changelog for v${NEW_VERSION}..."
towncrier build --yes --version "$NEW_VERSION"
towncrier build \
--yes \
--version "$NEW_VERSION" \
--date "$RELEASE_DATE"
fi

echo "📝 Backing up original README..."
Expand All @@ -31,9 +47,6 @@ cp "$README" "$README_BAK"
echo "🔧 Processing README.md for PyPI rendering..."
python utils/fix_readme_links.py "$BASE_URL" "$README" --inplace

echo "🧹 Removing stale package artifacts..."
rm -rf -- "$BUILD_DIR" "$DIST_DIR"

echo "📦 Building package..."
python -m build

Expand Down