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
55 changes: 55 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,61 @@ jobs:
- name: Check if documentation can be built
run: uv run zensical build -s

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: |
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"
{
echo "changelog<<PSR_EOF"
uv run semantic-release changelog --print 2>/dev/null | head -60 || echo "(no changelog preview)"
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: `{1}`).', steps.preview.outputs.next, steps.preview.outputs.current) || format('No version bump from the current commits (stays at `{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>Computed by python-semantic-release from the conventional commits on this branch.</sub>

benchmark:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
Expand Down
58 changes: 50 additions & 8 deletions .github/workflows/on-release-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,71 @@ name: release-main

on:
push:
tags: ['v[0-9]*']
branches: [main]
workflow_dispatch:

concurrency:
group: release
cancel-in-progress: false

jobs:

publish:
release:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC trusted publishing to PyPI
contents: write # push the release commit and tag, create the GitHub release
outputs:
released: ${{ steps.version.outputs.released }}
version: ${{ steps.version.outputs.version }}
steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history + tags so hatch-vcs derives the version
fetch-depth: 0 # full history + tags so PSR can analyze commits
ref: main

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

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Semantic release version
id: version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
before=$(git rev-parse HEAD)
uv run semantic-release version
after=$(git rev-parse HEAD)
if [ "$before" != "$after" ]; then
tag=$(git tag --points-at HEAD | grep -E '^v[0-9]' | head -1)
echo "released=true" >> "$GITHUB_OUTPUT"
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
echo "Released $tag"
else
echo "released=false" >> "$GITHUB_OUTPUT"
echo "No release for this push."
fi

- name: Build package
if: steps.version.outputs.released == 'true'
run: uv build

- name: Publish package
run: uv publish
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
- name: Check package metadata
if: steps.version.outputs.released == 'true'
run: uvx twine check dist/*

- name: Publish to PyPI (OIDC trusted publishing)
if: steps.version.outputs.released == 'true'
run: uv publish --trusted-publishing always

deploy-docs:
needs: publish
needs: release
if: needs.release.outputs.released == 'true'
permissions:
contents: read
pages: write
Expand All @@ -38,6 +78,8 @@ jobs:
steps:
- name: Check out
uses: actions/checkout@v4
with:
ref: main # include the release commit just pushed by PSR

- name: Set up the environment
uses: ./.github/actions/setup-python-env
Expand Down
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Install both the pre-commit and commit-msg stage hooks on a plain
# `pre-commit install` (no --hook-type flags needed).
default_install_hook_types: [pre-commit, commit-msg]

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v6.0.0"
Expand All @@ -20,3 +24,12 @@ repos:
- id: ruff-check
args: [--exit-non-zero-on-fix]
- id: ruff-format

# Enforce Conventional Commits locally so python-semantic-release can derive
# versions and the changelog. Runs on the commit-msg stage; install it with
# `pre-commit install --hook-type commit-msg` (done by `make install`).
- repo: https://github.com/compilerla/conventional-pre-commit
rev: "v4.0.0"
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Changelog
# CHANGELOG

## Version 0.1 (development)
All notable changes to this project are documented here. Versions are cut
automatically from Conventional Commits on every merge to main by
[python-semantic-release](https://python-semantic-release.readthedocs.io/). Do
not edit released sections by hand.

- Feature A added
- FIX: nasty bug #1729 fixed
- add your changes here!
<!-- version list -->
2 changes: 2 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ cff-version: 1.2.0
message: "If you use this software, please cite it as below."
type: software
title: "oold-python: Object Oriented Linked Data for Python"
# Managed by python-semantic-release (version_variables). Do not edit by hand.
version: 0.16.2
abstract: "A Python package for abstract and object-oriented access to knowledge graphs, bridging semantic web technologies (RDF, JSON-LD, SPARQL) with Pydantic type safety."
repository-code: "https://github.com/OO-LD/oold-python"
url: "https://OO-LD.github.io/oold-python/"
Expand Down
47 changes: 37 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ make install
Without `make`:

```bash
uv sync
uv sync --all-extras
uv run pre-commit install
```

`pre-commit install` installs both the `pre-commit` and `commit-msg` stage hooks
(via `default_install_hook_types`); the latter enforces Conventional Commits (see
below).

## Making Changes

1. Create a branch: `git checkout -b name-of-your-fix`
Expand Down Expand Up @@ -66,19 +70,42 @@ uv run zensical serve
uv run zensical build -s
```

## Releasing
## Commit messages (Conventional Commits)

This project uses [Conventional Commits](https://www.conventionalcommits.org/).
Commit messages drive versioning and the changelog automatically, so the format
matters. The local `commit-msg` hook rejects malformed messages.

Format: `type(scope): subject`, for example `fix: correct sidebar collapse on
small screens`. The scope is optional.

Releases are published automatically by CI when a version tag is pushed.
| Type | Release effect | Use for |
| ---- | -------------- | ------- |
| `feat` | minor bump | a new feature |
| `fix` | patch bump | a bug fix |
| `perf` | patch bump | a performance improvement |
| `docs`, `chore`, `test`, `refactor`, `ci`, `style`, `build` | no release | changes that do not ship user-facing behavior |
| `BREAKING CHANGE:` footer, or `!` after the type | major bump | an incompatible change |

A breaking change is marked either with a `!` (`feat!: drop Python 3.9`) or a
`BREAKING CHANGE:` footer in the commit body.

## Releasing

1. Ensure all changes are merged to `main`
2. Tag the commit and push:
Releases are fully automated by python-semantic-release. You do not tag or bump
the version by hand.

```bash
git tag v0.17.0
git push origin v0.17.0
```
1. Open a PR. CI comments the version that a merge would release, based on your
commits.
2. Merge to `main`. On merge, CI reads the new conventional commits, bumps the
version in `pyproject.toml` and `CITATION.cff`, updates `CHANGELOG.md`,
commits with `[skip ci]`, and pushes the `vX.Y.Z` tag.
3. CI then builds the package, publishes it to PyPI via OIDC trusted publishing,
and deploys the docs to GitHub Pages.

CI will build the package (`uv build`), publish it to PyPI, and deploy the docs to GitHub Pages. The version is derived from the git tag via `hatch-vcs`, so no manual version bumping is needed.
If a merge contains only non-releasing commit types (for example `docs` or
`chore`), no release is cut. The version lives in `pyproject.toml`; never edit it
manually.

## Citation and authorship

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: install
install: ## Install the virtual environment and install the pre-commit hooks
@echo "🚀 Creating virtual environment using uv"
@uv sync
@uv sync --all-extras
@uv run pre-commit install

.PHONY: check
Expand Down
34 changes: 29 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ license = "Apache-2.0"
license-files = ["LICENSE"]
keywords = ['python', 'linked-data', 'json-ld', 'pydantic', 'oo-ld']
requires-python = ">=3.10,<4.0"
dynamic = ["version"]
# Version is managed by python-semantic-release (see [tool.semantic_release]).
# Do not edit by hand; it is bumped automatically on merge to main.
version = "0.16.2"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
Expand Down Expand Up @@ -88,18 +90,40 @@ dev = [
"ruff>=0.15.7",
"zensical>=0.0.26",
"mkdocstrings-python>=1.0.3",
"python-semantic-release>=10.0.0",
]

[build-system]
requires = ["hatchling", "hatch-vcs"]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.version]
source = "vcs"

[tool.hatch.build.targets.wheel]
packages = ["src/oold"]

[tool.semantic_release]
# python-semantic-release owns the version and changelog. On merge to main it
# reads the conventional commits, bumps the static version in the files below,
# updates the changelog, commits, and tags. The v-prefixed tag namespace is
# preserved (matches the existing 32 releases).
commit_parser = "conventional"
tag_format = "v{version}"
commit_message = "chore(release): v{version} [skip ci]"
allow_zero_version = true
build_command = "uv lock"
version_toml = ["pyproject.toml:project.version"]
version_variables = ["CITATION.cff:version"]
exclude_commit_patterns = ['''chore\(release\):.*''']
# uv.lock pins oold's own version, so re-lock (build_command) and include it in
# the release commit to keep `uv sync --frozen` consistent on the next run.
assets = ["uv.lock"]

[tool.semantic_release.changelog]
mode = "update"
insertion_flag = "<!-- version list -->"

[tool.semantic_release.changelog.default_templates]
changelog_file = "CHANGELOG.md"

[tool.ty.environment]
python = "./.venv"
python-version = "3.10"
Expand Down
Loading
Loading