Skip to content

[hack] Add create-release-tag.py script#3913

Draft
mtnbikenc wants to merge 2 commits intoopenshift:masterfrom
mtnbikenc:feature/create-release-tag
Draft

[hack] Add create-release-tag.py script#3913
mtnbikenc wants to merge 2 commits intoopenshift:masterfrom
mtnbikenc:feature/create-release-tag

Conversation

@mtnbikenc
Copy link
Copy Markdown
Member

@mtnbikenc mtnbikenc commented Mar 30, 2026

Summary

  • Adds `hack/create-release-tag.py` to create consistent annotated release tags for WMCO
  • Resolves the commit SHA from the bundle image in the Red Hat Container Catalog,
    preferring the `org.opencontainers.image.revision` OCI label (full SHA) and falling
    back to the short-SHA image tag
  • Sets the tag date to the bundle image's `push_date` so the tag timestamp reflects
    the actual release date rather than when the tag was created, consistent with the
    commit source
  • Validates `--commit` input against a hex SHA pattern at parse time to prevent git
    option injection
  • Prompts for confirmation before creating the tag, showing the resolved commit, date,
    and their sources
  • Documents the team rationale (why) behind each process decision in the script
    header: why the bundle image commit is tagged, why the OCI label is preferred over
    the short SHA tag, why annotated tags, and why the push command is shown rather than
    executed
  • Includes a PREREQUISITES section covering runtime requirements, package dependencies,
    environment expectations, and permissions

Usage

python3 hack/create-release-tag.py <version> [--commit SHA] [--date YYYY-MM-DD]

`--commit` is required for releases not present in the bundle catalog (e.g. backport-only
releases). `--date` allows overriding the publish date when needed.

Test plan

  • Run `python3 hack/create-release-tag.py --help` and verify usage output
  • Run against an existing version (e.g. `10.21.1`) and verify it exits early with "tag already exists"
  • Run against a new version and verify the catalog-resolved commit and date match expectations
  • Run with `--commit` and `--date` overrides and verify the confirmation prompt reflects them
  • Verify the created tag is annotated (`git cat-file -t vX.Y.Z` returns `tag`) with correct message and date

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 30, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Excluded labels (none allowed) (2)
  • do-not-merge/work-in-progress
  • do-not-merge/hold

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 801d77b3-696a-40dc-a566-6305329fb7a7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 30, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@jrvaldes
Copy link
Copy Markdown
Contributor

@mtnbikenc thanks for working on this.

there is a presedence to maintain the hack scripts in bash, consider implementing the script in bash to avoid introducing another dependency. i.e. python

also, the script requires special permissions push the tag to the repository, please capture that requirement in the documentation/usage

@mtnbikenc
Copy link
Copy Markdown
Member Author

@jrvaldes Thanks for the feedback. I started out with a bash script but it became lengthy and cumbersome for the task at hand. I switched from bash to python for improved error handling and reducing the overall length of the script. I understand the desire for consistency for scripts, but I also balance that with the right tool for the job. I'd like feedback from the rest of the team if using python would be too burdensome or undesired.

I'll add a note about the repo permissions required for pushing tags.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hack/create-release-tag.py`:
- Around line 224-230: The tag_exists function currently calls git("rev-parse",
tag) which resolves any ref (branch or tag); change it to verify the tag ref
explicitly by invoking git with "refs/tags/{tag}" (e.g., git("rev-parse",
f"refs/tags/{tag}") or equivalent) so only actual tags cause a True return, and
keep the existing try/except RuntimeError behavior to return False when the
refs/tags/... lookup fails; update the reference in the tag_exists function
accordingly.
- Around line 297-298: The current check only uses re.fullmatch on args.date and
allows invalid calendar dates (e.g., 2026-02-31); update the validation to
attempt parsing the ISO date string (use datetime.date.fromisoformat or
datetime.strptime) inside a try/except and if parsing raises ValueError call
parser.error(f"--date must be YYYY-MM-DD, got: {args.date!r}") so invalid
calendar dates are rejected at argument parsing; replace or augment the existing
re.fullmatch branch that references args.date and parser.error to perform the
parse-and-catch logic instead.
- Around line 320-346: fetch_bundle_info() and fetch_operator_push_date() call
_fetch_pages() and may raise network/JSON exceptions; wrap both calls in
try/except and surface a clean ERROR + sys.exit(1) like the git-error handling
already used: when calling fetch_bundle_info(version) and
fetch_operator_push_date(version) catch Exception as err, print a descriptive
ERROR to stderr that includes the function name and err (same style as the git
failure prints), then sys.exit(1); keep setting commit_sha/commit_source and
published_date/date_source only on success so behavior matches the manual
--commit/--date branches.
- Around line 214-221: The git() helper currently allows FileNotFoundError from
subprocess.run to leak; modify git() to resolve the git executable first (e.g.,
use shutil.which("git")) and if not found raise a RuntimeError with a clear
message, or wrap the subprocess.run call in a try/except that catches
FileNotFoundError and re-raises a RuntimeError with the original error text;
ensure the change is applied in the git(...) function so all callers continue to
receive RuntimeError instead of an unhandled FileNotFoundError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5eca9ed4-d5b5-4540-82fc-b372ad2e25fd

📥 Commits

Reviewing files that changed from the base of the PR and between c676971 and 192dbea.

📒 Files selected for processing (1)
  • hack/create-release-tag.py

Comment thread hack/create-release-tag.py
Comment thread hack/create-release-tag.py
Comment thread hack/create-release-tag.py Outdated
Comment thread hack/create-release-tag.py Outdated
@mtnbikenc mtnbikenc force-pushed the feature/create-release-tag branch from fc5463b to 1f0d9b8 Compare April 11, 2026 23:29
Adds a script to create consistent annotated release tags for WMCO.

The commit SHA is resolved from the bundle image in the Red Hat Container
Catalog, preferring the org.opencontainers.image.revision OCI label (full
SHA) and falling back to the short-SHA image tag — matching the approach
used by hack/verify-release.py. The tag date is set to the operator
image's push_date so the timestamp reflects the actual release date.

A --commit override is available for releases not present in the catalog
(e.g. backport-only releases), and --date allows overriding the publish
date. The script prompts for confirmation before creating the tag and
prints the push command targeting the upstream repository.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mtnbikenc mtnbikenc force-pushed the feature/create-release-tag branch from 1f0d9b8 to 0f16e25 Compare April 11, 2026 23:36
Copy link
Copy Markdown
Contributor

@jrvaldes jrvaldes left a comment

Choose a reason for hiding this comment

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

@mtnbikenc thanks for working on this, LGTM. just minor suggestions, PTAL

def resolve_commit(ref: str) -> str:
"""Return the full commit SHA for ref, or raise RuntimeError."""
try:
return git("rev-parse", f"{ref}^{{commit}}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

consider validating the --commit input; the user-supplied SHA is passed to git rev-parse without format checking, enabling argument injection.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in commit ebb6fdb: added a _HEX_RE (^[0-9a-f]{7,40}$) check against the --commit value at argument-parse time via parser.error(). Any value that isn't a lowercase hex string of 7–40 characters is rejected before reaching git rev-parse, preventing git option injection (e.g. --upload-pack=evil-cmd or relative refs like HEAD~1). Note: since subprocess.run() uses a list rather than shell=True, shell metacharacter injection was never possible — the real risk was git option injection, which _HEX_RE now covers.



# pylint: disable=too-many-locals,too-many-branches,too-many-statements
def main():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this func is big and complex, consider generalizing orthogonal logic using helper functions, for example

resolve_commit_sha() and resolve_date()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in commit ebb6fdb: extracted resolve_commit_sha(tag, version, override) and resolve_date(tag, version, override) from main(). Each helper encapsulates the catalog lookup, exception handling, and missing-value error messaging for its respective concern. The too-many-branches pylint suppression on main() was also removed as it is no longer needed.

Comment thread hack/create-release-tag.py Outdated


# ---------------------------------------------------------------------------
# Catalog helpers (shared pattern with verify-release.py)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

still have mixed feelings about introducing helper tools in Python; anyhow, consider extracting a shared module under hack/lib/ to avoid duplicating this implementation

Copy link
Copy Markdown
Member Author

@mtnbikenc mtnbikenc Apr 16, 2026

Choose a reason for hiding this comment

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

Agreed on the value. Deferring the hack/lib/ extraction until team direction on future hack script organization is decided.

@mtnbikenc
Copy link
Copy Markdown
Member Author

Thanks for the feedback and suggestions, all good points. I'll look into these and come back with some updates.

@mtnbikenc
Copy link
Copy Markdown
Member Author

I didn't squash commits to allow easier review of the changes. If the team decides to move forward with using this script, and other python scripts, I can add python linting tests to this PR since it is the first.

@mtnbikenc mtnbikenc force-pushed the feature/create-release-tag branch from ebb6fdb to 7cdb3c7 Compare April 17, 2026 12:57
Copy link
Copy Markdown
Member

@mansikulkarni96 mansikulkarni96 left a comment

Choose a reason for hiding this comment

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

I have used this script and it works for me. I am okay with having this in python as this is easy to maintain and right for the job. I agree with adding a linter like pylint to help further updates to this.


# pylint: disable=too-many-locals,too-many-statements
def main():
"""Parse args, resolve tag details, confirm with user, create the tag."""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like that the script asks for confirmation to avoid accidental tagging.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 17, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mansikulkarni96, mtnbikenc

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 17, 2026
- Validate --commit flag against _HEX_RE at parse time to prevent git
  option injection (e.g. --upload-pack=evil-cmd or HEAD~1 references)
- Extract resolve_commit_sha() and resolve_date() helpers from main()
  to reduce branch count and remove too-many-branches pylint suppress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mtnbikenc mtnbikenc force-pushed the feature/create-release-tag branch from 7cdb3c7 to 7950a38 Compare April 17, 2026 13:50
@mtnbikenc
Copy link
Copy Markdown
Member Author

As part of this PR I reviewed the script against what I believe should be a baseline standard for hack scripts. Noting it here for team visibility and feedback.

Hack script checklist

  • Prerequisites / requirements — Python/Go/tool version, third-party packages, any required binaries (e.g. git in PATH)
  • Usage instructions — all supported invocation forms with argument descriptions
  • Permissions and access tokens — what credentials or access rights are needed, and whether external APIs require authentication
  • Data sources — every external system the script reads from, what it provides, and the API or mechanism used
  • Logic documentation — for each major step: Source, Data, Why (the team decision or rationale), and Logic (how it works)
  • Lint clean — passes the linter appropriate for the language (pylint/pyflakes for Python, golangci-lint for Go)

The Why is the most important piece — these scripts codify team process decisions, and without the reasoning future maintainers have no basis for knowing whether a detail is intentional or accidental.

Open to guidance from the team if there are other items that should be on this list.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 17, 2026

@mtnbikenc: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants