kettle-dev 1.0.13 → 1.0.15
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/.github/workflows/ancient.yml +9 -7
- data/.junie/guidelines.md +2 -1
- data/.rubocop.yml +3 -0
- data/Appraisals +13 -0
- data/CHANGELOG.md +40 -1
- data/Gemfile +3 -0
- data/README.md +30 -23
- data/README.md.example +1 -1
- data/Rakefile.example +1 -5
- data/gemfiles/modular/optional.gemfile +5 -1
- data/gemfiles/modular/optional.gemfile.example +1 -0
- data/lib/kettle/dev/ci_helpers.rb +12 -2
- data/lib/kettle/dev/ci_monitor.rb +10 -0
- data/lib/kettle/dev/git_adapter.rb +13 -0
- data/lib/kettle/dev/release_cli.rb +272 -4
- data/lib/kettle/dev/version.rb +1 -1
- data/lib/kettle-dev.rb +0 -1
- data/sig/kettle/dev/git_adapter.rbs +1 -0
- data/sig/kettle/dev/release_cli.rbs +11 -0
- data.tar.gz.sig +0 -0
- metadata +5 -4
- 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: 2bad6b81b9914fe8444ff6d626f42a2b3c567fd4c291f0bb95f47efebf34fb7b
|
4
|
+
data.tar.gz: 31921c38c683df7a384e1633a1bd46815cf98629775202dbb9354bc048c6712a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8eeeb8887d61cf1fe49bf4113ae1ac37232a85d3171ee24ba089f74dca25bec3f25b980150d72919ba611ca4cf9feab2736cc0ba4b8f25639cc057c19c2acc7
|
7
|
+
data.tar.gz: eac8be5a8e37590f83b91759b51d9af0efda35d0d51459a15476a456c44ce6551ddc65e7090393bfefd6fafa79ea0543d44546b64f0226017d67efff378b3901
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -39,13 +39,15 @@ jobs:
|
|
39
39
|
rubygems: "3.3.27"
|
40
40
|
bundler: "2.3.27"
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
# Ruby 2.4 testing is disabled because when vcr is required, any version, it raises
|
43
|
+
# NoMethodError: undefined method `delete_prefix' for "CONTENT_LENGTH":String
|
44
|
+
# # Ruby 2.4
|
45
|
+
# - ruby: "ruby-2.4"
|
46
|
+
# appraisal: "ruby-2-4"
|
47
|
+
# exec_cmd: "rake test"
|
48
|
+
# gemfile: "Appraisal.root"
|
49
|
+
# rubygems: "3.3.27"
|
50
|
+
# bundler: "2.3.27"
|
49
51
|
|
50
52
|
# Ruby 2.5
|
51
53
|
- ruby: "ruby-2.5"
|
data/.junie/guidelines.md
CHANGED
@@ -57,7 +57,8 @@ This document captures project-specific knowledge to streamline setup, testing,
|
|
57
57
|
- When adding new code or modifying existing code always add tests to cover the updated behavior, including branches, and different types of expected and unexpected inputs.
|
58
58
|
- Additional test utilities:
|
59
59
|
- rspec-stubbed_env: Use stub_env to control ENV safely within examples.
|
60
|
-
- timecop-rspec: Time manipulation available,
|
60
|
+
- timecop-rspec: Time manipulation is available, and is setup by kettle-test.
|
61
|
+
- To freeze time use `freeze: Time.new(*args)` tag on an example or group
|
61
62
|
- Running tests (verified)
|
62
63
|
- Full suite (recommended to satisfy coverage thresholds):
|
63
64
|
- bin/rspec
|
data/.rubocop.yml
CHANGED
data/Appraisals
CHANGED
@@ -27,6 +27,7 @@ appraise "unlocked_deps" do
|
|
27
27
|
eval_gemfile "modular/documentation.gemfile"
|
28
28
|
eval_gemfile "modular/style.gemfile"
|
29
29
|
eval_gemfile "modular/optional.gemfile"
|
30
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
30
31
|
end
|
31
32
|
|
32
33
|
# Used for head (nightly) releases of ruby, truffleruby, and jruby.
|
@@ -36,6 +37,7 @@ appraise "head" do
|
|
36
37
|
gem "mutex_m", ">= 0.2"
|
37
38
|
gem "stringio", ">= 3.0"
|
38
39
|
gem "benchmark", "~> 0.4", ">= 0.4.1"
|
40
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
39
41
|
end
|
40
42
|
|
41
43
|
# Used for current releases of ruby, truffleruby, and jruby.
|
@@ -44,6 +46,7 @@ appraise "current" do
|
|
44
46
|
gem "erb"
|
45
47
|
gem "mutex_m", ">= 0.2"
|
46
48
|
gem "stringio", ">= 3.0"
|
49
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
47
50
|
end
|
48
51
|
|
49
52
|
appraise "ruby-2-3" do
|
@@ -51,32 +54,38 @@ appraise "ruby-2-3" do
|
|
51
54
|
# This means we have no choice but to use the erb that shipped with Ruby 2.3
|
52
55
|
# /opt/hostedtoolcache/Ruby/2.3.8/x64/lib/ruby/gems/2.3.0/gems/erb-2.2.2/lib/erb.rb:670:in `prepare_trim_mode': undefined method `match?' for "-":String (NoMethodError)
|
53
56
|
# spec.add_development_dependency("erb", ">= 2.2") # ruby >= 2.3.0, not SemVer, old rubies get dropped in a patch.
|
57
|
+
eval_gemfile "modular/recording/r2.3/recording.gemfile"
|
54
58
|
end
|
55
59
|
|
56
60
|
appraise "ruby-2-4" do
|
57
61
|
gem "erb"
|
62
|
+
eval_gemfile "modular/recording/r2.4/recording.gemfile"
|
58
63
|
end
|
59
64
|
|
60
65
|
appraise "ruby-2-5" do
|
61
66
|
gem "erb"
|
67
|
+
eval_gemfile "modular/recording/r2.5/recording.gemfile"
|
62
68
|
end
|
63
69
|
|
64
70
|
appraise "ruby-2-6" do
|
65
71
|
gem "erb"
|
66
72
|
gem "mutex_m", "~> 0.2"
|
67
73
|
gem "stringio", "~> 3.0"
|
74
|
+
eval_gemfile "modular/recording/r2.5/recording.gemfile"
|
68
75
|
end
|
69
76
|
|
70
77
|
appraise "ruby-2-7" do
|
71
78
|
gem "erb"
|
72
79
|
gem "mutex_m", "~> 0.2"
|
73
80
|
gem "stringio", "~> 3.0"
|
81
|
+
eval_gemfile "modular/recording/r2.5/recording.gemfile"
|
74
82
|
end
|
75
83
|
|
76
84
|
appraise "ruby-3-0" do
|
77
85
|
gem "erb"
|
78
86
|
gem "mutex_m", "~> 0.2"
|
79
87
|
gem "stringio", "~> 3.0"
|
88
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
80
89
|
end
|
81
90
|
|
82
91
|
appraise "ruby-3-1" do
|
@@ -85,6 +94,7 @@ appraise "ruby-3-1" do
|
|
85
94
|
gem "erb"
|
86
95
|
gem "mutex_m", "~> 0.2"
|
87
96
|
gem "stringio", "~> 3.0"
|
97
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
88
98
|
end
|
89
99
|
|
90
100
|
appraise "ruby-3-2" do
|
@@ -93,9 +103,11 @@ appraise "ruby-3-2" do
|
|
93
103
|
gem "erb"
|
94
104
|
gem "mutex_m", "~> 0.2"
|
95
105
|
gem "stringio", "~> 3.0"
|
106
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
96
107
|
end
|
97
108
|
|
98
109
|
appraise "ruby-3-3" do
|
110
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
99
111
|
gem "erb"
|
100
112
|
gem "mutex_m", "~> 0.2"
|
101
113
|
gem "stringio", "~> 3.0"
|
@@ -115,6 +127,7 @@ appraise "coverage" do
|
|
115
127
|
gem "stringio", "~> 3.0"
|
116
128
|
eval_gemfile "modular/coverage.gemfile"
|
117
129
|
eval_gemfile "modular/optional.gemfile"
|
130
|
+
eval_gemfile "modular/recording/r3/recording.gemfile"
|
118
131
|
end
|
119
132
|
|
120
133
|
# Only run linter on latest Ruby version (but, in support of oldest supported Ruby version)
|
data/CHANGELOG.md
CHANGED
@@ -24,6 +24,41 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
24
24
|
### Fixed
|
25
25
|
### Security
|
26
26
|
|
27
|
+
## [1.0.15] - 2025-08-29
|
28
|
+
- TAG: [v1.0.15][1.0.15t]
|
29
|
+
- COVERAGE: 98.17% -- 2259/2301 lines in 20 files
|
30
|
+
- BRANCH COVERAGE: 81.00% -- 908/1121 branches in 20 files
|
31
|
+
- 76.03% documented
|
32
|
+
### Added
|
33
|
+
- kettle-release: early validation of identical set of copyright years in README.md and CHANGELOG.md, adds current year if missing, aborts on mismatch
|
34
|
+
- kettle-release: update KLOC in README.md
|
35
|
+
- kettle-release: update Rakefile.example with version and date
|
36
|
+
### Changed
|
37
|
+
- kettle-release: print package name and version released as final line
|
38
|
+
- use git adapter to wrap more git commands to make tests easier to build
|
39
|
+
- stop testing Ruby 2.4 on CI due to a strange issue with VCR.
|
40
|
+
- still testing Ruby 2.3
|
41
|
+
### Fixed
|
42
|
+
- include gemfiles/modular/*gemfile.example with packaged gem
|
43
|
+
- CI workflow result polling logic revised:
|
44
|
+
- includes a delay
|
45
|
+
- scopes queries to specific commit SHA
|
46
|
+
- prevents false failures from previous runs
|
47
|
+
|
48
|
+
## [1.0.14] - 2025-08-28
|
49
|
+
- TAG: [v1.0.14][1.0.14t]
|
50
|
+
- COVERAGE: 97.70% -- 2125/2175 lines in 20 files
|
51
|
+
- BRANCH COVERAGE: 78.77% -- 842/1069 branches in 20 files
|
52
|
+
- 76.03% documented
|
53
|
+
### Added
|
54
|
+
- kettle-release: Push tags to additional remotes after release
|
55
|
+
### Changed
|
56
|
+
- Improve .gitlab-ci.yml pipeline
|
57
|
+
### Fixed
|
58
|
+
- Removed README badges for unsupported old Ruby versions
|
59
|
+
- Minor inconsistencies in template files
|
60
|
+
- git added as a dependency to optional.gemfile instead of the example template
|
61
|
+
|
27
62
|
## [1.0.13] - 2025-08-28
|
28
63
|
- TAG: [v1.0.13][1.0.13t]
|
29
64
|
- COVERAGE: 41.94% -- 65/155 lines in 6 files
|
@@ -222,7 +257,7 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
222
257
|
- Selecting will run the selected workflow via `act`
|
223
258
|
- This may move to its own gem in the future.
|
224
259
|
|
225
|
-
[Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.
|
260
|
+
[Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.15...HEAD
|
226
261
|
[1.0.0]: https://github.com/kettle-rb/kettle-dev/compare/a427c302df09cfe4253a7c8d400333f9a4c1a208...v1.0.0
|
227
262
|
[1.0.0t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.0
|
228
263
|
[1.0.1]: https://gitlab.com/kettle-rb/kettle-dev/-/compare/v1.0.0...v1.0.1
|
@@ -251,3 +286,7 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
251
286
|
[1.0.12t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.12
|
252
287
|
[1.0.13]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.12...v1.0.13
|
253
288
|
[1.0.13t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.13
|
289
|
+
[1.0.14]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.13...v1.0.14
|
290
|
+
[1.0.14t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.14
|
291
|
+
[1.0.15]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.14...v1.0.15
|
292
|
+
[1.0.15t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.15
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -50,6 +50,28 @@ and concordant releases of JRuby, and TruffleRuby.
|
|
50
50
|
|
51
51
|
## 💡 Info you can shake a stick at
|
52
52
|
|
53
|
+
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
|
54
|
+
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
55
|
+
| Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
|
56
|
+
| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i] <br/> [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
|
57
|
+
| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
|
58
|
+
| Works with MRI Ruby 2 | <br/> [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] |
|
59
|
+
| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
|
60
|
+
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] |
|
61
|
+
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
62
|
+
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
|
63
|
+
| Support | [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
64
|
+
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
65
|
+
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
|
66
|
+
|
67
|
+
### Compatibility
|
68
|
+
|
69
|
+
Compatible with Ruby 2.3+, and concordant releases of JRuby, and TruffleRuby.
|
70
|
+
|
71
|
+
| 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 |
|
72
|
+
|------------------------------------------------|-------------------------------------------------------------------------------------|
|
73
|
+
| 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
|
74
|
+
|
53
75
|
### Federated DVCS
|
54
76
|
|
55
77
|
<details>
|
@@ -73,7 +95,7 @@ and concordant releases of JRuby, and TruffleRuby.
|
|
73
95
|
|
74
96
|
[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
|
75
97
|
|
76
|
-
- 💡Subscribe for support guarantees covering _all_ FLOSS dependencies
|
98
|
+
- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
|
77
99
|
- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
|
78
100
|
- 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
|
79
101
|
|
@@ -85,30 +107,19 @@ Alternatively:
|
|
85
107
|
|
86
108
|
</details>
|
87
109
|
|
88
|
-
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
|
89
|
-
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
90
|
-
| Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
|
91
|
-
| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i] <br/> [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
|
92
|
-
| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
|
93
|
-
| Works with MRI Ruby 2 | ![Ruby 2.0 Compat][💎ruby-2.0i] ![Ruby 2.1 Compat][💎ruby-2.1i] ![Ruby 2.2 Compat][💎ruby-2.2i] <br/> [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] |
|
94
|
-
| Works with MRI Ruby 1 | ![Ruby 1.9 Compat][💎ruby-1.9i] |
|
95
|
-
| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
|
96
|
-
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] |
|
97
|
-
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
98
|
-
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
|
99
|
-
| Support | [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
100
|
-
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
101
|
-
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
|
102
|
-
|
103
110
|
## ✨ Installation
|
104
111
|
|
105
112
|
Install the gem and add to the application's Gemfile by executing:
|
106
113
|
|
107
|
-
|
114
|
+
```console
|
115
|
+
bundle add kettle-dev
|
116
|
+
```
|
108
117
|
|
109
118
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
110
119
|
|
111
|
-
|
120
|
+
```console
|
121
|
+
gem install kettle-dev
|
122
|
+
```
|
112
123
|
|
113
124
|
### 🔒 Secure Installation
|
114
125
|
|
@@ -645,10 +656,6 @@ Thanks for RTFM. ☺️
|
|
645
656
|
[🚎13-🔒️-wfi]: https://github.com/kettle-rb/kettle-dev/actions/workflows/locked_deps.yml/badge.svg
|
646
657
|
[🚎14-🔓️-wf]: https://github.com/kettle-rb/kettle-dev/actions/workflows/unlocked_deps.yml
|
647
658
|
[🚎14-🔓️-wfi]: https://github.com/kettle-rb/kettle-dev/actions/workflows/unlocked_deps.yml/badge.svg
|
648
|
-
[💎ruby-1.9i]: https://img.shields.io/badge/Ruby-1.9_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
|
649
|
-
[💎ruby-2.0i]: https://img.shields.io/badge/Ruby-2.0_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
|
650
|
-
[💎ruby-2.1i]: https://img.shields.io/badge/Ruby-2.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
|
651
|
-
[💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
|
652
659
|
[💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
653
660
|
[💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
654
661
|
[💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
@@ -697,7 +704,7 @@ Thanks for RTFM. ☺️
|
|
697
704
|
[📌gitmoji]:https://gitmoji.dev
|
698
705
|
[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20😜%20😍-34495e.svg?style=flat-square
|
699
706
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
700
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.
|
707
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.301-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
701
708
|
[🔐security]: SECURITY.md
|
702
709
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
703
710
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/README.md.example
CHANGED
@@ -495,7 +495,7 @@ Thanks for RTFM. ☺️
|
|
495
495
|
[📌gitmoji]:https://gitmoji.dev
|
496
496
|
[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20😜%20😍-34495e.svg?style=flat-square
|
497
497
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
498
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-
|
498
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.301-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
499
499
|
[🔐security]: SECURITY.md
|
500
500
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
501
501
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/Rakefile.example
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# kettle-dev Rakefile v1.0.
|
3
|
+
# kettle-dev Rakefile v1.0.15 - 2025-08-29
|
4
4
|
# Ruby 2.3 (Safe Navigation) or higher required
|
5
5
|
#
|
6
6
|
# MIT License (see License.txt)
|
@@ -58,14 +58,10 @@ task :default do
|
|
58
58
|
puts "Default task complete."
|
59
59
|
end
|
60
60
|
|
61
|
-
Kettle::Dev.install_tasks
|
62
|
-
|
63
61
|
### RELEASE TASKS
|
64
62
|
# Setup stone_checksums
|
65
63
|
begin
|
66
64
|
require "stone_checksums"
|
67
|
-
|
68
|
-
GemChecksums.install_tasks
|
69
65
|
rescue LoadError
|
70
66
|
desc("(stub) build:generate_checksums is unavailable")
|
71
67
|
task("build:generate_checksums") do
|
@@ -1 +1,5 @@
|
|
1
|
-
# Optional dependencies are not
|
1
|
+
# Optional dependencies are not depended on directly, but may be used if present.
|
2
|
+
# git gem is not a direct dependency for two reasons:
|
3
|
+
# 1. it is incompatible with Truffleruby v23
|
4
|
+
# 2. it depends on activesupport, which is too heavy
|
5
|
+
gem "git", ">= 1.19.1" # ruby >= 2.3
|
@@ -0,0 +1 @@
|
|
1
|
+
# Optional dependencies are not depended on directly, but may be used if present.
|
@@ -90,14 +90,24 @@ module Kettle
|
|
90
90
|
return unless owner && repo
|
91
91
|
b = branch || current_branch
|
92
92
|
return unless b
|
93
|
-
|
93
|
+
# Scope to the exact commit SHA when available to avoid picking up a previous run on the same branch.
|
94
|
+
sha_out, status = Open3.capture2("git", "rev-parse", "HEAD")
|
95
|
+
sha = status.success? ? sha_out.strip : nil
|
96
|
+
base_url = "https://api.github.com/repos/#{owner}/#{repo}/actions/workflows/#{workflow_file}/runs?branch=#{URI.encode_www_form_component(b)}&per_page=5"
|
97
|
+
uri = URI(base_url)
|
94
98
|
req = Net::HTTP::Get.new(uri)
|
95
99
|
req["User-Agent"] = "kettle-dev/ci-helpers"
|
96
100
|
req["Authorization"] = "token #{token}" if token && !token.empty?
|
97
101
|
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
|
98
102
|
return unless res.is_a?(Net::HTTPSuccess)
|
99
103
|
data = JSON.parse(res.body)
|
100
|
-
|
104
|
+
runs = Array(data["workflow_runs"]) || []
|
105
|
+
# Try to match by head_sha first; fall back to first run (branch-scoped) if none matches yet.
|
106
|
+
run = if sha
|
107
|
+
runs.find { |r| r["head_sha"] == sha } || runs.first
|
108
|
+
else
|
109
|
+
runs.first
|
110
|
+
end
|
101
111
|
return unless run
|
102
112
|
{
|
103
113
|
"status" => run["status"],
|
@@ -70,6 +70,16 @@ module Kettle
|
|
70
70
|
pbar = if defined?(ProgressBar)
|
71
71
|
ProgressBar.create(title: "CI", total: total, format: "%t %b %c/%C", length: 30)
|
72
72
|
end
|
73
|
+
# Small initial delay to allow GitHub to register the newly pushed commit and enqueue workflows.
|
74
|
+
# Configurable via K_RELEASE_CI_INITIAL_SLEEP (seconds); defaults to 3s.
|
75
|
+
begin
|
76
|
+
initial_sleep = begin
|
77
|
+
Integer(ENV["K_RELEASE_CI_INITIAL_SLEEP"])
|
78
|
+
rescue
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
sleep((initial_sleep && initial_sleep >= 0) ? initial_sleep : 3)
|
73
83
|
idx = 0
|
74
84
|
loop do
|
75
85
|
wf = workflows[idx]
|
@@ -15,6 +15,19 @@ module Kettle
|
|
15
15
|
#
|
16
16
|
# Public API is intentionally small and only includes what we need right now.
|
17
17
|
class GitAdapter
|
18
|
+
# Execute a git command and capture its stdout and success flag.
|
19
|
+
# This is a generic escape hatch used by higher-level code for read-only
|
20
|
+
# queries that aren't covered by the explicit adapter API. Tests can stub
|
21
|
+
# this method to avoid shelling out.
|
22
|
+
# @param args [Array<String>]
|
23
|
+
# @return [Array<(String, Boolean)>] [output, success]
|
24
|
+
def capture(args)
|
25
|
+
out, status = Open3.capture2("git", *args)
|
26
|
+
[out.strip, status.success?]
|
27
|
+
rescue StandardError
|
28
|
+
["", false]
|
29
|
+
end
|
30
|
+
|
18
31
|
# Create a new adapter rooted at the current working directory.
|
19
32
|
# @return [void]
|
20
33
|
def initialize
|
@@ -73,12 +73,26 @@ module Kettle
|
|
73
73
|
overall = Gem::Version.new(latest_overall)
|
74
74
|
cur_series = cur.segments[0, 2]
|
75
75
|
overall_series = overall.segments[0, 2]
|
76
|
+
# Ensure latest_for_series actually matches our current series; ignore otherwise.
|
77
|
+
if latest_for_series
|
78
|
+
lfs_series = Gem::Version.new(latest_for_series).segments[0, 2]
|
79
|
+
latest_for_series = nil unless lfs_series == cur_series
|
80
|
+
end
|
81
|
+
# Determine the sanity-check target correctly for the current series.
|
82
|
+
# If RubyGems has a newer overall series than our current series, only compare
|
83
|
+
# against the latest published in our current series. If that cannot be determined
|
84
|
+
# (e.g., offline), skip the sanity check rather than treating the overall as target.
|
76
85
|
target = if (cur_series <=> overall_series) == -1
|
77
86
|
latest_for_series
|
78
87
|
else
|
79
88
|
latest_overall
|
80
89
|
end
|
81
|
-
|
90
|
+
# IMPORTANT: Never treat a higher different-series "latest_overall" as a downgrade target.
|
91
|
+
# If our current series is behind overall and RubyGems does not report a latest_for_series,
|
92
|
+
# then we cannot determine the correct target for this series and should skip the check.
|
93
|
+
if (cur_series <=> overall_series) == -1 && target.nil?
|
94
|
+
puts "Could not determine latest released version from RubyGems (offline?). Proceeding without sanity check."
|
95
|
+
elsif target
|
82
96
|
bump = Kettle::Dev::Versioning.classify_bump(target, version)
|
83
97
|
case bump
|
84
98
|
when :same
|
@@ -104,6 +118,23 @@ module Kettle
|
|
104
118
|
print("> ")
|
105
119
|
ans = Kettle::Dev::InputAdapter.gets&.strip
|
106
120
|
abort("Aborted: please update version.rb and CHANGELOG.md, then re-run.") unless ans&.downcase&.start_with?("y")
|
121
|
+
|
122
|
+
# Initial validation: Ensure README.md and LICENSE.txt have identical sets of copyright years; also ensure current year present when matched
|
123
|
+
validate_copyright_years!
|
124
|
+
|
125
|
+
# Ensure README KLOC badge reflects current CHANGELOG coverage denominator
|
126
|
+
begin
|
127
|
+
update_readme_kloc_badge!
|
128
|
+
rescue StandardError => e
|
129
|
+
warn("Failed to update KLOC badge in README: #{e.class}: #{e.message}")
|
130
|
+
end
|
131
|
+
|
132
|
+
# Update Rakefile.example header banner with current version and date
|
133
|
+
begin
|
134
|
+
update_rakefile_example_header!(version)
|
135
|
+
rescue StandardError => e
|
136
|
+
warn("Failed to update Rakefile.example header: #{e.class}: #{e.message}")
|
137
|
+
end
|
107
138
|
end
|
108
139
|
|
109
140
|
# 3. bin/setup
|
@@ -208,11 +239,225 @@ module Kettle
|
|
208
239
|
maybe_create_github_release!(version)
|
209
240
|
end
|
210
241
|
|
211
|
-
|
242
|
+
# 18. push tags to remotes (new final step)
|
243
|
+
push_tags! if @start_step <= 18
|
244
|
+
|
245
|
+
# Final success message
|
246
|
+
begin
|
247
|
+
version ||= detect_version
|
248
|
+
gem_name = detect_gem_name
|
249
|
+
puts "\n🚀 Release #{gem_name} v#{version} Complete 🚀"
|
250
|
+
rescue StandardError
|
251
|
+
# Fallback if detection fails for any reason
|
252
|
+
puts "\n🚀 Release v#{version || "unknown"} Complete 🚀"
|
253
|
+
end
|
212
254
|
end
|
213
255
|
|
214
256
|
private
|
215
257
|
|
258
|
+
# Update the README KLOC badge number based on the denominator in the current version's COVERAGE line in CHANGELOG.md.
|
259
|
+
# - Parses the current version section of CHANGELOG.md
|
260
|
+
# - Finds a line matching: "- COVERAGE: ... -- <tested>/<total> lines ..."
|
261
|
+
# - Computes KLOC = total / 1000.0
|
262
|
+
# - Formats with three decimals (e.g., 0.076, 2.175, 10.123)
|
263
|
+
# - Rewrites the [🧮kloc-img] badge line in README.md (and README.md.example when present)
|
264
|
+
# replacing only the numeric portion after "KLOC-" while preserving other URL params.
|
265
|
+
def update_readme_kloc_badge!
|
266
|
+
version = detect_version
|
267
|
+
# Extract only the current version's section
|
268
|
+
section, _compare_ref, _tag_ref = extract_changelog_for_version(version)
|
269
|
+
return unless section
|
270
|
+
|
271
|
+
# Example match: "- COVERAGE: 97.70% -- 2125/2175 lines in 20 files"
|
272
|
+
m = section.lines.find { |l| l =~ /-\s*COVERAGE:\s*.+--\s*\d+\/(\d+)\s+lines/i }
|
273
|
+
return unless m
|
274
|
+
denom = m.match(/-\s*COVERAGE:\s*.+--\s*\d+\/(\d+)\s+lines/i)[1].to_i
|
275
|
+
kloc = denom.to_f / 1000.0
|
276
|
+
kloc_str = format("%.3f", kloc)
|
277
|
+
|
278
|
+
update_badge_number_in_file(File.join(@root, "README.md"), kloc_str)
|
279
|
+
example_path = File.join(@root, "README.md.example")
|
280
|
+
update_badge_number_in_file(example_path, kloc_str) if File.file?(example_path)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Helper to update the [🧮kloc-img] badge in the given file path.
|
284
|
+
# Replaces only the numeric portion after "KLOC-" keeping other URL parts intact.
|
285
|
+
def update_badge_number_in_file(path, kloc_str)
|
286
|
+
return unless File.file?(path)
|
287
|
+
content = File.read(path)
|
288
|
+
# Match the specific reference line, capture groups around the number
|
289
|
+
# Example: [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.175-FFDD67.svg?style=...
|
290
|
+
new_content = content.gsub(/(\[🧮kloc-img\]:\s*https?:\/\/img\.shields\.io\/badge\/KLOC-)(\d+(?:\.\d+)?)(-[^\s]*)/, "\\1#{kloc_str}\\3")
|
291
|
+
if new_content != content
|
292
|
+
File.write(path, new_content)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Update Rakefile.example banner to include current gem version and current date.
|
297
|
+
# Looks for a line starting with "# kettle-dev Rakefile v" and replaces version/date.
|
298
|
+
def update_rakefile_example_header!(version)
|
299
|
+
path = File.join(@root, "Rakefile.example")
|
300
|
+
return unless File.file?(path)
|
301
|
+
content = File.read(path)
|
302
|
+
today = Time.now.strftime("%Y-%m-%d")
|
303
|
+
new_line = "# kettle-dev Rakefile v#{version} - #{today}"
|
304
|
+
new_content = content.gsub(/^# kettle-dev Rakefile v.*$/, new_line)
|
305
|
+
if new_content != content
|
306
|
+
File.write(path, new_content)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
# Validate that README.md and CHANGELOG.md contain identical sets of copyright years.
|
311
|
+
# This helps ensure docs are kept in sync when bumping the years.
|
312
|
+
# Aborts with a helpful message when they differ.
|
313
|
+
def validate_copyright_years!
|
314
|
+
require "set"
|
315
|
+
readme = File.join(@root, "README.md")
|
316
|
+
license = File.join(@root, "LICENSE.txt")
|
317
|
+
unless File.file?(readme) && File.file?(license)
|
318
|
+
# If either file is missing, skip this check silently (some projects might not have both initially)
|
319
|
+
return
|
320
|
+
end
|
321
|
+
|
322
|
+
# Normalize year formatting in both files before comparing
|
323
|
+
reformat_copyright_year_lines!(readme)
|
324
|
+
reformat_copyright_year_lines!(license)
|
325
|
+
|
326
|
+
r_years = extract_years_from_file(readme)
|
327
|
+
l_years = extract_years_from_file(license)
|
328
|
+
if r_years == l_years
|
329
|
+
# If they match, ensure the current year is present; if not, inject it into both files.
|
330
|
+
current_year = Time.now.year
|
331
|
+
unless r_years.include?(current_year)
|
332
|
+
# Update both files by appending current year to the set and rewriting the lines canonically
|
333
|
+
updated_years = r_years.dup
|
334
|
+
updated_years << current_year
|
335
|
+
# Write back to both files using canonical collapse formatting
|
336
|
+
inject_years_into_file!(readme, updated_years)
|
337
|
+
inject_years_into_file!(license, updated_years)
|
338
|
+
end
|
339
|
+
return
|
340
|
+
end
|
341
|
+
|
342
|
+
abort(<<~MSG)
|
343
|
+
Mismatched copyright years between README.md and LICENSE.txt.
|
344
|
+
README.md: #{r_years.to_a.sort.join(", ")}
|
345
|
+
LICENSE.txt: #{l_years.to_a.sort.join(", ")}
|
346
|
+
Please update both files so they contain the identical set of years.
|
347
|
+
MSG
|
348
|
+
end
|
349
|
+
|
350
|
+
# Extract a Set of Integer years from the given file.
|
351
|
+
# It searches for lines containing the word "Copyright" (case-insensitive),
|
352
|
+
# then parses four-digit years and year ranges like "2012-2015" (hyphen or en dash).
|
353
|
+
# Returns Set[Integer].
|
354
|
+
def extract_years_from_file(path)
|
355
|
+
require "set"
|
356
|
+
years = Set.new
|
357
|
+
content = File.read(path)
|
358
|
+
# Only consider lines that look like copyright notices to reduce false positives
|
359
|
+
content.each_line do |line|
|
360
|
+
next unless line =~ /copyright/i
|
361
|
+
|
362
|
+
# Expand ranges first (supports hyphen-minus and en dash)
|
363
|
+
line.scan(/\b(19\d{2}|20\d{2})\s*[\-–]\s*(19\d{2}|20\d{2})\b/).each do |a, b|
|
364
|
+
s = a.to_i
|
365
|
+
e = b.to_i
|
366
|
+
if e < s
|
367
|
+
s, e = e, s
|
368
|
+
end
|
369
|
+
(s..e).each { |y| years << y }
|
370
|
+
end
|
371
|
+
|
372
|
+
# Then single standalone years
|
373
|
+
line.scan(/\b(19\d{2}|20\d{2})\b/).each do |y|
|
374
|
+
years << y[0].to_i
|
375
|
+
end
|
376
|
+
end
|
377
|
+
years
|
378
|
+
end
|
379
|
+
|
380
|
+
# Collapse a set/array of years into a canonical, comma-separated string, combining
|
381
|
+
# consecutive runs into ranges with a hyphen (YYYY-YYYY) and leaving gaps as commas.
|
382
|
+
def collapse_years(enum)
|
383
|
+
arr = enum.to_a.map(&:to_i).uniq.sort
|
384
|
+
return "" if arr.empty?
|
385
|
+
segments = []
|
386
|
+
start = arr.first
|
387
|
+
prev = start
|
388
|
+
arr[1..-1].to_a.each do |y|
|
389
|
+
if y == prev + 1
|
390
|
+
prev = y
|
391
|
+
next
|
392
|
+
else
|
393
|
+
segments << ((start == prev) ? start.to_s : "#{start}-#{prev}")
|
394
|
+
start = prev = y
|
395
|
+
end
|
396
|
+
end
|
397
|
+
segments << ((start == prev) ? start.to_s : "#{start}-#{prev}")
|
398
|
+
segments.join(", ")
|
399
|
+
end
|
400
|
+
|
401
|
+
# Inject the provided set of years into copyright lines, rewriting them in canonical form.
|
402
|
+
# - Finds lines containing 'copyright' (case-insensitive) and a years blob.
|
403
|
+
# - Replaces that blob with the canonical collapsed form of the union of existing years and given years.
|
404
|
+
# - If multiple copyright lines, updates each consistently.
|
405
|
+
def inject_years_into_file!(path, years_set)
|
406
|
+
content = File.read(path)
|
407
|
+
changed = false
|
408
|
+
canonical_all = collapse_years(years_set)
|
409
|
+
new_lines = content.each_line.map do |line|
|
410
|
+
unless line =~ /copyright/i
|
411
|
+
next line
|
412
|
+
end
|
413
|
+
m = line.match(/\A(?<pre>.*?copyright[^0-9]*)(?<years>(?:\b(?:19|20)\d{2}\b(?:\s*[\-–]\s*\b(?:19|20)\d{2}\b)?)(?:\s*,\s*\b(?:19|20)\d{2}\b(?:\s*[\-–]\s*\b(?:19|20)\d{2}\b)?)*)(?<post>.*)\z/i)
|
414
|
+
unless m
|
415
|
+
next line
|
416
|
+
end
|
417
|
+
new_line = "#{m[:pre]}#{canonical_all}#{m[:post]}"
|
418
|
+
changed ||= (new_line != line)
|
419
|
+
new_line
|
420
|
+
end
|
421
|
+
if changed
|
422
|
+
File.write(path, new_lines.join)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Rewrite copyright lines in-place to collapse years into canonical ranges.
|
427
|
+
# Only modifies lines that contain the word "copyright" (case-insensitive).
|
428
|
+
def reformat_copyright_year_lines!(path)
|
429
|
+
content = File.read(path)
|
430
|
+
changed = false
|
431
|
+
new_lines = content.each_line.map do |line|
|
432
|
+
unless line =~ /copyright/i
|
433
|
+
next line
|
434
|
+
end
|
435
|
+
# Capture three parts: prefix up to first year, the year blob, and the rest
|
436
|
+
m = line.match(/\A(?<pre>.*?copyright[^0-9]*)(?<years>(?:\b(?:19|20)\d{2}\b(?:\s*[\-–]\s*\b(?:19|20)\d{2}\b)?)(?:\s*,\s*\b(?:19|20)\d{2}\b(?:\s*[\-–]\s*\b(?:19|20)\d{2}\b)?)*)(?<post>.*)\z/i)
|
437
|
+
unless m
|
438
|
+
# No parsable year sequence on this line; leave as-is
|
439
|
+
next line
|
440
|
+
end
|
441
|
+
years_blob = m[:years]
|
442
|
+
# Reuse extraction logic on just the years blob
|
443
|
+
years = []
|
444
|
+
years_blob.scan(/\b(19\d{2}|20\d{2})\s*[\-–]\s*(19\d{2}|20\d{2})\b/).each do |a, b|
|
445
|
+
s = a.to_i
|
446
|
+
e = b.to_i
|
447
|
+
s, e = e, s if e < s
|
448
|
+
(s..e).each { |y| years << y }
|
449
|
+
end
|
450
|
+
years_blob.scan(/\b(19\d{2}|20\d{2})\b/).each { |y| years << y[0].to_i }
|
451
|
+
canonical = collapse_years(years)
|
452
|
+
new_line = "#{m[:pre]}#{canonical}#{m[:post]}"
|
453
|
+
changed ||= (new_line != line)
|
454
|
+
new_line
|
455
|
+
end
|
456
|
+
if changed
|
457
|
+
File.write(path, new_lines.join)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
216
461
|
def monitor_workflows_after_push!
|
217
462
|
# Delegate to shared CI monitor to keep logic DRY across release flow and rake tasks
|
218
463
|
require "kettle/dev/ci_monitor"
|
@@ -233,8 +478,9 @@ module Kettle
|
|
233
478
|
end
|
234
479
|
|
235
480
|
def git_output(args)
|
236
|
-
|
237
|
-
|
481
|
+
# Route all git interactions through the GitAdapter so tests can safely mock them
|
482
|
+
out, ok = @git.capture(args)
|
483
|
+
[out.to_s.strip, !!ok]
|
238
484
|
end
|
239
485
|
|
240
486
|
def ensure_git_user!
|
@@ -339,6 +585,7 @@ module Kettle
|
|
339
585
|
uri = URI("https://rubygems.org/api/v1/versions/#{gem_name}.json")
|
340
586
|
res = Net::HTTP.get_response(uri)
|
341
587
|
return [nil, nil] unless res.is_a?(Net::HTTPSuccess)
|
588
|
+
|
342
589
|
data = JSON.parse(res.body)
|
343
590
|
versions = data.map { |h| h["number"] }.compact
|
344
591
|
versions.reject! { |v| v.to_s.include?("-pre") || v.to_s.include?(".pre") || v.to_s =~ /[a-zA-Z]/ }
|
@@ -409,6 +656,27 @@ module Kettle
|
|
409
656
|
end
|
410
657
|
end
|
411
658
|
|
659
|
+
def push_tags!
|
660
|
+
# After release, push tags to remotes according to policy:
|
661
|
+
# 1) If a remote named "all" exists, push tags only to it.
|
662
|
+
# 2) Otherwise, if other remotes exist, push tags to each of them.
|
663
|
+
# 3) If no remotes are configured, push tags using default remote.
|
664
|
+
if has_remote?("all")
|
665
|
+
run_cmd!("git push all --tags")
|
666
|
+
return
|
667
|
+
end
|
668
|
+
|
669
|
+
remotes = list_remotes
|
670
|
+
remotes -= ["all"] if remotes
|
671
|
+
if remotes.nil? || remotes.empty?
|
672
|
+
run_cmd!("git push --tags")
|
673
|
+
else
|
674
|
+
remotes.each do |remote|
|
675
|
+
run_cmd!("git push #{Shellwords.escape(remote)} --tags")
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
412
680
|
def detect_trunk_branch
|
413
681
|
out, ok = git_output(["remote", "show", "origin"])
|
414
682
|
abort("Failed to get origin remote info.") unless ok
|
data/lib/kettle/dev/version.rb
CHANGED
data/lib/kettle-dev.rb
CHANGED
@@ -3,6 +3,17 @@ module Kettle
|
|
3
3
|
class ReleaseCLI
|
4
4
|
def initialize: (?start_step: Integer) -> void
|
5
5
|
def run: () -> void
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def update_readme_kloc_badge!: () -> void
|
10
|
+
def update_badge_number_in_file: (String path, String kloc_str) -> void
|
11
|
+
def update_rakefile_example_header!: (String version) -> void
|
12
|
+
def validate_copyright_years!: () -> void
|
13
|
+
def extract_years_from_file: (String path) -> ::Set[Integer]
|
14
|
+
def collapse_years: (::_ToA[Integer] enum) -> String
|
15
|
+
def reformat_copyright_year_lines!: (String path) -> void
|
16
|
+
def inject_years_into_file!: (String path, ::Set[Integer] years_set) -> void
|
6
17
|
end
|
7
18
|
end
|
8
19
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kettle-dev
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter H. Boling
|
@@ -285,6 +285,7 @@ files:
|
|
285
285
|
- gemfiles/modular/coverage.gemfile
|
286
286
|
- gemfiles/modular/documentation.gemfile
|
287
287
|
- gemfiles/modular/optional.gemfile
|
288
|
+
- gemfiles/modular/optional.gemfile.example
|
288
289
|
- gemfiles/modular/style.gemfile
|
289
290
|
- lib/kettle-dev.rb
|
290
291
|
- lib/kettle/dev.rb
|
@@ -339,10 +340,10 @@ licenses:
|
|
339
340
|
- MIT
|
340
341
|
metadata:
|
341
342
|
homepage_uri: https://kettle-dev.galtzo.com/
|
342
|
-
source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.0.
|
343
|
-
changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.0.
|
343
|
+
source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.0.15
|
344
|
+
changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.0.15/CHANGELOG.md
|
344
345
|
bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
|
345
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.
|
346
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.15
|
346
347
|
funding_uri: https://github.com/sponsors/pboling
|
347
348
|
wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
|
348
349
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
Binary file
|