backspin 0.13.0 → 0.14.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
- data/.circleci/config.yml +12 -4
- data/.github/workflows/release.yml +24 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +3 -1
- data/CONTRIBUTING.md +20 -0
- data/Gemfile.lock +1 -1
- data/README.md +28 -3
- data/Rakefile +0 -2
- data/fixtures/projects/dummy_cli_gem/Gemfile.lock +2 -2
- data/lib/backspin/backspin_result.rb +12 -4
- data/lib/backspin/command_diff.rb +98 -9
- data/lib/backspin/configuration.rb +5 -1
- data/lib/backspin/version.rb +1 -1
- data/mise.toml +1 -0
- metadata +3 -4
- data/.gem_release.yml +0 -13
- data/release.rake +0 -105
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2fd83cd35c05021e0df8e585060247f0bee2fd1034d43861bf462bbb1d67101
|
|
4
|
+
data.tar.gz: 1ee344e3596a9442d17dbbaab2aaa541d676fb2f734d13b28ad34d49948b684b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9be21d06b0087032243e2df85bfffa060fa9a8f411ce4733a125fffbdbe40ac58d2637d1f18f9b70831730917ef9aa0baff38f6021439d37c444984f7acee886
|
|
7
|
+
data.tar.gz: 2720731b166213d551c0dd58e7478e23d20637b1d4157945fcac090298ff5059fdce1c86e3e98e20087fc5bd27de52a522b35e1a124158c914c23ede4fa53dfe
|
data/.circleci/config.yml
CHANGED
|
@@ -12,14 +12,22 @@ jobs:
|
|
|
12
12
|
ruby-version:
|
|
13
13
|
type: string
|
|
14
14
|
docker:
|
|
15
|
-
- image:
|
|
15
|
+
- image: ghcr.io/jdx/mise:2026.9.15-debian
|
|
16
|
+
environment:
|
|
17
|
+
MISE_RUBY_VERSION: << parameters.ruby-version >>
|
|
18
|
+
MISE_YES: "1"
|
|
16
19
|
steps:
|
|
17
20
|
- checkout
|
|
21
|
+
- run:
|
|
22
|
+
name: Install project tools
|
|
23
|
+
command: |
|
|
24
|
+
apt-get update && apt-get install -y --no-install-recommends build-essential libyaml-dev
|
|
25
|
+
mise install
|
|
18
26
|
- ruby/install-deps:
|
|
19
|
-
key: gems-
|
|
27
|
+
key: gems-v2-mise-ruby<< parameters.ruby-version >>
|
|
20
28
|
- run:
|
|
21
|
-
name: Run specs
|
|
22
|
-
command:
|
|
29
|
+
name: Run specs with Plur
|
|
30
|
+
command: plur spec
|
|
23
31
|
- run:
|
|
24
32
|
name: Run fake gem specs
|
|
25
33
|
command: bundle exec rake spec:fake_gem
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
previous-ref:
|
|
7
|
+
description: Tag or commit before the version increase (for retrying an unpublished release)
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [opened, synchronize, reopened]
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
release:
|
|
21
|
+
uses: rsanheim/.github/.github/workflows/gem-release.yml@4ced52859dd60019b930a3f2023424e77b37ee2a
|
|
22
|
+
with:
|
|
23
|
+
gem: backspin
|
|
24
|
+
ruby-version: "4.0.0"
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
* Improved verification errors with per-field summaries for stdout, stderr, and exit status, plus three lines of context around output changes. Long diffs are limited to 50 lines; set `BACKSPIN_FULL_DIFF=1` to show the full diff.
|
|
5
|
+
* Preserved custom matcher failure reasons and made missing final newlines visible in output diffs.
|
|
6
|
+
* Fixed `backspin_dir` configuration to accept String paths as well as Pathname objects.
|
|
7
|
+
|
|
3
8
|
## 0.13.0
|
|
4
9
|
* Added `Backspin.compare(reference:, actual:)` for differential testing - runs both commands live and compares their filtered output, with no record file. Only stdout, stderr, and exit status are compared, so the two commands may differ in argv and env without normalization.
|
|
5
10
|
* Added `Backspin::ReferenceCommandError`, raised when a compare's reference command produces no output at all (usually a sign it failed to start), rather than comparing empty to empty.
|
data/CLAUDE.md
CHANGED
|
@@ -24,9 +24,11 @@ bin/rspec spec/[file]:[line] # Run specific test
|
|
|
24
24
|
### Building and Releasing
|
|
25
25
|
```bash
|
|
26
26
|
bin/rake install # Install gem locally for testing
|
|
27
|
-
bin/rake
|
|
27
|
+
bin/rake build # Build the gem locally
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Releases use a pull request that updates `lib/backspin/version.rb` and adds a matching nonempty section to `CHANGELOG.md`. Use normal premerge CI to establish readiness. After merge to `main`, the shared GitHub Actions workflow rechecks the version and changelog, invokes `rubygems/release-gem@v1` and Bundler for tagging and gem publication, then creates the GitHub release from the changelog notes. Invalid release notes prevent publication. See `CONTRIBUTING.md` for trusted publisher setup.
|
|
31
|
+
|
|
30
32
|
### Code Quality
|
|
31
33
|
```bash
|
|
32
34
|
script/lint # Run Standard Ruby linter
|
data/CONTRIBUTING.md
CHANGED
|
@@ -11,6 +11,7 @@ Note that Backspin is in early development and the API _will_ change before stab
|
|
|
11
11
|
- [Making Changes](#making-changes)
|
|
12
12
|
- [Testing](#testing)
|
|
13
13
|
- [Submitting Changes](#submitting-changes)
|
|
14
|
+
- [Release Maintainer Setup](#release-maintainer-setup)
|
|
14
15
|
- [Code Style](#code-style)
|
|
15
16
|
- [Reporting Issues](#reporting-issues)
|
|
16
17
|
- [Feature Requests](#feature-requests)
|
|
@@ -161,6 +162,25 @@ end
|
|
|
161
162
|
- Update examples in README.md if changing public APIs
|
|
162
163
|
- Ensure CI passes (tests against Ruby 3.2, 3.3, 3.4, and 4.0)
|
|
163
164
|
|
|
165
|
+
## Release Maintainer Setup
|
|
166
|
+
|
|
167
|
+
Before the first automated release, configure a RubyGems trusted publisher for the `backspin` gem using the [reusable workflow setup](https://guides.rubygems.org/trusted-publishing/#using-reusable-workflows):
|
|
168
|
+
|
|
169
|
+
- Repository owner: `rsanheim`
|
|
170
|
+
- Repository name: `backspin`
|
|
171
|
+
- Workflow filename: `gem-release.yml`
|
|
172
|
+
- Workflow Repository Owner: `rsanheim`
|
|
173
|
+
- Workflow Repository Name: `.github`
|
|
174
|
+
- Environment: `release`
|
|
175
|
+
|
|
176
|
+
The workflow filename identifies the shared reusable workflow. The caller is `.github/workflows/release.yml`, which grants GitHub OIDC permission (`id-token: write`). No long-lived RubyGems API key is needed.
|
|
177
|
+
|
|
178
|
+
Release pull requests update `lib/backspin/version.rb` and add a nonempty `## VERSION` section to `CHANGELOG.md`; the section body becomes the GitHub release notes. Keep the version change and its notes in the same pull request. Use the normal premerge CI checks to establish release readiness. Merging to `main` starts the release workflow, which rechecks the version and changelog before publishing; invalid release notes prevent publication. Ordinary merges do not publish anything.
|
|
179
|
+
|
|
180
|
+
If publication fails before the gem is uploaded, fix the cause and manually run the Release workflow on `main`, setting `previous-ref` to the tag before the version increase. Confirm the target version is still unpublished before retrying.
|
|
181
|
+
|
|
182
|
+
The shared release workflow is pinned in `.github/workflows/release.yml`. Update that commit deliberately when adopting shared release changes. Bundler's standard `build`, `install`, and `release` tasks remain available; the automated workflow is the normal publishing path.
|
|
183
|
+
|
|
164
184
|
## Code Style
|
|
165
185
|
|
|
166
186
|
Backspin uses [Standard Ruby](https://github.com/standardrb/standard) for code formatting. Run the linter before committing:
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -253,6 +253,23 @@ result.error_message # Human-readable error if verification failed
|
|
|
253
253
|
result.diff # Diff between expected and actual output
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
### Verification diffs
|
|
257
|
+
|
|
258
|
+
Verification errors include the failure reason and a summary of changes to stdout, stderr,
|
|
259
|
+
and exit status. Output diffs show three lines of context around each change, with `...`
|
|
260
|
+
where unchanged lines are omitted. A `\ No newline at end of output` marker identifies
|
|
261
|
+
output without a final newline.
|
|
262
|
+
|
|
263
|
+
Diffs are limited to 50 lines by default. To see every diff line, run your tests with:
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
BACKSPIN_FULL_DIFF=1 bundle exec rake spec
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The limit applies to `result.diff` and the diff portion of `result.error_message`. The
|
|
270
|
+
environment variable removes the size limit; unchanged lines outside the context windows
|
|
271
|
+
are still omitted.
|
|
272
|
+
|
|
256
273
|
### Configuration
|
|
257
274
|
|
|
258
275
|
You can configure Backspin's behavior globally:
|
|
@@ -344,16 +361,24 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
|
344
361
|
This repo also includes a decoupled full-stack fixture gem at `fixtures/projects/dummy_cli_gem` that uses Backspin the way downstream projects do. Run it with:
|
|
345
362
|
|
|
346
363
|
```bash
|
|
347
|
-
bundle exec rake
|
|
364
|
+
bundle exec rake spec:fake_gem
|
|
348
365
|
```
|
|
349
366
|
|
|
350
367
|
To re-record that fixture's committed YAML snapshots:
|
|
351
368
|
|
|
352
369
|
```bash
|
|
353
|
-
bundle exec rake
|
|
370
|
+
bundle exec rake spec:fake_gem_record
|
|
354
371
|
```
|
|
355
372
|
|
|
356
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
373
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
374
|
+
|
|
375
|
+
### Releasing
|
|
376
|
+
|
|
377
|
+
Open a pull request that updates `lib/backspin/version.rb` and adds a nonempty matching version section to `CHANGELOG.md` (for example, `## 0.14.0`). CI checks the effective gemspec version and fails if a version change has no release notes.
|
|
378
|
+
|
|
379
|
+
Use the normal premerge CI checks to establish release readiness. After the pull request merges into `main`, the shared workflow rechecks the version and changelog, then invokes `rubygems/release-gem@v1`. The official action uses OIDC trusted publishing and Bundler's standard release task to build the gem, create and push `vVERSION`, and publish to RubyGems. After the action succeeds, the shared workflow creates a GitHub release using that changelog section. Invalid release notes prevent publication. Commits that leave the gem version unchanged do not trigger a release.
|
|
380
|
+
|
|
381
|
+
The release implementation lives in [`rsanheim/.github`](https://github.com/rsanheim/.github) and is pinned to a commit in `.github/workflows/release.yml`. See [the maintainer setup](CONTRIBUTING.md#release-maintainer-setup) before enabling releases.
|
|
357
382
|
|
|
358
383
|
## Contributing
|
|
359
384
|
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ../../..
|
|
3
3
|
specs:
|
|
4
|
-
backspin (0.
|
|
4
|
+
backspin (0.14.0)
|
|
5
5
|
logger
|
|
6
6
|
|
|
7
7
|
PATH
|
|
@@ -40,7 +40,7 @@ DEPENDENCIES
|
|
|
40
40
|
rspec (~> 3)
|
|
41
41
|
|
|
42
42
|
CHECKSUMS
|
|
43
|
-
backspin (0.
|
|
43
|
+
backspin (0.14.0)
|
|
44
44
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
45
45
|
dummy_cli_gem (0.1.0)
|
|
46
46
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
@@ -36,10 +36,18 @@ module Backspin
|
|
|
36
36
|
return nil unless verified? == false
|
|
37
37
|
return "Output verification failed" unless @command_diff
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
parts = ["Output verification failed:", @command_diff.summary, "", "Summary:"]
|
|
40
|
+
@command_diff.field_summary.each do |line|
|
|
41
|
+
parts << " #{line}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
diff = @command_diff.diff
|
|
45
|
+
if diff && !diff.empty?
|
|
46
|
+
parts << ""
|
|
47
|
+
parts << diff
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
parts.join("\n")
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
def success?
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
module Backspin
|
|
4
4
|
# Represents the difference between expected and actual snapshots.
|
|
5
5
|
class CommandDiff
|
|
6
|
+
CONTEXT_LINES = 3
|
|
7
|
+
MAX_DIFF_LINES = 50
|
|
8
|
+
|
|
6
9
|
attr_reader :expected, :actual, :matcher
|
|
7
10
|
|
|
8
11
|
def initialize(expected:, actual:, matcher: nil, filter: nil, filter_on: :both)
|
|
@@ -26,6 +29,15 @@ module Backspin
|
|
|
26
29
|
@verified = @matcher.match?
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
# @return [Array<String>] Per-field change status lines
|
|
33
|
+
def field_summary
|
|
34
|
+
lines = []
|
|
35
|
+
lines << stdout_field_summary
|
|
36
|
+
lines << stderr_field_summary
|
|
37
|
+
lines << status_field_summary
|
|
38
|
+
lines
|
|
39
|
+
end
|
|
40
|
+
|
|
29
41
|
# @return [String, nil] Human-readable diff if not verified
|
|
30
42
|
def diff
|
|
31
43
|
return nil if verified?
|
|
@@ -48,7 +60,8 @@ module Backspin
|
|
|
48
60
|
parts << "Exit status: expected #{expected_compare.status}, got #{actual_compare.status}"
|
|
49
61
|
end
|
|
50
62
|
|
|
51
|
-
parts.join("\n\n")
|
|
63
|
+
result = parts.join("\n\n")
|
|
64
|
+
maybe_truncate(result)
|
|
52
65
|
end
|
|
53
66
|
|
|
54
67
|
# @return [String] Single line summary for error messages
|
|
@@ -62,6 +75,30 @@ module Backspin
|
|
|
62
75
|
|
|
63
76
|
private
|
|
64
77
|
|
|
78
|
+
def stdout_field_summary
|
|
79
|
+
if expected_compare.stdout == actual_compare.stdout
|
|
80
|
+
"stdout: unchanged"
|
|
81
|
+
else
|
|
82
|
+
"stdout: changed"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def stderr_field_summary
|
|
87
|
+
if expected_compare.stderr == actual_compare.stderr
|
|
88
|
+
"stderr: unchanged"
|
|
89
|
+
else
|
|
90
|
+
"stderr: changed"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def status_field_summary
|
|
95
|
+
if expected_compare.status == actual_compare.status
|
|
96
|
+
"status: unchanged"
|
|
97
|
+
else
|
|
98
|
+
"status: changed (expected #{expected_compare.status}, actual #{actual_compare.status})"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
65
102
|
def command_types_match?
|
|
66
103
|
expected.command_type == actual.command_type
|
|
67
104
|
end
|
|
@@ -83,25 +120,77 @@ module Backspin
|
|
|
83
120
|
end
|
|
84
121
|
|
|
85
122
|
def generate_line_diff(expected, actual)
|
|
86
|
-
expected_lines = (expected
|
|
87
|
-
actual_lines = (actual
|
|
123
|
+
expected_lines = split_lines(expected)
|
|
124
|
+
actual_lines = split_lines(actual)
|
|
125
|
+
max_lines = [expected_lines.length, actual_lines.length].max
|
|
126
|
+
|
|
127
|
+
changed = Array.new(max_lines, false)
|
|
128
|
+
max_lines.times do |i|
|
|
129
|
+
changed[i] = (expected_lines[i] != actual_lines[i])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
visible = Array.new(max_lines, false)
|
|
133
|
+
max_lines.times do |i|
|
|
134
|
+
next unless changed[i]
|
|
135
|
+
range_start = [i - CONTEXT_LINES, 0].max
|
|
136
|
+
range_end = [i + CONTEXT_LINES, max_lines - 1].min
|
|
137
|
+
(range_start..range_end).each { |j| visible[j] = true }
|
|
138
|
+
end
|
|
88
139
|
|
|
89
140
|
diff_lines = []
|
|
90
|
-
|
|
141
|
+
first_visible = visible.index(true)
|
|
142
|
+
diff_lines << "..." if first_visible&.positive?
|
|
143
|
+
in_hunk = false
|
|
91
144
|
|
|
92
145
|
max_lines.times do |i|
|
|
93
|
-
|
|
94
|
-
|
|
146
|
+
unless visible[i]
|
|
147
|
+
if in_hunk
|
|
148
|
+
diff_lines << "..."
|
|
149
|
+
in_hunk = false
|
|
150
|
+
end
|
|
151
|
+
next
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
in_hunk = true
|
|
95
155
|
|
|
96
|
-
if
|
|
97
|
-
diff_lines << "-#{
|
|
98
|
-
diff_lines << "+#{
|
|
156
|
+
if changed[i]
|
|
157
|
+
diff_lines << "-#{render_line(expected_lines[i])}" if i < expected_lines.length
|
|
158
|
+
diff_lines << "+#{render_line(actual_lines[i])}" if i < actual_lines.length
|
|
159
|
+
else
|
|
160
|
+
line = expected_lines[i] || actual_lines[i]
|
|
161
|
+
diff_lines << " #{render_line(line)}"
|
|
99
162
|
end
|
|
100
163
|
end
|
|
101
164
|
|
|
102
165
|
diff_lines.join("\n")
|
|
103
166
|
end
|
|
104
167
|
|
|
168
|
+
def split_lines(value)
|
|
169
|
+
(value || "").lines
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def render_line(line)
|
|
173
|
+
rendered = line.to_s.chomp
|
|
174
|
+
return rendered if line.to_s.end_with?("\n")
|
|
175
|
+
|
|
176
|
+
"#{rendered}\n\\ No newline at end of output"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def maybe_truncate(diff_text)
|
|
180
|
+
return diff_text if full_diff?
|
|
181
|
+
|
|
182
|
+
lines = diff_text.lines
|
|
183
|
+
return diff_text if lines.length <= MAX_DIFF_LINES
|
|
184
|
+
|
|
185
|
+
truncated = lines.first(MAX_DIFF_LINES).join
|
|
186
|
+
truncated.chomp!
|
|
187
|
+
truncated + "\n(diff truncated, set BACKSPIN_FULL_DIFF=1 for full output)"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def full_diff?
|
|
191
|
+
ENV["BACKSPIN_FULL_DIFF"] == "1"
|
|
192
|
+
end
|
|
193
|
+
|
|
105
194
|
attr_reader :expected_compare
|
|
106
195
|
|
|
107
196
|
attr_reader :actual_compare
|
|
@@ -8,7 +8,7 @@ module Backspin
|
|
|
8
8
|
class Configuration
|
|
9
9
|
attr_accessor :scrub_credentials
|
|
10
10
|
# The directory where backspin will store its files - defaults to fixtures/backspin
|
|
11
|
-
|
|
11
|
+
attr_reader :backspin_dir
|
|
12
12
|
# Whether to raise an exception when verification fails in `run`/`capture` - defaults to true
|
|
13
13
|
attr_accessor :raise_on_verification_failure
|
|
14
14
|
# Logger for Backspin diagnostics - defaults to WARN level, logfmt-lite format
|
|
@@ -24,6 +24,10 @@ module Backspin
|
|
|
24
24
|
@logger = default_logger
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def backspin_dir=(path)
|
|
28
|
+
@backspin_dir = Pathname.new(path)
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
def add_credential_pattern(pattern)
|
|
28
32
|
@credential_patterns << pattern
|
|
29
33
|
end
|
data/lib/backspin/version.rb
CHANGED
data/mise.toml
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: backspin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rob Sanheim
|
|
@@ -33,7 +33,7 @@ extensions: []
|
|
|
33
33
|
extra_rdoc_files: []
|
|
34
34
|
files:
|
|
35
35
|
- ".circleci/config.yml"
|
|
36
|
-
- ".
|
|
36
|
+
- ".github/workflows/release.yml"
|
|
37
37
|
- ".gitignore"
|
|
38
38
|
- ".rspec"
|
|
39
39
|
- ".ruby-version"
|
|
@@ -84,7 +84,6 @@ files:
|
|
|
84
84
|
- lib/backspin/snapshot.rb
|
|
85
85
|
- lib/backspin/version.rb
|
|
86
86
|
- mise.toml
|
|
87
|
-
- release.rake
|
|
88
87
|
- script/lint
|
|
89
88
|
homepage: https://github.com/rsanheim/backspin
|
|
90
89
|
licenses:
|
|
@@ -107,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
107
106
|
- !ruby/object:Gem::Version
|
|
108
107
|
version: '0'
|
|
109
108
|
requirements: []
|
|
110
|
-
rubygems_version: 4.0.
|
|
109
|
+
rubygems_version: 4.0.3
|
|
111
110
|
specification_version: 4
|
|
112
111
|
summary: Record and replay CLI interactions for testing
|
|
113
112
|
test_files: []
|
data/.gem_release.yml
DELETED
data/release.rake
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "bundler/gem_tasks"
|
|
4
|
-
|
|
5
|
-
# Simplified release tasks using gem-release
|
|
6
|
-
# Install with: gem install gem-release
|
|
7
|
-
# https://github.com/svenfuchs/gem-release
|
|
8
|
-
namespace :release do
|
|
9
|
-
desc "Release a new version (bump, tag, release)"
|
|
10
|
-
task :version, [:level] do |t, args|
|
|
11
|
-
level = args[:level] || "patch"
|
|
12
|
-
|
|
13
|
-
# Pre-release checks
|
|
14
|
-
# Rake::Task["release:check"].invoke
|
|
15
|
-
|
|
16
|
-
puts "\nReleasing #{level} version..."
|
|
17
|
-
|
|
18
|
-
# Use gem-release to bump, tag, and release to rubygems and github
|
|
19
|
-
sh "gem bump --version #{level}"
|
|
20
|
-
new_version = File.read("lib/backspin/version.rb").match(/VERSION = "(\d+\.\d+\.\d+)"/)[1]
|
|
21
|
-
|
|
22
|
-
sh "bundle install"
|
|
23
|
-
sh "git commit -am 'Bump version to #{new_version}'"
|
|
24
|
-
sh "git push"
|
|
25
|
-
|
|
26
|
-
sh "gem release --tag --push"
|
|
27
|
-
Rake::Task["release:github"].invoke(new_version)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
desc "Create GitHub release for specified version or current version"
|
|
31
|
-
task :github, [:version] do |t, args|
|
|
32
|
-
version = args[:version] || Backspin::VERSION
|
|
33
|
-
|
|
34
|
-
if system("which gh > /dev/null 2>&1")
|
|
35
|
-
puts "\nCreating GitHub release for v#{version}..."
|
|
36
|
-
sh "gh release create v#{version} --title 'Release v#{version}' --generate-notes"
|
|
37
|
-
else
|
|
38
|
-
puts "\nGitHub CLI not found. Create release manually at:"
|
|
39
|
-
puts "https://github.com/rsanheim/backspin/releases/new?tag=v#{version}"
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
desc "Check if ready for release"
|
|
44
|
-
task :check do
|
|
45
|
-
require "open-uri"
|
|
46
|
-
require "json"
|
|
47
|
-
|
|
48
|
-
current_version = Backspin::VERSION
|
|
49
|
-
errors = []
|
|
50
|
-
|
|
51
|
-
# Check RubyGems for latest version
|
|
52
|
-
begin
|
|
53
|
-
gem_data = JSON.parse(URI.open("https://rubygems.org/api/v1/gems/backspin.json").read)
|
|
54
|
-
latest_version = gem_data["version"]
|
|
55
|
-
|
|
56
|
-
if Gem::Version.new(current_version) <= Gem::Version.new(latest_version)
|
|
57
|
-
errors << "Current version (#{current_version}) is not greater than latest released version (#{latest_version})"
|
|
58
|
-
else
|
|
59
|
-
puts "✓ Version #{current_version} is ready for release (latest: #{latest_version})"
|
|
60
|
-
end
|
|
61
|
-
rescue => e
|
|
62
|
-
puts "⚠ Could not check RubyGems version: #{e.message}"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Check git status
|
|
66
|
-
if system("git diff --quiet && git diff --cached --quiet")
|
|
67
|
-
puts "✓ No uncommitted changes"
|
|
68
|
-
else
|
|
69
|
-
errors << "You have uncommitted changes"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Check branch
|
|
73
|
-
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
|
74
|
-
if current_branch != "main"
|
|
75
|
-
puts "⚠ Not on main branch (currently on #{current_branch})"
|
|
76
|
-
print "Continue anyway? (y/N): "
|
|
77
|
-
response = $stdin.gets.chomp
|
|
78
|
-
errors << "Not on main branch" unless response.downcase == "y"
|
|
79
|
-
else
|
|
80
|
-
puts "✓ On main branch"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
unless errors.empty?
|
|
84
|
-
puts "\n❌ Cannot release:"
|
|
85
|
-
errors.each { |e| puts " - #{e}" }
|
|
86
|
-
abort
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
puts "\n✅ All checks passed!"
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Convenience tasks
|
|
94
|
-
desc "Release patch version"
|
|
95
|
-
task "release:patch" => ["release:version"]
|
|
96
|
-
|
|
97
|
-
desc "Release minor version"
|
|
98
|
-
task "release:minor" do
|
|
99
|
-
Rake::Task["release:version"].invoke("minor")
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
desc "Release major version"
|
|
103
|
-
task "release:major" do
|
|
104
|
-
Rake::Task["release:version"].invoke("major")
|
|
105
|
-
end
|