philiprehberger-env_diff 0.4.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e512ec86bdcf2702d8c0d53552284aa43f5f51b6506e61d1275aad3af1e9dca
4
- data.tar.gz: 7e65c48367a0e9d853c647e6339e40063ba23f8873f5e9668009a2c081cef5db
3
+ metadata.gz: 8fa424560bb3f733e97c91a5e643f04306eb81aa032184970889e71d987910c6
4
+ data.tar.gz: 32748ed41ac378a1dd96fdf74a91ebd1f7e72fa74628510cd2fcdbb61e4d3c74
5
5
  SHA512:
6
- metadata.gz: ec3677be78c8bc9df56fe6f2229fe625feae051ba6f6558f9d3bf0f033f0d575528a69ffe1c09642a6b8879bd01a363be75ef5a2524ddb46a19effe06fc59b1a
7
- data.tar.gz: c79673bf83bfde8cd8aec1bcc824e08b6e82f7e39314d31c5a35fae8f87469f9398196d7dd04e92524550af9ac0a602152ed368cc27e22a8639f268c50085ec3
6
+ metadata.gz: 1738b36b798d79c436f4471ee7a44f755d8d83ef4d2ba89ffcee2f7724de895573cdc3b198f7943c3c9d49c8737a9cc1887b76a0c035129a8f9b047d882469a1
7
+ data.tar.gz: bf909ac4ba6905c9889ce456b7c29d1ef087618562954305a553f58690c0f635dc32c9a8278de0946936ac33e27d0019e3ee3510535c3621482913aff8878c6a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-05-08
11
+
12
+ ### Added
13
+ - `EnvDiff.to_csv(diff, mask: [])` — exports a structured `key,status,source,target` CSV (RFC 4180 quoting). Complements the existing `to_markdown` and `to_html` exports for spreadsheet review. Supports the same `mask:` substring redaction as `Diff#summary`.
14
+
10
15
  ## [0.4.0] - 2026-04-24
11
16
 
12
17
  ### Added
data/README.md CHANGED
@@ -122,7 +122,7 @@ diff.unchanged # => ["DB_HOST"]
122
122
 
123
123
  ### Export Formats
124
124
 
125
- Format a diff result as a Markdown or HTML table:
125
+ Format a diff result as a Markdown, HTML, or CSV table:
126
126
 
127
127
  ```ruby
128
128
  diff = Philiprehberger::EnvDiff.compare(source, target)
@@ -141,6 +141,13 @@ puts Philiprehberger::EnvDiff.to_html(diff)
141
141
  # <tr><td>NEW_KEY</td><td>added</td><td></td><td>added</td></tr>
142
142
  # ...
143
143
  # </table>
144
+
145
+ puts Philiprehberger::EnvDiff.to_csv(diff, mask: ['SECRET'])
146
+ # key,status,source,target
147
+ # NEW_KEY,added,,added
148
+ # OLD_KEY,removed,remove_me,
149
+ # DATABASE_URL,changed,postgres://localhost/dev,postgres://prod-host/app
150
+ # SECRET,unchanged,***,***
144
151
  ```
145
152
 
146
153
  ### Compare against system ENV
@@ -180,6 +187,7 @@ ENV
180
187
  | `EnvDiff.validate(target, required:)` | Check that all required keys exist in target; returns `{ valid:, missing: }` |
181
188
  | `EnvDiff.to_markdown(diff)` | Format a diff result as a Markdown table string |
182
189
  | `EnvDiff.to_html(diff)` | Format a diff result as an HTML table string |
190
+ | `EnvDiff.to_csv(diff, mask: [])` | Format a diff result as a `key,status,source,target` CSV; supports value masking |
183
191
  | `Diff#added` | Array of keys in target but not source |
184
192
  | `Diff#removed` | Array of keys in source but not target |
185
193
  | `Diff#changed` | Hash of keys with different values |
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module EnvDiff
5
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
@@ -63,6 +63,49 @@ module Philiprehberger
63
63
  lines.join("\n")
64
64
  end
65
65
 
66
+ # Format a diff result as a CSV string with header `key,status,source,target`.
67
+ #
68
+ # Values containing commas, quotes, or newlines are quoted per RFC 4180.
69
+ # Keys whose name contains any of `mask` (substring match, case-insensitive)
70
+ # have their source and target values redacted to `***`.
71
+ #
72
+ # @param diff [Diff] the diff to format
73
+ # @param mask [Array<String>] substrings to match against keys for redaction
74
+ # @return [String] CSV body with a trailing newline
75
+ def self.to_csv(diff, mask: [])
76
+ data = diff.to_h
77
+ lines = ['key,status,source,target']
78
+
79
+ diff.added.each { |key| lines << csv_row(key, 'added', '', data[:added][key], mask) }
80
+ diff.removed.each { |key| lines << csv_row(key, 'removed', data[:removed][key], '', mask) }
81
+ diff.changed.each do |key, vals|
82
+ lines << csv_row(key, 'changed', vals[:source], vals[:target], mask)
83
+ end
84
+ diff.unchanged.each do |key|
85
+ val = data[:unchanged][key]
86
+ lines << csv_row(key, 'unchanged', val, val, mask)
87
+ end
88
+
89
+ "#{lines.join("\n")}\n"
90
+ end
91
+
92
+ def self.csv_row(key, status, source, target, mask)
93
+ masked = mask.any? { |m| key.to_s.downcase.include?(m.to_s.downcase) }
94
+ source = '***' if masked && source.to_s != ''
95
+ target = '***' if masked && target.to_s != ''
96
+ [key, status, source, target].map { |cell| csv_escape(cell) }.join(',')
97
+ end
98
+ private_class_method :csv_row
99
+
100
+ def self.csv_escape(value)
101
+ string = value.to_s
102
+ return string unless string.match?(/[",\n\r]/)
103
+
104
+ escaped = string.gsub('"', '""')
105
+ "\"#{escaped}\""
106
+ end
107
+ private_class_method :csv_escape
108
+
66
109
  # Format a diff result as an HTML table string.
67
110
  #
68
111
  # @param diff [Diff] the diff to format
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-env_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-24 00:00:00.000000000 Z
11
+ date: 2026-05-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Parse .env files or environment hashes, compare them, and get a clear
14
14
  report of added, removed, changed, and unchanged variables.