gempilot 0.2.2 → 0.2.4
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
- data/.rubocop.yml +15 -1
- data/CLAUDE.md +2 -9
- data/README.md +8 -4
- data/data/templates/gem/dotfiles/rubocop.yml.erb +1 -12
- data/docs/superpowers/plans/2026-07-20-release-task-hierarchy.md +583 -0
- data/docs/superpowers/specs/2026-07-20-release-task-hierarchy-design.md +229 -0
- data/issues.rec +74 -3
- data/lib/gempilot/cli/commands/console.rb +2 -3
- data/lib/gempilot/cli/commands/create.rb +6 -3
- data/lib/gempilot/cli/commands/new.rb +2 -3
- data/lib/gempilot/cli/commands/release.rb +2 -3
- data/lib/gempilot/cli/generator.rb +2 -1
- data/lib/gempilot/github_release.rb +0 -2
- data/lib/gempilot/origin.rb +52 -0
- data/lib/gempilot/project.rb +25 -5
- data/lib/gempilot/release_tasks.rb +73 -0
- data/lib/gempilot/version.rb +1 -1
- data/lib/gempilot/version_task.rb +3 -16
- data/vendor/vendored.gemv +0 -0
- metadata +8 -14
- data/docs/command_kit_comparison.md +0 -249
- data/docs/command_kit_reference.md +0 -517
- data/docs/plans/2026-02-18-gempilot-add-command.md +0 -718
- data/docs/superpowers/plans/2026-04-01-rubocop-new-config.md +0 -838
- data/docs/superpowers/plans/2026-04-06-dogfood-inflectable.md +0 -659
- data/docs/superpowers/plans/2026-04-06-inflection-tests-and-erb-rename.md +0 -166
- data/docs/superpowers/plans/2026-04-06-integrate-version-tools.md +0 -162
- data/docs/superpowers/plans/2026-04-06-new-readme.md +0 -185
- data/docs/superpowers/plans/2026-06-09-address-review-critiques.md +0 -679
- data/docs/version-management-redesign.md +0 -44
- data/notes.md +0 -31
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Release task hierarchy — design
|
|
2
|
+
|
|
3
|
+
**Issue:** `2C8EF3C8-83E9-11F1-9256-FE6CB9572C2F` — "gh release tasks need adjusting in task hierarchy"
|
|
4
|
+
|
|
5
|
+
**Date:** 2026-07-20
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Gempilot installs GitHub release tasks under a bespoke `version:github:*` namespace
|
|
10
|
+
(`version:github:release/unrelease/list`). Two things are wrong:
|
|
11
|
+
|
|
12
|
+
1. **Wrong hierarchy.** GitHub publishing should live in the standard `release` /
|
|
13
|
+
`unrelease` namespace alongside RubyGems publishing, not in a custom
|
|
14
|
+
`version:github:*` corner.
|
|
15
|
+
2. **GitHub releasing is broken.** The release flow bumps + commits + tags *locally*
|
|
16
|
+
via `version:release` (`version:tag` runs `git tag vX`). When the actual publish
|
|
17
|
+
runs through bundler's `release` task, bundler's `already_tagged?` guard sees the
|
|
18
|
+
tag already exists locally, prints *"Tag vX has already been created."*, and
|
|
19
|
+
**skips `release:source_control_push` entirely** — so the commit and tag never
|
|
20
|
+
reach the remote, and `gh release create vX` has nothing to attach to. This is the
|
|
21
|
+
"tags have already been pushed / doesn't work at all" symptom in the issue.
|
|
22
|
+
|
|
23
|
+
This is an approved **breaking change**. No backwards compatibility for the old
|
|
24
|
+
`version:github:*` tasks.
|
|
25
|
+
|
|
26
|
+
## Decisions
|
|
27
|
+
|
|
28
|
+
- **Namespace-only interface. No `[remote]` argument.** Target selection lives in the
|
|
29
|
+
task name (`release:github`), not an argument. This is the idiomatic Rake convention
|
|
30
|
+
(task name = what to do; argument = a value), and it avoids a direct collision:
|
|
31
|
+
`bundler/gem_tasks` already defines `task "release", [:remote]` where `remote` means
|
|
32
|
+
the *git remote name* (`origin`) passed to `git push`. Overloading that same word to
|
|
33
|
+
mean "publish target" is exactly what made the original `release[remote]` idea
|
|
34
|
+
confusing. So the final interface uses no argument at all.
|
|
35
|
+
- **Bare `rake release` publishes to all remotes** (RubyGems + GitHub), expressed as a
|
|
36
|
+
pure prerequisite composite — no dispatcher code.
|
|
37
|
+
- **Reuse bundler's building blocks for RubyGems.** `release:rubygems` composes
|
|
38
|
+
bundler's existing `build`, `release:guard_clean`, and `release:rubygem_push` leaf
|
|
39
|
+
tasks via prerequisites. No new object wraps them — this preserves `allowed_push_host`
|
|
40
|
+
(private gem servers), MFA/OTP prompts, and `gem_push=no`.
|
|
41
|
+
- **Fix the bug by overriding the one task that is wrong.** Replace bundler's
|
|
42
|
+
`release:source_control_push` with an idempotent push (no `already_tagged?` guard),
|
|
43
|
+
done the same idiomatic way bundler does it: an explicit `git push <remote> <branch>`
|
|
44
|
+
then `git push <remote> <tag>`. Both remotes share this single task as a prerequisite,
|
|
45
|
+
so both get the fix and the push runs exactly once per release.
|
|
46
|
+
- **Keep the local version lifecycle as-is.** `version:release` / `version:unrelease`
|
|
47
|
+
(local bump → commit → tag) are unchanged. The `version:` namespace and the top-level
|
|
48
|
+
`release` do not collide in Rake.
|
|
49
|
+
|
|
50
|
+
## Task interface
|
|
51
|
+
|
|
52
|
+
| Task | Behavior |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `rake release` | Publish current version to all remotes (RubyGems + GitHub) |
|
|
55
|
+
| `rake release:rubygems` | Build + push the gem to RubyGems |
|
|
56
|
+
| `rake release:github` | Push commit + tag, then create the GitHub release |
|
|
57
|
+
| `rake release:list:github` | List GitHub releases |
|
|
58
|
+
| `rake unrelease` | Delete the release from all remotes that support it (GitHub) |
|
|
59
|
+
| `rake unrelease:github` | Delete the GitHub release and remote tag |
|
|
60
|
+
|
|
61
|
+
`rake unrelease:rubygems` is intentionally undefined — Rake fails with "Don't know how
|
|
62
|
+
to build task", which is the correct signal that RubyGems yanking is out of scope.
|
|
63
|
+
|
|
64
|
+
## Task definitions
|
|
65
|
+
|
|
66
|
+
Defined in `Gempilot::VersionTask`, replacing the current `define_github_tasks`. Runs
|
|
67
|
+
after `require "bundler/gem_tasks"` (guaranteed by the generated `Rakefile` and by
|
|
68
|
+
gempilot's own `Rakefile`), so bundler's tasks exist when we override them. Each
|
|
69
|
+
override is guarded with `task_defined?` so the definitions also work when bundler's
|
|
70
|
+
gem tasks are absent (e.g. gempilot's own spec suite).
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
# THE FIX: idempotent push, replacing bundler's already_tagged?-guarded task.
|
|
74
|
+
Rake::Task["release:source_control_push"].clear if Rake::Task.task_defined?("release:source_control_push")
|
|
75
|
+
task "release:source_control_push" do
|
|
76
|
+
Origin.new(project.version_tag).push
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
namespace :release do
|
|
80
|
+
desc "Release the current version to RubyGems"
|
|
81
|
+
task rubygems: %w[build release:guard_clean release:source_control_push release:rubygem_push]
|
|
82
|
+
|
|
83
|
+
desc "Create a GitHub release for the current version"
|
|
84
|
+
task github: "release:source_control_push" do
|
|
85
|
+
GithubRelease.new(project.version_tag).create
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
namespace :list do
|
|
89
|
+
desc "List GitHub releases"
|
|
90
|
+
task(:github) { GithubRelease.new(project.version_tag).list }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
Rake::Task["release"].clear if Rake::Task.task_defined?("release")
|
|
95
|
+
desc "Release the current version to all remotes"
|
|
96
|
+
task release: %w[release:rubygems release:github]
|
|
97
|
+
|
|
98
|
+
desc "Delete the current release from all remotes that support it"
|
|
99
|
+
task unrelease: %w[unrelease:github]
|
|
100
|
+
|
|
101
|
+
namespace :unrelease do
|
|
102
|
+
desc "Delete the GitHub release for the current version"
|
|
103
|
+
task(:github) { GithubRelease.new(project.version_tag).destroy }
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Components
|
|
108
|
+
|
|
109
|
+
### `Gempilot::Origin` (new — `lib/gempilot/origin.rb`)
|
|
110
|
+
|
|
111
|
+
A small domain object (same style as the existing `GithubRelease` / `VersionTag`) that
|
|
112
|
+
pushes the current branch and a given tag to the branch's configured git remote. This
|
|
113
|
+
backs `release:source_control_push` and is the bug fix.
|
|
114
|
+
|
|
115
|
+
- `initialize(tag)` — the version tag string (e.g. `"v1.0.0"`).
|
|
116
|
+
- `#push` — runs, via `StrictShell#sh`:
|
|
117
|
+
- `git push <remote> refs/heads/<branch>`
|
|
118
|
+
- `git push <remote> refs/tags/<tag>`
|
|
119
|
+
- `<branch>` = `git rev-parse --abbrev-ref HEAD`; `<remote>` = the branch's configured
|
|
120
|
+
remote (`git config --get branch.<branch>.remote`), defaulting to `origin` — mirroring
|
|
121
|
+
bundler's own `current_branch` / `default_remote` logic.
|
|
122
|
+
- Idempotent: pushing an already-pushed branch/tag exits 0 ("Everything up-to-date"),
|
|
123
|
+
so re-running a release never fails on the tag. This is the property bundler's guard
|
|
124
|
+
broke.
|
|
125
|
+
|
|
126
|
+
### `Gempilot::GithubRelease` (modified — `lib/gempilot/github_release.rb`)
|
|
127
|
+
|
|
128
|
+
- `#create` drops its two internal `git push` / `git push --tags` lines. Pushing is now
|
|
129
|
+
`Origin`'s job (the `release:source_control_push` prerequisite runs first). `create`
|
|
130
|
+
becomes purely: `gh release create --generate-notes --fail-on-no-commits <tag>`.
|
|
131
|
+
- `#destroy` and `#list` are unchanged.
|
|
132
|
+
|
|
133
|
+
### `Gempilot::ReleaseTasks` (new — `lib/gempilot/release_tasks.rb`)
|
|
134
|
+
|
|
135
|
+
The release / unrelease task definitions above, extracted into a focused module that is
|
|
136
|
+
`include`d into `VersionTask`. This is a separate concern from the local version
|
|
137
|
+
lifecycle, and extracting it keeps `VersionTask` under RuboCop's `Metrics/ClassLength`
|
|
138
|
+
(the combined class would otherwise exceed the 100-line limit). All methods are private
|
|
139
|
+
and parameterized by `project` (no reliance on the host's ivars beyond the single
|
|
140
|
+
`define_release_tasks(project)` entry point).
|
|
141
|
+
|
|
142
|
+
### `Gempilot::VersionTask` (modified — `lib/gempilot/version_task.rb`)
|
|
143
|
+
|
|
144
|
+
- `include ReleaseTasks`.
|
|
145
|
+
- Remove `define_github_tasks`; replace its call in `define_tasks` with
|
|
146
|
+
`define_release_tasks(@project)`.
|
|
147
|
+
- `version:*` local lifecycle tasks and `version:release` / `version:unrelease`
|
|
148
|
+
composites are untouched.
|
|
149
|
+
|
|
150
|
+
There is **no** `RubygemsRelease` object. RubyGems publishing is bundler-task
|
|
151
|
+
composition only.
|
|
152
|
+
|
|
153
|
+
## Data flow
|
|
154
|
+
|
|
155
|
+
- `rake release:github` → `release:source_control_push` (push commit + tag) →
|
|
156
|
+
`gh release create`.
|
|
157
|
+
- `rake release:rubygems` → `build` → `release:guard_clean` →
|
|
158
|
+
`release:source_control_push` (push commit + tag) → `release:rubygem_push` (gem push).
|
|
159
|
+
- `rake release` → `release:rubygems` (which pushes during its chain), then
|
|
160
|
+
`release:github`. Rake invokes `release:source_control_push` once, so the git push
|
|
161
|
+
happens a single time, before both publishes.
|
|
162
|
+
- `rake unrelease` → `unrelease:github` → `gh release delete --yes --cleanup-tag`
|
|
163
|
+
(removes the GitHub release and the remote tag).
|
|
164
|
+
|
|
165
|
+
## Error handling
|
|
166
|
+
|
|
167
|
+
- **Unsupported unrelease target** (`rake unrelease:rubygems`): undefined task → Rake's
|
|
168
|
+
native "Don't know how to build task" error. No custom code.
|
|
169
|
+
- **Shell failures**: every git / gh / gem command runs through `StrictShell#sh`, which
|
|
170
|
+
already raises on non-zero exit.
|
|
171
|
+
- **Tag / commit not pushable**: if the local tag does not exist, the `git push
|
|
172
|
+
refs/tags/<tag>` fails loudly via `StrictShell` — the correct signal to run
|
|
173
|
+
`version:release` first. No separate guard task is added (deliberate: the
|
|
174
|
+
`version:release` flow is responsible for producing a clean, tagged commit; adding a
|
|
175
|
+
bundler-independent `guard_clean` is out of scope).
|
|
176
|
+
|
|
177
|
+
## Testing (TDD)
|
|
178
|
+
|
|
179
|
+
Written before implementation.
|
|
180
|
+
|
|
181
|
+
**`spec/gempilot/origin_spec.rb` (new)**
|
|
182
|
+
- `#push` issues `git push <remote> <branch>` then `git push <remote> <tag>` in order
|
|
183
|
+
(stub `sh`, assert ordered calls; assert branch/remote resolution).
|
|
184
|
+
- **Regression / integration**: in a tmpdir, create a real repo with a bare "origin"
|
|
185
|
+
remote and a tag, run `Origin#push`, assert the commit + tag land on origin; run it a
|
|
186
|
+
**second** time and assert it still succeeds (idempotent) — directly proving the bug
|
|
187
|
+
(bundler's guarded skip) is fixed.
|
|
188
|
+
|
|
189
|
+
**`spec/gempilot/github_release_spec.rb` (modified)**
|
|
190
|
+
- `#create` no longer calls `git push` / `git push --tags`; it calls only
|
|
191
|
+
`gh release create --generate-notes --fail-on-no-commits <tag>`.
|
|
192
|
+
- `#destroy` and `#list` specs unchanged.
|
|
193
|
+
|
|
194
|
+
**`spec/gempilot/version_task_spec.rb` (extended)**
|
|
195
|
+
- **Structure**: `release`, `release:rubygems`, `release:github`, `release:list:github`,
|
|
196
|
+
`unrelease`, `unrelease:github` are defined; `version:github:release`,
|
|
197
|
+
`version:github:unrelease`, `version:github:list` are **not**.
|
|
198
|
+
- **Composites by prerequisite** (no invocation needed): `release` prerequisites ==
|
|
199
|
+
`%w[release:rubygems release:github]`; `release:rubygems` prerequisites ==
|
|
200
|
+
`%w[build release:guard_clean release:source_control_push release:rubygem_push]`;
|
|
201
|
+
`unrelease` prerequisites == `%w[unrelease:github]`.
|
|
202
|
+
- **Behavior by invocation** (stub `Origin` / `GithubRelease` constructors):
|
|
203
|
+
- `release:source_control_push` invokes `Origin#push`.
|
|
204
|
+
- `release:github` runs the push, then `GithubRelease#create`.
|
|
205
|
+
- `release:list:github` invokes `GithubRelease#list`.
|
|
206
|
+
- `unrelease:github` invokes `GithubRelease#destroy`.
|
|
207
|
+
- `release:rubygems` is asserted by prerequisites only (its bundler prerequisites are
|
|
208
|
+
absent in the spec's fresh Rake app, so it is not invoked there).
|
|
209
|
+
|
|
210
|
+
## Docs
|
|
211
|
+
|
|
212
|
+
- `README.md` — replace the three `version:github:*` rows in the tasks table with the
|
|
213
|
+
new `release` / `unrelease` tasks; update the `gempilot release` note to reflect that
|
|
214
|
+
bare `rake release` now publishes to all remotes.
|
|
215
|
+
- `CLAUDE.md` — update the "Version lifecycle rake tasks" bullet: drop
|
|
216
|
+
`version:github:release/unrelease/list`, add `release` / `release:rubygems` /
|
|
217
|
+
`release:github` / `release:list:github` / `unrelease` / `unrelease:github`.
|
|
218
|
+
|
|
219
|
+
## Out of scope
|
|
220
|
+
|
|
221
|
+
- `data/templates/gem/Rakefile.erb` needs no change — it already requires
|
|
222
|
+
`bundler/gem_tasks` before `Gempilot::VersionTask.new`, which the override relies on.
|
|
223
|
+
- The stale `rakelib/*` excludes in `data/templates/gem/dotfiles/rubocop.yml.erb`
|
|
224
|
+
(including `rakelib/github_release.rb`) belong to a separate open issue about internal
|
|
225
|
+
concerns leaking to gem users (`B45C988A`). Not touched here.
|
|
226
|
+
- RubyGems yanking (`unrelease:rubygems`).
|
|
227
|
+
- The `gempilot release` CLI command keeps proxying to `rake release`; its behavior
|
|
228
|
+
changes only because bare `rake release` now targets all remotes.
|
|
229
|
+
- Any change to the local `version:*` lifecycle tasks.
|
data/issues.rec
CHANGED
|
@@ -26,7 +26,7 @@ Id: F0833E66-A35C-4054-884B-F90C81F08838
|
|
|
26
26
|
Updated: Mon, 02 Mar 2026 10:37:33 -0500
|
|
27
27
|
Title: Error handling should be graceful
|
|
28
28
|
Description: I did "gempilot create", and entered "A foo" as the title, and it crashed horrifically. This particular example should have validation at the minimum. Need to also use command kit's error bubbling/handling mechanism to swallow errors and print message to user. Can always use verbose or debug mode to see full trace.
|
|
29
|
-
Status:
|
|
29
|
+
Status: closed
|
|
30
30
|
|
|
31
31
|
Id: 741FFD05-9BD7-44EB-BB99-E7D705F12681
|
|
32
32
|
Updated: Mon, 02 Mar 2026 11:25:24 -0500
|
|
@@ -68,7 +68,7 @@ Id: 74D0FD90-0421-4452-8591-2798EE6FD23E
|
|
|
68
68
|
Updated: Sat, 28 Mar 2026 12:59:40 -0400
|
|
69
69
|
Title: Publish gem
|
|
70
70
|
Description: At some point the repo should be moved to public viz, and the gem should be published.
|
|
71
|
-
Status:
|
|
71
|
+
Status: closed
|
|
72
72
|
|
|
73
73
|
Id: 8C356024-3122-11F1-89F3-FE6CB9572C2F
|
|
74
74
|
Updated: Mon, 06 Apr 2026 00:00:00 -0400
|
|
@@ -101,7 +101,7 @@ Id: 0411D4DA-4323-11F1-847C-FE6CB9572C2F
|
|
|
101
101
|
Updated: Tue, 28 Apr 2026 12:55:09 -0400
|
|
102
102
|
Title: rubocop config should not exclude rakelib
|
|
103
103
|
Description: it is supplied by gempilot. Should not be eliminating author files
|
|
104
|
-
Status:
|
|
104
|
+
Status: closed
|
|
105
105
|
|
|
106
106
|
Id: C72CD130-4324-11F1-ACAE-FE6CB9572C2F
|
|
107
107
|
Updated: Tue, 28 Apr 2026 13:07:46 -0400
|
|
@@ -141,3 +141,74 @@ Description: "
|
|
|
141
141
|
+ ```
|
|
142
142
|
+ "
|
|
143
143
|
Status: closed
|
|
144
|
+
|
|
145
|
+
Id: 1C46F71C-70E9-11F1-B65B-FE6CB9572C2E
|
|
146
|
+
Updated: Thu, 25 Jun 2026 18:56:32 -0400
|
|
147
|
+
Title: Incorrectly creates test file
|
|
148
|
+
Description: When doing `gempilot new Reversal::Server`, in a project called "reversal-store", I get:
|
|
149
|
+
+
|
|
150
|
+
+ gempilot new class Reversal::Server
|
|
151
|
+
+
|
|
152
|
+
+ Adding class Reversal::Server...
|
|
153
|
+
+
|
|
154
|
+
+ create lib/reversal/server.rb
|
|
155
|
+
+ create test/reversal/store_test.rb
|
|
156
|
+
+
|
|
157
|
+
+ The test file is incorrectly named. Should be server_test.rb
|
|
158
|
+
Status: open
|
|
159
|
+
|
|
160
|
+
Id: F5486B48-71DB-11F1-980F-FE6CB9572C2E
|
|
161
|
+
Updated: Fri, 26 Jun 2026 23:54:55 -0400
|
|
162
|
+
Title: Rubymine <=> Minitest usage causes "no reporters allowed error"
|
|
163
|
+
Description: Has to do with how the ENV var RM_INFO is being interpreted
|
|
164
|
+
Status: open
|
|
165
|
+
|
|
166
|
+
Id: 8D847572-7273-11F1-B508-FE6CB9572C2E
|
|
167
|
+
Updated: Fri, 03 Jul 2026 22:38:22 +0000
|
|
168
|
+
Title: Gem extensions are broken
|
|
169
|
+
Description: when using gempilot on a gem extension, like 'foo-support', using idiomatic ruby patterns for dir structure, gempilot Project class throws an error
|
|
170
|
+
Status: closed
|
|
171
|
+
|
|
172
|
+
Id: B45C988A-83C3-11F1-A32B-FE6CB9572C2F
|
|
173
|
+
Updated: Sun, 19 Jul 2026 18:46:39 -0400
|
|
174
|
+
Title: Gempilot internal concerns leak to gemusers
|
|
175
|
+
Description: Gempilot fresh install leaks the following in .rubocop.yml:
|
|
176
|
+
+
|
|
177
|
+
+ AllCops:
|
|
178
|
+
+ 1 NewCops: enable
|
|
179
|
+
+ 2 TargetRubyVersion: 4.0
|
|
180
|
+
+ 3 Exclude:
|
|
181
|
+
+ 4 - bin/*
|
|
182
|
+
+ 5 - vendor/**/*
|
|
183
|
+
+ 6 - lib/core_ext/**/*
|
|
184
|
+
+ 7 - rakelib/project.rb
|
|
185
|
+
+ 8 - rakelib/project_version.rb
|
|
186
|
+
+ 9
|
|
187
|
+
Status: closed
|
|
188
|
+
|
|
189
|
+
Id: 2C8EF3C8-83E9-11F1-9256-FE6CB9572C2F
|
|
190
|
+
Updated: Sun, 19 Jul 2026 23:14:52 -0400
|
|
191
|
+
Title: gh release tasks need adjusting in task heirarchy
|
|
192
|
+
Description: the github release related tasks are invoked as follows:
|
|
193
|
+
+ ```
|
|
194
|
+
+ rake version:github:release
|
|
195
|
+
+ rake version:github:list
|
|
196
|
+
+ rake version:github:unrelease
|
|
197
|
+
+
|
|
198
|
+
+ ```
|
|
199
|
+
+ they should instead be under the main idiomatic release namespace:
|
|
200
|
+
+
|
|
201
|
+
+ ```
|
|
202
|
+
+ rake release[remote] # release to the remote, e.g. rubygems/github
|
|
203
|
+
+ rake release:github # alternative to rake release[github]
|
|
204
|
+
+ rake unrelease[remote] # currently only supported for github, not rubygems
|
|
205
|
+
+ rake unrelease:github # alternative to rake unrelease[github]
|
|
206
|
+
+ rake release:list:github # list GitHub releases
|
|
207
|
+
+ rake release:
|
|
208
|
+
+ ```
|
|
209
|
+
+ where remote is "github"
|
|
210
|
+
+
|
|
211
|
+
+ Note that this is a breaking change, and it is approved. Do not provide backwards compatibility for the old tasks. Move forward
|
|
212
|
+
+
|
|
213
|
+
+ Also, there is an issue that prevents github releasing from working at all via the old tasks. I get an error every time saying that "tags have already been pushed" or something along those lines. Ensure that is fixed as well
|
|
214
|
+
Status: closed
|
|
@@ -27,9 +27,8 @@ module Gempilot
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def print_console_banner
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
colors.bright_white("...")
|
|
30
|
+
styled_name = colors.bold(colors.cyan(@gem_name))
|
|
31
|
+
puts colors.bright_white("Starting console for ") + styled_name + colors.bright_white("...")
|
|
33
32
|
puts
|
|
34
33
|
end
|
|
35
34
|
end
|
|
@@ -156,7 +156,8 @@ module Gempilot
|
|
|
156
156
|
|
|
157
157
|
puts
|
|
158
158
|
puts colors.bright_black("An executable in exe/ lets users run your gem from the command line.")
|
|
159
|
-
|
|
159
|
+
prompt = colors.green("Create an executable")
|
|
160
|
+
options[:exe] = ask_yes_or_no(prompt, default: false)
|
|
160
161
|
end
|
|
161
162
|
|
|
162
163
|
def collect_git_options
|
|
@@ -169,7 +170,8 @@ module Gempilot
|
|
|
169
170
|
|
|
170
171
|
puts
|
|
171
172
|
puts colors.bright_black("Initialize a git repository with an initial commit.")
|
|
172
|
-
|
|
173
|
+
prompt = colors.green("Initialize git repo")
|
|
174
|
+
options[:git] = ask_yes_or_no(prompt, default: true)
|
|
173
175
|
end
|
|
174
176
|
|
|
175
177
|
def collect_branch
|
|
@@ -181,8 +183,9 @@ module Gempilot
|
|
|
181
183
|
end
|
|
182
184
|
|
|
183
185
|
def print_header
|
|
186
|
+
styled_name = colors.bold(colors.cyan(@gem_name))
|
|
184
187
|
puts
|
|
185
|
-
puts colors.bright_white("Creating gem '") +
|
|
188
|
+
puts colors.bright_white("Creating gem '") + styled_name + colors.bright_white("'...")
|
|
186
189
|
puts
|
|
187
190
|
end
|
|
188
191
|
|
|
@@ -77,10 +77,9 @@ module Gempilot
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def print_adding_banner(kind, label)
|
|
80
|
+
styled_label = colors.bold(colors.cyan(label))
|
|
80
81
|
puts
|
|
81
|
-
puts colors.bright_white("Adding #{kind} ") +
|
|
82
|
-
colors.bold(colors.cyan(label)) +
|
|
83
|
-
colors.bright_white("...")
|
|
82
|
+
puts colors.bright_white("Adding #{kind} ") + styled_label + colors.bright_white("...")
|
|
84
83
|
puts
|
|
85
84
|
end
|
|
86
85
|
|
|
@@ -18,9 +18,8 @@ module Gempilot
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def print_release_banner
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
colors.bright_white("...")
|
|
21
|
+
styled_name = colors.bold(colors.cyan(@gem_name))
|
|
22
|
+
puts colors.bright_white("Releasing ") + styled_name + colors.bright_white("...")
|
|
24
23
|
puts
|
|
25
24
|
end
|
|
26
25
|
|
|
@@ -37,8 +37,9 @@ module Gempilot
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def print_action(command, dest, source: nil)
|
|
40
|
+
styled_command = colors.bold(colors.green(command))
|
|
40
41
|
line = +""
|
|
41
|
-
line << "\t" <<
|
|
42
|
+
line << "\t" << styled_command
|
|
42
43
|
line << "\t" << colors.green(source) if source
|
|
43
44
|
line << "\t" << colors.green(dest) if dest
|
|
44
45
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
|
|
3
|
+
module Gempilot
|
|
4
|
+
## Pushes the current branch and a release tag to the branch's git remote.
|
|
5
|
+
## Backs the +release:source_control_push+ task. Idempotent: pushing an
|
|
6
|
+
## already-pushed branch or tag is a no-op, so re-running a release never
|
|
7
|
+
## fails on an existing tag (unlike bundler's +already_tagged?+ guard, which
|
|
8
|
+
## skips the push entirely once the tag exists locally).
|
|
9
|
+
class Origin
|
|
10
|
+
include StrictShell
|
|
11
|
+
|
|
12
|
+
attr_reader :tag
|
|
13
|
+
|
|
14
|
+
def initialize(tag)
|
|
15
|
+
@tag = tag
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def push
|
|
19
|
+
sh "git", "push", remote, "refs/heads/#{branch}"
|
|
20
|
+
sh "git", "push", remote, "refs/tags/#{tag}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def branch
|
|
26
|
+
@branch ||= resolve_branch
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def resolve_branch
|
|
30
|
+
name = capture("git", "rev-parse", "--abbrev-ref", "HEAD")
|
|
31
|
+
raise "Cannot push from a detached HEAD; check out a branch first" if name == "HEAD"
|
|
32
|
+
|
|
33
|
+
name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def remote
|
|
37
|
+
@remote ||= configured_remote || "origin"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def configured_remote
|
|
41
|
+
out, status = Open3.capture2("git", "config", "--get", "branch.#{branch}.remote")
|
|
42
|
+
out.strip if status.success?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def capture(*args)
|
|
46
|
+
out, status = Open3.capture2(*args)
|
|
47
|
+
raise "Command #{args.join(" ").inspect} failed (exit #{status.exitstatus})" unless status.success?
|
|
48
|
+
|
|
49
|
+
out.strip
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/gempilot/project.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
require "pathname"
|
|
2
1
|
require "warning"
|
|
3
2
|
|
|
4
3
|
module Gempilot
|
|
5
4
|
## Introspects a gem project to discover its name, module, and version.
|
|
5
|
+
## Works for both regular gems (+lib/my_gem.rb+) and extension gems whose
|
|
6
|
+
## entry point nests deeper (+lib/my_gem/extension.rb+ for +my_gem-extension+).
|
|
6
7
|
class Project
|
|
7
8
|
class ProjectIntrospectionError < StandardError; end
|
|
8
9
|
|
|
@@ -29,11 +30,11 @@ module Gempilot
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def name
|
|
32
|
-
|
|
33
|
+
project_segments.join("-")
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def klass
|
|
36
|
-
Object.const_get(
|
|
37
|
+
Object.const_get(project_segments.map(&:camelize).join("::"))
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def version
|
|
@@ -67,6 +68,10 @@ module Gempilot
|
|
|
67
68
|
|
|
68
69
|
private
|
|
69
70
|
|
|
71
|
+
def project_segments
|
|
72
|
+
lib_project.relative_path_from(lib).each_filename.to_a
|
|
73
|
+
end
|
|
74
|
+
|
|
70
75
|
def with_version_file
|
|
71
76
|
version.path.open(File::RDWR, 0o644) do |f|
|
|
72
77
|
f.flock File::LOCK_EX
|
|
@@ -76,8 +81,7 @@ module Gempilot
|
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
def fetch_lib_project
|
|
79
|
-
|
|
80
|
-
dirs = files.map { it.sub_ext("") }.select(&:directory?)
|
|
84
|
+
dirs = shallowest_entry_dirs
|
|
81
85
|
case dirs.count
|
|
82
86
|
in 0 then raise ProjectIntrospectionError, "Could not identify project dir"
|
|
83
87
|
in (2..)
|
|
@@ -87,6 +91,22 @@ module Gempilot
|
|
|
87
91
|
end
|
|
88
92
|
end
|
|
89
93
|
|
|
94
|
+
def shallowest_entry_dirs
|
|
95
|
+
entry_dirs.group_by { depth_below_lib(it) }
|
|
96
|
+
.min_by(&:first)
|
|
97
|
+
&.last || []
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def entry_dirs
|
|
101
|
+
lib.glob("**/*.rb")
|
|
102
|
+
.map { it.sub_ext("") }
|
|
103
|
+
.select(&:directory?)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def depth_below_lib(path)
|
|
107
|
+
path.relative_path_from(lib).each_filename.count
|
|
108
|
+
end
|
|
109
|
+
|
|
90
110
|
def fetch_version
|
|
91
111
|
Warning.ignore(REDEFINITION_WARNING)
|
|
92
112
|
Warning.ignore(REINITIALIZATION_WARNING)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Gempilot
|
|
2
|
+
## Rake task definitions for publishing a release to RubyGems and GitHub.
|
|
3
|
+
## Mixed into VersionTask. Assumes +bundler/gem_tasks+ has been required so the
|
|
4
|
+
## +build+, +release:guard_clean+, and +release:rubygem_push+ tasks exist (the
|
|
5
|
+
## generated Rakefile guarantees this). Fixes GitHub releasing by replacing
|
|
6
|
+
## bundler's +already_tagged?+-guarded +release:source_control_push+ with an
|
|
7
|
+
## idempotent push.
|
|
8
|
+
module ReleaseTasks
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def define_release_tasks(project)
|
|
12
|
+
override_source_control_push(project)
|
|
13
|
+
define_release_namespace(project)
|
|
14
|
+
define_root_release_task
|
|
15
|
+
define_unrelease_tasks(project)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def override_source_control_push(project)
|
|
19
|
+
clear_task "release:source_control_push"
|
|
20
|
+
task("release:source_control_push") { Origin.new(project.version_tag).push }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def define_release_namespace(project)
|
|
24
|
+
namespace :release do
|
|
25
|
+
define_rubygems_release
|
|
26
|
+
define_github_release(project)
|
|
27
|
+
define_release_list(project)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def define_rubygems_release
|
|
32
|
+
desc "Release the current version to RubyGems"
|
|
33
|
+
task rubygems: %w[build release:guard_clean release:source_control_push release:rubygem_push]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def define_github_release(project)
|
|
37
|
+
desc "Create a GitHub release for the current version"
|
|
38
|
+
task github: "release:source_control_push" do
|
|
39
|
+
GithubRelease.new(project.version_tag).create
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def define_release_list(project)
|
|
44
|
+
namespace :list do
|
|
45
|
+
desc "List GitHub releases"
|
|
46
|
+
task(:github) { GithubRelease.new(project.version_tag).list }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def define_root_release_task
|
|
51
|
+
clear_task "release"
|
|
52
|
+
desc "Release the current version to all remotes"
|
|
53
|
+
task release: %w[release:rubygems release:github]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def define_unrelease_tasks(project)
|
|
57
|
+
desc "Delete the current release from all remotes that support it"
|
|
58
|
+
task unrelease: %w[unrelease:github]
|
|
59
|
+
define_unrelease_namespace(project)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def define_unrelease_namespace(project)
|
|
63
|
+
namespace :unrelease do
|
|
64
|
+
desc "Delete the GitHub release for the current version"
|
|
65
|
+
task(:github) { GithubRelease.new(project.version_tag).destroy }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def clear_task(name)
|
|
70
|
+
Rake::Task[name].clear if Rake::Task.task_defined?(name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/gempilot/version.rb
CHANGED
|
@@ -4,6 +4,8 @@ require_relative "../gempilot"
|
|
|
4
4
|
module Gempilot
|
|
5
5
|
## Rake tasks for version lifecycle management.
|
|
6
6
|
class VersionTask < Rake::TaskLib
|
|
7
|
+
include ReleaseTasks
|
|
8
|
+
|
|
7
9
|
attr_reader :project
|
|
8
10
|
|
|
9
11
|
def initialize(root: Dir.pwd)
|
|
@@ -17,7 +19,7 @@ module Gempilot
|
|
|
17
19
|
def define_tasks
|
|
18
20
|
define_version_tasks
|
|
19
21
|
define_version_composite_tasks
|
|
20
|
-
|
|
22
|
+
define_release_tasks(@project)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def define_version_tasks
|
|
@@ -89,20 +91,5 @@ module Gempilot
|
|
|
89
91
|
project.refresh_version!
|
|
90
92
|
end
|
|
91
93
|
end
|
|
92
|
-
|
|
93
|
-
def define_github_tasks
|
|
94
|
-
project = @project
|
|
95
|
-
|
|
96
|
-
namespace "version:github" do
|
|
97
|
-
desc "Create a GitHub release for the current version"
|
|
98
|
-
task(:release) { GithubRelease.new(project.version_tag).create }
|
|
99
|
-
|
|
100
|
-
desc "Delete the GitHub release for the current version"
|
|
101
|
-
task(:unrelease) { GithubRelease.new(project.version_tag).destroy }
|
|
102
|
-
|
|
103
|
-
desc "List GitHub releases"
|
|
104
|
-
task(:list) { GithubRelease.new(project.version_tag).list }
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
94
|
end
|
|
108
95
|
end
|
|
Binary file
|