kettle-family 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +54 -1
- data/CONTRIBUTING.md +2 -2
- data/LICENSE.md +3 -1
- data/README.md +43 -70
- data/lib/kettle/family/changelog_check.rb +19 -7
- data/lib/kettle/family/cli.rb +6 -5
- data/lib/kettle/family/config.rb +84 -1
- data/lib/kettle/family/discovery.rb +24 -2
- data/lib/kettle/family/member.rb +8 -2
- data/lib/kettle/family/readiness_check.rb +72 -6
- data/lib/kettle/family/release_state_check.rb +256 -11
- data/lib/kettle/family/release_state_result.rb +4 -2
- data/lib/kettle/family/report.rb +37 -2
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +25 -8
- data.tar.gz.sig +0 -0
- metadata +13 -13
- 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: b3ec93e3425d765de198975bb390031d33f4a408c31994a18999e3178aec2cee
|
|
4
|
+
data.tar.gz: c22c0cec6c2dc9a387281adb456d7303bc838e687be27b0c69ecc59b7f8038ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7520e01212b4faebaed857c69a7bd0c068be891e8fa79c4ccb7485f9cd8fdd4daa824c5943cc030da765992422a1bf252fe76fde2e68b3b4f0feeaf179beb30a
|
|
7
|
+
data.tar.gz: '0683aae4dfdbbde5b004719856f32b65f7810acd81446145456a7810e4625f85a516f1d53d08d0c1622f65d7d8a2902c0dfb2ff57b35950f19bc76355cdded56'
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,55 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [0.1.4] - 2026-06-16
|
|
34
|
+
|
|
35
|
+
- TAG: [v0.1.4][0.1.4t]
|
|
36
|
+
- COVERAGE: 93.72% -- 1060/1131 lines in 19 files
|
|
37
|
+
- BRANCH COVERAGE: 76.12% -- 322/423 branches in 19 files
|
|
38
|
+
- 40.14% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- Added configurable readiness checks, root/shared changelog support, release
|
|
43
|
+
environment overrides, and an optional family changelog release phase for
|
|
44
|
+
monorepo gem families whose members share root release metadata.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Fixed the Ruby 3.2 CI appraisal so root changelog release-state checks have
|
|
49
|
+
Prism available.
|
|
50
|
+
|
|
51
|
+
## [0.1.3] - 2026-06-14
|
|
52
|
+
|
|
53
|
+
- TAG: [v0.1.3][0.1.3t]
|
|
54
|
+
- COVERAGE: 94.34% -- 917/972 lines in 19 files
|
|
55
|
+
- BRANCH COVERAGE: 78.36% -- 268/342 branches in 19 files
|
|
56
|
+
- 44.00% documented
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- Runtime dependency `kettle-dev` now requires 2.2.8 or newer.
|
|
61
|
+
- `kettle-family release-state` now expands configured
|
|
62
|
+
`release.target_branches` and reports release state for each branch
|
|
63
|
+
independently.
|
|
64
|
+
|
|
65
|
+
- Project licensing changed from MIT to AGPL-3.0-only.
|
|
66
|
+
- `kettle-family release-state` now invokes `kettle-changelog` from the active
|
|
67
|
+
toolchain instead of depending on `kettle-dev` as a published runtime
|
|
68
|
+
dependency.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- Fixed release-state checks to use the active `kettle-dev` API instead of each
|
|
73
|
+
member's potentially stale bundle.
|
|
74
|
+
- Fixed default discovery excludes so top-level `vendor/`, `tmp/`, `spec/`, and
|
|
75
|
+
`test/` directories are ignored.
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- Added `kettle-family metadata` to report each family member's version, Ruby
|
|
80
|
+
requirement, licenses, and authors.
|
|
81
|
+
|
|
33
82
|
## [0.1.2] - 2026-06-13
|
|
34
83
|
|
|
35
84
|
- TAG: [v0.1.2][0.1.2t]
|
|
@@ -91,7 +140,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
91
140
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
92
141
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
93
142
|
|
|
94
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.1.
|
|
143
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.1.4...HEAD
|
|
144
|
+
[0.1.4]: https://github.com/kettle-dev/kettle-family/compare/v0.1.3...v0.1.4
|
|
145
|
+
[0.1.4t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.1.4
|
|
146
|
+
[0.1.3]: https://github.com/kettle-dev/kettle-family/compare/v0.1.2...v0.1.3
|
|
147
|
+
[0.1.3t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.1.3
|
|
95
148
|
[0.1.2]: https://github.com/kettle-dev/kettle-family/compare/v0.1.1...v0.1.2
|
|
96
149
|
[0.1.2t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.1.2
|
|
97
150
|
[0.1.1]: https://github.com/kettle-dev/kettle-family/compare/v0.1.0...v0.1.1
|
data/CONTRIBUTING.md
CHANGED
|
@@ -109,14 +109,14 @@ Git diff driver setup
|
|
|
109
109
|
- Git hosting forges generally ignore external diff drivers, so pull request views may still show raw textual diffs even when local `git diff` uses semantic drivers.
|
|
110
110
|
|
|
111
111
|
```console
|
|
112
|
-
K_JEM_TEMPLATING=true
|
|
112
|
+
K_JEM_TEMPLATING=true kettle-jem install
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
Troubleshooting Git diffs
|
|
116
116
|
- Use `git diff --no-ext-diff` to compare against Git's built-in diff output.
|
|
117
117
|
- Use `git diff --no-textconv` when a textconv projection obscures the raw file bytes you need to inspect.
|
|
118
118
|
- If Git reports a missing `smorg-*` executable, rerun `bundle install` and the setup command above, then check `git config --local --get-regexp '^diff\.smorg-'`.
|
|
119
|
-
- To remove managed local entries, run `K_JEM_TEMPLATING=true
|
|
119
|
+
- To remove managed local entries, run `K_JEM_TEMPLATING=true kettle-jem install --undo`; remove global command registrations with `git config --global --unset-all diff.smorg-ruby.command`.
|
|
120
120
|
|
|
121
121
|
For a quick starting point, this repository’s `mise.toml` defines the shared defaults, and `.env.local` can override them locally. Copy `.env.local.example` to `.env.local`, use `KEY=value` lines, and either activate `mise` in your shell or run commands through `mise exec -C /path/to/project -- ...`.
|
|
122
122
|
|
data/LICENSE.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
This project is made available under the following license.
|
|
4
4
|
Choose the option that best fits your use case:
|
|
5
5
|
|
|
6
|
-
- [
|
|
6
|
+
- [AGPL-3.0-only](AGPL-3.0-only.md)
|
|
7
|
+
|
|
8
|
+
If none of the above licenses fit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
|
|
7
9
|
|
|
8
10
|
## Copyright Notice
|
|
9
11
|
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# 👩👩👧👧 Kettle::Family
|
|
4
4
|
|
|
5
|
-
[![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License:
|
|
5
|
+
[![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only][📄license-img]][📄license] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf]
|
|
6
6
|
|
|
7
7
|
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][✉️discord-invite], as I may have missed the [discord notification][✉️discord-invite].
|
|
8
8
|
|
|
@@ -51,7 +51,7 @@ while RubyGems MFA prompts remain interactive.
|
|
|
51
51
|
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
|
52
52
|
| 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] |
|
|
53
53
|
| 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] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
|
|
54
|
-
| Compliance | [![License:
|
|
54
|
+
| Compliance | [![License: AGPL-3.0-only][📄license-img]][📄license] [![Apache license compatibility: Category X][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
|
55
55
|
| 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] |
|
|
56
56
|
| 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] |
|
|
57
57
|
| `...` 💖 | [![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] |
|
|
@@ -142,6 +142,36 @@ members:
|
|
|
142
142
|
- "**/vendor/**"
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
Monorepo families whose member gems share release metadata from the repository
|
|
146
|
+
root can configure readiness and changelog ownership explicitly:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
check:
|
|
150
|
+
required_files:
|
|
151
|
+
- Gemfile
|
|
152
|
+
- Rakefile
|
|
153
|
+
- README.md
|
|
154
|
+
- LICENSE.md
|
|
155
|
+
required_bins:
|
|
156
|
+
- bin/rake
|
|
157
|
+
- bin/rspec
|
|
158
|
+
root_required_files:
|
|
159
|
+
- CHANGELOG.md
|
|
160
|
+
- SECURITY.md
|
|
161
|
+
|
|
162
|
+
changelog:
|
|
163
|
+
mode: root
|
|
164
|
+
path: CHANGELOG.md
|
|
165
|
+
version_file: gems/tree_haver/lib/tree_haver/version.rb
|
|
166
|
+
|
|
167
|
+
release:
|
|
168
|
+
env:
|
|
169
|
+
KETTLE_RB_DEV: false
|
|
170
|
+
family_changelog:
|
|
171
|
+
enabled: true
|
|
172
|
+
command: bundle exec kettle-changelog
|
|
173
|
+
```
|
|
174
|
+
|
|
145
175
|
For a flat repository that releases from multiple long-lived branches, list the
|
|
146
176
|
release branches under `release.target_branches`. The branch list is processed
|
|
147
177
|
in order. Each branch must be clean enough for `git checkout`, and each branch
|
|
@@ -232,63 +262,6 @@ If you intentionally need to continue after CI failures, opt in explicitly:
|
|
|
232
262
|
kettle-family release --publish --execute --continue-ci-failures
|
|
233
263
|
```
|
|
234
264
|
|
|
235
|
-
## 🦷 FLOSS Funding
|
|
236
|
-
|
|
237
|
-
While kettle-dev tools are free software and will always be, the project would benefit immensely from some funding.
|
|
238
|
-
Raising a monthly budget of... "dollars" would make the project more sustainable.
|
|
239
|
-
|
|
240
|
-
We welcome both individual and corporate sponsors! We also offer a
|
|
241
|
-
wide array of funding channels to account for your preferences.
|
|
242
|
-
Currently, [Open Collective][🖇osc] is our preferred funding platform.
|
|
243
|
-
|
|
244
|
-
**If you're working in a company that's making significant use of kettle-dev tools we'd
|
|
245
|
-
appreciate it if you suggest to your company to become a kettle-dev sponsor.**
|
|
246
|
-
|
|
247
|
-
You can support the development of kettle-dev tools via
|
|
248
|
-
[GitHub Sponsors][🖇sponsor],
|
|
249
|
-
[Liberapay][⛳liberapay],
|
|
250
|
-
[PayPal][🖇paypal],
|
|
251
|
-
[Open Collective][🖇osc]
|
|
252
|
-
and [Tidelift][🏙️entsup-tidelift].
|
|
253
|
-
|
|
254
|
-
| 📍 NOTE |
|
|
255
|
-
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
256
|
-
| If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
|
|
257
|
-
|
|
258
|
-
### Open Collective for Individuals
|
|
259
|
-
|
|
260
|
-
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/kettle-dev#backer)]
|
|
261
|
-
|
|
262
|
-
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
263
|
-
|
|
264
|
-
<!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
|
|
265
|
-
No backers yet. Be the first!
|
|
266
|
-
<!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
|
|
267
|
-
|
|
268
|
-
### Open Collective for Organizations
|
|
269
|
-
|
|
270
|
-
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/kettle-dev#sponsor)]
|
|
271
|
-
|
|
272
|
-
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
273
|
-
|
|
274
|
-
<!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
|
|
275
|
-
No sponsors yet. Be the first!
|
|
276
|
-
<!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
|
|
277
|
-
|
|
278
|
-
[kettle-readme-backers]: https://github.com/kettle-dev/kettle-family/blob/main/exe/kettle-readme-backers
|
|
279
|
-
|
|
280
|
-
### Another way to support open-source
|
|
281
|
-
|
|
282
|
-
I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈 cats).
|
|
283
|
-
|
|
284
|
-
If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
|
|
285
|
-
|
|
286
|
-
I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
|
|
287
|
-
|
|
288
|
-
**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
|
|
289
|
-
|
|
290
|
-
[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS efforts using Patreon][🖇patreon-img]][🖇patreon]
|
|
291
|
-
|
|
292
265
|
## 🔐 Security
|
|
293
266
|
|
|
294
267
|
See [SECURITY.md][🔐security].
|
|
@@ -374,8 +347,10 @@ See [CHANGELOG.md][📌changelog] for a list of releases.
|
|
|
374
347
|
|
|
375
348
|
## 📄 License
|
|
376
349
|
|
|
377
|
-
The gem is available
|
|
378
|
-
|
|
350
|
+
The gem is available under the following license: [AGPL-3.0-only](AGPL-3.0-only.md).
|
|
351
|
+
See [LICENSE.md][📄license] for details.
|
|
352
|
+
|
|
353
|
+
If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
|
|
379
354
|
|
|
380
355
|
### © Copyright
|
|
381
356
|
|
|
@@ -534,8 +509,6 @@ Thanks for RTFM. ☺️
|
|
|
534
509
|
[🚎13-🔒️-wfi]: https://github.com/kettle-dev/kettle-family/actions/workflows/locked_deps.yml/badge.svg
|
|
535
510
|
[🚎14-🔓️-wf]: https://github.com/kettle-dev/kettle-family/actions/workflows/unlocked_deps.yml
|
|
536
511
|
[🚎14-🔓️-wfi]: https://github.com/kettle-dev/kettle-family/actions/workflows/unlocked_deps.yml/badge.svg
|
|
537
|
-
[🚎15-🪪-wf]: https://github.com/kettle-dev/kettle-family/actions/workflows/license-eye.yml
|
|
538
|
-
[🚎15-🪪-wfi]: https://github.com/kettle-dev/kettle-family/actions/workflows/license-eye.yml/badge.svg
|
|
539
512
|
[💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
540
513
|
[💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
541
514
|
[💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
@@ -573,15 +546,15 @@ Thanks for RTFM. ☺️
|
|
|
573
546
|
[📌gitmoji]: https://gitmoji.dev
|
|
574
547
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
575
548
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
576
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-
|
|
549
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-1.131-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
577
550
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
578
551
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
579
552
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
580
553
|
[📄license]: LICENSE.md
|
|
581
|
-
[📄license-ref]:
|
|
582
|
-
[📄license-img]: https://img.shields.io/badge/License-
|
|
583
|
-
[📄license-compat]: https://www.apache.org/legal/resolved.html#category-
|
|
584
|
-
[📄license-compat-img]: https://img.shields.io/badge/
|
|
554
|
+
[📄license-ref]: AGPL-3.0-only.md
|
|
555
|
+
[📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only-259D6C.svg
|
|
556
|
+
[📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
|
|
557
|
+
[📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
|
|
585
558
|
|
|
586
559
|
[📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
|
|
587
560
|
[📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
|
|
@@ -601,7 +574,7 @@ Thanks for RTFM. ☺️
|
|
|
601
574
|
| Package | kettle-family |
|
|
602
575
|
| Description | 👩👩👧👧 Kettle::Family provides scripts and conventions for coordinating related Ruby gems as one family. |
|
|
603
576
|
| Homepage | https://github.com/kettle-dev/kettle-family |
|
|
604
|
-
| Source | https://github.com/kettle-dev/kettle-family
|
|
605
|
-
| License | `
|
|
577
|
+
| Source | https://github.com/kettle-dev/kettle-family |
|
|
578
|
+
| License | `AGPL-3.0-only` |
|
|
606
579
|
| Funding | https://github.com/sponsors/pboling, https://issuehunt.io/u/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/kettle-dev, https://opencollective.com/kettle-rb, https://patreon.com/galtzo, https://polar.sh/pboling, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/kettle-family, https://www.buymeacoffee.com/pboling |
|
|
607
580
|
<!-- kettle-jem:metadata:end -->
|
|
@@ -3,25 +3,37 @@
|
|
|
3
3
|
module Kettle
|
|
4
4
|
module Family
|
|
5
5
|
class ChangelogCheck
|
|
6
|
-
def self.call(member:)
|
|
7
|
-
new(member: member).call
|
|
6
|
+
def self.call(member:, config: nil)
|
|
7
|
+
new(member: member, config: config).call
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def initialize(member:)
|
|
10
|
+
def initialize(member:, config: nil)
|
|
11
11
|
@member = member
|
|
12
|
+
@config = config
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def call
|
|
15
16
|
diagnostics = []
|
|
16
|
-
changelog =
|
|
17
|
-
diagnostics << "missing
|
|
18
|
-
diagnostics << "
|
|
17
|
+
changelog = changelog_path
|
|
18
|
+
diagnostics << "missing #{relative_changelog_path}" unless File.file?(changelog)
|
|
19
|
+
diagnostics << "#{relative_changelog_path} missing Unreleased section" if File.file?(changelog) && !File.read(changelog).include?("## [Unreleased]")
|
|
19
20
|
result(diagnostics)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
24
|
-
attr_reader :member
|
|
25
|
+
attr_reader :member, :config
|
|
26
|
+
|
|
27
|
+
def changelog_path
|
|
28
|
+
config ? config.changelog_full_path(member) : File.join(member.root, "CHANGELOG.md")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def relative_changelog_path
|
|
32
|
+
return "CHANGELOG.md" unless config
|
|
33
|
+
|
|
34
|
+
base = config.shared_changelog? ? config.root : member.root
|
|
35
|
+
changelog_path.delete_prefix("#{base}/")
|
|
36
|
+
end
|
|
25
37
|
|
|
26
38
|
def result(diagnostics)
|
|
27
39
|
CommandResult.new(
|
data/lib/kettle/family/cli.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "optparse"
|
|
|
6
6
|
module Kettle
|
|
7
7
|
module Family
|
|
8
8
|
class CLI
|
|
9
|
-
COMMANDS = %w[discover plan report check test lint docs template bump-version release branch-lanes release-state].freeze
|
|
9
|
+
COMMANDS = %w[discover plan report metadata check test lint docs template bump-version release branch-lanes release-state].freeze
|
|
10
10
|
WORKFLOW_COMMANDS = %w[check test lint docs template release].freeze
|
|
11
11
|
|
|
12
12
|
def self.call(argv, out: $stdout, err: $stderr)
|
|
@@ -53,6 +53,7 @@ module Kettle
|
|
|
53
53
|
discover Discover family members and print selected order
|
|
54
54
|
plan Alias for discover while execution workflows are built
|
|
55
55
|
report Print family discovery and configuration report
|
|
56
|
+
metadata Print version, Ruby floor, license, and author metadata
|
|
56
57
|
check Run internal read-only readiness checks
|
|
57
58
|
test Plan or execute configured test command per member
|
|
58
59
|
lint Plan or execute configured lint command per member
|
|
@@ -139,7 +140,7 @@ module Kettle
|
|
|
139
140
|
def build_report(command, options)
|
|
140
141
|
config = Config.load(root: options[:root], path: options[:config])
|
|
141
142
|
members = Discovery.new(config: config).members
|
|
142
|
-
ordered = if
|
|
143
|
+
ordered = if %w[metadata release-state].include?(command)
|
|
143
144
|
members.sort_by(&:name)
|
|
144
145
|
else
|
|
145
146
|
Orderer.new(members: members, mode: config.order_mode, hints: config.order_hints).ordered
|
|
@@ -168,7 +169,7 @@ module Kettle
|
|
|
168
169
|
def command_results(command:, config:, members:, options:)
|
|
169
170
|
return bump_version_results(members: members, options: options) if command == "bump-version"
|
|
170
171
|
return branch_lane_results(config: config, members: members) if command == "branch-lanes"
|
|
171
|
-
return release_state_results(members: members) if command == "release-state"
|
|
172
|
+
return release_state_results(config: config, members: members) if command == "release-state"
|
|
172
173
|
return [] unless WORKFLOW_COMMANDS.include?(command)
|
|
173
174
|
|
|
174
175
|
Workflow.new(
|
|
@@ -207,8 +208,8 @@ module Kettle
|
|
|
207
208
|
BranchLaneAudit.new(config: config, members: members).results
|
|
208
209
|
end
|
|
209
210
|
|
|
210
|
-
def release_state_results(members:)
|
|
211
|
-
ReleaseStateCheck.new(members: members).results
|
|
211
|
+
def release_state_results(config:, members:)
|
|
212
|
+
ReleaseStateCheck.new(config: config, members: members).results
|
|
212
213
|
end
|
|
213
214
|
|
|
214
215
|
def write_report(report, options)
|
data/lib/kettle/family/config.rb
CHANGED
|
@@ -6,7 +6,16 @@ module Kettle
|
|
|
6
6
|
module Family
|
|
7
7
|
class Config
|
|
8
8
|
DEFAULT_PATHS = [".kettle-family.yml", ".structuredmerge/kettle-family.yml"].freeze
|
|
9
|
-
DEFAULT_MEMBER_EXCLUDES = [
|
|
9
|
+
DEFAULT_MEMBER_EXCLUDES = [
|
|
10
|
+
"vendor/**",
|
|
11
|
+
"**/vendor/**",
|
|
12
|
+
"tmp/**",
|
|
13
|
+
"**/tmp/**",
|
|
14
|
+
"spec/**",
|
|
15
|
+
"**/spec/**",
|
|
16
|
+
"test/**",
|
|
17
|
+
"**/test/**"
|
|
18
|
+
].freeze
|
|
10
19
|
|
|
11
20
|
attr_reader :data, :path, :root
|
|
12
21
|
|
|
@@ -81,6 +90,64 @@ module Kettle
|
|
|
81
90
|
fetch_path("commands", name)
|
|
82
91
|
end
|
|
83
92
|
|
|
93
|
+
def check_required_files
|
|
94
|
+
fetch_path("check", "required_files") || ReadinessCheck::REQUIRED_FILES
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def check_required_bins
|
|
98
|
+
fetch_path("check", "required_bins") || ReadinessCheck::REQUIRED_BINS
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def check_root_required_files
|
|
102
|
+
fetch_path("check", "root_required_files") || []
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def check_member_required_dirs
|
|
106
|
+
fetch_path("check", "member_required_dirs") || []
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def check_forbidden_tracked_member_dirs
|
|
110
|
+
fetch_path("check", "forbidden_tracked_member_dirs") || []
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def check_forbidden_tracked_member_dirs_except
|
|
114
|
+
fetch_path("check", "forbidden_tracked_member_dirs_except") || []
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def check_readme_links
|
|
118
|
+
fetch_path("check", "readme_links") || {}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def changelog_mode
|
|
122
|
+
fetch_path("changelog", "mode") || "member"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def shared_changelog?
|
|
126
|
+
changelog_mode == "root"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def changelog_path
|
|
130
|
+
fetch_path("changelog", "path") || "CHANGELOG.md"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def changelog_version_file
|
|
134
|
+
fetch_path("changelog", "version_file")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def changelog_workdir(_member = nil)
|
|
138
|
+
shared_changelog? ? root : nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def changelog_full_path(member)
|
|
142
|
+
File.expand_path(changelog_path, shared_changelog? ? root : member.root)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def changelog_env
|
|
146
|
+
return {} unless changelog_version_file
|
|
147
|
+
|
|
148
|
+
{"K_CHANGELOG_VERSION_FILE" => changelog_version_file.to_s}
|
|
149
|
+
end
|
|
150
|
+
|
|
84
151
|
def template_command
|
|
85
152
|
fetch_path("template", "command") || command_for("template")
|
|
86
153
|
end
|
|
@@ -109,6 +176,18 @@ module Kettle
|
|
|
109
176
|
fetch_path("release", "publish_command") || command_for("release_publish") || "bundle exec kettle-release"
|
|
110
177
|
end
|
|
111
178
|
|
|
179
|
+
def release_env
|
|
180
|
+
stringify_env(fetch_path("release", "env") || {})
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def release_family_changelog?
|
|
184
|
+
fetch_path("release", "family_changelog", "enabled") == true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def release_family_changelog_command
|
|
188
|
+
fetch_path("release", "family_changelog", "command") || "bundle exec kettle-changelog"
|
|
189
|
+
end
|
|
190
|
+
|
|
112
191
|
def release_tag_command
|
|
113
192
|
fetch_path("release", "tag_command") || command_for("release_tag") || "git tag"
|
|
114
193
|
end
|
|
@@ -151,6 +230,10 @@ module Kettle
|
|
|
151
230
|
value
|
|
152
231
|
end
|
|
153
232
|
end
|
|
233
|
+
|
|
234
|
+
def stringify_env(value)
|
|
235
|
+
stringify_keys(value).to_h { |key, item| [key.to_s, item.to_s] }
|
|
236
|
+
end
|
|
154
237
|
end
|
|
155
238
|
end
|
|
156
239
|
end
|
|
@@ -63,17 +63,39 @@ module Kettle
|
|
|
63
63
|
gemspec_path: path,
|
|
64
64
|
version_file: version_file(File.dirname(path)),
|
|
65
65
|
version: spec.version.to_s,
|
|
66
|
-
dependencies: spec.dependencies.map(&:name).sort
|
|
66
|
+
dependencies: spec.dependencies.map(&:name).sort,
|
|
67
|
+
required_ruby_version: required_ruby_version(spec),
|
|
68
|
+
licenses: licenses(spec),
|
|
69
|
+
authors: authors(spec)
|
|
67
70
|
)
|
|
68
71
|
end
|
|
69
72
|
|
|
73
|
+
def required_ruby_version(spec)
|
|
74
|
+
value = spec.required_ruby_version&.to_s&.strip
|
|
75
|
+
value.empty? ? nil : value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def licenses(spec)
|
|
79
|
+
values = Array(spec.licenses).compact.map(&:to_s).map(&:strip).reject(&:empty?)
|
|
80
|
+
values = [spec.license.to_s.strip] if values.empty? && spec.respond_to?(:license) && !spec.license.to_s.strip.empty?
|
|
81
|
+
values
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def authors(spec)
|
|
85
|
+
Array(spec.authors).compact.map(&:to_s).map(&:strip).reject(&:empty?)
|
|
86
|
+
end
|
|
87
|
+
|
|
70
88
|
def version_file(root)
|
|
71
89
|
candidates = Dir.glob(File.join(root, "lib", "**", "version.rb"))
|
|
72
90
|
candidates.min
|
|
73
91
|
end
|
|
74
92
|
|
|
75
93
|
def load_gemspec(path)
|
|
76
|
-
|
|
94
|
+
# Some legacy gemspecs use root-relative Kernel.load calls, and RubyGems
|
|
95
|
+
# evaluates gemspecs relative to the current process directory.
|
|
96
|
+
# rubocop:disable ThreadSafety/DirChdir
|
|
97
|
+
spec = Dir.chdir(File.dirname(path)) { Gem::Specification.load(path) }
|
|
98
|
+
# rubocop:enable ThreadSafety/DirChdir
|
|
77
99
|
raise Error, "could not load gemspec #{path}" unless spec
|
|
78
100
|
|
|
79
101
|
spec
|
data/lib/kettle/family/member.rb
CHANGED
|
@@ -8,7 +8,10 @@ module Kettle
|
|
|
8
8
|
:gemspec_path,
|
|
9
9
|
:version_file,
|
|
10
10
|
:version,
|
|
11
|
-
:dependencies
|
|
11
|
+
:dependencies,
|
|
12
|
+
:required_ruby_version,
|
|
13
|
+
:licenses,
|
|
14
|
+
:authors
|
|
12
15
|
) do
|
|
13
16
|
def to_h
|
|
14
17
|
{
|
|
@@ -17,7 +20,10 @@ module Kettle
|
|
|
17
20
|
"gemspec_path" => gemspec_path,
|
|
18
21
|
"version_file" => version_file,
|
|
19
22
|
"version" => version,
|
|
20
|
-
"dependencies" => dependencies
|
|
23
|
+
"dependencies" => dependencies,
|
|
24
|
+
"required_ruby_version" => required_ruby_version,
|
|
25
|
+
"licenses" => Array(licenses),
|
|
26
|
+
"authors" => Array(authors)
|
|
21
27
|
}
|
|
22
28
|
end
|
|
23
29
|
end
|
|
@@ -6,28 +6,33 @@ module Kettle
|
|
|
6
6
|
REQUIRED_FILES = %w[Gemfile Rakefile README.md CHANGELOG.md LICENSE.md].freeze
|
|
7
7
|
REQUIRED_BINS = %w[bin/rake bin/rspec].freeze
|
|
8
8
|
|
|
9
|
-
def self.call(member:)
|
|
10
|
-
new(member: member).call
|
|
9
|
+
def self.call(member:, config: nil)
|
|
10
|
+
new(member: member, config: config).call
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def initialize(member:)
|
|
13
|
+
def initialize(member:, config: nil)
|
|
14
14
|
@member = member
|
|
15
|
+
@config = config
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def call
|
|
18
19
|
diagnostics = []
|
|
19
20
|
diagnostics.concat(missing_required_files)
|
|
20
21
|
diagnostics.concat(missing_required_bins)
|
|
22
|
+
diagnostics.concat(missing_root_required_files)
|
|
23
|
+
diagnostics.concat(missing_member_required_dirs)
|
|
24
|
+
diagnostics.concat(forbidden_tracked_member_dirs)
|
|
25
|
+
diagnostics.concat(missing_readme_links)
|
|
21
26
|
diagnostics.concat(local_path_lockfile_entries)
|
|
22
27
|
result(diagnostics)
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
private
|
|
26
31
|
|
|
27
|
-
attr_reader :member
|
|
32
|
+
attr_reader :member, :config
|
|
28
33
|
|
|
29
34
|
def missing_required_files
|
|
30
|
-
|
|
35
|
+
required_files.filter_map do |path|
|
|
31
36
|
next if File.file?(File.join(member.root, path))
|
|
32
37
|
|
|
33
38
|
"missing required file #{path}"
|
|
@@ -35,7 +40,7 @@ module Kettle
|
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
def missing_required_bins
|
|
38
|
-
|
|
43
|
+
required_bins.filter_map do |path|
|
|
39
44
|
full_path = File.join(member.root, path)
|
|
40
45
|
next if File.file?(full_path) && File.executable?(full_path)
|
|
41
46
|
|
|
@@ -43,6 +48,52 @@ module Kettle
|
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
def missing_root_required_files
|
|
52
|
+
return [] unless config
|
|
53
|
+
|
|
54
|
+
config.check_root_required_files.filter_map do |path|
|
|
55
|
+
next if File.file?(File.join(config.root, path))
|
|
56
|
+
|
|
57
|
+
"missing root required file #{path}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def missing_member_required_dirs
|
|
62
|
+
return [] unless config
|
|
63
|
+
|
|
64
|
+
config.check_member_required_dirs.filter_map do |path|
|
|
65
|
+
next if Dir.exist?(File.join(member.root, path))
|
|
66
|
+
|
|
67
|
+
"missing required directory #{path}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def forbidden_tracked_member_dirs
|
|
72
|
+
return [] unless config
|
|
73
|
+
return [] if config.check_forbidden_tracked_member_dirs_except.include?(member.name)
|
|
74
|
+
|
|
75
|
+
config.check_forbidden_tracked_member_dirs.filter_map do |path|
|
|
76
|
+
full_path = File.join(member.root, path)
|
|
77
|
+
next unless Dir.exist?(full_path) && tracked_path?(full_path)
|
|
78
|
+
|
|
79
|
+
"forbidden tracked directory #{path}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def missing_readme_links
|
|
84
|
+
return [] unless config
|
|
85
|
+
|
|
86
|
+
readme = File.join(member.root, "README.md")
|
|
87
|
+
return [] unless File.file?(readme)
|
|
88
|
+
|
|
89
|
+
content = File.read(readme)
|
|
90
|
+
config.check_readme_links.filter_map do |label, target|
|
|
91
|
+
next if content.include?("/#{target}") || content.include?("../../#{target}") || content.include?("../#{target}")
|
|
92
|
+
|
|
93
|
+
"README.md missing link to root #{label}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
46
97
|
def local_path_lockfile_entries
|
|
47
98
|
lockfile = File.join(member.root, "Gemfile.lock")
|
|
48
99
|
return [] unless File.file?(lockfile)
|
|
@@ -54,6 +105,21 @@ module Kettle
|
|
|
54
105
|
end
|
|
55
106
|
end
|
|
56
107
|
|
|
108
|
+
def required_files
|
|
109
|
+
config ? config.check_required_files : REQUIRED_FILES
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def required_bins
|
|
113
|
+
config ? config.check_required_bins : REQUIRED_BINS
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def tracked_path?(path)
|
|
117
|
+
return false unless config
|
|
118
|
+
|
|
119
|
+
relative = path.delete_prefix("#{config.root}/")
|
|
120
|
+
system("git", "-C", config.root, "ls-files", "--error-unmatch", relative, out: File::NULL, err: File::NULL)
|
|
121
|
+
end
|
|
122
|
+
|
|
57
123
|
def result(diagnostics)
|
|
58
124
|
CommandResult.new(
|
|
59
125
|
member_name: member.name,
|
|
@@ -1,40 +1,205 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require "fileutils"
|
|
4
5
|
require "open3"
|
|
6
|
+
require "rbconfig"
|
|
7
|
+
require "securerandom"
|
|
5
8
|
|
|
6
9
|
module Kettle
|
|
7
10
|
module Family
|
|
8
11
|
class ReleaseStateCheck
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def initialize(members:)
|
|
12
|
+
def initialize(members:, config: nil)
|
|
12
13
|
@members = members
|
|
14
|
+
@config = config
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def results
|
|
18
|
+
return branch_results unless release_target_branches.empty?
|
|
19
|
+
return [check_family_changelog] if shared_changelog?
|
|
20
|
+
|
|
16
21
|
members.map { |member| check_member(member) }
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
private
|
|
20
25
|
|
|
21
|
-
attr_reader :members
|
|
26
|
+
attr_reader :members, :config
|
|
27
|
+
|
|
28
|
+
def branch_results
|
|
29
|
+
root = git_root
|
|
30
|
+
selected_names = members.map(&:name)
|
|
31
|
+
release_target_branches.each_with_object([]) do |branch, memo|
|
|
32
|
+
with_branch_worktree(root: root, branch: branch) do |worktree_root|
|
|
33
|
+
if shared_changelog?
|
|
34
|
+
memo << check_family_changelog(branch: branch, worktree_root: worktree_root)
|
|
35
|
+
next
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
branch_members = discover_branch_members(worktree_root: worktree_root, selected_names: selected_names)
|
|
39
|
+
memo.concat(branch_members.map { |member| check_member(member, branch: branch) })
|
|
40
|
+
end
|
|
41
|
+
rescue Error => error
|
|
42
|
+
memo << error_result(branch: branch, error: error)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
22
45
|
|
|
23
|
-
def check_member(member)
|
|
46
|
+
def check_member(member, branch: nil)
|
|
24
47
|
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
25
|
-
|
|
48
|
+
command = release_state_command
|
|
49
|
+
stdout, stderr, status = Open3.capture3(release_state_env, *command, chdir: release_state_workdir(member))
|
|
26
50
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
|
27
51
|
success = status.success?
|
|
28
52
|
state = success ? JSON.parse(stdout) : {}
|
|
29
|
-
result(member: member, stdout: stdout, stderr: stderr, status: status.exitstatus, elapsed: elapsed, success: success, state: state)
|
|
53
|
+
result(member: member, command: command, stdout: stdout, stderr: stderr, status: status.exitstatus, elapsed: elapsed, success: success, state: state, branch: branch)
|
|
30
54
|
rescue JSON::ParserError => error
|
|
31
|
-
result(member: member, stdout: stdout, stderr: stderr, status: 1, elapsed: elapsed || 0.0, success: false, state: {}, reason: "invalid release-state JSON: #{error.message}")
|
|
55
|
+
result(member: member, command: command || release_state_command, stdout: stdout, stderr: stderr, status: 1, elapsed: elapsed || 0.0, success: false, state: {}, reason: "invalid release-state JSON: #{error.message}", branch: branch)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def release_state_command
|
|
59
|
+
[RbConfig.ruby, "-S", "kettle-changelog", "--release-state", "--json"]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def check_family_changelog(branch: nil, worktree_root: nil)
|
|
63
|
+
member = family_member(root: worktree_root ? branch_config_root(worktree_root) : config.root)
|
|
64
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
65
|
+
state = family_changelog_state(member.root)
|
|
66
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
|
67
|
+
result(
|
|
68
|
+
member: member,
|
|
69
|
+
command: ["internal", "release-state", "root-changelog"],
|
|
70
|
+
stdout: "",
|
|
71
|
+
stderr: "",
|
|
72
|
+
status: 0,
|
|
73
|
+
elapsed: elapsed,
|
|
74
|
+
success: true,
|
|
75
|
+
state: state,
|
|
76
|
+
branch: branch
|
|
77
|
+
)
|
|
78
|
+
rescue Error => error
|
|
79
|
+
result(
|
|
80
|
+
member: member,
|
|
81
|
+
command: ["internal", "release-state", "root-changelog"],
|
|
82
|
+
stdout: "",
|
|
83
|
+
stderr: error.message,
|
|
84
|
+
status: 1,
|
|
85
|
+
elapsed: 0.0,
|
|
86
|
+
success: false,
|
|
87
|
+
state: {},
|
|
88
|
+
reason: "release state check failed",
|
|
89
|
+
branch: branch
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def family_member(root:)
|
|
94
|
+
Member.new(
|
|
95
|
+
name: config.family_name,
|
|
96
|
+
root: root,
|
|
97
|
+
gemspec_path: nil,
|
|
98
|
+
version_file: nil,
|
|
99
|
+
version: nil,
|
|
100
|
+
dependencies: []
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def release_state_workdir(member)
|
|
105
|
+
return member.root unless config
|
|
106
|
+
return member.root if config.shared_changelog?
|
|
107
|
+
|
|
108
|
+
config.changelog_workdir(member) || member.root
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def release_state_env
|
|
112
|
+
config ? config.changelog_env : {}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def family_changelog_state(root)
|
|
116
|
+
changelog = File.expand_path(config.changelog_path, root)
|
|
117
|
+
raise Error, "missing root changelog #{config.changelog_path}" unless File.file?(changelog)
|
|
118
|
+
|
|
119
|
+
version = root_changelog_version(root)
|
|
120
|
+
content = File.read(changelog)
|
|
121
|
+
latest_changelog_version = latest_changelog_version(content)
|
|
122
|
+
unreleased_entries = unreleased_entries?(content)
|
|
123
|
+
prepared_release_pending = !version.to_s.empty? && latest_changelog_version == version
|
|
124
|
+
{
|
|
125
|
+
"gem_name" => config.family_name,
|
|
126
|
+
"version" => version,
|
|
127
|
+
"latest_released" => nil,
|
|
128
|
+
"latest_changelog_version" => latest_changelog_version,
|
|
129
|
+
"unreleased_entries" => unreleased_entries,
|
|
130
|
+
"prepared_release_pending" => prepared_release_pending,
|
|
131
|
+
"pending_release" => unreleased_entries || prepared_release_pending
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def root_changelog_version(root)
|
|
136
|
+
version_file = config.changelog_version_file
|
|
137
|
+
return nil unless version_file
|
|
138
|
+
|
|
139
|
+
path = File.expand_path(version_file, root)
|
|
140
|
+
raise Error, "missing changelog version file #{version_file}" unless File.file?(path)
|
|
141
|
+
|
|
142
|
+
version_string_node(File.read(path), path).unescaped
|
|
32
143
|
end
|
|
33
144
|
|
|
34
|
-
def
|
|
145
|
+
def version_string_node(source, path)
|
|
146
|
+
require_prism
|
|
147
|
+
parse_result = Prism.parse(source)
|
|
148
|
+
raise Error, "could not parse #{path}" unless parse_result.success?
|
|
149
|
+
|
|
150
|
+
constant = each_node(parse_result.value).find do |node|
|
|
151
|
+
node.is_a?(Prism::ConstantWriteNode) && node.name == :VERSION && node.value.is_a?(Prism::StringNode)
|
|
152
|
+
end
|
|
153
|
+
raise Error, "could not find string VERSION constant in #{path}" unless constant
|
|
154
|
+
|
|
155
|
+
constant.value
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def latest_changelog_version(content)
|
|
159
|
+
content.each_line.filter_map do |line|
|
|
160
|
+
match = line.match(/\A## \[([^\]]+)\]/)
|
|
161
|
+
next unless match
|
|
162
|
+
|
|
163
|
+
version = match[1]
|
|
164
|
+
next if version == "Unreleased"
|
|
165
|
+
|
|
166
|
+
version
|
|
167
|
+
end.first
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def unreleased_entries?(content)
|
|
171
|
+
lines = content.lines
|
|
172
|
+
start = lines.index { |line| line.start_with?("## [Unreleased]") }
|
|
173
|
+
return false unless start
|
|
174
|
+
|
|
175
|
+
following = lines[(start + 1)..] || []
|
|
176
|
+
block = following.take_while { |line| !line.start_with?("## [") }
|
|
177
|
+
block.any? { |line| line.match?(/\S/) && !line.match?(/\A###? /) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def require_prism
|
|
181
|
+
return if defined?(Prism)
|
|
182
|
+
|
|
183
|
+
require "prism"
|
|
184
|
+
rescue LoadError => error
|
|
185
|
+
raise Error, "root changelog release-state requires Prism; install the prism gem or run on a Ruby engine that provides it (#{error.message})"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def each_node(root)
|
|
189
|
+
return enum_for(__method__, root) unless block_given?
|
|
190
|
+
|
|
191
|
+
queue = [root]
|
|
192
|
+
until queue.empty?
|
|
193
|
+
node = queue.shift
|
|
194
|
+
yield node
|
|
195
|
+
queue.concat(node.child_nodes.compact) if node.respond_to?(:child_nodes)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def result(member:, command:, stdout:, stderr:, status:, elapsed:, success:, state:, reason: nil, branch: nil)
|
|
35
200
|
ReleaseStateResult.new(
|
|
36
201
|
member_name: member.name,
|
|
37
|
-
command:
|
|
202
|
+
command: command,
|
|
38
203
|
workdir: member.root,
|
|
39
204
|
status: status,
|
|
40
205
|
success: success,
|
|
@@ -42,9 +207,89 @@ module Kettle
|
|
|
42
207
|
stderr: stderr,
|
|
43
208
|
elapsed_seconds: elapsed,
|
|
44
209
|
state: state,
|
|
45
|
-
reason: reason || (success ? nil : "release state check failed")
|
|
210
|
+
reason: reason || (success ? nil : "release state check failed"),
|
|
211
|
+
branch: branch
|
|
212
|
+
)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def error_result(branch:, error:)
|
|
216
|
+
ReleaseStateResult.new(
|
|
217
|
+
member_name: branch,
|
|
218
|
+
command: ["internal", "release-state", branch],
|
|
219
|
+
workdir: config.root,
|
|
220
|
+
status: 1,
|
|
221
|
+
success: false,
|
|
222
|
+
stdout: "",
|
|
223
|
+
stderr: error.message,
|
|
224
|
+
elapsed_seconds: 0.0,
|
|
225
|
+
state: {},
|
|
226
|
+
reason: "branch release state failed",
|
|
227
|
+
branch: branch
|
|
46
228
|
)
|
|
47
229
|
end
|
|
230
|
+
|
|
231
|
+
def release_target_branches
|
|
232
|
+
return [] unless config
|
|
233
|
+
|
|
234
|
+
config.release_target_branches
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def shared_changelog?
|
|
238
|
+
config&.shared_changelog?
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def git_root
|
|
242
|
+
stdout, stderr, status = Open3.capture3("git", "rev-parse", "--show-toplevel", chdir: config.root)
|
|
243
|
+
raise Error, "could not determine git root for #{config.root}: #{stderr}" unless status.success?
|
|
244
|
+
|
|
245
|
+
File.realpath(stdout.strip)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def with_branch_worktree(root:, branch:)
|
|
249
|
+
base = File.join(root, "tmp", "kettle-family-release-state")
|
|
250
|
+
FileUtils.mkdir_p(base)
|
|
251
|
+
worktree_root = File.join(base, "worktree-#{Process.pid}-#{SecureRandom.hex(8)}")
|
|
252
|
+
add_branch_worktree(root: root, branch: branch, worktree_root: worktree_root)
|
|
253
|
+
yield worktree_root
|
|
254
|
+
ensure
|
|
255
|
+
remove_branch_worktree(root: root, worktree_root: worktree_root)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def add_branch_worktree(root:, branch:, worktree_root:)
|
|
259
|
+
_stdout, stderr, status = Open3.capture3("git", "worktree", "add", "--detach", worktree_root, branch, chdir: root)
|
|
260
|
+
raise Error, "could not add worktree for #{branch}: #{stderr}" unless status.success?
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def remove_branch_worktree(root:, worktree_root:)
|
|
264
|
+
return unless worktree_root && Dir.exist?(worktree_root)
|
|
265
|
+
|
|
266
|
+
Open3.capture3("git", "worktree", "remove", "--force", worktree_root, chdir: root)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def discover_branch_members(worktree_root:, selected_names:)
|
|
270
|
+
branch_config = Config.load(root: branch_config_root(worktree_root))
|
|
271
|
+
Discovery.new(config: branch_config).members
|
|
272
|
+
.sort_by(&:name)
|
|
273
|
+
.select { |member| selected_names.include?(member.name) }
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def branch_config_root(worktree_root)
|
|
277
|
+
File.join(worktree_root, relative_config_root)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def relative_config_root
|
|
281
|
+
@relative_config_root ||= begin
|
|
282
|
+
root = git_root
|
|
283
|
+
config_root = File.realpath(config.root)
|
|
284
|
+
if config_root == root
|
|
285
|
+
"."
|
|
286
|
+
elsif config_root.start_with?("#{root}/")
|
|
287
|
+
config_root.delete_prefix("#{root}/")
|
|
288
|
+
else
|
|
289
|
+
raise Error, "configured root #{config.root} is outside git root #{root}"
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
48
293
|
end
|
|
49
294
|
end
|
|
50
295
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Kettle
|
|
4
4
|
module Family
|
|
5
5
|
class ReleaseStateResult
|
|
6
|
-
attr_reader :member_name, :phase, :command, :workdir, :status, :success, :stdout, :stderr, :elapsed_seconds, :skipped, :reason, :state
|
|
6
|
+
attr_reader :member_name, :phase, :command, :workdir, :status, :success, :stdout, :stderr, :elapsed_seconds, :skipped, :reason, :state, :branch
|
|
7
7
|
|
|
8
|
-
def initialize(member_name:, command:, workdir:, status:, success:, stdout:, stderr:, elapsed_seconds:, state:, reason: nil)
|
|
8
|
+
def initialize(member_name:, command:, workdir:, status:, success:, stdout:, stderr:, elapsed_seconds:, state:, reason: nil, branch: nil)
|
|
9
9
|
@member_name = member_name
|
|
10
10
|
@phase = "release_state"
|
|
11
11
|
@command = command
|
|
@@ -18,6 +18,7 @@ module Kettle
|
|
|
18
18
|
@skipped = false
|
|
19
19
|
@reason = reason
|
|
20
20
|
@state = state
|
|
21
|
+
@branch = branch
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def ok?
|
|
@@ -37,6 +38,7 @@ module Kettle
|
|
|
37
38
|
"elapsed_seconds" => elapsed_seconds,
|
|
38
39
|
"skipped" => skipped,
|
|
39
40
|
"reason" => reason,
|
|
41
|
+
"branch" => branch,
|
|
40
42
|
"release_state" => state
|
|
41
43
|
}
|
|
42
44
|
end
|
data/lib/kettle/family/report.rb
CHANGED
|
@@ -64,6 +64,7 @@ module Kettle
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
66
|
def append_results(lines)
|
|
67
|
+
return append_metadata_results(lines) if command == "metadata"
|
|
67
68
|
return if results.empty?
|
|
68
69
|
return append_release_state_results(lines) if command == "release-state"
|
|
69
70
|
|
|
@@ -82,9 +83,24 @@ module Kettle
|
|
|
82
83
|
"failed"
|
|
83
84
|
end
|
|
84
85
|
|
|
86
|
+
def append_metadata_results(lines)
|
|
87
|
+
lines << "metadata:"
|
|
88
|
+
rows = [["gem", "version", "ruby", "licenses", "authors"]]
|
|
89
|
+
selected_members.each do |member|
|
|
90
|
+
rows << [
|
|
91
|
+
member.name.to_s,
|
|
92
|
+
member.version.to_s,
|
|
93
|
+
blank_as_none(member.required_ruby_version),
|
|
94
|
+
blank_as_none(Array(member.licenses).join(", ")),
|
|
95
|
+
blank_as_none(Array(member.authors).join(", "))
|
|
96
|
+
]
|
|
97
|
+
end
|
|
98
|
+
lines.concat(format_table(rows).map { |line| " #{line}" })
|
|
99
|
+
end
|
|
100
|
+
|
|
85
101
|
def append_release_state_results(lines)
|
|
86
102
|
lines << "release state:"
|
|
87
|
-
rows =
|
|
103
|
+
rows = release_state_header
|
|
88
104
|
results.each do |result|
|
|
89
105
|
rows << release_state_row(result)
|
|
90
106
|
end
|
|
@@ -101,7 +117,7 @@ module Kettle
|
|
|
101
117
|
|
|
102
118
|
def release_state_row(result)
|
|
103
119
|
state = result.state || {}
|
|
104
|
-
[
|
|
120
|
+
row = [
|
|
105
121
|
state.fetch("gem_name", result.member_name).to_s,
|
|
106
122
|
state.fetch("version", "unknown").to_s,
|
|
107
123
|
state.fetch("latest_released", nil).to_s.empty? ? "unknown" : state.fetch("latest_released").to_s,
|
|
@@ -110,6 +126,20 @@ module Kettle
|
|
|
110
126
|
yes_no(state.fetch("prepared_release_pending", nil)),
|
|
111
127
|
yes_no(state.fetch("pending_release", nil))
|
|
112
128
|
]
|
|
129
|
+
return row unless release_state_has_branches?
|
|
130
|
+
|
|
131
|
+
[result.branch.to_s.empty? ? "current" : result.branch.to_s, *row]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def release_state_header
|
|
135
|
+
header = [["gem", "version.rb", "latest released", "latest changelog", "unreleased", "prepared", "pending"]]
|
|
136
|
+
return header unless release_state_has_branches?
|
|
137
|
+
|
|
138
|
+
[["branch", *header.first]]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def release_state_has_branches?
|
|
142
|
+
results.any? { |result| !result.branch.to_s.empty? }
|
|
113
143
|
end
|
|
114
144
|
|
|
115
145
|
def format_table(rows)
|
|
@@ -131,6 +161,11 @@ module Kettle
|
|
|
131
161
|
end
|
|
132
162
|
end
|
|
133
163
|
|
|
164
|
+
def blank_as_none(value)
|
|
165
|
+
text = value.to_s.strip
|
|
166
|
+
text.empty? ? "(none)" : text
|
|
167
|
+
end
|
|
168
|
+
|
|
134
169
|
def resume_hint
|
|
135
170
|
failed = results.find { |result| !result.ok? }
|
|
136
171
|
resume_hint_for(failed) if failed
|
|
@@ -54,14 +54,14 @@ module Kettle
|
|
|
54
54
|
attr_reader :command, :config, :members, :execute, :commit, :allow_dirty, :publish, :push, :tag, :start_step, :local_ci, :continue_ci_failures
|
|
55
55
|
|
|
56
56
|
def check_results
|
|
57
|
-
members.map { |member| ReadinessCheck.call(member: member) }
|
|
57
|
+
members.map { |member| ReadinessCheck.call(member: member, config: config) }
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def release_results
|
|
61
61
|
prompt_for_gem_signing_password if execute && publish && gem_signing_required?
|
|
62
62
|
return branch_target_release_results unless config.release_target_branches.empty?
|
|
63
63
|
|
|
64
|
-
release_member_results(members)
|
|
64
|
+
release_member_results(members, include_family_changelog: true)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def branch_target_release_results
|
|
@@ -72,14 +72,18 @@ module Kettle
|
|
|
72
72
|
break memo unless memo.last.ok?
|
|
73
73
|
|
|
74
74
|
branch_members = rediscovered_selected_members(selected_names)
|
|
75
|
-
memo.concat(release_member_results(branch_members))
|
|
75
|
+
memo.concat(release_member_results(branch_members, include_family_changelog: true))
|
|
76
76
|
break memo unless memo.last&.ok?
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
def release_member_results(release_members)
|
|
80
|
+
def release_member_results(release_members, include_family_changelog: false)
|
|
81
81
|
runner = command_runner
|
|
82
|
-
|
|
82
|
+
results = []
|
|
83
|
+
append_family_changelog_result(runner: runner, memo: results) if include_family_changelog
|
|
84
|
+
return results unless results.all?(&:ok?)
|
|
85
|
+
|
|
86
|
+
release_members.each_with_object(results) do |member, memo|
|
|
83
87
|
if skip_already_released?(member)
|
|
84
88
|
memo << already_released_result(member)
|
|
85
89
|
next
|
|
@@ -121,8 +125,19 @@ module Kettle
|
|
|
121
125
|
end
|
|
122
126
|
|
|
123
127
|
def append_release_internal_checks(member:, memo:)
|
|
124
|
-
memo << ReadinessCheck.call(member: member)
|
|
125
|
-
memo << ChangelogCheck.call(member: member) if memo.last.ok?
|
|
128
|
+
memo << ReadinessCheck.call(member: member, config: config)
|
|
129
|
+
memo << ChangelogCheck.call(member: member, config: config) if memo.last.ok?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def append_family_changelog_result(runner:, memo:)
|
|
133
|
+
return unless config.release_family_changelog?
|
|
134
|
+
|
|
135
|
+
memo << runner.call(
|
|
136
|
+
member: family_member,
|
|
137
|
+
phase: "family_changelog",
|
|
138
|
+
command: config.release_family_changelog_command,
|
|
139
|
+
env: release_env.merge(config.changelog_env)
|
|
140
|
+
)
|
|
126
141
|
end
|
|
127
142
|
|
|
128
143
|
def release_phase
|
|
@@ -155,7 +170,9 @@ module Kettle
|
|
|
155
170
|
end
|
|
156
171
|
|
|
157
172
|
def release_env
|
|
158
|
-
|
|
173
|
+
env = config.release_env.dup
|
|
174
|
+
env["K_RELEASE_CI_CONTINUE"] = "true" if continue_ci_failures
|
|
175
|
+
env
|
|
159
176
|
end
|
|
160
177
|
|
|
161
178
|
def append_release_git_phases(member:, runner:, memo:)
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kettle-family
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -60,7 +60,7 @@ dependencies:
|
|
|
60
60
|
version: '1.1'
|
|
61
61
|
- - ">="
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 1.1.
|
|
63
|
+
version: 1.1.12
|
|
64
64
|
type: :runtime
|
|
65
65
|
prerelease: false
|
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -70,7 +70,7 @@ dependencies:
|
|
|
70
70
|
version: '1.1'
|
|
71
71
|
- - ">="
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: 1.1.
|
|
73
|
+
version: 1.1.12
|
|
74
74
|
- !ruby/object:Gem::Dependency
|
|
75
75
|
name: kettle-dev
|
|
76
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,7 +80,7 @@ dependencies:
|
|
|
80
80
|
version: '2.2'
|
|
81
81
|
- - ">="
|
|
82
82
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: 2.2.
|
|
83
|
+
version: 2.2.9
|
|
84
84
|
type: :development
|
|
85
85
|
prerelease: false
|
|
86
86
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -90,7 +90,7 @@ dependencies:
|
|
|
90
90
|
version: '2.2'
|
|
91
91
|
- - ">="
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
|
-
version: 2.2.
|
|
93
|
+
version: 2.2.9
|
|
94
94
|
- !ruby/object:Gem::Dependency
|
|
95
95
|
name: bundler-audit
|
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -188,7 +188,7 @@ dependencies:
|
|
|
188
188
|
version: '3.1'
|
|
189
189
|
- - ">="
|
|
190
190
|
- !ruby/object:Gem::Version
|
|
191
|
-
version: 3.1.
|
|
191
|
+
version: 3.1.3
|
|
192
192
|
type: :development
|
|
193
193
|
prerelease: false
|
|
194
194
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -198,7 +198,7 @@ dependencies:
|
|
|
198
198
|
version: '3.1'
|
|
199
199
|
- - ">="
|
|
200
200
|
- !ruby/object:Gem::Version
|
|
201
|
-
version: 3.1.
|
|
201
|
+
version: 3.1.3
|
|
202
202
|
- !ruby/object:Gem::Dependency
|
|
203
203
|
name: ruby-progressbar
|
|
204
204
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,7 +242,7 @@ dependencies:
|
|
|
242
242
|
version: '2.0'
|
|
243
243
|
- - ">="
|
|
244
244
|
- !ruby/object:Gem::Version
|
|
245
|
-
version: 2.0.
|
|
245
|
+
version: 2.0.2
|
|
246
246
|
type: :development
|
|
247
247
|
prerelease: false
|
|
248
248
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -252,7 +252,7 @@ dependencies:
|
|
|
252
252
|
version: '2.0'
|
|
253
253
|
- - ">="
|
|
254
254
|
- !ruby/object:Gem::Version
|
|
255
|
-
version: 2.0.
|
|
255
|
+
version: 2.0.2
|
|
256
256
|
description: "\U0001F469\U0001F469\U0001F467\U0001F467 Kettle::Family provides
|
|
257
257
|
scripts and conventions for coordinating related Ruby gems as one family."
|
|
258
258
|
email:
|
|
@@ -305,13 +305,13 @@ files:
|
|
|
305
305
|
- sig/kettle/family/version.rbs
|
|
306
306
|
homepage: https://github.com/kettle-dev/kettle-family
|
|
307
307
|
licenses:
|
|
308
|
-
-
|
|
308
|
+
- AGPL-3.0-only
|
|
309
309
|
metadata:
|
|
310
310
|
homepage_uri: https://kettle-family.galtzo.com
|
|
311
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.1.
|
|
312
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.1.
|
|
311
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.1.4
|
|
312
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.1.4/CHANGELOG.md
|
|
313
313
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
314
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.1.
|
|
314
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.1.4
|
|
315
315
|
funding_uri: https://github.com/sponsors/pboling
|
|
316
316
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
317
317
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
metadata.gz.sig
CHANGED
|
Binary file
|