dotenv-merge 1.0.0 → 1.0.1

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.
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
- | 📍 NOTE |
2
- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
3
- | RubyGems (the [GitHub org][rubygems-org], not the website) [suffered][draper-security] a [hostile takeover][ellen-takeover] in September 2025. |
4
- | Ultimately [4 maintainers][simi-removed] were [hard removed][martin-removed] and a reason has been given for only 1 of those, while 2 others resigned in protest. |
5
- | It is a [complicated story][draper-takeover] which is difficult to [parse quickly][draper-lies]. |
6
- | Simply put - there was active policy for adding or removing maintainers/owners of [rubygems][rubygems-maint-policy] and [bundler][bundler-maint-policy], and those [policies were not followed][policy-fail]. |
7
- | I'm adding notes like this to gems because I [don't condone theft][draper-theft] of repositories or gems from their rightful owners. |
8
- | If a similar theft happened with my repos/gems, I'd hope some would stand up for me. |
9
- | Disenfranchised former-maintainers have started [gem.coop][gem-coop]. |
10
- | Once available I will publish there exclusively; unless RubyCentral makes amends with the community. |
11
- | The ["Technology for Humans: Joel Draper"][reinteractive-podcast] podcast episode by [reinteractive][reinteractive] is the most cogent summary I'm aware of. |
12
- | See [here][gem-naming], [here][gem-coop] and [here][martin-ann] for more info on what comes next. |
13
- | What I'm doing: A (WIP) proposal for [bundler/gem scopes][gem-scopes], and a (WIP) proposal for a federated [gem server][gem-server]. |
1
+ | 📍 NOTE |
2
+ | --- |
3
+ | RubyGems (the [GitHub org](https://github.com/rubygems/), not the website) [suffered](https://joel.drapper.me/p/ruby-central-security-measures/) a [hostile takeover](https://pup-e.com/blog/goodbye-rubygems/) in September 2025. |
4
+ | Ultimately [4 maintainers](https://www.reddit.com/r/ruby/s/gOk42POCaV) were [hard removed](https://bsky.app/profile/martinemde.com/post/3m3occezxxs2q) and a reason has been given for only 1 of those, while 2 others resigned in protest. |
5
+ | It is a [complicated story](https://joel.drapper.me/p/ruby-central-takeover/) which is difficult to [parse quickly](https://joel.drapper.me/p/ruby-central-fact-check/). |
6
+ | Simply put - there was active policy for adding or removing maintainers/owners of [rubygems](https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/rubygems/POLICIES.md?plain=1#L187-L196) and [bundler](https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/bundler/playbooks/TEAM_CHANGES.md), and those [policies were not followed](https://www.reddit.com/r/ruby/comments/1ove9vp/rubycentral_hates_this_one_fact/). |
7
+ | I'm adding notes like this to gems because I [don't condone theft](https://joel.drapper.me/p/ruby-central/) of repositories or gems from their rightful owners. |
8
+ | If a similar theft happened with my repos/gems, I'd hope some would stand up for me. |
9
+ | Disenfranchised former-maintainers have started [gem.coop](https://gem.coop). |
10
+ | Once available I will publish there exclusively; unless RubyCentral makes amends with the community. |
11
+ | The ["Technology for Humans: Joel Draper"](https://youtu.be/_H4qbtC5qzU?si=BvuBU90R2wAqD2E6) podcast episode by [reinteractive](https://reinteractive.com/ruby-on-rails) is the most cogent summary I'm aware of. |
12
+ | See [here](https://github.com/gem-coop/gem.coop/issues/12), [here](https://gem.coop) and [here](https://martinemde.com/2025/10/05/announcing-gem-coop.html) for more info on what comes next. |
13
+ | What I'm doing: A (WIP) proposal for [bundler/gem scopes](https://github.com/galtzo-floss/bundle-namespace), and a (WIP) proposal for a federated [gem server](https://github.com/galtzo-floss/gem-server). |
14
14
 
15
15
  [rubygems-org]: https://github.com/rubygems/
16
16
  [draper-security]: https://joel.drapper.me/p/ruby-central-security-measures/
@@ -31,7 +31,7 @@
31
31
  [rubygems-maint-policy]: https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/rubygems/POLICIES.md?plain=1#L187-L196
32
32
  [policy-fail]: https://www.reddit.com/r/ruby/comments/1ove9vp/rubycentral_hates_this_one_fact/
33
33
 
34
- [![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]][🖼️galtzo-discord] [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5][🖼️ruby-lang-i]][🖼️ruby-lang] [![kettle-rb Logo by Aboling0, CC BY-SA 4.0][🖼️kettle-rb-i]][🖼️kettle-rb]
34
+ [![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0](https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg)](https://discord.gg/3qme4XHNKN) [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5](https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg)](https://www.ruby-lang.org/) [![kettle-rb Logo by Aboling0, CC BY-SA 4.0](https://logos.galtzo.com/assets/images/kettle-rb/avatar-192px.svg)](https://github.com/kettle-rb)
35
35
 
36
36
  [🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
37
37
  [🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
@@ -42,15 +42,14 @@
42
42
 
43
43
  # ☯️ Dotenv::Merge
44
44
 
45
- [![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
45
+ [![Version](https://img.shields.io/gem/v/dotenv-merge.svg)](https://bestgems.org/gems/dotenv-merge) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/kettle-rb/dotenv-merge.svg)](http://github.com/kettle-rb/dotenv-merge/releases) [![License: MIT](https://img.shields.io/badge/License-MIT-259D6C.svg)](https://opensource.org/licenses/MIT) [![Downloads Rank](https://img.shields.io/gem/rd/dotenv-merge.svg)](https://bestgems.org/gems/dotenv-merge) [![Open Source Helpers](https://www.codetriage.com/kettle-rb/dotenv-merge/badges/users.svg)](https://www.codetriage.com/kettle-rb/dotenv-merge) [![CodeCov Test Coverage](https://codecov.io/gh/kettle-rb/dotenv-merge/graph/badge.svg)](https://codecov.io/gh/kettle-rb/dotenv-merge) [![Coveralls Test Coverage](https://coveralls.io/repos/github/kettle-rb/dotenv-merge/badge.svg?branch=main)](https://coveralls.io/github/kettle-rb/dotenv-merge?branch=main) [![QLTY Test Coverage](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge/coverage.svg)](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge/metrics/code?sort=coverageRating) [![QLTY Maintainability](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge/maintainability.svg)](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge) [![CI Heads](https://github.com/kettle-rb/dotenv-merge/actions/workflows/heads.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/heads.yml) [![CI Runtime Dependencies @ HEAD](https://github.com/kettle-rb/dotenv-merge/actions/workflows/dep-heads.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/dep-heads.yml) [![CI Current](https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml) [![CI Truffle Ruby](https://github.com/kettle-rb/dotenv-merge/actions/workflows/truffle.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/truffle.yml) [![Deps Locked](https://github.com/kettle-rb/dotenv-merge/actions/workflows/locked_deps.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/locked_deps.yml) [![Deps Unlocked](https://github.com/kettle-rb/dotenv-merge/actions/workflows/unlocked_deps.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/unlocked_deps.yml) [![CI Supported](https://github.com/kettle-rb/dotenv-merge/actions/workflows/supported.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/supported.yml) [![CI Test Coverage](https://github.com/kettle-rb/dotenv-merge/actions/workflows/coverage.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/coverage.yml) [![CI Style](https://github.com/kettle-rb/dotenv-merge/actions/workflows/style.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/style.yml) [![CodeQL](https://github.com/kettle-rb/dotenv-merge/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/security/code-scanning) [![Apache SkyWalking Eyes License Compatibility Check](https://github.com/kettle-rb/dotenv-merge/actions/workflows/license-eye.yml/badge.svg)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/license-eye.yml)
46
46
 
47
- `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
48
-
49
- ---
47
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know](https://discord.gg/3qme4XHNKN), as I may have missed the [discord notification](https://discord.gg/3qme4XHNKN).
50
48
 
49
+ -----
51
50
  `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
52
51
 
53
- [![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 at ko-fi.com][🖇kofi-img]][🖇kofi]
52
+ [![OpenCollective Backers](https://opencollective.com/kettle-rb/backers/badge.svg?style=flat)](https://opencollective.com/kettle-rb#backer) [![OpenCollective Sponsors](https://opencollective.com/kettle-rb/sponsors/badge.svg?style=flat)](https://opencollective.com/kettle-rb#sponsor) [![Sponsor Me on Github](https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github)](https://github.com/sponsors/pboling) [![Liberapay Goal Progress](https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat)](https://liberapay.com/pboling/donate) [![Donate on PayPal](https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal)](https://www.paypal.com/paypalme/peterboling) [![Buy me a coffee](https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat)](https://www.buymeacoffee.com/pboling) [![Donate on Polar](https://img.shields.io/badge/polar-donate-a51611.svg?style=flat)](https://polar.sh/pboling) [![Donate at ko-fi.com](https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat)](https://ko-fi.com/O5O86SNP4)
54
53
 
55
54
  ## 🌻 Synopsis
56
55
 
@@ -58,23 +57,23 @@ Dotenv::Merge is a standalone Ruby module that intelligently merges two versions
58
57
 
59
58
  ### Key Features
60
59
 
61
- - **Dotenv-Aware**: Understands dotenv file format (KEY=value, comments, exports)
62
- - **Intelligent**: Matches environment variables by key name
63
- - **Comment-Preserving**: Comments and blank lines are preserved in their context
64
- - **Freeze Block Support**: Respects freeze markers (default: `dotenv-merge:freeze` / `dotenv-merge:unfreeze`) for merge control - customizable to match your project's conventions
65
- - **Full Provenance**: Tracks origin of every line
66
- - **Standalone**: Minimal dependencies - just `ast-merge`
67
- - **Customizable**:
68
- - `signature_generator` - callable custom signature generators
69
- - `preference` - setting of `:template`, `:destination`, or a Hash for per-node-type preferences
70
- - `node_splitter` - Hash mapping node types to callables for per-node-type merge customization (see [ast-merge](https://github.com/kettle-rb/ast-merge) docs)
71
- - `add_template_only_nodes` - setting to retain variables that do not exist in destination
72
- - `freeze_token` - customize freeze block markers (default: `"dotenv-merge"`)
60
+ - **Dotenv-Aware**: Understands dotenv file format (KEY=value, comments, exports)
61
+ - **Intelligent**: Matches environment variables by key name
62
+ - **Comment-Preserving**: Comments and blank lines are preserved in their context
63
+ - **Freeze Block Support**: Respects freeze markers (default: `dotenv-merge:freeze` / `dotenv-merge:unfreeze`) for merge control - customizable to match your project's conventions
64
+ - **Full Provenance**: Tracks origin of every line
65
+ - **Standalone**: Minimal dependencies - just `ast-merge`
66
+ - **Customizable**:
67
+ - `signature_generator` - callable custom signature generators
68
+ - `preference` - setting of `:template`, `:destination`, or a Hash for per-node-type preferences
69
+ - `node_splitter` - Hash mapping node types to callables for per-node-type merge customization (see [ast-merge](https://github.com/kettle-rb/ast-merge) docs)
70
+ - `add_template_only_nodes` - setting to retain variables that do not exist in destination
71
+ - `freeze_token` - customize freeze block markers (default: `"dotenv-merge"`)
73
72
 
74
73
  ### Supported Line Types
75
74
 
76
75
  | Line Type | Format | Matching Behavior |
77
- |-----------|--------|-------------------|
76
+ | --- | --- | --- |
78
77
  | Assignment | `KEY=value` | Variables match by key name |
79
78
  | Export | `export KEY=value` | Treated as assignment with export flag |
80
79
  | Comment | `# comment text` | Preserved in context |
@@ -85,7 +84,7 @@ Dotenv::Merge is a standalone Ruby module that intelligently merges two versions
85
84
 
86
85
  ### Example
87
86
 
88
- ```ruby
87
+ ``` ruby
89
88
  require "dotenv/merge"
90
89
 
91
90
  template = File.read("template.env")
@@ -97,139 +96,35 @@ result = merger.merge
97
96
  File.write("merged.env", result.to_s)
98
97
  ```
99
98
 
100
- ### Part of a gem family
101
-
102
- | Gem | File Type | Parser |
103
- |-----|-----------|--------|
104
- | [ast-merge](https://github.com/kettle-rb/ast-merge) | Text | internal |
105
- | [prism-merge](https://github.com/kettle-rb/prism-merge) | Ruby | Prism |
106
- | [psych-merge](https://github.com/kettle-rb/psych-merge) | YAML | Psych |
107
- | [json-merge](https://github.com/kettle-rb/json-merge) | JSON | tree-sitter-json |
108
- | [jsonc-merge](https://github.com/kettle-rb/jsonc-merge) | JSONC | ⚠️ [tree-sitter-jsonc](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc) (PoC) |
109
- | [bash-merge](https://github.com/kettle-rb/bash-merge) | Shell | tree-sitter-bash |
110
- | [rbs-merge](https://github.com/kettle-rb/rbs-merge) | RBS Types | RBS |
111
- | **dotenv-merge** | Dotenv | internal |
112
- | [toml-merge](https://github.com/kettle-rb/toml-merge) | TOML | tree-sitter-toml |
113
- | [markdown-merge](https://github.com/kettle-rb/markdown-merge) | Markdown | _base classes_ |
114
- | [markly-merge](https://github.com/kettle-rb/markly-merge) | Markdown | Markly (cmark-gfm) |
115
- | [commonmarker-merge](https://github.com/kettle-rb/commonmarker-merge) | Markdown | Commonmarker (Comrak) |
116
-
117
- **Example implementations** for the gem templating use case:
118
-
119
- | Gem | Purpose |
120
- |-----|---------|
121
- | [kettle-dev](https://github.com/kettle-rb/kettle-dev) | Gem templating tool |
122
- | [kettle-jem](https://github.com/kettle-rb/kettle-jem) | Gem template library |
123
-
124
- ### Configuration
125
-
126
- ```ruby
127
- merger = Dotenv::Merge::SmartMerger.new(
128
- template_content,
129
- dest_content,
130
- # Which version to prefer when variables match
131
- # :destination (default) - keep destination values
132
- # :template - use template values
133
- preference: :destination,
134
-
135
- # Whether to add template-only variables to the result
136
- # false (default) - only include variables that exist in destination
137
- # true - include all template variables
138
- add_template_only_nodes: false,
139
-
140
- # Token for freeze block markers
141
- # Default: "dotenv-merge"
142
- # Looks for: # dotenv-merge:freeze / # dotenv-merge:unfreeze
143
- freeze_token: "dotenv-merge",
144
-
145
- # Custom signature generator (optional)
146
- # Receives an EnvLine, returns a signature array or nil
147
- signature_generator: ->(line) { [:env, line.key] if line.assignment? },
148
- )
149
- ```
150
-
151
- ### Basic Usage
152
-
153
- #### Simple Merge
154
-
155
- ```ruby
156
- require "dotenv/merge"
157
-
158
- # Template defines the structure
159
- template = <<~ENV
160
- # Database configuration
161
- DATABASE_URL=postgres://localhost/myapp_dev
162
- DATABASE_POOL=5
163
-
164
- # API keys
165
- API_KEY=your_api_key_here
166
- API_SECRET=your_secret_here
167
- ENV
168
-
169
- # Destination has customizations
170
- destination = <<~ENV
171
- # Database configuration
172
- DATABASE_URL=postgres://production.example.com/myapp
173
- DATABASE_POOL=25
174
-
175
- # Custom setting not in template
176
- CUSTOM_SETTING=my_value
177
- ENV
178
-
179
- merger = Dotenv::Merge::SmartMerger.new(template, destination)
180
- result = merger.merge
181
- puts result
182
- ```
183
-
184
- #### Using Freeze Blocks
185
-
186
- Freeze blocks protect sections from being overwritten during merge:
187
99
 
188
- ```env
189
- # Database configuration
190
- DATABASE_URL=postgres://localhost/myapp_dev
191
-
192
- # dotenv-merge:freeze Custom API credentials
193
- API_KEY=my_secret_production_key
194
- API_SECRET=super_secret_value
195
- # dotenv-merge:unfreeze
196
-
197
- # Other settings
198
- DEBUG=false
199
- ```
200
-
201
- Content between `# dotenv-merge:freeze` and `# dotenv-merge:unfreeze` markers is preserved from the destination file, regardless of what the template contains.
202
-
203
- #### Adding Template-Only Variables
100
+ ### The `*-merge` Gem Family
204
101
 
205
- ```ruby
206
- merger = Dotenv::Merge::SmartMerger.new(
207
- template,
208
- destination,
209
- add_template_only_nodes: true,
210
- )
211
- result = merger.merge
212
- # Result includes variables from template that don't exist in destination
213
- ```
102
+ The `*-merge` gem family provides intelligent, AST-based merging for various file formats. At the foundation is [tree_haver][tree_haver], which provides a unified cross-Ruby parsing API that works seamlessly across MRI, JRuby, and TruffleRuby.
103
+
104
+ | Gem | Format | Parser Backend(s) | Description |
105
+ |------------------------------------------|----------|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
106
+ | [tree_haver][tree_haver] | Multi | MRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus | **Foundation**: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP) |
107
+ | [ast-merge][ast-merge] | Text | internal | **Infrastructure**: Shared base classes and merge logic for all `*-merge` gems |
108
+ | [prism-merge][prism-merge] | Ruby | [Prism][prism] | Smart merge for Ruby source files |
109
+ | [psych-merge][psych-merge] | YAML | [Psych][psych] | Smart merge for YAML files |
110
+ | [json-merge][json-merge] | JSON | [tree-sitter-json][ts-json] (via tree_haver) | Smart merge for JSON files |
111
+ | [jsonc-merge][jsonc-merge] | JSONC | [tree-sitter-jsonc][ts-jsonc] (via tree_haver) | ⚠️ Proof of concept; Smart merge for JSON with Comments |
112
+ | [bash-merge][bash-merge] | Bash | [tree-sitter-bash][ts-bash] (via tree_haver) | Smart merge for Bash scripts |
113
+ | [rbs-merge][rbs-merge] | RBS | [RBS][rbs] | Smart merge for Ruby type signatures |
114
+ | [dotenv-merge][dotenv-merge] | Dotenv | internal | Smart merge for `.env` files |
115
+ | [toml-merge][toml-merge] | TOML | [Citrus + toml-rb][toml-rb] (default, via tree_haver), [tree-sitter-toml][ts-toml] (via tree_haver) | Smart merge for TOML files |
116
+ | [markdown-merge][markdown-merge] | Markdown | [Commonmarker][commonmarker] / [Markly][markly] (via tree_haver) | **Foundation**: Shared base for Markdown mergers with inner code block merging |
117
+ | [markly-merge][markly-merge] | Markdown | [Markly][markly] (via tree_haver) | Smart merge for Markdown (CommonMark via cmark-gfm C) |
118
+ | [commonmarker-merge][commonmarker-merge] | Markdown | [Commonmarker][commonmarker] (via tree_haver) | Smart merge for Markdown (CommonMark via comrak Rust) |
214
119
 
215
- ### The `*-merge` Gem Family
120
+ **Example implementations** for the gem templating use case:
216
121
 
217
- This gem is part of a family of gems that provide intelligent merging for various file formats:
218
-
219
- | Gem | Format | Parser | Description |
220
- |-----|--------|--------|-------------|
221
- | [ast-merge][ast-merge] | Text | internal | Shared infrastructure for all `*-merge` gems |
222
- | [prism-merge][prism-merge] | Ruby | [Prism][prism] | Smart merge for Ruby source files |
223
- | [psych-merge][psych-merge] | YAML | [Psych][psych] | Smart merge for YAML files |
224
- | [json-merge][json-merge] | JSON | [tree-sitter-json][ts-json] | Smart merge for JSON files |
225
- | [jsonc-merge][jsonc-merge] | JSONC | [tree-sitter-jsonc][ts-jsonc] | ⚠️ Proof of concept; Smart merge for JSON with Comments |
226
- | [bash-merge][bash-merge] | Bash | [tree-sitter-bash][ts-bash] | Smart merge for Bash scripts |
227
- | [rbs-merge][rbs-merge] | RBS | [RBS][rbs] | Smart merge for Ruby type signatures |
228
- | [dotenv-merge][dotenv-merge] | Dotenv | internal ([dotenv][dotenv]) | Smart merge for `.env` files |
229
- | [toml-merge][toml-merge] | TOML | [tree-sitter-toml][ts-toml] | Smart merge for TOML files |
230
- | [markly-merge][markly-merge] | Markdown | [Markly][markly] | Smart merge for Markdown (CommonMark via libcmark-gfm) |
231
- | [commonmarker-merge][commonmarker-merge] | Markdown | [Commonmarker][commonmarker] | Smart merge for Markdown (CommonMark via comrak) |
122
+ | Gem | Purpose | Description |
123
+ | --- | --- | --- |
124
+ | [kettle-dev](https://github.com/kettle-rb/kettle-dev) | Gem Development | Gem templating tool using `*-merge` gems |
125
+ | [kettle-jem](https://github.com/kettle-rb/kettle-jem) | Gem Templating | Gem template library with smart merge support |
232
126
 
127
+ [tree_haver]: https://github.com/kettle-rb/tree_haver
233
128
  [ast-merge]: https://github.com/kettle-rb/ast-merge
234
129
  [prism-merge]: https://github.com/kettle-rb/prism-merge
235
130
  [psych-merge]: https://github.com/kettle-rb/psych-merge
@@ -239,53 +134,57 @@ This gem is part of a family of gems that provide intelligent merging for variou
239
134
  [rbs-merge]: https://github.com/kettle-rb/rbs-merge
240
135
  [dotenv-merge]: https://github.com/kettle-rb/dotenv-merge
241
136
  [toml-merge]: https://github.com/kettle-rb/toml-merge
137
+ [markdown-merge]: https://github.com/kettle-rb/markdown-merge
242
138
  [markly-merge]: https://github.com/kettle-rb/markly-merge
243
139
  [commonmarker-merge]: https://github.com/kettle-rb/commonmarker-merge
140
+ [kettle-dev]: https://github.com/kettle-rb/kettle-dev
141
+ [kettle-jem]: https://github.com/kettle-rb/kettle-jem
244
142
  [prism]: https://github.com/ruby/prism
245
143
  [psych]: https://github.com/ruby/psych
246
144
  [ts-json]: https://github.com/tree-sitter/tree-sitter-json
247
- [ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
248
145
  [ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
249
146
  [ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
250
147
  [rbs]: https://github.com/ruby/rbs
251
- [dotenv]: https://github.com/bkeepers/dotenv
252
- [markly]: https://github.com/kivikakk/markly
148
+ [toml-rb]: https://github.com/emancu/toml-rb
149
+ [markly]: https://github.com/ioquatix/markly
253
150
  [commonmarker]: https://github.com/gjtorikian/commonmarker
151
+ [ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
152
+ [dotenv]: https://github.com/bkeepers/dotenv
254
153
 
255
154
  ## 💡 Info you can shake a stick at
256
155
 
257
- | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
258
- |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
259
- | Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
260
- | Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
261
- | Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
262
- | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
263
- | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
264
- | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
265
- | Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
266
- | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
267
- | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
268
- | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
156
+ | Tokens to Remember | [![Gem name](https://img.shields.io/badge/name-dotenv--merge-3C2D2D.svg?style=square&logo=rubygems&logoColor=red)](https://bestgems.org/gems/dotenv-merge) [![Gem namespace](https://img.shields.io/badge/namespace-Dotenv::Merge-3C2D2D.svg?style=square&logo=ruby&logoColor=white)](https://github.com/kettle-rb/dotenv-merge) |
157
+ | --- | --- |
158
+ | Works with JRuby | [![JRuby 10.0 Compat](https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml) [![JRuby HEAD Compat](https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/heads.yml) |
159
+ | Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat](https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/truffle.yml) [![Truffle Ruby 24.1 Compat](https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml) |
160
+ | Works with MRI Ruby 3 | [![Ruby 3.2 Compat](https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/supported.yml) [![Ruby 3.3 Compat](https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/supported.yml) [![Ruby 3.4 Compat](https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml) [![Ruby HEAD Compat](https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue)](https://github.com/kettle-rb/dotenv-merge/actions/workflows/heads.yml) |
161
+ | Support & Community | [![Join Me on Daily.dev's RubyFriends](https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white)](https://app.daily.dev/squads/rubyfriends) [![Live Chat on Discord](https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord)](https://discord.gg/3qme4XHNKN) [![Get help from me on Upwork](https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white)](https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share) [![Get help from me on Codementor](https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white)](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github) |
162
+ | Source | [![Source on GitLab.com](https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange)](https://gitlab.com/kettle-rb/dotenv-merge/) [![Source on CodeBerg.org](https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue)](https://codeberg.org/kettle-rb/dotenv-merge) [![Source on Github.com](https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green)](https://github.com/kettle-rb/dotenv-merge) [![The best SHA: dQw4w9WgXcQ\!](https://img.shields.io/badge/KLOC-0.326-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue)](https://www.youtube.com/watch?v=dQw4w9WgXcQ) |
163
+ | Documentation | [![Current release on RubyDoc.info](https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white)](http://rubydoc.info/gems/dotenv-merge) [![YARD on Galtzo.com](https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white)](https://dotenv-merge.galtzo.com) [![Maintainer Blog](https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange)](http://www.railsbling.com/tags/dotenv-merge) [![GitLab Wiki](https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white)](https://gitlab.com/kettle-rb/dotenv-merge/-/wikis/home) [![GitHub Wiki](https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/kettle-rb/dotenv-merge/wiki) |
164
+ | Compliance | [![License: MIT](https://img.shields.io/badge/License-MIT-259D6C.svg)](https://opensource.org/licenses/MIT) [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes](https://img.shields.io/badge/Apache_Compatible:_Category_A-%E2%9C%93-259D6C.svg?style=flat&logo=Apache)](https://dev.to/galtzo/how-to-check-license-compatibility-41h0) [![📄ilo-declaration-img](https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat)](https://www.ilo.org/declaration/lang--en/index.htm) [![Security Policy](https://img.shields.io/badge/security-policy-259D6C.svg?style=flat)](SECURITY.md) [![Contributor Covenant 2.1](https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg)](CODE_OF_CONDUCT.md) [![SemVer 2.0.0](https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat)](https://semver.org/spec/v2.0.0.html) |
165
+ | Style | [![Enforced Code Style Linter](https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white)](https://github.com/rubocop-lts/rubocop-lts) [![Keep-A-Changelog 1.0.0](https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat)](https://keepachangelog.com/en/1.0.0/) [![Gitmoji Commits](https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square)](https://gitmoji.dev) [![Compatibility appraised by: appraisal2](https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white)](https://github.com/appraisal-rb/appraisal2) |
166
+ | Maintainer 🎖️ | [![Follow Me on LinkedIn](https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat&logo=newjapanprowrestling)](http://www.linkedin.com/in/peterboling) [![Follow Me on Ruby.Social](https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo)](https://ruby.social/@galtzo) [![Follow Me on Bluesky](https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white)](https://bsky.app/profile/galtzo.com) [![Contact Maintainer](https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red)](http://www.railsbling.com/contact) [![My technical writing](https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white)](https://dev.to/galtzo) |
167
+ | `...` 💖 | [![Find Me on WellFound:](https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound)](https://wellfound.com/u/peter-boling) [![Find Me on CrunchBase](https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase)](https://www.crunchbase.com/person/peter-boling) [![My LinkTree](https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree)](https://linktr.ee/galtzo) [![More About Me](https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white)](https://about.me/peter.boling) [🧊](https://codeberg.org/pboling) [🐙](https://github.org/pboling) [🛖](https://sr.ht/~galtzo/) [🧪](https://gitlab.com/pboling) |
269
168
 
270
169
  ### Compatibility
271
170
 
272
171
  Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRuby.
273
172
 
274
- | 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
275
- |------------------------------------------------|--------------------------------------------------------|
276
- | 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
173
+ | 🚚 *Amazing* test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
174
+ | --- | --- |
175
+ | 👟 Check it out\! | ✨ [github.com/appraisal-rb/appraisal2](https://github.com/appraisal-rb/appraisal2) ✨ |
277
176
 
278
177
  ### Federated DVCS
279
178
 
280
179
  <details markdown="1">
281
180
  <summary>Find this repo on federated forges (Coming soon!)</summary>
282
181
 
283
- | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
284
- |-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
285
- | 🧪 [kettle-rb/dotenv-merge on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
286
- | 🧊 [kettle-rb/dotenv-merge on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
287
- | 🐙 [kettle-rb/dotenv-merge on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
288
- | 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] |
182
+ | Federated [DVCS](https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/) Repository | Status | Issues | PRs | Wiki | CI | Discussions |
183
+ | --- | --- | --- | --- | --- | --- | --- |
184
+ | 🧪 [kettle-rb/dotenv-merge on GitLab](https://gitlab.com/kettle-rb/dotenv-merge/) | The Truth | [💚](https://gitlab.com/kettle-rb/dotenv-merge/-/issues) | [💚](https://gitlab.com/kettle-rb/dotenv-merge/-/merge_requests) | [💚](https://gitlab.com/kettle-rb/dotenv-merge/-/wikis/home) | 🐭 Tiny Matrix | ➖ |
185
+ | 🧊 [kettle-rb/dotenv-merge on CodeBerg](https://codeberg.org/kettle-rb/dotenv-merge) | An Ethical Mirror ([Donate](https://donate.codeberg.org/)) | [💚](https://codeberg.org/kettle-rb/dotenv-merge/issues) | [💚](https://codeberg.org/kettle-rb/dotenv-merge/pulls) | ➖ | ⭕️ No Matrix | ➖ |
186
+ | 🐙 [kettle-rb/dotenv-merge on GitHub](https://github.com/kettle-rb/dotenv-merge) | Another Mirror | [💚](https://github.com/kettle-rb/dotenv-merge/issues) | [💚](https://github.com/kettle-rb/dotenv-merge/pulls) | [💚](https://github.com/kettle-rb/dotenv-merge/wiki) | 💯 Full Matrix | [💚](https://github.com/kettle-rb/dotenv-merge/discussions) |
187
+ | 🎮️ [Discord Server](https://discord.gg/3qme4XHNKN) | [![Live Chat on Discord](https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord)](https://discord.gg/3qme4XHNKN) | [Let's](https://discord.gg/3qme4XHNKN) | [talk](https://discord.gg/3qme4XHNKN) | [about](https://discord.gg/3qme4XHNKN) | [this](https://discord.gg/3qme4XHNKN) | [library\!](https://discord.gg/3qme4XHNKN) |
289
188
 
290
189
  </details>
291
190
 
@@ -300,31 +199,29 @@ Available as part of the Tidelift Subscription.
300
199
 
301
200
  The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
302
201
 
303
- [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
304
-
305
- - 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
306
- - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
307
- - 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
202
+ [![Get help from me on Tidelift](https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white)](https://tidelift.com/subscription/pkg/rubygems-dotenv-merge?utm_source=rubygems-dotenv-merge&utm_medium=referral&utm_campaign=readme)
308
203
 
204
+ - 💡Subscribe for support guarantees covering *all* your FLOSS dependencies
205
+ - 💡Tidelift is part of [Sonar](https://blog.tidelift.com/tidelift-joins-sonar)
206
+ - 💡Tidelift pays maintainers to maintain the software you depend on\!<br/>📊`@`Pointy Haired Boss: An [enterprise support](https://tidelift.com/subscription/pkg/rubygems-dotenv-merge?utm_source=rubygems-dotenv-merge&utm_medium=referral&utm_campaign=readme) subscription is "[never gonna let you down](https://www.youtube.com/watch?v=dQw4w9WgXcQ)", and *supports* open source maintainers
309
207
  Alternatively:
310
208
 
311
- - [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
312
- - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
313
- - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
314
-
209
+ - [![Live Chat on Discord](https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord)](https://discord.gg/3qme4XHNKN)
210
+ - [![Get help from me on Upwork](https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white)](https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share)
211
+ - [![Get help from me on Codementor](https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white)](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github)
315
212
  </details>
316
213
 
317
214
  ## ✨ Installation
318
215
 
319
216
  Install the gem and add to the application's Gemfile by executing:
320
217
 
321
- ```console
218
+ ``` console
322
219
  bundle add dotenv-merge
323
220
  ```
324
221
 
325
222
  If bundler is not being used to manage dependencies, install the gem by executing:
326
223
 
327
- ```console
224
+ ``` console
328
225
  gem install dotenv-merge
329
226
  ```
330
227
 
@@ -333,19 +230,19 @@ gem install dotenv-merge
333
230
  <details markdown="1">
334
231
  <summary>For Medium or High Security Installations</summary>
335
232
 
336
- This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
337
- [stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with
233
+ This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512](https://gitlab.com/kettle-rb/dotenv-merge/-/tree/main/checksums) checksums by
234
+ [stone\_checksums](https://github.com/galtzo-floss/stone_checksums). Be sure the gem you install hasn’t been tampered with
338
235
  by following the instructions below.
339
236
 
340
237
  Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:
341
238
 
342
- ```console
239
+ ``` console
343
240
  gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
344
241
  ```
345
242
 
346
243
  You only need to do that once. Then proceed to install with:
347
244
 
348
- ```console
245
+ ``` console
349
246
  gem install dotenv-merge -P HighSecurity
350
247
  ```
351
248
 
@@ -353,7 +250,7 @@ The `HighSecurity` trust profile will verify signed gems, and not allow the inst
353
250
 
354
251
  If you want to up your security game full-time:
355
252
 
356
- ```console
253
+ ``` console
357
254
  bundle config set --global trust-policy MediumSecurity
358
255
  ```
359
256
 
@@ -365,7 +262,7 @@ NOTE: Be prepared to track down certs for signed gems and add them the same way
365
262
 
366
263
  ## ⚙️ Configuration
367
264
 
368
- ```ruby
265
+ ``` ruby
369
266
  merger = Dotenv::Merge::SmartMerger.new(
370
267
  template,
371
268
  destination,
@@ -380,31 +277,33 @@ merger = Dotenv::Merge::SmartMerger.new(
380
277
 
381
278
  Control which source wins when both files have the same key:
382
279
 
383
- - **`:template`** - Template values replace destination values
384
- - Version files (`VERSION=2.0.0` should replace `VERSION=1.0.0`)
385
- - API endpoint updates (`API_URL=https://new-api.example.com`)
280
+ - **`:template`** - Template values replace destination values
281
+
282
+ - Version files (`VERSION=2.0.0` should replace `VERSION=1.0.0`)
283
+ - API endpoint updates (`API_URL=https://new-api.example.com`)
386
284
 
387
- - **`:destination`** (default) - Destination values are preserved
388
- - Local development settings
389
- - Project-specific customizations
285
+ - **`:destination`** (default) - Destination values are preserved
390
286
 
287
+ - Local development settings
288
+ - Project-specific customizations
391
289
  ### Template-Only Nodes
392
290
 
393
291
  Control whether to add entries that only exist in the template:
394
292
 
395
- - **`true`** - Add new entries from template
396
- - New required environment variables
397
- - New configuration options
293
+ - **`true`** - Add new entries from template
294
+
295
+ - New required environment variables
296
+ - New configuration options
398
297
 
399
- - **`false`** (default) - Skip template-only entries
400
- - Template has placeholder values
401
- - Destination is authoritative
298
+ - **`false`** (default) - Skip template-only entries
402
299
 
300
+ - Template has placeholder values
301
+ - Destination is authoritative
403
302
  ## 🔧 Basic Usage
404
303
 
405
304
  ### Simple Merge
406
305
 
407
- ```ruby
306
+ ``` ruby
408
307
  require "dotenv/merge"
409
308
 
410
309
  template = File.read("template.env")
@@ -420,19 +319,17 @@ File.write("merged.env", result)
420
319
 
421
320
  Freeze blocks protect sections of your `.env` file from being modified during merges:
422
321
 
423
- ```
424
- # << FREEZE: project_secrets
425
- DATABASE_URL=postgresql://localhost/myapp_dev
426
- SECRET_KEY_BASE=my_local_secret_key_value
427
- # >> FREEZE: project_secrets
322
+ # << FREEZE: project_secrets
323
+ DATABASE_URL=postgresql://localhost/myapp_dev
324
+ SECRET_KEY_BASE=my_local_secret_key_value
325
+ # >> FREEZE: project_secrets
428
326
 
429
- # These entries can be updated by template
430
- API_VERSION=v2
431
- ```
327
+ # These entries can be updated by template
328
+ API_VERSION=v2
432
329
 
433
330
  ### Adding Template-Only Entries
434
331
 
435
- ```ruby
332
+ ``` ruby
436
333
  # Template introduces a new required variable
437
334
  template = <<~ENV
438
335
  DATABASE_URL=postgresql://localhost/template_db
@@ -457,42 +354,42 @@ result = merger.merge
457
354
  While kettle-rb tools are free software and will always be, the project would benefit immensely from some funding.
458
355
  Raising a monthly budget of... "dollars" would make the project more sustainable.
459
356
 
460
- We welcome both individual and corporate sponsors! We also offer a
357
+ We welcome both individual and corporate sponsors\! We also offer a
461
358
  wide array of funding channels to account for your preferences
462
- (although currently [Open Collective][🖇osc] is our preferred funding platform).
359
+ (although currently [Open Collective](https://opencollective.com/kettle-rb) is our preferred funding platform).
463
360
 
464
361
  **If you're working in a company that's making significant use of kettle-rb tools we'd
465
362
  appreciate it if you suggest to your company to become a kettle-rb sponsor.**
466
363
 
467
364
  You can support the development of kettle-rb tools via
468
- [GitHub Sponsors][🖇sponsor],
469
- [Liberapay][⛳liberapay],
470
- [PayPal][🖇paypal],
471
- [Open Collective][🖇osc]
472
- and [Tidelift][🏙️entsup-tidelift].
473
-
474
- | 📍 NOTE |
475
- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
365
+ [GitHub Sponsors](https://github.com/sponsors/pboling),
366
+ [Liberapay](https://liberapay.com/pboling/donate),
367
+ [PayPal](https://www.paypal.com/paypalme/peterboling),
368
+ [Open Collective](https://opencollective.com/kettle-rb)
369
+ and [Tidelift](https://tidelift.com/subscription/pkg/rubygems-dotenv-merge?utm_source=rubygems-dotenv-merge&utm_medium=referral&utm_campaign=readme).
370
+
371
+ | 📍 NOTE |
372
+ | --- |
476
373
  | If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
477
374
 
478
375
  ### Open Collective for Individuals
479
376
 
480
- Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/kettle-rb#backer)]
377
+ Support us with a monthly donation and help us continue our activities. \[[Become a backer](https://opencollective.com/kettle-rb#backer)\]
481
378
 
482
- NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
379
+ NOTE: [kettle-readme-backers](https://github.com/kettle-rb/dotenv-merge/blob/main/exe/kettle-readme-backers) updates this list every day, automatically.
483
380
 
484
381
  <!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
485
- No backers yet. Be the first!
382
+ No backers yet. Be the first\!
486
383
  <!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
487
384
 
488
385
  ### Open Collective for Organizations
489
386
 
490
- Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/kettle-rb#sponsor)]
387
+ Become a sponsor and get your logo on our README on GitHub with a link to your site. \[[Become a sponsor](https://opencollective.com/kettle-rb#sponsor)\]
491
388
 
492
- NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
389
+ NOTE: [kettle-readme-backers](https://github.com/kettle-rb/dotenv-merge/blob/main/exe/kettle-readme-backers) updates this list every day, automatically.
493
390
 
494
391
  <!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
495
- No sponsors yet. Be the first!
392
+ No sponsors yet. Be the first\!
496
393
  <!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
497
394
 
498
395
  [kettle-readme-backers]: https://github.com/kettle-rb/dotenv-merge/blob/main/exe/kettle-readme-backers
@@ -503,50 +400,50 @@ I’m driven by a passion to foster a thriving open-source community – a space
503
400
 
504
401
  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`.
505
402
 
506
- 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.
403
+ I’m developing a new library, [floss\_funding](https://github.com/galtzo-floss/floss_funding), 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.
507
404
 
508
- **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
405
+ **[Floss-Funding.dev](https://floss-funding.dev): 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
509
406
 
510
- [![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]
407
+ [![OpenCollective Backers](https://opencollective.com/kettle-rb/backers/badge.svg?style=flat)](https://opencollective.com/kettle-rb#backer) [![OpenCollective Sponsors](https://opencollective.com/kettle-rb/sponsors/badge.svg?style=flat)](https://opencollective.com/kettle-rb#sponsor) [![Sponsor Me on Github](https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github)](https://github.com/sponsors/pboling) [![Liberapay Goal Progress](https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat)](https://liberapay.com/pboling/donate) [![Donate on PayPal](https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal)](https://www.paypal.com/paypalme/peterboling) [![Buy me a coffee](https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat)](https://www.buymeacoffee.com/pboling) [![Donate on Polar](https://img.shields.io/badge/polar-donate-a51611.svg?style=flat)](https://polar.sh/pboling) [![Donate to my FLOSS efforts at ko-fi.com](https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat)](https://ko-fi.com/O5O86SNP4) [![Donate to my FLOSS efforts using Patreon](https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat)](https://patreon.com/galtzo)
511
408
 
512
409
  ## 🔐 Security
513
410
 
514
- See [SECURITY.md][🔐security].
411
+ See [SECURITY.md](SECURITY.md).
515
412
 
516
413
  ## 🤝 Contributing
517
414
 
518
415
  If you need some ideas of where to help, you could work on adding more code coverage,
519
- or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
416
+ or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues](https://github.com/kettle-rb/dotenv-merge/issues), or [PRs](https://github.com/kettle-rb/dotenv-merge/pulls),
520
417
  or use the gem and think about how it could be better.
521
418
 
522
- We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
419
+ We [![Keep A Changelog](https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat)](https://keepachangelog.com/en/1.0.0/) so if you make changes, remember to update it.
523
420
 
524
- See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
421
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for more detailed instructions.
525
422
 
526
423
  ### 🚀 Release Instructions
527
424
 
528
- See [CONTRIBUTING.md][🤝contributing].
425
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
529
426
 
530
427
  ### Code Coverage
531
428
 
532
- [![Coverage Graph][🏀codecov-g]][🏀codecov]
429
+ [![Coverage Graph](https://codecov.io/gh/kettle-rb/dotenv-merge/graphs/tree.svg)](https://codecov.io/gh/kettle-rb/dotenv-merge)
533
430
 
534
- [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
431
+ [![Coveralls Test Coverage](https://coveralls.io/repos/github/kettle-rb/dotenv-merge/badge.svg?branch=main)](https://coveralls.io/github/kettle-rb/dotenv-merge?branch=main)
535
432
 
536
- [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
433
+ [![QLTY Test Coverage](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge/coverage.svg)](https://qlty.sh/gh/kettle-rb/projects/dotenv-merge/metrics/code?sort=coverageRating)
537
434
 
538
435
  ### 🪇 Code of Conduct
539
436
 
540
437
  Everyone interacting with this project's codebases, issue trackers,
541
- chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
438
+ chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1](https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg)](CODE_OF_CONDUCT.md).
542
439
 
543
440
  ## 🌈 Contributors
544
441
 
545
- [![Contributors][🖐contributors-img]][🖐contributors]
442
+ [![Contributors](https://contrib.rocks/image?repo=kettle-rb/dotenv-merge)](https://github.com/kettle-rb/dotenv-merge/graphs/contributors)
546
443
 
547
- Made with [contributors-img][🖐contrib-rocks].
444
+ Made with [contributors-img](https://contrib.rocks).
548
445
 
549
- Also see GitLab Contributors: [https://gitlab.com/kettle-rb/dotenv-merge/-/graphs/main][🚎contributors-gl]
446
+ Also see GitLab Contributors: <https://gitlab.com/kettle-rb/dotenv-merge/-/graphs/main>
550
447
 
551
448
  <details>
552
449
  <summary>⭐️ Star History</summary>
@@ -563,23 +460,23 @@ Also see GitLab Contributors: [https://gitlab.com/kettle-rb/dotenv-merge/-/graph
563
460
 
564
461
  ## 📌 Versioning
565
462
 
566
- This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver].
463
+ This Library adheres to [![Semantic Versioning 2.0.0](https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat)](https://semver.org/spec/v2.0.0.html).
567
464
  Violations of this scheme should be reported as bugs.
568
465
  Specifically, if a minor or patch version is released that breaks backward compatibility,
569
466
  a new version should be immediately released that restores compatibility.
570
467
  Breaking changes to the public API will only be introduced with new major versions.
571
468
 
572
469
  > dropping support for a platform is both obviously and objectively a breaking change <br/>
573
- >—Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
470
+ > —Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716](https://github.com/semver/semver/issues/716#issuecomment-869336139)
574
471
 
575
- I understand that policy doesn't work universally ("exceptions to every rule!"),
472
+ I understand that policy doesn't work universally ("exceptions to every rule\!"),
576
473
  but it is the policy here.
577
474
  As such, in many cases it is good to specify a dependency on this library using
578
- the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
475
+ the [Pessimistic Version Constraint](http://guides.rubygems.org/patterns/#pessimistic-version-constraint) with two digits of precision.
579
476
 
580
477
  For example:
581
478
 
582
- ```ruby
479
+ ``` ruby
583
480
  spec.add_dependency("dotenv-merge", "~> 1.0")
584
481
  ```
585
482
 
@@ -592,23 +489,22 @@ is a *breaking change* to an API, and for that reason the bike shedding is endle
592
489
  To get a better understanding of how SemVer is intended to work over a project's lifetime,
593
490
  read this article from the creator of SemVer:
594
491
 
595
- - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
596
-
492
+ - ["Major Version Numbers are Not Sacred"](https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html)
597
493
  </details>
598
494
 
599
- See [CHANGELOG.md][📌changelog] for a list of releases.
495
+ See [CHANGELOG.md](CHANGELOG.md) for a list of releases.
600
496
 
601
497
  ## 📄 License
602
498
 
603
499
  The gem is available as open source under the terms of
604
- the [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref].
605
- See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].
500
+ the [MIT License](LICENSE.txt) [![License: MIT](https://img.shields.io/badge/License-MIT-259D6C.svg)](https://opensource.org/licenses/MIT).
501
+ See [LICENSE.txt](LICENSE.txt) for the official [Copyright Notice](https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year).
606
502
 
607
503
  ### © Copyright
608
504
 
609
505
  <ul>
610
506
  <li>
611
- Copyright (c) 2025 Peter H. Boling, of
507
+ Copyright (c) 2025-2026 Peter H. Boling, of
612
508
  <a href="https://discord.gg/3qme4XHNKN">
613
509
  Galtzo.com
614
510
  <picture>
@@ -629,11 +525,11 @@ Please consider sponsoring me or the project.
629
525
 
630
526
  To join the community or get help 👇️ Join the Discord.
631
527
 
632
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
528
+ [![Live Chat on Discord](https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord)](https://discord.gg/3qme4XHNKN)
633
529
 
634
- To say "thanks!" ☝️ Join the Discord or 👇️ send money.
530
+ To say "thanks\!" ☝️ Join the Discord or 👇️ send money.
635
531
 
636
- [![Sponsor kettle-rb/dotenv-merge 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]
532
+ [![Sponsor kettle-rb/dotenv-merge on Open Source Collective](https://img.shields.io/opencollective/all/kettle-rb?style=for-the-badge)](https://opencollective.com/kettle-rb) 💌 [![Sponsor me on GitHub Sponsors](https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github)](https://github.com/sponsors/pboling) 💌 [![Sponsor me on Liberapay](https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611)](https://liberapay.com/pboling/donate) 💌 [![Donate on PayPal](https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A)](https://www.paypal.com/paypalme/peterboling)
637
533
 
638
534
  ### Please give the project a star ⭐ ♥.
639
535
 
@@ -798,7 +694,7 @@ Thanks for RTFM. ☺️
798
694
  [📌gitmoji]: https://gitmoji.dev
799
695
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
800
696
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
801
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.326-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
697
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.351-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
802
698
  [🔐security]: SECURITY.md
803
699
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
804
700
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year