kettle-family 1.2.21 → 1.2.23
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 +79 -23
- data/README.md +25 -16
- data/lib/kettle/family/appraisal_bootstrap.rb +108 -0
- data/lib/kettle/family/cli.rb +18 -2
- data/lib/kettle/family/command_runner.rb +14 -0
- data/lib/kettle/family/config.rb +6 -0
- data/lib/kettle/family/discovery.rb +23 -7
- data/lib/kettle/family/release_state_check.rb +78 -18
- data/lib/kettle/family/release_waves.rb +33 -6
- data/lib/kettle/family/report.rb +13 -3
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +191 -9
- data/lib/kettle/family.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +9 -8
- 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: c7444a3d6ecfad0866aae1d481043a9daebb360a185d628ffff7250dc46d8cf4
|
|
4
|
+
data.tar.gz: dbfa71d55c722830254b219d60b98e330918fc853e5d76f026c90e1b6929d971
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a10e4a4db9fe4bc4769c45757a93767286b19de7657b09b4e6cac32877510f5a46342c9a566dd35776d3ed26ac91e4b7c688deaf646107e8d162873331ed64de
|
|
7
|
+
data.tar.gz: dbb1cab4664586a13210038ac5e48a8fb8be2b7e5ec6022cc516f40f58da2c9587c7f3d884b6acc31c7afd89fc01b4b1e9d15b8296bff2986e037b9d5474f6c7
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,80 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [1.2.23] - 2026-08-02
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.2.23][1.2.23t]
|
|
36
|
+
- COVERAGE: 93.91% -- 4658/4960 lines in 31 files
|
|
37
|
+
- BRANCH COVERAGE: 76.23% -- 1822/2390 branches in 31 files
|
|
38
|
+
- 29.31% documented
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Budget kettle-jem template workers across concurrent family members.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- Remove trailing blank lines when retiring legacy appraisal source declarations during templating.
|
|
47
|
+
|
|
48
|
+
- Allow direct --only member selections to proceed when unrelated member Gemfiles are temporarily unparsable.
|
|
49
|
+
|
|
50
|
+
- kettle-jem-template-20260802-001 - Devcontainer JSON files now merge as JSONC,
|
|
51
|
+
preserving comments and trailing commas during template updates.
|
|
52
|
+
|
|
53
|
+
- Preserve the completed generated kettle-jem lockfile when restoring an autostash conflicts only on that file.
|
|
54
|
+
|
|
55
|
+
- Mise-managed template members now retain PATH when launching nested Bundler commands.
|
|
56
|
+
|
|
57
|
+
- Local kettle-jem template commands now bypass login-shell PATH resets.
|
|
58
|
+
|
|
59
|
+
- Nested Bundler commands now retain the active Ruby executable paths during autostashed templating.
|
|
60
|
+
|
|
61
|
+
- Autostashed mise configuration remains available while templating runs.
|
|
62
|
+
|
|
63
|
+
## [1.2.22] - 2026-08-01
|
|
64
|
+
|
|
65
|
+
- TAG: [v1.2.22][1.2.22t]
|
|
66
|
+
- COVERAGE: 93.87% -- 4626/4928 lines in 31 files
|
|
67
|
+
- BRANCH COVERAGE: 76.18% -- 1807/2372 branches in 31 files
|
|
68
|
+
- 29.31% documented
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- Template runs can now use --autostash to preserve dirty worktrees while synchronizing and templating.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- Template runs now pull configured upstream branches before planning; branch-target runs synchronize each checked-out branch.
|
|
77
|
+
|
|
78
|
+
- Template autostash is now enabled by default; use --no-autostash to fail on dirty worktrees.
|
|
79
|
+
|
|
80
|
+
- Clarify that replayed template changelog entries are destination release work and affect release state.
|
|
81
|
+
|
|
82
|
+
- Release state now reports filter-aware transfer changelog lag as missing versus applicable entries.
|
|
83
|
+
|
|
84
|
+
- kettle-jem-template-20260801-001 - Generated README gem dashboard links now
|
|
85
|
+
use ClickGems instead of BestGems.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- Evaluate release-state GitHub release and commit counts against each configured target branch.
|
|
90
|
+
|
|
91
|
+
- kettle-jem-template-20260801-002 - Generated RSpec helpers now normalize
|
|
92
|
+
managed configuration block bindings structurally, preventing mixed block
|
|
93
|
+
parameter names from producing invalid configuration after a merge.
|
|
94
|
+
- kettle-jem-template-20260801-003 - Generated project metadata and
|
|
95
|
+
documentation now normalize configured underscore hostnames to valid
|
|
96
|
+
hyphenated hostnames.
|
|
97
|
+
- kettle-jem-template-20260801-004 - Generated organization README logos now
|
|
98
|
+
use GitHub's stable organization avatar endpoint instead of assuming a
|
|
99
|
+
matching Galtzo-hosted asset exists.
|
|
100
|
+
|
|
101
|
+
- Template autostash now recovers lockfile-only restore conflicts by keeping the normalized lockfile.
|
|
102
|
+
|
|
103
|
+
- Allow family configuration to define ordered single-member release waves.
|
|
104
|
+
|
|
105
|
+
- Remove retired pboling/appraisal declarations before templating legacy members.
|
|
106
|
+
|
|
33
107
|
## [1.2.21] - 2026-08-01
|
|
34
108
|
|
|
35
109
|
- TAG: [v1.2.21][1.2.21t]
|
|
@@ -396,10 +470,6 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
396
470
|
|
|
397
471
|
### Added
|
|
398
472
|
|
|
399
|
-
- kettle-jem-template-20260729-005 - Gemspec metadata now publishes this
|
|
400
|
-
project's RubyForum tag as `mailing_list_uri`, and support docs link to the
|
|
401
|
-
tagged RubyForum community alongside Discord.
|
|
402
|
-
|
|
403
473
|
### Fixed
|
|
404
474
|
|
|
405
475
|
- Family release changelog phases now pass `--yes` to configured
|
|
@@ -407,25 +477,16 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
407
477
|
behavior and keeping publish runs non-interactive unless `--no-accept` is
|
|
408
478
|
used.
|
|
409
479
|
|
|
410
|
-
- kettle-jem-template-20260728-003 - Generated dep-heads workflows now run
|
|
411
|
-
TruffleRuby jobs with current RubyGems and Bundler, avoiding setup failures
|
|
412
|
-
before the test suite starts.
|
|
413
480
|
- kettle-jem-template-20260728-004 - Generated dep-heads workflows now use the
|
|
414
481
|
setup-ruby Bundler install path for direct appraisal Gemfiles, avoiding rv
|
|
415
482
|
lockfile parser failures on Git and path dependencies.
|
|
416
483
|
- kettle-jem-template-20260728-005 - VersionGem bootstrap now creates the
|
|
417
484
|
missing canonical version spec when a project only has shim namespace version
|
|
418
485
|
specs.
|
|
419
|
-
- kettle-jem-template-20260729-001 - Generated JRuby 9.4 workflows now use the
|
|
420
|
-
legacy manual bundle install path, avoiding setup-time Bundler full-index
|
|
421
|
-
failures against `gem.coop`.
|
|
422
486
|
- kettle-jem-template-20260729-002 - VersionGem bootstrap now preserves
|
|
423
487
|
and templates dedicated `version_gem.rb` entrypoints even when the gemspec
|
|
424
488
|
dependency is intentionally omitted, and generated anonymous-loader specs
|
|
425
489
|
cover both `version.rb` and `version_gem.rb`.
|
|
426
|
-
- kettle-jem-template-20260729-003 - Old-Ruby gems below the VersionGem runtime
|
|
427
|
-
floor now get managed minimal `version.rb` files and anonymous-loader version
|
|
428
|
-
specs without adding `version_gem`.
|
|
429
490
|
|
|
430
491
|
## [1.2.3] - 2026-07-28
|
|
431
492
|
|
|
@@ -647,11 +708,6 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
647
708
|
|
|
648
709
|
### Changed
|
|
649
710
|
|
|
650
|
-
- kettle-jem-template-20260725-001 - Generated JRuby and TruffleRuby workflow
|
|
651
|
-
files now run when pull request head branches start with `feature/release`,
|
|
652
|
-
so release CI monitoring does not report intentionally skipped engine
|
|
653
|
-
workflows as failures.
|
|
654
|
-
|
|
655
711
|
## [1.1.5] - 2026-07-25
|
|
656
712
|
|
|
657
713
|
- TAG: [v1.1.5][1.1.5t]
|
|
@@ -835,8 +891,6 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
835
891
|
|
|
836
892
|
- `kettle-family release-state` now reports the checked-out branch and displays
|
|
837
893
|
release distance as local/remote ahead and behind counts.
|
|
838
|
-
- kettle-jem-template-20260720-001 - Generated READMEs can now render
|
|
839
|
-
template-managed corporate sponsor logos from project or family config.
|
|
840
894
|
- kettle-jem-template-20260720-002 - Generated development Gemfiles now use the
|
|
841
895
|
released `tree_sitter_language_pack` gem 1.13.3 or newer by default.
|
|
842
896
|
- kettle-jem-template-20260720-003 - Generated StructuredMerge Git diff driver
|
|
@@ -923,8 +977,6 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
923
977
|
the next `kettle-jem` release depends on unreleased `kettle-family` fixes.
|
|
924
978
|
- Raised the runtime Ruby floor to 4.0.0.
|
|
925
979
|
|
|
926
|
-
- kettle-jem-template-20260716-001 - Shim gemspec manifests now include
|
|
927
|
-
`LICENSE.md` instead of nonexistent `LICENSE.txt`.
|
|
928
980
|
- kettle-jem-template-20260716-002 - Generated gemspec manifests now ship fewer
|
|
929
981
|
repository-only files by default to reduce downstream distro packaging churn.
|
|
930
982
|
|
|
@@ -1608,7 +1660,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
1608
1660
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
1609
1661
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
1610
1662
|
|
|
1611
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.
|
|
1663
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.23...HEAD
|
|
1664
|
+
[1.2.23]: https://github.com/kettle-dev/kettle-family/compare/v1.2.22...v1.2.23
|
|
1665
|
+
[1.2.23t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.23
|
|
1666
|
+
[1.2.22]: https://github.com/kettle-dev/kettle-family/compare/v1.2.21...v1.2.22
|
|
1667
|
+
[1.2.22t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.22
|
|
1612
1668
|
[1.2.21]: https://github.com/kettle-dev/kettle-family/compare/v1.2.20...v1.2.21
|
|
1613
1669
|
[1.2.21t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.21
|
|
1614
1670
|
[1.2.20]: https://github.com/kettle-dev/kettle-family/compare/v1.2.19...v1.2.20
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by
|
|
1
|
+
<a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by GitHub" src="https://github.com/kettle-dev.png?size=192" width="14%" align="right"/></a>
|
|
2
2
|
|
|
3
3
|
# 👩👩👧👧 Kettle::Family
|
|
4
4
|
|
|
5
|
-
[![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only][📄license-img]][📄license] [![
|
|
5
|
+
[![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only][📄license-img]][📄license] [![Total downloads][👽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] [![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 on Discord][✉️discord-invite] or [RubyForum][✉️ruby-forum], as I may have missed the notification.
|
|
8
8
|
|
|
@@ -69,16 +69,16 @@ The _amazing_ test matrix is powered by the kettle-dev stack.
|
|
|
69
69
|
<details markdown="1">
|
|
70
70
|
<summary>How kettle-dev manages complexity in tests</summary>
|
|
71
71
|
|
|
72
|
-
| Gem | Source | Role |
|
|
72
|
+
| Gem | Source | Role | Total downloads |
|
|
73
73
|
|-----|--------|------|---------------------|
|
|
74
|
-
| [appraisal2](https://
|
|
75
|
-
| [appraisal2-rubocop](https://
|
|
76
|
-
| [kettle-dev](https://
|
|
77
|
-
| [kettle-jem](https://
|
|
78
|
-
| [kettle-soup-cover](https://
|
|
79
|
-
| [kettle-test](https://
|
|
80
|
-
| [rubocop-lts](https://
|
|
81
|
-
| [turbo_tests2](https://
|
|
74
|
+
| [appraisal2](https://clickgems.clickhouse.com/dashboard/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [](https://clickgems.clickhouse.com/dashboard/appraisal2) |
|
|
75
|
+
| [appraisal2-rubocop](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) |
|
|
76
|
+
| [kettle-dev](https://clickgems.clickhouse.com/dashboard/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [](https://clickgems.clickhouse.com/dashboard/kettle-dev) |
|
|
77
|
+
| [kettle-jem](https://clickgems.clickhouse.com/dashboard/kettle-jem) | [GitHub](https://github.com/kettle-dev/kettle-jem) | Appraisals & CI workflow templates | [](https://clickgems.clickhouse.com/dashboard/kettle-jem) |
|
|
78
|
+
| [kettle-soup-cover](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) |
|
|
79
|
+
| [kettle-test](https://clickgems.clickhouse.com/dashboard/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [](https://clickgems.clickhouse.com/dashboard/kettle-test) |
|
|
80
|
+
| [rubocop-lts](https://clickgems.clickhouse.com/dashboard/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [](https://clickgems.clickhouse.com/dashboard/rubocop-lts) |
|
|
81
|
+
| [turbo_tests2](https://clickgems.clickhouse.com/dashboard/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [](https://clickgems.clickhouse.com/dashboard/turbo_tests2) |
|
|
82
82
|
|
|
83
83
|
</details>
|
|
84
84
|
|
|
@@ -342,6 +342,15 @@ when it does not match `V.rel`. The `T(n)` header shows the total number of
|
|
|
342
342
|
kettle-jem transfer changelog entries. Each row value in that column shows how
|
|
343
343
|
many entries are after that member's stored replay cursor.
|
|
344
344
|
|
|
345
|
+
Transfer entries are destination release notes. `kettle-jem` may replay one
|
|
346
|
+
only when the template change affects a destination gem's users or developers,
|
|
347
|
+
such as behavior, configuration, compatibility, packaging, documentation, or a
|
|
348
|
+
developer workflow. They are deliberately placed in `Unreleased` and count as
|
|
349
|
+
pending release work. The stable `kettle-jem-template-YYYYMMDD-NNN` prefix is a
|
|
350
|
+
replay identifier only; it does not make an entry exempt from `unreleased`,
|
|
351
|
+
`pending`, or `bump`. `T(n)` reports replay progress separately and never
|
|
352
|
+
changes those release-state decisions.
|
|
353
|
+
|
|
345
354
|
Use release-state tokens with `--only` to select gems by state instead of by
|
|
346
355
|
name. Multiple tokens are combined with logical AND, so this selects only gems
|
|
347
356
|
that are both unreleased and pending. Status tokens cannot be mixed with member
|
|
@@ -716,7 +725,7 @@ Thanks for RTFM. ☺️
|
|
|
716
725
|
[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
|
|
717
726
|
[⛳️gem-namespace]: https://github.com/kettle-dev/kettle-family
|
|
718
727
|
[⛳️namespace-img]: https://img.shields.io/badge/namespace-Kettle::Family-3C2D2D.svg?style=square&logo=ruby&logoColor=white
|
|
719
|
-
[⛳️gem-name]: https://
|
|
728
|
+
[⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/kettle-family
|
|
720
729
|
[⛳️name-img]: https://img.shields.io/badge/name-kettle--family-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
|
|
721
730
|
[⛳️tag-img]: https://img.shields.io/github/tag/kettle-dev/kettle-family.svg
|
|
722
731
|
[⛳️tag]: https://github.com/kettle-dev/kettle-family/releases
|
|
@@ -765,9 +774,9 @@ Thanks for RTFM. ☺️
|
|
|
765
774
|
[📜gh-wiki]: https://github.com/kettle-dev/kettle-family/wiki
|
|
766
775
|
[📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
|
|
767
776
|
[📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
|
|
768
|
-
[👽dl-rank]: https://
|
|
769
|
-
[👽dl-ranki]: https://img.shields.io/gem/
|
|
770
|
-
[👽version]: https://
|
|
777
|
+
[👽dl-rank]: https://clickgems.clickhouse.com/dashboard/kettle-family
|
|
778
|
+
[👽dl-ranki]: https://img.shields.io/gem/dt/kettle-family.svg
|
|
779
|
+
[👽version]: https://clickgems.clickhouse.com/dashboard/kettle-family
|
|
771
780
|
[👽versioni]: https://img.shields.io/gem/v/kettle-family.svg
|
|
772
781
|
[🏀qlty-mnt]: https://qlty.sh/gh/kettle-dev/projects/kettle-family
|
|
773
782
|
[🏀qlty-mnti]: https://qlty.sh/gh/kettle-dev/projects/kettle-family/maintainability.svg
|
|
@@ -819,7 +828,7 @@ Thanks for RTFM. ☺️
|
|
|
819
828
|
[📌gitmoji]: https://gitmoji.dev
|
|
820
829
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
821
830
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
822
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.
|
|
831
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.960-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
823
832
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
824
833
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
825
834
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "kettle/dev"
|
|
4
|
+
|
|
5
|
+
module Kettle
|
|
6
|
+
module Family
|
|
7
|
+
# Removes the retired pre-fork appraisal source before Bundler evaluates a
|
|
8
|
+
# legacy member. kettle-jem owns the current appraisal wiring.
|
|
9
|
+
class AppraisalBootstrap
|
|
10
|
+
LEGACY_GEM_NAME = "appraisal"
|
|
11
|
+
LEGACY_GITHUB = "pboling/appraisal"
|
|
12
|
+
LEGACY_BRANCH = "galtzo"
|
|
13
|
+
|
|
14
|
+
def initialize(mode: :dry_run)
|
|
15
|
+
@mode = mode
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :mode
|
|
19
|
+
|
|
20
|
+
def member_needs_bootstrap?(member)
|
|
21
|
+
edits_for(member).any?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def bootstrap_member(member)
|
|
25
|
+
edits = edits_for(member)
|
|
26
|
+
write_edits(edits)
|
|
27
|
+
CommandResult.new(
|
|
28
|
+
member.name,
|
|
29
|
+
"template_bootstrap_appraisal",
|
|
30
|
+
["internal", "appraisal-bootstrap", LEGACY_GITHUB],
|
|
31
|
+
member.root,
|
|
32
|
+
0,
|
|
33
|
+
true,
|
|
34
|
+
"removed retired #{LEGACY_GITHUB} appraisal dependency from #{edits.map { |edit| File.basename(edit.fetch(:path)) }.uniq.join(", ")}\n",
|
|
35
|
+
"",
|
|
36
|
+
0.0,
|
|
37
|
+
mode != :execute,
|
|
38
|
+
(mode == :execute) ? nil : "dry run"
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def write_edits(edits)
|
|
45
|
+
return if mode != :execute || edits.empty?
|
|
46
|
+
|
|
47
|
+
Kettle::Dev::VersionBump.write_edits(edits)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def edits_for(member)
|
|
51
|
+
[File.join(member.root, "Gemfile"), File.join(member.root, "Appraisal.root.gemfile")].filter_map do |path|
|
|
52
|
+
file_edits(path)
|
|
53
|
+
end.flatten
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def file_edits(path)
|
|
57
|
+
return [] unless File.file?(path)
|
|
58
|
+
|
|
59
|
+
source = File.read(path)
|
|
60
|
+
parse_result = Kettle::Dev::VersionBump.parse_source(source, path)
|
|
61
|
+
Kettle::Dev::VersionBump.each_node(parse_result.value).filter_map do |node|
|
|
62
|
+
obsolete_appraisal_edit(path, source, node)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def obsolete_appraisal_edit(path, source, node)
|
|
67
|
+
return unless legacy_appraisal_call?(node)
|
|
68
|
+
|
|
69
|
+
line_start = source.rindex("\n", node.location.start_offset - 1).to_i + 1
|
|
70
|
+
line_end = source.index("\n", node.location.end_offset) || source.length
|
|
71
|
+
line_end += 1 if line_end < source.length
|
|
72
|
+
if final_standalone_call?(source, node, line_start, line_end)
|
|
73
|
+
# Remove the preceding separator and normalize EOF when the retired
|
|
74
|
+
# declaration is the final standalone line in the file.
|
|
75
|
+
line_start = source[0...line_start].rstrip.length
|
|
76
|
+
return Kettle::Dev::VersionBump.file_edit(path, source, line_start, source.length, "\n")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
Kettle::Dev::VersionBump.file_edit(path, source, line_start, line_end, "")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def final_standalone_call?(source, node, line_start, line_end)
|
|
83
|
+
source[line_start...node.location.start_offset].to_s.strip.empty? &&
|
|
84
|
+
source[node.location.end_offset...line_end].to_s.strip.empty? &&
|
|
85
|
+
source[line_end..].to_s.strip.empty?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def legacy_appraisal_call?(node)
|
|
89
|
+
return false unless node.is_a?(Prism::CallNode) && node.name == :gem
|
|
90
|
+
|
|
91
|
+
arguments = node.arguments&.arguments || []
|
|
92
|
+
name_node = arguments.first
|
|
93
|
+
return false unless name_node.is_a?(Prism::StringNode) && name_node.unescaped == LEGACY_GEM_NAME
|
|
94
|
+
|
|
95
|
+
keyword_values(arguments).values_at("github", "branch") == [LEGACY_GITHUB, LEGACY_BRANCH]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def keyword_values(arguments)
|
|
99
|
+
arguments.grep(Prism::KeywordHashNode).flat_map(&:elements).each_with_object({}) do |association, values|
|
|
100
|
+
next unless association.is_a?(Prism::AssocNode)
|
|
101
|
+
next unless association.key.is_a?(Prism::SymbolNode) && association.value.is_a?(Prism::StringNode)
|
|
102
|
+
|
|
103
|
+
values[association.key.unescaped] = association.value.unescaped
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/kettle/family/cli.rb
CHANGED
|
@@ -59,6 +59,10 @@ module Kettle
|
|
|
59
59
|
options[:commit] = false
|
|
60
60
|
end
|
|
61
61
|
base.option :allow_dirty, long: "--allow-dirty", desc: "Reserved for compatibility; member repos manage their own commit safety"
|
|
62
|
+
base.option :autostash, long: "--autostash", desc: "Use the default template autostash behavior"
|
|
63
|
+
base.option :no_autostash, long: "--no-autostash", desc: "Fail template runs when a member worktree is dirty" do
|
|
64
|
+
options[:autostash] = false
|
|
65
|
+
end
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
|
|
@@ -158,6 +162,7 @@ module Kettle
|
|
|
158
162
|
push: false,
|
|
159
163
|
commit: !options.key?(:commit) || options[:commit],
|
|
160
164
|
allow_dirty: truthy_option?(:allow_dirty),
|
|
165
|
+
autostash: !options.key?(:autostash) || options[:autostash],
|
|
161
166
|
target_version: nil,
|
|
162
167
|
reset_target: nil,
|
|
163
168
|
bup_args: [],
|
|
@@ -620,7 +625,11 @@ module Kettle
|
|
|
620
625
|
def build_report(command, options)
|
|
621
626
|
config = Config.load(root: options[:root], path: options[:config])
|
|
622
627
|
start_at = parse_start_at(options[:start_at])
|
|
623
|
-
|
|
628
|
+
effective_only = default_only_filter(command: command, only: options[:only])
|
|
629
|
+
discovery = Discovery.new(
|
|
630
|
+
config: config,
|
|
631
|
+
release_dependency_member_names: direct_member_only_names(effective_only)
|
|
632
|
+
)
|
|
624
633
|
members = discovery.members
|
|
625
634
|
ordered = if command == "install"
|
|
626
635
|
install_order(members, config)
|
|
@@ -629,7 +638,6 @@ module Kettle
|
|
|
629
638
|
else
|
|
630
639
|
Orderer.new(members: members, mode: config.order_mode, hints: config.order_hints).ordered
|
|
631
640
|
end
|
|
632
|
-
effective_only = default_only_filter(command: command, only: options[:only])
|
|
633
641
|
release_state_results = release_state_results_for_selection(config: config, members: ordered, only: effective_only)
|
|
634
642
|
selected = Selection.new(members: ordered, release_state_results: release_state_results).apply(only: effective_only, exclude: options[:exclude], start_at: start_at.member)
|
|
635
643
|
result_members = selected
|
|
@@ -678,6 +686,13 @@ module Kettle
|
|
|
678
686
|
only
|
|
679
687
|
end
|
|
680
688
|
|
|
689
|
+
def direct_member_only_names(only)
|
|
690
|
+
names = only.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
691
|
+
return nil if names.empty? || names.any? { |name| Selection.status_token?(name) }
|
|
692
|
+
|
|
693
|
+
names
|
|
694
|
+
end
|
|
695
|
+
|
|
681
696
|
def command_results_for_current_branch(command:, config:, members:, options:, start_at: StartAt.new(nil, nil))
|
|
682
697
|
return mise_trust_results(config: config, members: members) if command == "mise-trust"
|
|
683
698
|
return bump_version_results(members: members, options: options, phase: command) if %w[bump bump-version].include?(command)
|
|
@@ -697,6 +712,7 @@ module Kettle
|
|
|
697
712
|
accept: options[:accept],
|
|
698
713
|
commit: options[:commit],
|
|
699
714
|
allow_dirty: options[:allow_dirty],
|
|
715
|
+
autostash: options[:autostash],
|
|
700
716
|
publish: options[:publish],
|
|
701
717
|
push: options[:push],
|
|
702
718
|
tag: options[:tag],
|
|
@@ -586,6 +586,15 @@ module Kettle
|
|
|
586
586
|
|
|
587
587
|
def process_env(member:, env:)
|
|
588
588
|
base_env = unbundled_process_env
|
|
589
|
+
# Bundler's unbundled environment intentionally omits activation
|
|
590
|
+
# variables, but it may also omit PATH. When `unsetenv_others` is used
|
|
591
|
+
# for mise-managed members, dropping PATH makes nested commands such
|
|
592
|
+
# as `bundle` unresolvable inside kettle-jem.
|
|
593
|
+
base_env["PATH"] = if base_env.key?("PATH")
|
|
594
|
+
executable_path(base_env["PATH"])
|
|
595
|
+
else
|
|
596
|
+
ENV["PATH"]
|
|
597
|
+
end
|
|
589
598
|
return base_env.merge(env) unless mise_configured?(member)
|
|
590
599
|
|
|
591
600
|
base_env.merge(sensitive_env(env))
|
|
@@ -603,6 +612,11 @@ module Kettle
|
|
|
603
612
|
SENSITIVE_ENV_KEYS.include?(key.to_s)
|
|
604
613
|
end
|
|
605
614
|
|
|
615
|
+
def executable_path(path)
|
|
616
|
+
paths = [Gem.bindir, File.dirname(RbConfig.ruby), path, ENV["PATH"]]
|
|
617
|
+
paths.compact.map(&:to_s).flat_map { |value| value.split(File::PATH_SEPARATOR) }.reject(&:empty?).uniq.join(File::PATH_SEPARATOR)
|
|
618
|
+
end
|
|
619
|
+
|
|
606
620
|
def unbundled_process_env
|
|
607
621
|
return Bundler.original_env if defined?(Bundler) && Bundler.respond_to?(:original_env)
|
|
608
622
|
return Bundler.unbundled_env if defined?(Bundler) && Bundler.respond_to?(:unbundled_env)
|
data/lib/kettle/family/config.rb
CHANGED
|
@@ -265,6 +265,12 @@ module Kettle
|
|
|
265
265
|
fetch_path("release", "jobs")
|
|
266
266
|
end
|
|
267
267
|
|
|
268
|
+
def release_waves
|
|
269
|
+
Array(fetch_path("release", "waves")).map do |wave|
|
|
270
|
+
Array(wave).map(&:to_s).map(&:strip).reject(&:empty?)
|
|
271
|
+
end.reject(&:empty?)
|
|
272
|
+
end
|
|
273
|
+
|
|
268
274
|
def release_auto_dependency_floors?
|
|
269
275
|
fetch_path("release", "auto_dependency_floors") != false
|
|
270
276
|
end
|
|
@@ -5,8 +5,9 @@ require "open3"
|
|
|
5
5
|
module Kettle
|
|
6
6
|
module Family
|
|
7
7
|
class Discovery
|
|
8
|
-
def initialize(config:)
|
|
8
|
+
def initialize(config:, release_dependency_member_names: nil)
|
|
9
9
|
@config = config
|
|
10
|
+
@release_dependency_member_names = release_dependency_member_names&.map(&:to_s)
|
|
10
11
|
@warnings = []
|
|
11
12
|
end
|
|
12
13
|
|
|
@@ -18,7 +19,7 @@ module Kettle
|
|
|
18
19
|
|
|
19
20
|
private
|
|
20
21
|
|
|
21
|
-
attr_reader :config
|
|
22
|
+
attr_reader :config, :release_dependency_member_names
|
|
22
23
|
|
|
23
24
|
def build_members
|
|
24
25
|
@warnings = []
|
|
@@ -51,11 +52,15 @@ module Kettle
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def configured_root_members
|
|
54
|
-
config.configured_member_roots
|
|
55
|
+
config.configured_member_roots
|
|
56
|
+
.select { |root| selected_configured_root?(root) }
|
|
57
|
+
.map { |root| member_from_gemspec(primary_gemspec(root)) }
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
def explicit_members
|
|
58
|
-
config.explicit_members.
|
|
61
|
+
config.explicit_members.filter_map do |entry|
|
|
62
|
+
next unless selected_configured_root?(entry.fetch("root"))
|
|
63
|
+
|
|
59
64
|
gemspec_path = if entry["gemspec"]
|
|
60
65
|
File.expand_path(entry["gemspec"], entry.fetch("root"))
|
|
61
66
|
else
|
|
@@ -65,6 +70,12 @@ module Kettle
|
|
|
65
70
|
end
|
|
66
71
|
end
|
|
67
72
|
|
|
73
|
+
def selected_configured_root?(root)
|
|
74
|
+
return true if release_dependency_member_names.nil?
|
|
75
|
+
|
|
76
|
+
release_dependency_member_names.include?(File.basename(File.expand_path(root)))
|
|
77
|
+
end
|
|
78
|
+
|
|
68
79
|
def primary_gemspec(root)
|
|
69
80
|
gemspecs = Dir.glob(File.join(root, "*.gemspec"))
|
|
70
81
|
raise Error, "no gemspec found in #{root}" if gemspecs.empty?
|
|
@@ -104,18 +115,23 @@ module Kettle
|
|
|
104
115
|
version_file: version_file(root, spec.name),
|
|
105
116
|
version: spec.version.to_s,
|
|
106
117
|
dependencies: dependencies,
|
|
107
|
-
release_dependencies: release_dependencies(
|
|
118
|
+
release_dependencies: release_dependencies(
|
|
119
|
+
root: root,
|
|
120
|
+
spec: spec,
|
|
121
|
+
dependencies: dependencies,
|
|
122
|
+
include_gemfile_dependencies: release_dependency_member_names.nil? || release_dependency_member_names.include?(spec.name)
|
|
123
|
+
),
|
|
108
124
|
required_ruby_version: required_ruby_version(spec),
|
|
109
125
|
licenses: licenses(spec),
|
|
110
126
|
authors: authors(spec)
|
|
111
127
|
)
|
|
112
128
|
end
|
|
113
129
|
|
|
114
|
-
def release_dependencies(root:, spec:, dependencies:)
|
|
130
|
+
def release_dependencies(root:, spec:, dependencies:, include_gemfile_dependencies:)
|
|
115
131
|
(
|
|
116
132
|
dependencies +
|
|
117
133
|
spec.development_dependencies.map(&:name) +
|
|
118
|
-
gemfile_dependencies(root)
|
|
134
|
+
(include_gemfile_dependencies ? gemfile_dependencies(root) : [])
|
|
119
135
|
).uniq.sort
|
|
120
136
|
end
|
|
121
137
|
|
|
@@ -23,6 +23,7 @@ module Kettle
|
|
|
23
23
|
@members = members
|
|
24
24
|
@config = config
|
|
25
25
|
@github_latest_release_by_repo = {}
|
|
26
|
+
@transfer_changelog_status_by_root = {}
|
|
26
27
|
@transfer_changelog_lag_by_key = {}
|
|
27
28
|
@transfer_changelog_total_count = TRANSFER_CHANGELOG_TOTAL_UNSET
|
|
28
29
|
end
|
|
@@ -72,8 +73,8 @@ module Kettle
|
|
|
72
73
|
state = success ? JSON.parse(stdout) : {}
|
|
73
74
|
state = branch_filtered_state(member, state, branch) if success && branch
|
|
74
75
|
state = state_with_computed_booleans(state) if success
|
|
75
|
-
state = enrich_git_state(member.root, state) if success
|
|
76
|
-
state = enrich_github_release(member.root, state) if success
|
|
76
|
+
state = enrich_git_state(member.root, state, branch: branch) if success
|
|
77
|
+
state = enrich_github_release(member.root, state, branch: branch) if success
|
|
77
78
|
state = enrich_transfer_changelog_lag(member.root, state) if success
|
|
78
79
|
result(member: member, command: command, stdout: stdout, stderr: stderr, status: status.exitstatus, elapsed: elapsed, success: success, state: state, branch: branch)
|
|
79
80
|
rescue JSON::ParserError => error
|
|
@@ -98,8 +99,8 @@ module Kettle
|
|
|
98
99
|
success = status.success?
|
|
99
100
|
state = success ? JSON.parse(stdout) : {}
|
|
100
101
|
state = branch_filtered_state(member, state, branch) if success && branch
|
|
101
|
-
state = enrich_git_state(member.root, state) if success
|
|
102
|
-
state = enrich_github_release(member.root, state) if success
|
|
102
|
+
state = enrich_git_state(member.root, state, branch: branch) if success
|
|
103
|
+
state = enrich_github_release(member.root, state, branch: branch) if success
|
|
103
104
|
state = enrich_transfer_changelog_lag(member.root, state) if success
|
|
104
105
|
result(
|
|
105
106
|
member: member,
|
|
@@ -219,29 +220,29 @@ module Kettle
|
|
|
219
220
|
status.success?
|
|
220
221
|
end
|
|
221
222
|
|
|
222
|
-
def enrich_git_state(root, state)
|
|
223
|
+
def enrich_git_state(root, state, branch: nil)
|
|
223
224
|
return state unless git_work_tree?(root)
|
|
224
225
|
|
|
225
226
|
enriched = state.dup
|
|
226
|
-
current_branch = git_output(root, "branch", "--show-current")
|
|
227
|
+
current_branch = branch || git_output(root, "branch", "--show-current")
|
|
227
228
|
enriched["current_branch"] = current_branch unless current_branch.to_s.empty?
|
|
228
229
|
|
|
229
230
|
version = state["latest_released"].to_s.empty? ? state["latest_changelog_version"] : state["latest_released"]
|
|
230
231
|
return enriched if version.to_s.empty?
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
if
|
|
234
|
-
enriched["default_branch"] =
|
|
235
|
-
if (counts = release_counts_for_ref(root, version,
|
|
233
|
+
local_ref = branch || default_branch(root)
|
|
234
|
+
if local_ref
|
|
235
|
+
enriched["default_branch"] = local_ref
|
|
236
|
+
if (counts = release_counts_for_ref(root, version, local_ref))
|
|
236
237
|
enriched["behind"] = counts.fetch(:behind)
|
|
237
238
|
enriched["ahead"] = counts.fetch(:ahead)
|
|
238
239
|
end
|
|
239
240
|
end
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
if
|
|
243
|
-
enriched["remote_default_branch"] =
|
|
244
|
-
if (counts = release_counts_for_ref(root, version,
|
|
242
|
+
remote_ref = branch ? "origin/#{branch}" : remote_default_branch(root, local_ref)
|
|
243
|
+
if remote_ref && git_ref_exists?(root, "refs/remotes/#{remote_ref}")
|
|
244
|
+
enriched["remote_default_branch"] = remote_ref
|
|
245
|
+
if (counts = release_counts_for_ref(root, version, remote_ref))
|
|
245
246
|
enriched["remote_behind"] = counts.fetch(:behind)
|
|
246
247
|
enriched["remote_ahead"] = counts.fetch(:ahead)
|
|
247
248
|
end
|
|
@@ -319,17 +320,43 @@ module Kettle
|
|
|
319
320
|
state["unreleased_entries"] == true && state["version"].to_s == state["latest_released"].to_s
|
|
320
321
|
end
|
|
321
322
|
|
|
322
|
-
def enrich_github_release(root, state)
|
|
323
|
-
tag = github_latest_release(root)
|
|
323
|
+
def enrich_github_release(root, state, branch: nil)
|
|
324
|
+
tag = branch ? github_release_for_version(root, state["latest_released"]) : github_latest_release(root)
|
|
324
325
|
return state unless tag
|
|
325
326
|
|
|
326
327
|
state.merge("github_latest_release" => tag)
|
|
327
328
|
end
|
|
328
329
|
|
|
330
|
+
def github_release_for_version(root, version)
|
|
331
|
+
return nil if version.to_s.empty?
|
|
332
|
+
|
|
333
|
+
repo = github_repo_slug(root)
|
|
334
|
+
return nil unless repo
|
|
335
|
+
|
|
336
|
+
tag = "v#{version.to_s.delete_prefix("v")}"
|
|
337
|
+
stdout, _stderr, status = Open3.capture3("gh", "release", "view", tag, "--repo", repo, "--json", "tagName", "--jq", ".tagName")
|
|
338
|
+
status.success? ? normalize_github_release_tag(stdout) : nil
|
|
339
|
+
rescue SystemCallError
|
|
340
|
+
nil
|
|
341
|
+
end
|
|
342
|
+
|
|
329
343
|
def enrich_transfer_changelog_lag(root, state)
|
|
330
344
|
kettle_jem_state = kettle_jem_config_state(root)
|
|
331
345
|
return state unless kettle_jem_state
|
|
332
346
|
|
|
347
|
+
status = transfer_changelog_status(root)
|
|
348
|
+
if status
|
|
349
|
+
total = status.key?("total_count") ? status.fetch("total_count") : transfer_changelog_total_count
|
|
350
|
+
return state.merge(
|
|
351
|
+
"transfer_changelog_lag" => status.fetch("missing_count", 0).to_i,
|
|
352
|
+
"transfer_changelog_applicable" => status.fetch("applicable_count", 0).to_i,
|
|
353
|
+
"transfer_changelog_excluded_present" => status.fetch("excluded_present_count", 0).to_i,
|
|
354
|
+
"transfer_changelog_total" => total.to_i
|
|
355
|
+
)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Compatibility with already released kettle-jem versions while a
|
|
359
|
+
# workspace is upgraded. New versions use the context-aware status API.
|
|
333
360
|
replay = kettle_jem_state["changelog_replay"]
|
|
334
361
|
replay = replay.is_a?(Hash) ? replay : {}
|
|
335
362
|
last_entry_key = replay["last_entry_key"] || replay[:last_entry_key]
|
|
@@ -368,6 +395,15 @@ module Kettle
|
|
|
368
395
|
nil
|
|
369
396
|
end
|
|
370
397
|
|
|
398
|
+
def transfer_changelog_status(root)
|
|
399
|
+
cache_key = File.expand_path(root.to_s)
|
|
400
|
+
@transfer_changelog_status_by_root.fetch(cache_key) do
|
|
401
|
+
@transfer_changelog_status_by_root[cache_key] = in_process_transfer_changelog_status(cache_key) || external_transfer_changelog_status(cache_key)
|
|
402
|
+
end
|
|
403
|
+
rescue
|
|
404
|
+
nil
|
|
405
|
+
end
|
|
406
|
+
|
|
371
407
|
def transfer_changelog_total_count
|
|
372
408
|
return @transfer_changelog_total_count unless @transfer_changelog_total_count.equal?(TRANSFER_CHANGELOG_TOTAL_UNSET)
|
|
373
409
|
|
|
@@ -383,11 +419,20 @@ module Kettle
|
|
|
383
419
|
nil
|
|
384
420
|
end
|
|
385
421
|
|
|
422
|
+
def in_process_transfer_changelog_status(root)
|
|
423
|
+
return nil unless load_kettle_jem_transfer_api
|
|
424
|
+
return nil unless ::Kettle::Jem.respond_to?(:transfer_changelog_status)
|
|
425
|
+
|
|
426
|
+
normalize_transfer_changelog_lag(::Kettle::Jem.transfer_changelog_status(root))
|
|
427
|
+
rescue
|
|
428
|
+
nil
|
|
429
|
+
end
|
|
430
|
+
|
|
386
431
|
def load_kettle_jem_transfer_api
|
|
387
|
-
return true if defined?(::Kettle::Jem) && ::Kettle::Jem.respond_to?(:transfer_changelog_lag)
|
|
432
|
+
return true if defined?(::Kettle::Jem) && (::Kettle::Jem.respond_to?(:transfer_changelog_lag) || ::Kettle::Jem.respond_to?(:transfer_changelog_status))
|
|
388
433
|
|
|
389
434
|
require "kettle/jem"
|
|
390
|
-
defined?(::Kettle::Jem) && ::Kettle::Jem.respond_to?(:transfer_changelog_lag)
|
|
435
|
+
defined?(::Kettle::Jem) && (::Kettle::Jem.respond_to?(:transfer_changelog_lag) || ::Kettle::Jem.respond_to?(:transfer_changelog_status))
|
|
391
436
|
rescue LoadError
|
|
392
437
|
false
|
|
393
438
|
end
|
|
@@ -409,6 +454,21 @@ module Kettle
|
|
|
409
454
|
nil
|
|
410
455
|
end
|
|
411
456
|
|
|
457
|
+
def external_transfer_changelog_status(root)
|
|
458
|
+
code = <<~RUBY
|
|
459
|
+
puts JSON.generate(Kettle::Jem.transfer_changelog_status(ARGV.fetch(0)))
|
|
460
|
+
RUBY
|
|
461
|
+
command = [RbConfig.ruby, "-rjson", "-rkettle/jem", "-e", code, root]
|
|
462
|
+
stdout, _stderr, status = if defined?(::Bundler)
|
|
463
|
+
::Bundler.with_unbundled_env { Open3.capture3(*command) }
|
|
464
|
+
else
|
|
465
|
+
Open3.capture3(*command)
|
|
466
|
+
end
|
|
467
|
+
status.success? ? normalize_transfer_changelog_lag(JSON.parse(stdout)) : nil
|
|
468
|
+
rescue JSON::ParserError, SystemCallError
|
|
469
|
+
nil
|
|
470
|
+
end
|
|
471
|
+
|
|
412
472
|
def normalize_transfer_changelog_lag(lag)
|
|
413
473
|
return nil unless lag.is_a?(Hash)
|
|
414
474
|
|
|
@@ -3,12 +3,43 @@
|
|
|
3
3
|
module Kettle
|
|
4
4
|
module Family
|
|
5
5
|
class ReleaseWaves
|
|
6
|
-
def initialize(members:)
|
|
6
|
+
def initialize(members:, configured_waves: [])
|
|
7
7
|
@members = members
|
|
8
|
+
@configured_waves = configured_waves
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def waves
|
|
11
|
-
|
|
12
|
+
configured = configured_waves_for_selected_members
|
|
13
|
+
configured_names = configured.flatten.map(&:name)
|
|
14
|
+
remaining_members = members.reject { |member| configured_names.include?(member.name) }
|
|
15
|
+
|
|
16
|
+
configured + inferred_waves(remaining_members)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :members, :configured_waves
|
|
22
|
+
|
|
23
|
+
def configured_waves_for_selected_members
|
|
24
|
+
duplicates = configured_waves.flatten.group_by(&:itself).select { |_name, names| names.length > 1 }.keys
|
|
25
|
+
raise Error, "release waves contain duplicate member(s): #{duplicates.join(", ")}" unless duplicates.empty?
|
|
26
|
+
|
|
27
|
+
selected_names = members.map(&:name)
|
|
28
|
+
selected_members = members.to_h { |member| [member.name, member] }
|
|
29
|
+
# Configured waves commonly name the complete family while --only selects
|
|
30
|
+
# a subset. Ignore entries outside the current selection.
|
|
31
|
+
return [] if configured_waves.empty?
|
|
32
|
+
|
|
33
|
+
configured_waves.filter_map do |wave|
|
|
34
|
+
names = wave & selected_names
|
|
35
|
+
names.map { |name| selected_members.fetch(name) } unless names.empty?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inferred_waves(candidate_members)
|
|
40
|
+
return [] if candidate_members.empty?
|
|
41
|
+
|
|
42
|
+
by_name = candidate_members.to_h { |member| [member.name, member] }
|
|
12
43
|
pending = by_name.keys
|
|
13
44
|
completed = []
|
|
14
45
|
[].tap do |planned_waves|
|
|
@@ -30,10 +61,6 @@ module Kettle
|
|
|
30
61
|
end
|
|
31
62
|
end
|
|
32
63
|
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
attr_reader :members
|
|
36
|
-
|
|
37
64
|
def selected_hard_dependencies_for(member, by_name)
|
|
38
65
|
Array(member.dependencies).map(&:to_s).select { |dependency| by_name.key?(dependency) }
|
|
39
66
|
end
|
data/lib/kettle/family/report.rb
CHANGED
|
@@ -509,12 +509,12 @@ module Kettle
|
|
|
509
509
|
def append_release_state_results(lines)
|
|
510
510
|
lines << "release state:"
|
|
511
511
|
lines << " boolean columns:"
|
|
512
|
-
lines << " unrel: unreleased changelog entries are present"
|
|
512
|
+
lines << " unrel: unreleased changelog entries are present (including replayed transfer entries)"
|
|
513
513
|
lines << " prep: V.ch.md matches V.rb and is ready to publish"
|
|
514
514
|
lines << " pend: unrel or prep"
|
|
515
515
|
lines << " bump: unrel is yes and V.rb matches V.rel"
|
|
516
516
|
lines << " count columns:"
|
|
517
|
-
lines << " T(n): kettle-jem transfer changelog lag; n is the total
|
|
517
|
+
lines << " T(n): filter-aware kettle-jem transfer changelog lag; n is the total source entry count and row values are missing / applicable (x excluded-present)"
|
|
518
518
|
rows = release_state_header
|
|
519
519
|
results.each do |result|
|
|
520
520
|
rows << release_state_row(result)
|
|
@@ -539,7 +539,7 @@ module Kettle
|
|
|
539
539
|
state.fetch("latest_changelog_version", nil).to_s.empty? ? "none" : state.fetch("latest_changelog_version").to_s,
|
|
540
540
|
state.fetch("latest_released", nil).to_s.empty? ? "unknown" : state.fetch("latest_released").to_s,
|
|
541
541
|
release_state_github_release(state),
|
|
542
|
-
state
|
|
542
|
+
release_state_transfer_changelog_value(state),
|
|
543
543
|
release_state_ahead_behind(state),
|
|
544
544
|
yes_no(state.fetch("unreleased_entries", nil)),
|
|
545
545
|
yes_no(state.fetch("prepared_release_pending", nil)),
|
|
@@ -566,6 +566,16 @@ module Kettle
|
|
|
566
566
|
"T(#{total || "?"})"
|
|
567
567
|
end
|
|
568
568
|
|
|
569
|
+
def release_state_transfer_changelog_value(state)
|
|
570
|
+
missing = state.fetch("transfer_changelog_lag", 0).to_i
|
|
571
|
+
applicable = state.fetch("transfer_changelog_applicable", nil)
|
|
572
|
+
return missing.to_s if applicable.nil?
|
|
573
|
+
|
|
574
|
+
excluded_present = state.fetch("transfer_changelog_excluded_present", 0).to_i
|
|
575
|
+
value = "#{missing} / #{applicable.to_i}"
|
|
576
|
+
excluded_present.positive? ? "#{value} (x#{excluded_present})" : value
|
|
577
|
+
end
|
|
578
|
+
|
|
569
579
|
def release_state_github_release(state)
|
|
570
580
|
github_release = state.fetch("github_latest_release", nil).to_s
|
|
571
581
|
return "unknown" if github_release.empty?
|
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = "1.2.
|
|
8
|
+
VERSION = "1.2.23"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -102,7 +102,7 @@ module Kettle
|
|
|
102
102
|
REGISTRY_WAIT_ATTEMPTS = 15
|
|
103
103
|
REGISTRY_WAIT_INTERVAL_SECONDS = 15
|
|
104
104
|
|
|
105
|
-
def initialize(command:, config:, members:, execute: false, accept: true, commit: true, allow_dirty: false, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, env_overrides: {}, debug: false, verbose: false, gem_signing_password: nil, secrets_provider: nil, jobs: nil, progress_io: nil, reset_target: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil, **options)
|
|
105
|
+
def initialize(command:, config:, members:, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, env_overrides: {}, debug: false, verbose: false, gem_signing_password: nil, secrets_provider: nil, jobs: nil, progress_io: nil, reset_target: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil, **options)
|
|
106
106
|
@command = command
|
|
107
107
|
@config = config
|
|
108
108
|
@members = members
|
|
@@ -110,6 +110,7 @@ module Kettle
|
|
|
110
110
|
@accept = accept
|
|
111
111
|
@commit = commit
|
|
112
112
|
@allow_dirty = allow_dirty
|
|
113
|
+
@autostash = autostash
|
|
113
114
|
@publish = publish
|
|
114
115
|
@push = push
|
|
115
116
|
@tag = tag
|
|
@@ -150,6 +151,8 @@ module Kettle
|
|
|
150
151
|
return current_branch_results(members)
|
|
151
152
|
end
|
|
152
153
|
|
|
154
|
+
return template_with_worktree_sync_results if command == "template" && execute
|
|
155
|
+
|
|
153
156
|
preflight = branch_checkout_dirty_preflight_results
|
|
154
157
|
return preflight if preflight.any? { |result| !result.ok? }
|
|
155
158
|
|
|
@@ -162,7 +165,159 @@ module Kettle
|
|
|
162
165
|
|
|
163
166
|
private
|
|
164
167
|
|
|
165
|
-
attr_reader :command, :config, :members, :execute, :accept, :commit, :allow_dirty, :publish, :push, :tag, :start_step, :skip_steps, :local_ci, :continue_ci_failures, :ci_workflows, :skip_bundle_audit, :skip_remotes, :required_remotes, :auto_dependency_floors, :gha_sha_pins_upgrade, :gha_sha_pins_check, :env_overrides, :debug, :verbose, :jobs, :progress_io, :reset_target, :bup_args, :bex_args, :start_member, :start_branch
|
|
168
|
+
attr_reader :command, :config, :members, :execute, :accept, :commit, :allow_dirty, :autostash, :publish, :push, :tag, :start_step, :skip_steps, :local_ci, :continue_ci_failures, :ci_workflows, :skip_bundle_audit, :skip_remotes, :required_remotes, :auto_dependency_floors, :gha_sha_pins_upgrade, :gha_sha_pins_check, :env_overrides, :debug, :verbose, :jobs, :progress_io, :reset_target, :bup_args, :bex_args, :start_member, :start_branch
|
|
169
|
+
|
|
170
|
+
def template_with_worktree_sync_results
|
|
171
|
+
runner = command_runner
|
|
172
|
+
checkout_preflight = branch_checkout_dirty_preflight_results
|
|
173
|
+
if checkout_preflight.any? { |result| !result.ok? } && !autostash
|
|
174
|
+
return checkout_preflight
|
|
175
|
+
end
|
|
176
|
+
checkout_preflight = [] unless checkout_preflight.all?(&:ok?)
|
|
177
|
+
|
|
178
|
+
sync_results, stashes = template_worktree_sync_results(runner: runner)
|
|
179
|
+
unless sync_results.all?(&:ok?)
|
|
180
|
+
return checkout_preflight + sync_results + restore_template_autostashes(stashes, runner: runner)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
workflow_results = branch_checkout_dirty_preflight_results
|
|
184
|
+
if workflow_results.all?(&:ok?)
|
|
185
|
+
workflow_results += if !config.release_target_branches.empty?
|
|
186
|
+
branch_target_results
|
|
187
|
+
elsif member_local_branch_targets?
|
|
188
|
+
member_local_branch_target_results
|
|
189
|
+
else
|
|
190
|
+
current_branch_results(members)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
checkout_preflight + sync_results + workflow_results + restore_template_autostashes(stashes, runner: runner)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def template_worktree_sync_results(runner:)
|
|
198
|
+
stashes = []
|
|
199
|
+
results = []
|
|
200
|
+
template_sync_members.each do |member|
|
|
201
|
+
dirty_paths = GitStatus.dirty_paths(member.root)
|
|
202
|
+
if dirty_paths.any?
|
|
203
|
+
unless autostash
|
|
204
|
+
results << template_dirty_worktree_result(member, dirty_paths)
|
|
205
|
+
break
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
stash_result = runner.call(
|
|
209
|
+
member: member,
|
|
210
|
+
phase: "template_autostash",
|
|
211
|
+
command: ["sh", "-lc", template_autostash_command(member)]
|
|
212
|
+
)
|
|
213
|
+
results << stash_result
|
|
214
|
+
break unless stash_result.ok?
|
|
215
|
+
|
|
216
|
+
stash_ref = stash_result.stdout.to_s.lines.last.to_s.strip
|
|
217
|
+
if stash_ref.empty?
|
|
218
|
+
results << template_sync_failure_result(member, "could not determine the temporary template autostash reference")
|
|
219
|
+
break
|
|
220
|
+
end
|
|
221
|
+
stashes << {member: member, ref: stash_ref}
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
upstream = git_upstream_for(member)
|
|
225
|
+
next unless upstream
|
|
226
|
+
|
|
227
|
+
results << runner.call(member: member, phase: "template_sync", command: ["git", "pull", "--rebase"])
|
|
228
|
+
break unless results.last.ok?
|
|
229
|
+
end
|
|
230
|
+
[results, stashes]
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def restore_template_autostashes(stashes, runner:)
|
|
234
|
+
stashes.reverse_each.map do |stash|
|
|
235
|
+
restore = runner.call(
|
|
236
|
+
member: stash.fetch(:member),
|
|
237
|
+
phase: "template_autostash_restore",
|
|
238
|
+
command: ["git", "stash", "pop", stash.fetch(:ref)]
|
|
239
|
+
)
|
|
240
|
+
next restore if restore.ok? || !template_generated_lockfile_only_conflict?(stash.fetch(:member))
|
|
241
|
+
|
|
242
|
+
runner.call(
|
|
243
|
+
member: stash.fetch(:member),
|
|
244
|
+
phase: "template_autostash_generated_lockfile_recovery",
|
|
245
|
+
command: [
|
|
246
|
+
"sh", "-lc",
|
|
247
|
+
"paths=$(git diff --name-only --diff-filter=U); " \
|
|
248
|
+
"git checkout --ours -- $paths && git add -- $paths && " \
|
|
249
|
+
"git stash drop #{Shellwords.escape(stash.fetch(:ref))}"
|
|
250
|
+
]
|
|
251
|
+
)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def template_generated_lockfile_only_conflict?(member)
|
|
256
|
+
stdout, _stderr, status = Open3.capture3("git", "diff", "--name-only", "--diff-filter=U", chdir: member.root)
|
|
257
|
+
conflicts = stdout.lines.map(&:strip).reject(&:empty?).sort
|
|
258
|
+
status.success? && !conflicts.empty? && conflicts.all? { |path| ["Gemfile.lock", ".structuredmerge/kettle-jem.lock"].include?(path) }
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def template_branch_sync_results(branch_members, runner:)
|
|
262
|
+
template_sync_members_for(branch_members).each_with_object([]) do |member, memo|
|
|
263
|
+
next unless git_upstream_for(member)
|
|
264
|
+
|
|
265
|
+
memo << runner.call(member: member, phase: "template_sync", command: ["git", "pull", "--rebase"])
|
|
266
|
+
break memo unless memo.last.ok?
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def template_sync_members
|
|
271
|
+
template_sync_members_for(members)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def template_sync_members_for(candidates)
|
|
275
|
+
candidates.each_with_object([]) do |member, unique_members|
|
|
276
|
+
unique_members << member unless unique_members.any? { |candidate| git_root_for(candidate) == git_root_for(member) }
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def git_upstream_for(member)
|
|
281
|
+
stdout, _stderr, status = Open3.capture3("git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}", chdir: member.root)
|
|
282
|
+
status.success? ? stdout.strip : nil
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def template_autostash_command(member)
|
|
286
|
+
label = "kettle-family-template-#{member.name}-#{Process.pid}-#{Time.now.to_i}"
|
|
287
|
+
"git stash push --include-untracked --message #{Shellwords.escape(label)} && " \
|
|
288
|
+
"ref=$(git stash list -1 --format=%gd) && " \
|
|
289
|
+
"git checkout \"$ref\" -- .tool-versions 2>/dev/null || true; " \
|
|
290
|
+
"for path in mise.toml .mise.toml; do " \
|
|
291
|
+
"if git cat-file -e \"$ref^3:$path\" 2>/dev/null; then git show \"$ref^3:$path\" > \"$path\"; fi; " \
|
|
292
|
+
"done; printf '%s\\n' \"$ref\""
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def template_dirty_worktree_result(member, dirty_paths)
|
|
296
|
+
template_sync_failure_result(
|
|
297
|
+
member,
|
|
298
|
+
[
|
|
299
|
+
"dirty worktree blocks template sync; remove --no-autostash, or commit or stash changes before retrying",
|
|
300
|
+
*dirty_paths
|
|
301
|
+
].join("\n"),
|
|
302
|
+
reason: "dirty worktree blocks template sync"
|
|
303
|
+
)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def template_sync_failure_result(member, message, reason: "template sync failed")
|
|
307
|
+
CommandResult.new(
|
|
308
|
+
member_name: member.name,
|
|
309
|
+
phase: "template_sync_preflight",
|
|
310
|
+
command: ["internal", "template-sync"],
|
|
311
|
+
workdir: member.root,
|
|
312
|
+
status: 1,
|
|
313
|
+
success: false,
|
|
314
|
+
stdout: "",
|
|
315
|
+
stderr: message,
|
|
316
|
+
elapsed_seconds: 0.0,
|
|
317
|
+
skipped: false,
|
|
318
|
+
reason: reason
|
|
319
|
+
)
|
|
320
|
+
end
|
|
166
321
|
|
|
167
322
|
def current_branch_results(workflow_members)
|
|
168
323
|
return check_results(workflow_members) if command == "check"
|
|
@@ -231,8 +386,11 @@ module Kettle
|
|
|
231
386
|
debugger_results = template_debugger_bootstrap_results(workflow_members)
|
|
232
387
|
return debugger_results unless debugger_results.all?(&:ok?)
|
|
233
388
|
|
|
389
|
+
appraisal_results = template_appraisal_bootstrap_results(workflow_members)
|
|
390
|
+
return debugger_results + appraisal_results unless appraisal_results.all?(&:ok?)
|
|
391
|
+
|
|
234
392
|
bootstrap_results = template_bootstrap_dependency_results(workflow_members)
|
|
235
|
-
return debugger_results + bootstrap_results unless bootstrap_results.all?(&:ok?)
|
|
393
|
+
return debugger_results + appraisal_results + bootstrap_results unless bootstrap_results.all?(&:ok?)
|
|
236
394
|
|
|
237
395
|
template_progress = start_template_progress(workflow_members)
|
|
238
396
|
results = template_dependency_waves(workflow_members).each_with_object([]) do |wave, memo|
|
|
@@ -241,7 +399,7 @@ module Kettle
|
|
|
241
399
|
break memo unless wave_results.all?(&:ok?)
|
|
242
400
|
end
|
|
243
401
|
emit_template_progress_summary(results, progress: template_progress)
|
|
244
|
-
debugger_results + bootstrap_results + results
|
|
402
|
+
debugger_results + appraisal_results + bootstrap_results + results
|
|
245
403
|
end
|
|
246
404
|
|
|
247
405
|
def template_debugger_bootstrap_results(workflow_members)
|
|
@@ -253,6 +411,15 @@ module Kettle
|
|
|
253
411
|
[template_bootstrap_failure_result(workflow_members.first, error.message)]
|
|
254
412
|
end
|
|
255
413
|
|
|
414
|
+
def template_appraisal_bootstrap_results(workflow_members)
|
|
415
|
+
bootstrap = AppraisalBootstrap.new(mode: :execute)
|
|
416
|
+
workflow_members.filter_map do |member|
|
|
417
|
+
bootstrap.bootstrap_member(member) if bootstrap.member_needs_bootstrap?(member)
|
|
418
|
+
end
|
|
419
|
+
rescue => error
|
|
420
|
+
[template_bootstrap_failure_result(workflow_members.first, error.message)]
|
|
421
|
+
end
|
|
422
|
+
|
|
256
423
|
def template_results_for_wave(wave, progress:)
|
|
257
424
|
queue = Queue.new
|
|
258
425
|
wave.each_with_index { |member, index| queue << [index, member] }
|
|
@@ -433,6 +600,10 @@ module Kettle
|
|
|
433
600
|
|
|
434
601
|
branch_members = rediscovered_selected_members(selected_names)
|
|
435
602
|
branch_members = members if branch_members.empty?
|
|
603
|
+
if command == "template" && execute
|
|
604
|
+
memo.concat(template_branch_sync_results(branch_members, runner: runner))
|
|
605
|
+
break memo unless memo.last&.ok?
|
|
606
|
+
end
|
|
436
607
|
branch_results = current_branch_results(branch_members)
|
|
437
608
|
tag_branch_results(branch_results, branch)
|
|
438
609
|
memo.concat(branch_results)
|
|
@@ -914,7 +1085,7 @@ module Kettle
|
|
|
914
1085
|
end
|
|
915
1086
|
|
|
916
1087
|
def release_waves(release_members)
|
|
917
|
-
ReleaseWaves.new(members: release_members).waves
|
|
1088
|
+
ReleaseWaves.new(members: release_members, configured_waves: config.release_waves).waves
|
|
918
1089
|
end
|
|
919
1090
|
|
|
920
1091
|
def append_dependency_floor_results(released_members:, dependent_members:, runner:, memo:)
|
|
@@ -1868,6 +2039,7 @@ module Kettle
|
|
|
1868
2039
|
if command == "template"
|
|
1869
2040
|
env["KETTLE_JEM_TEMPLATE_PROFILE"] = config.template_profile if config.template_profile
|
|
1870
2041
|
env["KJ_REPOSITORY_TOPOLOGY"] = config.template_repository_topology if config.template_repository_topology
|
|
2042
|
+
env["KETTLE_JEM_THREAD_WORKERS"] ||= template_thread_worker_budget.to_s
|
|
1871
2043
|
if monorepo_template?
|
|
1872
2044
|
template_git_lock_path = template_git_commit_lock_path
|
|
1873
2045
|
env["KETTLE_JEM_GIT_LOCK"] = template_git_lock_path
|
|
@@ -1951,12 +2123,17 @@ module Kettle
|
|
|
1951
2123
|
prefix = prefix[0...-2] if prefix.last(2) == %w[bundle exec]
|
|
1952
2124
|
prefix + [RbConfig.ruby, executable] + argv[(index + 1)..]
|
|
1953
2125
|
else
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
return
|
|
2126
|
+
argv = Shellwords.split(command_text.to_s)
|
|
2127
|
+
index = argv.index("kettle-jem")
|
|
2128
|
+
return command_text unless index
|
|
2129
|
+
return command_text if argv.any? { |token| %w[&& || ; |].include?(token) }
|
|
1957
2130
|
|
|
1958
|
-
|
|
2131
|
+
prefix = argv[0...index]
|
|
2132
|
+
prefix = prefix[0...-2] if prefix.last(2) == %w[bundle exec]
|
|
2133
|
+
prefix + [RbConfig.ruby, executable] + argv[(index + 1)..]
|
|
1959
2134
|
end
|
|
2135
|
+
rescue ArgumentError
|
|
2136
|
+
command_text
|
|
1960
2137
|
end
|
|
1961
2138
|
|
|
1962
2139
|
def local_kettle_jem_executable
|
|
@@ -2795,6 +2972,11 @@ module Kettle
|
|
|
2795
2972
|
env
|
|
2796
2973
|
end
|
|
2797
2974
|
|
|
2975
|
+
def template_thread_worker_budget
|
|
2976
|
+
member_jobs = template_jobs(members)
|
|
2977
|
+
[1, (Etc.nprocessors / member_jobs) - 1].max
|
|
2978
|
+
end
|
|
2979
|
+
|
|
2798
2980
|
def normalize_release_lockfiles(member:, runner:, memo:)
|
|
2799
2981
|
result = runner.call(
|
|
2800
2982
|
member: member,
|
data/lib/kettle/family.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "family/version"
|
|
4
3
|
require_relative "family/member"
|
|
5
4
|
require_relative "family/command_result"
|
|
6
5
|
require_relative "family/release_state_result"
|
|
@@ -18,6 +17,7 @@ require_relative "family/unreleased_gem_cleanup"
|
|
|
18
17
|
require_relative "family/local_install"
|
|
19
18
|
require_relative "family/nomono_bootstrap"
|
|
20
19
|
require_relative "family/debugger_bootstrap"
|
|
20
|
+
require_relative "family/appraisal_bootstrap"
|
|
21
21
|
require_relative "family/release_waves"
|
|
22
22
|
require_relative "family/workflow"
|
|
23
23
|
require_relative "family/discovery"
|
|
@@ -25,6 +25,7 @@ require_relative "family/orderer"
|
|
|
25
25
|
require_relative "family/selection"
|
|
26
26
|
require_relative "family/report"
|
|
27
27
|
require_relative "family/cli"
|
|
28
|
+
require_relative "family/version"
|
|
28
29
|
|
|
29
30
|
module Kettle
|
|
30
31
|
module Family
|
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: 1.2.
|
|
4
|
+
version: 1.2.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -74,7 +74,7 @@ dependencies:
|
|
|
74
74
|
version: '2.3'
|
|
75
75
|
- - ">="
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 2.5.
|
|
77
|
+
version: 2.5.16
|
|
78
78
|
type: :runtime
|
|
79
79
|
prerelease: false
|
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -84,7 +84,7 @@ dependencies:
|
|
|
84
84
|
version: '2.3'
|
|
85
85
|
- - ">="
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: 2.5.
|
|
87
|
+
version: 2.5.16
|
|
88
88
|
- !ruby/object:Gem::Dependency
|
|
89
89
|
name: kettle-test
|
|
90
90
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,7 +94,7 @@ dependencies:
|
|
|
94
94
|
version: '2.0'
|
|
95
95
|
- - ">="
|
|
96
96
|
- !ruby/object:Gem::Version
|
|
97
|
-
version: 2.0.
|
|
97
|
+
version: 2.0.18
|
|
98
98
|
type: :runtime
|
|
99
99
|
prerelease: false
|
|
100
100
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -104,7 +104,7 @@ dependencies:
|
|
|
104
104
|
version: '2.0'
|
|
105
105
|
- - ">="
|
|
106
106
|
- !ruby/object:Gem::Version
|
|
107
|
-
version: 2.0.
|
|
107
|
+
version: 2.0.18
|
|
108
108
|
- !ruby/object:Gem::Dependency
|
|
109
109
|
name: tsort
|
|
110
110
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -309,6 +309,7 @@ files:
|
|
|
309
309
|
- README.md
|
|
310
310
|
- exe/kettle-family
|
|
311
311
|
- lib/kettle/family.rb
|
|
312
|
+
- lib/kettle/family/appraisal_bootstrap.rb
|
|
312
313
|
- lib/kettle/family/branch_lane_audit.rb
|
|
313
314
|
- lib/kettle/family/branch_target_config.rb
|
|
314
315
|
- lib/kettle/family/changelog_check.rb
|
|
@@ -344,10 +345,10 @@ licenses:
|
|
|
344
345
|
- AGPL-3.0-only
|
|
345
346
|
metadata:
|
|
346
347
|
homepage_uri: https://kettle-family.galtzo.com
|
|
347
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.
|
|
348
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.
|
|
348
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.23
|
|
349
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.23/CHANGELOG.md
|
|
349
350
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
350
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.
|
|
351
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.23
|
|
351
352
|
funding_uri: https://github.com/sponsors/pboling
|
|
352
353
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
353
354
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
metadata.gz.sig
CHANGED
|
Binary file
|