From e20b4f5235475f3afb129903fab42aae9a1ec765 Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Sat, 29 Nov 2025 19:21:24 -0600 Subject: [PATCH 1/7] Added Python script for Schema Version Updates Signed-off-by: ImJustHenry --- tools/bump_version.py | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tools/bump_version.py diff --git a/tools/bump_version.py b/tools/bump_version.py new file mode 100644 index 0000000..3196950 --- /dev/null +++ b/tools/bump_version.py @@ -0,0 +1,81 @@ +import sys + +# The GitHub label passed as an argument: patch, minor, major, release +LABEL = sys.argv[1] + +# The file that stores the current version +VERSION_FILE = "version.txt" + +def read_version(): + """Read the current version from version.txt""" + with open(VERSION_FILE, "r") as f: + return f.read().strip() + +def write_version(version): + """Write the new version back to version.txt""" + with open(VERSION_FILE, "w") as f: + f.write(version + "\n") + +def parse_version(v): + """ + Parse a version string like "0.5.0-draft" into parts: + major, minor, patch, and optional suffix (e.g., "-draft"). + """ + parts = v.split(".") # Split into ['0', '5', '0-draft'] + major = int(parts[0]) + minor = int(parts[1]) + + # Handle patch number and suffix(if exist) + if "-" in parts[2]: + patch_str, suffix = parts[2].split("-", 1) + patch = int(patch_str) + suffix = "-" + suffix # Keep the dash in the suffix + else: + patch = int(parts[2]) + suffix = "" + + return major, minor, patch, suffix + + +def bump_version(major, minor, patch): + """ + Increment the version based on LABEL: + - patch: increment patch + - minor: increment minor, reset patch + - major: increment major, reset minor and patch + """ + if LABEL == "patch": + patch = patch + 1 + elif LABEL == "minor": + minor = minor + 1 + patch = 0 + elif LABEL == "major": + major = major + 1 + minor = 0 + patch = 0 + return major, minor, patch + +def main(): + # Read current version + old_version = read_version() + + # Parse current version + major, minor, patch, suffix = parse_version(old_version) + + # If label is release, remove the "-draft" suffix + if LABEL == "release": + new_version = f"{major}.{minor}.{patch}" + else: + # Otherwise, bump version according to label + new_major, new_minor, new_patch = bump_version(major, minor, patch) + new_version = f"{new_major}.{new_minor}.{new_patch}-draft" + + # Prevent race conditions by only writing if version changed + if new_version != old_version: + write_version(new_version) + print(f"Version updated: {old_version} -> {new_version}") + else: + print(f"No version change needed. Current version: {old_version}") + +if __name__ == "__main__": + main() From b76ccbedaba3c0912aef2e3c8aaad8cf94112e56 Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Sat, 29 Nov 2025 19:23:53 -0600 Subject: [PATCH 2/7] Add GitHub Actions workflow for version bump Signed-off-by: ImJustHenry --- .github/workflows/version-bump.yml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/version-bump.yml diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml new file mode 100644 index 0000000..6d275c3 --- /dev/null +++ b/.github/workflows/version-bump.yml @@ -0,0 +1,41 @@ +name: Auto Version Bump + +on: + pull_request: + types: [labeled] + +jobs: + bump-version: + if: > + github.event.label.name == 'patch' || + github.event.label.name == 'minor' || + github.event.label.name == 'major' || + github.event.label.name == 'release' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Run version bump script + run: | + python tools/bump_version.py ${{ github.event.label.name }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + commit-message: "chore: bump version (${{ github.event.label.name }})" + title: "chore: bump version (${{ github.event.label.name }})" + body: | + This version bump was triggered by PR #${{ github.event.pull_request.number }} + Label applied: **${{ github.event.label.name }}** + labels: version-bump + base: main + branch: auto-version-bump/${{ github.event.pull_request.number }} From df23da297a0e3430f8f66429ac76db86770b6139 Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Fri, 19 Dec 2025 05:35:47 -0600 Subject: [PATCH 3/7] Added spec_release.py with ci and tests with ci Signed-off-by: ImJustHenry --- .github/workflows/release.yml | 56 ++++++++++++ .github/workflows/release_tests.yml | 28 ++++++ .github/workflows/version-bump.yml | 41 --------- tests/test_release.py | 41 +++++++++ tools/__init__.py | 0 tools/bump_version.py | 81 ------------------ tools/spec_release.py | 128 ++++++++++++++++++++++++++++ 7 files changed, 253 insertions(+), 122 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/release_tests.yml delete mode 100644 .github/workflows/version-bump.yml create mode 100644 tests/test_release.py create mode 100644 tools/__init__.py delete mode 100644 tools/bump_version.py create mode 100644 tools/spec_release.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d64576e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release + +on: + workflow_dispatch: + inputs: + action: + description: "Release action to perform" + required: true + type: choice + options: + - major + - minor + - patch + - set-version + version: + description: "Version to set (required only for set-version)" + required: false + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: pip install semver + + - name: Run release script + run: | + if [ "${{ inputs.action }}" = "set-version" ]; then + if [ -z "${{ inputs.version }}" ]; then + echo "Version is required for set-version" + exit 1 + fi + python tools/release.py set=${{ inputs.version }} + else + python tools/release.py ${{ inputs.action }} + + - name: Commit version updates + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git add . + git commit -m "chore(release): bump version (${{ inputs.action }})" || echo "No changes to commit" + + - name: Push changes + run: git push diff --git a/.github/workflows/release_tests.yml b/.github/workflows/release_tests.yml new file mode 100644 index 0000000..d798928 --- /dev/null +++ b/.github/workflows/release_tests.yml @@ -0,0 +1,28 @@ +name: Python Tests + +on: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest semver + + - name: Run tests + run: | + pytest -v tests/ diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml deleted file mode 100644 index 6d275c3..0000000 --- a/.github/workflows/version-bump.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Auto Version Bump - -on: - pull_request: - types: [labeled] - -jobs: - bump-version: - if: > - github.event.label.name == 'patch' || - github.event.label.name == 'minor' || - github.event.label.name == 'major' || - github.event.label.name == 'release' - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Run version bump script - run: | - python tools/bump_version.py ${{ github.event.label.name }} - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - commit-message: "chore: bump version (${{ github.event.label.name }})" - title: "chore: bump version (${{ github.event.label.name }})" - body: | - This version bump was triggered by PR #${{ github.event.pull_request.number }} - Label applied: **${{ github.event.label.name }}** - labels: version-bump - base: main - branch: auto-version-bump/${{ github.event.pull_request.number }} diff --git a/tests/test_release.py b/tests/test_release.py new file mode 100644 index 0000000..7b062ed --- /dev/null +++ b/tests/test_release.py @@ -0,0 +1,41 @@ +import pytest +from semver import VersionInfo +from tools.spec_release import compute_next_version + +# start release + +def test_start_minor_release_from_stable(): + v = VersionInfo.parse("1.2.3") + new = compute_next_version(v, "minor") + assert new.major == 1 + assert new.minor == 3 # bump minor + assert new.patch == 0 + +def test_start_major_release_from_stable(): + v = VersionInfo.parse("1.2.3") + new = compute_next_version(v, "major") + assert new.major == 2 # bump major + assert new.minor == 0 + assert new.patch == 0 + +def test_start_release_invalid_action(): + v = VersionInfo.parse("1.2.3") + with pytest.raises(SystemExit): + compute_next_version(v, "invalid-action") + +# patch release + +def test_patch_release_from_stable(): + v = VersionInfo.parse("1.2.3") + new = compute_next_version(v, "patch") + assert str(new) == "1.2.4" + +# set version + +def test_set_version_valid(): + v = compute_next_version(VersionInfo.parse("0.0.0"), "set", "2.0.1") + assert str(v) == "2.0.1" + +def test_set_version_invalid(): + with pytest.raises(SystemExit): + compute_next_version(VersionInfo.parse("0.0.0"), "set", "not-a-version") diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/bump_version.py b/tools/bump_version.py deleted file mode 100644 index 3196950..0000000 --- a/tools/bump_version.py +++ /dev/null @@ -1,81 +0,0 @@ -import sys - -# The GitHub label passed as an argument: patch, minor, major, release -LABEL = sys.argv[1] - -# The file that stores the current version -VERSION_FILE = "version.txt" - -def read_version(): - """Read the current version from version.txt""" - with open(VERSION_FILE, "r") as f: - return f.read().strip() - -def write_version(version): - """Write the new version back to version.txt""" - with open(VERSION_FILE, "w") as f: - f.write(version + "\n") - -def parse_version(v): - """ - Parse a version string like "0.5.0-draft" into parts: - major, minor, patch, and optional suffix (e.g., "-draft"). - """ - parts = v.split(".") # Split into ['0', '5', '0-draft'] - major = int(parts[0]) - minor = int(parts[1]) - - # Handle patch number and suffix(if exist) - if "-" in parts[2]: - patch_str, suffix = parts[2].split("-", 1) - patch = int(patch_str) - suffix = "-" + suffix # Keep the dash in the suffix - else: - patch = int(parts[2]) - suffix = "" - - return major, minor, patch, suffix - - -def bump_version(major, minor, patch): - """ - Increment the version based on LABEL: - - patch: increment patch - - minor: increment minor, reset patch - - major: increment major, reset minor and patch - """ - if LABEL == "patch": - patch = patch + 1 - elif LABEL == "minor": - minor = minor + 1 - patch = 0 - elif LABEL == "major": - major = major + 1 - minor = 0 - patch = 0 - return major, minor, patch - -def main(): - # Read current version - old_version = read_version() - - # Parse current version - major, minor, patch, suffix = parse_version(old_version) - - # If label is release, remove the "-draft" suffix - if LABEL == "release": - new_version = f"{major}.{minor}.{patch}" - else: - # Otherwise, bump version according to label - new_major, new_minor, new_patch = bump_version(major, minor, patch) - new_version = f"{new_major}.{new_minor}.{new_patch}-draft" - - # Prevent race conditions by only writing if version changed - if new_version != old_version: - write_version(new_version) - print(f"Version updated: {old_version} -> {new_version}") - else: - print(f"No version change needed. Current version: {old_version}") - -if __name__ == "__main__": - main() diff --git a/tools/spec_release.py b/tools/spec_release.py new file mode 100644 index 0000000..df68ec7 --- /dev/null +++ b/tools/spec_release.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +import sys +from pathlib import Path +from semver import VersionInfo + +VERSION_FILE = Path("version.txt") + +SCHEMA_DIRS = ["schemas", "custom"] +CONFORMANCE_DIRS = ["conformance", "custom"] +DOC_FILES = ["cloudevents-binding.md", "spec.md", "links.md"] +README_FILE = "README.md" + +# Utilities + +def read_version() -> VersionInfo: + if not VERSION_FILE.exists(): + sys.exit(f"{VERSION_FILE} not found") + try: + return VersionInfo.parse(VERSION_FILE.read_text().strip()) + except ValueError as e: + sys.exit(f"Invalid version: {e}") + +def write_version(version: VersionInfo): + VERSION_FILE.write_text(f"{version}\n") + +def replace_all(files, old: str, new: str): + for f in files: + p = Path(f) + if p.exists(): + p.write_text(p.read_text().replace(old, new)) + +def find_files(dirs, suffix=".json"): + return [ + f for d in dirs if Path(d).exists() + for f in Path(d).rglob(f"*{suffix}") + ] + +# Version transitions + +def compute_next_version(old: VersionInfo, action: str, value=None) -> VersionInfo: + """ + Compute next version based on the action: + - major: bump major, reset minor & patch + - minor: bump minor, reset patch + - patch: bump patch + - set: set to exact version + """ + if action == "set": + try: + return VersionInfo.parse(value) + except ValueError as e: + sys.exit(f"Invalid version: {e}") + + if action == "major": + return old.bump_major() + if action == "minor": + return old.bump_minor() + if action == "patch": + return old.bump_patch() + + sys.exit(f"Unknown action: {action}") + +# Repository updates + +def update_repository(old: VersionInfo, new: VersionInfo): + old_v, new_v = str(old), str(new) + + # Update schema references + replace_all( + find_files(SCHEMA_DIRS), + f"https://cdevents.dev/{old_v}/schema/", + f"https://cdevents.dev/{new_v}/schema/", + ) + + # Update conformance files + replace_all( + find_files(CONFORMANCE_DIRS), + f'"version": "{old_v}"', + f'"version": "{new_v}"', + ) + + # Update documentation files + replace_all( + DOC_FILES, + f'"version": "{old_v}"', + f'"version": "{new_v}"', + ) + + # Update README + replace_all([README_FILE], f"v{old_v}", f"v{new_v}") + +# CLI + +def parse_action(): + """ + Get release action from command-line arguments. + Usage: + python release.py major + python release.py minor + python release.py patch + python release.py set=X.Y.Z + """ + if len(sys.argv) < 2: + sys.exit("Usage: release.py ") + + arg = sys.argv[1].lower() + + if arg in ("major", "minor", "patch"): + return arg, None + + if arg.startswith("set="): + return "set", arg.split("=", 1)[1] + + sys.exit(f"Unknown action: {arg}") + +# Main + +def main(): + action, value = parse_action() + old = read_version() + new = compute_next_version(old, action, value) + update_repository(old, new) + write_version(new) + print(f"Version updated: {old} -> {new}") + +if __name__ == "__main__": + main() From afa2878c67229a802ceab6f0503f28212bee030d Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Fri, 19 Dec 2025 05:42:41 -0600 Subject: [PATCH 4/7] change name for better clarity Signed-off-by: ImJustHenry --- .github/workflows/release_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_tests.yml b/.github/workflows/release_tests.yml index d798928..b92de39 100644 --- a/.github/workflows/release_tests.yml +++ b/.github/workflows/release_tests.yml @@ -1,4 +1,4 @@ -name: Python Tests +name: Test Python Release on: workflow_dispatch: From bddd540c4ca5a75e2298b1c0e4c015082beedb4e Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Fri, 19 Dec 2025 05:45:02 -0600 Subject: [PATCH 5/7] fix python test path Signed-off-by: ImJustHenry --- .github/workflows/release_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_tests.yml b/.github/workflows/release_tests.yml index b92de39..1526f50 100644 --- a/.github/workflows/release_tests.yml +++ b/.github/workflows/release_tests.yml @@ -25,4 +25,4 @@ jobs: - name: Run tests run: | - pytest -v tests/ + PYTHONPATH=$PYTHONPATH:. pytest -v tests/ From ac72d63e336c741d6835e9bb9d624b32a000b1f9 Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Fri, 19 Dec 2025 05:48:29 -0600 Subject: [PATCH 6/7] fix release.yml missing fi Signed-off-by: ImJustHenry --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d64576e..7154770 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,7 @@ jobs: python tools/release.py set=${{ inputs.version }} else python tools/release.py ${{ inputs.action }} + fi - name: Commit version updates run: | From cbe17db43e3871c364166ad6d99e17dffa804f39 Mon Sep 17 00:00:00 2001 From: ImJustHenry Date: Fri, 19 Dec 2025 05:54:55 -0600 Subject: [PATCH 7/7] fix release script Signed-off-by: ImJustHenry --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7154770..0a6ba4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,9 +41,9 @@ jobs: echo "Version is required for set-version" exit 1 fi - python tools/release.py set=${{ inputs.version }} + python tools/spec_release.py set=${{ inputs.version }} else - python tools/release.py ${{ inputs.action }} + python tools/spec_release.py ${{ inputs.action }} fi - name: Commit version updates