kettle-dev 1.1.45 → 1.1.48

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8069b7963c8a87d179dd475edcbb066b6a5c1fd7d322b338819dfb86379aa3c8
4
- data.tar.gz: e6a69655d6c2803aa1e27fe3c91048c2d98f2580260fce709220d09a1f5b0e02
3
+ metadata.gz: 81727729ce153f3e6b055ffc4ec51edb98ac88948e721672ba594d7922073603
4
+ data.tar.gz: 18deca20469a370c4532b82d2f19536807218e31cf52838a96872c5896bc2c23
5
5
  SHA512:
6
- metadata.gz: cc02fca8fc6fc2625d433e6be1c1f16a6165a31af84fa6096c696de8363a887ba3b6398063e9f4996b6d2f6832de0de4e1b584f95ec74434f66d66b5a9525c1f
7
- data.tar.gz: 850ad6fc5a7606081f4da6d77aae2613a15b7b239f1a88478727fdd38aca2176afbf3386868b2668508099e487924791644d5d61870b37cfdb3315f759e45335
6
+ metadata.gz: b1f3dfe80ca9936aee99f3d300c9eaef1a3e9c3c2eb871af63574b130d6a5b2de5c33d083745e50f352e6cfc532c8495c57ae0201cd92c9ed68a74de215befb2
7
+ data.tar.gz: b0396f0e7e0f975ef32256c276bd0fbe7e9026eba2c8685a33c942b480561277c0a9330868b5472a4e2c82d6b37de05d03c5f2eae604c3e933ba8bd6b0728d52
checksums.yaml.gz.sig CHANGED
Binary file
data/.envrc CHANGED
@@ -21,8 +21,8 @@ export K_SOUP_COV_DO=true # Means you want code coverage
21
21
  export K_SOUP_COV_COMMAND_NAME="Test Coverage"
22
22
  # Available formats are html, xml, rcov, lcov, json, tty
23
23
  export K_SOUP_COV_FORMATTERS="html,xml,rcov,lcov,json,tty"
24
- export K_SOUP_COV_MIN_BRANCH=79 # Means you want to enforce X% branch coverage
25
- export K_SOUP_COV_MIN_LINE=95 # Means you want to enforce X% line coverage
24
+ export K_SOUP_COV_MIN_BRANCH=77 # Means you want to enforce X% branch coverage
25
+ export K_SOUP_COV_MIN_LINE=92 # Means you want to enforce X% line coverage
26
26
  export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met
27
27
  export K_SOUP_COV_MULTI_FORMATTERS=true
28
28
  export K_SOUP_COV_OPEN_BIN= # Means don't try to open coverage results in browser
@@ -1,5 +1,5 @@
1
1
  ⚡️ A message from a fellow meat-based-AI ⚡️
2
- - [❤️] Finely-crafted open-source tools like <%= @gem_name %> (& many more) are a full-time endeavor.
2
+ - [❤️] Finely-crafted open-source tools like <%= @gem_name %> (& many more) require time and effort.
3
3
  - [❤️] Though I adore my work, it lacks financial sustainability.
4
4
  - [❤️] Please, help me continue enhancing your tools by becoming a sponsor:
5
5
  - [💲] https://liberapay.com/pboling/donate
@@ -6,9 +6,8 @@ permissions:
6
6
  id-token: write
7
7
 
8
8
  env:
9
- # Lower than local, which is at 100/100, because rubocop-lts isn't installed in the coverage workflow
10
- K_SOUP_COV_MIN_BRANCH: 78
11
- K_SOUP_COV_MIN_LINE: 93
9
+ K_SOUP_COV_MIN_BRANCH: 77
10
+ K_SOUP_COV_MIN_LINE: 92
12
11
  K_SOUP_COV_MIN_HARD: true
13
12
  K_SOUP_COV_FORMATTERS: "xml,rcov,lcov,tty"
14
13
  K_SOUP_COV_DO: true
@@ -116,7 +115,7 @@ jobs:
116
115
  hide_complexity: true
117
116
  indicators: true
118
117
  output: both
119
- thresholds: '93 78'
118
+ thresholds: '92 77'
120
119
  continue-on-error: ${{ matrix.experimental != 'false' }}
121
120
 
122
121
  - name: Add Coverage PR Comment
@@ -63,3 +63,5 @@ jobs:
63
63
  run: bundle exec appraisal ${{ matrix.appraisal }} bundle
64
64
  - name: Run ${{ matrix.appraisal }} checks via ${{ matrix.exec_cmd }}
65
65
  run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }}
66
+ - name: Validate RBS Types
67
+ run: bundle exec appraisal ${{ matrix.appraisal }} bin/rbs validate
data/Appraisals CHANGED
@@ -136,6 +136,8 @@ appraise "coverage" do
136
136
  eval_gemfile "modular/optional.gemfile"
137
137
  eval_gemfile "modular/recording/r3/recording.gemfile"
138
138
  eval_gemfile "modular/x_std_libs.gemfile"
139
+ # Normally style is included in coverage runs only, but we need it for the test suite to get full coverage
140
+ eval_gemfile "modular/style.gemfile"
139
141
  # Dependencies injected by the kettle-dev-setup script & kettle:dev:install rake task
140
142
  # eval_gemfile "modular/injected.gemfile"
141
143
  end
data/CHANGELOG.md CHANGED
@@ -30,6 +30,49 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.1.48] - 2025-11-06
34
+
35
+ - TAG: [v1.1.48][1.1.48t]
36
+ - COVERAGE: 94.39% -- 4038/4278 lines in 26 files
37
+ - BRANCH COVERAGE: 78.93% -- 1663/2107 branches in 26 files
38
+ - 79.89% documented
39
+
40
+ ### Fixed
41
+
42
+ - Typo in markdown link
43
+ - Handling of pre-existing gemfile
44
+
45
+ ## [1.1.47] - 2025-11-06
46
+
47
+ - TAG: [v1.1.47][1.1.47t]
48
+ - COVERAGE: 95.68% -- 4054/4237 lines in 26 files
49
+ - BRANCH COVERAGE: 80.45% -- 1675/2082 branches in 26 files
50
+ - 79.89% documented
51
+
52
+ ### Added
53
+
54
+ - Handle custom dependencies in Gemfiles gracefully
55
+ - Intelligent templating of Appraisals
56
+
57
+ ### Fixed
58
+
59
+ - Typos in funding links
60
+
61
+ ## [1.1.46] - 2025-11-04
62
+
63
+ - TAG: [v1.1.46][1.1.46t]
64
+ - COVERAGE: 96.25% -- 3958/4112 lines in 26 files
65
+ - BRANCH COVERAGE: 80.95% -- 1636/2021 branches in 26 files
66
+ - 79.68% documented
67
+
68
+ ### Added
69
+
70
+ - Validate RBS Types within style workflow
71
+
72
+ ### Fixed
73
+
74
+ - typos in README.md
75
+
33
76
  ## [1.1.45] - 2025-10-31
34
77
 
35
78
  - TAG: [v1.1.45][1.1.45t]
@@ -1252,7 +1295,13 @@ Please file a bug if you notice a violation of semantic versioning.
1252
1295
  - Selecting will run the selected workflow via `act`
1253
1296
  - This may move to its own gem in the future.
1254
1297
 
1255
- [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.45...HEAD
1298
+ [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.48...HEAD
1299
+ [1.1.48]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.47...v1.1.48
1300
+ [1.1.48t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.48
1301
+ [1.1.47]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.46...v1.1.47
1302
+ [1.1.47t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.47
1303
+ [1.1.46]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.45...v1.1.46
1304
+ [1.1.46t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.46
1256
1305
  [1.1.45]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.44...v1.1.45
1257
1306
  [1.1.45t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.45
1258
1307
  [1.1.44]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.43...v1.1.44
data/FUNDING.md CHANGED
@@ -6,7 +6,7 @@ Many paths lead to being a sponsor or a backer of this project. Are you on such
6
6
 
7
7
  [![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]
8
8
 
9
- [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
9
+ [![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]
10
10
 
11
11
  [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
12
12
  [⛳liberapay]: https://liberapay.com/pboling/donate
@@ -46,7 +46,7 @@ To join the community or get help 👇️ Join the Discord.
46
46
 
47
47
  To say "thanks for maintaining such a great tool" ☝️ Join the Discord or 👇️ send money.
48
48
 
49
- [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
49
+ [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
50
50
 
51
51
  # Another Way to Support Open Source Software
52
52
 
@@ -6,7 +6,7 @@ Many paths lead to being a sponsor or a backer of this project. Are you on such
6
6
 
7
7
  [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal]
8
8
 
9
- [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
9
+ [![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]
10
10
 
11
11
  [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
12
12
  [⛳liberapay]: https://liberapay.com/pboling/donate
data/README.md CHANGED
@@ -652,7 +652,7 @@ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed
652
652
 
653
653
  **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
654
654
 
655
- [![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 or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
655
+ [![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]
656
656
 
657
657
  ## 🔐 Security
658
658
 
@@ -779,7 +779,7 @@ To join the community or get help 👇️ Join the Discord.
779
779
 
780
780
  To say "thanks!" ☝️ Join the Discord or 👇️ send money.
781
781
 
782
- [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
782
+ [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
783
783
 
784
784
  ### Please give the project a star ⭐ ♥.
785
785
 
@@ -964,10 +964,10 @@ Thanks for RTFM. ☺️
964
964
  [📌changelog]: CHANGELOG.md
965
965
  [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
966
966
  [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
967
- [📌gitmoji]:https://gitmoji.dev
968
- [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
967
+ [📌gitmoji]: https://gitmoji.dev
968
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
969
969
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
970
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.112-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
970
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.278-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
971
971
  [🔐security]: SECURITY.md
972
972
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
973
973
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
data/README.md.example CHANGED
@@ -225,7 +225,7 @@ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed
225
225
 
226
226
  **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
227
227
 
228
- [![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 or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
228
+ [![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]
229
229
 
230
230
  ## 🔐 Security
231
231
 
@@ -352,7 +352,7 @@ To join the community or get help 👇️ Join the Discord.
352
352
 
353
353
  To say "thanks!" ☝️ Join the Discord or 👇️ send money.
354
354
 
355
- [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
355
+ [![Sponsor kettle-rb/kettle-dev on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
356
356
 
357
357
  ### Please give the project a star ⭐ ♥.
358
358
 
@@ -542,10 +542,10 @@ Thanks for RTFM. ☺️
542
542
  [📌changelog]: CHANGELOG.md
543
543
  [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
544
544
  [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
545
- [📌gitmoji]:https://gitmoji.dev
546
- [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
545
+ [📌gitmoji]: https://gitmoji.dev
546
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
547
547
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
548
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.112-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
548
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.278-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
549
549
  [🔐security]: SECURITY.md
550
550
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
551
551
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -202,7 +202,7 @@ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed
202
202
 
203
203
  **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
204
204
 
205
- [![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 or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
205
+ [![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]
206
206
 
207
207
  ## 🔐 Security
208
208
 
@@ -329,7 +329,7 @@ To join the community or get help 👇️ Join the Discord.
329
329
 
330
330
  To say "thanks!" ☝️ Join the Discord or 👇️ send money.
331
331
 
332
- [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
332
+ [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
333
333
 
334
334
  ### Please give the project a star ⭐ ♥.
335
335
 
@@ -508,8 +508,8 @@ Thanks for RTFM. ☺️
508
508
  [📌changelog]: CHANGELOG.md
509
509
  [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
510
510
  [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
511
- [📌gitmoji]:https://gitmoji.dev
512
- [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
511
+ [📌gitmoji]: https://gitmoji.dev
512
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
513
513
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
514
514
  [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.076-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
515
515
  [🔐security]: SECURITY.md
data/Rakefile.example CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # kettle-dev Rakefile v1.1.45 - 2025-10-31
3
+ # kettle-dev Rakefile v1.1.48 - 2025-11-06
4
4
  # Ruby 2.3 (Safe Navigation) or higher required
5
5
  #
6
6
  # MIT License (see License.txt)
@@ -2,6 +2,7 @@
2
2
 
3
3
  # External stdlibs
4
4
  require "find"
5
+ require "set"
5
6
 
6
7
  module Kettle
7
8
  module Dev
@@ -277,6 +278,46 @@ module Kettle
277
278
  Kettle::Dev.debug_error(e, __method__)
278
279
  # If replacement fails unexpectedly, proceed with content as-is
279
280
  end
281
+
282
+ # If updating the Appraisals file and a destination already exists,
283
+ # merge appraise blocks: augment matching blocks with missing gem/eval_gemfile lines,
284
+ # preserve destination-only blocks and comments/preamble.
285
+ begin
286
+ if dest_exists && File.basename(dest_path.to_s) == "Appraisals" && File.file?(dest_path.to_s)
287
+ existing = begin
288
+ File.read(dest_path)
289
+ rescue
290
+ ""
291
+ end
292
+ content = merge_appraisals(content, existing)
293
+ end
294
+ rescue StandardError => e
295
+ Kettle::Dev.debug_error(e, __method__)
296
+ # On any error, fall back to generated content
297
+ end
298
+
299
+ # If updating a Gemfile or modular .gemfile and the destination already exists,
300
+ # merge dependency lines from the source into the destination to preserve any
301
+ # user-defined gem entries. We append missing `gem "name"` lines; we never
302
+ # alter or remove existing gem lines in the destination.
303
+ begin
304
+ if dest_exists
305
+ dest_str = dest_path.to_s
306
+ is_gemfile_like = File.basename(dest_str) == "Gemfile" || dest_str.end_with?(".gemfile")
307
+ if is_gemfile_like && File.file?(dest_str)
308
+ begin
309
+ existing = File.read(dest_str)
310
+ content = merge_gemfile_dependencies(content, existing)
311
+ rescue StandardError => e
312
+ Kettle::Dev.debug_error(e, __method__)
313
+ # If merging fails, fall back to writing generated content
314
+ end
315
+ end
316
+ end
317
+ rescue StandardError => e
318
+ Kettle::Dev.debug_error(e, __method__)
319
+ end
320
+
280
321
  write_file(dest_path, content)
281
322
  begin
282
323
  # Ensure executable bit for git hook scripts when writing under .git-hooks
@@ -291,6 +332,272 @@ module Kettle
291
332
  puts "Wrote #{dest_path}"
292
333
  end
293
334
 
335
+ # Merge gem dependency lines from a source Gemfile-like content into an existing
336
+ # destination Gemfile-like content. Existing gem lines in the destination win;
337
+ # we only append missing gem declarations from the source at the end of the file.
338
+ # This is deliberately conservative and avoids attempting to relocate gems inside
339
+ # group/platform blocks or reconcile version constraints.
340
+ # @param src_content [String]
341
+ # @param dest_content [String]
342
+ # @return [String] merged content
343
+ def merge_gemfile_dependencies(src_content, dest_content)
344
+ begin
345
+ gem_re = /^\s*gem\s+['"]([^'"\s]+)['"].*$/
346
+ # Collect first occurrence of each gem line in source
347
+ src_gems = {}
348
+ src_content.each_line do |ln|
349
+ next if ln.strip.start_with?("#")
350
+ if (m = ln.match(gem_re))
351
+ name = m[1]
352
+ src_gems[name] ||= ln.rstrip
353
+ end
354
+ end
355
+
356
+ # --- Handle `source` replacement/insertion ---
357
+ src_source_line = nil
358
+ src_content.each_line do |ln|
359
+ next if ln.strip.start_with?("#")
360
+ if ln =~ /^\s*source\s+['"][^'"]+['"]\s*$/
361
+ src_source_line = ln.rstrip + "\n"
362
+ break
363
+ end
364
+ end
365
+
366
+ dest_lines = dest_content.lines.dup
367
+
368
+ if src_source_line
369
+ dest_source_idx = dest_lines.index do |ln|
370
+ !ln.strip.start_with?("#") && ln =~ /^\s*source\s+['"][^'"]+['"]\s*$/
371
+ end
372
+ if dest_source_idx
373
+ dest_lines[dest_source_idx] = src_source_line
374
+ else
375
+ # Insert after any leading contiguous comment/blank block at top of file
376
+ insert_idx = 0
377
+ while insert_idx < dest_lines.length && (dest_lines[insert_idx].strip.empty? || dest_lines[insert_idx].lstrip.start_with?("#"))
378
+ insert_idx += 1
379
+ end
380
+ dest_lines.insert(insert_idx, src_source_line)
381
+ end
382
+ end
383
+
384
+ # --- Handle `git_source` replacement/insertion ---
385
+ # Collect non-comment git_source lines from source (preserve order)
386
+ src_git_lines = src_content.each_line.select { |ln| !ln.strip.start_with?("#") && ln =~ /^\s*git_source\s*\(/ }
387
+ if src_git_lines.any?
388
+ # Insert new git_source lines in the same order as they appear in the source
389
+ # When inserting (not replacing), place them immediately after the source line if present
390
+ insert_after_source_idx = dest_lines.index { |ln| !ln.strip.start_with?("#") && ln =~ /^\s*source\s+['"][^'"]+['"]\s*$/ }
391
+
392
+ # Iterate source git lines in reverse for insertion so order is preserved when inserting at same index
393
+ src_git_lines.reverse_each do |gln|
394
+ # Attempt to extract the git_source "name" (handles forms like git_source(:github) or git_source :github)
395
+ name_match = gln.match(/^\s*git_source\s*\(?\s*:?(\w+)\b/)
396
+ name = name_match ? name_match[1].to_s : nil
397
+
398
+ replaced = false
399
+ if name
400
+ # Try to find a git_source in destination with the same name
401
+ dest_same_idx = dest_lines.index do |dln|
402
+ !dln.strip.start_with?("#") && dln =~ /^\s*git_source\s*\(?\s*:?#{Regexp.escape(name)}\b/
403
+ end
404
+ if dest_same_idx
405
+ dest_lines[dest_same_idx] = gln.rstrip + "\n"
406
+ replaced = true
407
+ end
408
+ end
409
+
410
+ unless replaced
411
+ # If destination has a github git_source, replace that
412
+ dest_github_idx = dest_lines.index do |dln|
413
+ !dln.strip.start_with?("#") && dln =~ /^\s*git_source\s*\(?\s*:?github\b/
414
+ end
415
+ if dest_github_idx
416
+ dest_lines[dest_github_idx] = gln.rstrip + "\n"
417
+ else
418
+ # Insert below the source line if present, otherwise at top (after comments)
419
+ insert_idx =
420
+ if insert_after_source_idx
421
+ insert_after_source_idx + 1
422
+ else
423
+ 0
424
+ end
425
+ dest_lines.insert(insert_idx, gln.rstrip + "\n")
426
+ end
427
+ end
428
+ end
429
+ end
430
+
431
+ # Index existing gems in destination (after potential source/git_source changes)
432
+ dest_gems = {}
433
+ dest_lines.join.each_line do |ln|
434
+ next if ln.strip.start_with?("#")
435
+ if (m = ln.match(gem_re))
436
+ dest_gems[m[1]] = true
437
+ end
438
+ end
439
+
440
+ missing = src_gems.keys.reject { |n| dest_gems.key?(n) }
441
+ # If nothing to change, return original destination content
442
+ if missing.empty? && src_source_line.nil? && src_git_lines.empty?
443
+ return dest_content
444
+ end
445
+
446
+ out = dest_lines.join
447
+ out << "\n" unless out.end_with?("\n") || out.empty?
448
+ if missing.any?
449
+ out << missing.map { |n| src_gems[n] }.join("\n")
450
+ out << "\n"
451
+ end
452
+ out
453
+ rescue StandardError => e
454
+ Kettle::Dev.debug_error(e, __method__)
455
+ dest_content
456
+ end
457
+ end
458
+
459
+ # Merge Appraisals template into existing Appraisals file.
460
+ # Rules:
461
+ # - For each appraise "name" block in template:
462
+ # * If destination has same block, ensure all gem/ eval_gemfile lines from template
463
+ # exist in destination (append missing just before end), keep other dest lines.
464
+ # Use template's contiguous header comment lines (immediately preceding the appraise line)
465
+ # if any; otherwise retain destination's header comments.
466
+ # * If destination lacks the block, add the full template block (with its header).
467
+ # - Preserve destination-only blocks (not present in template) unchanged and after
468
+ # the merged template-ordered blocks.
469
+ # - Preamble (content before first appraise) comes from template when present, else destination.
470
+ def merge_appraisals(template_content, dest_content)
471
+ begin
472
+ parse_blocks = lambda do |text|
473
+ lines = text.lines
474
+ blocks = []
475
+ i = 0
476
+ while i < lines.length
477
+ line = lines[i]
478
+ if line =~ /^\s*appraise\s+["']([^"']+)["']\s+do\s*$/
479
+ name = $1
480
+ # collect header comment lines immediately preceding (contiguous, no blank between comment group and appraise line)
481
+ header_lines = []
482
+ j = i - 1
483
+ while j >= 0
484
+ prev = lines[j]
485
+ break if prev.strip.empty?
486
+ if prev.lstrip.start_with?("#")
487
+ header_lines.unshift(prev)
488
+ j -= 1
489
+ else
490
+ break
491
+ end
492
+ end
493
+ body_lines = []
494
+ i += 1
495
+ while i < lines.length
496
+ l2 = lines[i]
497
+ if l2 =~ /^\s*end\s*$/
498
+ end_line = l2
499
+ blocks << {
500
+ name: name,
501
+ header: header_lines.dup,
502
+ body: body_lines.dup,
503
+ end_line: end_line,
504
+ raw_order: blocks.length,
505
+ original_indices: (j ? (j + 1)..i : i),
506
+ }
507
+ break
508
+ else
509
+ body_lines << l2
510
+ end
511
+ i += 1
512
+ end
513
+ end
514
+ i += 1
515
+ end
516
+ preamble = if blocks.empty?
517
+ text
518
+ else
519
+ # preamble = lines from start up to first block start (exclusive)
520
+ first_block = blocks.first
521
+ # Take lines up to first occurrence of the appraise line (supports either quote type)
522
+ re = /^\s*appraise\s+["']#{Regexp.escape(first_block[:name])}["']\s+do\s*$/
523
+ idx = lines.index { |l| l =~ re } || 0
524
+ lines[0...idx].join
525
+ end
526
+ {blocks: blocks, preamble: preamble}
527
+ end
528
+
529
+ tmpl = parse_blocks.call(template_content)
530
+ dest = parse_blocks.call(dest_content)
531
+ tmpl_blocks = tmpl[:blocks]
532
+ dest_blocks = dest[:blocks]
533
+ dest_by_name = dest_blocks.map { |b| [b[:name], b] }.to_h
534
+
535
+ merged_blocks_strings = []
536
+ gem_or_eval_re = /^\s*(?:gem|eval_gemfile)\b/
537
+
538
+ tmpl_blocks.each do |tb|
539
+ if (db = dest_by_name[tb[:name]])
540
+ # Merge lines
541
+ existing_lines = db[:body].map(&:rstrip)
542
+ existing_set = existing_lines.to_set
543
+ # Collect template gem/eval lines
544
+ tmpl_needed = tb[:body].select { |l| gem_or_eval_re =~ l }
545
+ additions = []
546
+ tmpl_needed.each do |l|
547
+ line_key = l.rstrip
548
+ additions << l unless existing_set.include?(line_key)
549
+ end
550
+ merged_body = db[:body].dup
551
+ unless additions.empty?
552
+ # insert before end (just append; body excludes 'end')
553
+ merged_body += additions
554
+ end
555
+ header = tb[:header].any? ? tb[:header] : db[:header]
556
+ block_text = +""
557
+ block_text << "\n" unless merged_blocks_strings.empty?
558
+ header.each { |hl| block_text << hl } if header.any?
559
+ block_text << "appraise \"#{tb[:name]}\" do\n"
560
+ merged_body.each { |bl| block_text << bl }
561
+ block_text << db[:end_line]
562
+ merged_blocks_strings << block_text
563
+ dest_by_name.delete(tb[:name])
564
+ else
565
+ # New block from template
566
+ block_text = +""
567
+ block_text << "\n" unless merged_blocks_strings.empty?
568
+ tb[:header].each { |hl| block_text << hl } if tb[:header].any?
569
+ block_text << "appraise \"#{tb[:name]}\" do\n"
570
+ tb[:body].each { |bl| block_text << bl }
571
+ block_text << tb[:end_line]
572
+ merged_blocks_strings << block_text
573
+ end
574
+ end
575
+ # Append destination-only blocks preserving their original text
576
+ dest_remaining_order = dest_blocks.select { |b| dest_by_name.key?(b[:name]) }
577
+ dest_remaining_order.each do |b|
578
+ block_text = +""
579
+ block_text << "\n" unless merged_blocks_strings.empty?
580
+ b[:header].each { |hl| block_text << hl } if b[:header].any?
581
+ block_text << "appraise \"#{b[:name]}\" do\n"
582
+ b[:body].each { |bl| block_text << bl }
583
+ block_text << b[:end_line]
584
+ merged_blocks_strings << block_text
585
+ end
586
+
587
+ preamble = tmpl[:preamble].to_s.strip.empty? ? dest[:preamble] : tmpl[:preamble]
588
+ out = +""
589
+ out << preamble unless preamble.nil? || preamble.empty?
590
+ out << "\n" unless out.end_with?("\n")
591
+ out << merged_blocks_strings.join
592
+ out << "\n" unless out.end_with?("\n")
593
+ out
594
+ rescue StandardError => e
595
+ Kettle::Dev.debug_error(e, __method__)
596
+ # Fallback: prefer destination (user changes) and append template content to allow manual reconciliation
597
+ dest_content + "\n# --- TEMPLATE APPRAISALS (unmerged) ---\n" + template_content
598
+ end
599
+ end
600
+
294
601
  # Copy a directory tree, prompting before creating or overwriting.
295
602
  # @return [void]
296
603
  def copy_dir_with_prompt(src_dir, dest_dir)
@@ -6,7 +6,7 @@ module Kettle
6
6
  module Version
7
7
  # The gem version.
8
8
  # @return [String]
9
- VERSION = "1.1.45"
9
+ VERSION = "1.1.48"
10
10
 
11
11
  module_function
12
12
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.45
4
+ version: 1.1.48
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -396,10 +396,10 @@ licenses:
396
396
  - MIT
397
397
  metadata:
398
398
  homepage_uri: https://kettle-dev.galtzo.com/
399
- source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.1.45
400
- changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.1.45/CHANGELOG.md
399
+ source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.1.48
400
+ changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.1.48/CHANGELOG.md
401
401
  bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
402
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.45
402
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.48
403
403
  funding_uri: https://github.com/sponsors/pboling
404
404
  wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
405
405
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file