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
63 changes: 63 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,69 @@ jobs:
# (full copy + make init) and run the generated repository's own check
# suite. A red profile blocks the merge, so no broken core ever reaches a
# grafted repository through an update.
version-preview:
# Show contributors what a merge would release, based on the conventional
# commits on the branch. Pull requests only; informational.
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0 # PSR needs full history + tags to compute the bump
ref: ${{ github.event.pull_request.head.sha }}

- name: Set up the environment
uses: ./.github/actions/setup-python-env

- name: Compute release preview
id: preview
run: |
# PSR only computes a release on the main branch; name the checked-out
# PR head 'main' so the preview reflects what a merge would release.
git checkout -B main >/dev/null 2>&1 || true
current=$(uv version --short 2>/dev/null || echo "unknown")
next=$(uv run semantic-release version --print 2>/dev/null || echo "")
if [ -n "$next" ] && [ "$next" != "$current" ]; then
echo "released=true" >> "$GITHUB_OUTPUT"
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "next=$next" >> "$GITHUB_OUTPUT"
# Stamp the upcoming changelog section locally (no commit/tag/push/build)
# so the preview shows the notes this merge would add.
if [ -n "$next" ] && [ "$next" != "$current" ]; then
uv run semantic-release version --no-commit --no-tag --no-push --no-vcs-release --skip-build >/dev/null 2>&1 || true
fi
{
echo "changelog<<PSR_EOF"
git --no-pager diff HEAD -- CHANGELOG.md | awk '/^\+/ && !/^\+\+\+/ {sub(/^\+/,""); print}' | head -80
echo "PSR_EOF"
} >> "$GITHUB_OUTPUT"

- name: Post release preview comment
uses: marocchino/sticky-pull-request-comment@v2
with:
header: version-preview
message: |
## Release preview

${{ steps.preview.outputs.released == 'true' && format('Merging this PR would release **v{0}** (current: `v{1}`).', steps.preview.outputs.next, steps.preview.outputs.current) || format('No version bump from the current commits (stays at `v{0}`). Use conventional commit types (`feat`, `fix`, ...) to trigger a release.', steps.preview.outputs.current) }}

<details><summary>Changelog preview (truncated)</summary>

```markdown
${{ steps.preview.outputs.changelog }}
```

</details>

<sub>Preview via python-semantic-release and conventional commits.</sub>

test-profiles:
runs-on: ubuntu-latest
strategy:
Expand Down
63 changes: 63 additions & 0 deletions profiles/python/.github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,69 @@ jobs:
- name: Check if documentation can be built
run: make docs-test

version-preview:
# Show contributors what a merge would release, based on the conventional
# commits on the branch. Pull requests only; informational.
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0 # PSR needs full history + tags to compute the bump
ref: ${{ github.event.pull_request.head.sha }}

- name: Set up the environment
uses: ./.github/actions/setup-python-env

- name: Compute release preview
id: preview
run: |
# PSR only computes a release on the main branch; name the checked-out
# PR head 'main' so the preview reflects what a merge would release.
git checkout -B main >/dev/null 2>&1 || true
current=$(uv version --short 2>/dev/null || echo "unknown")
next=$(uv run semantic-release version --print 2>/dev/null || echo "")
if [ -n "$next" ] && [ "$next" != "$current" ]; then
echo "released=true" >> "$GITHUB_OUTPUT"
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "next=$next" >> "$GITHUB_OUTPUT"
# Stamp the upcoming changelog section locally (no commit/tag/push/build)
# so the preview shows the notes this merge would add.
if [ -n "$next" ] && [ "$next" != "$current" ]; then
uv run semantic-release version --no-commit --no-tag --no-push --no-vcs-release --skip-build >/dev/null 2>&1 || true
fi
{
echo "changelog<<PSR_EOF"
git --no-pager diff HEAD -- CHANGELOG.md | awk '/^\+/ && !/^\+\+\+/ {sub(/^\+/,""); print}' | head -80
echo "PSR_EOF"
} >> "$GITHUB_OUTPUT"

- name: Post release preview comment
uses: marocchino/sticky-pull-request-comment@v2
with:
header: version-preview
message: |
## Release preview

${{ steps.preview.outputs.released == 'true' && format('Merging this PR would release **v{0}** (current: `v{1}`).', steps.preview.outputs.next, steps.preview.outputs.current) || format('No version bump from the current commits (stays at `v{0}`). Use conventional commit types (`feat`, `fix`, ...) to trigger a release.', steps.preview.outputs.current) }}

<details><summary>Changelog preview (truncated)</summary>

```markdown
${{ steps.preview.outputs.changelog }}
```

</details>

<sub>Preview via python-semantic-release and conventional commits.</sub>

# >>> benchmarks
# Continuous benchmarking with Bencher: pytest-benchmark produces the JSON,
# `bencher run` uploads it and comments the comparison against the base
Expand Down
63 changes: 63 additions & 0 deletions profiles/schema/.github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,66 @@ jobs:

- name: Check if documentation can be built
run: make docs-test

version-preview:
# Show contributors what a merge would release, based on the conventional
# commits on the branch. Pull requests only; informational.
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0 # PSR needs full history + tags to compute the bump
ref: ${{ github.event.pull_request.head.sha }}

- name: Set up the environment
uses: ./.github/actions/setup-python-env

- name: Compute release preview
id: preview
run: |
# PSR only computes a release on the main branch; name the checked-out
# PR head 'main' so the preview reflects what a merge would release.
git checkout -B main >/dev/null 2>&1 || true
current=$(uv version --short 2>/dev/null || echo "unknown")
next=$(uv run semantic-release version --print 2>/dev/null || echo "")
if [ -n "$next" ] && [ "$next" != "$current" ]; then
echo "released=true" >> "$GITHUB_OUTPUT"
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "next=$next" >> "$GITHUB_OUTPUT"
# Stamp the upcoming changelog section locally (no commit/tag/push/build)
# so the preview shows the notes this merge would add.
if [ -n "$next" ] && [ "$next" != "$current" ]; then
uv run semantic-release version --no-commit --no-tag --no-push --no-vcs-release --skip-build >/dev/null 2>&1 || true
fi
{
echo "changelog<<PSR_EOF"
git --no-pager diff HEAD -- CHANGELOG.md | awk '/^\+/ && !/^\+\+\+/ {sub(/^\+/,""); print}' | head -80
echo "PSR_EOF"
} >> "$GITHUB_OUTPUT"

- name: Post release preview comment
uses: marocchino/sticky-pull-request-comment@v2
with:
header: version-preview
message: |
## Release preview

${{ steps.preview.outputs.released == 'true' && format('Merging this PR would release **v{0}** (current: `v{1}`).', steps.preview.outputs.next, steps.preview.outputs.current) || format('No version bump from the current commits (stays at `v{0}`). Use conventional commit types (`feat`, `fix`, ...) to trigger a release.', steps.preview.outputs.current) }}

<details><summary>Changelog preview (truncated)</summary>

```markdown
${{ steps.preview.outputs.changelog }}
```

</details>

<sub>Preview via python-semantic-release and conventional commits.</sub>
28 changes: 28 additions & 0 deletions tests/test_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,34 @@ def test_ci_target_names_the_current_branch(makefile: str) -> None:
assert 'Ready to push"' in ci, f"{makefile}: ci has no fallback without a branch"


@pytest.mark.parametrize(
"workflow",
[".github/workflows/main.yml", *(f"profiles/{p}/.github/workflows/main.yml" for p in PROFILES)],
)
def test_every_main_workflow_previews_the_release(workflow: str) -> None:
# Conventional commit types decide the version, and until this job existed
# nobody saw the consequence of getting one wrong until after the merge.
# Three copies of the same job, so they drift unless something checks.
spec = yaml.safe_load((REPO / workflow).read_text(encoding="utf-8"))
job = spec["jobs"].get("version-preview")
assert job is not None, f"{workflow}: no version-preview job"
assert job["permissions"]["pull-requests"] == "write", "cannot comment without this"
assert "pull_request" in job["if"] and "draft" in job["if"], "must be pull requests only, and not drafts"
steps = {step.get("name", ""): step for step in job["steps"]}
checkout = next(step for step in job["steps"] if str(step.get("uses", "")).startswith("actions/checkout"))
# PSR walks history and tags to compute the bump; a shallow clone yields
# a wrong answer rather than an error.
assert checkout["with"]["fetch-depth"] == 0
preview = steps["Compute release preview"]["run"]
# PSR only computes a release on the main branch, so the PR head has to be
# named main or the preview is always "no release".
assert "git checkout -B main" in preview
assert "semantic-release version --print" in preview
# The changelog stamp must not commit, tag, push or release anything.
for guard in ("--no-commit", "--no-tag", "--no-push", "--no-vcs-release"):
assert guard in preview, f"{workflow}: changelog preview missing {guard}"


@pytest.mark.parametrize("profile", PROFILES)
def test_instances_explain_conventional_commits(tmp_path: Path, profile: str) -> None:
# The commit-msg hook rejects a non-conventional message, and until this
Expand Down
Loading