bake-gem 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/agent.md +61 -0
- data/bake/gem/release/branch.rb +2 -8
- data/bake/gem/release/version.rb +1 -6
- data/bake/gem/release.rb +35 -0
- data/bake/gem.rb +10 -31
- data/context/getting-started.md +228 -0
- data/context/index.yaml +13 -0
- data/lib/bake/gem/helper.rb +165 -5
- data/lib/bake/gem/shell.rb +35 -2
- data/lib/bake/gem/version.rb +1 -1
- data/lib/bake/gem.rb +7 -0
- data/readme.md +19 -1
- data/releases.md +18 -0
- data.tar.gz.sig +0 -0
- metadata +5 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b25175c2d6cf7d1761d1e907fcd305627d7d8aeb394877b02133ebb4b04ce83
|
4
|
+
data.tar.gz: dc59a56876e85654723fa2b1e600078f99f9c70b34b4e7538796788f9d34f3c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53bbab1dc7b1ce0ee92b176624a9a0032ffebd2a482c13477187c6e03965871f1f95c132e84aad5f0361930dc32cd3f2827df5e36cdb5a4e8f405ba8faa17198
|
7
|
+
data.tar.gz: 6af3f18bee98f04af4c700c4282873eec88d927945e43176c9a24cff5dbc78eb3f88097d817e43373243c51894c41c4a41d855a949a343d827ef9ecd908fca5b
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/agent.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Agent
|
2
|
+
|
3
|
+
## Context
|
4
|
+
|
5
|
+
This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`.
|
6
|
+
|
7
|
+
**Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**
|
8
|
+
|
9
|
+
**Setup Instructions:** If the referenced files are not present or if dependencies have been updated, run `bake agent:context:install` to install the latest context files.
|
10
|
+
|
11
|
+
### agent-context
|
12
|
+
|
13
|
+
Install and manage context files from Ruby gems.
|
14
|
+
|
15
|
+
#### [Getting Started](.context/agent-context/getting-started.md)
|
16
|
+
|
17
|
+
This guide explains how to use `agent-context`, a tool for discovering and installing contextual information from Ruby gems to help AI agents.
|
18
|
+
|
19
|
+
### decode
|
20
|
+
|
21
|
+
Code analysis for documentation generation.
|
22
|
+
|
23
|
+
#### [Getting Started with Decode](.context/decode/getting-started.md)
|
24
|
+
|
25
|
+
The Decode gem provides programmatic access to Ruby code structure and metadata. It can parse Ruby files and extract definitions, comments, and documentation pragmas, enabling code analysis, documentation generation, and other programmatic manipulations of Ruby codebases.
|
26
|
+
|
27
|
+
#### [Documentation Coverage](.context/decode/coverage.md)
|
28
|
+
|
29
|
+
This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
|
30
|
+
|
31
|
+
#### [Ruby Documentation](.context/decode/ruby-documentation.md)
|
32
|
+
|
33
|
+
This guide covers documentation practices and pragmas supported by the Decode gem for documenting Ruby code. These pragmas provide structured documentation that can be parsed and used to generate API documentation and achieve complete documentation coverage.
|
34
|
+
|
35
|
+
#### [Setting Up RBS Types and Steep Type Checking for Ruby Gems](.context/decode/types.md)
|
36
|
+
|
37
|
+
This guide covers the process for establishing robust type checking in Ruby gems using RBS and Steep, focusing on automated generation from source documentation and proper validation.
|
38
|
+
|
39
|
+
### sus
|
40
|
+
|
41
|
+
A fast and scalable test runner.
|
42
|
+
|
43
|
+
#### [Using Sus Testing Framework](.context/sus/usage.md)
|
44
|
+
|
45
|
+
Sus is a modern Ruby testing framework that provides a clean, BDD-style syntax for writing tests. It's designed to be fast, simple, and expressive.
|
46
|
+
|
47
|
+
#### [Mocking](.context/sus/mocking.md)
|
48
|
+
|
49
|
+
There are two types of mocking in sus: `receive` and `mock`. The `receive` matcher is a subset of full mocking and is used to set expectations on method calls, while `mock` can be used to replace method implementations or set up more complex behavior.
|
50
|
+
|
51
|
+
#### [Shared Test Behaviors and Fixtures](.context/sus/shared.md)
|
52
|
+
|
53
|
+
Sus provides shared test contexts which can be used to define common behaviours or tests that can be reused across one or more test files.
|
54
|
+
|
55
|
+
### sus-fixtures-console
|
56
|
+
|
57
|
+
Test fixtures for capturing Console output.
|
58
|
+
|
59
|
+
#### [Getting Started](.context/sus-fixtures-console/getting-started.md)
|
60
|
+
|
61
|
+
This guide explains how to use the `Sus::Fixtures::Console` gem to redirect console logging output during tests.
|
data/bake/gem/release/branch.rb
CHANGED
@@ -3,10 +3,6 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative "../../../lib/bake/gem/shell"
|
7
|
-
|
8
|
-
include Bake::Gem::Shell
|
9
|
-
|
10
6
|
# Increment the patch number of the current version.
|
11
7
|
def patch
|
12
8
|
commit([nil, nil, 1], message: "Bump patch version.")
|
@@ -36,9 +32,7 @@ def commit(bump, message: "Bump version.")
|
|
36
32
|
version_path = context.lookup("gem:release:version:increment").call(bump, message: message)
|
37
33
|
|
38
34
|
if version_path
|
39
|
-
|
40
|
-
system("git", "add", version_path, chdir: context.root)
|
41
|
-
system("git", "commit", "-m", message, chdir: context.root)
|
35
|
+
branch_name = helper.create_release_branch(version_path, message: message)
|
42
36
|
else
|
43
37
|
raise "Could not find version number!"
|
44
38
|
end
|
@@ -46,6 +40,6 @@ def commit(bump, message: "Bump version.")
|
|
46
40
|
return {
|
47
41
|
version: gemspec.version,
|
48
42
|
version_path: version_path,
|
49
|
-
branch:
|
43
|
+
branch: branch_name,
|
50
44
|
}
|
51
45
|
end
|
data/bake/gem/release/version.rb
CHANGED
@@ -3,10 +3,6 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative "../../../lib/bake/gem/shell"
|
7
|
-
|
8
|
-
include Bake::Gem::Shell
|
9
|
-
|
10
6
|
# Increment the patch number of the current version.
|
11
7
|
def patch
|
12
8
|
commit([nil, nil, 1], message: "Bump patch version.")
|
@@ -59,8 +55,7 @@ def commit(bump, message: "Bump version.")
|
|
59
55
|
version_path = increment(bump, message: message)
|
60
56
|
|
61
57
|
if version_path
|
62
|
-
|
63
|
-
system("git", "commit", "-m", message, chdir: context.root)
|
58
|
+
helper.commit_version_changes(message: message)
|
64
59
|
else
|
65
60
|
raise "Could not find version number!"
|
66
61
|
end
|
data/bake/gem/release.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Released under the MIT License.
|
5
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
6
|
+
|
7
|
+
# Bump the patch version and release the gem in one command.
|
8
|
+
# @parameter tag [Boolean] Whether to tag the release.
|
9
|
+
def patch(tag: true)
|
10
|
+
version_commit_task = context.lookup("gem:release:version:commit")
|
11
|
+
version_commit_task.call([nil, nil, 1], message: "Bump patch version.")
|
12
|
+
|
13
|
+
release_task = context.lookup("gem:release")
|
14
|
+
release_task.call(tag: tag)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Bump the minor version and release the gem in one command.
|
18
|
+
# @parameter tag [Boolean] Whether to tag the release.
|
19
|
+
def minor(tag: true)
|
20
|
+
version_commit_task = context.lookup("gem:release:version:commit")
|
21
|
+
version_commit_task.call([nil, 1, 0], message: "Bump minor version.")
|
22
|
+
|
23
|
+
release_task = context.lookup("gem:release")
|
24
|
+
release_task.call(tag: tag)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Bump the major version and release the gem in one command.
|
28
|
+
# @parameter tag [Boolean] Whether to tag the release.
|
29
|
+
def major(tag: true)
|
30
|
+
version_commit_task = context.lookup("gem:release:version:commit")
|
31
|
+
version_commit_task.call([1, 0, 0], message: "Bump major version.")
|
32
|
+
|
33
|
+
release_task = context.lookup("gem:release")
|
34
|
+
release_task.call(tag: tag)
|
35
|
+
end
|
data/bake/gem.rb
CHANGED
@@ -3,14 +3,13 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
include Bake::Gem::Shell
|
10
|
-
|
6
|
+
# Initialize the gem context with helper for gem operations.
|
7
|
+
# @parameter context [Bake::Context] The bake execution context.
|
11
8
|
def initialize(context)
|
12
9
|
super(context)
|
13
10
|
|
11
|
+
require_relative "../lib/bake/gem/helper"
|
12
|
+
|
14
13
|
@helper = Bake::Gem::Helper.new(context.root)
|
15
14
|
end
|
16
15
|
|
@@ -52,44 +51,24 @@ def release(tag: true)
|
|
52
51
|
@helper.guard_clean
|
53
52
|
|
54
53
|
version = @helper.gemspec.version
|
54
|
+
current_branch = @helper.current_branch
|
55
55
|
|
56
|
-
|
57
|
-
name = "v#{version}"
|
58
|
-
system("git", "fetch", "--all", "--tags")
|
59
|
-
system("git", "tag", name)
|
60
|
-
end
|
56
|
+
tag_name = @helper.create_release_tag(tag: tag, version: version)
|
61
57
|
|
62
58
|
begin
|
63
|
-
path = @helper.
|
59
|
+
path = @helper.build_gem_in_worktree
|
64
60
|
@helper.push_gem(path: path)
|
65
61
|
rescue => error
|
66
|
-
|
62
|
+
@helper.delete_git_tag(tag_name) if tag_name
|
67
63
|
raise
|
68
64
|
end
|
69
65
|
|
70
|
-
|
71
|
-
if current_branch
|
72
|
-
system("git", "push")
|
73
|
-
end
|
74
|
-
|
75
|
-
system("git", "push", "--tags")
|
66
|
+
@helper.push_release(current_branch: current_branch)
|
76
67
|
|
77
68
|
return {
|
78
69
|
name: @helper.gemspec.name,
|
79
70
|
version: @helper.gemspec.version,
|
80
71
|
package_path: path,
|
81
|
-
tag:
|
72
|
+
tag: tag_name,
|
82
73
|
}
|
83
74
|
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# Figure out if there is a current branch, if not, return `nil`.
|
88
|
-
def current_branch
|
89
|
-
# We originally used this but it is not supported by older versions of git.
|
90
|
-
# readlines("git", "branch", "--show-current").first&.chomp
|
91
|
-
|
92
|
-
readlines("git", "symbolic-ref", "--short", "--quiet", "HEAD").first&.chomp
|
93
|
-
rescue
|
94
|
-
nil
|
95
|
-
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
This guide explains how to use `bake-gem` to release gems safely and efficiently.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the `bake-gem` gem to your project:
|
8
|
+
|
9
|
+
``` bash
|
10
|
+
$ bundle add bake-gem
|
11
|
+
```
|
12
|
+
|
13
|
+
You may prefer to keep it in a separate `maintenance` group:
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
group :maintenance, optional: true do
|
17
|
+
gem "bake-gem"
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Before using `bake-gem`, ensure you have:
|
24
|
+
|
25
|
+
1. A properly configured `gemspec` file in your project root
|
26
|
+
2. A clean git repository (no uncommitted changes)
|
27
|
+
3. Your gem's version file (typically `lib/your_gem/version.rb`)
|
28
|
+
4. RubyGems credentials configured for publishing
|
29
|
+
|
30
|
+
### Local Release Process
|
31
|
+
|
32
|
+
The most typical process for releasing a gem locally:
|
33
|
+
|
34
|
+
``` bash
|
35
|
+
$ bake gem:release:version:patch gem:release
|
36
|
+
```
|
37
|
+
|
38
|
+
This command will:
|
39
|
+
1. **Guard against consecutive version bumps** - Prevents accidentally bumping version twice
|
40
|
+
2. **Check repository cleanliness** - Ensures no uncommitted changes
|
41
|
+
3. **Increment the version** - Updates your version file (patch/minor/major)
|
42
|
+
4. **Commit the version change** - Creates a commit with the version bump
|
43
|
+
5. **Build the gem in a clean worktree** - Isolates the build process
|
44
|
+
6. **Push to RubyGems** - Publishes your gem
|
45
|
+
7. **Create and push git tags** - Tags the release
|
46
|
+
|
47
|
+
### Version Increment Options
|
48
|
+
|
49
|
+
Choose the appropriate version increment:
|
50
|
+
|
51
|
+
``` bash
|
52
|
+
# For bug fixes (1.0.0 -> 1.0.1)
|
53
|
+
$ bake gem:release:version:patch gem:release
|
54
|
+
|
55
|
+
# For new features (1.0.0 -> 1.1.0)
|
56
|
+
$ bake gem:release:version:minor gem:release
|
57
|
+
|
58
|
+
# For breaking changes (1.0.0 -> 2.0.0)
|
59
|
+
$ bake gem:release:version:major gem:release
|
60
|
+
```
|
61
|
+
|
62
|
+
## Advanced Workflows
|
63
|
+
|
64
|
+
### Automated CI/CD Pipeline
|
65
|
+
|
66
|
+
For releasing gems via automated pipelines, use a two-step process:
|
67
|
+
|
68
|
+
#### Step 1: Create Release Branch (Locally)
|
69
|
+
|
70
|
+
``` bash
|
71
|
+
# Create a release branch with version bump
|
72
|
+
$ bake gem:release:branch:patch # or minor/major
|
73
|
+
```
|
74
|
+
|
75
|
+
This will:
|
76
|
+
- Create a new branch named `releases/v[new-version]`
|
77
|
+
- Bump the gem version
|
78
|
+
- Commit the version change
|
79
|
+
- Push the branch to origin
|
80
|
+
|
81
|
+
#### Step 2: Release from CI (After Merge)
|
82
|
+
|
83
|
+
Once the release branch is merged into main:
|
84
|
+
|
85
|
+
``` bash
|
86
|
+
$ export RUBYGEMS_HOST=https://rubygems.org
|
87
|
+
$ export GEM_HOST_API_KEY=your_api_key
|
88
|
+
|
89
|
+
$ bake gem:release
|
90
|
+
```
|
91
|
+
|
92
|
+
### Individual Commands
|
93
|
+
|
94
|
+
You can also run individual steps:
|
95
|
+
|
96
|
+
``` bash
|
97
|
+
# Just build the gem
|
98
|
+
$ bake gem:build
|
99
|
+
|
100
|
+
# Install the gem locally for testing
|
101
|
+
$ bake gem:install
|
102
|
+
|
103
|
+
# List files that will be included in the gem
|
104
|
+
$ bake gem:files
|
105
|
+
|
106
|
+
# Build without signing
|
107
|
+
$ bake gem:build signing_key=false
|
108
|
+
```
|
109
|
+
|
110
|
+
## Safety Features
|
111
|
+
|
112
|
+
`bake-gem` includes several safety features:
|
113
|
+
|
114
|
+
### Consecutive Version Bump Prevention
|
115
|
+
The tool automatically prevents consecutive version bumps by checking the last commit message. If the last commit was already a version bump (e.g., "Bump patch version."), it will raise an error.
|
116
|
+
|
117
|
+
### Clean Worktree Building
|
118
|
+
Gems are built in isolated git worktrees to ensure the build environment exactly matches your committed code, preventing issues with uncommitted changes affecting the build.
|
119
|
+
|
120
|
+
### Repository Cleanliness Check
|
121
|
+
Before any release operation, the tool ensures your repository has no uncommitted changes.
|
122
|
+
|
123
|
+
## Configuration
|
124
|
+
|
125
|
+
### Gem Signing
|
126
|
+
|
127
|
+
To sign your gems, ensure your gemspec includes:
|
128
|
+
|
129
|
+
``` ruby
|
130
|
+
spec.signing_key = "path/to/private_key.pem"
|
131
|
+
spec.cert_chain = ["path/to/certificate.pem"]
|
132
|
+
```
|
133
|
+
|
134
|
+
Or disable signing explicitly:
|
135
|
+
|
136
|
+
``` bash
|
137
|
+
$ bake gem:build signing_key=false
|
138
|
+
```
|
139
|
+
|
140
|
+
### RubyGems Configuration
|
141
|
+
|
142
|
+
For automated releases, set these environment variables:
|
143
|
+
|
144
|
+
``` bash
|
145
|
+
export RUBYGEMS_HOST=https://rubygems.org # or your private gem server
|
146
|
+
export GEM_HOST_API_KEY=your_api_key
|
147
|
+
```
|
148
|
+
|
149
|
+
## Examples
|
150
|
+
|
151
|
+
### Complete Release Example
|
152
|
+
|
153
|
+
``` bash
|
154
|
+
# 1. Ensure clean repository
|
155
|
+
$ git status
|
156
|
+
|
157
|
+
# 2. Run tests
|
158
|
+
$ bundle exec rake test # or your test command
|
159
|
+
|
160
|
+
# 3. Release with patch version increment
|
161
|
+
$ bake gem:release:version:patch gem:release
|
162
|
+
|
163
|
+
# Output:
|
164
|
+
# Updated version: v1.2.4
|
165
|
+
# Successfully built RubyGem
|
166
|
+
# Name: my-gem
|
167
|
+
# Version: 1.2.4
|
168
|
+
# File: my-gem-1.2.4.gem
|
169
|
+
# Pushing gem to https://rubygems.org...
|
170
|
+
# Tagged: v1.2.4
|
171
|
+
```
|
172
|
+
|
173
|
+
### Branch-based Release Example
|
174
|
+
|
175
|
+
``` bash
|
176
|
+
# Create release branch
|
177
|
+
$ bake gem:release:branch:minor
|
178
|
+
# Creates branch: releases/v1.3.0
|
179
|
+
# Commits version bump
|
180
|
+
# Pushes branch
|
181
|
+
|
182
|
+
# After code review and merge:
|
183
|
+
$ git checkout main
|
184
|
+
$ git pull
|
185
|
+
$ bake gem:release
|
186
|
+
```
|
187
|
+
|
188
|
+
## Troubleshooting
|
189
|
+
|
190
|
+
### Common Issues
|
191
|
+
|
192
|
+
**"Repository has uncommitted changes"**
|
193
|
+
```bash
|
194
|
+
$ git status # Check what's uncommitted
|
195
|
+
$ git add . # Stage changes
|
196
|
+
$ git commit -m "Prepare for release" # Or stash them
|
197
|
+
```
|
198
|
+
|
199
|
+
**"Last commit appears to be a version bump"**
|
200
|
+
```bash
|
201
|
+
# Make some changes first, or use --force if intentional
|
202
|
+
$ git log -1 # Check the last commit message
|
203
|
+
```
|
204
|
+
|
205
|
+
**"Multiple gemspecs found"**
|
206
|
+
```bash
|
207
|
+
# Specify which gemspec to use or remove extras
|
208
|
+
$ ls *.gemspec
|
209
|
+
```
|
210
|
+
|
211
|
+
**"No version file found"**
|
212
|
+
```bash
|
213
|
+
# Ensure your version file follows the expected pattern:
|
214
|
+
# VERSION = "1.0.0"
|
215
|
+
```
|
216
|
+
|
217
|
+
### Getting Help
|
218
|
+
|
219
|
+
List all available gem commands:
|
220
|
+
``` bash
|
221
|
+
$ bake list | grep gem
|
222
|
+
```
|
223
|
+
|
224
|
+
Get help for specific commands:
|
225
|
+
``` bash
|
226
|
+
$ bake gem:release --help
|
227
|
+
```
|
228
|
+
````
|
data/context/index.yaml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
3
|
+
---
|
4
|
+
description: Release management for Ruby gems.
|
5
|
+
metadata:
|
6
|
+
documentation_uri: https://ioquatix.github.io/bake-gem/
|
7
|
+
funding_uri: https://github.com/sponsors/ioquatix/
|
8
|
+
source_code_uri: https://github.com/ioquatix/bake-gem.git
|
9
|
+
files:
|
10
|
+
- path: getting-started.md
|
11
|
+
title: Getting Started
|
12
|
+
description: This guide explains how to use `bake-gem` to release gems safely and
|
13
|
+
efficiently.
|
data/lib/bake/gem/helper.rb
CHANGED
@@ -6,11 +6,13 @@
|
|
6
6
|
require "rubygems"
|
7
7
|
require "rubygems/package"
|
8
8
|
require "fileutils"
|
9
|
+
require "tmpdir"
|
9
10
|
|
10
11
|
require_relative "shell"
|
11
12
|
|
12
13
|
module Bake
|
13
14
|
module Gem
|
15
|
+
# Represents a gem version with support for parsing and incrementing version numbers.
|
14
16
|
class Version
|
15
17
|
LINE_PATTERN = /VERSION = ['"](?<version>(?<parts>\d+\.\d+\.\d+)(-(?<suffix>.*?))?)['"]/
|
16
18
|
|
@@ -28,11 +30,16 @@ module Bake
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
33
|
+
# Initialize a new version with the given parts and optional suffix.
|
34
|
+
# @parameter parts [Array(Integer)] The version number parts (e.g., [1, 2, 3] for "1.2.3").
|
35
|
+
# @parameter suffix [String | Nil] The optional version suffix (e.g., "alpha", "beta").
|
31
36
|
def initialize(parts, suffix)
|
32
37
|
@parts = parts
|
33
38
|
@suffix = suffix
|
34
39
|
end
|
35
40
|
|
41
|
+
# Check if this version represents a release version.
|
42
|
+
# @returns [Boolean] True if the version has no suffix, indicating it's a release version.
|
36
43
|
def release?
|
37
44
|
@suffix.nil?
|
38
45
|
end
|
@@ -51,10 +58,13 @@ module Bake
|
|
51
58
|
"v#{join}"
|
52
59
|
end
|
53
60
|
|
61
|
+
# Increment the version according to the provided bump specification.
|
62
|
+
# @parameter bump [Array(Integer | Nil)] Array specifying how to increment each version part.
|
63
|
+
# @returns [Version] Self, for method chaining.
|
54
64
|
def increment(bump)
|
55
65
|
bump.each_with_index do |increment, index|
|
56
66
|
if index > @parts.size
|
57
|
-
@suffix = bump[index
|
67
|
+
@suffix = bump[index..].join(".")
|
58
68
|
break
|
59
69
|
end
|
60
70
|
|
@@ -63,30 +73,49 @@ module Bake
|
|
63
73
|
elsif increment == 0
|
64
74
|
@parts[index] = 0
|
65
75
|
end
|
76
|
+
# If increment is nil, we don't change that part of the version
|
66
77
|
end
|
67
78
|
|
68
79
|
return self
|
69
80
|
end
|
70
81
|
end
|
71
82
|
|
83
|
+
# Helper class for performing gem-related operations like building, installing, and publishing gems.
|
72
84
|
class Helper
|
73
85
|
include Shell
|
74
86
|
|
87
|
+
# Initialize a new helper with the specified root directory and optional gemspec.
|
88
|
+
# @parameter root [String] The root directory of the gem project.
|
89
|
+
# @parameter gemspec [Gem::Specification | Nil] The gemspec to use, or nil to find it automatically.
|
75
90
|
def initialize(root = Dir.pwd, gemspec: nil)
|
76
91
|
@root = root
|
77
92
|
@gemspec = gemspec || find_gemspec
|
78
93
|
end
|
79
94
|
|
95
|
+
# @attribute [String] The root directory of the gem project.
|
80
96
|
attr :root
|
97
|
+
|
98
|
+
# @attribute [Gem::Specification] The gemspec for the gem.
|
81
99
|
attr :gemspec
|
82
100
|
|
101
|
+
# Find the path to the version.rb file in the gem.
|
102
|
+
# @returns [String | Nil] The path to the version file, or nil if not found.
|
83
103
|
def version_path
|
84
|
-
@gemspec
|
104
|
+
if @gemspec
|
105
|
+
@gemspec.files.grep(/lib(.*?)\/version.rb/).first
|
106
|
+
end
|
85
107
|
end
|
86
108
|
|
109
|
+
# Update the version number in the version file according to the bump specification.
|
110
|
+
# @parameter bump [Array(Integer)] Array specifying how to increment each version part.
|
111
|
+
# @parameter version_path [String] The path to the version file.
|
112
|
+
# @returns [String | Boolean] The path to the version file if updated, or false if no version file found.
|
87
113
|
def update_version(bump, version_path = self.version_path)
|
88
114
|
return false unless version_path
|
89
115
|
|
116
|
+
# Guard against consecutive version bumps
|
117
|
+
guard_last_commit_not_version_bump
|
118
|
+
|
90
119
|
lines = File.readlines(version_path)
|
91
120
|
new_version = nil
|
92
121
|
|
@@ -107,6 +136,9 @@ module Bake
|
|
107
136
|
end
|
108
137
|
end
|
109
138
|
|
139
|
+
# Verify that the repository has no uncommitted changes.
|
140
|
+
# @returns [Boolean] True if the repository is clean.
|
141
|
+
# @raises [RuntimeError] If there are uncommitted changes in the repository.
|
110
142
|
def guard_clean
|
111
143
|
lines = readlines("git", "status", "--porcelain", chdir: @root)
|
112
144
|
|
@@ -117,14 +149,37 @@ module Bake
|
|
117
149
|
return true
|
118
150
|
end
|
119
151
|
|
152
|
+
# Verify that the last commit was not a version bump.
|
153
|
+
# @returns [Boolean] True if the last commit was not a version bump.
|
154
|
+
# @raises [RuntimeError] If the last commit was a version bump.
|
155
|
+
def guard_last_commit_not_version_bump
|
156
|
+
# Get the last commit message:
|
157
|
+
begin
|
158
|
+
last_commit_message = readlines("git", "log", "-1", "--pretty=format:%s", chdir: @root).first&.strip
|
159
|
+
rescue CommandExecutionError => error
|
160
|
+
# If git log fails (e.g., no commits yet), skip the check:
|
161
|
+
if error.exit_code == 128
|
162
|
+
return true
|
163
|
+
else
|
164
|
+
raise
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
if last_commit_message && last_commit_message.match?(/^Bump (patch|minor|major|version)( version)?\.?$/i)
|
169
|
+
raise "Last commit appears to be a version bump: #{last_commit_message.inspect}. Cannot bump version consecutively."
|
170
|
+
end
|
171
|
+
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
120
175
|
# @parameter root [String] The root path for package files.
|
121
176
|
# @parameter signing_key [String | Nil] The signing key to use for signing the package.
|
122
177
|
# @returns [String] The path to the built gem package.
|
123
178
|
def build_gem(root: "pkg", signing_key: nil)
|
124
179
|
# Ensure the output directory exists:
|
125
|
-
FileUtils.mkdir_p(
|
180
|
+
FileUtils.mkdir_p(root)
|
126
181
|
|
127
|
-
output_path = File.join(
|
182
|
+
output_path = File.join(root, @gemspec.file_name)
|
128
183
|
|
129
184
|
if signing_key == false
|
130
185
|
@gemspec.signing_key = nil
|
@@ -137,14 +192,119 @@ module Bake
|
|
137
192
|
::Gem::Package.build(@gemspec, false, false, output_path)
|
138
193
|
end
|
139
194
|
|
195
|
+
# Install the gem using the `gem install` command.
|
196
|
+
# @parameter arguments [Array] Additional arguments to pass to `gem install`.
|
197
|
+
# @parameter path [String] The path to the gem file to install.
|
140
198
|
def install_gem(*arguments, path: @gemspec.file_name)
|
141
199
|
system("gem", "install", path, *arguments)
|
142
200
|
end
|
143
201
|
|
202
|
+
# Push the gem to a gem repository using the `gem push` command.
|
203
|
+
# @parameter arguments [Array] Additional arguments to pass to `gem push`.
|
204
|
+
# @parameter path [String] The path to the gem file to push.
|
144
205
|
def push_gem(*arguments, path: @gemspec.file_name)
|
145
206
|
system("gem", "push", path, *arguments)
|
146
207
|
end
|
147
208
|
|
209
|
+
# Build the gem in a clean worktree for better isolation
|
210
|
+
# @parameter root [String] The root path for package files.
|
211
|
+
# @parameter signing_key [String | Nil] The signing key to use for signing the package.
|
212
|
+
# @returns [String] The path to the built gem package.
|
213
|
+
def build_gem_in_worktree(root: "pkg", signing_key: nil)
|
214
|
+
original_pkg_path = File.join(@root, root)
|
215
|
+
|
216
|
+
# Create a unique temporary path for the worktree
|
217
|
+
timestamp = Time.now.strftime("%Y%m%d-%H%M%S-%N")
|
218
|
+
worktree_path = File.join(Dir.tmpdir, "bake-gem-build-#{timestamp}")
|
219
|
+
|
220
|
+
begin
|
221
|
+
# Create worktree from current HEAD
|
222
|
+
unless system("git", "worktree", "add", worktree_path, "HEAD", chdir: @root)
|
223
|
+
raise "Failed to create git worktree. Make sure you have at least one commit in the repository."
|
224
|
+
end
|
225
|
+
|
226
|
+
# Create helper for the worktree
|
227
|
+
worktree_helper = self.class.new(worktree_path)
|
228
|
+
|
229
|
+
# Build gem directly into the target pkg directory
|
230
|
+
output_path = worktree_helper.build_gem(root: original_pkg_path, signing_key: signing_key)
|
231
|
+
|
232
|
+
output_path
|
233
|
+
ensure
|
234
|
+
# Clean up the worktree
|
235
|
+
system("git", "worktree", "remove", worktree_path, "--force", chdir: @root)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Create a release branch, add the version file, and commit the changes.
|
240
|
+
# @parameter version_path [String] The path to the version file that was updated.
|
241
|
+
# @parameter message [String] The commit message to use.
|
242
|
+
# @returns [String] The name of the created branch.
|
243
|
+
def create_release_branch(version_path, message: "Bump version.")
|
244
|
+
branch_name = "release-v#{@gemspec.version}"
|
245
|
+
|
246
|
+
system("git", "checkout", "-b", branch_name, chdir: @root)
|
247
|
+
system("git", "add", version_path, chdir: @root)
|
248
|
+
system("git", "commit", "-m", message, chdir: @root)
|
249
|
+
|
250
|
+
return branch_name
|
251
|
+
end
|
252
|
+
|
253
|
+
# Commit version changes to the current branch.
|
254
|
+
# @parameter message [String] The commit message to use.
|
255
|
+
def commit_version_changes(message: "Bump version.")
|
256
|
+
system("git", "add", "--all", chdir: @root)
|
257
|
+
system("git", "commit", "-m", message, chdir: @root)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Fetch remote tags and create a release tag for the specified version.
|
261
|
+
# @parameter tag [Boolean] Whether to tag the release.
|
262
|
+
# @parameter version [String] The version to tag.
|
263
|
+
# @returns [String | Nil] The tag name if created, nil otherwise.
|
264
|
+
def create_release_tag(tag: true, version:)
|
265
|
+
tag_name = nil
|
266
|
+
|
267
|
+
if tag
|
268
|
+
tag_name = "v#{version}"
|
269
|
+
system("git", "fetch", "--all", "--tags", chdir: @root)
|
270
|
+
system("git", "tag", tag_name, chdir: @root)
|
271
|
+
end
|
272
|
+
|
273
|
+
return tag_name
|
274
|
+
end
|
275
|
+
|
276
|
+
# Delete a git tag.
|
277
|
+
# @parameter tag_name [String] The name of the tag to delete.
|
278
|
+
def delete_git_tag(tag_name)
|
279
|
+
system("git", "tag", "--delete", tag_name, chdir: @root)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Push changes and tags to the remote repository.
|
283
|
+
# @parameter current_branch [String | Nil] The current branch name, or nil if not on a branch.
|
284
|
+
def push_release(current_branch: nil)
|
285
|
+
# If we are on a branch, push, otherwise just push the tags (assuming shallow checkout):
|
286
|
+
if current_branch
|
287
|
+
system("git", "push", chdir: @root)
|
288
|
+
end
|
289
|
+
|
290
|
+
system("git", "push", "--tags", chdir: @root)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Figure out if there is a current branch, if not, return `nil`.
|
294
|
+
# @returns [String | Nil] The current branch name, or nil if not on a branch.
|
295
|
+
def current_branch
|
296
|
+
# We originally used this but it is not supported by older versions of git.
|
297
|
+
# readlines("git", "branch", "--show-current").first&.chomp
|
298
|
+
|
299
|
+
readlines("git", "symbolic-ref", "--short", "--quiet", "HEAD", chdir: @root).first&.chomp
|
300
|
+
rescue CommandExecutionError
|
301
|
+
nil
|
302
|
+
end
|
303
|
+
|
304
|
+
# Find a gemspec file in the root directory.
|
305
|
+
# @parameter glob [String] The glob pattern to use for finding gemspec files.
|
306
|
+
# @returns [Gem::Specification | Nil] The loaded gemspec, or nil if none found.
|
307
|
+
# @raises [RuntimeError] If multiple gemspec files are found.
|
148
308
|
def find_gemspec(glob = "*.gemspec")
|
149
309
|
paths = Dir.glob(glob, base: @root).sort
|
150
310
|
|
@@ -153,7 +313,7 @@ module Bake
|
|
153
313
|
end
|
154
314
|
|
155
315
|
if path = paths.first
|
156
|
-
return ::Gem::Specification.load(path)
|
316
|
+
return ::Gem::Specification.load(File.expand_path(path, @root))
|
157
317
|
end
|
158
318
|
end
|
159
319
|
end
|
data/lib/bake/gem/shell.rb
CHANGED
@@ -8,7 +8,29 @@ require "console/event/spawn"
|
|
8
8
|
|
9
9
|
module Bake
|
10
10
|
module Gem
|
11
|
+
# Exception raised when a command execution fails.
|
12
|
+
class CommandExecutionError < RuntimeError
|
13
|
+
def initialize(message, status)
|
14
|
+
super(message)
|
15
|
+
@status = status
|
16
|
+
end
|
17
|
+
|
18
|
+
# @attribute [Process::Status] The status object of the failed command.
|
19
|
+
attr_reader :status
|
20
|
+
|
21
|
+
# Helper method for convenience.
|
22
|
+
def exit_code
|
23
|
+
@status.exitstatus
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Provides shell command execution methods with proper logging and error handling.
|
11
28
|
module Shell
|
29
|
+
# Execute a system command with logging and error handling.
|
30
|
+
# @parameter arguments [Array] The command and its arguments to execute.
|
31
|
+
# @parameter options [Hash] Additional options to pass to Process.spawn.
|
32
|
+
# @returns [Boolean] True if the command executed successfully.
|
33
|
+
# @raises [CommandExecutionError] If the command fails.
|
12
34
|
def system(*arguments, **options)
|
13
35
|
Console::Event::Spawn.for(*arguments, **options).emit(self)
|
14
36
|
|
@@ -19,13 +41,19 @@ module Bake
|
|
19
41
|
pid, status = Process.wait2(pid) if pid
|
20
42
|
|
21
43
|
unless status.success?
|
22
|
-
raise "Failed to execute #{arguments}: #{status}!"
|
44
|
+
raise Bake::Gem::CommandExecutionError.new("Failed to execute #{arguments}: #{status}!", status)
|
23
45
|
end
|
24
46
|
|
25
47
|
return true
|
26
48
|
end
|
27
49
|
end
|
28
50
|
|
51
|
+
# Execute a command and yield its output to a block.
|
52
|
+
# @parameter arguments [Array] The command and its arguments to execute.
|
53
|
+
# @parameter options [Hash] Additional options to pass to Process.spawn.
|
54
|
+
# @yields {|input| ...} The input stream from the executed command.
|
55
|
+
# @returns [Object] The return value of the block.
|
56
|
+
# @raises [CommandExecutionError] If the command fails.
|
29
57
|
def execute(*arguments, **options)
|
30
58
|
Console::Event::Spawn.for(*arguments, **options).emit(self)
|
31
59
|
|
@@ -39,12 +67,17 @@ module Bake
|
|
39
67
|
pid, status = Process.wait2(pid)
|
40
68
|
|
41
69
|
unless status.success?
|
42
|
-
raise "Failed to execute #{arguments}: #{status}!"
|
70
|
+
raise Bake::Gem::CommandExecutionError.new("Failed to execute #{arguments}: #{status}!", status)
|
43
71
|
end
|
44
72
|
end
|
45
73
|
end
|
46
74
|
end
|
47
75
|
|
76
|
+
# Execute a command and return its output as an array of lines.
|
77
|
+
# @parameter arguments [Array] The command and its arguments to execute.
|
78
|
+
# @parameter options [Hash] Additional options to pass to Process.spawn.
|
79
|
+
# @returns [Array(String)] The output lines from the executed command.
|
80
|
+
# @raises [CommandExecutionError] If the command fails.
|
48
81
|
def readlines(*arguments, **options)
|
49
82
|
execute(*arguments, **options) do |output|
|
50
83
|
return output.readlines
|
data/lib/bake/gem/version.rb
CHANGED
data/lib/bake/gem.rb
CHANGED
data/readme.md
CHANGED
@@ -8,16 +8,34 @@ Provides bake tasks for common gem release workflows.
|
|
8
8
|
|
9
9
|
Please see the [project documentation](https://ioquatix.github.io/bake-gem/) for more details.
|
10
10
|
|
11
|
-
- [Getting Started](https://ioquatix.github.io/bake-gem/guides/getting-started/index) - This guide explains how to use `bake-gem` to release gems.
|
11
|
+
- [Getting Started](https://ioquatix.github.io/bake-gem/guides/getting-started/index) - This guide explains how to use `bake-gem` to release gems safely and efficiently.
|
12
12
|
|
13
13
|
## Releases
|
14
14
|
|
15
15
|
Please see the [project releases](https://ioquatix.github.io/bake-gem/releases/index) for all releases.
|
16
16
|
|
17
|
+
### v0.12.0
|
18
|
+
|
19
|
+
- Add `guard_last_commit_not_version_bump` method to prevent consecutive version bumps.
|
20
|
+
- Add `build_gem_in_worktree` method for building gems in isolated git worktrees.
|
21
|
+
- Improve shell command execution with better error handling and specific exit code access.
|
22
|
+
|
23
|
+
### v0.11.1
|
24
|
+
|
25
|
+
- Better integration with `bake`'s default output.
|
26
|
+
|
17
27
|
### v0.11.0
|
18
28
|
|
19
29
|
- Improved bake task return values.
|
20
30
|
|
31
|
+
### v0.10.0
|
32
|
+
|
33
|
+
- Better handling of versions.
|
34
|
+
|
35
|
+
### v0.9.0
|
36
|
+
|
37
|
+
- Add `after_gem_release_version_increment` hook.
|
38
|
+
|
21
39
|
## See Also
|
22
40
|
|
23
41
|
- [Bake](https://github.com/ioquatix/bake) — The bake task execution tool.
|
data/releases.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v0.12.0
|
4
|
+
|
5
|
+
- Add `guard_last_commit_not_version_bump` method to prevent consecutive version bumps.
|
6
|
+
- Add `build_gem_in_worktree` method for building gems in isolated git worktrees.
|
7
|
+
- Improve shell command execution with better error handling and specific exit code access.
|
8
|
+
|
9
|
+
## v0.11.1
|
10
|
+
|
11
|
+
- Better integration with `bake`'s default output.
|
12
|
+
|
3
13
|
## v0.11.0
|
4
14
|
|
5
15
|
- Improved bake task return values.
|
16
|
+
|
17
|
+
## v0.10.0
|
18
|
+
|
19
|
+
- Better handling of versions.
|
20
|
+
|
21
|
+
## v0.9.0
|
22
|
+
|
23
|
+
- Add `after_gem_release_version_increment` hook.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bake-gem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -56,9 +56,13 @@ executables: []
|
|
56
56
|
extensions: []
|
57
57
|
extra_rdoc_files: []
|
58
58
|
files:
|
59
|
+
- agent.md
|
59
60
|
- bake/gem.rb
|
61
|
+
- bake/gem/release.rb
|
60
62
|
- bake/gem/release/branch.rb
|
61
63
|
- bake/gem/release/version.rb
|
64
|
+
- context/getting-started.md
|
65
|
+
- context/index.yaml
|
62
66
|
- lib/bake/gem.rb
|
63
67
|
- lib/bake/gem/helper.rb
|
64
68
|
- lib/bake/gem/shell.rb
|
metadata.gz.sig
CHANGED
Binary file
|