kettle-dev 2.2.18 → 2.2.19
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/CHANGELOG.md +17 -1
- data/CONTRIBUTING.md +4 -1
- data/README.md +1 -1
- data/exe/kettle-changelog +51 -53
- data/exe/kettle-release +7 -3
- data/lib/kettle/dev/ci_monitor.rb +6 -6
- data/lib/kettle/dev/gha_sha_pins_cli.rb +20 -25
- data/lib/kettle/dev/git_adapter.rb +20 -24
- data/lib/kettle/dev/git_commit_footer.rb +1 -1
- data/lib/kettle/dev/open_collective_config.rb +2 -2
- data/lib/kettle/dev/pre_release_cli.rb +4 -6
- data/lib/kettle/dev/rakelib/appraisal.rake +14 -18
- data/lib/kettle/dev/readme_backers.rb +2 -3
- data/lib/kettle/dev/release_cli.rb +15 -7
- data/lib/kettle/dev/tasks/ci_task.rb +79 -85
- data/lib/kettle/dev/version.rb +1 -1
- data/lib/kettle/dev.rb +21 -25
- data.tar.gz.sig +0 -0
- metadata +4 -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: 309dfdff5a98f7d5d2f189a3b51a4d0f231d037b61ea37023918a70ec436d997
|
|
4
|
+
data.tar.gz: 9dff167d60de601c34738beb35c81df5e3c1a8874630ccaebf753507fa205a41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: baf9a43aeac7238588295facc22c7a151c68abeed3b754872d539bc25571ea43e87eaac9ffb81ea4f5133206d1e9f1d0ef7d2fc609b666fa0bfad54df27069d0
|
|
7
|
+
data.tar.gz: 6f607059c8cd545bad791de6277f40f0b75d956fbc7654677cdcfb53345c2b658e98881e80f694992041e872e96b4e643d69f61a578ca305821fe449fe6c21ae
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,20 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [2.2.19] - 2026-06-26
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.2.19][2.2.19t]
|
|
36
|
+
- COVERAGE: 91.75% -- 4082/4449 lines in 33 files
|
|
37
|
+
- BRANCH COVERAGE: 72.94% -- 1615/2214 branches in 33 files
|
|
38
|
+
- 61.84% documented
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- `kettle-release` now runs `bin/rake appraisal:generate` by default when
|
|
43
|
+
`Appraisals` is present; use `--appraisal-update` or
|
|
44
|
+
`KETTLE_RELEASE_APPRAISAL_TASK=appraisal:update` for the slower lock-resolving
|
|
45
|
+
update path.
|
|
46
|
+
|
|
33
47
|
## [2.2.18] - 2026-06-24
|
|
34
48
|
|
|
35
49
|
- TAG: [v2.2.18][2.2.18t]
|
|
@@ -2279,7 +2293,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2279
2293
|
- Selecting will run the selected workflow via `act`
|
|
2280
2294
|
- This may move to its own gem in the future.
|
|
2281
2295
|
|
|
2282
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.
|
|
2296
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.19...HEAD
|
|
2297
|
+
[2.2.19]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.18...v2.2.19
|
|
2298
|
+
[2.2.19t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.2.19
|
|
2283
2299
|
[2.2.18]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.17...v2.2.18
|
|
2284
2300
|
[2.2.18t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.2.18
|
|
2285
2301
|
[2.2.17]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.16...v2.2.17
|
data/CONTRIBUTING.md
CHANGED
|
@@ -131,9 +131,12 @@ toolchain, and it may be higher than the gemspec runtime floor.
|
|
|
131
131
|
They are created and updated with the commands:
|
|
132
132
|
|
|
133
133
|
```console
|
|
134
|
-
bin/rake appraisal:
|
|
134
|
+
bin/rake appraisal:generate
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
Use `bin/rake appraisal:update` when you intentionally need to resolve fresh
|
|
138
|
+
appraisal locks.
|
|
139
|
+
|
|
137
140
|
If you need to reset all gemfiles/*.gemfile.lock files:
|
|
138
141
|
|
|
139
142
|
```console
|
data/README.md
CHANGED
|
@@ -874,7 +874,7 @@ Thanks for RTFM. ☺️
|
|
|
874
874
|
[📌gitmoji]: https://gitmoji.dev
|
|
875
875
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
876
876
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
877
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.
|
|
877
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.449-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
878
878
|
[🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
|
|
879
879
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
880
880
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/exe/kettle-changelog
CHANGED
|
@@ -52,59 +52,57 @@ rescue LoadError => e
|
|
|
52
52
|
exit(1)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
exit(0)
|
|
107
|
-
end
|
|
55
|
+
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
56
|
+
puts <<~USAGE
|
|
57
|
+
Usage: kettle-changelog [--version VERSION] [--update-prep] [--pending-release] [--release-state] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
|
|
58
|
+
|
|
59
|
+
Detects the current version from lib/**/version.rb, the latest live release, and
|
|
60
|
+
the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
|
|
61
|
+
create a new release section, update the prepared release section in place, or reformat only.
|
|
62
|
+
|
|
63
|
+
Release plans add coverage and documentation stats, move entries from [Unreleased],
|
|
64
|
+
and update bottom link references to GitHub style.
|
|
65
|
+
|
|
66
|
+
Options:
|
|
67
|
+
--version VERSION Use this version instead of detecting VERSION from lib/**/version.rb
|
|
68
|
+
--update-prep Force updating the most recent prepared release section in place
|
|
69
|
+
--pending-release Query whether the changelog has release work pending; exits 0 for yes, 1 for no
|
|
70
|
+
--release-state,
|
|
71
|
+
--release-status Print changelog release state, including latest published release and pending sources
|
|
72
|
+
--add-unreleased-entry Add one entry to an existing section under ## [Unreleased]
|
|
73
|
+
--section SECTION Unreleased section to receive the entry (Added, Changed, Deprecated, Removed, Fixed, Security)
|
|
74
|
+
--entry TEXT Changelog entry text; "- " is added when omitted
|
|
75
|
+
--json Print query output as JSON
|
|
76
|
+
--no-strict Allow missing coverage and yard data (warnings only, no errors)
|
|
77
|
+
--no-coverage-threshold Generate coverage without hard-failing below configured thresholds
|
|
78
|
+
|
|
79
|
+
Environment:
|
|
80
|
+
K_CHANGELOG_STRICT=false Disable strict mode (equivalent to --no-strict flag)
|
|
81
|
+
K_CHANGELOG_COVERAGE_HARD=false Disable coverage threshold hard-failure
|
|
82
|
+
K_CHANGELOG_VERSION_FILE=path Read VERSION from this file instead of lib/**/version.rb
|
|
83
|
+
|
|
84
|
+
Data generation:
|
|
85
|
+
Strict mode is the default. Release plans remove stale coverage output,
|
|
86
|
+
run bundle exec kettle-test with JSON coverage enabled, read the resulting
|
|
87
|
+
coverage/coverage.json, and collect documentation stats from bin/rake yard
|
|
88
|
+
or bin/yard.
|
|
89
|
+
|
|
90
|
+
Strict mode fails if specs fail, coverage JSON is not produced, coverage
|
|
91
|
+
is below the configured project thresholds, or documentation stats cannot
|
|
92
|
+
be collected.
|
|
93
|
+
|
|
94
|
+
Non-strict mode does not generate missing coverage or documentation data;
|
|
95
|
+
it warns and leaves unavailable stats blank.
|
|
96
|
+
|
|
97
|
+
Runtime requirements:
|
|
98
|
+
- bundle exec kettle-test available for strict coverage generation
|
|
99
|
+
- bin/rake yard or bin/yard available for strict documentation stats
|
|
100
|
+
|
|
101
|
+
Use --no-strict or K_CHANGELOG_STRICT=false to allow missing data.
|
|
102
|
+
Use --no-coverage-threshold or K_CHANGELOG_COVERAGE_HARD=false to run fresh
|
|
103
|
+
coverage without hard-failing below configured thresholds.
|
|
104
|
+
USAGE
|
|
105
|
+
exit(0)
|
|
108
106
|
end
|
|
109
107
|
|
|
110
108
|
begin
|
data/exe/kettle-release
CHANGED
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
# Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
|
|
44
44
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
45
45
|
puts <<~USAGE
|
|
46
|
-
Usage: kettle-release [--version VERSION] [--local-ci] [start_step=<0-19>]
|
|
46
|
+
Usage: kettle-release [--version VERSION] [--local-ci] [--appraisal-update] [start_step=<0-19>]
|
|
47
47
|
|
|
48
48
|
Automates the release flow for a Ruby gem in the host project.
|
|
49
49
|
|
|
@@ -58,7 +58,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
58
58
|
2. Detect version; RubyGems sanity check; confirm CHANGELOG/version; sync copyright years; update badges/headers
|
|
59
59
|
3. Run bin/setup
|
|
60
60
|
4. Run bin/rake (default task)
|
|
61
|
-
5. Run bin/rake appraisal:
|
|
61
|
+
5. Run bin/rake appraisal:generate if Appraisals present
|
|
62
62
|
6. Ensure git user configured; commit release prep
|
|
63
63
|
7. Optional local CI with `act` (controlled by K_RELEASE_LOCAL_CI)
|
|
64
64
|
8. Ensure trunk in sync across remotes; rebase feature as needed
|
|
@@ -77,6 +77,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
77
77
|
Options:
|
|
78
78
|
start_step=<number> # Begin at the numbered step above (e.g., 10 to resume at CI monitoring)
|
|
79
79
|
--version VERSION # Use this version instead of detecting VERSION from lib/**/version.rb
|
|
80
|
+
--appraisal-update # Use slower appraisal:update instead of default appraisal:generate
|
|
80
81
|
--local-ci # Sensitive release mode: run act locally, publish before any git push,
|
|
81
82
|
# create the git tag locally, then push commits and tags after publish
|
|
82
83
|
|
|
@@ -85,6 +86,8 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
85
86
|
GEM_CERT_USER=<user> # Select certs/<user>.pem for signing
|
|
86
87
|
K_RELEASE_LOCAL_CI=ask|1|0 # Use 'act' locally before push; 'ask' prompts, '1' forces, default off
|
|
87
88
|
K_RELEASE_LOCAL_CI_WORKFLOW # Name of workflow (without .yml) for local CI; defaults to locked_deps or first
|
|
89
|
+
KETTLE_RELEASE_APPRAISAL_TASK=appraisal:update
|
|
90
|
+
# Use slower appraisal:update instead of default appraisal:generate
|
|
88
91
|
GITHUB_TOKEN / GH_TOKEN # Optional; enables GitHub release creation and API queries
|
|
89
92
|
GITLAB_TOKEN / GL_TOKEN # Optional; enables GitLab API queries when applicable
|
|
90
93
|
DEBUG=true # Print backtraces on errors
|
|
@@ -119,12 +122,13 @@ def extract_version_arg!(argv)
|
|
|
119
122
|
end
|
|
120
123
|
|
|
121
124
|
local_ci = ARGV.include?("--local-ci")
|
|
125
|
+
appraisal_task = ARGV.delete("--appraisal-update") ? "appraisal:update" : nil
|
|
122
126
|
start_step_arg = ARGV.find { |a| a.start_with?("start_step=") }
|
|
123
127
|
start_step = start_step_arg ? start_step_arg.split("=", 2)[1].to_i : 0
|
|
124
128
|
version_override = extract_version_arg!(ARGV)
|
|
125
129
|
|
|
126
130
|
begin
|
|
127
|
-
Kettle::Dev::ReleaseCLI.new(start_step: start_step, local_ci: local_ci, version: version_override).run
|
|
131
|
+
Kettle::Dev::ReleaseCLI.new(start_step: start_step, local_ci: local_ci, version: version_override, appraisal_task: appraisal_task).run
|
|
128
132
|
rescue LoadError => e
|
|
129
133
|
warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
|
|
130
134
|
warn(Array(e.backtrace).join("\n")) if ENV["DEBUG"]
|
|
@@ -297,13 +297,13 @@ module Kettle
|
|
|
297
297
|
end
|
|
298
298
|
# Small initial delay to allow GitHub to register the newly pushed commit and enqueue workflows.
|
|
299
299
|
# Configurable via K_RELEASE_CI_INITIAL_SLEEP (seconds); defaults to 3s.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
end
|
|
300
|
+
|
|
301
|
+
initial_sleep = begin
|
|
302
|
+
Integer(ENV["K_RELEASE_CI_INITIAL_SLEEP"])
|
|
303
|
+
rescue
|
|
304
|
+
nil
|
|
306
305
|
end
|
|
306
|
+
|
|
307
307
|
sleep((initial_sleep && initial_sleep >= 0) ? initial_sleep : 3)
|
|
308
308
|
start_timeout = github_start_timeout
|
|
309
309
|
poll_interval = github_poll_interval
|
|
@@ -6,7 +6,6 @@ require "net/http"
|
|
|
6
6
|
require "open3"
|
|
7
7
|
require "optparse"
|
|
8
8
|
require "pathname"
|
|
9
|
-
require "set"
|
|
10
9
|
require "time"
|
|
11
10
|
require "uri"
|
|
12
11
|
|
|
@@ -265,11 +264,9 @@ module Kettle
|
|
|
265
264
|
@options[:progress] = bool
|
|
266
265
|
end
|
|
267
266
|
opt.on("--skip-pattern PATTERN", "Skip workflow paths matching pattern (repeatable)") do |pattern|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
Kettle::Dev::ExitAdapter.abort("Invalid --skip-pattern #{pattern.inspect}: #{e.message}")
|
|
272
|
-
end
|
|
267
|
+
@options[:reject_patterns] << Regexp.new(pattern)
|
|
268
|
+
rescue RegexpError => e
|
|
269
|
+
Kettle::Dev::ExitAdapter.abort("Invalid --skip-pattern #{pattern.inspect}: #{e.message}")
|
|
273
270
|
end
|
|
274
271
|
opt.on("--[no-]validate", "Validate YAML after editing") do |bool|
|
|
275
272
|
@options[:validate] = bool
|
|
@@ -285,27 +282,25 @@ module Kettle
|
|
|
285
282
|
def load_workflows(paths, state)
|
|
286
283
|
file_progress = progress_bar(title: "Files", total: paths.length)
|
|
287
284
|
paths.each_with_object([]) do |path, workflows|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
parsed = begin
|
|
298
|
-
Psych.parse_stream(text)
|
|
299
|
-
rescue Psych::Exception => e
|
|
300
|
-
record_failure(state, path: path, error: "yaml_parse_error: #{e.message}")
|
|
301
|
-
next
|
|
302
|
-
end
|
|
285
|
+
state[:files_scanned] += 1
|
|
286
|
+
text = begin
|
|
287
|
+
File.read(path)
|
|
288
|
+
rescue Errno::EACCES => e
|
|
289
|
+
record_failure(state, path: path, error: "read_error: #{e.message}")
|
|
290
|
+
next
|
|
291
|
+
end
|
|
303
292
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
parsed = begin
|
|
294
|
+
Psych.parse_stream(text)
|
|
295
|
+
rescue Psych::Exception => e
|
|
296
|
+
record_failure(state, path: path, error: "yaml_parse_error: #{e.message}")
|
|
297
|
+
next
|
|
308
298
|
end
|
|
299
|
+
|
|
300
|
+
uses_nodes = extract_uses_nodes(parsed, text)
|
|
301
|
+
workflows << {path: path, text: text, uses_nodes: uses_nodes} unless uses_nodes.empty?
|
|
302
|
+
ensure
|
|
303
|
+
file_progress&.increment
|
|
309
304
|
end
|
|
310
305
|
end
|
|
311
306
|
|
|
@@ -53,26 +53,24 @@ module Kettle
|
|
|
53
53
|
# Create a new adapter rooted at the current working directory.
|
|
54
54
|
# @return [void]
|
|
55
55
|
def initialize
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if disable_gem
|
|
63
|
-
@backend = :cli
|
|
64
|
-
else
|
|
65
|
-
Kernel.require "git"
|
|
66
|
-
@backend = :gem
|
|
67
|
-
@git = ::Git.open(Dir.pwd)
|
|
68
|
-
end
|
|
69
|
-
rescue LoadError => e
|
|
70
|
-
Kettle::Dev.debug_error(e, __method__, backtrace: false)
|
|
71
|
-
# Optional dependency: fall back to CLI
|
|
56
|
+
# Allow users/CI to opt out of using the 'git' gem even when available.
|
|
57
|
+
# Set KETTLE_DEV_DISABLE_GIT_GEM to a truthy value ("1", "true", "yes") to force CLI backend.
|
|
58
|
+
env_val = ENV["KETTLE_DEV_DISABLE_GIT_GEM"]
|
|
59
|
+
# Ruby 2.3 compatibility: String#match? was added in 2.4; use Regexp#=== / =~ instead
|
|
60
|
+
disable_gem = env_val && !!(/\A(1|true|yes)\z/i =~ env_val)
|
|
61
|
+
if disable_gem
|
|
72
62
|
@backend = :cli
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
else
|
|
64
|
+
Kernel.require "git"
|
|
65
|
+
@backend = :gem
|
|
66
|
+
@git = ::Git.open(Dir.pwd)
|
|
75
67
|
end
|
|
68
|
+
rescue LoadError => e
|
|
69
|
+
Kettle::Dev.debug_error(e, __method__, backtrace: false)
|
|
70
|
+
# Optional dependency: fall back to CLI
|
|
71
|
+
@backend = :cli
|
|
72
|
+
rescue => e
|
|
73
|
+
raise Kettle::Dev::Error, "Failed to open git repository: #{e.message}"
|
|
76
74
|
end
|
|
77
75
|
|
|
78
76
|
# Push a branch to a remote.
|
|
@@ -200,12 +198,10 @@ module Kettle
|
|
|
200
198
|
def remotes_with_urls
|
|
201
199
|
if @backend == :gem
|
|
202
200
|
@git.remotes.each_with_object({}) do |r, h|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
# ignore
|
|
208
|
-
end
|
|
201
|
+
h[r.name] = r.url
|
|
202
|
+
rescue => e
|
|
203
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
204
|
+
# ignore
|
|
209
205
|
end
|
|
210
206
|
else
|
|
211
207
|
out, status = Open3.capture2("git", "remote", "-v")
|
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
class GitCommitFooter
|
|
6
6
|
# Regex to extract `name = "value"` assignments from a gemspec.
|
|
7
7
|
# @return [Regexp]
|
|
8
|
-
NAME_ASSIGNMENT_REGEX = /\bname\s*=\s*(["'])([^"']+)\1
|
|
8
|
+
NAME_ASSIGNMENT_REGEX = /\bname\s*=\s*(["'])([^"']+)\1/
|
|
9
9
|
|
|
10
10
|
# Whether footer appending is enabled (via GIT_HOOK_FOOTER_APPEND=true)
|
|
11
11
|
# @return [Boolean]
|
|
@@ -44,14 +44,14 @@ module Kettle
|
|
|
44
44
|
|
|
45
45
|
ypath = yaml_path(root)
|
|
46
46
|
if strict
|
|
47
|
-
yml = YAML.
|
|
47
|
+
yml = YAML.safe_load_file(ypath)
|
|
48
48
|
if yml.is_a?(Hash)
|
|
49
49
|
handle = yml["collective"] || yml[:collective] || yml["org"] || yml[:org]
|
|
50
50
|
return handle.to_s unless handle.nil? || handle.to_s.strip.empty? || handle.to_s.match?(/\{KJ\|[^}]+}/)
|
|
51
51
|
end
|
|
52
52
|
elsif File.file?(ypath)
|
|
53
53
|
begin
|
|
54
|
-
yml = YAML.
|
|
54
|
+
yml = YAML.safe_load_file(ypath)
|
|
55
55
|
if yml.is_a?(Hash)
|
|
56
56
|
handle = yml["collective"] || yml[:collective] || yml["org"] || yml[:org]
|
|
57
57
|
return handle.to_s unless handle.nil? || handle.to_s.strip.empty? || handle.to_s.match?(/\{KJ\|[^}]+}/)
|
|
@@ -253,12 +253,10 @@ module Kettle
|
|
|
253
253
|
Array(glob_pattern)
|
|
254
254
|
end
|
|
255
255
|
urls = files.flat_map do |f|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
[]
|
|
261
|
-
end
|
|
256
|
+
extract_image_urls_from_text(File.read(f))
|
|
257
|
+
rescue => e
|
|
258
|
+
warn("[kettle-pre-release] Could not read #{Kettle::Dev.display_path(f)}: #{e.class}: #{e.message}")
|
|
259
|
+
[]
|
|
262
260
|
end
|
|
263
261
|
urls.uniq
|
|
264
262
|
end
|
|
@@ -33,20 +33,18 @@ begin
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
run_appraisal_task = lambda do |task_name, primary_steps = nil|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
warn("[kettle-dev][#{task_name}] #{e.message}; falling back to appraisal:generate")
|
|
42
|
-
run_generate_steps.call
|
|
43
|
-
end
|
|
44
|
-
else
|
|
36
|
+
if primary_steps
|
|
37
|
+
begin
|
|
38
|
+
primary_steps.call
|
|
39
|
+
rescue RuntimeError => e
|
|
40
|
+
warn("[kettle-dev][#{task_name}] #{e.message}; falling back to appraisal:generate")
|
|
45
41
|
run_generate_steps.call
|
|
46
42
|
end
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
else
|
|
44
|
+
run_generate_steps.call
|
|
49
45
|
end
|
|
46
|
+
rescue RuntimeError => e
|
|
47
|
+
abort(e.message)
|
|
50
48
|
end
|
|
51
49
|
|
|
52
50
|
desc("Install Appraisal gemfiles (initial setup for projects that didn't previously use Appraisal)")
|
|
@@ -159,13 +157,11 @@ begin
|
|
|
159
157
|
else
|
|
160
158
|
failures = []
|
|
161
159
|
locks.each do |f|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
failures << [f, e]
|
|
168
|
-
end
|
|
160
|
+
File.delete(f)
|
|
161
|
+
rescue Errno::ENOENT
|
|
162
|
+
# Ignore if already gone
|
|
163
|
+
rescue => e
|
|
164
|
+
failures << [f, e]
|
|
169
165
|
end
|
|
170
166
|
|
|
171
167
|
unless failures.empty?
|
|
@@ -5,7 +5,6 @@ require "yaml"
|
|
|
5
5
|
require "json"
|
|
6
6
|
require "uri"
|
|
7
7
|
require "net/http"
|
|
8
|
-
require "set"
|
|
9
8
|
|
|
10
9
|
module Kettle
|
|
11
10
|
module Dev
|
|
@@ -244,7 +243,7 @@ module Kettle
|
|
|
244
243
|
|
|
245
244
|
if File.file?(OC_YML_PATH)
|
|
246
245
|
begin
|
|
247
|
-
yml = YAML.
|
|
246
|
+
yml = YAML.safe_load_file(OC_YML_PATH)
|
|
248
247
|
if yml.is_a?(Hash)
|
|
249
248
|
from_yml = yml["readme-osc-tag"] || yml[:"readme-osc-tag"]
|
|
250
249
|
from_yml = from_yml.to_s if from_yml
|
|
@@ -660,7 +659,7 @@ module Kettle
|
|
|
660
659
|
|
|
661
660
|
if File.file?(OC_YML_PATH)
|
|
662
661
|
begin
|
|
663
|
-
yml = YAML.
|
|
662
|
+
yml = YAML.safe_load_file(OC_YML_PATH)
|
|
664
663
|
if yml.is_a?(Hash)
|
|
665
664
|
from_yml = yml["readme-backers-commit-subject"] || yml[:"readme-backers-commit-subject"]
|
|
666
665
|
from_yml = from_yml.to_s if from_yml
|
|
@@ -72,13 +72,14 @@ module Kettle
|
|
|
72
72
|
|
|
73
73
|
public
|
|
74
74
|
|
|
75
|
-
def initialize(start_step: 0, local_ci: false, version: nil)
|
|
75
|
+
def initialize(start_step: 0, local_ci: false, version: nil, appraisal_task: nil)
|
|
76
76
|
@root = Kettle::Dev::CIHelpers.project_root
|
|
77
77
|
@git = Kettle::Dev::GitAdapter.new
|
|
78
78
|
@start_step = (start_step || 0).to_i
|
|
79
79
|
@start_step = 0 if @start_step < 0
|
|
80
80
|
@local_ci = !!local_ci
|
|
81
81
|
@version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
|
|
82
|
+
@appraisal_task = normalize_appraisal_task(appraisal_task || ENV["KETTLE_RELEASE_APPRAISAL_TASK"])
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
def run
|
|
@@ -190,14 +191,14 @@ module Kettle
|
|
|
190
191
|
# 4. bin/rake
|
|
191
192
|
run_cmd!("bin/rake") if @start_step <= 4
|
|
192
193
|
|
|
193
|
-
# 5. appraisal:
|
|
194
|
+
# 5. appraisal:generate (optional) + canonical docs build
|
|
194
195
|
if @start_step <= 5
|
|
195
196
|
appraisals_path = File.join(@root, "Appraisals")
|
|
196
197
|
if File.file?(appraisals_path)
|
|
197
|
-
puts "Appraisals detected at #{Kettle::Dev.display_path(appraisals_path)}. Running: bin/rake
|
|
198
|
-
run_cmd!("bin/rake
|
|
198
|
+
puts "Appraisals detected at #{Kettle::Dev.display_path(appraisals_path)}. Running: bin/rake #{@appraisal_task}"
|
|
199
|
+
run_cmd!("bin/rake #{@appraisal_task}")
|
|
199
200
|
else
|
|
200
|
-
puts "No Appraisals file found; skipping
|
|
201
|
+
puts "No Appraisals file found; skipping #{@appraisal_task}"
|
|
201
202
|
end
|
|
202
203
|
|
|
203
204
|
puts "Generating docs site via canonical task: bin/rake yard"
|
|
@@ -320,6 +321,15 @@ module Kettle
|
|
|
320
321
|
end
|
|
321
322
|
end
|
|
322
323
|
|
|
324
|
+
def normalize_appraisal_task(value)
|
|
325
|
+
task = value.to_s.strip
|
|
326
|
+
return "appraisal:generate" if task.empty?
|
|
327
|
+
return "appraisal:generate" if task == "generate" || task == "appraisal:generate"
|
|
328
|
+
return "appraisal:update" if task == "update" || task == "appraisal:update"
|
|
329
|
+
|
|
330
|
+
abort("Unsupported appraisal task #{value.inspect}; use appraisal:generate or appraisal:update.")
|
|
331
|
+
end
|
|
332
|
+
|
|
323
333
|
private
|
|
324
334
|
|
|
325
335
|
def local_ci?
|
|
@@ -405,7 +415,6 @@ module Kettle
|
|
|
405
415
|
# This helps ensure docs are kept in sync when bumping the years.
|
|
406
416
|
# Aborts with a helpful message when they differ.
|
|
407
417
|
def validate_copyright_years!
|
|
408
|
-
require "set"
|
|
409
418
|
readme = File.join(@root, "README.md")
|
|
410
419
|
license = File.join(@root, "LICENSE.txt")
|
|
411
420
|
unless File.file?(readme) && File.file?(license)
|
|
@@ -446,7 +455,6 @@ module Kettle
|
|
|
446
455
|
# then parses four-digit years and year ranges like "2012-2015" (hyphen or en dash).
|
|
447
456
|
# Returns Set[Integer].
|
|
448
457
|
def extract_years_from_file(path)
|
|
449
|
-
require "set"
|
|
450
458
|
years = Set.new
|
|
451
459
|
content = File.read(path)
|
|
452
460
|
# Only consider lines that look like copyright notices to reduce false positives
|
|
@@ -91,48 +91,46 @@ module Kettle
|
|
|
91
91
|
|
|
92
92
|
# Print GitLab pipeline status (if configured) for the current branch.
|
|
93
93
|
print_gitlab_status = proc do
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
94
|
+
branch = Kettle::Dev::CIHelpers.current_branch
|
|
95
|
+
# Detect any GitLab remote (not just origin), mirroring CIMonitor behavior
|
|
96
|
+
gl_remotes = Kettle::Dev::CIMonitor.gitlab_remote_candidates
|
|
97
|
+
if gl_remotes.nil? || gl_remotes.empty? || branch.nil?
|
|
98
|
+
puts "Latest GL (#{branch || "n/a"}) pipeline: n/a"
|
|
99
|
+
next
|
|
100
|
+
end
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
# Parse owner/repo from the first GitLab remote URL
|
|
103
|
+
gl_url = Kettle::Dev::CIMonitor.remote_url(gl_remotes.first)
|
|
104
|
+
owner = repo = nil
|
|
105
|
+
if gl_url =~ %r{git@gitlab.com:(.+?)/(.+?)(\.git)?$}
|
|
106
|
+
owner = Regexp.last_match(1)
|
|
107
|
+
repo = Regexp.last_match(2).sub(/\.git\z/, "")
|
|
108
|
+
elsif gl_url =~ %r{https://gitlab.com/(.+?)/(.+?)(\.git)?$}
|
|
109
|
+
owner = Regexp.last_match(1)
|
|
110
|
+
repo = Regexp.last_match(2).sub(/\.git\z/, "")
|
|
111
|
+
end
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
unless owner && repo
|
|
114
|
+
puts "Latest GL (#{branch}) pipeline: n/a"
|
|
115
|
+
next
|
|
116
|
+
end
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
else
|
|
125
|
-
((st == "failed") ? "failure" : nil)
|
|
126
|
-
end
|
|
127
|
-
emoji = Kettle::Dev::CIMonitor.status_emoji(st, status)
|
|
128
|
-
details = [st, pipe["failure_reason"]].compact.join("/")
|
|
129
|
-
puts "Latest GL (#{branch}) pipeline: #{emoji} (#{details})"
|
|
118
|
+
pipe = Kettle::Dev::CIHelpers.gitlab_latest_pipeline(owner: owner, repo: repo, branch: branch)
|
|
119
|
+
if pipe
|
|
120
|
+
st = pipe["status"].to_s
|
|
121
|
+
status = if st == "success"
|
|
122
|
+
"success"
|
|
130
123
|
else
|
|
131
|
-
|
|
124
|
+
((st == "failed") ? "failure" : nil)
|
|
132
125
|
end
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
emoji = Kettle::Dev::CIMonitor.status_emoji(st, status)
|
|
127
|
+
details = [st, pipe["failure_reason"]].compact.join("/")
|
|
128
|
+
puts "Latest GL (#{branch}) pipeline: #{emoji} (#{details})"
|
|
129
|
+
else
|
|
130
|
+
puts "Latest GL (#{branch}) pipeline: none"
|
|
135
131
|
end
|
|
132
|
+
rescue => e
|
|
133
|
+
puts "GL status: error #{e.class}: #{e.message}"
|
|
136
134
|
end
|
|
137
135
|
|
|
138
136
|
run_act_for = proc do |file_path|
|
|
@@ -264,12 +262,10 @@ module Kettle
|
|
|
264
262
|
selected = nil
|
|
265
263
|
input_thread = nil
|
|
266
264
|
read_input = proc do
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
selected = :input_error
|
|
272
|
-
end
|
|
265
|
+
selected = Kettle::Dev::InputAdapter.gets&.strip
|
|
266
|
+
rescue StandardError, SystemExit, Interrupt => error
|
|
267
|
+
puts "Error reading input: #{error.class}: #{error.message}" if Kettle::Dev::DEBUGGING
|
|
268
|
+
selected = :input_error
|
|
273
269
|
end
|
|
274
270
|
if tty
|
|
275
271
|
input_thread = Thread.new(&read_input) # rubocop:disable ThreadSafety/NewThread
|
|
@@ -283,54 +279,52 @@ module Kettle
|
|
|
283
279
|
|
|
284
280
|
options.each do |code, file|
|
|
285
281
|
workers << Thread.new(code, file, owner, repo, branch, token, start_at) do |c, f, ow, rp, br, tk, st_at| # rubocop:disable ThreadSafety/NewThread
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
break if st == "completed"
|
|
313
|
-
else
|
|
314
|
-
status_q << [c, f, "none"]
|
|
315
|
-
break
|
|
316
|
-
end
|
|
282
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
283
|
+
delay = 0.12 - (now - st_at)
|
|
284
|
+
sleep(delay) if delay && delay > 0
|
|
285
|
+
|
|
286
|
+
if ow.nil? || rp.nil? || br.nil?
|
|
287
|
+
status_q << [c, f, "n/a"]
|
|
288
|
+
Thread.exit
|
|
289
|
+
end
|
|
290
|
+
uri = URI("https://api.github.com/repos/#{ow}/#{rp}/actions/workflows/#{f}/runs?branch=#{URI.encode_www_form_component(br)}&per_page=1")
|
|
291
|
+
poll_interval = Integer(ENV["CI_ACT_POLL_INTERVAL"] || 5)
|
|
292
|
+
loop do
|
|
293
|
+
begin
|
|
294
|
+
req = Net::HTTP::Get.new(uri)
|
|
295
|
+
req["User-Agent"] = "ci:act rake task"
|
|
296
|
+
req["Authorization"] = "token #{tk}" if tk && !tk.empty?
|
|
297
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
|
|
298
|
+
if res.is_a?(Net::HTTPSuccess)
|
|
299
|
+
data = JSON.parse(res.body)
|
|
300
|
+
run = data["workflow_runs"]&.first
|
|
301
|
+
if run
|
|
302
|
+
st = run["status"]
|
|
303
|
+
con = run["conclusion"]
|
|
304
|
+
emoji = Kettle::Dev::CIMonitor.status_emoji(st, con)
|
|
305
|
+
details = [st, con].compact.join("/")
|
|
306
|
+
status_q << [c, f, "#{emoji} (#{details})"]
|
|
307
|
+
break if st == "completed"
|
|
317
308
|
else
|
|
318
|
-
status_q << [c, f, "
|
|
309
|
+
status_q << [c, f, "none"]
|
|
310
|
+
break
|
|
319
311
|
end
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
# Catch all exceptions to prevent crashing the process from a worker thread
|
|
323
|
-
status_q << [c, f, "err"]
|
|
312
|
+
else
|
|
313
|
+
status_q << [c, f, "fail #{res.code}"]
|
|
324
314
|
end
|
|
325
|
-
|
|
315
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
316
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
317
|
+
# Catch all exceptions to prevent crashing the process from a worker thread
|
|
318
|
+
status_q << [c, f, "err"]
|
|
326
319
|
end
|
|
327
|
-
|
|
328
|
-
Kettle::Dev.debug_error(e, __method__)
|
|
329
|
-
# simplecov:disable
|
|
330
|
-
# Catch all exceptions in the worker thread boundary, including SystemExit
|
|
331
|
-
status_q << [c, f, "err"]
|
|
332
|
-
# simplecov:enable
|
|
320
|
+
sleep(poll_interval)
|
|
333
321
|
end
|
|
322
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
323
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
324
|
+
# simplecov:disable
|
|
325
|
+
# Catch all exceptions in the worker thread boundary, including SystemExit
|
|
326
|
+
status_q << [c, f, "err"]
|
|
327
|
+
# simplecov:enable
|
|
334
328
|
end
|
|
335
329
|
end
|
|
336
330
|
|
data/lib/kettle/dev/version.rb
CHANGED
data/lib/kettle/dev.rb
CHANGED
|
@@ -177,38 +177,34 @@ module Kettle
|
|
|
177
177
|
# Set up rubocop-lts, which cascades to rubocop-rubyX_X => rubocop_gradual)
|
|
178
178
|
# @return [void]
|
|
179
179
|
def linting_tasks
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Kettle::Dev.register_default("rubocop_gradual:autocorrect")
|
|
189
|
-
end
|
|
190
|
-
rescue LoadError
|
|
191
|
-
# OK, no styles for you.
|
|
180
|
+
# Lazy loaded because it won't be installed for Ruby < 2.7
|
|
181
|
+
require "rubocop/lts"
|
|
182
|
+
|
|
183
|
+
Rubocop::Lts.install_tasks
|
|
184
|
+
if Kettle::Dev::IS_CI
|
|
185
|
+
Kettle::Dev.register_default("rubocop_gradual:check")
|
|
186
|
+
else
|
|
187
|
+
Kettle::Dev.register_default("rubocop_gradual:autocorrect")
|
|
192
188
|
end
|
|
189
|
+
rescue LoadError
|
|
190
|
+
# OK, no styles for you.
|
|
193
191
|
end
|
|
194
192
|
|
|
195
193
|
### TEST COVERAGE TASKS
|
|
196
194
|
# Set up kettle-soup-cover
|
|
197
195
|
# @return [void]
|
|
198
196
|
def coverage_tasks
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
# OK, no soup for you.
|
|
211
|
-
end
|
|
197
|
+
# Lazy loaded because it won't be installed for Ruby < 2.7
|
|
198
|
+
require "kettle-soup-cover"
|
|
199
|
+
|
|
200
|
+
Kettle::Soup::Cover.install_tasks
|
|
201
|
+
# NOTE: Coverage on CI is configured independent of this task.
|
|
202
|
+
# This task is for local development, as it opens results in browser
|
|
203
|
+
# simplecov:disable
|
|
204
|
+
Kettle::Dev.register_default("coverage") unless Kettle::Dev::IS_CI
|
|
205
|
+
# simplecov:enable
|
|
206
|
+
rescue LoadError
|
|
207
|
+
# OK, no soup for you.
|
|
212
208
|
end
|
|
213
209
|
end
|
|
214
210
|
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: 2.2.
|
|
4
|
+
version: 2.2.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -339,10 +339,10 @@ licenses:
|
|
|
339
339
|
- AGPL-3.0-only
|
|
340
340
|
metadata:
|
|
341
341
|
homepage_uri: https://kettle-dev.galtzo.com
|
|
342
|
-
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.2.
|
|
343
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.2.
|
|
342
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.2.19
|
|
343
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.2.19/CHANGELOG.md
|
|
344
344
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
345
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.2.
|
|
345
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.2.19
|
|
346
346
|
funding_uri: https://github.com/sponsors/pboling
|
|
347
347
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
348
348
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|