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
49 changes: 49 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,56 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Verify release version
run: |
python - <<'PY'
import os
import re
import tomllib
from pathlib import Path

ref_type = os.environ.get("GITHUB_REF_TYPE")
tag_name = os.environ.get("GITHUB_REF_NAME", "")
if ref_type != "tag":
raise SystemExit(
"Publishing requires a tag ref. For workflow_dispatch, run this workflow from the release tag."
)

expected = tag_name[1:] if tag_name.startswith("v") else tag_name
if not expected:
raise SystemExit("Could not determine release version from tag.")

with Path("pyproject.toml").open("rb") as file:
pyproject_version = tomllib.load(file)["project"]["version"]

def read_version(path: str, pattern: str) -> str:
match = re.search(pattern, Path(path).read_text(encoding="utf-8"), re.MULTILINE)
if match is None:
raise SystemExit(f"Could not find package version in {path}.")
return match.group(1)

versions = {
"pyproject.toml": pyproject_version,
"setup.py": read_version("setup.py", r'^VERSION = "([^"]+)"$'),
"audithub_sdk/__init__.py": read_version(
"audithub_sdk/__init__.py", r'^__version__ = "([^"]+)"$'
),
}

mismatches = {
path: version
for path, version in versions.items()
if version != expected
}
if mismatches:
details = "\n".join(
f"- {path}: {version} != tag {tag_name} ({expected})"
for path, version in mismatches.items()
)
raise SystemExit(f"Release version mismatch:\n{details}")

print(f"Release version {expected} matches package metadata.")
PY
- name: Build distributions
run: |
python -m pip install --upgrade pip
Expand Down
12 changes: 12 additions & 0 deletions .openapi-generator-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,15 @@
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

# Repository-maintained files. Generated source, docs, and generated tests are
# still replaced during regeneration.
README.md
AGENTS.md
pyproject.toml
setup.py
.openapi-generator-ignore
.github/
scripts/
audithub_sdk_ext/
tests/
29 changes: 27 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ It is intended to be the shared/core dependency for other Python repositories th
- Package version currently used during generation: `0.1.0`
- Transport template: `httpx`

Generation command:
Preferred regeneration command:

```sh
scripts/regenerate-sdk.sh 0.1.0
```

The script runs the equivalent of:

```sh
openapi-generator generate \
Expand All @@ -28,22 +34,41 @@ openapi-generator generate \
## Important Decisions From Prior Sessions

- The repo should remain mostly generated output. Manual edits should stay limited to repository-specific files and metadata.
- `bump-my-version` is configured for release version bumps, but `README.md`'s generator command should only be updated during SDK regeneration.

## Files That May Be Manually Maintained

- `README.md`
- `AGENTS.md`
- `pyproject.toml`
- `setup.py`
- `.openapi-generator-ignore`
- `scripts/regenerate-sdk.sh`
- `audithub_sdk_ext/`
- `tests/`
- `.github/workflows/python.yml`
- `.github/workflows/publish.yml`

Generated source under `audithub_sdk/`, generated docs under `docs/`, generated tests under `test/`, and generator metadata under `.openapi-generator/` should generally be replaced by regeneration rather than hand-edited.
Generated source under `audithub_sdk/`, generated docs under `docs/`, generated tests under `test/`, and generator metadata under `.openapi-generator/` should generally be replaced by regeneration rather than hand-edited. The narrow exception is package-version strings updated by `bump-my-version` in `audithub_sdk/__init__.py`, `audithub_sdk/api_client.py`, and `audithub_sdk/configuration.py`.

## Version Bumping

`bump-my-version` configuration lives in `pyproject.toml`.

Common commands:

```sh
bump-my-version bump patch
bump-my-version bump --new-version 0.1.1
```

The configured bump is for release-only version bumps. For API schema changes, use `scripts/regenerate-sdk.sh VERSION` instead. The configured bump creates a commit and `v{new_version}` tag. It updates `pyproject.toml`, `setup.py`, and runtime package-version strings in generated source. It intentionally does not update `README.md` or `AGENTS.md`; those generation-contract versions are updated by `scripts/regenerate-sdk.sh`.

## CI And Publishing

- Test workflow: `.github/workflows/python.yml`
- Publish workflow: `.github/workflows/publish.yml`
- Publish workflow is read-only with respect to package version files and fails unless the release tag matches `pyproject.toml`, `setup.py`, and `audithub_sdk/__init__.py`.
- Publish workflow uses GitHub Actions trusted publishing to PyPI via OIDC.
- PyPI publishing expects the GitHub repository to be registered as a trusted publisher for the `audithub-sdk` project.

Expand Down
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ The client code in this repository is generated from the live OpenAPI document:

`https://audithub.dev.veridise.tools/api/v1/openapi.json`

The repository contents are produced with OpenAPI Generator 7.20.0 using:
The repository contents are produced with OpenAPI Generator 7.20.0. The current generated package version is `0.1.0`.

The current generation command is:

```sh
openapi-generator generate \
Expand All @@ -21,16 +23,50 @@ openapi-generator generate \

## Regenerating

When the AuditHub API schema changes, regenerate the SDK from the repository root with the command above.
When the AuditHub API schema changes, regenerate the SDK from the repository root and pass the new SDK version:

```sh
scripts/regenerate-sdk.sh 0.1.1
```

The script passes that version as `packageVersion` to OpenAPI Generator, then syncs repository-maintained version references in:

- `pyproject.toml`
- `setup.py`
- `README.md`
- `AGENTS.md`

The version in this README's generator command is intentionally updated only when regenerating the SDK.

Manual changes in this repo should stay limited to repository-specific files such as:

- `README.md`
- packaging metadata
- CI workflows
- `scripts/regenerate-sdk.sh`

Generated source, models, docs, and tests should be replaced by regeneration rather than edited by hand.

## Version Bumping

`bump-my-version` is configured in `pyproject.toml` for release-only version bumps. For API schema changes, use `scripts/regenerate-sdk.sh VERSION` instead. The bump command updates package metadata and runtime package-version strings, commits the change, and creates a `vX.Y.Z` tag:

```sh
uv tool install bump-my-version
```

```sh
bump-my-version bump patch
```

To set an exact version:

```sh
bump-my-version bump --new-version 0.1.1
```

The bump configuration intentionally does not update this README's generator command. That command is updated by `scripts/regenerate-sdk.sh` when the SDK is regenerated.

## Installation

From PyPI:
Expand Down Expand Up @@ -99,3 +135,16 @@ To build distributions locally:
```sh
python -m build
```

Publishing is handled by `.github/workflows/publish.yml` from a GitHub release tag. The workflow does not edit version files; it fails unless the tag version matches:

- `pyproject.toml`
- `setup.py`
- `audithub_sdk/__init__.py`

Use `vX.Y.Z` tags for releases, for example:

```sh
git tag v0.1.1
git push origin v0.1.1
```
35 changes: 35 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,41 @@ types-python-dateutil = ">= 2.8.19.14"
mypy = ">= 1.5"


[tool.bumpversion]
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
serialize = ["{major}.{minor}.{patch}"]
search = "{current_version}"
replace = "{new_version}"
regex = false
ignore_missing_version = false
tag = true
sign_tags = false
tag_name = "v{new_version}"
tag_message = "Bump version: {current_version} -> {new_version}"
commit = true
message = "Bump version: {current_version} -> {new_version}"

[[tool.bumpversion.files]]
filename = "setup.py"
search = 'VERSION = "{current_version}"'
replace = 'VERSION = "{new_version}"'

[[tool.bumpversion.files]]
filename = "audithub_sdk/__init__.py"
search = '__version__ = "{current_version}"'
replace = '__version__ = "{new_version}"'

[[tool.bumpversion.files]]
filename = "audithub_sdk/api_client.py"
search = "OpenAPI-Generator/{current_version}/python"
replace = "OpenAPI-Generator/{new_version}/python"

[[tool.bumpversion.files]]
filename = "audithub_sdk/configuration.py"
search = "SDK Package Version: {current_version}"
replace = "SDK Package Version: {new_version}"


[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
Expand Down
98 changes: 98 additions & 0 deletions scripts/regenerate-sdk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<'USAGE'
Usage: scripts/regenerate-sdk.sh VERSION

Regenerates the AuditHub Python SDK from the live OpenAPI document using
VERSION as the packageVersion, then syncs repository-maintained version
references.
USAGE
}

if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi

if [[ $# -ne 1 ]]; then
usage >&2
exit 2
fi

VERSION="$1"

if [[ -z "$VERSION" || "$VERSION" == *","* || "$VERSION" =~ [[:space:]] ]]; then
echo "Version must be non-empty and must not contain commas or whitespace." >&2
exit 2
fi

if ! command -v openapi-generator >/dev/null 2>&1; then
echo "openapi-generator is required. Install OpenAPI Generator 7.20.0 before regenerating." >&2
exit 127
fi

PYTHON="${PYTHON:-python3}"
if ! command -v "$PYTHON" >/dev/null 2>&1; then
echo "$PYTHON is required to sync repository metadata." >&2
exit 127
fi

openapi-generator generate \
-i https://audithub.dev.veridise.tools/api/v1/openapi.json \
-g python \
-o . \
--additional-properties="packageName=audithub_sdk,projectName=audithub-sdk,packageVersion=${VERSION},hideGenerationTimestamp=true,library=httpx"

"$PYTHON" - "$VERSION" <<'PY'
from __future__ import annotations

from pathlib import Path
import re
import sys


version = sys.argv[1]


def replace_exact(path: str, pattern: str, replacement, expected: int) -> None:
file_path = Path(path)
text = file_path.read_text(encoding="utf-8")
updated, count = re.subn(pattern, replacement, text, flags=re.MULTILINE)
if count != expected:
raise SystemExit(f"{path}: expected {expected} replacement(s), made {count}")
file_path.write_text(updated, encoding="utf-8")


replace_exact(
"pyproject.toml",
r'^(version = ")[^"]+(")$',
lambda match: f"{match.group(1)}{version}{match.group(2)}",
1,
)
replace_exact(
"setup.py",
r'^(VERSION = ")[^"]+(")$',
lambda match: f"{match.group(1)}{version}{match.group(2)}",
1,
)
replace_exact(
"README.md",
r"packageVersion=[^,]+",
f"packageVersion={version}",
1,
Comment on lines +81 to +84
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Keep README current version in sync on regeneration

The regeneration script says it syncs README.md, but this replacement only updates the packageVersion=... fragment in the generator command. It does not update the README sentence declaring the “current generated package version”, so after running scripts/regenerate-sdk.sh VERSION the file can immediately contain contradictory versions and mislead future releases.

Useful? React with 👍 / 👎.

)
replace_exact(
"AGENTS.md",
r"(Package version currently used during generation: `)[^`]+(`)",
lambda match: f"{match.group(1)}{version}{match.group(2)}",
1,
Comment on lines +87 to +90
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Sync AGENTS preferred command version during regeneration

The AGENTS sync only rewrites the package-version bullet and the packageVersion=... generator argument, but it leaves the “Preferred regeneration command” example unchanged. After the first regen, AGENTS.md will show an outdated scripts/regenerate-sdk.sh <old-version> command alongside updated version metadata, which creates an inconsistent generation contract.

Useful? React with 👍 / 👎.

)
replace_exact(
"AGENTS.md",
r"packageVersion=[^,]+",
f"packageVersion={version}",
1,
)
PY
Loading