kettle-dev 1.0.21 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c207c08e4e9933149728224c355077d0023be3794639f3e9eae9132207fb403b
4
- data.tar.gz: 82c963e4d47084fdce04e9951e46b77109d782c2d206cb46b39f0f8965509a6e
3
+ metadata.gz: adce6d5a19e8f845c579e554910e6a0fbb1c8ad36447128a4d19029109dde152
4
+ data.tar.gz: b7bc5ec30e0427a941de9bff8b1fc859aa36f0f0d5ff30a21480f156f78943d1
5
5
  SHA512:
6
- metadata.gz: 422cd88655f41ceff9b98af93024ae86d107f0311238bd79630c88e576b4fc8b0e4023bd4804ec58a334e43a0c89dd21a1fc6ac4e410786298d7ed688ee28317
7
- data.tar.gz: 36ee1bd93cc0c8422b292bbd616b277cba903feb5194cdca82167f3e4e40cce086860f76f7a5c8bb748e36e329851abbe843ebcb047cc6aed7e0ae073e80b829
6
+ metadata.gz: 137aeb6d2e1fd6a8856093119abd2c6540df36cc1b3863a5e2ba63b959592d919521fd73e9618323b18aed4c77791925721826d4be3f9af69b39cd3d6089d838
7
+ data.tar.gz: fb2396ed69522155e4fe03f0e763b75ae6fd187ec34975c0fda67a90ad4167cbff38c86233ac4fc95352fa8f96da191272f98a66d23ecec4abb3e0263b05f095
checksums.yaml.gz.sig CHANGED
Binary file
@@ -77,9 +77,21 @@ jobs:
77
77
  # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root)
78
78
  # We need to do this first to get appraisal installed.
79
79
  # NOTE: This does not use the primary Gemfile at all.
80
- - name: Install Root Appraisal
80
+ - name: "Install Root Appraisal"
81
81
  run: bundle
82
- - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}
82
+
83
+ - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
84
+ id: bundleAttempt1
85
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle
86
+ # Continue to the next step on failure
87
+ continue-on-error: true
88
+
89
+ # Effectively an automatic retry of the previous step.
90
+ - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
91
+ id: bundleAttempt2
92
+ # If bundleAttempt1 failed, try again here; Otherwise skip.
93
+ if: steps.bundleAttempt1.outcome == 'failure'
83
94
  run: bundle exec appraisal ${{ matrix.appraisal }} bundle
95
+
84
96
  - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }}
85
97
  run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }}
@@ -0,0 +1,97 @@
1
+ name: Heads
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ env:
7
+ K_SOUP_COV_DO: false
8
+
9
+ on:
10
+ push:
11
+ branches:
12
+ - 'main'
13
+ - '*-stable'
14
+ tags:
15
+ - '!*' # Do not execute on tags
16
+ pull_request:
17
+ branches:
18
+ - '*'
19
+ # Allow manually triggering the workflow.
20
+ workflow_dispatch:
21
+
22
+ # Cancels all previous workflow runs for the same branch that have not yet completed.
23
+ concurrency:
24
+ # The concurrency group contains the workflow name and the branch name.
25
+ group: "${{ github.workflow }}-${{ github.ref }}"
26
+ cancel-in-progress: true
27
+
28
+ jobs:
29
+ test:
30
+ if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')"
31
+ name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }}${{ matrix.name_extra || '' }}
32
+ runs-on: ubuntu-latest
33
+ continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }}
34
+ env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps
35
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile
36
+ strategy:
37
+ fail-fast: true
38
+ matrix:
39
+ include:
40
+ # NOTE: Heads use default rubygems / bundler; their defaults are custom, unreleased, and from the future!
41
+ # ruby-head
42
+ - ruby: "ruby-head"
43
+ appraisal: "head"
44
+ exec_cmd: "rake test"
45
+ gemfile: "Appraisal.root"
46
+ rubygems: default
47
+ bundler: default
48
+
49
+ # truffleruby-head
50
+ - ruby: "truffleruby-head"
51
+ appraisal: "head"
52
+ exec_cmd: "rake test"
53
+ gemfile: "Appraisal.root"
54
+ rubygems: default
55
+ bundler: default
56
+
57
+ # jruby-head
58
+ - ruby: "jruby-head"
59
+ appraisal: "head"
60
+ exec_cmd: "rake test"
61
+ gemfile: "Appraisal.root"
62
+ rubygems: default
63
+ bundler: default
64
+
65
+ steps:
66
+ - name: Checkout
67
+ uses: actions/checkout@v5
68
+
69
+ - name: Setup Ruby & RubyGems
70
+ uses: ruby/setup-ruby@v1
71
+ with:
72
+ ruby-version: ${{ matrix.ruby }}
73
+ rubygems: ${{ matrix.rubygems }}
74
+ bundler: ${{ matrix.bundler }}
75
+ bundler-cache: false
76
+
77
+ # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root)
78
+ # We need to do this first to get appraisal installed.
79
+ # NOTE: This does not use the primary Gemfile at all.
80
+ - name: "Install Root Appraisal"
81
+ run: bundle
82
+
83
+ - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
84
+ id: bundleAttempt1
85
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle
86
+ # Continue to the next step on failure
87
+ continue-on-error: true
88
+
89
+ # Effectively an automatic retry of the previous step.
90
+ - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
91
+ id: bundleAttempt2
92
+ # If bundleAttempt1 failed, try again here; Otherwise skip.
93
+ if: steps.bundleAttempt1.outcome == 'failure'
94
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle
95
+
96
+ - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }}
97
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }}
data/Appraisals CHANGED
@@ -32,6 +32,8 @@ end
32
32
  # Split into discrete appraisals if one of them needs a dependency locked discretely.
33
33
  appraise "head" do
34
34
  gem "erb"
35
+ # See: https://github.com/vcr/vcr/issues/1057
36
+ gem "cgi", ">= 0.5"
35
37
  gem "mutex_m", ">= 0.2"
36
38
  gem "stringio", ">= 3.0"
37
39
  gem "benchmark", "~> 0.4", ">= 0.4.1"
data/CHANGELOG.md CHANGED
@@ -24,6 +24,29 @@ Please file a bug if you notice a violation of semantic versioning.
24
24
  ### Fixed
25
25
  ### Security
26
26
 
27
+ ## [1.0.23] - 2025-08-30
28
+ - TAG: [v1.0.23][1.0.23t]
29
+ - COVERAGE: 97.75% -- 2428/2484 lines in 21 files
30
+ - BRANCH COVERAGE: 81.76% -- 1013/1239 branches in 21 files
31
+ - 76.00% documented
32
+ ### Added
33
+ - Carryover important fields from the original gemspec during templating
34
+ - refactor gemspec parsing
35
+ - normalize template gemspec data
36
+ ### Fixed
37
+ - include FUNDING.md in the released gem package
38
+ - typo of required_ruby_version
39
+
40
+ ## [1.0.22] - 2025-08-30
41
+ - TAG: [v1.0.22][1.0.22t]
42
+ - COVERAGE: 97.82% -- 2375/2428 lines in 20 files
43
+ - BRANCH COVERAGE: 81.34% -- 972/1195 branches in 20 files
44
+ - 76.23% documented
45
+ ### Added
46
+ - improved documentation
47
+ - example version of heads workflow
48
+ - give heads two attempts to succeed
49
+
27
50
  ## [1.0.21] - 2025-08-30
28
51
  - TAG: [v1.0.21][1.0.21t]
29
52
  - COVERAGE: 97.82% -- 2375/2428 lines in 20 files
@@ -328,7 +351,7 @@ Please file a bug if you notice a violation of semantic versioning.
328
351
  - Selecting will run the selected workflow via `act`
329
352
  - This may move to its own gem in the future.
330
353
 
331
- [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.21...HEAD
354
+ [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.23...HEAD
332
355
  [1.0.0]: https://github.com/kettle-rb/kettle-dev/compare/a427c302df09cfe4253a7c8d400333f9a4c1a208...v1.0.0
333
356
  [1.0.0t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.0
334
357
  [1.0.1]: https://gitlab.com/kettle-rb/kettle-dev/-/compare/v1.0.0...v1.0.1
@@ -373,3 +396,7 @@ Please file a bug if you notice a violation of semantic versioning.
373
396
  [1.0.20t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.20
374
397
  [1.0.21]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.20...v1.0.21
375
398
  [1.0.21t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.21
399
+ [1.0.22]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.21...v1.0.22
400
+ [1.0.22t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.22
401
+ [1.0.23]: https://github.com/kettle-rb/kettle-dev/compare/v1.0.22...v1.0.23
402
+ [1.0.23t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.0.23
data/FUNDING.md ADDED
@@ -0,0 +1,77 @@
1
+ <!-- RELEASE-NOTES-FOOTER-START -->
2
+
3
+ Official Discord 👉️ [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite]
4
+
5
+ Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
6
+
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
+
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]
10
+
11
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
12
+ [⛳liberapay]: https://liberapay.com/pboling/donate
13
+ [🖇osc-backers]: https://opencollective.com/kettle-rb#backer
14
+ [🖇osc-backers-i]: https://opencollective.com/kettle-rb/backers/badge.svg?style=flat
15
+ [🖇osc-sponsors]: https://opencollective.com/kettle-rb#sponsor
16
+ [🖇osc-sponsors-i]: https://opencollective.com/kettle-rb/sponsors/badge.svg?style=flat
17
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
18
+ [🖇sponsor]: https://github.com/sponsors/pboling
19
+ [🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat
20
+ [🖇polar]: https://polar.sh/pboling
21
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-✓-a51611.svg?style=flat
22
+ [🖇kofi]: https://ko-fi.com/O5O86SNP4
23
+ [🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat
24
+ [🖇patreon]: https://patreon.com/galtzo
25
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-✓-a51611.svg?style=flat
26
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
27
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
28
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
29
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
30
+ [✉️discord-invite-img]: https://img.shields.io/discord/1373797679469170758?style=flat
31
+
32
+ <!-- RELEASE-NOTES-FOOTER-END -->
33
+
34
+ # 🤑 Request for Help
35
+
36
+ Maintainers have teeth and need to pay their dentists.
37
+ After getting laid off in an RIF in March and filled with many dozens of rejections,
38
+ I'm now spending ~60+ hours a week building open source tools.
39
+ I'm hoping to be able to pay for my kids' health insurance this month,
40
+ so if you value the work I am doing, I need your support.
41
+ Please consider sponsoring me or the project.
42
+
43
+ To join the community or get help 👇️ Join the Discord.
44
+
45
+ [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
46
+
47
+ To say "thanks for maintaining such a great tool" ☝️ Join the Discord or 👇️ send money.
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]
50
+
51
+ # Another Way to Support Open Source Software
52
+
53
+ > How wonderful it is that nobody need wait a single moment before starting to improve the world.<br/>
54
+ >—Anne Frank
55
+
56
+ I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
57
+
58
+ If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
59
+
60
+ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
61
+
62
+ **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
63
+
64
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
65
+ [🖇osc-all-img]: https://img.shields.io/opencollective/all/kettle-rb
66
+ [🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/kettle-rb
67
+ [🖇osc-backers-img]: https://img.shields.io/opencollective/backers/kettle-rb
68
+ [🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/kettle-rb?style=for-the-badge
69
+ [🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/kettle-rb?style=for-the-badge
70
+ [🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/kettle-rb?style=for-the-badge
71
+ [🖇osc]: https://opencollective.com/kettle-rb
72
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
73
+ [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
74
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
75
+ [🖇floss-funding.dev]: https://floss-funding.dev
76
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
77
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge
data/README.md CHANGED
@@ -735,7 +735,7 @@ Thanks for RTFM. ☺️
735
735
  [📌gitmoji]:https://gitmoji.dev
736
736
  [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20😜%20😍-34495e.svg?style=flat-square
737
737
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
738
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.428-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
738
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.484-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
739
739
  [🔐security]: SECURITY.md
740
740
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
741
741
  [📄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
@@ -501,7 +501,7 @@ Thanks for RTFM. ☺️
501
501
  [📌gitmoji]:https://gitmoji.dev
502
502
  [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20😜%20😍-34495e.svg?style=flat-square
503
503
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
504
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.428-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
504
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.484-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
505
505
  [🔐security]: SECURITY.md
506
506
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
507
507
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
data/Rakefile.example CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # kettle-dev Rakefile v1.0.21 - 2025-08-30
3
+ # kettle-dev Rakefile v1.0.23 - 2025-08-30
4
4
  # Ruby 2.3 (Safe Navigation) or higher required
5
5
  #
6
6
  # MIT License (see License.txt)
@@ -26,12 +26,12 @@ Gem::Specification.new do |spec|
26
26
  spec.summary = "🍲 "
27
27
  spec.description = "🍲 "
28
28
  spec.homepage = "https://github.com/kettle-rb/kettle-dev"
29
- spec.license = "MIT"
29
+ spec.licenses = ["MIT"]
30
30
  spec.required_ruby_version = ">= 2.3.0"
31
31
 
32
32
  # Linux distros often package gems and securely certify them independent
33
33
  # of the official RubyGem certification process. Allowed via ENV["SKIP_GEM_SIGNING"]
34
- # Ref: https://gitlab.com/oauth-xx/version_gem/-/issues/3
34
+ # Ref: https://gitlab.com/ruby-oauth/version_gem/-/issues/3
35
35
  # Hence, only enable signing if `SKIP_GEM_SIGNING` is not set in ENV.
36
36
  # See CONTRIBUTING.md
37
37
  unless ENV.include?("SKIP_GEM_SIGNING")
@@ -75,6 +75,7 @@ Gem::Specification.new do |spec|
75
75
  "CITATION.cff",
76
76
  "CODE_OF_CONDUCT.md",
77
77
  "CONTRIBUTING.md",
78
+ "FUNDING.md",
78
79
  "LICENSE.txt",
79
80
  "README.md",
80
81
  "REEK",
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+
5
+ module Kettle
6
+ module Dev
7
+ # Unified gemspec reader using RubyGems loader instead of regex parsing.
8
+ # Returns a Hash with all data used by this project from gemspecs.
9
+ # Cache within the process to avoid repeated loads.
10
+ class GemSpecReader
11
+ DEFAULT_MINIMUM_RUBY = Gem::Version.new("1.8").freeze
12
+ class << self
13
+ # Load gemspec data for the project at root.
14
+ # @param root [String]
15
+ # @return [Hash]
16
+ def load(root)
17
+ gemspec_path = Dir.glob(File.join(root.to_s, "*.gemspec")).first
18
+ spec = nil
19
+ if gemspec_path && File.file?(gemspec_path)
20
+ begin
21
+ spec = Gem::Specification.load(gemspec_path)
22
+ rescue StandardError
23
+ spec = nil
24
+ end
25
+ end
26
+
27
+ gem_name = spec&.name.to_s
28
+ # minimum ruby version: derived from spec.required_ruby_version
29
+ # Always an instance of Gem::Version
30
+ min_ruby =
31
+ begin
32
+ # irb(main):004> Gem::Requirement.parse(spec.required_ruby_version)
33
+ # => [">=", Gem::Version.new("2.3.0")]
34
+ requirement = spec&.required_ruby_version
35
+ if requirement
36
+ tuple = Gem::Requirement.parse(requirement)
37
+ tuple[1] # an instance of Gem::Version
38
+ else
39
+ # Default to a minimum of Ruby 1.8
40
+ puts "WARNING: Minimum Ruby not detected"
41
+ DEFAULT_MINIMUM_RUBY
42
+ end
43
+ rescue StandardError => e
44
+ # Default to a minimum of Ruby 1.8
45
+ puts "WARNING: Minimum Ruby detection failed: #{e.class}: #{e.message}"
46
+ DEFAULT_MINIMUM_RUBY
47
+ end
48
+
49
+ homepage_val = spec&.homepage.to_s
50
+
51
+ # Derive org/repo from homepage or git remote
52
+ forge_org = nil
53
+ gh_repo = nil
54
+ if homepage_val && !homepage_val.empty?
55
+ if (m = homepage_val.match(%r{github\.com[/:]([^/]+)/([^/]+)}i))
56
+ forge_org = m[1]
57
+ gh_repo = m[2].to_s.sub(/\.git\z/, "")
58
+ end
59
+ end
60
+ if forge_org.nil?
61
+ begin
62
+ origin_out = IO.popen(["git", "-C", root.to_s, "remote", "get-url", "origin"], &:read)
63
+ origin_out = origin_out.read if origin_out.respond_to?(:read)
64
+ origin_url = origin_out.to_s.strip
65
+ if (m = origin_url.match(%r{github\.com[/:]([^/]+)/([^/]+)}i))
66
+ forge_org = m[1]
67
+ gh_repo = m[2].to_s.sub(/\.git\z/, "")
68
+ end
69
+ rescue StandardError
70
+ # ignore
71
+ end
72
+ end
73
+
74
+ camel = lambda do |s|
75
+ s.to_s.split(/[_-]/).map { |p| p.gsub(/\b([a-z])/) { Regexp.last_match(1).upcase } }.join
76
+ end
77
+ namespace = gem_name.to_s.split("-").map { |seg| camel.call(seg) }.join("::")
78
+ namespace_shield = namespace.gsub("::", "%3A%3A")
79
+ entrypoint_require = gem_name.to_s.tr("-", "/")
80
+ gem_shield = gem_name.to_s.gsub("-", "--").gsub("_", "__")
81
+
82
+ # Funding org detection (ENV, .opencollective.yml, fallback to forge_org)
83
+ funding_org = ENV["FUNDING_ORG"].to_s.strip
84
+ funding_org = ENV["OPENCOLLECTIVE_ORG"].to_s.strip if funding_org.empty?
85
+ funding_org = ENV["OPENCOLLECTIVE_HANDLE"].to_s.strip if funding_org.empty?
86
+ if funding_org.empty?
87
+ begin
88
+ oc_path = File.join(root.to_s, ".opencollective.yml")
89
+ if File.file?(oc_path)
90
+ txt = File.read(oc_path)
91
+ if (m = txt.match(/\borg:\s*([\w\-]+)/i))
92
+ funding_org = m[1].to_s
93
+ end
94
+ end
95
+ rescue StandardError
96
+ # ignore
97
+ end
98
+ end
99
+ funding_org = forge_org.to_s if funding_org.to_s.empty?
100
+
101
+ {
102
+ gemspec_path: gemspec_path,
103
+ gem_name: gem_name,
104
+ min_ruby: min_ruby, # Gem::Version instance
105
+ homepage: homepage_val.to_s,
106
+ gh_org: forge_org, # Might allow divergence from forge_org someday
107
+ forge_org: forge_org,
108
+ funding_org: funding_org,
109
+ gh_repo: gh_repo,
110
+ namespace: namespace,
111
+ namespace_shield: namespace_shield,
112
+ entrypoint_require: entrypoint_require,
113
+ gem_shield: gem_shield,
114
+ # Additional fields sourced from the gemspec for templating carry-over
115
+ authors: Array(spec&.authors).compact.uniq,
116
+ email: Array(spec&.email).compact.uniq,
117
+ summary: spec&.summary.to_s,
118
+ description: spec&.description.to_s,
119
+ licenses: Array(spec&.licenses), # licenses will include any specified as license (singular)
120
+ required_ruby_version: spec&.required_ruby_version, # Gem::Requirement instance
121
+ require_paths: Array(spec&.require_paths),
122
+ bindir: (spec&.bindir || "").to_s,
123
+ executables: Array(spec&.executables),
124
+ }
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -44,107 +44,98 @@ module Kettle
44
44
  readme_path = File.join(project_root, "README.md")
45
45
  if File.file?(readme_path)
46
46
  md = helpers.gemspec_metadata(project_root)
47
- min_ruby = md[:min_ruby].to_s.strip
48
- if !min_ruby.empty?
49
- # Compare using Gem::Version on major.minor
50
- require "rubygems"
51
- min_ver = begin
52
- Gem::Version.new(min_ruby.sub(/\A(~>\s*|>=\s*)/, ""))
53
- rescue
54
- nil
55
- end
56
- if min_ver
57
- content = File.read(readme_path)
47
+ min_ruby = md[:min_ruby] # an instance of Gem::Version
48
+ if min_ruby
49
+ content = File.read(readme_path)
58
50
 
59
- # Detect all MRI ruby badge labels present
60
- removed_labels = []
51
+ # Detect all MRI ruby badge labels present
52
+ removed_labels = []
61
53
 
62
- content.scan(/\[(?<label>💎ruby-(?<ver>\d+\.\d+)i)\]/) do |arr|
63
- label, ver_s = arr
64
- begin
65
- ver = Gem::Version.new(ver_s)
66
- if ver < min_ver
67
- # Remove occurrences of badges using this label
68
- label_re = Regexp.escape(label)
69
- # Linked form: [![...][label]][...]
70
- content = content.gsub(/\[!\[[^\]]*?\]\s*\[#{label_re}\]\s*\]\s*\[[^\]]+\]/, "")
71
- # Unlinked form: ![...][label]
72
- content = content.gsub(/!\[[^\]]*?\]\s*\[#{label_re}\]/, "")
73
- removed_labels << label
74
- end
75
- rescue StandardError
76
- # ignore
54
+ content.scan(/\[(?<label>💎ruby-(?<ver>\d+\.\d+)i)\]/) do |arr|
55
+ label, ver_s = arr
56
+ begin
57
+ ver = Gem::Version.new(ver_s)
58
+ if ver < min_ruby
59
+ # Remove occurrences of badges using this label
60
+ label_re = Regexp.escape(label)
61
+ # Linked form: [![...][label]][...]
62
+ content = content.gsub(/\[!\[[^\]]*?\]\s*\[#{label_re}\]\s*\]\s*\[[^\]]+\]/, "")
63
+ # Unlinked form: ![...][label]
64
+ content = content.gsub(/!\[[^\]]*?\]\s*\[#{label_re}\]/, "")
65
+ removed_labels << label
77
66
  end
67
+ rescue StandardError
68
+ # ignore
78
69
  end
70
+ end
79
71
 
80
- # Fix leading <br/> in MRI rows and remove rows that end up empty
81
- content = content.lines.map { |ln|
82
- if ln.start_with?("| Works with MRI Ruby")
83
- cells = ln.split("|", -1)
84
- # cells[0] is empty (leading |), cells[1] = label cell, cells[2] = badges cell
85
- badge_cell = cells[2] || ""
86
- # If badge cell is only a <br/> (possibly with whitespace), treat as empty (row will be removed later)
87
- if badge_cell.strip == "<br/>"
88
- cells[2] = " "
89
- cells.join("|")
90
- elsif badge_cell =~ /\A\s*<br\/>/i
91
- # If badge cell starts with <br/> and there are no badges before it, strip the leading <br/>
92
- # We consider "no badges before" as any leading whitespace followed immediately by <br/>
93
- cleaned = badge_cell.sub(/\A\s*<br\/>\s*/i, "")
94
- cells[2] = " #{cleaned}" # prefix with a single space
95
- cells.join("|")
96
- else
97
- ln
98
- end
72
+ # Fix leading <br/> in MRI rows and remove rows that end up empty
73
+ content = content.lines.map { |ln|
74
+ if ln.start_with?("| Works with MRI Ruby")
75
+ cells = ln.split("|", -1)
76
+ # cells[0] is empty (leading |), cells[1] = label cell, cells[2] = badges cell
77
+ badge_cell = cells[2] || ""
78
+ # If badge cell is only a <br/> (possibly with whitespace), treat as empty (row will be removed later)
79
+ if badge_cell.strip == "<br/>"
80
+ cells[2] = " "
81
+ cells.join("|")
82
+ elsif badge_cell =~ /\A\s*<br\/>/i
83
+ # If badge cell starts with <br/> and there are no badges before it, strip the leading <br/>
84
+ # We consider "no badges before" as any leading whitespace followed immediately by <br/>
85
+ cleaned = badge_cell.sub(/\A\s*<br\/>\s*/i, "")
86
+ cells[2] = " #{cleaned}" # prefix with a single space
87
+ cells.join("|")
99
88
  else
100
89
  ln
101
90
  end
102
- }.reject { |ln|
103
- if ln.start_with?("| Works with MRI Ruby")
104
- cells = ln.split("|", -1)
105
- badge_cell = cells[2] || ""
106
- badge_cell.strip.empty?
107
- else
108
- false
109
- end
110
- }.join
111
-
112
- # Clean up extra repeated whitespace only when it appears between word characters, and only for non-table lines.
113
- # This preserves Markdown table alignment and spacing around punctuation/symbols.
114
- content = content.lines.map do |ln|
115
- if ln.start_with?("|")
116
- ln
117
- else
118
- # Squish only runs of spaces/tabs between word characters
119
- ln.gsub(/(\w)[ \t]{2,}(\w)/u, "\\1 \\2")
120
- end
121
- end.join
122
-
123
- # Remove reference definitions for removed labels that are no longer used
124
- unless removed_labels.empty?
125
- # Unique
126
- removed_labels.uniq!
127
- # Determine which labels are still referenced after edits
128
- still_referenced = {}
129
- removed_labels.each do |lbl|
130
- lbl_re = Regexp.escape(lbl)
131
- # Consider a label referenced only when it appears not as a definition (i.e., not followed by colon)
132
- still_referenced[lbl] = !!(content =~ /\[#{lbl_re}\](?!:)/)
133
- end
91
+ else
92
+ ln
93
+ end
94
+ }.reject { |ln|
95
+ if ln.start_with?("| Works with MRI Ruby")
96
+ cells = ln.split("|", -1)
97
+ badge_cell = cells[2] || ""
98
+ badge_cell.strip.empty?
99
+ else
100
+ false
101
+ end
102
+ }.join
134
103
 
135
- new_lines = content.lines.map do |line|
136
- if line =~ /^\[(?<lab>[^\]]+)\]:/ && removed_labels.include?(Regexp.last_match(:lab))
137
- # Only drop if not referenced anymore
138
- still_referenced[Regexp.last_match(:lab)] ? line : nil
139
- else
140
- line
141
- end
142
- end.compact
143
- content = new_lines.join
104
+ # Clean up extra repeated whitespace only when it appears between word characters, and only for non-table lines.
105
+ # This preserves Markdown table alignment and spacing around punctuation/symbols.
106
+ content = content.lines.map do |ln|
107
+ if ln.start_with?("|")
108
+ ln
109
+ else
110
+ # Squish only runs of spaces/tabs between word characters
111
+ ln.gsub(/(\w)[ \t]{2,}(\w)/u, "\\1 \\2")
112
+ end
113
+ end.join
114
+
115
+ # Remove reference definitions for removed labels that are no longer used
116
+ unless removed_labels.empty?
117
+ # Unique
118
+ removed_labels.uniq!
119
+ # Determine which labels are still referenced after edits
120
+ still_referenced = {}
121
+ removed_labels.each do |lbl|
122
+ lbl_re = Regexp.escape(lbl)
123
+ # Consider a label referenced only when it appears not as a definition (i.e., not followed by colon)
124
+ still_referenced[lbl] = !!(content =~ /\[#{lbl_re}\](?!:)/)
144
125
  end
145
126
 
146
- File.open(readme_path, "w") { |f| f.write(content) }
127
+ new_lines = content.lines.map do |line|
128
+ if line =~ /^\[(?<lab>[^\]]+)\]:/ && removed_labels.include?(Regexp.last_match(:lab))
129
+ # Only drop if not referenced anymore
130
+ still_referenced[Regexp.last_match(:lab)] ? line : nil
131
+ else
132
+ line
133
+ end
134
+ end.compact
135
+ content = new_lines.join
147
136
  end
137
+
138
+ File.open(readme_path, "w") { |f| f.write(content) }
148
139
  end
149
140
  end
150
141
  rescue StandardError => e
@@ -123,10 +123,9 @@ module Kettle
123
123
  rubocop_ruby_gem_version = nil
124
124
  ruby1_8 = version_map.first
125
125
  begin
126
- if min_ruby && !min_ruby.empty?
127
- v = Gem::Version.new(min_ruby.split(".")[0, 2].join("."))
126
+ if min_ruby
128
127
  version_map.reverse_each do |min, req|
129
- if v >= min
128
+ if min_ruby >= min
130
129
  new_constraint = req
131
130
  rubocop_ruby_gem_version = min.segments.join("_")
132
131
  break
@@ -205,8 +204,20 @@ module Kettle
205
204
  File.join(project_root, fallback_name)
206
205
  end
207
206
  end
207
+
208
+ # If a destination gemspec already exists, get metadata from GemSpecReader via helpers
209
+ orig_meta = nil
210
+ if File.exist?(dest_gemspec)
211
+ begin
212
+ orig_meta = helpers.gemspec_metadata(File.dirname(dest_gemspec))
213
+ rescue StandardError
214
+ orig_meta = nil
215
+ end
216
+ end
217
+
208
218
  helpers.copy_file_with_prompt(gemspec_template_src, dest_gemspec, allow_create: true, allow_replace: true) do |content|
209
- helpers.apply_common_replacements(
219
+ # First apply standard replacements from the template example
220
+ c = helpers.apply_common_replacements(
210
221
  content,
211
222
  org: forge_org,
212
223
  gem_name: gem_name,
@@ -214,6 +225,93 @@ module Kettle
214
225
  namespace_shield: namespace_shield,
215
226
  gem_shield: gem_shield,
216
227
  )
228
+
229
+ if orig_meta
230
+ # Replace a scalar string assignment like: spec.field = "..."
231
+ replace_string_field = lambda do |txt, field, value|
232
+ v = value.to_s
233
+ txt.gsub(/(\bspec\.#{Regexp.escape(field)}\s*=\s*)(["']).*?(\2)/) do
234
+ pre = Regexp.last_match(1)
235
+ q = '"'
236
+ pre + q + v.gsub('"', '\\"') + q
237
+ end
238
+ end
239
+
240
+ # Replace an array assignment like: spec.field = ["a", "b"]
241
+ replace_array_field = lambda do |txt, field, ary|
242
+ ary = Array(ary).compact.map(&:to_s).reject(&:empty?).uniq
243
+ # literal = "[" + arr.map { |e| '"' + e.gsub('"', '\\"') + '"' }.join(", ") + "]"
244
+ literal = ary.inspect
245
+ if txt =~ /(\bspec\.#{Regexp.escape(field)}\s*=\s*)\[[^\]]*\]/
246
+ txt.gsub(/(\bspec\.#{Regexp.escape(field)}\s*=\s*)\[[^\]]*\]/, "\\1#{literal}")
247
+ else
248
+ # If no existing assignment, insert a new line after spec.version if possible
249
+ insert_after = (txt =~ /^\s*spec\.version\s*=.*$/) ? :version : nil
250
+ if insert_after == :version
251
+ txt.sub(/^(\s*spec\.version\s*=.*$)/) { |line| line + "\n spec.#{field} = #{literal}" }
252
+ else
253
+ txt + "\n spec.#{field} = #{literal}\n"
254
+ end
255
+ end
256
+ end
257
+
258
+ begin
259
+ # 1. spec.name — retain original
260
+ if (name = orig_meta[:gem_name]) && !name.to_s.empty?
261
+ c = replace_string_field.call(c, "name", name)
262
+ end
263
+
264
+ # 2. spec.authors — retain original, normalize to array
265
+ orig_auth = orig_meta[:authors]
266
+ c = replace_array_field.call(c, "authors", orig_auth)
267
+
268
+ # 3. spec.email — retain original, normalize to array
269
+ orig_email = orig_meta[:email]
270
+ c = replace_array_field.call(c, "email", orig_email)
271
+
272
+ # 4. spec.summary — retain original; grapheme emoji prefix handled by "install" task
273
+ if (sum = orig_meta[:summary]) && !sum.to_s.empty?
274
+ c = replace_string_field.call(c, "summary", sum)
275
+ end
276
+
277
+ # 5. spec.description — retain original; grapheme emoji prefix handled by "install" task
278
+ if (desc = orig_meta[:description]) && !desc.to_s.empty?
279
+ c = replace_string_field.call(c, "description", desc)
280
+ end
281
+
282
+ # 6. spec.licenses — retain original, normalize to array
283
+ lic = orig_meta[:licenses]
284
+ if lic && !lic.empty?
285
+ c = replace_array_field.call(c, "licenses", lic)
286
+ end
287
+
288
+ # 7. spec.required_ruby_version — retain original
289
+ if (rrv = orig_meta[:required_ruby_version].to_s) && !rrv.empty?
290
+ c = replace_string_field.call(c, "required_ruby_version", rrv)
291
+ end
292
+
293
+ # 8. spec.require_paths — retain original, normalize to array
294
+ req_paths = orig_meta[:require_paths]
295
+ unless req_paths.empty?
296
+ c = replace_array_field.call(c, "require_paths", req_paths)
297
+ end
298
+
299
+ # 9. spec.bindir — retain original
300
+ if (bd = orig_meta[:bindir]) && !bd.to_s.empty?
301
+ c = replace_string_field.call(c, "bindir", bd)
302
+ end
303
+
304
+ # 10. spec.executables — retain original, normalize to array
305
+ exes = orig_meta[:executables]
306
+ unless exes.empty?
307
+ c = replace_array_field.call(c, "executables", exes)
308
+ end
309
+ rescue StandardError
310
+ # Best-effort carry-over; ignore any individual failure
311
+ end
312
+ end
313
+
314
+ c
217
315
  end
218
316
  end
219
317
  rescue StandardError
@@ -266,7 +364,6 @@ module Kettle
266
364
  if prev_readme
267
365
  first_h1_prev = prev_readme.lines.find { |ln| ln =~ /^#\s+/ }
268
366
  if first_h1_prev
269
- require "kettle/emoji_regex"
270
367
  emoji_re = Kettle::EmojiRegex::REGEX
271
368
  tail = first_h1_prev.sub(/^#\s+/, "")
272
369
  # Extract consecutive leading emoji graphemes
@@ -362,83 +362,7 @@ module Kettle
362
362
  # @param root [String] project root
363
363
  # @return [Hash]
364
364
  def gemspec_metadata(root = project_root)
365
- gemspecs = Dir.glob(File.join(root, "*.gemspec"))
366
- gemspec_path = gemspecs.first
367
- gemspec_text = (gemspec_path && File.file?(gemspec_path)) ? File.read(gemspec_path) : ""
368
- gem_name = (gemspec_text[/\bspec\.name\s*=\s*["']([^"']+)["']/, 1] || "").strip
369
- min_ruby = (
370
- gemspec_text[/\bspec\.minimum_ruby_version\s*=\s*["'](?:>=\s*)?([0-9]+\.[0-9]+(?:\.[0-9]+)?)["']/i, 1] ||
371
- gemspec_text[/\bspec\.required_ruby_version\s*=\s*["']>=\s*([0-9]+\.[0-9]+(?:\.[0-9]+)?)["']/i, 1] ||
372
- gemspec_text[/\brequired_ruby_version\s*[:=]\s*["'](?:>=\s*)?([0-9]+\.[0-9]+(?:\.[0-9]+)?)["']/i, 1] ||
373
- ""
374
- ).strip
375
- homepage_line = gemspec_text.lines.find { |l| l =~ /\bspec\.homepage\s*=\s*/ }
376
- homepage_val = homepage_line ? homepage_line.split("=", 2).last.to_s.strip : ""
377
- if (homepage_val.start_with?("\"") && homepage_val.end_with?("\"")) || (homepage_val.start_with?("'") && homepage_val.end_with?("'"))
378
- homepage_val = begin
379
- homepage_val[1..-2]
380
- rescue
381
- homepage_val
382
- end
383
- end
384
- gh_match = homepage_val&.match(%r{github\.com/([^/]+)/([^/]+)}i)
385
- forge_org = gh_match && gh_match[1]
386
- gh_repo = gh_match && gh_match[2]&.sub(/\.git\z/, "")
387
- if forge_org.nil?
388
- begin
389
- origin_out = IO.popen(["git", "-C", root.to_s, "remote", "get-url", "origin"], &:read)
390
- origin_out = origin_out.read if origin_out.respond_to?(:read)
391
- origin_url = origin_out.to_s.strip
392
- if (m = origin_url.match(%r{github\.com[/:]([^/]+)/([^/]+)}i))
393
- forge_org = m[1]
394
- gh_repo = m[2]&.sub(/\.git\z/, "")
395
- end
396
- rescue StandardError
397
- # ignore
398
- end
399
- end
400
-
401
- camel = lambda do |s|
402
- s.split(/[_-]/).map { |p| p.gsub(/\b([a-z])/) { Regexp.last_match(1).upcase } }.join
403
- end
404
- namespace = gem_name.to_s.split("-").map { |seg| camel.call(seg) }.join("::")
405
- namespace_shield = namespace.gsub("::", "%3A%3A")
406
- entrypoint_require = gem_name.to_s.tr("-", "/")
407
- gem_shield = gem_name.to_s.gsub("-", "--").gsub("_", "__")
408
-
409
- # Determine funding_org independently of forge_org (GitHub org)
410
- funding_org = ENV["FUNDING_ORG"].to_s.strip
411
- funding_org = ENV["OPENCOLLECTIVE_ORG"].to_s.strip if funding_org.empty?
412
- funding_org = ENV["OPENCOLLECTIVE_HANDLE"].to_s.strip if funding_org.empty?
413
- if funding_org.empty?
414
- begin
415
- oc_path = File.join(root.to_s, ".opencollective.yml")
416
- if File.file?(oc_path)
417
- txt = File.read(oc_path)
418
- if (m = txt.match(/\borg:\s*([\w\-]+)/i))
419
- funding_org = m[1].to_s
420
- end
421
- end
422
- rescue StandardError
423
- # ignore
424
- end
425
- end
426
- funding_org = forge_org.to_s if funding_org.to_s.empty?
427
-
428
- {
429
- gemspec_path: gemspec_path,
430
- gem_name: gem_name,
431
- min_ruby: min_ruby,
432
- homepage: homepage_val,
433
- gh_org: forge_org, # Backward compat: keep old key synonymous with forge_org
434
- forge_org: forge_org,
435
- funding_org: funding_org,
436
- gh_repo: gh_repo,
437
- namespace: namespace,
438
- namespace_shield: namespace_shield,
439
- entrypoint_require: entrypoint_require,
440
- gem_shield: gem_shield,
441
- }
365
+ Kettle::Dev::GemSpecReader.load(root)
442
366
  end
443
367
  end
444
368
  end
@@ -6,7 +6,7 @@ module Kettle
6
6
  module Version
7
7
  # The gem version.
8
8
  # @return [String]
9
- VERSION = "1.0.21"
9
+ VERSION = "1.0.23"
10
10
  end
11
11
  end
12
12
  end
data/lib/kettle/dev.rb CHANGED
@@ -114,6 +114,7 @@ module Kettle
114
114
  module Dev
115
115
  autoload :CIHelpers, "kettle/dev/ci_helpers"
116
116
  autoload :CommitMsg, "kettle/dev/commit_msg"
117
+ autoload :GemSpecReader, "kettle/dev/gem_spec_reader"
117
118
  autoload :GitCommitFooter, "kettle/dev/git_commit_footer"
118
119
  autoload :ReadmeBackers, "kettle/dev/readme_backers"
119
120
  autoload :ReleaseCLI, "kettle/dev/release_cli"
@@ -3,7 +3,7 @@
3
3
  # because that project is Ruby >= 2.4, and this project is Ruby >= 2.3.
4
4
  # That project is under the MIT license, same as this project:
5
5
  # https://github.com/ticky/ruby-emoji-regex/blob/develop/LICENSE.md
6
- # If the minimum_ruby_version numbers align, will switch to a real dependency.
6
+ # If the required_ruby_version numbers align, will switch to a real dependency.
7
7
  # Wrapping it in a namespace to avoid collisions with the real emoji_regex.
8
8
  module Kettle
9
9
  module EmojiRegex
@@ -0,0 +1,30 @@
1
+ module Kettle
2
+ module Dev
3
+ class GemSpecReader
4
+ def self.load: (String root) -> {
5
+ gemspec_path: String?,
6
+ gem_name: String,
7
+ min_ruby: String,
8
+ homepage: String,
9
+ gh_org: String?,
10
+ forge_org: String?,
11
+ funding_org: String?,
12
+ gh_repo: String?,
13
+ namespace: String,
14
+ namespace_shield: String,
15
+ entrypoint_require: String,
16
+ gem_shield: String,
17
+ authors: Array[String],
18
+ email: Array[String],
19
+ summary: String,
20
+ description: String,
21
+ license: String,
22
+ licenses: Array[String],
23
+ required_ruby_version: String,
24
+ require_paths: Array[String],
25
+ bindir: String,
26
+ executables: Array[String],
27
+ }
28
+ end
29
+ end
30
+ end
@@ -66,6 +66,16 @@ module Kettle
66
66
  namespace_shield: String,
67
67
  entrypoint_require: String,
68
68
  gem_shield: String,
69
+ authors: Array[String],
70
+ email: Array[String],
71
+ summary: String,
72
+ description: String,
73
+ license: String,
74
+ licenses: Array[String],
75
+ required_ruby_version: String,
76
+ require_paths: Array[String],
77
+ bindir: String,
78
+ executables: Array[String],
69
79
  }
70
80
  end
71
81
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.21
4
+ version: 1.0.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -219,6 +219,7 @@ extra_rdoc_files:
219
219
  - CITATION.cff
220
220
  - CODE_OF_CONDUCT.md
221
221
  - CONTRIBUTING.md
222
+ - FUNDING.md
222
223
  - LICENSE.txt
223
224
  - README.md
224
225
  - REEK
@@ -243,6 +244,7 @@ files:
243
244
  - ".github/workflows/dependency-review.yml"
244
245
  - ".github/workflows/discord-notifier.yml"
245
246
  - ".github/workflows/heads.yml"
247
+ - ".github/workflows/heads.yml.example"
246
248
  - ".github/workflows/jruby.yml"
247
249
  - ".github/workflows/jruby.yml.example"
248
250
  - ".github/workflows/legacy.yml"
@@ -272,6 +274,7 @@ files:
272
274
  - CITATION.cff
273
275
  - CODE_OF_CONDUCT.md
274
276
  - CONTRIBUTING.md
277
+ - FUNDING.md
275
278
  - Gemfile
276
279
  - Gemfile.example
277
280
  - LICENSE.txt
@@ -298,6 +301,7 @@ files:
298
301
  - lib/kettle/dev/ci_monitor.rb
299
302
  - lib/kettle/dev/commit_msg.rb
300
303
  - lib/kettle/dev/exit_adapter.rb
304
+ - lib/kettle/dev/gem_spec_reader.rb
301
305
  - lib/kettle/dev/git_adapter.rb
302
306
  - lib/kettle/dev/git_commit_footer.rb
303
307
  - lib/kettle/dev/input_adapter.rb
@@ -328,6 +332,7 @@ files:
328
332
  - sig/kettle/dev/ci_monitor.rbs
329
333
  - sig/kettle/dev/commit_msg.rbs
330
334
  - sig/kettle/dev/exit_adapter.rbs
335
+ - sig/kettle/dev/gem_spec_reader.rbs
331
336
  - sig/kettle/dev/git_adapter.rbs
332
337
  - sig/kettle/dev/git_commit_footer.rbs
333
338
  - sig/kettle/dev/input_adapter.rbs
@@ -345,10 +350,10 @@ licenses:
345
350
  - MIT
346
351
  metadata:
347
352
  homepage_uri: https://kettle-dev.galtzo.com/
348
- source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.0.21
349
- changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.0.21/CHANGELOG.md
353
+ source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.0.23
354
+ changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.0.23/CHANGELOG.md
350
355
  bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
351
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.21
356
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.23
352
357
  funding_uri: https://github.com/sponsors/pboling
353
358
  wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
354
359
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file