ratatui_ruby-devtools 0.1.0

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.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.builds/ruby-4.0.yml +38 -0
  3. data/.pre-commit-config.yaml +16 -0
  4. data/.rubocop.yml +8 -0
  5. data/AGENTS.md +72 -0
  6. data/CHANGELOG.md +23 -0
  7. data/LICENSE +661 -0
  8. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  9. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  10. data/LICENSES/CC0-1.0.txt +121 -0
  11. data/LICENSES/MIT-0.txt +16 -0
  12. data/LICENSES/MIT.txt +18 -0
  13. data/README.md +199 -0
  14. data/REUSE.toml +18 -0
  15. data/Rakefile +13 -0
  16. data/bin/agent_rake +13 -0
  17. data/bin/announce +13 -0
  18. data/bin/console +14 -0
  19. data/bin/consolidate_md +13 -0
  20. data/bin/hbs +13 -0
  21. data/bin/setup +17 -0
  22. data/doc/contributors/documentation_style.md +121 -0
  23. data/doc/custom.css +22 -0
  24. data/exe/agent_rake +96 -0
  25. data/exe/announce +1120 -0
  26. data/exe/consolidate_md +246 -0
  27. data/exe/hbs +670 -0
  28. data/exe/scaffold +662 -0
  29. data/lib/ratatui_ruby/devtools/tasks/autodoc/examples.rb +133 -0
  30. data/lib/ratatui_ruby/devtools/tasks/autodoc/member.rb +116 -0
  31. data/lib/ratatui_ruby/devtools/tasks/autodoc/name.rb +33 -0
  32. data/lib/ratatui_ruby/devtools/tasks/autodoc.rake +21 -0
  33. data/lib/ratatui_ruby/devtools/tasks/bump/cargo_lockfile.rb +38 -0
  34. data/lib/ratatui_ruby/devtools/tasks/bump/changelog.rb +67 -0
  35. data/lib/ratatui_ruby/devtools/tasks/bump/header.rb +43 -0
  36. data/lib/ratatui_ruby/devtools/tasks/bump/history.rb +50 -0
  37. data/lib/ratatui_ruby/devtools/tasks/bump/links.rb +78 -0
  38. data/lib/ratatui_ruby/devtools/tasks/bump/manifest.rb +63 -0
  39. data/lib/ratatui_ruby/devtools/tasks/bump/ruby_gem.rb +77 -0
  40. data/lib/ratatui_ruby/devtools/tasks/bump/sem_ver.rb +63 -0
  41. data/lib/ratatui_ruby/devtools/tasks/bump/unreleased_section.rb +75 -0
  42. data/lib/ratatui_ruby/devtools/tasks/bump.rake +80 -0
  43. data/lib/ratatui_ruby/devtools/tasks/cargo.rake +47 -0
  44. data/lib/ratatui_ruby/devtools/tasks/doc.rake +887 -0
  45. data/lib/ratatui_ruby/devtools/tasks/example_viewer.html.erb +172 -0
  46. data/lib/ratatui_ruby/devtools/tasks/license/headers_md.rb +276 -0
  47. data/lib/ratatui_ruby/devtools/tasks/license/headers_rb.rb +236 -0
  48. data/lib/ratatui_ruby/devtools/tasks/license/license_utils.rb +143 -0
  49. data/lib/ratatui_ruby/devtools/tasks/license/snippets_md.rb +353 -0
  50. data/lib/ratatui_ruby/devtools/tasks/license/snippets_rdoc.rb +186 -0
  51. data/lib/ratatui_ruby/devtools/tasks/license.rake +91 -0
  52. data/lib/ratatui_ruby/devtools/tasks/lint.rake +84 -0
  53. data/lib/ratatui_ruby/devtools/tasks/rdoc_config.rb +45 -0
  54. data/lib/ratatui_ruby/devtools/tasks/resources/build.yml.erb +54 -0
  55. data/lib/ratatui_ruby/devtools/tasks/resources/rubies.yml +7 -0
  56. data/lib/ratatui_ruby/devtools/tasks/reuse.rake +104 -0
  57. data/lib/ratatui_ruby/devtools/tasks/sourcehut.rake +94 -0
  58. data/lib/ratatui_ruby/devtools/tasks/test.rake +18 -0
  59. data/lib/ratatui_ruby/devtools/templates/.builds/ruby.yml.erb +47 -0
  60. data/lib/ratatui_ruby/devtools/templates/.gitignore.erb +18 -0
  61. data/lib/ratatui_ruby/devtools/templates/.pre-commit-config.yaml.erb +16 -0
  62. data/lib/ratatui_ruby/devtools/templates/.rubocop.yml.erb +8 -0
  63. data/lib/ratatui_ruby/devtools/templates/AGENTS.md.erb +65 -0
  64. data/lib/ratatui_ruby/devtools/templates/CHANGELOG.md.erb +18 -0
  65. data/lib/ratatui_ruby/devtools/templates/Gemfile.erb +32 -0
  66. data/lib/ratatui_ruby/devtools/templates/README.md.erb +127 -0
  67. data/lib/ratatui_ruby/devtools/templates/REUSE.toml.erb +33 -0
  68. data/lib/ratatui_ruby/devtools/templates/Rakefile.erb +29 -0
  69. data/lib/ratatui_ruby/devtools/templates/bin/console.erb +18 -0
  70. data/lib/ratatui_ruby/devtools/templates/bin/setup.erb +24 -0
  71. data/lib/ratatui_ruby/devtools/templates/doc/concepts/application_architecture.md.erb +16 -0
  72. data/lib/ratatui_ruby/devtools/templates/doc/concepts/application_testing.md.erb +49 -0
  73. data/lib/ratatui_ruby/devtools/templates/doc/custom.css.erb +24 -0
  74. data/lib/ratatui_ruby/devtools/templates/doc/getting_started/quickstart.md.erb +56 -0
  75. data/lib/ratatui_ruby/devtools/templates/doc/images/.gitkeep +0 -0
  76. data/lib/ratatui_ruby/devtools/templates/doc/index.md.erb +25 -0
  77. data/lib/ratatui_ruby/devtools/templates/exe/.gitkeep +0 -0
  78. data/lib/ratatui_ruby/devtools/templates/gemspec.erb +58 -0
  79. data/lib/ratatui_ruby/devtools/templates/mise.toml.erb +12 -0
  80. data/lib/ratatui_ruby/devtools/templates/tasks/example_viewer.html.erb +174 -0
  81. data/lib/ratatui_ruby/devtools/templates/tasks/resources/build.yml.erb +62 -0
  82. data/lib/ratatui_ruby/devtools/templates/tasks/resources/index.html.erb +46 -0
  83. data/lib/ratatui_ruby/devtools/templates/tasks/resources/rubies.yml.erb +9 -0
  84. data/lib/ratatui_ruby/devtools/templates/vendor/goodcop/base.yml +1047 -0
  85. data/lib/ratatui_ruby/devtools/version.rb +13 -0
  86. data/lib/ratatui_ruby/devtools.rb +137 -0
  87. data/mise.toml +7 -0
  88. data/sig/ratatui_ruby/devtools.rbs +15 -0
  89. data/vendor/goodcop/base.yml +1047 -0
  90. metadata +252 -0
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ # Coordinates version bumping across multiple manifests.
9
+ #
10
+ # Ruby gems have versions in multiple files: version.rb, Cargo.toml, lockfiles.
11
+ # Bumping manually leads to mismatches. Forgetting the changelog produces
12
+ # incomplete releases.
13
+ #
14
+ # This class orchestrates the bump. It updates all manifests, refreshes
15
+ # lockfiles, and updates the changelog. One command. Consistent versions.
16
+ #
17
+ # Use it in rake tasks to bump major, minor, or patch versions.
18
+ class RubyGem
19
+ # Creates a new RubyGem coordinator.
20
+ #
21
+ # [manifests] Array of Manifest objects. Exactly one must be primary.
22
+ # [lockfile] A lockfile object that responds to <tt>refresh</tt>.
23
+ # [changelog] A Changelog object for updating release notes.
24
+ def initialize(manifests:, lockfile:, changelog:)
25
+ raise ArgumentError, "Must have exactly one primary manifest" unless manifests.count(&:primary) == 1
26
+ @manifests = manifests
27
+ @lockfile = lockfile
28
+ @changelog = changelog
29
+ end
30
+
31
+ # Returns the current version from the primary manifest.
32
+ def version
33
+ @manifests.find(&:primary).version
34
+ end
35
+
36
+ # Bumps the version by the given segment.
37
+ #
38
+ # Updates all manifests, refreshes lockfiles, and updates the changelog.
39
+ # Prints a suggested commit message.
40
+ #
41
+ # [segment] One of <tt>:major</tt>, <tt>:minor</tt>, or <tt>:patch</tt>.
42
+ def bump(segment)
43
+ target = version.next(segment)
44
+ commit_message = @changelog.commit_message(target)
45
+
46
+ puts "Bumping #{segment}: #{version} -> #{target}"
47
+ @changelog.release(target)
48
+ @manifests.each { |manifest| manifest.write(target) }
49
+ @lockfile.refresh
50
+
51
+ puts_commit_message(commit_message)
52
+ end
53
+
54
+ # Sets the version to an exact value.
55
+ #
56
+ # Updates all manifests, refreshes lockfiles, and updates the changelog.
57
+ # Prints a suggested commit message.
58
+ #
59
+ # [version_string] A version string like <tt>"1.2.3"</tt>.
60
+ def set(version_string)
61
+ target = SemVer.parse(version_string)
62
+ commit_message = @changelog.commit_message(target)
63
+
64
+ puts "Setting version: #{version} -> #{target}"
65
+ @changelog.release(target)
66
+ @manifests.each { |manifest| manifest.write(target) }
67
+ @lockfile.refresh
68
+
69
+ puts_commit_message(commit_message)
70
+ end
71
+
72
+ private def puts_commit_message(message)
73
+ puts "=" * 80
74
+ puts message
75
+ puts "=" * 80
76
+ end
77
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ # Represents a semantic version number.
9
+ #
10
+ # Version numbers have three segments: major, minor, patch. Incrementing one
11
+ # resets the following segments to zero. Parsing strings by hand is tedious.
12
+ #
13
+ # This class parses version strings and computes the next version for any
14
+ # segment. It follows the Semantic Versioning 2.0.0 specification.
15
+ #
16
+ # Use it to bump versions in rake tasks.
17
+ #
18
+ # === Example
19
+ #
20
+ # SemVer.parse("1.2.3").next(:minor).to_s # => "1.3.0"
21
+ #
22
+ # See https://semver.org/spec/v2.0.0.html
23
+ class SemVer
24
+ # The valid segment names for version bumping.
25
+ SEGMENTS = [:major, :minor, :patch].freeze
26
+
27
+ # Parses a version string into a SemVer instance.
28
+ #
29
+ # [string] A version string like <tt>"1.2.3"</tt>.
30
+ def self.parse(string)
31
+ require "rubygems"
32
+ segments = Gem::Version.new(string).segments.fill(0, 3).first(3)
33
+ new(segments)
34
+ end
35
+
36
+ # Creates a new SemVer from an array of segments.
37
+ #
38
+ # [segments] An array of three integers: <tt>[major, minor, patch]</tt>.
39
+ def initialize(segments)
40
+ @segments = segments
41
+ end
42
+
43
+ # Returns the next version after bumping the given segment.
44
+ #
45
+ # Bumping a segment resets all following segments to zero.
46
+ #
47
+ # [segment] One of <tt>:major</tt>, <tt>:minor</tt>, or <tt>:patch</tt>.
48
+ def next(segment)
49
+ index = SEGMENTS.index(segment)
50
+ raise ArgumentError, "Invalid segment: #{segment}" unless index
51
+
52
+ new_segments = @segments.dup
53
+ new_segments[index] += 1
54
+ new_segments.fill(0, (index + 1)..2)
55
+
56
+ SemVer.new(new_segments)
57
+ end
58
+
59
+ # Returns the version as a string.
60
+ def to_s
61
+ @segments.join(".")
62
+ end
63
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ require "date"
9
+ require "rdoc"
10
+
11
+ # Manages the Unreleased section of a changelog.
12
+ #
13
+ # Changelogs accumulate changes under an Unreleased heading. During a release,
14
+ # this section becomes a dated version entry. Parsing and transforming it by
15
+ # hand is tedious.
16
+ #
17
+ # This class extracts the Unreleased section from markdown. It transforms it
18
+ # into a versioned section. It generates commit message bodies.
19
+ #
20
+ # Use it during release preparation.
21
+ class UnreleasedSection
22
+ # Regex to match the Unreleased section.
23
+ PATTERN = /^(## \[Unreleased\].*?)(?=## \[\d)/m
24
+
25
+ # Parses the Unreleased section from changelog content.
26
+ #
27
+ # [content] The full changelog text.
28
+ def self.parse(content)
29
+ match = content.match(PATTERN)
30
+ new(match[1].strip) if match
31
+ end
32
+
33
+ # Returns a fresh Unreleased section with standard headings.
34
+ def self.fresh
35
+ new("## [Unreleased]\n\n### Added\n\n### Changed\n\n### Fixed\n\n### Removed")
36
+ end
37
+
38
+ # Creates a new UnreleasedSection.
39
+ #
40
+ # [content] The raw section text.
41
+ def initialize(content)
42
+ @content = content.dup
43
+ end
44
+
45
+ # Converts the section to a dated version entry.
46
+ #
47
+ # [new_version] The version string or SemVer.
48
+ def as_version(new_version)
49
+ date = Date.today.iso8601
50
+ @content.sub(/^## \[Unreleased\]/, "## [#{new_version}] - #{date}")
51
+ end
52
+
53
+ # Returns the section as a string.
54
+ def to_s
55
+ @content
56
+ end
57
+
58
+ # Generates a commit message body from the changes.
59
+ #
60
+ # Strips markdown formatting and wraps lines to 72 characters.
61
+ def commit_body
62
+ formatter = Class.new { include RDoc::Text }.new
63
+ @content
64
+ .sub(/^## \[Unreleased\].*$/, "")
65
+ .gsub(/^### (Added|Changed|Fixed|Removed)\n*$/, "")
66
+ .gsub(/^- \*\*([^*]+)\*\*:/, '\1:')
67
+ .gsub(/`([^`]+)`/, '\1')
68
+ .strip
69
+ .lines
70
+ .map { |line| line.gsub(/^- /, "").strip }
71
+ .reject(&:empty?)
72
+ .map { |line| formatter.wrap(line, 72) }
73
+ .join("\n\n")
74
+ end
75
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ require "rubygems"
9
+
10
+ require_relative "bump/sem_ver"
11
+ require_relative "bump/manifest"
12
+ require_relative "bump/cargo_lockfile"
13
+ require_relative "bump/ruby_gem"
14
+ require_relative "bump/changelog"
15
+
16
+ # Null lockfile for gems without Rust
17
+ class NullLockfile
18
+ def refresh
19
+ # No-op
20
+ end
21
+ end
22
+
23
+ namespace :bump do
24
+ # Auto-discover gem configuration
25
+ def devtools_gem
26
+ @devtools_gem ||= begin
27
+ version_file = RatatuiRuby::Devtools.version_file
28
+ gem_name = RatatuiRuby::Devtools.gem_name
29
+
30
+ manifests = [
31
+ Manifest.new(
32
+ path: version_file,
33
+ pattern: /(?<=VERSION = ")[^"]+(?=")/,
34
+ primary: true
35
+ ),
36
+ ]
37
+
38
+ # Optionally add Cargo.toml if this gem has Rust extensions
39
+ cargo_toml = "ext/#{gem_name}/Cargo.toml"
40
+ if File.exist?(cargo_toml)
41
+ manifests << Manifest.new(
42
+ path: cargo_toml,
43
+ pattern: /(?<=^version = ")[^"]+(?=")/,
44
+ primary: false
45
+ )
46
+ end
47
+
48
+ # Optionally use Cargo lockfile if it exists
49
+ cargo_lock = "ext/#{gem_name}/Cargo.lock"
50
+ lockfile = if File.exist?(cargo_lock)
51
+ CargoLockfile.new(
52
+ path: cargo_lock,
53
+ dir: "ext/#{gem_name}",
54
+ name: gem_name
55
+ )
56
+ else
57
+ # No-op lockfile for pure Ruby gems
58
+ NullLockfile.new
59
+ end
60
+
61
+ RubyGem.new(
62
+ manifests:,
63
+ lockfile:,
64
+ changelog: Changelog.new
65
+ )
66
+ end
67
+ end
68
+
69
+ SemVer::SEGMENTS.each do |segment|
70
+ desc "Bump #{segment} version"
71
+ task segment do
72
+ devtools_gem.bump(segment)
73
+ end
74
+ end
75
+
76
+ desc "Set exact version (e.g. rake bump:exact[0.1.0])"
77
+ task :exact, [:version] do |_, args|
78
+ devtools_gem.set(args[:version])
79
+ end
80
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ # Cargo tasks for Rust extensions.
9
+ # Only registered if ext/ directory exists.
10
+
11
+ if Dir.exist?("ext")
12
+ namespace :cargo do
13
+ desc "Run cargo fmt"
14
+ task :fmt do
15
+ ext_dirs = Dir.glob("ext/*").select { |d| File.directory?(d) && File.exist?("#{d}/Cargo.toml") }
16
+ ext_dirs.each do |dir|
17
+ sh "cd #{dir} && cargo fmt --all -- --check"
18
+ end
19
+ end
20
+
21
+ desc "Run cargo clippy"
22
+ task :clippy do
23
+ ext_dirs = Dir.glob("ext/*").select { |d| File.directory?(d) && File.exist?("#{d}/Cargo.toml") }
24
+ ext_dirs.each do |dir|
25
+ sh "cd #{dir} && cargo clippy -- -D warnings"
26
+ end
27
+ end
28
+
29
+ desc "Run cargo test"
30
+ task :test do
31
+ ext_dirs = Dir.glob("ext/*").select { |d| File.directory?(d) && File.exist?("#{d}/Cargo.toml") }
32
+ ext_dirs.each do |dir|
33
+ sh "cd #{dir} && cargo test"
34
+ end
35
+ end
36
+
37
+ namespace :fix do
38
+ desc "Auto-fix Clippy warnings (most aggressive)"
39
+ task :clippy do
40
+ ext_dirs = Dir.glob("ext/*").select { |d| File.directory?(d) && File.exist?("#{d}/Cargo.toml") }
41
+ ext_dirs.each do |dir|
42
+ sh "cd #{dir} && cargo clippy --fix --allow-dirty --allow-staged"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end