Releases: kettle-rb/kettle-dev
v2.0.0
2.0.0 - 2026-02-25
- TAG: v2.0.0
- COVERAGE: 96.06% -- 2683/2793 lines in 20 files
- BRANCH COVERAGE: 79.16% -- 1113/1406 branches in 20 files
- 80.25% documented
Added
- New
kettle-gh-releaseexecutable for standalone GitHub release creation- Extracted from
kettle-releasestep 18 - Useful when RubyGems release succeeded but GitHub release failed
- Supports explicit version via
version=<VERSION>argument - Auto-detects version from
lib/**/version.rbif not specified - Requires
GITHUB_TOKENwithrepo:public_repo(classic) orcontents:writescope
- Extracted from
- Added
.kettle-dev.ymlconfiguration file for per-file merge options- Hybrid format:
defaultsfor shared merge options,patternsfor glob fallbacks,filesfor per-file config - Nested directory structure under
filesallows individual file configuration - Supports all
Prism::Merge::SmartMergeroptions:preference,add_template_only_nodes,freeze_token,max_recursion_depth - Added
TemplateHelpers.kettle_config,.config_for,.find_file_configmethods - Added spec coverage in
template_helpers_config_spec.rb
- Hybrid format:
Changed
- Updated documentation on hostile takeover of RubyGems
- BREAKING: Replaced
template_manifest.ymlwith.kettle-dev.yml- New hybrid format supports both glob patterns and per-file configuration
TemplateHelpers.load_manifestnow reads from.kettle-dev.ymlpatterns sectionTemplateHelpers.strategy_forchecks explicit file configs before falling back to patterns
- BREAKING: Simplified
SourceMergerto fully rely on prism-merge for AST merging- Reduced from ~610 lines to ~175 lines (71% reduction)
- Removed custom newline normalization - prism-merge preserves original formatting
- Removed custom comment deduplication logic - prism-merge handles this natively
- All strategies (
:skip,:replace,:append,:merge) now use prism-merge consistently - Freeze blocks (
kettle-dev:freeze/kettle-dev:unfreeze) handled by prism-merge'sfreeze_tokenoption
Removed
- Removed all
.exampletemplate files from the kettle-dev root and subdirectories
(27 files). Template files now live exclusively in kettle-jem'stemplate/directory.
Only.env.local.exampleis retained as a dev resource for all destination gems. - Removed unused methods from
SourceMerger:normalize_source- replaced by prism-mergenormalize_newlines- prism-merge preserves original formattingshebang?,magic_comment?,ruby_magic_comment_key?- no longer needed- Comment extraction/deduplication:
extract_magic_comments,extract_file_leading_comments,
create_comment_tuples,deduplicate_comment_sequences,deduplicate_sequences_pass1,
deduplicate_singles_pass2,extract_nodes_with_comments,count_blank_lines_before,
build_source_from_nodes - Unused comment restoration:
restore_custom_leading_comments,deduplicate_leading_comment_block,
extract_comment_lines,normalize_comment,leading_comment_block
- Removed unused constants:
RUBY_MAGIC_COMMENT_KEYS,MAGIC_COMMENT_REGEXES
Fixed
- Fixed
PrismAppraisalsvarious comment chunk spacing- extract_block_header:
- skips the blank spacer immediately above an
appraiseblock - treats any following blank line as the stop boundary once comment lines have been collected
- prevents preamble comments from being pulled into the first block’s header
- skips the blank spacer immediately above an
- restores expected ordering:
- magic comments and their blank line stay at the top
- block headers remain adjacent to their blocks
- preserves blank lines between comment chunks
- extract_block_header:
- Fixed
SourceMergerfreeze block location preservation- Freeze blocks now stay in their original location in the file structure
- Skip normalization for files with existing freeze blocks to prevent movement
- Only include contiguous comments immediately before freeze markers (no arbitrary 3-line lookback)
- Don't add freeze reminder to files that already have freeze/unfreeze blocks
- Prevents unrelated comments from being incorrectly captured in freeze block ranges
- Added comprehensive test coverage for multiple freeze blocks at different nesting levels
- Fixed
TemplateTaskto not override template summary/description with empty strings from destination gemspec- Only carries over summary/description when they contain actual content (non-empty)
- Allows token replacements to work correctly (e.g.,
kettle-dev summary→my-gem summary) - Prevents empty destination fields from erasing meaningful template values
- Fixed
SourceMergermagic comment ordering and freeze block protection- Magic comments now preserve original order
- No blank lines inserted between consecutive magic comments
- Freeze reminder block properly separated from magic comments (not merged)
- Leverages Prism's built-in
parse_result.magic_commentsAPI for accurate detection - Detects
kettle-dev:freeze/unfreezepairs using Prism, then reclassifies as file-level comments to keep blocks intact - Removed obsolete
is_magic_comment?method in favor of Prism's native detection
- Fixed
PrismGemspecandPrismGemfileto use pure Prism AST traversal instead of regex fallbacks- Removed regex-based
extract_gemspec_emojithat parsedspec.summary =andspec.description =with regex - Now traverses Prism AST to find Gem::Specification block, extracts summary/description nodes, and gets literal values
- Removed regex-based source line detection in
PrismGemfile.merge_gem_calls - Now uses
PrismUtils.statement_keyto find source statements via AST instead ofln =~ /^\s*source\s+/ - Aligns with project goal: move away from regex parsing toward proper AST manipulation with Prism
- All functionality preserved, tested, and working correctly
- Removed regex-based
- Fixed
PrismGemspec.replace_gemspec_fieldsblock parameter extraction to use Prism AST- CRITICAL: Was using regex fallback that incorrectly captured entire block body as parameter name
- Removed buggy regex fallback in favor of pure Prism AST traversal
- Now properly extracts block parameter from Prism::BlockParametersNode → Prism::ParametersNode → Prism::RequiredParameterNode
- Fixed
PrismGemspec.replace_gemspec_fieldsinsert offset calculation for emoji-containing gemspecs- CRITICAL: Was using character length (
String#length) instead of byte length (String#bytesize) to calculate insert offset - When gemspecs contain multi-byte UTF-8 characters (emojis like 🍲), character length != byte length
- This caused fields to be inserted at wrong byte positions, resulting in truncated strings and massive corruption
- Changed
body_src.rstrip.lengthtobody_src.rstrip.bytesizefor correct byte-offset calculations - Prevents gemspec templating from producing corrupted output with truncated dependency lines
- Added comprehensive debug logging to trace byte offset calculations and edit operations
- CRITICAL: Was using character length (
- Fixed
SourceMergervariable assignment duplication during merge operationsnode_signaturenow identifies variable/constant assignments by name only, not full source- Previously used full source text as signature, causing duplicates when assignment bodies differed
- Added specific handlers for: LocalVariableWriteNode, InstanceVariableWriteNode, ClassVariableWriteNode, ConstantWriteNode, GlobalVariableWriteNode
- Also added handlers for ClassNode and ModuleNode to match by name
- Example:
gem_version = ...assignments with different bodies now correctly merge instead of duplicating - Prevents
bin/kettle-dev-setupfrom creating duplicate variable assignments in gemspecs and other files - Added comprehensive specs for variable assignment deduplication and idempotency
- Fixed
SourceMergerconditional block duplication during merge operationsnode_signaturenow identifies conditional nodes (if/unless/case) by their predicate only- Previously used full source text, causing duplicate blocks when template updates conditional bodies
- Example: if ENV["FOO"] blocks with different bodies now correctly merge instead of duplicating
- Prevents
bin/kettle-dev-setupfrom creating duplicate if/else blocks in gemfiles - Added comprehensive specs for conditional merging behavior and idempotency
- Fixed
PrismGemspec.replace_gemspec_fieldsto use byte-aware string operations- CRITICAL: Was using character-based
String#[]=with Prism's byte offsets - This caused catastrophic corruption when emojis or multi-byte UTF-8 characters were present
- Symptoms: gemspec blocks duplicated/fragmented, statements escaped outside blocks
- Now uses
bytesliceand byte-aware concatenation for all edit operations - Prevents gemspec templating from producing mangled output with duplicated Gem::Specification blocks
- CRITICAL: Was using character-based
- Fixed
PrismGemspec.replace_gemspec_fieldsto correctly handle multi-byte UTF-8 characters (e.g., emojis)- Prism uses byte offsets, not character offsets, when parsing Ruby code
- Changed string slicing from
String#[]toString#byteslicefor all offset-based operations - Added validation to use
String#bytesizeinstead ofString#lengthfor offset bounds checking - Prevents
TypeError: no implicit conversion of nil into Stringwhen gemspecs contain emojis - Ensures gemspec field carryover works correctly with emoji in summary/description fields
- Enhanced error reporting to show backtraces when debug mode is enabled
Official Discord 👉️ [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite]
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![Open...
v1.2.4
1.2.4 - 2025-11-28
- TAG: v1.2.4
- COVERAGE: 93.53% -- 4701/5026 lines in 31 files
- BRANCH COVERAGE: 76.61% -- 1913/2497 branches in 31 files
- 69.78% documented
Fixed
- Fixed comment deduplication in
restore_custom_leading_commentsto prevent accumulation across multiple template runs- Comments from destination are now deduplicated before being merged back into result
- Fixes issue where
:replacestrategy (used bykettle-dev-setup --force) would accumulate duplicate comments - Ensures truly idempotent behavior when running templating multiple times on the same file
- Example:
frozen_string_literalcomments no longer multiply from 1→4→5→6 on repeated runs
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.2.3
1.2.3 - 2025-11-28
- TAG: v1.2.3
- COVERAGE: 93.43% -- 4681/5010 lines in 31 files
- BRANCH COVERAGE: 76.63% -- 1912/2495 branches in 31 files
- 70.55% documented
Fixed
- Fixed Gemfile parsing to properly deduplicate comments across multiple template runs
- Implemented two-pass comment deduplication: sequences first, then individual lines
- Magic comments (frozen_string_literal, encoding, etc.) are now properly deduplicated by content, not line position
- File-level comments are deduplicated while preserving leading comments attached to statements
- Ensures idempotent behavior when running templating multiple times on the same file
- Prevents accumulation of duplicate frozen_string_literal comments and comment blocks
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.2.2
1.2.2 - 2025-11-27
- TAG: v1.2.2
- COVERAGE: 93.28% -- 4596/4927 lines in 31 files
- BRANCH COVERAGE: 76.45% -- 1883/2463 branches in 31 files
- 70.00% documented
Added
- Prism AST-based manipulation of ruby during templating
- Gemfiles
- gemspecs
- .simplecov
- Stop rescuing Exception in certain scenarios (just StandardError)
- Refactored logging logic and documentation
- Prevent self-referential gemfile injection
- in Gemfiles, gemspecs, and Appraisals
- Improve reliability of coverage and documentation stats
- in the changelog version heading
- fails hard when unable to generate stats, unless
--no-strictprovided
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.2.1
[1.2.1] - 2025-11-25
- TAG: [v1.2.0][1.2.0t]
- COVERAGE: 94.38% -- 4066/4308 lines in 26 files
- BRANCH COVERAGE: 78.81% -- 1674/2124 branches in 26 files
- 69.14% documented
Changed
- Source merging switched from Regex-based string manipulation to Prism AST-based manipulation
- Comments are preserved in the resulting file
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.2.0
1.2.0 - 2025-11-25
- TAG: v1.2.0
- COVERAGE: 94.38% -- 4066/4308 lines in 26 files
- BRANCH COVERAGE: 78.81% -- 1674/2124 branches in 26 files
- 69.14% documented
Changed
- Source merging switched from Regex-based string manipulation to Prism AST-based manipulation
- Comments are preserved in the resulting file
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.1.60
1.1.60 - 2025-11-23
- TAG: v1.1.60
- COVERAGE: 94.38% -- 4066/4308 lines in 26 files
- BRANCH COVERAGE: 78.86% -- 1675/2124 branches in 26 files
- 79.89% documented
Added
- Add KETTLE_DEV_DEBUG to direnv defaults
- Documentation of the explicit policy violations of RubyGems.org leadership toward open source projects they funded
Fixed
- Prevent double test runs by ensuring only one of test/coverage/spec are in default task
- Add debugging when more than one registered
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.1.59
1.1.59 - 2025-11-13
- TAG: v1.1.59
- COVERAGE: 94.38% -- 4066/4308 lines in 26 files
- BRANCH COVERAGE: 78.77% -- 1673/2124 branches in 26 files
- 79.89% documented
Changed
- Improved default devcontainer with common dependencies of most Ruby projects
Fixed
- token replacement of {TARGET|GEM|NAME}
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.1.58
1.1.58 - 2025-11-13
- TAG: v1.1.58
- COVERAGE: 94.41% -- 4067/4308 lines in 26 files
- BRANCH COVERAGE: 78.77% -- 1673/2124 branches in 26 files
- 79.89% documented
Added
- Ignore more .idea plugin artifacts
Fixed
- bin/rake yard no longer overrides the .yardignore for checksums
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?
v1.1.57
1.1.57 - 2025-11-13
- TAG: v1.1.57
- COVERAGE: 94.36% -- 4065/4308 lines in 26 files
- BRANCH COVERAGE: 78.81% -- 1674/2124 branches in 26 files
- 79.89% documented
Added
- New Rake task:
appraisal:reset— deletes all Appraisal lockfiles (gemfiles/*.gemfile.lock). - Improved .env.local.example template
Fixed
- .yardignore more comprehensively ignores directories that are not relevant to documentation
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?