kettle-dev 3.0.12 ā 3.0.14
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 +45 -1
- data/README.md +30 -11
- data/exe/kettle-release +44 -13
- data/lib/kettle/dev/ci_monitor.rb +3 -3
- data/lib/kettle/dev/git_adapter.rb +15 -0
- data/lib/kettle/dev/lockfile_reset.rb +12 -5
- data/lib/kettle/dev/release_cli.rb +98 -7
- data/lib/kettle/dev/version.rb +1 -1
- data/lib/kettle/dev/version_bump.rb +29 -5
- data.tar.gz.sig +0 -0
- metadata +14 -14
- 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: '039cd8bb8df2cc898259f53fb65be82bca84a516067e02c61a31295475f92f79'
|
|
4
|
+
data.tar.gz: 6e0c274ac0c78a6e5960d076cd0cf63ad9639b806fb024a2712c4e81e2697856
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c756231572b2f1e2d446b2011942937032e089b05218560a20b73b5d1f087de688919eb7b8056b0638926b2e1ea9c80023eca85dd402a3ce99b20f9f90e45173
|
|
7
|
+
data.tar.gz: 87000a51e097e84a8d21ed9bf40e11792d2fd55937f728176c931df54715400ea5c07dfcdc76fce2979914dda7412b12b9287f68ec78e48f97cee255244f05b5
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,46 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [3.0.14] - 2026-08-28
|
|
34
|
+
|
|
35
|
+
- TAG: [v3.0.14][3.0.14t]
|
|
36
|
+
- COVERAGE: 91.46% -- 5818/6361 lines in 47 files
|
|
37
|
+
- BRANCH COVERAGE: 76.14% -- 2230/2929 branches in 47 files
|
|
38
|
+
- 50.86% documented
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- [kc] kettle-jem/prepare: updated 9 project files:
|
|
43
|
+
- configuration (1)
|
|
44
|
+
- dependencies (8)
|
|
45
|
+
|
|
46
|
+
- [kc] kettle-jem/template: updated 14 project files:
|
|
47
|
+
- code and tests (2)
|
|
48
|
+
- configuration (1)
|
|
49
|
+
- dependencies (1)
|
|
50
|
+
- documentation (1)
|
|
51
|
+
- other (1)
|
|
52
|
+
- workflows (8)
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Teach kettle-bump to update literal fallback versions in conditional gemspec loaders.
|
|
57
|
+
|
|
58
|
+
- Release lockfile resets now update the locked Bundler version before the release preparation commit, preventing the release's own Bundler upgrade from dirtying the working tree during publication.
|
|
59
|
+
|
|
60
|
+
- Standalone releases now commit Bundler lockfile updates separately before release preparation.
|
|
61
|
+
|
|
62
|
+
## [3.0.13] - 2026-08-25
|
|
63
|
+
|
|
64
|
+
- TAG: [v3.0.13][3.0.13t]
|
|
65
|
+
- COVERAGE: 91.54% -- 5758/6290 lines in 47 files
|
|
66
|
+
- BRANCH COVERAGE: 76.44% -- 2210/2891 branches in 47 files
|
|
67
|
+
- 50.74% documented
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- Add named standalone release aliases for CI bypass and numbered-step recovery.
|
|
72
|
+
|
|
33
73
|
## [3.0.12] - 2026-08-25
|
|
34
74
|
|
|
35
75
|
- TAG: [v3.0.12][3.0.12t]
|
|
@@ -3557,7 +3597,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
3557
3597
|
- Selecting will run the selected workflow via `act`
|
|
3558
3598
|
- This may move to its own gem in the future.
|
|
3559
3599
|
|
|
3560
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.
|
|
3600
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.14...HEAD
|
|
3601
|
+
[3.0.14]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.13...v3.0.14
|
|
3602
|
+
[3.0.14t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.14
|
|
3603
|
+
[3.0.13]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.12...v3.0.13
|
|
3604
|
+
[3.0.13t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.13
|
|
3561
3605
|
[3.0.12]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.11...v3.0.12
|
|
3562
3606
|
[3.0.12t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.12
|
|
3563
3607
|
[3.0.11]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.10...v3.0.11
|
data/README.md
CHANGED
|
@@ -145,7 +145,9 @@ The _amazing_ test matrix is powered by the kettle-dev stack.
|
|
|
145
145
|
|
|
146
146
|
</details>
|
|
147
147
|
|
|
148
|
-
### Enterprise Support
|
|
148
|
+
### Enterprise Support
|
|
149
|
+
|
|
150
|
+
[](https://tidelift.com/subscription/pkg/rubygems-kettle-dev?utm_source=rubygems-kettle-dev&utm_medium=referral&utm_campaign=readme)
|
|
149
151
|
|
|
150
152
|
Available as part of the Tidelift Subscription.
|
|
151
153
|
|
|
@@ -412,7 +414,8 @@ What it does:
|
|
|
412
414
|
- Validates Markdown reference definitions and local heading anchors across releasable documentation before network checks.
|
|
413
415
|
Markdown fixtures under `spec/` and `test/`, along with `tmp/`, are intentionally excluded.
|
|
414
416
|
- Skips pre-release checks for any `start_step` greater than `0`, so resumptions begin directly at the requested numbered step.
|
|
415
|
-
-
|
|
417
|
+
- Verifies Bundler >= 2.7, updates the primary and appraisal bundles with `bundle update --bundler`, and commits those lockfiles separately as `šļø Update bundle` before release preparation.
|
|
418
|
+
- Runs sanity checks (`bin/setup`, `bin/rake`), confirms version/changelog, optionally updates Appraisals, regenerates docs via `bin/rake yard`, and commits āš Prepare release vX.Y.Zā.
|
|
416
419
|
- Optionally runs your CI locally with `act` before any push:
|
|
417
420
|
- Enable with env: `K_RELEASE_LOCAL_CI="true"` (run automatically) or `K_RELEASE_LOCAL_CI="ask"` (prompt \[Y/n\]).
|
|
418
421
|
- Select workflow with `K_RELEASE_LOCAL_CI_WORKFLOW` (with or without .yml/.yaml). Defaults to `locked_deps.yml` if present; otherwise the first workflow discovered.
|
|
@@ -422,7 +425,7 @@ What it does:
|
|
|
422
425
|
- Options:
|
|
423
426
|
- `start_step` map (skip directly to a phase):
|
|
424
427
|
0. Run `kettle-pre-release` gates
|
|
425
|
-
1. Verify Bundler \>= 2.7 (start at 1 to skip pre-release checks)
|
|
428
|
+
1. Verify Bundler \>= 2.7; update and commit the primary/appraisal Bundler lockfiles (start at 1 to skip pre-release checks)
|
|
426
429
|
2. Detect version; RubyGems sanity check; confirm CHANGELOG/version; sync copyright years; update badges/headers
|
|
427
430
|
3. Run bin/setup
|
|
428
431
|
4. Run bin/rake (default task)
|
|
@@ -443,11 +446,11 @@ What it does:
|
|
|
443
446
|
18. Create GitHub Release (requires `GITHUB_TOKEN`)
|
|
444
447
|
19. Push tags to remotes (final)
|
|
445
448
|
- Examples:
|
|
446
|
-
- After a transient CI or provider failure with no code, version, changelog, or lockfile changes, restart from monitoring: `bundle exec kettle-release start_step=10`
|
|
449
|
+
- After a transient CI or provider failure with no code, version, changelog, or lockfile changes, restart from monitoring: `bundle exec kettle-release --start-step 10` (the legacy `start_step=10` spelling remains supported)
|
|
447
450
|
- If the failure required a code or release-metadata change, rerun `bundle exec kettle-release` from step 0 so the release checks, prep metadata, commit, and CI all describe the new release commit.
|
|
448
|
-
- For an intentional same-commit CI bypass, use `bundle exec kettle-release
|
|
451
|
+
- For an intentional same-commit CI bypass, use `bundle exec kettle-release --skip-ci` (or `--skip-steps 10`); this skips remote CI monitoring only and does not claim that CI passed.
|
|
449
452
|
- After fixing a failed pre-release gate, rerun from step 0: `bundle exec kettle-release`
|
|
450
|
-
- After intentionally handling a failed pre-release gate, skip it and start numbered release steps: `bundle exec kettle-release
|
|
453
|
+
- After intentionally handling a failed pre-release gate, skip it and start numbered release steps: `bundle exec kettle-release --start-step 1`
|
|
451
454
|
- Auto-approve release confirmation prompts: `bundle exec kettle-release --yes`
|
|
452
455
|
- Run direct release with 1Password secrets: `bundle exec kettle-release --secrets-provider 1password`
|
|
453
456
|
- Lockfile recovery:
|
|
@@ -463,10 +466,26 @@ What it does:
|
|
|
463
466
|
variables, known local-template toggles, and writes checksums while
|
|
464
467
|
updating path-sourced and checksum-gap gems back to released registry
|
|
465
468
|
versions.
|
|
466
|
-
- Release lockfile resets
|
|
467
|
-
|
|
468
|
-
`kettle-changelog`
|
|
469
|
-
|
|
469
|
+
- Release lockfile resets normally keep the complete development Gemfile
|
|
470
|
+
graph, including the development-only `kettle-changelog` dependency.
|
|
471
|
+
`kettle-changelog` depends on `kettle-dev`, so including it in
|
|
472
|
+
`kettle-dev`'s development bundle creates a downstream development-cycle
|
|
473
|
+
when sibling versions are unpublished. The
|
|
474
|
+
`KETTLE_DEV_SKIP_CHANGELOG_DEPENDENCY=true` switch remains available for
|
|
475
|
+
an explicit bootstrap/release graph that cannot resolve that downstream
|
|
476
|
+
tool, but it is not used while rewriting the tracked development
|
|
477
|
+
lockfiles.
|
|
478
|
+
- At release step 1, `kettle-release` runs the equivalent of
|
|
479
|
+
`kettle-family bupb`: it updates the primary lockfile and, when present,
|
|
480
|
+
`Appraisal.root.gemfile.lock`, resets appraisal locks, and commits only
|
|
481
|
+
those lockfiles as `šļø Update bundle`. This keeps Bundler maintenance in
|
|
482
|
+
its own atomic commit. `kettle-changelog` writes changelog content but
|
|
483
|
+
does not own this commit; the release helper commits release metadata
|
|
484
|
+
separately.
|
|
485
|
+
- Release-target resets also pass `bundle lock --bundler`, recording the
|
|
486
|
+
Bundler that performed the final reset in each committed release
|
|
487
|
+
lockfile. This keeps the normalized lockfile consistent with the
|
|
488
|
+
Bundler used by the release.
|
|
470
489
|
- During `kettle-release`, release lockfiles are reset before the release
|
|
471
490
|
prep commit. Before pushing, `kettle-release` validates those committed
|
|
472
491
|
lockfiles again; if local paths or checksum gaps reappear, it prints the
|
|
@@ -1014,7 +1033,7 @@ Thanks for RTFM. āŗļø
|
|
|
1014
1033
|
[šgitmoji]: https://gitmoji.dev
|
|
1015
1034
|
[šgitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1016
1035
|
[š§®kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1017
|
-
[š§®kloc-img]: https://img.shields.io/badge/KLOC-6.
|
|
1036
|
+
[š§®kloc-img]: https://img.shields.io/badge/KLOC-6.361-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1018
1037
|
[šsecurity]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
|
|
1019
1038
|
[šsecurity-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1020
1039
|
[šcopyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/exe/kettle-release
CHANGED
|
@@ -49,16 +49,16 @@ end
|
|
|
49
49
|
# Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
|
|
50
50
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
51
51
|
puts <<~USAGE
|
|
52
|
-
Usage: kettle-release [--version VERSION] [--local-ci] [--yes] [--appraisal-update] [--skip-changelog] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [--required-remotes REMOTES] [--secrets-provider NAME] [--json|--events[=TYPES]] [--report PATH] [start_step=<0-19>]
|
|
52
|
+
Usage: kettle-release [--version VERSION] [--local-ci] [--yes] [--appraisal-update] [--skip-changelog] [--skip-bundle-audit] [--start-step STEP] [--skip-ci] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [--required-remotes REMOTES] [--secrets-provider NAME] [--json|--events[=TYPES]] [--report PATH] [start_step=<0-19>]
|
|
53
53
|
|
|
54
54
|
Automates the release flow for a Ruby gem in the host project.
|
|
55
55
|
|
|
56
56
|
Full releases start at step 0 and run `kettle-pre-release` plus
|
|
57
|
-
`kettle-changelog` before the numbered release steps. Any
|
|
57
|
+
`kettle-changelog` before the numbered release steps. Any start step
|
|
58
58
|
greater than 0 skips pre-release prep and resumes directly at the
|
|
59
59
|
requested numbered step.
|
|
60
60
|
|
|
61
|
-
Start steps (use start_step=<n>
|
|
61
|
+
Start steps (use --start-step <n>; start_step=<n> remains supported):
|
|
62
62
|
0. Run kettle-pre-release gates and kettle-changelog
|
|
63
63
|
1. Verify Bundler >= 2.7 (start at 1 to skip pre-release checks)
|
|
64
64
|
2. Detect version; RubyGems sanity check; confirm CHANGELOG/version; sync copyright years; update badges/headers
|
|
@@ -82,7 +82,9 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
82
82
|
19. Push tags to remotes (final)
|
|
83
83
|
|
|
84
84
|
Options:
|
|
85
|
-
|
|
85
|
+
--start-step NUMBER # Begin at the numbered step above; use 10 only for a transient CI failure on the same commit
|
|
86
|
+
start_step=<number> # Legacy alias for --start-step
|
|
87
|
+
--skip-ci # Alias for --skip-steps 10; skips remote CI monitoring while running the rest
|
|
86
88
|
skip_steps=<numbers> # Comma-separated numbered steps to skip while running the rest; 10 skips remote CI monitoring
|
|
87
89
|
--skip-steps NUMBERS # Alias for skip_steps=NUMBERS (e.g., --skip-steps 10 or --skip-steps 10,18)
|
|
88
90
|
--version VERSION # Use this version instead of detecting VERSION from lib/**/version.rb
|
|
@@ -101,9 +103,11 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
101
103
|
# create the git tag locally, then push commits and tags after publish
|
|
102
104
|
|
|
103
105
|
Recovery guidance:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
--start-step 10 # Retry only CI monitoring after a transient CI/provider failure; do not use after code or metadata changes
|
|
107
|
+
--start-step 11 # Continue after CI monitoring only when that same release commit was intentionally accepted
|
|
108
|
+
--skip-ci # Skip remote CI monitoring for the same prepared release commit; this does not rerun or replace CI
|
|
109
|
+
start_step=10 # Legacy spelling of --start-step 10
|
|
110
|
+
skip_steps=10 # Legacy spelling of --skip-ci
|
|
107
111
|
kettle-release # After code, version, changelog, or lockfile changes, rerun the complete flow from step 0
|
|
108
112
|
|
|
109
113
|
Environment:
|
|
@@ -139,10 +143,11 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
139
143
|
Examples:
|
|
140
144
|
kettle-release # full flow from the top (start_step=0)
|
|
141
145
|
kettle-release --local-ci # run local act CI, publish gem, then push commits/tags
|
|
142
|
-
kettle-release
|
|
143
|
-
kettle-release
|
|
144
|
-
kettle-release
|
|
145
|
-
|
|
146
|
+
kettle-release --start-step 1 # skip pre-release checks and start numbered release steps
|
|
147
|
+
kettle-release --start-step 10 # retry CI monitoring after a transient failure on the unchanged release commit
|
|
148
|
+
kettle-release --skip-ci # intentionally skip remote CI monitoring for the unchanged release commit
|
|
149
|
+
kettle-release start_step=10 # legacy spelling of --start-step 10
|
|
150
|
+
SKIP_GEM_SIGNING=true kettle-release --start-step 14 # build+release without signing
|
|
146
151
|
USAGE
|
|
147
152
|
exit 0
|
|
148
153
|
end
|
|
@@ -184,6 +189,31 @@ def extract_skip_steps_arg!(argv)
|
|
|
184
189
|
skip_steps
|
|
185
190
|
end
|
|
186
191
|
|
|
192
|
+
def extract_start_step_arg!(argv)
|
|
193
|
+
start_step = nil
|
|
194
|
+
if (idx = argv.index("--start-step"))
|
|
195
|
+
start_step = argv[idx + 1]
|
|
196
|
+
if start_step.to_s.empty? || start_step.to_s.start_with?("-")
|
|
197
|
+
Kettle::Dev::ExitAdapter.abort("--start-step requires a numbered release step")
|
|
198
|
+
end
|
|
199
|
+
argv.slice!(idx, 2)
|
|
200
|
+
end
|
|
201
|
+
argv.delete_if do |arg|
|
|
202
|
+
if arg.start_with?("--start-step=", "start_step=")
|
|
203
|
+
start_step = arg.split("=", 2)[1]
|
|
204
|
+
Kettle::Dev::ExitAdapter.abort("--start-step requires a numbered release step") if start_step.to_s.empty?
|
|
205
|
+
true
|
|
206
|
+
else
|
|
207
|
+
false
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
start_step
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def extract_skip_ci_arg!(argv)
|
|
214
|
+
!!argv.delete("--skip-ci")
|
|
215
|
+
end
|
|
216
|
+
|
|
187
217
|
def extract_skip_changelog_arg!(argv)
|
|
188
218
|
skip_changelog = !!argv.delete("--skip-changelog")
|
|
189
219
|
argv.delete_if do |arg|
|
|
@@ -326,10 +356,11 @@ skip_changelog = extract_skip_changelog_arg!(ARGV)
|
|
|
326
356
|
json_output = !!ARGV.delete("--json")
|
|
327
357
|
events_output, event_types = extract_events_arg!(ARGV)
|
|
328
358
|
Kettle::Dev::ExitAdapter.abort("--events cannot be combined with --json") if events_output && json_output
|
|
329
|
-
|
|
330
|
-
|
|
359
|
+
start_step = extract_start_step_arg!(ARGV) || 0
|
|
360
|
+
skip_ci = extract_skip_ci_arg!(ARGV)
|
|
331
361
|
version_override = extract_version_arg!(ARGV)
|
|
332
362
|
skip_steps = extract_skip_steps_arg!(ARGV)
|
|
363
|
+
skip_steps = [skip_steps, "10"].compact.join(",") if skip_ci
|
|
333
364
|
ci_workflows = extract_ci_workflows_arg!(ARGV)
|
|
334
365
|
skip_remotes = extract_skip_remotes_arg!(ARGV)
|
|
335
366
|
required_remotes = extract_required_remotes_arg!(ARGV)
|
|
@@ -49,7 +49,7 @@ module Kettle
|
|
|
49
49
|
#
|
|
50
50
|
# @param restart_hint [String] guidance command shown on failure
|
|
51
51
|
# @return [Boolean] whether all configured CI providers passed
|
|
52
|
-
def monitor_all!(restart_hint: "bundle exec kettle-release
|
|
52
|
+
def monitor_all!(restart_hint: "bundle exec kettle-release --start-step 10", workflows: nil, keepalive: nil, event_recorder: nil, **options)
|
|
53
53
|
results = [
|
|
54
54
|
monitor_github_internal!(restart_hint: restart_hint, workflows: workflows, keepalive: keepalive, event_recorder: event_recorder),
|
|
55
55
|
monitor_gitlab_internal!(restart_hint: restart_hint, keepalive: keepalive, event_recorder: event_recorder)
|
|
@@ -69,7 +69,7 @@ module Kettle
|
|
|
69
69
|
# Returns false when GitLab is not configured for this repo/branch.
|
|
70
70
|
# @param restart_hint [String]
|
|
71
71
|
# @return [Boolean]
|
|
72
|
-
def monitor_gitlab!(restart_hint: "bundle exec kettle-release
|
|
72
|
+
def monitor_gitlab!(restart_hint: "bundle exec kettle-release --start-step 10")
|
|
73
73
|
result = monitor_gitlab_internal!(restart_hint: restart_hint)
|
|
74
74
|
result.nil? ? false : result
|
|
75
75
|
end
|
|
@@ -138,7 +138,7 @@ module Kettle
|
|
|
138
138
|
# Designed for kettle-release.
|
|
139
139
|
# @param restart_hint [String]
|
|
140
140
|
# @return [void]
|
|
141
|
-
def monitor_and_prompt_for_release!(restart_hint: "bundle exec kettle-release
|
|
141
|
+
def monitor_and_prompt_for_release!(restart_hint: "bundle exec kettle-release --start-step 10")
|
|
142
142
|
results = collect_all
|
|
143
143
|
any_checks = !(results[:github].nil? || results[:github].empty?) || !!results[:gitlab]
|
|
144
144
|
abort("CI configuration not detected (GitHub or GitLab). Ensure CI is configured and remotes point to the correct hosts.") unless any_checks
|
|
@@ -82,6 +82,21 @@ module Kettle
|
|
|
82
82
|
false
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
# Commit only the changes already staged by the caller.
|
|
86
|
+
#
|
|
87
|
+
# This is intentionally different from +commit_all+: bundle maintenance
|
|
88
|
+
# must not sweep unrelated release-preparation changes into its own
|
|
89
|
+
# atomic commit.
|
|
90
|
+
#
|
|
91
|
+
# @param message [String]
|
|
92
|
+
# @return [Boolean]
|
|
93
|
+
def commit_staged(message)
|
|
94
|
+
git_system("commit", "-m", message.to_s)
|
|
95
|
+
rescue => e
|
|
96
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
85
100
|
# Amend the current commit without changing its message.
|
|
86
101
|
#
|
|
87
102
|
# @return [Boolean]
|
|
@@ -116,7 +116,6 @@ module Kettle
|
|
|
116
116
|
BundlerEnvGuard.warn_unexpected_env!
|
|
117
117
|
paths = lockfile_paths_for(target)
|
|
118
118
|
force_full_update = release_lockfiles_target?(target)
|
|
119
|
-
skip_changelog_dependency ||= force_full_update
|
|
120
119
|
platforms = release_platforms_for(paths) if force_full_update
|
|
121
120
|
uninstall_unreleased_local_gems(paths) if force_full_update
|
|
122
121
|
paths.each do |path|
|
|
@@ -125,7 +124,8 @@ module Kettle
|
|
|
125
124
|
path,
|
|
126
125
|
full_update: force_full_update,
|
|
127
126
|
platforms: platforms&.fetch(path),
|
|
128
|
-
skip_changelog_dependency: skip_changelog_dependency
|
|
127
|
+
skip_changelog_dependency: skip_changelog_dependency,
|
|
128
|
+
update_bundler: force_full_update
|
|
129
129
|
)
|
|
130
130
|
end
|
|
131
131
|
end
|
|
@@ -135,7 +135,8 @@ module Kettle
|
|
|
135
135
|
path,
|
|
136
136
|
full_update: true,
|
|
137
137
|
platforms: platforms.fetch(path),
|
|
138
|
-
skip_changelog_dependency: skip_changelog_dependency
|
|
138
|
+
skip_changelog_dependency: skip_changelog_dependency,
|
|
139
|
+
update_bundler: true
|
|
139
140
|
)
|
|
140
141
|
end
|
|
141
142
|
end
|
|
@@ -145,7 +146,7 @@ module Kettle
|
|
|
145
146
|
paths
|
|
146
147
|
end
|
|
147
148
|
|
|
148
|
-
def reset_lockfile!(path, full_update: false, platforms: nil, skip_changelog_dependency: false)
|
|
149
|
+
def reset_lockfile!(path, full_update: false, platforms: nil, skip_changelog_dependency: false, update_bundler: false)
|
|
149
150
|
gemfile = gemfile_for_lockfile(path)
|
|
150
151
|
unless gemfile && File.file?(gemfile)
|
|
151
152
|
warn("Cannot reset #{display_path(path)} because its Gemfile was not found.")
|
|
@@ -159,6 +160,7 @@ module Kettle
|
|
|
159
160
|
full_update: full_update,
|
|
160
161
|
platforms: platforms,
|
|
161
162
|
skip_changelog_dependency: skip_changelog_dependency,
|
|
163
|
+
update_bundler: update_bundler,
|
|
162
164
|
isolated_gem_home: gem_home
|
|
163
165
|
)
|
|
164
166
|
if full_update || has_local_path_remote?(path)
|
|
@@ -169,7 +171,7 @@ module Kettle
|
|
|
169
171
|
end
|
|
170
172
|
end
|
|
171
173
|
|
|
172
|
-
def reset_command(path:, gemfile:, full_update: false, platforms: nil, skip_changelog_dependency: false, isolated_gem_home: nil)
|
|
174
|
+
def reset_command(path:, gemfile:, full_update: false, platforms: nil, skip_changelog_dependency: false, update_bundler: false, isolated_gem_home: nil)
|
|
173
175
|
update_gems = (full_update || has_local_path_remote?(path)) ? [] : reset_update_gems(path)
|
|
174
176
|
platforms ||= reset_platforms(path)
|
|
175
177
|
removed_platforms = full_update ? [] : lockfile_platforms(path) - platforms
|
|
@@ -198,6 +200,11 @@ module Kettle
|
|
|
198
200
|
end
|
|
199
201
|
command << " --update"
|
|
200
202
|
command << " #{update_gems.map { |gem_name| Shellwords.escape(gem_name) }.join(" ")}" unless update_gems.empty?
|
|
203
|
+
# Bundler preserves BUNDLED WITH during a normal lockfile update.
|
|
204
|
+
# Release lockfiles must record the Bundler that performed the final
|
|
205
|
+
# reset, or the next release command can dirty them after the prep
|
|
206
|
+
# commit by reconciling to a newer installed Bundler.
|
|
207
|
+
command << " --bundler" if update_bundler
|
|
201
208
|
command << " --add-checksums"
|
|
202
209
|
command
|
|
203
210
|
end
|
|
@@ -145,6 +145,7 @@ module Kettle
|
|
|
145
145
|
words = effective_command_words(cmd)
|
|
146
146
|
words == ["bin/setup"] ||
|
|
147
147
|
words.first(1) == ["bin/rake"] ||
|
|
148
|
+
words.first(2) == ["bundle", "update"] ||
|
|
148
149
|
words.first(3) == ["bundle", "exec", "rake"] ||
|
|
149
150
|
words.first(3) == ["bundle", "exec", "kettle-changelog"]
|
|
150
151
|
rescue ArgumentError
|
|
@@ -252,8 +253,12 @@ module Kettle
|
|
|
252
253
|
def run_with_release_environment
|
|
253
254
|
run_pre_release_checks! if run_step?(0)
|
|
254
255
|
|
|
255
|
-
# 1. Ensure Bundler version
|
|
256
|
-
|
|
256
|
+
# 1. Ensure Bundler version and record its current release in the
|
|
257
|
+
# development lockfiles before any release-preparation commit.
|
|
258
|
+
if run_step?(1)
|
|
259
|
+
ensure_bundler_2_7_plus!
|
|
260
|
+
update_bundler_and_commit!
|
|
261
|
+
end
|
|
257
262
|
|
|
258
263
|
version = nil
|
|
259
264
|
committed = nil
|
|
@@ -690,8 +695,7 @@ module Kettle
|
|
|
690
695
|
puts "Resetting release lockfiles with local path dependencies disabled #{stage} (attempt #{attempt}/#{attempts})..."
|
|
691
696
|
begin
|
|
692
697
|
lockfile_reset.reset(
|
|
693
|
-
Kettle::Dev::LockfileReset::RELEASE_LOCKFILES_TARGET
|
|
694
|
-
skip_changelog_dependency: true
|
|
698
|
+
Kettle::Dev::LockfileReset::RELEASE_LOCKFILES_TARGET
|
|
695
699
|
)
|
|
696
700
|
emit_release_lockfile_event(action: "reset", status: "ok", stage: stage, attempt: attempt, attempts: attempts)
|
|
697
701
|
break
|
|
@@ -778,7 +782,7 @@ module Kettle
|
|
|
778
782
|
end
|
|
779
783
|
|
|
780
784
|
def normalize_release_lockfile!(path)
|
|
781
|
-
lockfile_reset.reset_lockfile!(path
|
|
785
|
+
lockfile_reset.reset_lockfile!(path)
|
|
782
786
|
end
|
|
783
787
|
|
|
784
788
|
def release_gemfile_for_lockfile(path)
|
|
@@ -786,7 +790,7 @@ module Kettle
|
|
|
786
790
|
end
|
|
787
791
|
|
|
788
792
|
def release_lockfile_normalization_env
|
|
789
|
-
lockfile_reset.normalization_env
|
|
793
|
+
lockfile_reset.normalization_env
|
|
790
794
|
end
|
|
791
795
|
|
|
792
796
|
def release_lockfile_diagnostics(path)
|
|
@@ -1253,7 +1257,7 @@ module Kettle
|
|
|
1253
1257
|
def monitor_workflows_after_push!
|
|
1254
1258
|
ensure_github_pull_request_for_ci!
|
|
1255
1259
|
keep_release_secrets_alive!("CI monitoring")
|
|
1256
|
-
restart_hint = "bundle exec kettle-release
|
|
1260
|
+
restart_hint = "bundle exec kettle-release --start-step 10"
|
|
1257
1261
|
emit_ci_monitor_event(action: "start", status: "started", workflows: @ci_workflows, restart_hint: restart_hint)
|
|
1258
1262
|
# The monitor preserves fail-fast behavior by default and returns false
|
|
1259
1263
|
# when K_RELEASE_CI_CONTINUE explicitly allows failed CI checks.
|
|
@@ -1602,6 +1606,90 @@ module Kettle
|
|
|
1602
1606
|
end
|
|
1603
1607
|
end
|
|
1604
1608
|
|
|
1609
|
+
# Keep Bundler maintenance separate from the release metadata commit.
|
|
1610
|
+
# The project lockfiles are shared development state, so the update must
|
|
1611
|
+
# be real and committed rather than hidden behind a temporary lockfile.
|
|
1612
|
+
def update_bundler_and_commit!
|
|
1613
|
+
run_cmd!(release_project_command("bundle update --bundler"))
|
|
1614
|
+
|
|
1615
|
+
appraisal_gemfile = File.join(@root, "Appraisal.root.gemfile")
|
|
1616
|
+
if File.file?(appraisal_gemfile)
|
|
1617
|
+
appraisal_lockfile = File.join(@root, "Appraisal.root.gemfile.lock")
|
|
1618
|
+
appraisal_command = "BUNDLE_GEMFILE=#{Shellwords.escape(appraisal_gemfile)} " \
|
|
1619
|
+
"BUNDLE_LOCKFILE=#{Shellwords.escape(appraisal_lockfile)} bundle update --bundler"
|
|
1620
|
+
run_cmd!(release_project_command(appraisal_command))
|
|
1621
|
+
run_cmd!(release_project_command("bundle exec rake appraisal:reset"))
|
|
1622
|
+
end
|
|
1623
|
+
|
|
1624
|
+
commit_bundle_update!
|
|
1625
|
+
end
|
|
1626
|
+
|
|
1627
|
+
def commit_bundle_update!
|
|
1628
|
+
paths = changed_bundle_lockfile_paths
|
|
1629
|
+
return false if paths.empty?
|
|
1630
|
+
|
|
1631
|
+
unexpected = staged_paths - paths
|
|
1632
|
+
unless unexpected.empty?
|
|
1633
|
+
abort(<<~MSG)
|
|
1634
|
+
Cannot commit Bundler updates atomically because unrelated files are already staged:
|
|
1635
|
+
#{unexpected.map { |path| " - #{path}" }.join("\n")}
|
|
1636
|
+
Commit or unstage those files, then retry the release.
|
|
1637
|
+
MSG
|
|
1638
|
+
end
|
|
1639
|
+
|
|
1640
|
+
puts "Committing Bundler update in #{paths.length} lockfile(s)."
|
|
1641
|
+
abort("Failed to stage Bundler update lockfiles.") unless @git.add_paths(paths)
|
|
1642
|
+
abort("Failed to commit Bundler update lockfiles.") unless @git.commit_staged("šļø Update bundle")
|
|
1643
|
+
reconcile_bundle_update_commit!
|
|
1644
|
+
true
|
|
1645
|
+
end
|
|
1646
|
+
|
|
1647
|
+
def changed_bundle_lockfile_paths
|
|
1648
|
+
tracked, tracked_ok = git_output(["diff", "--name-only", "HEAD", "--"])
|
|
1649
|
+
untracked, untracked_ok = git_output(["ls-files", "--others", "--exclude-standard"])
|
|
1650
|
+
return [] unless tracked_ok && untracked_ok
|
|
1651
|
+
|
|
1652
|
+
(tracked.lines + untracked.lines).map(&:strip).reject(&:empty?).select { |path| bundle_lockfile_path?(path) }.uniq
|
|
1653
|
+
end
|
|
1654
|
+
|
|
1655
|
+
def staged_paths
|
|
1656
|
+
output, ok = git_output(["diff", "--cached", "--name-only"])
|
|
1657
|
+
return [] unless ok
|
|
1658
|
+
|
|
1659
|
+
output.lines.map(&:strip).reject(&:empty?).uniq
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
def bundle_lockfile_path?(path)
|
|
1663
|
+
path == "Gemfile.lock" || File.basename(path).end_with?(".lock")
|
|
1664
|
+
end
|
|
1665
|
+
|
|
1666
|
+
def reconcile_bundle_update_commit!
|
|
1667
|
+
3.times do
|
|
1668
|
+
paths = changed_bundle_lockfile_paths
|
|
1669
|
+
return if paths.empty?
|
|
1670
|
+
|
|
1671
|
+
puts "Bundler changed lockfiles while committing; amending the bundle update commit."
|
|
1672
|
+
abort("Failed to stage post-commit Bundler lockfile changes.") unless @git.add_paths(paths)
|
|
1673
|
+
abort("Failed to amend the bundle update commit.") unless @git.commit_amend_no_edit
|
|
1674
|
+
end
|
|
1675
|
+
|
|
1676
|
+
abort("Bundler continued changing lockfiles after the bundle update commit.") unless changed_bundle_lockfile_paths.empty?
|
|
1677
|
+
end
|
|
1678
|
+
|
|
1679
|
+
def reconcile_release_prep_commit!
|
|
1680
|
+
3.times do
|
|
1681
|
+
output, = git_output(["status", "--porcelain"])
|
|
1682
|
+
return if output.empty?
|
|
1683
|
+
|
|
1684
|
+
puts "Release commit hooks changed tracked files; amending the release preparation commit."
|
|
1685
|
+
abort("Failed to stage post-commit release changes.") unless @git.add_all
|
|
1686
|
+
abort("Failed to amend the release preparation commit.") unless @git.commit_amend_no_edit
|
|
1687
|
+
end
|
|
1688
|
+
|
|
1689
|
+
output, = git_output(["status", "--porcelain"])
|
|
1690
|
+
abort("Release commit hooks continued changing files after the release preparation commit.") unless output.empty?
|
|
1691
|
+
end
|
|
1692
|
+
|
|
1605
1693
|
def maybe_run_local_ci_before_push!(committed, force: false)
|
|
1606
1694
|
mode = (ENV["K_RELEASE_LOCAL_CI"] || "").strip.downcase
|
|
1607
1695
|
run_it =
|
|
@@ -1898,6 +1986,7 @@ module Kettle
|
|
|
1898
1986
|
false
|
|
1899
1987
|
else
|
|
1900
1988
|
abort("Failed to commit release prep changes.") unless @git.commit_all(msg)
|
|
1989
|
+
reconcile_release_prep_commit!
|
|
1901
1990
|
true
|
|
1902
1991
|
end
|
|
1903
1992
|
end
|
|
@@ -1982,6 +2071,7 @@ module Kettle
|
|
|
1982
2071
|
return "gem_release" if joined == "bundle exec rake release"
|
|
1983
2072
|
return "gem_push" if effective_words.first(2) == ["gem", "push"]
|
|
1984
2073
|
return "gem_checksums" if effective_words.first == "bin/gem_checksums"
|
|
2074
|
+
return "bundle_update" if effective_words.first(2) == ["bundle", "update"]
|
|
1985
2075
|
return "bundle_lock" if effective_words.first(2) == ["bundle", "lock"]
|
|
1986
2076
|
return "bin_setup" if effective_words == ["bin/setup"]
|
|
1987
2077
|
return "default_task" if effective_words == ["bin/rake"]
|
|
@@ -2023,6 +2113,7 @@ module Kettle
|
|
|
2023
2113
|
joined = effective_words.join(" ")
|
|
2024
2114
|
|
|
2025
2115
|
return "changelog" if joined.start_with?("bundle exec kettle-changelog")
|
|
2116
|
+
return "bundle update" if effective_words.first(2) == ["bundle", "update"]
|
|
2026
2117
|
return command_event_bundle_lock_summary(words) if effective_words.first(2) == ["bundle", "lock"]
|
|
2027
2118
|
return "setup" if effective_words == ["bin/setup"]
|
|
2028
2119
|
return "default task" if effective_words == ["bin/rake"]
|
data/lib/kettle/dev/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = "3.0.
|
|
8
|
+
VERSION = "3.0.14"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -189,11 +189,7 @@ module Kettle
|
|
|
189
189
|
|
|
190
190
|
source = File.read(gemspec_path)
|
|
191
191
|
parse_result = self.class.parse_source(source, gemspec_path)
|
|
192
|
-
|
|
193
|
-
next unless node.is_a?(Prism::CallNode) && node.name == :version=
|
|
194
|
-
|
|
195
|
-
version_node = node.arguments&.arguments&.first
|
|
196
|
-
next unless version_node.is_a?(Prism::StringNode)
|
|
192
|
+
gemspec_version_literal_nodes(parse_result).filter_map do |version_node|
|
|
197
193
|
next unless version_node.unescaped == current_version
|
|
198
194
|
next if version_node.unescaped == target_version
|
|
199
195
|
|
|
@@ -202,6 +198,34 @@ module Kettle
|
|
|
202
198
|
end
|
|
203
199
|
end
|
|
204
200
|
|
|
201
|
+
def gemspec_version_literal_nodes(parse_result)
|
|
202
|
+
version_assignments = self.class.each_node(parse_result.value).select do |node|
|
|
203
|
+
node.is_a?(Prism::CallNode) && node.name == :version=
|
|
204
|
+
end
|
|
205
|
+
local_version_assignments = self.class.each_node(parse_result.value).select do |node|
|
|
206
|
+
node.is_a?(Prism::LocalVariableWriteNode) && node.name == :gem_version
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
version_assignments.flat_map do |assignment|
|
|
210
|
+
value = assignment.arguments&.arguments&.first
|
|
211
|
+
if value.is_a?(Prism::StringNode)
|
|
212
|
+
[value]
|
|
213
|
+
elsif value.is_a?(Prism::LocalVariableReadNode) && value.name == :gem_version
|
|
214
|
+
local_version_assignments.flat_map { |node| version_literals(node.value) }
|
|
215
|
+
else
|
|
216
|
+
version_literals(value)
|
|
217
|
+
end
|
|
218
|
+
end.uniq
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def version_literals(node)
|
|
222
|
+
return [] unless node
|
|
223
|
+
|
|
224
|
+
self.class.each_node(node).select do |candidate|
|
|
225
|
+
candidate.is_a?(Prism::StringNode)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
205
229
|
def gemspec_path_for_bump
|
|
206
230
|
paths = Dir.glob(File.join(root, "*.gemspec")).sort
|
|
207
231
|
return nil if paths.empty?
|
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: 3.0.
|
|
4
|
+
version: 3.0.14
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -60,7 +60,7 @@ dependencies:
|
|
|
60
60
|
version: '0.3'
|
|
61
61
|
- - ">="
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 0.3.
|
|
63
|
+
version: 0.3.14
|
|
64
64
|
type: :runtime
|
|
65
65
|
prerelease: false
|
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -70,7 +70,7 @@ dependencies:
|
|
|
70
70
|
version: '0.3'
|
|
71
71
|
- - ">="
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: 0.3.
|
|
73
|
+
version: 0.3.14
|
|
74
74
|
- !ruby/object:Gem::Dependency
|
|
75
75
|
name: kettle-ndjson
|
|
76
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,7 +80,7 @@ dependencies:
|
|
|
80
80
|
version: '0.1'
|
|
81
81
|
- - ">="
|
|
82
82
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: 0.1.
|
|
83
|
+
version: 0.1.13
|
|
84
84
|
type: :runtime
|
|
85
85
|
prerelease: false
|
|
86
86
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -90,7 +90,7 @@ dependencies:
|
|
|
90
90
|
version: '0.1'
|
|
91
91
|
- - ">="
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
|
-
version: 0.1.
|
|
93
|
+
version: 0.1.13
|
|
94
94
|
- !ruby/object:Gem::Dependency
|
|
95
95
|
name: kettle-rb
|
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,7 +100,7 @@ dependencies:
|
|
|
100
100
|
version: '0.1'
|
|
101
101
|
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: 0.1.
|
|
103
|
+
version: 0.1.11
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -110,7 +110,7 @@ dependencies:
|
|
|
110
110
|
version: '0.1'
|
|
111
111
|
- - ">="
|
|
112
112
|
- !ruby/object:Gem::Version
|
|
113
|
-
version: 0.1.
|
|
113
|
+
version: 0.1.11
|
|
114
114
|
- !ruby/object:Gem::Dependency
|
|
115
115
|
name: kettle-test
|
|
116
116
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -256,7 +256,7 @@ dependencies:
|
|
|
256
256
|
version: '3.2'
|
|
257
257
|
- - ">="
|
|
258
258
|
- !ruby/object:Gem::Version
|
|
259
|
-
version: 3.2.
|
|
259
|
+
version: 3.2.6
|
|
260
260
|
type: :development
|
|
261
261
|
prerelease: false
|
|
262
262
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -266,7 +266,7 @@ dependencies:
|
|
|
266
266
|
version: '3.2'
|
|
267
267
|
- - ">="
|
|
268
268
|
- !ruby/object:Gem::Version
|
|
269
|
-
version: 3.2.
|
|
269
|
+
version: 3.2.6
|
|
270
270
|
- !ruby/object:Gem::Dependency
|
|
271
271
|
name: stone_checksums
|
|
272
272
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -296,7 +296,7 @@ dependencies:
|
|
|
296
296
|
version: '2.0'
|
|
297
297
|
- - ">="
|
|
298
298
|
- !ruby/object:Gem::Version
|
|
299
|
-
version: 2.0.
|
|
299
|
+
version: 2.0.12
|
|
300
300
|
type: :development
|
|
301
301
|
prerelease: false
|
|
302
302
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -306,7 +306,7 @@ dependencies:
|
|
|
306
306
|
version: '2.0'
|
|
307
307
|
- - ">="
|
|
308
308
|
- !ruby/object:Gem::Version
|
|
309
|
-
version: 2.0.
|
|
309
|
+
version: 2.0.12
|
|
310
310
|
description: "\U0001F372 Kettle::Dev is a meta tool from kettle-dev to streamline
|
|
311
311
|
development and testing. Acts as a shim dependency, pulling in many other dependencies,
|
|
312
312
|
to give you OOTB productivity with a RubyGem, or Ruby app project. Configures a
|
|
@@ -400,10 +400,10 @@ licenses:
|
|
|
400
400
|
- AGPL-3.0-only
|
|
401
401
|
metadata:
|
|
402
402
|
homepage_uri: https://kettle-dev.galtzo.com
|
|
403
|
-
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.
|
|
404
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.
|
|
403
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.14
|
|
404
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.14/CHANGELOG.md
|
|
405
405
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
406
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.
|
|
406
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.14
|
|
407
407
|
funding_uri: https://github.com/sponsors/pboling
|
|
408
408
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
409
409
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|