Skip to content

Conversation

@ImJustHenry
Copy link

@ImJustHenry ImJustHenry commented Nov 30, 2025

Changes

  • Added a Python script (tools/bump_version.py) to automate version bumps based on PR labels (patch, minor, major, release).
  • Added a GitHub Actions workflow (.github/workflows/version-bump.yml) that triggers on PR labeling.
  • For release labels, the workflow removes the -draft suffix from the version.
  • Non-release bumps retain any existing suffix (e.g., -draft) while incrementing the appropriate version component.
  • Commits and PRs for version bumps are automatically generated by the workflow.

Note: This PR only implements part 1 of issue #218 (Schema Version Updates). Part 2 (automated backports and patch updates via GH bot) has not been addressed.

Related Issue

Partially fixes #218

Submitter Checklist

As the author of this PR, please check off the items in this checklist:

@ImJustHenry ImJustHenry requested a review from a team as a code owner November 30, 2025 01:36
@afrittoli
Copy link
Contributor

Thanks @ImJustHenry for your contribution! A couple of comments:

  • We should not bump versions on each PR, only at release time. We could use manual dispatch when we want to create a release: the workflow creates a new PR. Once the PR is approved and merged, we could have another workflow doing the tagging and release notes,

  • I would follow the rule that if we are making a release on main, the version bump will be either major or minor (although in practice only minor until we release 1.0), depending on the labels of the PRs since the last minor release.

    • If there is at least on PR labelled as major we use major
    • If there is at least one PR labelled as minor we use minor
    • If we try to release only patch PRs on main, we should instead make a patch release on the latest release branch
  • There is already a shell script that can be used to update versions. The script updates schemas and examples and docs as well. Having a Python script is definitely an improvement, but should avoid having two scripts that do the same thing.

  • @xibz proposal in the issue was to switch from -draft versions to using latest - any thoughts on that? @xibz do we still need latest if everything is automated for contributors?

Copy link
Contributor

@xibz xibz left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Overall it looks good, but just had a few minor comments.

return major, minor, patch, suffix


def bump_version(major, minor, patch):
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe instead we could have just 3 new variables, and we should probably throw an exception for unknown LABELs

new_patch = 0
new_minor = 0
new_major = 0

if LABEL == "patch":
    new_patch = patch + 1
elif LABEL == "minor":
    new_minor = minor + 1
elif LABEL == "major":
    new_major = major + 1
else
    raise ExceptionType(f"invalid LABEL: {LABEL}")

return new_major, new_minor, new_patch

Additionally, we may need a suffix or prefix potentially, e.g. "v1.0.0-beta". But I think that can come when we need it. It looks like you started some functionality for suffix, but I dont think it's done.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks you for the feedback!

I understand the suggestion to use three new variables, it does make the bump logic more explicit and clear. I also see your point about throwing an exception for unknown labels. In the current setup, the GitHub workflow only passes predefined labels (patch, minor, major, release), so in practice an exception isn’t strictly necessary.

That said, adding an exception could serve as a useful safeguard if someone were to run the script manually with an invalid label. I’ll update the script to incorporate both of these suggestions.

old_version = read_version()

# Parse current version
major, minor, patch, suffix = parse_version(old_version)
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens to suffix?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the question! The suffix is parsed so that I can correctly separate the numeric patch value from any trailing suffix such as -draft. I designed the logic under the assumption that versions should always include -draft unless the label is release, in which case the suffix is intentionally removed.

So the suffix is extracted purely so the script can reliably parse versions like 0.5.0-draft without errors, and so a release label can strip the suffix. Outside of that case, the script intentionally replaces the suffix with -draft on all non-release bumps.

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"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we use suffix here?

Copy link
Author

Choose a reason for hiding this comment

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

As mentioned, the original assumption was that only finalized releases should omit the -draft suffix. That’s why I intentionally append -draft during version bumps, so all non-release versions are clearly marked.

If the goal is to preserve existing suffixes (potentially for more complex cases in the future like -beta) the logic can certainly be updated to consistently use the parsed suffix instead of forcing -draft. The current implementation mainly ensures that non-release versions are easily distinguishable from official releases.

@xibz
Copy link
Contributor

xibz commented Dec 1, 2025

@afrittoli - The shell script seems a little larger in scope. This doesn't do version file replacement in JSON, for example, but to manage the version specifically. I think we can use the shell script and this in conjunction. So I think we will need to update the shell script as well or move the rest of it to the python script.

@ImJustHenry - Can you take a look at that shell script? I think we may be able to utilize both of the files to do a release, or we can completely replace the shell script with full python, but that requires doing the file version replacement to also be added.

@afrittoli
Copy link
Contributor

The main problem I see with this approach is that it will attempt to create a new PR to update versions every time a PR is labelled. I think we should execute this at release time, not on every PR.
The only thing to be checked on every PR should be that a label is set.

Even if we wanted to update versions every PR, we shouldn't do it with an extra PR, there would be no easy way to coordinate merging the PRs in the right order then.
There are a couple of alternatives:

  • use pre-commit and have the script executed before commit - commit fails if the script was not executed
  • use a workflow to add a commit on top within the same PR

But again, I don't think we should update versions for every single PR. @xibz @davidB thoughts?

@ImJustHenry
Copy link
Author

@xibz - I’ve reviewed the shell script. Please correct me if I miss anything but the shell script basically manages releases by updating all version references across the repository, including JSON schemas, conformance files, and documentation, based on the version specified in version.txt. The approach could be that we can utilize both scripts, the Python script calculates the next version based on the label and updates version.txt, while the shell script reads version.txt and updates all references across the repository. Or alternatively, if you prefer having everything in a single language, I can convert the shell script’s functionality into the Python script. I’m happy to proceed with whichever approach is preferred.

@afrittoli - Thank you for the feedback! I actually modeled the workflow after what I observed with Dependabot, which creates a new PR every time it bumps a dependency version. My assumption was that each merged PR into main would correspond to either a minor, major, or patch change, and thought that the repository should have a PR for every version commit for consistency. I understand your concern about creating a PR on every labeled PR and the potential coordination issues. To address this, the approach you mentioned of using manual dispatch to create releases makes sense. This way it does ensure that the version bumps only happen intentionally, instead of triggering on every PR.

I can start updating the code and incorporate all the feedback if there are no other issues that haven’t been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improvements on automated release

3 participants