Skip to content

Bump to next prerelease#44

Open
dlabaj wants to merge 3 commits into
patternfly:mainfrom
dlabaj:bump-to-next-prerelease
Open

Bump to next prerelease#44
dlabaj wants to merge 3 commits into
patternfly:mainfrom
dlabaj:bump-to-next-prerelease

Conversation

@dlabaj
Copy link
Copy Markdown
Contributor

@dlabaj dlabaj commented May 26, 2026

What / why

Added new command that can bump to the next prerelease for a repo.

How to test

Go to a repo and run "pfcli bump-prerelease --dry-run" verify the next minor version would be bumped.
Go to a repo and run "pfcli bump-prerelease --dry-run --major" verify the next major version would be bumped.

Checklist

  • [ x ] Tests added or updated where appropriate
  • [ x ] npm run lint and npm test pass locally
  • [ x ] Commit messages follow Conventional Commits (required for semantic-release), e.g. feat:, fix:, docs:, chore:

Summary by CodeRabbit

  • New Features

    • Added bump-prerelease CLI command to create and manage prerelease tags with support for --major and --dry-run options.
  • Documentation

    • Updated README with documentation for the new bump-prerelease command.
  • Tests

    • Added comprehensive test suite for the new prerelease versioning functionality.
  • Chores

    • Updated commit-msg hook configuration.

Review Change Stack

@dlabaj dlabaj requested a review from cdcabrera May 26, 2026 17:35
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Warning

Review limit reached

@dlabaj, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 54 minutes and 18 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 89b24401-5ffe-4a54-80f1-6516b11ac959

📥 Commits

Reviewing files that changed from the base of the PR and between 50e0ad3 and 22ad917.

📒 Files selected for processing (2)
  • src/__tests__/bump-prerelease.test.ts
  • src/bump-prerelease.ts

Walkthrough

This PR adds a complete bump-prerelease CLI command for managing prerelease Git tags. It includes core tag-parsing and Git-remote-selection logic, a main orchestration function computing and creating prerelease versions, comprehensive test coverage, CLI integration, and documentation updates.

Changes

Prerelease version bump feature

Layer / File(s) Summary
Core tag and remote management functions
src/bump-prerelease.ts
Helper functions parsePrereleaseTag, getUpstreamRemote, getLatestPrereleaseTag, and tagExists validate prerelease tag format, select Git remotes (preferring upstream), fetch and version-sort remote prerelease tags, and check local tag existence.
Prerelease bump orchestration
src/bump-prerelease.ts
runBumpPrerelease orchestrates the full workflow: determines the remote, fetches the latest prerelease tag, computes the next version (initial or incremented), validates the new tag does not exist, logs planned actions in dry-run mode, and performs Git tag creation and push in normal mode.
Prerelease bump test suite
src/__tests__/bump-prerelease.test.ts
Jest test suite with mocked execa validates initial tag creation, minor/major version bumps, double-digit minor versions, remote selection behavior, latest tag selection from multiple matches, error handling for existing tags, dry-run mode (skipping Git operations while fetching/checking), and major bump logic.
CLI command registration
src/cli.ts
Imports runBumpPrerelease and registers a new bump-prerelease Commander command accepting optional repo path, --dry-run, and --major flags, with error handling and exit code 1 on failure.
Documentation and hook update
README.md, .husky/commit-msg
Added bump-prerelease command description to README and updated Husky commit-msg hook to include --no-install flag for commitlint.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wise-king-sullyman
  • jpuzz0
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature added: a new command that bumps to the next prerelease version, which matches the primary changes across all files.
Description check ✅ Passed The description includes all required sections: a clear 'What / why' explanation, specific testing instructions with both minor and major version examples, and a completed checklist confirming tests, linting, and conventional commits.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@dlabaj dlabaj requested a review from nicolethoen May 26, 2026 17:35
Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (1)
src/__tests__/bump-prerelease.test.ts (1)

17-27: ⚡ Quick win

Replace repeated as any in execa mocks with typed mock results (optional)

The file disables @typescript-eslint/no-explicit-any at the top, so the current CI failure is unlikely to be from that rule; still, the repeated Promise.resolve({ ... } as any) casts reduce type safety—centralizing them behind a typed helper keeps the mocks honest.

Suggested pattern
 import { runBumpPrerelease } from '../bump-prerelease';
 import { execa } from 'execa';
@@
 const mockedExeca = execa as jest.MockedFunction<typeof execa>;
+type ExecaResult = Awaited<ReturnType<typeof execa>>;
+const ok = (stdout = ''): ExecaResult => ({ stdout } as unknown as ExecaResult);
@@
-        return Promise.resolve({ stdout: 'origin' } as any);
+        return Promise.resolve(ok('origin'));
@@
-        return Promise.resolve({ stdout: '' } as any);
+        return Promise.resolve(ok(''));
@@
-      return Promise.resolve({} as any);
+      return Promise.resolve(ok());
🤖 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 `@src/__tests__/bump-prerelease.test.ts` around lines 17 - 27, The execa mocks
in mockedExeca.mockImplementation use repeated Promise.resolve({ ... } as any);
replace those casts with a small typed helper (e.g., typedResolve<T>(value: T):
Promise<ReturnType<typeof mockedExeca>>) or a helper that returns a properly
typed mock result so each branch (the 'git remote', 'git tag -l', and default
branches inside mockedExeca.mockImplementation) returns a correctly typed object
instead of using as any; update the branches checking args?.[0] === 'remote',
args?.[0] === 'tag' && args?.[1] === '-l', and args?.[0] === 'rev-parse' to use
that helper.
🤖 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 `@src/bump-prerelease.ts`:
- Around line 74-80: tagExists currently calls git rev-parse on the plain tag
which can resolve non-tag refs; change the execa call in function tagExists to
resolve the tag under refs/tags by calling git rev-parse refs/tags/${tag} (still
passing the cwd option and preserving the existing try/catch and boolean return
behavior) so the check only succeeds for actual annotated/created tags.
- Around line 34-37: The tagExists helper in src/bump-prerelease.ts currently
uses a general git rev-parse which can return true for non-tag refs; update
tagExists to verify the actual tag ref by running git show-ref --tags --verify
--quiet refs/tags/<tag> (or the equivalent API call) and return true only on a
successful exit code, false on not-found, and rethrow other errors (preserving
original error as cause if present); reference the tagExists function name when
making this change so callers keep behavior but no longer get false positives
for branches or other refs with the same name.

---

Nitpick comments:
In `@src/__tests__/bump-prerelease.test.ts`:
- Around line 17-27: The execa mocks in mockedExeca.mockImplementation use
repeated Promise.resolve({ ... } as any); replace those casts with a small typed
helper (e.g., typedResolve<T>(value: T): Promise<ReturnType<typeof
mockedExeca>>) or a helper that returns a properly typed mock result so each
branch (the 'git remote', 'git tag -l', and default branches inside
mockedExeca.mockImplementation) returns a correctly typed object instead of
using as any; update the branches checking args?.[0] === 'remote', args?.[0] ===
'tag' && args?.[1] === '-l', and args?.[0] === 'rev-parse' to use that helper.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: af816658-65a4-4344-abd8-796e310480e4

📥 Commits

Reviewing files that changed from the base of the PR and between 530e3b9 and 50e0ad3.

📒 Files selected for processing (5)
  • .husky/commit-msg
  • README.md
  • src/__tests__/bump-prerelease.test.ts
  • src/bump-prerelease.ts
  • src/cli.ts

Comment thread src/bump-prerelease.ts
Comment on lines +34 to +37
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to determine upstream remote: ${error.message}`);
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/bump-prerelease.ts"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE"
  exit 1
fi

echo "== File head =="
head -n 120 "$FILE" | sed -n '1,120p'

echo
echo "== Lines 1-120 (with numbers) =="
nl -ba "$FILE" | sed -n '1,140p'

echo
echo "== Lines 30-90 (with numbers) =="
nl -ba "$FILE" | sed -n '30,90p'

echo
echo "== Lines 90-140 (with numbers) =="
nl -ba "$FILE" | sed -n '90,170p'

echo
echo "== Search for 'new Error(`Failed' in file =="
rg -n "new Error\\(`Failed" "$FILE" || true

echo
echo "== Search for rethrow patterns in file =="
rg -n "throw new Error\\(" "$FILE" || true

Repository: patternfly/patternfly-cli

Length of output: 3904


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/bump-prerelease.ts"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE"
  exit 1
fi

echo "== rg for catch blocks =="
rg -n "catch \\(error\\)" "$FILE" || true

echo
echo "== Show 25 lines after each 'catch (error)' =="
# Use awk to print with line numbers since nl isn't available
awk '
  /catch \(error\)/ {print "---- match at line " NR; start=NR; next}
  start && NR>start && NR<=start+25 {print NR ":" $0}
  start && NR>start+25 {start=0}
' "$FILE"

echo
echo "== Find all 'new Error(`Failed' occurrences =="
rg -n "new Error\\(\\`Failed" "$FILE" || true

echo
echo "== Check each throw new Error(...) line and whether it has { cause: error } =="
awk '
  /throw new Error\(/ {
    line=NR;
    print "---- throw at line " NR;
    for(i=NR;i<=NR+3;i++) if(getline l<0) break; # no-op
  }
' "$FILE" >/dev/null 2>&1 || true

# Simpler: print the exact lines containing throw new Error(...) and nearby
rg -n -C 2 "throw new Error\\(" "$FILE"

echo
echo "== tagExists definition =="
rg -n "async function tagExists" "$FILE" -n
# Print function block (up to closing brace) with a small window
rg -n -C 3 "async function tagExists\\(" "$FILE"

echo
echo "== Usages of tagExists =="
rg -n "tagExists\\(" "$FILE" || true

Repository: patternfly/patternfly-cli

Length of output: 3428


Harden tagExists to check only actual tags

  • src/bump-prerelease.ts already preserves error context in the rethrows (both catch blocks include { cause: error }), so no change is needed there.
  • tagExists uses git rev-parse <tag>, which can return true for non-tag refs (e.g., a branch) with the same name; verify refs/tags/<tag> instead (e.g., git show-ref --tags --verify --quiet refs/tags/<tag> ) to avoid false positives.
🧰 Tools
🪛 ESLint

[error] 36-36: There is no cause attached to the symptom error being thrown.

(preserve-caught-error)

🤖 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 `@src/bump-prerelease.ts` around lines 34 - 37, The tagExists helper in
src/bump-prerelease.ts currently uses a general git rev-parse which can return
true for non-tag refs; update tagExists to verify the actual tag ref by running
git show-ref --tags --verify --quiet refs/tags/<tag> (or the equivalent API
call) and return true only on a successful exit code, false on not-found, and
rethrow other errors (preserving original error as cause if present); reference
the tagExists function name when making this change so callers keep behavior but
no longer get false positives for branches or other refs with the same name.

Comment thread src/bump-prerelease.ts
Comment on lines +74 to +80
async function tagExists(tag: string, cwd: string): Promise<boolean> {
try {
await execa('git', ['rev-parse', tag], { cwd });
return true;
} catch {
return false;
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant section of bump-prerelease.ts
FILE="src/bump-prerelease.ts"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE"
  exit 1
fi

echo "== Snippet around tagExists =="
nl -ba "$FILE" | sed -n '50,110p'

echo "== tagExists usages =="
rg -n "tagExists\(" "$FILE"

Repository: patternfly/patternfly-cli

Length of output: 162


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/bump-prerelease.ts"
echo "== Checking file =="
ls -la "$FILE" || { echo "Missing $FILE"; exit 1; }

echo "== File length (lines) =="
wc -l "$FILE"

echo "== Snippet around tagExists (lines 60-95) =="
python3 - <<'PY'
import itertools
path="src/bump-prerelease.ts"
start,end=60,95
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if start<=i<=end:
            print(f"{i:4d}: {line.rstrip()}")
        if i>end:
            break
PY

echo "== tagExists usages in file =="
rg -n "tagExists\(" "$FILE"

Repository: patternfly/patternfly-cli

Length of output: 1573


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/bump-prerelease.ts"
echo "== Snippet around tagExists usage (lines 110-150) =="
python3 - <<'PY'
path="src/bump-prerelease.ts"
start,end=105,153
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if start<=i<=end:
            print(f"{i:4d}: {line.rstrip()}")
        if i>end:
            break
PY

Repository: patternfly/patternfly-cli

Length of output: 2074


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="src/bump-prerelease.ts"
python3 - <<'PY'
path="src/bump-prerelease.ts"
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if i<=170:
            print(f"{i:4d}: {line.rstrip()}")
PY

Repository: patternfly/patternfly-cli

Length of output: 5757


Narrow tag existence check to tags only (refs/tags)tagExists() currently uses git rev-parse <tag> (line 76), which can resolve non-tag refs and cause false “already exists” errors; verify refs/tags/${tag} instead.

Suggested fix
 async function tagExists(tag: string, cwd: string): Promise<boolean> {
   try {
-    await execa('git', ['rev-parse', tag], { cwd });
+    await execa('git', ['rev-parse', '--verify', '--quiet', `refs/tags/${tag}`], { cwd });
     return true;
   } catch {
     return false;
   }
 }
🤖 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 `@src/bump-prerelease.ts` around lines 74 - 80, tagExists currently calls git
rev-parse on the plain tag which can resolve non-tag refs; change the execa call
in function tagExists to resolve the tag under refs/tags by calling git
rev-parse refs/tags/${tag} (still passing the cwd option and preserving the
existing try/catch and boolean return behavior) so the check only succeeds for
actual annotated/created tags.

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