Skip to content

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Jan 20, 2026

Summary

  • Add CONTRIBUTING.md with guidelines for reporting issues, submitting PRs, development setup, testing, and linting (consistent with Shakapacker/React on Rails patterns)
  • Update CHANGELOG.md to follow keepachangelog format with proper structure and detailed initial release notes
  • Add rakelib/release.rake for gem releases with:
    • Version bumping support (patch/minor/major or explicit versions)
    • Dry-run mode for testing
    • Retry logic for RubyGems publishing
    • OTP support for 2FA
  • Update gemspec to use UberTask::VERSION constant instead of hardcoded version string

Test plan

  • bundle exec rubocop passes
  • bundle exec rspec passes (40 examples, 0 failures)
  • Review CONTRIBUTING.md for completeness and clarity
  • Review CHANGELOG.md format
  • Review release.rake task logic

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Restructured CHANGELOG with improved organization and Keep a Changelog format
    • Added comprehensive contributor guidelines
  • Chores

    • Improved release process automation for gem publishing
    • Enhanced version management in gem specification

✏️ Tip: You can customize this high-level summary in your review settings.

- Add CONTRIBUTING.md with guidelines for reporting issues, PRs, setup,
  testing, and linting (consistent with Shakapacker/React on Rails)
- Update CHANGELOG.md to follow keepachangelog format with proper
  structure and initial release notes
- Add rakelib/release.rake for gem releases with support for version
  bumping (patch/minor/major or explicit), dry-run mode, and retry logic
- Update gemspec to use UberTask::VERSION constant instead of hardcoded
  version string

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

Walkthrough

Infrastructure and documentation enhancements including a restructured changelog with proper versioning sections, new contributor guidelines, automated gem release workflow via Rake task, and dynamic version referencing in the gemspec file.

Changes

Cohort / File(s) Summary
Documentation & Changelog
CHANGELOG.md, CONTRIBUTING.md
Restructured changelog to Keep-a-Changelog format with Unreleased and 0.1.0 sections. New CONTRIBUTING.md establishes contributor guidelines covering issue reporting, PR submission, development setup, testing, linting, Git hooks, and release procedures.
Release Automation
rakelib/release.rake
New Rake task automating gem releases with version bumping (patch/minor/major), dependency updates, git tagging, and RubyGems publishing. Includes retry logic, OTP authentication prompts, and dry-run support.
Gem Configuration
uber_task.gemspec
Updated to load version from uber_task/version constant instead of hard-coded string, eliminating version duplication.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Through documents we hop and bound,
Release workflows safe and sound,
With changelog and guides so clear,
The rabbit's work brings cheer!
Automation hops ahead! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: adding CONTRIBUTING.md, enhancing CHANGELOG.md, and adding a release rake task, matching the core modifications in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jg/contributing-changelog-release

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.

Copy link

@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

🤖 Fix all issues with AI agents
In `@CONTRIBUTING.md`:
- Around line 101-109: The CONTRIBUTING.md section claims lefthook configuration
exists but the repo lacks a .lefthook.yml (lefthook.yml) file despite lefthook
being in the Gemfile; either add the lefthook config file to the repository
(create .lefthook.yml with the project's pre-commit/pre-push hook definitions)
or update the CONTRIBUTING.md text to remove/clarify the lefthook instructions
and note that hooks are opt-in and require manual config; update references to
lefthook in CONTRIBUTING.md and ensure the Gemfile or README mentions how to
obtain/configure the missing .lefthook.yml if you choose not to commit it.

In `@rakelib/release.rake`:
- Around line 117-119: The otp handling currently builds otp_option from
ENV['RUBYGEMS_OTP'] and injects it into the gem release command (otp_option /
sh_with_retry and the "gem release" invocation), which exposes the OTP on the
command line; remove the --otp flag usage and instead ensure GEM_HOST_OTP_CODE
is used: if ENV['RUBYGEMS_OTP'] is present, set ENV['GEM_HOST_OTP_CODE'] =
ENV['RUBYGEMS_OTP'] (or prefer reading ENV['GEM_HOST_OTP_CODE'] directly) and
call sh_with_retry("gem release") without the --otp argument so the OTP is
provided via environment rather than process args.
🧹 Nitpick comments (6)
CHANGELOG.md (1)

11-17: Missing link definitions for version references.

The bracketed version headers [Unreleased] and [0.1.0] use Markdown reference-style link syntax, but the link definitions are missing. Per keepachangelog convention, these should link to GitHub compare/tag URLs.

📝 Add link definitions at the bottom of the file
 - Colorized console output for task progress
+
+[Unreleased]: https://github.com/shakacode/uber_task/compare/v0.1.0...HEAD
+[0.1.0]: https://github.com/shakacode/uber_task/releases/tag/v0.1.0
rakelib/release.rake (5)

27-50: Consider extracting helper methods outside the task block.

Defining methods inside the task block works but is unconventional. These methods are redefined on each task invocation and aren't easily testable or reusable.

♻️ Move helpers to module scope
 # frozen_string_literal: true

+module ReleaseHelpers
+  def self.log(message, force: false)
+    puts message if ENV['VERBOSE'] == '1' || force
+  end
+
+  def self.sh_with_retry(command, max_retries: 3)
+    retries = 0
+    max_retries = ENV.fetch('GEM_RELEASE_MAX_RETRIES', max_retries).to_i
+
+    loop do
+      log "Running: #{command}"
+      success = system(command)
+      return true if success
+
+      retries += 1
+      raise "Command failed after #{max_retries} attempts: #{command}" if retries >= max_retries
+
+      puts "Command failed (attempt #{retries}/#{max_retries}). Retrying..."
+      if command.include?('gem push') || command.include?('gem release')
+        puts 'Enter new RubyGems OTP if needed:'
+      end
+    end
+  end
+end
+
 desc <<~DESC

85-89: Redundant require_relative before load.

The require_relative on line 86 is unnecessary since load on line 88 unconditionally re-evaluates the file. The load alone is sufficient for reloading the version after the bump.

♻️ Simplify version reload
   # Get the new version
-  require_relative '../lib/uber_task/version'
   # Force reload of version file
   load File.join(gem_root, 'lib', 'uber_task', 'version.rb')
   new_version = UberTask::VERSION

99-101: Consider explicitly staging only the expected files.

git add -A stages all changes. While the clean git check provides safety, explicitly adding only the version file and Gemfile.lock would be more predictable and self-documenting.

♻️ Explicit file staging
   unless dry_run
-    sh_with_retry("git add -A && git commit -m 'Release v#{new_version}'")
+    sh_with_retry("git add lib/uber_task/version.rb Gemfile.lock && git commit -m 'Release v#{new_version}'")
     sh_with_retry("git tag -a v#{new_version} -m 'Version #{new_version}'")
   end

52-56: Consider validating gem-release is installed before proceeding.

The task documents the gem-release dependency but doesn't verify it's available. Adding an early check would provide a clearer error message.

♻️ Add dependency check
+  # Check for gem-release dependency
+  unless system('gem list -i gem-release > /dev/null 2>&1')
+    raise 'ERROR: gem-release is required. Install it with: gem install gem-release'
+  end
+
   # Check for uncommitted changes
   unless `git status --porcelain`.strip.empty?
     raise 'ERROR: Uncommitted changes found. Commit or stash them first.'
   end

126-129: CHANGELOG update recommended before release, not after.

The post-release guidance suggests updating CHANGELOG.md after the release. However, best practice is to update the changelog before tagging so release notes are included in the tagged version. Consider either:

  1. Prompting for CHANGELOG updates before committing/tagging, or
  2. Documenting that maintainers should update CHANGELOG before running the release task.

Comment on lines +101 to +109
## Git Hooks (Optional)

This project includes configuration for git hooks via `lefthook`. They are **opt-in for contributors**.

To enable pre-commit hooks locally:

```bash
lefthook install
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for lefthook configuration files in common locations
echo "=== Searching for lefthook configuration files ==="
fd -type f "lefthook" 2>/dev/null | head -20

# Also try common naming patterns
echo ""
echo "=== Checking for .lefthook.yml or lefthook.yml ==="
fd -type f -name "*lefthook*" 2>/dev/null | head -20

# Check for lefthook in dependency files
echo ""
echo "=== Checking for lefthook in Gemfile ==="
if [ -f "Gemfile" ]; then
  rg -n "lefthook" Gemfile 2>/dev/null || echo "Not found in Gemfile"
else
  echo "Gemfile not found"
fi

echo ""
echo "=== Checking for lefthook in package.json ==="
if [ -f "package.json" ]; then
  rg -n "lefthook" package.json 2>/dev/null || echo "Not found in package.json"
else
  echo "package.json not found"
fi

# Check for any git hooks configuration
echo ""
echo "=== Checking for .husky, .git/hooks, or other hook configs ==="
fd -type d -name ".husky" 2>/dev/null || echo "No .husky directory"
fd -type f "husky\|pre-commit\|git-hooks" 2>/dev/null | head -10 || echo "No other hook configs found"

Repository: shakacode/uber_task

Length of output: 393


Lefthook configuration file is missing from the repository.

While the Gemfile declares lefthook as a dependency (line 6), there is no .lefthook.yml or lefthook.yml configuration file present in the repository. The documentation claims "This project includes configuration for git hooks via lefthook", but without the configuration file, contributors running lefthook install will not have any hooks configured. Either add the lefthook configuration file to the repository or remove/clarify this section in CONTRIBUTING.md to reflect the actual state of git hooks setup.

🤖 Prompt for AI Agents
In `@CONTRIBUTING.md` around lines 101 - 109, The CONTRIBUTING.md section claims
lefthook configuration exists but the repo lacks a .lefthook.yml (lefthook.yml)
file despite lefthook being in the Gemfile; either add the lefthook config file
to the repository (create .lefthook.yml with the project's pre-commit/pre-push
hook definitions) or update the CONTRIBUTING.md text to remove/clarify the
lefthook instructions and note that hooks are opt-in and require manual config;
update references to lefthook in CONTRIBUTING.md and ensure the Gemfile or
README mentions how to obtain/configure the missing .lefthook.yml if you choose
not to commit it.

Comment on lines +117 to +119
unless dry_run
otp_option = ENV['RUBYGEMS_OTP'] ? "--otp #{ENV['RUBYGEMS_OTP']}" : ''
sh_with_retry("gem release #{otp_option}")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

gem-release Ruby gem OTP authentication environment variable

💡 Result:

Use the GEM_HOST_OTP_CODE environment variable (or the --otp flag) to supply a RubyGems OTP when pushing/releases. Example:

GEM_HOST_OTP_CODE=$(ykman oath accounts code -s rubygems.org) gem push mygem-0.1.0.gem

Sources: RubyGems command docs and Gem utilities (env var name) and example usage. [1][2][3]

References

  • RubyGems guides: Using OTP / command reference. [1]
  • Ruby stdlib docs (Gem::GemcutterUtilities add_otp_option shows GEM_HOST_OTP_CODE). [2]
  • Example (YubiKey + env var) blog showing GEM_HOST_OTP_CODE usage. [3]

🌐 Web query:

gem-release gem documentation OTP handling rubygems

💡 Result:

Short answer: gem-release itself doesn’t implement OTP handling — it calls the normal RubyGems push/signin flow, so OTP (2FA) is handled by the rubygems CLI. To release a gem when your account has OTP MFA you must either provide the OTP to the rubygems command (gem push / gem signin) or use an alternative publishing flow (API key / CI tokens / trusted-publisher action). [1][2][5]

Practical options and notes

  • Provide OTP on the command line: recent rubygems supports --otp or the GEM_HOST_OTP_CODE environment variable: e.g. gem push mygem-0.1.0.gem --otp 123456 or GEM_HOST_OTP_CODE=$(oathtool ...) gem push .... Rubygems documents using OTP in the CLI. [1]
  • If your local gem/rubygems is old it may not accept --otp or prompt correctly (known pre-3.1 / pre-3.0 issues). Upgrade rubygems (gem update --system) or use a newer Ruby that ships a modern gem CLI. [1][8]
  • Bundler / rake release: older bundler/release tasks can “freeze” during interactive OTP prompts. Workarounds: type the OTP when prompted, upgrade Bundler to a version that handles prompts (≥ 2.0.2), or fetch and store the API key manually (curl with OTP header) before releasing. [3]
  • Non-interactive CI publishing: use Rubygems’ trusted-publishing / release-gem GitHub Action or store an API key / GitHub OIDC/token in CI to avoid interactive OTP prompts. Also you can configure a hardware TOTP (YubiKey) and script OTP generation. [5][4]
  • Enforcing MFA for a gem: gem owners can mark a gem as requiring MFA for subsequent releases (metadata.rubygems_mfa_required). If a gem requires MFA, owners without MFA cannot push. [6]

If you want, I can:

  • show exact commands for your environment (local CLI, Bundler rake release, or GitHub Actions), or
  • check your rubygems/gem-release versions and recommend specific upgrades.

Use GEM_HOST_OTP_CODE environment variable instead of passing OTP via --otp flag.

The current approach passes OTP as a command-line argument (--otp #{ENV['RUBYGEMS_OTP']}), which is visible in process lists on shared systems. RubyGems supports the more secure GEM_HOST_OTP_CODE environment variable, which is not visible in process listings. Either rely on GEM_HOST_OTP_CODE directly (which gem push reads natively) or avoid the --otp flag entirely when OTP is set via environment variable.

🤖 Prompt for AI Agents
In `@rakelib/release.rake` around lines 117 - 119, The otp handling currently
builds otp_option from ENV['RUBYGEMS_OTP'] and injects it into the gem release
command (otp_option / sh_with_retry and the "gem release" invocation), which
exposes the OTP on the command line; remove the --otp flag usage and instead
ensure GEM_HOST_OTP_CODE is used: if ENV['RUBYGEMS_OTP'] is present, set
ENV['GEM_HOST_OTP_CODE'] = ENV['RUBYGEMS_OTP'] (or prefer reading
ENV['GEM_HOST_OTP_CODE'] directly) and call sh_with_retry("gem release") without
the --otp argument so the OTP is provided via environment rather than process
args.

@justin808
Copy link
Member Author

@claude review this PR.

@claude
Copy link

claude bot commented Jan 20, 2026

Claude finished @justin808's task in 1m 0s —— View job


Code Review Complete ✅

I've reviewed all four changed files in this PR. Overall, this is a solid addition of project infrastructure with well-documented guidelines and automation. Below is my detailed feedback organized by file.


🟢 CONTRIBUTING.md - Excellent with one minor note

Strengths:

  • Clear, well-structured documentation following best practices from Shakapacker/React on Rails
  • Comprehensive coverage of issue reporting, PR workflow, development setup, testing, and linting
  • Good use of code examples and command references
  • Helpful references to the release process

Note:

  • Lefthook configuration exists: I noticed CodeRabbit flagged the lefthook section, but the repository does have a lefthook.yml file with pre-commit and pre-push rubocop hooks. The documentation is accurate. ✅

Minor suggestions:

  • Consider adding a "Code of Conduct" link if one exists
  • The git hooks section correctly marks them as opt-in, which is good practice

🟡 CHANGELOG.md - Good format, missing link definitions

Strengths:

  • Follows keepachangelog.com format correctly
  • Clear version sections with proper date format
  • Well-organized "Added" items for v0.1.0
  • Good contributor guidance at the top

Issue - Missing link definitions:
The bracketed version headers [Unreleased] and [0.1.0] on lines 11 and 17 use reference-style link syntax, but the link definitions are missing at the bottom of the file.

Per keepachangelog convention, add these at the end:

[Unreleased]: https://github.com/shakacode/uber_task/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/shakacode/uber_task/releases/tag/v0.1.0

Fix this →


🟡 rakelib/release.rake - Well-designed with security concern

Strengths:

  • Comprehensive release automation with excellent documentation
  • Good safety checks (uncommitted changes validation)
  • Retry logic for network failures
  • Dry-run mode for testing
  • Environment variable support for configuration
  • Clear logging and user prompts
  • Proper version file reloading after bump

Critical Security Issue - OTP exposure in process arguments:

Lines 117-119: The OTP is passed via command-line argument (--otp #{ENV['RUBYGEMS_OTP']}), which exposes it in process listings on shared systems.

Current code:

otp_option = ENV['RUBYGEMS_OTP'] ? "--otp #{ENV['RUBYGEMS_OTP']}" : ''
sh_with_retry("gem release #{otp_option}")

Recommended fix:
RubyGems natively supports the GEM_HOST_OTP_CODE environment variable. Use it instead:

# Set the environment variable that gem push/release reads
ENV['GEM_HOST_OTP_CODE'] = ENV['RUBYGEMS_OTP'] if ENV['RUBYGEMS_OTP']
sh_with_retry("gem release")  # No --otp flag needed

This keeps the OTP out of the process argument list while maintaining the same functionality.

Fix this →

Additional suggestions (optional improvements):

  1. Lines 27-50 - Helper method location: The log and sh_with_retry helper methods are defined inside the task block. While functional, extracting them to a module would make them testable and reusable:

    module ReleaseHelpers
      def self.log(message, force: false)
        # ...
      end
    end
  2. Lines 85-88 - Redundant require: Line 86's require_relative is unnecessary since line 88's load already reloads the file. The load alone is sufficient.

  3. Line 100 - Git staging specificity: git add -A stages all changes. While the clean git check provides safety, explicitly adding only expected files would be more predictable:

    sh_with_retry("git add lib/uber_task/version.rb Gemfile.lock && git commit -m 'Release v#{new_version}'")
  4. Lines 126-127 - CHANGELOG update timing: Best practice is to update CHANGELOG.md before the release (so it's included in the tagged version), not after. Consider prompting for CHANGELOG updates before committing/tagging, or documenting that maintainers should update it beforehand.


🟢 uber_task.gemspec - Perfect improvement

Strengths:

  • Correctly uses UberTask::VERSION constant instead of hardcoded string
  • Eliminates version duplication
  • Clean implementation with proper require
  • All metadata is properly maintained

No issues found. This is a clean refactor that improves maintainability.


Summary

Approve with minor fixes recommended:

Must fix:

  1. Add missing CHANGELOG.md link definitions (cosmetic but follows convention)
  2. Security: Fix OTP exposure in release.rake (use GEM_HOST_OTP_CODE environment variable)

Optional improvements:

  • Extract release.rake helper methods to module
  • Remove redundant require in release.rake
  • Make git staging more explicit
  • Update CHANGELOG timing guidance

The PR adds valuable infrastructure and documentation. The release automation is particularly well-designed with good safety checks and retry logic. Once the OTP security concern is addressed, this will be ready to merge.

Test plan status:

  • bundle exec rubocop passes
  • bundle exec rspec passes (40 examples, 0 failures)
  • The unchecked manual review items in the test plan should be completed by the maintainer

@justin808 justin808 merged commit 3965334 into main Jan 21, 2026
4 checks passed
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.

3 participants