metanorma-release 0.2.2 → 0.2.3
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/.rubocop.yml +19 -1
- data/.rubocop_todo.yml +250 -319
- data/README.adoc +120 -233
- data/Rakefile +2 -2
- data/exe/metanorma-release +2 -2
- data/lib/metanorma/release/aggregation_pipeline.rb +59 -45
- data/lib/metanorma/release/asset_processor.rb +10 -8
- data/lib/metanorma/release/cache_store.rb +6 -6
- data/lib/metanorma/release/change_detector.rb +7 -3
- data/lib/metanorma/release/channel.rb +13 -39
- data/lib/metanorma/release/channel_filter.rb +26 -10
- data/lib/metanorma/release/cli.rb +129 -100
- data/lib/metanorma/release/commands/aggregate.rb +39 -54
- data/lib/metanorma/release/commands/package.rb +20 -12
- data/lib/metanorma/release/commands/{publish.rb → release_command.rb} +20 -12
- data/lib/metanorma/release/config.rb +104 -0
- data/lib/metanorma/release/content_hash.rb +11 -3
- data/lib/metanorma/release/delta_state.rb +55 -18
- data/lib/metanorma/release/file_routing.rb +8 -5
- data/lib/metanorma/release/index.rb +132 -0
- data/lib/metanorma/release/interfaces.rb +15 -15
- data/lib/metanorma/release/platform/github/manifest_reader.rb +4 -4
- data/lib/metanorma/release/platform/github/publisher.rb +23 -11
- data/lib/metanorma/release/platform/github/release_fetcher.rb +12 -3
- data/lib/metanorma/release/platform/github.rb +10 -7
- data/lib/metanorma/release/platform/local/directory_discoverer.rb +1 -1
- data/lib/metanorma/release/platform/local/fetcher.rb +17 -12
- data/lib/metanorma/release/platform/local/publisher.rb +9 -7
- data/lib/metanorma/release/platform/local.rb +4 -4
- data/lib/metanorma/release/platform/null/publisher.rb +3 -2
- data/lib/metanorma/release/platform/null.rb +1 -1
- data/lib/metanorma/release/platform.rb +3 -3
- data/lib/metanorma/release/platform_factory.rb +48 -29
- data/lib/metanorma/release/publication.rb +335 -0
- data/lib/metanorma/release/release_pipeline.rb +85 -52
- data/lib/metanorma/release/repo_ref.rb +5 -2
- data/lib/metanorma/release/site.rb +66 -0
- data/lib/metanorma/release/slug_strategy.rb +163 -0
- data/lib/metanorma/release/version.rb +1 -1
- data/lib/metanorma/release/zip_packager.rb +31 -8
- data/lib/metanorma/release.rb +68 -94
- metadata +22 -26
- data/lib/metanorma/release/aggregation_interfaces.rb +0 -27
- data/lib/metanorma/release/channel_audience.rb +0 -24
- data/lib/metanorma/release/channel_config.rb +0 -55
- data/lib/metanorma/release/channel_manifest.rb +0 -192
- data/lib/metanorma/release/channel_registry.rb +0 -60
- data/lib/metanorma/release/config_fetcher.rb +0 -11
- data/lib/metanorma/release/config_locator.rb +0 -37
- data/lib/metanorma/release/config_resolver.rb +0 -37
- data/lib/metanorma/release/document_id.rb +0 -45
- data/lib/metanorma/release/document_index.rb +0 -183
- data/lib/metanorma/release/document_metadata.rb +0 -39
- data/lib/metanorma/release/document_stage.rb +0 -86
- data/lib/metanorma/release/document_type.rb +0 -55
- data/lib/metanorma/release/document_version.rb +0 -50
- data/lib/metanorma/release/naming_strategy.rb +0 -158
- data/lib/metanorma/release/platform/github/config_fetcher.rb +0 -40
- data/lib/metanorma/release/platform/local/config_fetcher.rb +0 -20
- data/lib/metanorma/release/rake_tasks.rb +0 -71
- data/lib/metanorma/release/relaton_enricher.rb +0 -138
- data/lib/metanorma/release/release_metadata.rb +0 -79
- data/lib/metanorma/release/release_tag.rb +0 -49
- data/lib/metanorma/release/rxl_extractor.rb +0 -115
- data/lib/metanorma/release/stage_filter.rb +0 -18
data/README.adoc
CHANGED
|
@@ -8,13 +8,16 @@ toc::[]
|
|
|
8
8
|
|
|
9
9
|
== Overview
|
|
10
10
|
|
|
11
|
-
`metanorma-release` manages the full release lifecycle of Metanorma documents:
|
|
11
|
+
`metanorma-release` manages the full release lifecycle of Metanorma documents through three actors:
|
|
12
12
|
|
|
13
|
-
**
|
|
14
|
-
Discover compiled documents -> extract metadata from RXL -> detect changes -> package as zip ->
|
|
13
|
+
**Doc repo** (producer)::
|
|
14
|
+
Discover compiled documents -> extract metadata from RXL -> detect changes -> package as zip -> release to a platform (GitHub Releases, local filesystem).
|
|
15
15
|
|
|
16
|
-
**
|
|
17
|
-
|
|
16
|
+
**Org/publisher** (governor)::
|
|
17
|
+
Define channels and routing rules via config. Map document metadata (stage, doctype) to channel labels that control visibility and distribution.
|
|
18
|
+
|
|
19
|
+
**Aggregator** (consumer)::
|
|
20
|
+
Discover repositories -> fetch published releases -> filter by channel and stage -> extract zip assets -> generate `index.json` with Relaton enrichment and a file tree for any site generator.
|
|
18
21
|
|
|
19
22
|
The output is platform-agnostic: a directory containing `index.json` and a tree of document files. Any site generator (Jekyll, Hugo, Vite) consumes that output independently.
|
|
20
23
|
|
|
@@ -34,7 +37,11 @@ Or install directly:
|
|
|
34
37
|
gem install metanorma-release
|
|
35
38
|
----
|
|
36
39
|
|
|
37
|
-
Requires Ruby >= 3.2.
|
|
40
|
+
Requires Ruby >= 3.2. Optional runtime dependencies:
|
|
41
|
+
|
|
42
|
+
* `relaton-bib` -- RXL metadata extraction (required for `package` and `release`)
|
|
43
|
+
* `octokit` -- GitHub platform adapter (required for GitHub releases/aggregation)
|
|
44
|
+
* `rubyzip` -- zip packaging (required for `package` and `release`)
|
|
38
45
|
|
|
39
46
|
== Quick start
|
|
40
47
|
|
|
@@ -45,67 +52,39 @@ The gem ships three commands:
|
|
|
45
52
|
[source,sh]
|
|
46
53
|
----
|
|
47
54
|
# Package compiled documents as zip archives
|
|
48
|
-
metanorma-release package --output-dir _site
|
|
55
|
+
metanorma-release package --output-dir _site
|
|
49
56
|
|
|
50
|
-
# Package and
|
|
51
|
-
metanorma-release
|
|
57
|
+
# Package and release to a platform
|
|
58
|
+
metanorma-release release --platform github --output-dir _site --token $GITHUB_TOKEN
|
|
52
59
|
|
|
53
60
|
# Aggregate published releases into a file tree + index.json
|
|
54
|
-
metanorma-release aggregate --
|
|
55
|
-
----
|
|
56
|
-
|
|
57
|
-
=== Rake tasks
|
|
58
|
-
|
|
59
|
-
Register tasks in your `Rakefile`:
|
|
60
|
-
|
|
61
|
-
[source,ruby]
|
|
62
|
-
----
|
|
63
|
-
require "metanorma/release/rake_tasks"
|
|
64
|
-
|
|
65
|
-
Metanorma::Release::RakeTasks.install do |config|
|
|
66
|
-
config.output_dir = "_site"
|
|
67
|
-
config.manifest = "metanorma.release.yml"
|
|
68
|
-
config.platform = "github"
|
|
69
|
-
end
|
|
61
|
+
metanorma-release aggregate --repos my-org/my-repo --output-dir _site/cc
|
|
70
62
|
----
|
|
71
63
|
|
|
72
|
-
This provides:
|
|
73
|
-
|
|
74
|
-
* `rake mn:package` -- package compiled documents
|
|
75
|
-
* `rake mn:publish` -- package and publish documents
|
|
76
|
-
* `rake mn:aggregate` -- aggregate published releases
|
|
77
|
-
|
|
78
64
|
=== Ruby API
|
|
79
65
|
|
|
80
|
-
Use the pipelines directly for fine-grained control:
|
|
81
|
-
|
|
82
66
|
[source,ruby]
|
|
83
67
|
----
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
filters: [],
|
|
87
|
-
change_detector: Metanorma::Release::ContentHashChangeDetector.new(previous_releases: {}),
|
|
88
|
-
packager: Metanorma::Release::ZipPackager.new,
|
|
89
|
-
publisher: Metanorma::Release::PlatformFactory.build_publisher("null", {}),
|
|
90
|
-
naming_registry: Metanorma::Release::NamingRegistry.default_registry,
|
|
91
|
-
manifest: nil,
|
|
92
|
-
channel_override: nil,
|
|
93
|
-
channel_config: nil
|
|
94
|
-
)
|
|
68
|
+
# Discover publications from compiled RXL files
|
|
69
|
+
publications = Metanorma::Release::Publication.discover("_site")
|
|
95
70
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
71
|
+
# Each publication carries metadata from Relaton
|
|
72
|
+
pub = publications.first
|
|
73
|
+
pub.identifier # => "CC 18011:2018"
|
|
74
|
+
pub.slug # => "cc-18011-2018"
|
|
75
|
+
pub.title # => "Date and time — Explicit representation"
|
|
76
|
+
pub.edition # => "1"
|
|
77
|
+
pub.stage # => "60"
|
|
78
|
+
pub.doctype # => "standard"
|
|
79
|
+
pub.formats # => ["html", "pdf", "xml"]
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
# Serialization (used in release body and sidecar metadata)
|
|
82
|
+
pub.to_release_body # => "<!-- mn-release-metadata\n{...}\n-->"
|
|
83
|
+
pub.to_json # => "{...}"
|
|
84
|
+
|
|
85
|
+
# Parse from release body (used in aggregation)
|
|
86
|
+
pub = Publication.from_release_body(body)
|
|
87
|
+
pub = Publication.from_json(json_string)
|
|
109
88
|
----
|
|
110
89
|
|
|
111
90
|
== CLI reference
|
|
@@ -125,30 +104,30 @@ metanorma-release package [options]
|
|
|
125
104
|
|`--output-dir DIR` |Directory containing compiled documents (default: `_site`)
|
|
126
105
|
|`--dest DIR` |Destination for zip packages (default: `dist`)
|
|
127
106
|
|`--manifest FILE` |Release manifest file (default: `metanorma.release.yml`)
|
|
128
|
-
|`--config SOURCE` |
|
|
107
|
+
|`--config SOURCE` |Config file
|
|
129
108
|
|===
|
|
130
109
|
|
|
131
|
-
=== `metanorma-release
|
|
110
|
+
=== `metanorma-release release`
|
|
132
111
|
|
|
133
|
-
Package and
|
|
112
|
+
Package and release documents to a platform.
|
|
134
113
|
|
|
135
114
|
[source,sh]
|
|
136
115
|
----
|
|
137
|
-
metanorma-release
|
|
116
|
+
metanorma-release release [options]
|
|
138
117
|
----
|
|
139
118
|
|
|
140
119
|
[cols="1m,3",options="header"]
|
|
141
120
|
|===
|
|
142
121
|
|Option |Description
|
|
143
|
-
|`--platform NAME` |Target platform: `github`, `local` (default: `github`)
|
|
122
|
+
|`--platform NAME` |Target platform: `github`, `local`, `null` (default: `github`)
|
|
144
123
|
|`--output-dir DIR` |Compiled docs directory (default: `_site`)
|
|
145
124
|
|`--manifest FILE` |Release manifest file (default: `metanorma.release.yml`)
|
|
146
125
|
|`--force` |Force release even if unchanged
|
|
147
126
|
|`--force-replace PAT` |Glob pattern for forced replacement (repeatable)
|
|
148
|
-
|`--channels CHANS` |Override channels
|
|
127
|
+
|`--channels CHANS` |Override channels
|
|
149
128
|
|`--concurrency N` |Parallel workers (default: 4)
|
|
150
129
|
|`--token TOKEN` |Platform auth token
|
|
151
|
-
|`--config SOURCE` |
|
|
130
|
+
|`--config SOURCE` |Config file
|
|
152
131
|
|===
|
|
153
132
|
|
|
154
133
|
=== `metanorma-release aggregate`
|
|
@@ -164,11 +143,11 @@ metanorma-release aggregate [options]
|
|
|
164
143
|
|===
|
|
165
144
|
|Option |Description
|
|
166
145
|
|`--source SOURCE` |Discovery source: `github`, `local:PATH` (default: `github`)
|
|
167
|
-
|`--organizations ORGS` |
|
|
146
|
+
|`--organizations ORGS` |Organization list
|
|
168
147
|
|`--topic TOPIC` |Repository topic filter (default: `metanorma-release`)
|
|
169
|
-
|`--repos REPOS` |Explicit repo list
|
|
170
|
-
|`--channels CHANS` |Filter channels
|
|
171
|
-
|`--stages STAGES` |Filter stages
|
|
148
|
+
|`--repos REPOS` |Explicit repo list
|
|
149
|
+
|`--channels CHANS` |Filter channels
|
|
150
|
+
|`--stages STAGES` |Filter stages
|
|
172
151
|
|`--output-dir DIR` |Output directory (default: `_site/cc`)
|
|
173
152
|
|`--file-routing MODE` |File layout: `by-document`, `flat`, `by-format` (default: `by-document`)
|
|
174
153
|
|`--cache-dir DIR` |Cache directory for delta state
|
|
@@ -180,110 +159,79 @@ metanorma-release aggregate [options]
|
|
|
180
159
|
|
|
181
160
|
== Concepts
|
|
182
161
|
|
|
183
|
-
===
|
|
162
|
+
=== Publication
|
|
184
163
|
|
|
185
|
-
|
|
164
|
+
The central domain model. A `Publication` carries metadata from Relaton RXL extraction, files from the filesystem, and channels from config routing.
|
|
186
165
|
|
|
187
166
|
[source,ruby]
|
|
188
167
|
----
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
168
|
+
pub = Metanorma::Release::Publication.new(
|
|
169
|
+
identifier: "CC 18011:2018",
|
|
170
|
+
slug: "cc-18011-2018",
|
|
171
|
+
title: "Date and time — Explicit representation",
|
|
172
|
+
edition: "1",
|
|
173
|
+
stage: "60",
|
|
174
|
+
doctype: "standard",
|
|
175
|
+
revdate: "2018-06-01",
|
|
176
|
+
files: [PublicationFile.new(format: "html", name: "cc-18011.html", path: "cc-18011.html")],
|
|
177
|
+
channels: ["public"]
|
|
178
|
+
)
|
|
194
179
|
|
|
195
|
-
|
|
180
|
+
pub.base_dir # => "."
|
|
181
|
+
pub.content_hash # => #<ContentHash ...>
|
|
182
|
+
pub.with_channels(["members"]) # => new Publication with different channels
|
|
183
|
+
----
|
|
196
184
|
|
|
197
|
-
===
|
|
185
|
+
=== Channels
|
|
198
186
|
|
|
199
|
-
|
|
187
|
+
Channels are simple string labels that control document visibility and distribution. Typical values: `public`, `members`, `internal`.
|
|
200
188
|
|
|
201
|
-
|
|
202
|
-
[arabic]
|
|
203
|
-
. `--config` CLI flag (highest priority)
|
|
204
|
-
. `config:` key in the release manifest
|
|
205
|
-
. Directory walk: `.metanorma.yml`, `.metanorma.yaml`, or `.metanorma/channels.yml`
|
|
206
|
-
. No config -- all channels allowed
|
|
189
|
+
=== Config
|
|
207
190
|
|
|
208
|
-
|
|
191
|
+
A `metanorma.release.yml` config file defines channels and routing rules for an organization:
|
|
209
192
|
|
|
210
193
|
[source,yaml]
|
|
211
194
|
----
|
|
212
|
-
# .metanorma.yml
|
|
213
195
|
channels:
|
|
214
|
-
- public
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
- internal/working-drafts
|
|
218
|
-
defaults:
|
|
219
|
-
visibility: public
|
|
220
|
-
channels:
|
|
221
|
-
- public/standards
|
|
222
|
-
----
|
|
223
|
-
|
|
224
|
-
The `channels` list defines the taxonomy -- only these channels are valid. The `defaults` section sets fallback visibility and channels when a document doesn't match any manifest entry.
|
|
196
|
+
- public
|
|
197
|
+
- members
|
|
198
|
+
- internal
|
|
225
199
|
|
|
226
|
-
|
|
200
|
+
routing:
|
|
201
|
+
default: [public]
|
|
202
|
+
rules:
|
|
203
|
+
- stage: ["20", "30"]
|
|
204
|
+
channels: [internal]
|
|
205
|
+
- stage: ["60"]
|
|
206
|
+
channels: [public]
|
|
207
|
+
- doctype: [report]
|
|
208
|
+
channels: [public]
|
|
227
209
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
210
|
+
slug:
|
|
211
|
+
default: edition
|
|
212
|
+
strategies:
|
|
213
|
+
ietf: internet-draft
|
|
214
|
+
ieee: draft-suffix
|
|
215
|
+
iho: version
|
|
216
|
+
ogc: version
|
|
231
217
|
----
|
|
232
|
-
config: local:/path/to/config.yml
|
|
233
|
-
defaults:
|
|
234
|
-
visibility: public
|
|
235
|
-
documents:
|
|
236
|
-
- source: sources/cc-18011.adoc
|
|
237
|
-
channels:
|
|
238
|
-
- public/standards
|
|
239
|
-
----
|
|
240
|
-
|
|
241
|
-
The config source can be:
|
|
242
218
|
|
|
243
|
-
|
|
244
|
-
* `myorg/myrepo` -- GitHub repo (reads `channels.yml` from root)
|
|
245
|
-
* `myorg/myrepo#path/to/config.yml` -- GitHub repo with explicit path
|
|
219
|
+
Routing rules match raw metadata values from Relaton (stage, doctype) to channel labels. When no config is present, all documents route to `public`.
|
|
246
220
|
|
|
247
|
-
|
|
221
|
+
=== Slug strategies
|
|
248
222
|
|
|
249
|
-
|
|
250
|
-
----
|
|
251
|
-
# Parse from YAML
|
|
252
|
-
config = Metanorma::Release::ChannelConfig.from_yaml(File.read(".metanorma.yml"))
|
|
253
|
-
|
|
254
|
-
# Permissive config (all channels allowed)
|
|
255
|
-
config = Metanorma::Release::ChannelConfig.empty
|
|
256
|
-
|
|
257
|
-
# Validate a channel
|
|
258
|
-
config.registry.valid?(Channel.parse("public/standards")) # => true
|
|
259
|
-
config.registry.valid?(Channel.parse("public/secret")) # => false
|
|
260
|
-
|
|
261
|
-
# Locate config by walking up from a directory
|
|
262
|
-
config = Metanorma::Release::ConfigLocator.find("/path/to/project")
|
|
263
|
-
----
|
|
264
|
-
|
|
265
|
-
=== Naming strategies
|
|
266
|
-
|
|
267
|
-
Tag and file naming varies by document type. Strategies are resolved via a registry:
|
|
223
|
+
Tag and file naming varies by publisher (derived from the document identifier prefix). Strategies are resolved via a registry:
|
|
268
224
|
|
|
269
225
|
[cols="1m,1,2",options="header"]
|
|
270
226
|
|===
|
|
271
|
-
|
|
|
272
|
-
|
|
|
273
|
-
|IETF draft |`
|
|
274
|
-
|IETF RFC |`
|
|
275
|
-
|IEEE |`
|
|
276
|
-
|IHO, OGC |`
|
|
227
|
+
|Publisher |Strategy |Tag format
|
|
228
|
+
|default (CalConnect, ISO) |`EditionSlug` |`cc-18011-2018/ed1`
|
|
229
|
+
|IETF draft |`InternetDraftSlug` |`id-ietf-foo/1`
|
|
230
|
+
|IETF RFC |`RfcSlug` |`rfc-1234/ed1`
|
|
231
|
+
|IEEE |`DraftSuffixSlug` |`ieee-8021/d1`
|
|
232
|
+
|IHO, OGC |`VersionSlug` |`iho-s44/v1`
|
|
277
233
|
|===
|
|
278
234
|
|
|
279
|
-
Register custom strategies:
|
|
280
|
-
|
|
281
|
-
[source,ruby]
|
|
282
|
-
----
|
|
283
|
-
registry = Metanorma::Release::NamingRegistry.default_registry
|
|
284
|
-
registry.register("my-type", MyCustomNaming.new)
|
|
285
|
-
----
|
|
286
|
-
|
|
287
235
|
=== File routing
|
|
288
236
|
|
|
289
237
|
The aggregation pipeline supports three file layout modes:
|
|
@@ -296,105 +244,46 @@ The aggregation pipeline supports three file layout modes:
|
|
|
296
244
|
|`by-format` |`html/cc-18011.html`
|
|
297
245
|
|===
|
|
298
246
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
A `metanorma.release.yml` file controls which documents are published and to which channels:
|
|
302
|
-
|
|
303
|
-
[source,yaml]
|
|
304
|
-
----
|
|
305
|
-
config: myorg/.metanorma
|
|
306
|
-
defaults:
|
|
307
|
-
visibility: public
|
|
308
|
-
channels:
|
|
309
|
-
- public/standards
|
|
310
|
-
documents:
|
|
311
|
-
- source: sources/cc-18011.adoc
|
|
312
|
-
channels:
|
|
313
|
-
- public/standards
|
|
314
|
-
- source: sources/cc-19060.adoc
|
|
315
|
-
visibility: members
|
|
316
|
-
channels:
|
|
317
|
-
- members/early-access
|
|
318
|
-
- pattern: "sources/draft-*.adoc"
|
|
319
|
-
channels:
|
|
320
|
-
- internal/working-drafts
|
|
321
|
-
stages:
|
|
322
|
-
- working-draft
|
|
323
|
-
- committee-draft
|
|
324
|
-
----
|
|
325
|
-
|
|
326
|
-
Documents not listed in the manifest use the `defaults` section. If no manifest exists, all documents are released as `public/standards`.
|
|
327
|
-
|
|
328
|
-
Key fields:
|
|
329
|
-
|
|
330
|
-
[cols="1m,3",options="header"]
|
|
331
|
-
|===
|
|
332
|
-
|Field |Description
|
|
333
|
-
|`source` |Exact path match (highest priority)
|
|
334
|
-
|`pattern` |Glob pattern match
|
|
335
|
-
|`visibility` |`public`, `members`, or `private`
|
|
336
|
-
|`channels` |List of target channels
|
|
337
|
-
|`stages` |Allow-list of document stages
|
|
338
|
-
|`config` |Channel config source (see <<channel-configuration>>)
|
|
339
|
-
|===
|
|
340
|
-
|
|
341
|
-
=== Value objects
|
|
342
|
-
|
|
343
|
-
All domain types are immutable, frozen, and use value-based equality:
|
|
344
|
-
|
|
345
|
-
* `DocumentId` -- normalized document identifier (`CC 18011` -> `cc-18011`)
|
|
346
|
-
* `DocumentVersion` -- edition + stage + pre-release flag
|
|
347
|
-
* `DocumentStage` -- published, draft, working-draft, committee-draft, etc.
|
|
348
|
-
* `Channel` -- audience/category pair
|
|
349
|
-
* `ReleaseTag` -- tag string with pre-release flag
|
|
350
|
-
* `ContentHash` -- SHA-256 content fingerprint
|
|
351
|
-
* `RepoRef` -- owner/repo reference
|
|
352
|
-
|
|
353
|
-
=== Bibliography enrichment
|
|
247
|
+
== Architecture
|
|
354
248
|
|
|
355
|
-
|
|
249
|
+
=== Domain model
|
|
356
250
|
|
|
357
|
-
|
|
358
|
-
----
|
|
359
|
-
enricher = Metanorma::Release::RelatonEnricher.new(flavor: "calconnect")
|
|
360
|
-
result = enricher.enrich(document_index, output_dir)
|
|
361
|
-
# writes: output_dir/relaton/index.json
|
|
362
|
-
# output_dir/relaton/index.yaml
|
|
363
|
-
----
|
|
251
|
+
All core types are immutable, frozen value objects:
|
|
364
252
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
253
|
+
* `Publication` -- metadata + files + channels + source
|
|
254
|
+
* `PublicationFile` -- format, name, path
|
|
255
|
+
* `PublicationSource` -- owner, repo, tag, url, date
|
|
256
|
+
* `Channel` -- string label wrapper
|
|
257
|
+
* `Index` -- collection of Publications with parameters
|
|
258
|
+
* `Site` -- aggregated output (index + file tree + Relaton enrichment)
|
|
368
259
|
|
|
369
260
|
=== Dependency flow
|
|
370
261
|
|
|
371
262
|
Unidirectional, no cycles:
|
|
372
263
|
|
|
373
264
|
----
|
|
374
|
-
domain/ ->
|
|
375
|
-
|
|
376
|
-
-> cli/
|
|
265
|
+
domain/ -> pipelines/ -> platform/
|
|
266
|
+
-> cli/commands/
|
|
377
267
|
----
|
|
378
268
|
|
|
379
|
-
*
|
|
380
|
-
* Pipelines
|
|
381
|
-
* Platform adapters
|
|
382
|
-
* CLI delegates to command classes; commands
|
|
383
|
-
* Commands use `ConfigResolver` mixin for channel config resolution
|
|
269
|
+
* Domain models have zero knowledge of pipelines, platforms, or CLI
|
|
270
|
+
* Pipelines receive all dependencies through constructors (dependency injection)
|
|
271
|
+
* Platform adapters implement interface modules (Publisher, RepoDiscoverer, ReleaseFetcher, etc.)
|
|
272
|
+
* CLI delegates to command classes; commands construct pipelines
|
|
384
273
|
|
|
385
274
|
=== Patterns
|
|
386
275
|
|
|
387
|
-
Value Objects:: Immutable, frozen, value-based equality via `eql?`/`hash`.
|
|
276
|
+
Value Objects:: Immutable, frozen, value-based equality via `eql?`/`hash`.
|
|
388
277
|
|
|
389
|
-
Strategy Pattern:: Pluggable
|
|
278
|
+
Strategy Pattern:: Pluggable slug strategies resolved via registry. Adding a new publisher type requires zero changes to existing code.
|
|
390
279
|
|
|
391
280
|
Pipeline with DI:: Pipelines receive all dependencies through constructors. No global state, no service locators.
|
|
392
281
|
|
|
393
|
-
|
|
282
|
+
Interface Modules:: Type contracts using `include Module` -- not duck typing. Dependencies are validated at construction time.
|
|
394
283
|
|
|
395
|
-
|
|
284
|
+
Null Object:: Disabled features inject null implementations (`NullDeltaState`, `NullPublisher`, `NullCacheStore`).
|
|
396
285
|
|
|
397
|
-
|
|
286
|
+
Result Types:: Pipelines return frozen Structs. Errors are collected, not raised.
|
|
398
287
|
|
|
399
288
|
=== Extending
|
|
400
289
|
|
|
@@ -402,19 +291,16 @@ Command Pattern:: CLI delegates to `PackageCommand`, `PublishCommand`, and `Aggr
|
|
|
402
291
|
|To add... |Do this
|
|
403
292
|
|
|
404
293
|
|A new platform
|
|
405
|
-
|Create a directory under `platform/` with `Publisher`, `Discoverer`, `Fetcher`, `ManifestReader` classes; register in `PlatformFactory`
|
|
294
|
+
|Create a directory under `platform/` with `Publisher`, `Discoverer`, `Fetcher`, `ManifestReader` classes that include the corresponding interface modules; register in `PlatformFactory`
|
|
406
295
|
|
|
407
|
-
|A new
|
|
408
|
-
|Create a class that includes `
|
|
296
|
+
|A new slug strategy
|
|
297
|
+
|Create a class that includes `SlugStrategy`; register via `SlugRegistry#register`
|
|
409
298
|
|
|
410
299
|
|A new file routing mode
|
|
411
|
-
|Create a class with `#compute_path
|
|
300
|
+
|Create a class that includes `FileRouting` with a `#compute_path` method; register in `FileRoutingFactory`
|
|
412
301
|
|
|
413
302
|
|A new filter
|
|
414
303
|
|Create a class that includes `Filter`; pass to the pipeline's `filters` array
|
|
415
|
-
|
|
416
|
-
|A new channel config source
|
|
417
|
-
|Create a class that includes `ConfigFetcher` with a `#fetch(source)` method
|
|
418
304
|
|===
|
|
419
305
|
|
|
420
306
|
== Development
|
|
@@ -423,6 +309,7 @@ Command Pattern:: CLI delegates to `PackageCommand`, `PublishCommand`, and `Aggr
|
|
|
423
309
|
----
|
|
424
310
|
bundle install
|
|
425
311
|
bundle exec rspec
|
|
312
|
+
bundle exec rubocop
|
|
426
313
|
----
|
|
427
314
|
|
|
428
315
|
== License
|
data/Rakefile
CHANGED
data/exe/metanorma-release
CHANGED