textus 0.14.0 → 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/textus/builder/pipeline.rb +46 -2
- data/lib/textus/manifest.rb +13 -16
- data/lib/textus/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7f46590895cb1b7dccf71efbb6cc7632ac1054e545323478e831875c7b7bf621
|
|
4
|
+
data.tar.gz: e84857018c0bf4e61e6e60a476191e04dd2fe470f476b27860deaf59611993ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e21ff9e7d9a9cfe93ab3865646b53bbea4647f0c7ace9d329e7ccec13a2ce7343568df836bd25fccfa19c39d4384a2d522d827e5d01b7ebbdd0b2ab3098978be
|
|
7
|
+
data.tar.gz: 700e831af5ba5c13a9e66984e59ee4774def27e1c098b7dfd2d6e942b6cd064b7e3e3d1480faa110417ede67aa229d0b7bfea063f8f5f59e91bcaa0dce282355
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,23 @@ The **gem version** (`0.x.y`) is distinct from the **protocol version**
|
|
|
9
9
|
bump is a breaking change that requires a store migration; the gem version
|
|
10
10
|
tracks both additive improvements and breaking protocol bumps independently.
|
|
11
11
|
|
|
12
|
+
## 0.14.1 — 2026-05-26
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Build pipeline now skips rewriting a built artifact when the only
|
|
17
|
+
difference from the existing file on disk would be the freshly-stamped
|
|
18
|
+
`generated_at` (markdown: `generated.at`) timestamp. Stores under git
|
|
19
|
+
versioning no longer churn on every `textus build` (#52).
|
|
20
|
+
- Strict policy: any other byte difference — changed `from`,
|
|
21
|
+
`template`, `reduce`, body content — still triggers a write. Text
|
|
22
|
+
format falls back to plain byte-equality (no timestamp to normalize).
|
|
23
|
+
|
|
24
|
+
### Internal
|
|
25
|
+
|
|
26
|
+
- Extracted `Manifest.check_version!` to dedupe the parse/load version
|
|
27
|
+
guard (#51).
|
|
28
|
+
|
|
12
29
|
## 0.14.0 — 2026-05-26
|
|
13
30
|
|
|
14
31
|
### Breaking (Ruby API only — CLI JSON output unchanged)
|
|
@@ -19,6 +19,35 @@ module Textus
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Replaces the freshly-stamped timestamp inside `new_bytes` with the
|
|
23
|
+
# timestamp pulled from `old_bytes` (same format). Returns the rewritten
|
|
24
|
+
# bytes, or nil if either side lacks a parseable timestamp.
|
|
25
|
+
module IdempotentWrite
|
|
26
|
+
def self.rewrite_with_prior_timestamp(new_bytes:, old_bytes:, format:)
|
|
27
|
+
prior = extract_timestamp(old_bytes, format)
|
|
28
|
+
fresh = extract_timestamp(new_bytes, format)
|
|
29
|
+
return nil unless prior && fresh
|
|
30
|
+
return new_bytes if prior == fresh
|
|
31
|
+
|
|
32
|
+
new_bytes.sub(fresh, prior)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.extract_timestamp(bytes, format)
|
|
36
|
+
case format
|
|
37
|
+
when "markdown"
|
|
38
|
+
parsed = Entry.for_format("markdown").parse(bytes)
|
|
39
|
+
parsed.dig("_meta", "generated", "at")
|
|
40
|
+
when "json", "yaml"
|
|
41
|
+
parsed = Entry.for_format(format).parse(bytes)
|
|
42
|
+
parsed.dig("_meta", "generated_at")
|
|
43
|
+
else # rubocop:disable Style/EmptyElse
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
rescue Textus::BadFrontmatter
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
22
51
|
module Pipeline
|
|
23
52
|
def self.renderers
|
|
24
53
|
@renderers ||= {
|
|
@@ -44,13 +73,28 @@ module Textus
|
|
|
44
73
|
raise UsageError.new("builder: unsupported format #{mentry.format.inspect} for '#{mentry.key}'")
|
|
45
74
|
bytes = klass.new(template_loader: template_loader).call(mentry: mentry, data: data)
|
|
46
75
|
|
|
47
|
-
# 3. Write
|
|
76
|
+
# 3. Write (idempotent: skip if only generated_at would differ)
|
|
48
77
|
target_path = Key::Path.resolve(store.manifest, mentry)
|
|
49
78
|
FileUtils.mkdir_p(File.dirname(target_path))
|
|
50
|
-
|
|
79
|
+
write_if_changed(target_path, bytes, mentry.format)
|
|
51
80
|
|
|
52
81
|
target_path
|
|
53
82
|
end
|
|
83
|
+
|
|
84
|
+
def self.write_if_changed(target_path, bytes, format)
|
|
85
|
+
if File.exist?(target_path)
|
|
86
|
+
old_bytes = File.binread(target_path)
|
|
87
|
+
if format == "text"
|
|
88
|
+
return if old_bytes == bytes
|
|
89
|
+
else
|
|
90
|
+
rewritten = IdempotentWrite.rewrite_with_prior_timestamp(
|
|
91
|
+
new_bytes: bytes, old_bytes: old_bytes, format: format,
|
|
92
|
+
)
|
|
93
|
+
return if rewritten && rewritten == old_bytes
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
File.binwrite(target_path, bytes)
|
|
97
|
+
end
|
|
54
98
|
end
|
|
55
99
|
end
|
|
56
100
|
end
|
data/lib/textus/manifest.rb
CHANGED
|
@@ -39,14 +39,7 @@ module Textus
|
|
|
39
39
|
|
|
40
40
|
def self.parse(yaml_text, root: ".")
|
|
41
41
|
raw = YAML.safe_load(yaml_text, aliases: false)
|
|
42
|
-
|
|
43
|
-
raise BadFrontmatter.new(
|
|
44
|
-
"<string>",
|
|
45
|
-
"unsupported manifest version #{raw["version"].inspect}; expected #{PROTOCOL.inspect}",
|
|
46
|
-
hint: version_hint_for(raw["version"]),
|
|
47
|
-
)
|
|
48
|
-
end
|
|
49
|
-
|
|
42
|
+
check_version!(raw, "<string>")
|
|
50
43
|
new(root, raw)
|
|
51
44
|
end
|
|
52
45
|
|
|
@@ -55,17 +48,21 @@ module Textus
|
|
|
55
48
|
raise IoError.new("manifest not found: #{manifest_path}") unless File.exist?(manifest_path)
|
|
56
49
|
|
|
57
50
|
raw = YAML.safe_load_file(manifest_path, aliases: false)
|
|
58
|
-
|
|
59
|
-
raise BadFrontmatter.new(
|
|
60
|
-
manifest_path,
|
|
61
|
-
"unsupported manifest version #{raw["version"].inspect}; expected #{PROTOCOL.inspect}",
|
|
62
|
-
hint: version_hint_for(raw["version"]),
|
|
63
|
-
)
|
|
64
|
-
end
|
|
65
|
-
|
|
51
|
+
check_version!(raw, manifest_path)
|
|
66
52
|
new(root, raw)
|
|
67
53
|
end
|
|
68
54
|
|
|
55
|
+
def self.check_version!(raw, source)
|
|
56
|
+
return if raw["version"] == PROTOCOL
|
|
57
|
+
|
|
58
|
+
raise BadFrontmatter.new(
|
|
59
|
+
source,
|
|
60
|
+
"unsupported manifest version #{raw["version"].inspect}; expected #{PROTOCOL.inspect}",
|
|
61
|
+
hint: version_hint_for(raw["version"]),
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
private_class_method :check_version!
|
|
65
|
+
|
|
69
66
|
def initialize(root, raw)
|
|
70
67
|
@root = root
|
|
71
68
|
@raw = raw
|
data/lib/textus/version.rb
CHANGED