Skip to content

Adds release script to do the minor or patch release - #301

Open
PuneetPunamiya wants to merge 1 commit into
redhat-data-and-ai:mainfrom
PuneetPunamiya:release-script
Open

Adds release script to do the minor or patch release#301
PuneetPunamiya wants to merge 1 commit into
redhat-data-and-ai:mainfrom
PuneetPunamiya:release-script

Conversation

@PuneetPunamiya

@PuneetPunamiya PuneetPunamiya commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Adds release script to do the minor or patch release

Summary by CodeRabbit

  • New Features
    • Added automated GitHub Draft Releases for both minor and patch versions.
    • Introduced a release automation script with dry-run output, version overrides, and force behavior, plus strict safety checks for tags, branches, and commit ancestry.
  • CI/CD
    • Added a Release workflow that triggers on v* tags, validates vMAJOR.MINOR.PATCH, computes minor vs patch, and runs the automation accordingly.
    • Workflow publishes a Draft Release summary with controller/MCP image tags (and includes a release branch for minor releases).

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Bash tool for minor and patch releases plus a tag-triggered GitHub Actions workflow that validates tags, invokes the tool, and reports draft release details and image tags.

Changes

Release automation

Layer / File(s) Summary
CLI, repository checks, and version helpers
hack/release.sh
Defines release commands and flags, validates GitHub and upstream remote access, fetches tags, and computes tag-based versions.
Minor release workflow
hack/release.sh
Validates minor release state, creates and pushes the release branch when needed, and creates a GitHub draft release.
Patch release workflow
hack/release.sh
Validates the release branch and patch history, then creates a GitHub draft release for the validated patch tag.
Tag-triggered workflow and release report
.github/workflows/release.yml
Validates pushed version tags, runs the appropriate minor or patch command, and writes release links and image tags to the workflow summary.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHub
  participant ReleaseWorkflow
  participant ReleaseScript
  participant Git
  participant GitHubCLI

  GitHub->>ReleaseWorkflow: deliver version tag push
  ReleaseWorkflow->>ReleaseScript: invoke minor or patch release
  ReleaseScript->>Git: inspect tags, branches, and commits
  ReleaseScript->>GitHubCLI: create draft release
  ReleaseWorkflow->>GitHub: publish release summary and image tags
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a release script for minor and patch release flows.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
hack/release.sh (1)

352-359: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an array for the optional flag. $notes_flag relies on unquoted word splitting, which also breaks the run_cmd fix above once eval is removed.

♻️ Proposed refactor
-        local notes_flag=""
+        local notes_args=()
         if [[ -n "$prev_tag" ]]; then
-            notes_flag="--notes-start-tag ${prev_tag}"
+            notes_args=(--notes-start-tag "$prev_tag")
         fi
         run_cmd gh release create "$next_version" \
             --repo "$REPO" \
             --generate-notes \
-            $notes_flag
+            "${notes_args[@]}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 352 - 359, Update the release creation flow
around notes_flag to use an array for the optional --notes-start-tag argument,
append its flag and value only when prev_tag is set, and pass the array safely
to run_cmd without unquoted expansion. Preserve --generate-notes and the
existing behavior when no previous tag exists.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hack/release.sh`:
- Around line 220-227: Validate VERSION_OVERRIDE before deriving next_minor_num
in the version-override flow. Require the value to match the expected v0.<minor>
format, and exit with a clear validation error for invalid versions; only then
compute release_branch, prev_minor_num, prev_release_branch, and prev_tag.
- Around line 512-521: Update the patch argument validation in the patch branch
of the release command so a missing value or a value beginning with “-” is
rejected before assigning PATCH_MINOR and shifting arguments. Preserve the
existing usage error and prevent options such as --dry-run from being consumed
as the minor version.
- Around line 27-34: Update run_cmd to invoke the received arguments directly
with "$@" instead of re-parsing them through eval. Preserve the existing dry-run
branch and logging behavior while ensuring arguments containing spaces, quotes,
or shell metacharacters remain data rather than executable shell code.
- Around line 156-175: Bound the polling loop around gh run list with an attempt
count or consecutive-error limit, treating unknown results as failures and
exiting once the limit is reached. Ensure the exhaustion path terminates the
script through the existing manual-release instructions, while preserving normal
polling until status becomes completed.
- Around line 91-111: Update get_latest_patch_for_minor and get_max_patch_number
to avoid grep -P by using POSIX-compatible parsing, or add an early GNU grep
dependency check that fails clearly before these helpers run. Preserve the
existing version filtering and sorting behavior.

---

Nitpick comments:
In `@hack/release.sh`:
- Around line 352-359: Update the release creation flow around notes_flag to use
an array for the optional --notes-start-tag argument, append its flag and value
only when prev_tag is set, and pass the array safely to run_cmd without unquoted
expansion. Preserve --generate-notes and the existing behavior when no previous
tag exists.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53708c5a-b169-41b8-916d-8bfbce0e9057

📥 Commits

Reviewing files that changed from the base of the PR and between abd4ce9 and 38e36dd.

📒 Files selected for processing (1)
  • hack/release.sh

Comment thread hack/release.sh
Comment on lines +27 to +34
run_cmd() {
if $DRY_RUN; then
dry_run "$*"
else
info "Running: $*"
eval "$@"
fi
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Drop eval in run_cmd. eval "$@" re-parses already-split arguments, so any value containing spaces, quotes, or shell metacharacters (e.g. --version 'v0.1.0 && …', which is never validated) is executed as code. Invoking "$@" directly preserves quoting and removes the injection path.

🔒 Proposed fix
 run_cmd() {
     if $DRY_RUN; then
         dry_run "$*"
     else
-        info "Running: $*"
-        eval "$@"
+        info "Running: $*"
+        "$@"
     fi
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run_cmd() {
if $DRY_RUN; then
dry_run "$*"
else
info "Running: $*"
eval "$@"
fi
}
run_cmd() {
if $DRY_RUN; then
dry_run "$*"
else
info "Running: $*"
"$@"
fi
}
🧰 Tools
🪛 ast-grep (0.44.1)

[error] 31-31: eval is invoked on a variable, parameter expansion, or command-substitution result, which re-parses the value as shell code. If any part of that value is attacker-controlled (arguments, environment, file contents, network output), it allows arbitrary command execution. Do not eval dynamic data: invoke the command directly with proper quoting (e.g. "$cmd" "$arg"), use arrays for argument lists (cmd=(prog --flag "$value"); "${cmd[@]}"), or restrict input to a validated allowlist before running it.
Context: eval "$@"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(eval-on-variable-bash)

🪛 Shellcheck (0.11.0)

[warning] 32-32: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 27 - 34, Update run_cmd to invoke the received
arguments directly with "$@" instead of re-parsing them through eval. Preserve
the existing dry-run branch and logging behavior while ensuring arguments
containing spaces, quotes, or shell metacharacters remain data rather than
executable shell code.

Source: Linters/SAST tools

Comment thread hack/release.sh
Comment on lines +91 to +111
get_all_tags() {
git tag -l 'v*' | sort -V
}

get_latest_minor() {
get_all_tags | grep -oP 'v\K[0-9]+\.[0-9]+' | sort -t. -k1,1n -k2,2n | tail -1
}

get_latest_patch_for_minor() {
local minor="$1"
get_all_tags | grep -P "^v${minor}\.[0-9]+$" | sort -V | tail -1
}

get_max_patch_number() {
local minor="$1"
get_all_tags | grep -P "^v${minor}\.[0-9]+$" | grep -oP '\.[0-9]+$' | tr -d '.' | sort -n | tail -1
}

get_latest_tag() {
get_all_tags | tail -1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the repo documents macOS/GNU tooling requirements for hack scripts
fd -t f -e md . -x rg -n --heading 'gnu-|coreutils|grep -P|GNU grep' {} \; | head -50
rg -nP 'grep -[oP]{1,2}P?' --glob 'hack/**' -C1

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the release script around the cited lines
if [ -f hack/release.sh ]; then
  wc -l hack/release.sh
  sed -n '1,160p' hack/release.sh | cat -n
fi

# Search for release tooling docs or preflight checks mentioning GNU grep/coreutils/macOS
rg -n "release\.sh|GNU grep|coreutils|grep -P|macOS|BSD grep|brew install|greadlink|gsed|ggrep" -S . --glob '!**/.git/**'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 12374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the release flow around the version helpers' callers
sed -n '360,450p' hack/release.sh | cat -n

# Find docs or scripts that mention running the release helper locally
rg -n "hack/release\.sh|release helper|release script|minor release|patch release|v0\.[0-9]+\.[0-9]+" -S . --glob '!**/.git/**' --glob '!vendor/**'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 30435


Replace grep -P with portable parsing or add a GNU grep check
These helpers fail on BSD grep (macOS), so release detection breaks outside GNU environments. Either switch to POSIX-compatible parsing or fail fast with a grep dependency check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 91 - 111, Update get_latest_patch_for_minor and
get_max_patch_number to avoid grep -P by using POSIX-compatible parsing, or add
an early GNU grep dependency check that fails clearly before these helpers run.
Preserve the existing version filtering and sorting behavior.

Comment thread hack/release.sh Outdated
Comment on lines +156 to +175
while true; do
local run_data
run_data=$(gh run list \
--repo "$REPO" \
--workflow "test-e2e.yml" \
--branch "$version" \
--limit 1 \
--json status,conclusion \
--jq '.[0] | "\(.status) \(.conclusion)"' 2>/dev/null || echo "unknown unknown")

status=$(echo "$run_data" | awk '{print $1}')
conclusion=$(echo "$run_data" | awk '{print $2}')

if [[ "$status" == "completed" ]]; then
break
fi

echo -ne "\r ⏳ CI status: ${status}... (polling every 30s, Ctrl+C to stop)"
sleep 30
done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Polling loop can spin forever. When gh run list fails (auth expiry, rate limit, network), the fallback is "unknown unknown", so status never becomes completed and the loop polls indefinitely with no timeout. Add a bounded attempt count / consecutive-error limit so the script exits with the manual-release instructions instead of hanging.

⏱️ Proposed fix
             local status=""
             local conclusion=""
+            local attempts=0
+            local max_attempts=120   # ~60 minutes at 30s
             while true; do
                 local run_data
                 run_data=$(gh run list \
@@
                 status=$(echo "$run_data" | awk '{print $1}')
                 conclusion=$(echo "$run_data" | awk '{print $2}')
 
                 if [[ "$status" == "completed" ]]; then
                     break
                 fi
 
+                if (( ++attempts >= max_attempts )); then
+                    echo ""
+                    warn "Timed out waiting for CI. Tag ${version} is pushed; create the release manually:"
+                    echo "  gh release create ${version} --repo ${REPO} --generate-notes"
+                    exit 1
+                fi
+
                 echo -ne "\r  ⏳ CI status: ${status}... (polling every 30s, Ctrl+C to stop)"
                 sleep 30
             done
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while true; do
local run_data
run_data=$(gh run list \
--repo "$REPO" \
--workflow "test-e2e.yml" \
--branch "$version" \
--limit 1 \
--json status,conclusion \
--jq '.[0] | "\(.status) \(.conclusion)"' 2>/dev/null || echo "unknown unknown")
status=$(echo "$run_data" | awk '{print $1}')
conclusion=$(echo "$run_data" | awk '{print $2}')
if [[ "$status" == "completed" ]]; then
break
fi
echo -ne "\r ⏳ CI status: ${status}... (polling every 30s, Ctrl+C to stop)"
sleep 30
done
local status=""
local conclusion=""
local attempts=0
local max_attempts=120 # ~60 minutes at 30s
while true; do
local run_data
run_data=$(gh run list \
--repo "$REPO" \
--workflow "test-e2e.yml" \
--branch "$version" \
--limit 1 \
--json status,conclusion \
--jq '.[0] | "\(.status) \(.conclusion)"' 2>/dev/null || echo "unknown unknown")
status=$(echo "$run_data" | awk '{print $1}')
conclusion=$(echo "$run_data" | awk '{print $2}')
if [[ "$status" == "completed" ]]; then
break
fi
if (( ++attempts >= max_attempts )); then
echo ""
warn "Timed out waiting for CI. Tag ${version} is pushed; create the release manually:"
echo " gh release create ${version} --repo ${REPO} --generate-notes"
exit 1
fi
echo -ne "\r ⏳ CI status: ${status}... (polling every 30s, Ctrl+C to stop)"
sleep 30
done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 156 - 175, Bound the polling loop around gh run
list with an attempt count or consecutive-error limit, treating unknown results
as failures and exiting once the limit is reached. Ensure the exhaustion path
terminates the script through the existing manual-release instructions, while
preserving normal polling until status becomes completed.

Comment thread hack/release.sh
Comment on lines +220 to +227
if [[ -n "$VERSION_OVERRIDE" ]]; then
next_version="${VERSION_OVERRIDE}"
next_minor_num=$(echo "$next_version" | grep -oP 'v0\.\K[0-9]+')
release_branch="release-v0.${next_minor_num}.x"

local prev_minor_num=$((next_minor_num - 1))
prev_release_branch="release-v0.${prev_minor_num}.x"
prev_tag=$(get_latest_patch_for_minor "0.${prev_minor_num}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate --version before deriving the minor number. grep -oP 'v0\.\K[0-9]+' returns empty for anything not matching v0.<n> (e.g. v1.0.0, 0.11.0, a typo). With set -e and an empty next_minor_num, $((next_minor_num - 1)) aborts with an arithmetic error, and release_branch becomes release-v0..x in other paths.

🐛 Proposed fix
     if [[ -n "$VERSION_OVERRIDE" ]]; then
+        if [[ ! "$VERSION_OVERRIDE" =~ ^v0\.[0-9]+\.0$ ]]; then
+            error "Invalid --version for a minor release: ${VERSION_OVERRIDE} (expected v0.X.0)"
+            exit 1
+        fi
         next_version="${VERSION_OVERRIDE}"
         next_minor_num=$(echo "$next_version" | grep -oP 'v0\.\K[0-9]+')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ -n "$VERSION_OVERRIDE" ]]; then
next_version="${VERSION_OVERRIDE}"
next_minor_num=$(echo "$next_version" | grep -oP 'v0\.\K[0-9]+')
release_branch="release-v0.${next_minor_num}.x"
local prev_minor_num=$((next_minor_num - 1))
prev_release_branch="release-v0.${prev_minor_num}.x"
prev_tag=$(get_latest_patch_for_minor "0.${prev_minor_num}")
if [[ -n "$VERSION_OVERRIDE" ]]; then
if [[ ! "$VERSION_OVERRIDE" =~ ^v0\.[0-9]+\.0$ ]]; then
error "Invalid --version for a minor release: ${VERSION_OVERRIDE} (expected v0.X.0)"
exit 1
fi
next_version="${VERSION_OVERRIDE}"
next_minor_num=$(echo "$next_version" | grep -oP 'v0\.\K[0-9]+')
release_branch="release-v0.${next_minor_num}.x"
local prev_minor_num=$((next_minor_num - 1))
prev_release_branch="release-v0.${prev_minor_num}.x"
prev_tag=$(get_latest_patch_for_minor "0.${prev_minor_num}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 220 - 227, Validate VERSION_OVERRIDE before
deriving next_minor_num in the version-override flow. Require the value to match
the expected v0.<minor> format, and exit with a clear validation error for
invalid versions; only then compute release_branch, prev_minor_num,
prev_release_branch, and prev_tag.

Comment thread hack/release.sh
Comment on lines +512 to +521
patch)
COMMAND="patch"
if [[ -z "${2:-}" ]]; then
error "patch requires a minor version argument (e.g., v0.10)"
echo " Usage: $(basename "$0") patch v0.X [OPTIONS]"
exit 1
fi
PATCH_MINOR="$2"
shift 2
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

patch blindly consumes the next argument. release.sh patch --dry-run sets PATCH_MINOR=--dry-run and drops the flag, then fails later with a confusing "Invalid minor version format". Reject values starting with -.

🐛 Proposed fix
         patch)
             COMMAND="patch"
-            if [[ -z "${2:-}" ]]; then
+            if [[ -z "${2:-}" || "${2}" == -* ]]; then
                 error "patch requires a minor version argument (e.g., v0.10)"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
patch)
COMMAND="patch"
if [[ -z "${2:-}" ]]; then
error "patch requires a minor version argument (e.g., v0.10)"
echo " Usage: $(basename "$0") patch v0.X [OPTIONS]"
exit 1
fi
PATCH_MINOR="$2"
shift 2
;;
patch)
COMMAND="patch"
if [[ -z "${2:-}" || "${2}" == -* ]]; then
error "patch requires a minor version argument (e.g., v0.10)"
echo " Usage: $(basename "$0") patch v0.X [OPTIONS]"
exit 1
fi
PATCH_MINOR="$2"
shift 2
;;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 512 - 521, Update the patch argument validation
in the patch branch of the release command so a missing value or a value
beginning with “-” is rejected before assigning PATCH_MINOR and shifting
arguments. Preserve the existing usage error and prevent options such as
--dry-run from being consumed as the minor version.

@PuneetPunamiya
PuneetPunamiya force-pushed the release-script branch 2 times, most recently from 38e36dd to cccd60b Compare July 28, 2026 11:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (4)
hack/release.sh (4)

27-34: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Still unsafe: eval "$@" allows shell injection.

Re-parsing already-split arguments via eval executes any embedded shell metacharacters as code. This was flagged in a prior review and remains unresolved. Invoke "$@" directly to preserve quoting.

🔒 Proposed fix
 run_cmd() {
     if $DRY_RUN; then
         dry_run "$*"
     else
         info "Running: $*"
-        eval "$@"
+        "$@"
     fi
 }

Note: once this is fixed, sites that build dynamic option strings for run_cmd (e.g. notes_flag around lines 250-258) should switch to arrays instead of relying on unquoted word-splitting, so both quoting and correctness are preserved together.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 27 - 34, Replace eval "$@" in run_cmd with
direct "$@" invocation so arguments retain their original quoting and cannot
execute shell metacharacters. Also update callers such as notes_flag to
construct and pass command options as arrays rather than dynamic strings or
unquoted word-splitting.

Source: Linters/SAST tools


94-106: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Still relies on GNU-only grep -P.

get_latest_minor, get_latest_patch_for_minor, and get_max_patch_number all use grep -oP/grep -P, which fails on BSD grep (macOS default). This was flagged previously and remains unresolved. Either use POSIX-compatible parsing or add an explicit GNU-grep dependency check in preflight.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 94 - 106, Replace the GNU-only grep usage in
get_latest_minor, get_latest_patch_for_minor, and get_max_patch_number with
POSIX-compatible shell parsing and filtering so these helpers work with BSD
grep; alternatively, add and invoke an explicit GNU grep dependency check during
preflight. Preserve each helper’s existing tag-selection and patch-number
results.

Source: Linters/SAST tools


127-134: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

--version override still unvalidated before deriving next_minor_num.

If VERSION_OVERRIDE doesn't match v0.<n>.0 (typo, v1.0.0, etc.), grep -oP 'v0\.\K[0-9]+' returns empty and $((next_minor_num - 1)) at line 132 aborts with an arithmetic error under set -e. Still unresolved from a prior review.

🐛 Proposed fix
     if [[ -n "$VERSION_OVERRIDE" ]]; then
+        if [[ ! "$VERSION_OVERRIDE" =~ ^v0\.[0-9]+\.0$ ]]; then
+            error "Invalid --version for a minor release: ${VERSION_OVERRIDE} (expected v0.X.0)"
+            exit 1
+        fi
         next_version="${VERSION_OVERRIDE}"
         next_minor_num=$(echo "$next_version" | grep -oP 'v0\.\K[0-9]+')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 127 - 134, Validate VERSION_OVERRIDE in the
override branch before deriving next_minor_num, requiring the expected
v0.<minor>.0 format and rejecting invalid values with a clear error. Keep the
existing release_branch, previous-minor, and previous-tag calculations unchanged
for valid overrides, and ensure invalid input exits before arithmetic
evaluation.

403-412: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

patch still consumes the next token unconditionally as the minor argument.

release.sh patch --dry-run sets PATCH_MINOR=--dry-run and drops the flag, later failing with a confusing "Invalid minor version format" instead of a clear usage error. Flagged previously, still present.

🐛 Proposed fix
         patch)
             COMMAND="patch"
-            if [[ -z "${2:-}" ]]; then
+            if [[ -z "${2:-}" || "${2}" == -* ]]; then
                 error "patch requires a minor version argument (e.g., v0.10)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 403 - 412, Update the patch command argument
handling so the required minor-version argument is rejected when the next token
is an option such as --dry-run, rather than consumed as PATCH_MINOR. Validate
that $2 is present and represents the expected minor-version format before shift
2; otherwise emit the existing usage error and exit, while preserving normal
option parsing after a valid version.
🧹 Nitpick comments (1)
hack/release.sh (1)

417-432: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Same "swallows next flag" pattern also affects --upstream-remote and --version.

Neither checks whether the next token starts with -. E.g. release.sh minor --upstream-remote --dry-run would set UPSTREAM_REMOTE="--dry-run" and silently drop --dry-run, since only emptiness is checked. Same class of bug as the patch argument issue above; worth applying the same guard here too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 417 - 432, The option parsing cases for
--upstream-remote and --version accept another flag as their value and then
consume it. Update both validation branches to reject values that are empty or
begin with “-”, while preserving their existing specific error messages and
shift behavior for valid values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 21-27: Update the release workflow steps that use github.ref_name,
steps.info.outputs.type, steps.info.outputs.minor, or github.repository in run
scripts, including “Validate tag format,” “Determine release type,” and
“Summary,” to pass each value through the step’s env mapping and reference the
resulting quoted environment variables in shell commands. Ensure no GitHub
context expressions are interpolated directly into script text, while preserving
the existing validation and release-summary behavior.
- Around line 34-46: Fix the patch-release invocation of hack/release.sh so
steps.info.outputs.minor, which already contains the major and minor components
such as 0.12, is prefixed with v only once. Update the patch path to pass v${{
steps.info.outputs.minor }} and preserve the existing minor-release behavior.

In `@hack/release.sh`:
- Around line 36-55: Update usage() to accept an exit-status argument and exit
with that value instead of unconditionally exiting 0. Keep the help path
invoking usage with status 0, and update the unknown-argument path near the
argument parser to invoke usage with a non-zero status after reporting the
error.
- Around line 158-176: Validate an existing release_branch alongside the
existing tag validation: when branch_exists is true, resolve the branch commit
and compare it with target_sha, reporting the branch and expected commit details
and exiting on mismatch. Keep the current behavior for a matching branch and for
branches that do not yet exist, using the existing release_branch, target_sha,
and target_short symbols.

---

Duplicate comments:
In `@hack/release.sh`:
- Around line 27-34: Replace eval "$@" in run_cmd with direct "$@" invocation so
arguments retain their original quoting and cannot execute shell metacharacters.
Also update callers such as notes_flag to construct and pass command options as
arrays rather than dynamic strings or unquoted word-splitting.
- Around line 94-106: Replace the GNU-only grep usage in get_latest_minor,
get_latest_patch_for_minor, and get_max_patch_number with POSIX-compatible shell
parsing and filtering so these helpers work with BSD grep; alternatively, add
and invoke an explicit GNU grep dependency check during preflight. Preserve each
helper’s existing tag-selection and patch-number results.
- Around line 127-134: Validate VERSION_OVERRIDE in the override branch before
deriving next_minor_num, requiring the expected v0.<minor>.0 format and
rejecting invalid values with a clear error. Keep the existing release_branch,
previous-minor, and previous-tag calculations unchanged for valid overrides, and
ensure invalid input exits before arithmetic evaluation.
- Around line 403-412: Update the patch command argument handling so the
required minor-version argument is rejected when the next token is an option
such as --dry-run, rather than consumed as PATCH_MINOR. Validate that $2 is
present and represents the expected minor-version format before shift 2;
otherwise emit the existing usage error and exit, while preserving normal option
parsing after a valid version.

---

Nitpick comments:
In `@hack/release.sh`:
- Around line 417-432: The option parsing cases for --upstream-remote and
--version accept another flag as their value and then consume it. Update both
validation branches to reject values that are empty or begin with “-”, while
preserving their existing specific error messages and shift behavior for valid
values.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fafa7621-3301-48ef-8ac9-628d3c37e75e

📥 Commits

Reviewing files that changed from the base of the PR and between 38e36dd and cccd60b.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • hack/release.sh

Comment on lines +21 to +27
- name: Validate tag format
run: |
TAG="${{ github.ref_name }}"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid tag format: ${TAG}. Expected vMAJOR.MINOR.PATCH (e.g., v0.12.0)"
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Unsanitized ${{ github.ref_name }}/context values interpolated directly into run: scripts (template injection).

Lines 23, 37, 54, 66, 71-73 splice GitHub context expressions (github.ref_name, steps.info.outputs.*, github.repository) directly into shell script text rather than passing them through env:. Notably, the very step meant to validate the tag (line 21-27) itself performs this interpolation before any validation occurs, so a maliciously-crafted tag name (by anyone with tag-push access) could inject shell syntax before the format check ever runs. The standard mitigation is to pass these values via env: and reference them as "$VAR" inside the script, which makes the value pure data instead of script text.

🔒 Proposed fix (pattern to apply at each flagged step)
       - name: Validate tag format
+        env:
+          TAG: ${{ github.ref_name }}
         run: |
-          TAG="${{ github.ref_name }}"
           if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
             echo "::error::Invalid tag format: ${TAG}. Expected vMAJOR.MINOR.PATCH (e.g., v0.12.0)"
             exit 1
           fi

Apply the same env:-passing pattern to the "Determine release type" and "Summary" steps for github.ref_name, steps.info.outputs.type, and steps.info.outputs.minor.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Validate tag format
run: |
TAG="${{ github.ref_name }}"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid tag format: ${TAG}. Expected vMAJOR.MINOR.PATCH (e.g., v0.12.0)"
exit 1
fi
- name: Validate tag format
env:
TAG: ${{ github.ref_name }}
run: |
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid tag format: ${TAG}. Expected vMAJOR.MINOR.PATCH (e.g., v0.12.0)"
exit 1
fi
🧰 Tools
🪛 zizmor (1.26.1)

[error] 23-23: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 21 - 27, Update the release
workflow steps that use github.ref_name, steps.info.outputs.type,
steps.info.outputs.minor, or github.repository in run scripts, including
“Validate tag format,” “Determine release type,” and “Summary,” to pass each
value through the step’s env mapping and reference the resulting quoted
environment variables in shell commands. Ensure no GitHub context expressions
are interpolated directly into script text, while preserving the existing
validation and release-summary behavior.

Source: Linters/SAST tools

Comment thread .github/workflows/release.yml Outdated
Comment thread hack/release.sh
Comment on lines +36 to +55
usage() {
cat <<EOF
Usage:
$(basename "$0") minor [OPTIONS] Create a new minor release (v0.X.0) from origin/main
$(basename "$0") patch v0.X [OPTIONS] Create a patch release (v0.X.Y) on an existing release branch

Options:
--dry-run Show what would happen without making changes
--upstream-remote NAME Override the remote name (default: origin)
--version VERSION Override the auto-detected version (e.g., v0.11.0)
--force Proceed even when there are no new commits
-h, --help Show this help message

Examples:
$(basename "$0") minor --dry-run
$(basename "$0") patch v0.10 --dry-run
$(basename "$0") minor --version v0.11.0 # resume a partial release
EOF
exit 0
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

usage() always exits 0, even on the error path — masks real failures.

usage() unconditionally calls exit 0 (line 54). It's invoked both for -h|--help (should exit 0) and after error "Unknown argument: $1" (line 441-442, should exit non-zero). As written, an invalid CLI invocation prints an error but the script still reports success, which is misleading for any caller checking the exit code.

🐛 Proposed fix
-usage() {
+usage() {
+    local exit_code="${1:-0}"
     cat <<EOF
 ...
 EOF
-    exit 0
+    exit "$exit_code"
 }
         *)
             error "Unknown argument: $1"
-            usage
+            usage 1
             ;;

Also applies to: 437-449

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 36 - 55, Update usage() to accept an
exit-status argument and exit with that value instead of unconditionally exiting
0. Keep the help path invoking usage with status 0, and update the
unknown-argument path near the argument parser to invoke usage with a non-zero
status after reporting the error.

Comment thread hack/release.sh
Comment on lines +158 to +176
if git rev-parse "${UPSTREAM_REMOTE}/${release_branch}" &>/dev/null 2>&1; then
branch_exists=true
fi

local target_sha
target_sha=$(git rev-parse "${UPSTREAM_REMOTE}/main")
local target_short
target_short=$(git rev-parse --short "${UPSTREAM_REMOTE}/main")

if $tag_exists; then
local existing_tag_sha
existing_tag_sha=$(git rev-parse "$next_version")
if [[ "$existing_tag_sha" != "$target_sha" ]]; then
error "Tag ${next_version} already exists but points to a different commit!"
echo " Tag points to: $(git rev-parse --short "$next_version")"
echo " Expected (main): ${target_short}"
exit 1
fi
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

branch_exists isn't validated against the target commit, unlike tag_exists.

Lines 154-176 verify that an already-existing tag points at the expected target_sha, but the same check is missing for branch_exists (line 158-160). If release_branch already exists but points elsewhere (e.g. created earlier from a different commit), line 238-244 will silently skip it, leaving the release branch misaligned with the tag/release for future patch computations.

🐛 Proposed fix
     if git rev-parse "${UPSTREAM_REMOTE}/${release_branch}" &>/dev/null 2>&1; then
         branch_exists=true
     fi
+    if $branch_exists; then
+        local existing_branch_sha
+        existing_branch_sha=$(git rev-parse "${UPSTREAM_REMOTE}/${release_branch}")
+        if [[ "$existing_branch_sha" != "$target_sha" ]]; then
+            error "Branch ${release_branch} already exists but points to a different commit!"
+            echo "  Branch points to: $(git rev-parse --short "${UPSTREAM_REMOTE}/${release_branch}")"
+            echo "  Expected (main):   ${target_short}"
+            exit 1
+        fi
+    fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if git rev-parse "${UPSTREAM_REMOTE}/${release_branch}" &>/dev/null 2>&1; then
branch_exists=true
fi
local target_sha
target_sha=$(git rev-parse "${UPSTREAM_REMOTE}/main")
local target_short
target_short=$(git rev-parse --short "${UPSTREAM_REMOTE}/main")
if $tag_exists; then
local existing_tag_sha
existing_tag_sha=$(git rev-parse "$next_version")
if [[ "$existing_tag_sha" != "$target_sha" ]]; then
error "Tag ${next_version} already exists but points to a different commit!"
echo " Tag points to: $(git rev-parse --short "$next_version")"
echo " Expected (main): ${target_short}"
exit 1
fi
fi
if git rev-parse "${UPSTREAM_REMOTE}/${release_branch}" &>/dev/null 2>&1; then
branch_exists=true
fi
local target_sha
target_sha=$(git rev-parse "${UPSTREAM_REMOTE}/main")
local target_short
target_short=$(git rev-parse --short "${UPSTREAM_REMOTE}/main")
if $branch_exists; then
local existing_branch_sha
existing_branch_sha=$(git rev-parse "${UPSTREAM_REMOTE}/${release_branch}")
if [[ "$existing_branch_sha" != "$target_sha" ]]; then
error "Branch ${release_branch} already exists but points to a different commit!"
echo " Branch points to: $(git rev-parse --short "${UPSTREAM_REMOTE}/${release_branch}")"
echo " Expected (main): ${target_short}"
exit 1
fi
fi
if $tag_exists; then
local existing_tag_sha
existing_tag_sha=$(git rev-parse "$next_version")
if [[ "$existing_tag_sha" != "$target_sha" ]]; then
error "Tag ${next_version} already exists but points to a different commit!"
echo " Tag points to: $(git rev-parse --short "$next_version")"
echo " Expected (main): ${target_short}"
exit 1
fi
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 158 - 176, Validate an existing release_branch
alongside the existing tag validation: when branch_exists is true, resolve the
branch commit and compare it with target_sha, reporting the branch and expected
commit details and exiting on mismatch. Keep the current behavior for a matching
branch and for branches that do not yet exist, using the existing
release_branch, target_sha, and target_short symbols.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
hack/release.sh (1)

219-227: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an array for the optional --notes-start-tag flag.

Unquoted $notes_flag relies on word splitting (SC2086) and breaks the moment run_cmd drops eval and quotes arguments properly.

♻️ Proposed refactor
-        local notes_flag=""
+        local notes_flag=()
         if [[ -n "$prev_tag" ]]; then
-            notes_flag="--notes-start-tag ${prev_tag}"
+            notes_flag=(--notes-start-tag "$prev_tag")
         fi
         run_cmd gh release create "$next_version" \
             --repo "$REPO" \
             --generate-notes \
             --draft \
-            $notes_flag
+            "${notes_flag[@]}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 219 - 227, Update the release creation flow
around notes_flag to represent the optional --notes-start-tag arguments as an
array, append the flag and prev_tag only when prev_tag is set, and expand the
array safely when invoking run_cmd gh release create. Remove the unquoted scalar
expansion while preserving the existing command behavior.

Source: Linters/SAST tools

.github/workflows/release.yml (1)

16-19: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Serialize release runs before mutating release state.

Two tags pushed close together can run concurrently while the release script creates/pushes branches and draft releases, causing push conflicts or duplicate drafts. Add a workflow-level concurrency group with cancel-in-progress: false, or provide equivalent locking/idempotency in the script.

Proposed fix
 permissions:
   contents: write
 
+concurrency:
+  group: release
+  cancel-in-progress: false
+
 env:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 16 - 19, Add workflow-level
concurrency to the release job identified by release, using a stable group for
release runs and setting cancel-in-progress to false so queued tag-triggered
runs execute serially without cancellation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 29-32: Update the release workflow before the hack/release.sh
execution to guard v* tag pushes: allow only trusted maintainers or validate
that the tag matches protected release history, and skip the release script
otherwise. Preserve the existing checkout and release behavior for authorized,
validated tags.

In `@hack/release.sh`:
- Around line 273-295: Validate VERSION_OVERRIDE in the patch-version flow
before deriving override_patch, requiring the exact v${minor}.<numeric_patch>
format and rejecting mismatched or malformed values with the script’s standard
error handling. When deriving latest_patch_tag in this path, verify that the
resulting previous tag exists before calling git rev-list; report a clear error
and exit if it is missing, while preserving the existing tag validation for
next_version.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 16-19: Add workflow-level concurrency to the release job
identified by release, using a stable group for release runs and setting
cancel-in-progress to false so queued tag-triggered runs execute serially
without cancellation.

In `@hack/release.sh`:
- Around line 219-227: Update the release creation flow around notes_flag to
represent the optional --notes-start-tag arguments as an array, append the flag
and prev_tag only when prev_tag is set, and expand the array safely when
invoking run_cmd gh release create. Remove the unquoted scalar expansion while
preserving the existing command behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a37b49f-c708-4025-8efd-464905f4a1f1

📥 Commits

Reviewing files that changed from the base of the PR and between cccd60b and c7dab71.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • hack/release.sh

Comment on lines +29 to +32
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l .github/workflows/release.yml
cat -n .github/workflows/release.yml | sed -n '1,220p'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 3223


Guard release tags before running hack/release.sh

Any push to v* can check out that tag’s commit and run hack/release.sh with contents: write and GH_TOKEN. Limit v* tag creation/update to trusted maintainers or require the tag to match protected release history before executing the script.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 29-32: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 29 - 32, Update the release
workflow before the hack/release.sh execution to guard v* tag pushes: allow only
trusted maintainers or validate that the tag matches protected release history,
and skip the release script otherwise. Preserve the existing checkout and
release behavior for authorized, validated tags.

Comment thread hack/release.sh
Comment on lines +273 to +295
local next_version
if [[ -n "$VERSION_OVERRIDE" ]]; then
next_version="${VERSION_OVERRIDE}"
local override_patch="${next_version##*.}"
if (( override_patch > 0 )); then
latest_patch_tag="v${minor}.$((override_patch - 1))"
fi
else
local next_patch=$((max_patch + 1))
next_version="v${minor}.${next_patch}"
fi

info "Existing v${minor}.x tags: $(get_all_tags | grep -P "^v${minor}\.[0-9]+$" | tr '\n' ' ')"
info "Latest patch: ${latest_patch_tag}"
info "Next version: ${BOLD}${next_version}${NC}"

if ! git rev-parse "$next_version" &>/dev/null 2>&1; then
error "Tag ${next_version} does not exist. Create and push the tag first."
exit 1
fi

local new_commits
new_commits=$(git rev-list --count "${latest_patch_tag}..${UPSTREAM_REMOTE}/${release_branch}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate --version in the patch path and verify the derived previous tag exists.

Two gaps here:

  • VERSION_OVERRIDE is never checked against v${minor}.<n>, so a mismatched or malformed value (e.g. patch v0.10 --version v0.9.3, or a non-numeric suffix) either builds notes against the wrong lineage or aborts on the arithmetic at line 277.
  • Line 278 assumes v${minor}.$((override_patch-1)) exists. With a gap in patch numbering, git rev-list at line 295 dies with a raw "unknown revision" error.
🐛 Proposed fix
     if [[ -n "$VERSION_OVERRIDE" ]]; then
+        if [[ ! "$VERSION_OVERRIDE" =~ ^v${minor}\.[0-9]+$ ]]; then
+            error "Invalid --version for a v${minor}.x patch release: ${VERSION_OVERRIDE}"
+            exit 1
+        fi
         next_version="${VERSION_OVERRIDE}"
         local override_patch="${next_version##*.}"
         if (( override_patch > 0 )); then
             latest_patch_tag="v${minor}.$((override_patch - 1))"
+            if ! git rev-parse "$latest_patch_tag" &>/dev/null 2>&1; then
+                latest_patch_tag=$(get_latest_patch_for_minor "$minor")
+            fi
         fi
     else
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
local next_version
if [[ -n "$VERSION_OVERRIDE" ]]; then
next_version="${VERSION_OVERRIDE}"
local override_patch="${next_version##*.}"
if (( override_patch > 0 )); then
latest_patch_tag="v${minor}.$((override_patch - 1))"
fi
else
local next_patch=$((max_patch + 1))
next_version="v${minor}.${next_patch}"
fi
info "Existing v${minor}.x tags: $(get_all_tags | grep -P "^v${minor}\.[0-9]+$" | tr '\n' ' ')"
info "Latest patch: ${latest_patch_tag}"
info "Next version: ${BOLD}${next_version}${NC}"
if ! git rev-parse "$next_version" &>/dev/null 2>&1; then
error "Tag ${next_version} does not exist. Create and push the tag first."
exit 1
fi
local new_commits
new_commits=$(git rev-list --count "${latest_patch_tag}..${UPSTREAM_REMOTE}/${release_branch}")
local next_version
if [[ -n "$VERSION_OVERRIDE" ]]; then
if [[ ! "$VERSION_OVERRIDE" =~ ^v${minor}\.[0-9]+$ ]]; then
error "Invalid --version for a v${minor}.x patch release: ${VERSION_OVERRIDE}"
exit 1
fi
next_version="${VERSION_OVERRIDE}"
local override_patch="${next_version##*.}"
if (( override_patch > 0 )); then
latest_patch_tag="v${minor}.$((override_patch - 1))"
if ! git rev-parse "$latest_patch_tag" &>/dev/null 2>&1; then
latest_patch_tag=$(get_latest_patch_for_minor "$minor")
fi
fi
else
local next_patch=$((max_patch + 1))
next_version="v${minor}.${next_patch}"
fi
info "Existing v${minor}.x tags: $(get_all_tags | grep -P "^v${minor}\.[0-9]+$" | tr '\n' ' ')"
info "Latest patch: ${latest_patch_tag}"
info "Next version: ${BOLD}${next_version}${NC}"
if ! git rev-parse "$next_version" &>/dev/null 2>&1; then
error "Tag ${next_version} does not exist. Create and push the tag first."
exit 1
fi
local new_commits
new_commits=$(git rev-list --count "${latest_patch_tag}..${UPSTREAM_REMOTE}/${release_branch}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/release.sh` around lines 273 - 295, Validate VERSION_OVERRIDE in the
patch-version flow before deriving override_patch, requiring the exact
v${minor}.<numeric_patch> format and rejecting mismatched or malformed values
with the script’s standard error handling. When deriving latest_patch_tag in
this path, verify that the resulting previous tag exists before calling git
rev-list; report a clear error and exit if it is missing, while preserving the
existing tag validation for next_version.

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.

1 participant