Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Release Process

This document describes how to release a new version of the `cypress-on-rails` gem.

## Prerequisites

1. Install the `gem-release` gem globally:
```bash
gem install gem-release
```
Comment on lines +7 to +10
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

Verify gem-release installation method.

The documentation suggests installing gem-release globally, but the task description at line 7 of lib/tasks/release.rake states it "is installed via bundle install". This inconsistency could confuse users.

Consider updating the documentation to clarify whether the gem should be installed globally or via Bundler, or document both approaches.

-1. Install the `gem-release` gem globally:
+1. Install the `gem-release` gem (either globally or via your Gemfile):
    ```bash
+   # Option 1: Global installation
    gem install gem-release
+   
+   # Option 2: Add to your Gemfile and run bundle install
+   # gem 'gem-release'
    ```
🤖 Prompt for AI Agents
In docs/RELEASE.md around lines 7 to 10, the instructions conflict with
lib/tasks/release.rake which says gem-release is installed via bundle; update
the docs to be explicit about both supported installation methods (global vs
Bundler) and show the recommended approach: either a single-line note to run
`gem install gem-release` for global use or, preferably, add gem 'gem-release'
to the project's Gemfile and run `bundle install`; ensure the wording clarifies
which method the release task expects (Bundler by default) and give both options
so readers are not confused.


2. Ensure you have write access to the rubygems.org package

3. Set up two-factor authentication (2FA) for RubyGems and have your OTP generator ready

## Release Steps

### 1. Prepare for Release

Ensure your working directory is clean:
```bash
git status
```

If you have uncommitted changes, commit or stash them first.

### 2. Pull Latest Changes

```bash
git pull --rebase
```

### 3. Run the Release Task

To release a specific version:
```bash
rake release[1.19.0]
```

To automatically bump the patch version:
```bash
rake release
```

To perform a dry run (without actually publishing):
```bash
rake release[1.19.0,true]
```

### 4. Enter Your OTP

When prompted, enter your one-time password (OTP) from your authenticator app for RubyGems.

If you get an error during gem publishing, you can run `gem release` manually to retry.

### 5. Update the CHANGELOG

After successfully publishing the gem, update the CHANGELOG:

```bash
bundle exec rake update_changelog
git commit -a -m 'Update CHANGELOG.md'
git push
```

## Version Numbering

Follow [Semantic Versioning](https://semver.org/):

- **Major version** (X.0.0): Breaking changes
- **Minor version** (0.X.0): New features, backwards compatible
- **Patch version** (0.0.X): Bug fixes, backwards compatible
- **Pre-release versions**: Use dot notation, not dashes (e.g., `2.0.0.beta.1`, not `2.0.0-beta.1`)

## What the Release Task Does

The release task automates the following steps:

1. Checks for uncommitted changes (will abort if found)
2. Pulls the latest changes from the repository
3. Bumps the version number in `lib/cypress_on_rails/version.rb`
4. Creates a git commit with the version bump
5. Creates a git tag for the new version
6. Pushes the commit and tag to GitHub
7. Builds the gem
8. Publishes the gem to RubyGems

## Troubleshooting

### Authentication Error

If you get an authentication error with RubyGems:
1. Verify your OTP is correct and current
2. Ensure your RubyGems API key is valid
3. Run `gem release` manually to retry

### Version Already Exists

If the version already exists on RubyGems:
1. Bump to a higher version number
2. Or fix the version in `lib/cypress_on_rails/version.rb` and try again

### Uncommitted Changes Error

If you have uncommitted changes:
1. Review your changes with `git status`
2. Commit them with `git commit -am "Your message"`
3. Or stash them with `git stash`
4. Then retry the release

## Post-Release

After releasing:

1. Announce the release on relevant channels (Slack, forum, etc.)
2. Update any documentation that references version numbers
3. Consider creating a GitHub release with release notes
49 changes: 49 additions & 0 deletions lib/tasks/release.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

desc("Releases the gem using the given version.

IMPORTANT: the gem version must be in valid rubygem format (no dashes).

This task depends on the gem-release ruby gem which is installed via `bundle install`

1st argument: The new version in rubygem format (no dashes). Pass no argument to
automatically perform a patch version bump.
2nd argument: Perform a dry run by passing 'true' as a second argument.

Example: `rake release[1.19.0,false]`")
task :release, %i[gem_version dry_run] do |_t, args|
def sh_in_dir(dir, command)
puts "Running in #{dir}: #{command}"
system("cd #{dir} && #{command}") || raise("Command failed: #{command}")
end
Comment on lines +15 to +18
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Command injection vulnerability.

The sh_in_dir method is vulnerable to command injection because it directly interpolates dir and command into a shell string without sanitization. A malicious directory path or command could escape the intended context.

Apply this diff to use safer command execution:

  def sh_in_dir(dir, command)
    puts "Running in #{dir}: #{command}"
-   system("cd #{dir} && #{command}") || raise("Command failed: #{command}")
+   Dir.chdir(dir) do
+     system(command) || raise("Command failed: #{command}")
+   end
  end

Alternatively, for even better safety, consider using Bundler.with_unbundled_env and splitting commands into arrays when possible.

📝 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
def sh_in_dir(dir, command)
puts "Running in #{dir}: #{command}"
system("cd #{dir} && #{command}") || raise("Command failed: #{command}")
end
def sh_in_dir(dir, command)
puts "Running in #{dir}: #{command}"
Dir.chdir(dir) do
system(command) || raise("Command failed: #{command}")
end
end
🤖 Prompt for AI Agents
In lib/tasks/release.rake around lines 15-18, the sh_in_dir method interpolates
dir and command into a single shell string which allows command injection;
replace the shell interpolation with a safe approach: change to using
Dir.chdir(dir) { success = system(*Shellwords.split(command)) } (require
'shellwords') or use Open3.capture3/system with array arguments and a chdir
option (e.g., Open3.capture3(command_array, chdir: dir)) so no untrusted string
is passed to the shell; also consider wrapping execution with
Bundler.with_unbundled_env where appropriate and raise with a clear error when
the command returns false.


def gem_root
File.expand_path('..', __dir__)
end

# Check if there are uncommitted changes
unless `git status --porcelain`.strip.empty?
raise "You have uncommitted changes. Please commit or stash them before releasing."
end

args_hash = args.to_hash
is_dry_run = args_hash[:dry_run] == 'true'
gem_version = args_hash.fetch(:gem_version, "")

# See https://github.com/svenfuchs/gem-release
sh_in_dir(gem_root, "git pull --rebase")
sh_in_dir(gem_root, "gem bump --no-commit #{%(--version #{gem_version}) unless gem_version.strip.empty?}")

# Release the new gem version
puts "Carefully add your OTP for Rubygems. If you get an error, run 'gem release' again."
sh_in_dir(gem_root, "gem release") unless is_dry_run

msg = <<~MSG
Once you have successfully published, run these commands to update CHANGELOG.md:

bundle exec rake update_changelog
git commit -a -m 'Update CHANGELOG.md'
git push
MSG
puts msg
end