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
|
@@ -1,129 +1,158 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "thor"
|
|
4
4
|
|
|
5
5
|
module Metanorma
|
|
6
6
|
module Release
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def run(argv)
|
|
11
|
-
command = argv.shift
|
|
12
|
-
case command
|
|
13
|
-
when 'package' then run_package(argv)
|
|
14
|
-
when 'publish' then run_publish(argv)
|
|
15
|
-
when 'aggregate' then run_aggregate(argv)
|
|
16
|
-
when nil
|
|
17
|
-
warn 'Usage: metanorma-release <package|publish|aggregate> [options]'
|
|
18
|
-
exit 2
|
|
19
|
-
else
|
|
20
|
-
warn "Unknown command: #{command}"
|
|
21
|
-
exit 2
|
|
22
|
-
end
|
|
7
|
+
class CLI < Thor
|
|
8
|
+
def self.exit_on_failure?
|
|
9
|
+
true
|
|
23
10
|
end
|
|
24
11
|
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
class PipelineError < Thor::Error; end
|
|
13
|
+
|
|
14
|
+
desc "package", "Package compiled documents"
|
|
15
|
+
option :output_dir, type: :string, default: "_site",
|
|
16
|
+
desc: "Compiled docs directory"
|
|
17
|
+
option :dest, type: :string, default: "dist",
|
|
18
|
+
desc: "Destination for packages"
|
|
19
|
+
option :manifest, type: :string, default: "metanorma.release.yml",
|
|
20
|
+
desc: "Release manifest file"
|
|
21
|
+
option :config, type: :string, desc: "Config file"
|
|
22
|
+
|
|
23
|
+
def package
|
|
24
|
+
config = PackageCommand::Config.new(
|
|
25
|
+
output_dir: options[:output_dir],
|
|
26
|
+
dest: options[:dest],
|
|
27
|
+
manifest: options[:manifest],
|
|
28
|
+
config_source: options[:config],
|
|
29
|
+
)
|
|
27
30
|
result = PackageCommand.new(config).call
|
|
28
31
|
print_package_result(result, config.dest)
|
|
29
|
-
|
|
32
|
+
raise PipelineError, format_failures(result) unless result.failed.empty?
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
desc "release", "Package and release documents"
|
|
36
|
+
option :platform, type: :string, default: "github",
|
|
37
|
+
desc: "Publishing platform (github|local)"
|
|
38
|
+
option :output_dir, type: :string, default: "_site",
|
|
39
|
+
desc: "Compiled docs directory"
|
|
40
|
+
option :manifest, type: :string, default: "metanorma.release.yml",
|
|
41
|
+
desc: "Release manifest file"
|
|
42
|
+
option :force, type: :boolean, default: false,
|
|
43
|
+
desc: "Force release even if unchanged"
|
|
44
|
+
option :force_replace, type: :array, default: [],
|
|
45
|
+
desc: "Glob patterns for force-replace"
|
|
46
|
+
option :channels, type: :array, desc: "Override channels"
|
|
47
|
+
option :concurrency, type: :numeric, default: 4
|
|
48
|
+
option :token, type: :string, desc: "Platform auth token"
|
|
49
|
+
option :config, type: :string, desc: "Config file"
|
|
50
|
+
|
|
51
|
+
def release
|
|
52
|
+
config = ReleaseCommand::Config.new(
|
|
53
|
+
output_dir: options[:output_dir],
|
|
54
|
+
platform: options[:platform],
|
|
55
|
+
manifest: options[:manifest],
|
|
56
|
+
force: options[:force],
|
|
57
|
+
force_replace: options[:force_replace],
|
|
58
|
+
channels: options[:channels],
|
|
59
|
+
concurrency: options[:concurrency],
|
|
60
|
+
token: options[:token],
|
|
61
|
+
config_source: options[:config],
|
|
62
|
+
)
|
|
63
|
+
result = ReleaseCommand.new(config).call
|
|
35
64
|
print_publish_result(result)
|
|
36
|
-
|
|
65
|
+
raise PipelineError, format_failures(result) unless result.failed.empty?
|
|
37
66
|
end
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
desc "aggregate", "Aggregate released documents"
|
|
69
|
+
option :source, type: :string, default: "github",
|
|
70
|
+
desc: "Source (github|local:PATH)"
|
|
71
|
+
option :organizations, type: :array, default: [],
|
|
72
|
+
desc: "Organizations to discover"
|
|
73
|
+
option :topic, type: :string, default: "metanorma-release",
|
|
74
|
+
desc: "Repository topic"
|
|
75
|
+
option :repos, type: :array, desc: "Explicit repo list"
|
|
76
|
+
option :channels, type: :array, default: [],
|
|
77
|
+
desc: "Filter channels"
|
|
78
|
+
option :stages, type: :array, default: [],
|
|
79
|
+
desc: "Filter stages"
|
|
80
|
+
option :output_dir, type: :string, default: "_site/cc",
|
|
81
|
+
desc: "Output directory"
|
|
82
|
+
option :file_routing, type: :string, default: "by-document",
|
|
83
|
+
desc: "File routing (by-document|flat|by-format)"
|
|
84
|
+
option :cache_dir, type: :string, desc: "Cache directory"
|
|
85
|
+
option :include_drafts, type: :boolean, default: false,
|
|
86
|
+
desc: "Include draft releases"
|
|
87
|
+
option :concurrency, type: :numeric, default: 4
|
|
88
|
+
option :min_documents, type: :numeric, default: 0,
|
|
89
|
+
desc: "Minimum required documents"
|
|
90
|
+
option :token, type: :string, desc: "Platform auth token"
|
|
91
|
+
|
|
92
|
+
def aggregate
|
|
93
|
+
config = AggregateCommand::Config.new(
|
|
94
|
+
source: options[:source],
|
|
95
|
+
organizations: options[:organizations],
|
|
96
|
+
topic: options[:topic],
|
|
97
|
+
repos: options[:repos],
|
|
98
|
+
channels: options[:channels],
|
|
99
|
+
stages: options[:stages],
|
|
100
|
+
output_dir: options[:output_dir],
|
|
101
|
+
file_routing: options[:file_routing],
|
|
102
|
+
cache_dir: options[:cache_dir],
|
|
103
|
+
include_drafts: options[:include_drafts],
|
|
104
|
+
concurrency: options[:concurrency],
|
|
105
|
+
min_documents: options[:min_documents],
|
|
106
|
+
token: options[:token],
|
|
107
|
+
create_zip: nil,
|
|
108
|
+
)
|
|
41
109
|
result = AggregateCommand.new(config).call
|
|
42
110
|
print_aggregate_result(result)
|
|
43
|
-
if config.min_documents.positive? && result.documents.length < config.min_documents
|
|
44
|
-
warn "Error: Found #{result.documents.length} documents, minimum is #{config.min_documents}"
|
|
45
|
-
exit 1
|
|
46
|
-
end
|
|
47
|
-
exit(result.failed_repos.empty? ? 0 : 1)
|
|
48
|
-
end
|
|
49
111
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def parse_package_args(argv)
|
|
54
|
-
options = { output_dir: '_site', dest: 'dist', manifest: 'metanorma.release.yml',
|
|
55
|
-
config_source: nil }
|
|
56
|
-
OptionParser.new do |opts|
|
|
57
|
-
opts.banner = 'Usage: metanorma-release package [options]'
|
|
58
|
-
opts.on('--output-dir DIR', 'Compiled docs directory') { |v| options[:output_dir] = v }
|
|
59
|
-
opts.on('--dest DIR', 'Destination for packages') { |v| options[:dest] = v }
|
|
60
|
-
opts.on('--manifest FILE', 'Release manifest file') { |v| options[:manifest] = v }
|
|
61
|
-
opts.on('--config SOURCE', 'Channel config (file path or platform ref)') { |v| options[:config_source] = v }
|
|
62
|
-
end.parse!(argv)
|
|
63
|
-
PackageCommand::Config.new(**options)
|
|
112
|
+
if options[:min_documents].positive? && result.publications.length < options[:min_documents]
|
|
113
|
+
raise PipelineError,
|
|
114
|
+
"Found #{result.publications.length} documents, minimum is #{options[:min_documents]}"
|
|
64
115
|
end
|
|
65
116
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
config_source: nil }
|
|
70
|
-
OptionParser.new do |opts|
|
|
71
|
-
opts.banner = 'Usage: metanorma-release publish [options]'
|
|
72
|
-
opts.on('--platform NAME', 'github|local') { |v| options[:platform] = v }
|
|
73
|
-
opts.on('--output-dir DIR', 'Compiled docs directory') { |v| options[:output_dir] = v }
|
|
74
|
-
opts.on('--manifest FILE', 'Release manifest file') { |v| options[:manifest] = v }
|
|
75
|
-
opts.on('--force', 'Force release even if unchanged') { |v| options[:force] = v }
|
|
76
|
-
opts.on('--force-replace PAT', 'Glob patterns for force-replace') { |v| options[:force_replace] << v }
|
|
77
|
-
opts.on('--channels CHANS', 'Override channels (comma-separated)') { |v| options[:channels] = v.split(',') }
|
|
78
|
-
opts.on('--concurrency N', Integer) { |v| options[:concurrency] = v }
|
|
79
|
-
opts.on('--token TOKEN', 'Platform auth token') { |v| options[:token] = v }
|
|
80
|
-
opts.on('--config SOURCE', 'Channel config (file path or platform ref)') { |v| options[:config_source] = v }
|
|
81
|
-
end.parse!(argv)
|
|
82
|
-
PublishCommand::Config.new(**options)
|
|
117
|
+
unless result.failed_repos.empty?
|
|
118
|
+
raise PipelineError,
|
|
119
|
+
format_repo_failures(result)
|
|
83
120
|
end
|
|
121
|
+
end
|
|
84
122
|
|
|
85
|
-
|
|
86
|
-
options = { source: 'github', organizations: [], topic: 'metanorma-release',
|
|
87
|
-
repos: nil, channels: [], stages: [], output_dir: '_site/cc',
|
|
88
|
-
file_routing: 'by-document', cache_dir: nil,
|
|
89
|
-
include_drafts: false, concurrency: 4, min_documents: 0, token: nil }
|
|
90
|
-
OptionParser.new do |opts|
|
|
91
|
-
opts.banner = 'Usage: metanorma-release aggregate [options]'
|
|
92
|
-
opts.on('--source SOURCE', 'github|local:PATH') { |v| options[:source] = v }
|
|
93
|
-
opts.on('--organizations ORGS', 'Comma-separated org list') { |v| options[:organizations] = v.split(',') }
|
|
94
|
-
opts.on('--topic TOPIC', 'Repository topic') { |v| options[:topic] = v }
|
|
95
|
-
opts.on('--repos REPOS', 'Explicit repo list (comma-separated)') { |v| options[:repos] = v.split(',') }
|
|
96
|
-
opts.on('--channels CHANS', 'Filter channels (comma-separated)') { |v| options[:channels] = v.split(',') }
|
|
97
|
-
opts.on('--stages STAGES', 'Filter stages (comma-separated)') { |v| options[:stages] = v.split(',') }
|
|
98
|
-
opts.on('--output-dir DIR', 'Output directory') { |v| options[:output_dir] = v }
|
|
99
|
-
opts.on('--file-routing MODE', 'by-document|flat|by-format') { |v| options[:file_routing] = v }
|
|
100
|
-
opts.on('--cache-dir DIR', 'Cache directory') { |v| options[:cache_dir] = v }
|
|
101
|
-
opts.on('--[no-]include-drafts', 'Include draft releases') { |v| options[:include_drafts] = v }
|
|
102
|
-
opts.on('--concurrency N', Integer) { |v| options[:concurrency] = v }
|
|
103
|
-
opts.on('--min-documents N', Integer) { |v| options[:min_documents] = v }
|
|
104
|
-
opts.on('--token TOKEN', 'Platform auth token') { |v| options[:token] = v }
|
|
105
|
-
end.parse!(argv)
|
|
106
|
-
AggregateCommand::Config.new(**options)
|
|
107
|
-
end
|
|
123
|
+
private
|
|
108
124
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
def print_package_result(result, dest)
|
|
126
|
+
released = result.released
|
|
127
|
+
puts "Packaged #{released.length} documents → #{dest}/"
|
|
128
|
+
released.each { |pub| puts " #{pub.slug}" }
|
|
129
|
+
end
|
|
114
130
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
result.failed.each { |f| puts " FAILED: #{f[:document].id} - #{f[:error]}" }
|
|
131
|
+
def print_publish_result(result)
|
|
132
|
+
puts "Released #{result.released.length}, skipped #{result.skipped.length}, failed #{result.failed.length}"
|
|
133
|
+
result.released.each do |pub|
|
|
134
|
+
puts " RELEASED: #{pub.slug} (ed#{pub.edition})"
|
|
120
135
|
end
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
puts "
|
|
124
|
-
puts "Index: #{result.channels_found.join(', ')}" unless result.channels_found.empty?
|
|
136
|
+
result.skipped.each { |pub| puts " SKIPPED: #{pub.slug} (unchanged)" }
|
|
137
|
+
result.failed.each do |f|
|
|
138
|
+
puts " FAILED: #{f[:document].slug} - #{f[:error]}"
|
|
125
139
|
end
|
|
126
140
|
end
|
|
141
|
+
|
|
142
|
+
def print_aggregate_result(result)
|
|
143
|
+
puts "Aggregated #{result.publications.length} documents from #{result.repo_count} repos"
|
|
144
|
+
puts "Channels: #{result.channels_found.join(', ')}" unless result.channels_found.empty?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def format_failures(result)
|
|
148
|
+
result.failed.map do |f|
|
|
149
|
+
"#{f[:document].slug}: #{f[:error]}"
|
|
150
|
+
end.join("\n")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def format_repo_failures(result)
|
|
154
|
+
result.failed_repos.map { |r| "#{r.tag}: #{r.message}" }.join("\n")
|
|
155
|
+
end
|
|
127
156
|
end
|
|
128
157
|
end
|
|
129
158
|
end
|
|
@@ -6,7 +6,7 @@ module Metanorma
|
|
|
6
6
|
Config = Struct.new(
|
|
7
7
|
:source, :organizations, :topic, :repos, :repo_pattern, :local_path,
|
|
8
8
|
:channels, :stages, :output_dir, :file_routing, :cache_dir,
|
|
9
|
-
:include_drafts, :concurrency, :min_documents, :token, :
|
|
9
|
+
:include_drafts, :concurrency, :min_documents, :token, :create_zip,
|
|
10
10
|
keyword_init: true
|
|
11
11
|
)
|
|
12
12
|
|
|
@@ -16,8 +16,15 @@ module Metanorma
|
|
|
16
16
|
|
|
17
17
|
def call
|
|
18
18
|
result = run_aggregation
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
return result unless result.publications.any?
|
|
20
|
+
|
|
21
|
+
index = build_index(result)
|
|
22
|
+
site = Site.new(index: index, output_dir: @config.output_dir)
|
|
23
|
+
site.write!
|
|
24
|
+
site.enrich!
|
|
25
|
+
site.package! if @config.create_zip
|
|
26
|
+
|
|
27
|
+
stamp_primary_identifiers(index)
|
|
21
28
|
result
|
|
22
29
|
end
|
|
23
30
|
|
|
@@ -29,18 +36,18 @@ module Metanorma
|
|
|
29
36
|
organizations: @config.organizations,
|
|
30
37
|
topic: @config.topic,
|
|
31
38
|
repos: @config.repos,
|
|
32
|
-
token: @config.token
|
|
39
|
+
token: @config.token,
|
|
33
40
|
)
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
channels: Channel.parse_list(@config.channels)
|
|
42
|
+
metadata_filter = MetadataFilter.new(
|
|
43
|
+
channels: Channel.parse_list(@config.channels),
|
|
44
|
+
stages: @config.stages || [],
|
|
37
45
|
)
|
|
38
|
-
stage_filter = StageFilter.new(@config.stages || [])
|
|
39
46
|
routing = FileRoutingFactory.from_name(@config.file_routing)
|
|
40
47
|
asset_processor = AssetProcessor.new(
|
|
41
48
|
output_dir: @config.output_dir,
|
|
42
49
|
routing: routing,
|
|
43
|
-
canonicalize: true
|
|
50
|
+
canonicalize: true,
|
|
44
51
|
)
|
|
45
52
|
delta_state = build_delta_state
|
|
46
53
|
|
|
@@ -48,10 +55,9 @@ module Metanorma
|
|
|
48
55
|
discoverer: adapters[:discoverer],
|
|
49
56
|
fetcher: adapters[:fetcher],
|
|
50
57
|
manifest_reader: adapters[:manifest_reader],
|
|
51
|
-
|
|
52
|
-
stage_filter: stage_filter,
|
|
58
|
+
metadata_filter: metadata_filter,
|
|
53
59
|
asset_processor: asset_processor,
|
|
54
|
-
delta_state: delta_state
|
|
60
|
+
delta_state: delta_state,
|
|
55
61
|
)
|
|
56
62
|
|
|
57
63
|
config = AggregationPipeline::Config.new(
|
|
@@ -60,66 +66,45 @@ module Metanorma
|
|
|
60
66
|
topic: @config.topic,
|
|
61
67
|
concurrency: @config.concurrency,
|
|
62
68
|
include_drafts: @config.include_drafts,
|
|
63
|
-
fail_on_error: false
|
|
69
|
+
fail_on_error: false,
|
|
64
70
|
)
|
|
65
71
|
|
|
66
72
|
AggregationPipeline.new(deps).run(config, @config.output_dir)
|
|
67
73
|
end
|
|
68
74
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
cache_store: FileCacheStore.new(@config.cache_dir),
|
|
74
|
-
output_dir: @config.output_dir
|
|
75
|
-
)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def enrich(result)
|
|
79
|
-
index = DocumentIndex.from_documents(
|
|
80
|
-
result.documents,
|
|
81
|
-
parameters: IndexParameters.new(
|
|
75
|
+
def build_index(result)
|
|
76
|
+
Index.from_documents(
|
|
77
|
+
result.publications,
|
|
78
|
+
parameters: {
|
|
82
79
|
organizations: @config.organizations,
|
|
83
80
|
channels: @config.channels || [],
|
|
84
81
|
topic: @config.topic,
|
|
85
|
-
repo_count: result.repo_count
|
|
86
|
-
|
|
82
|
+
repo_count: result.repo_count,
|
|
83
|
+
},
|
|
87
84
|
)
|
|
88
|
-
enricher = RelatonEnricher.new
|
|
89
|
-
enrich_result = enricher.enrich(index, @config.output_dir)
|
|
90
|
-
return unless enrich_result
|
|
91
|
-
|
|
92
|
-
stamp_primary_identifiers(enrich_result.documents)
|
|
93
|
-
rescue LoadError
|
|
94
|
-
warn ' (relaton gem not available — bibliography skipped)'
|
|
95
85
|
end
|
|
96
86
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
next doc unless doc['bibliographic']
|
|
100
|
-
|
|
101
|
-
ids = doc['bibliographic']['docidentifier']
|
|
102
|
-
next doc unless ids&.any?
|
|
87
|
+
def build_delta_state
|
|
88
|
+
return NullDeltaState.new unless @config.cache_dir
|
|
103
89
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
DeltaState.new(
|
|
91
|
+
cache_store: FileCacheStore.new(@config.cache_dir),
|
|
92
|
+
output_dir: @config.output_dir,
|
|
93
|
+
)
|
|
107
94
|
end
|
|
108
95
|
|
|
109
|
-
def
|
|
110
|
-
|
|
96
|
+
def stamp_primary_identifiers(index)
|
|
97
|
+
index.publications.each do |pub|
|
|
98
|
+
next unless pub.to_h["bibliographic"]
|
|
111
99
|
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
ids = pub.to_h.dig("bibliographic", "docidentifier")
|
|
101
|
+
next unless ids&.any?
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
next if File.directory?(file)
|
|
118
|
-
|
|
119
|
-
entry_name = file.sub("#{File.dirname(dir)}/", '')
|
|
120
|
-
zipfile.add(entry_name, file)
|
|
121
|
-
end
|
|
103
|
+
primary = ids.find { |di| di["primary"] == true } || ids.first
|
|
104
|
+
pub.to_h.merge("primary_identifier" => primary["content"])
|
|
122
105
|
end
|
|
106
|
+
rescue LoadError
|
|
107
|
+
warn " (relaton gem not available — bibliography skipped)"
|
|
123
108
|
end
|
|
124
109
|
end
|
|
125
110
|
end
|
|
@@ -8,26 +8,22 @@ module Metanorma
|
|
|
8
8
|
keyword_init: true
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
-
include ConfigResolver
|
|
12
|
-
|
|
13
11
|
def initialize(config)
|
|
14
12
|
@config = config
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
def call
|
|
18
|
-
|
|
19
|
-
channel_config = resolve_channel_config(@config.config_source, manifest)
|
|
20
|
-
|
|
16
|
+
config = load_config
|
|
21
17
|
deps = ReleasePipeline::Dependencies.new(
|
|
22
|
-
extractor:
|
|
18
|
+
extractor: Publication,
|
|
23
19
|
filters: [],
|
|
24
20
|
change_detector: ContentHashChangeDetector.new(previous_releases: {}),
|
|
25
|
-
packager: ZipPackager.new,
|
|
26
|
-
publisher: PlatformFactory.build_publisher(
|
|
27
|
-
|
|
28
|
-
manifest:
|
|
21
|
+
packager: ZipPackager.new(output_dir: @config.output_dir),
|
|
22
|
+
publisher: PlatformFactory.build_publisher("null", {}),
|
|
23
|
+
slug_registry: SlugRegistry.from_config(config),
|
|
24
|
+
manifest: nil,
|
|
29
25
|
channel_override: nil,
|
|
30
|
-
|
|
26
|
+
config: config,
|
|
31
27
|
)
|
|
32
28
|
|
|
33
29
|
pipeline_config = ReleasePipeline::Config.new(
|
|
@@ -36,11 +32,23 @@ module Metanorma
|
|
|
36
32
|
force: false,
|
|
37
33
|
force_replace_patterns: nil,
|
|
38
34
|
concurrency: 4,
|
|
39
|
-
default_visibility:
|
|
35
|
+
default_visibility: "public",
|
|
40
36
|
)
|
|
41
37
|
|
|
42
38
|
ReleasePipeline.new(deps).run(pipeline_config)
|
|
43
39
|
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def load_config
|
|
44
|
+
if @config.config_source && File.exist?(@config.config_source)
|
|
45
|
+
Metanorma::Release::Config.from_file(@config.config_source)
|
|
46
|
+
elsif @config.manifest && File.exist?(@config.manifest)
|
|
47
|
+
Metanorma::Release::Config.from_file(@config.manifest)
|
|
48
|
+
else
|
|
49
|
+
Metanorma::Release::Config.defaults
|
|
50
|
+
end
|
|
51
|
+
end
|
|
44
52
|
end
|
|
45
53
|
end
|
|
46
54
|
end
|
|
@@ -2,37 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
module Metanorma
|
|
4
4
|
module Release
|
|
5
|
-
class
|
|
5
|
+
class ReleaseCommand
|
|
6
6
|
Config = Struct.new(
|
|
7
7
|
:output_dir, :platform, :manifest, :force,
|
|
8
8
|
:force_replace, :channels, :concurrency, :token, :config_source,
|
|
9
9
|
keyword_init: true
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
include ConfigResolver
|
|
13
|
-
|
|
14
12
|
def initialize(config)
|
|
15
13
|
@config = config
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def call
|
|
19
|
-
|
|
20
|
-
channel_config = resolve_channel_config(@config.config_source, manifest)
|
|
21
|
-
|
|
17
|
+
config = load_config
|
|
22
18
|
options = { token: @config.token }
|
|
23
19
|
publisher = PlatformFactory.build_publisher(@config.platform, options)
|
|
24
20
|
channel_override = Channel.parse_list(@config.channels) if @config.channels
|
|
25
21
|
|
|
26
22
|
deps = ReleasePipeline::Dependencies.new(
|
|
27
|
-
extractor:
|
|
23
|
+
extractor: Publication,
|
|
28
24
|
filters: [],
|
|
29
25
|
change_detector: ContentHashChangeDetector.new(previous_releases: {}),
|
|
30
|
-
packager: ZipPackager.new,
|
|
26
|
+
packager: ZipPackager.new(output_dir: @config.output_dir),
|
|
31
27
|
publisher: publisher,
|
|
32
|
-
|
|
33
|
-
manifest:
|
|
28
|
+
slug_registry: SlugRegistry.from_config(config),
|
|
29
|
+
manifest: nil,
|
|
34
30
|
channel_override: channel_override,
|
|
35
|
-
|
|
31
|
+
config: config,
|
|
36
32
|
)
|
|
37
33
|
|
|
38
34
|
pipeline_config = ReleasePipeline::Config.new(
|
|
@@ -41,11 +37,23 @@ module Metanorma
|
|
|
41
37
|
force: @config.force,
|
|
42
38
|
force_replace_patterns: @config.force_replace && !@config.force_replace.empty? ? @config.force_replace : nil,
|
|
43
39
|
concurrency: @config.concurrency,
|
|
44
|
-
default_visibility:
|
|
40
|
+
default_visibility: "public",
|
|
45
41
|
)
|
|
46
42
|
|
|
47
43
|
ReleasePipeline.new(deps).run(pipeline_config)
|
|
48
44
|
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def load_config
|
|
49
|
+
if @config.config_source && File.exist?(@config.config_source)
|
|
50
|
+
Metanorma::Release::Config.from_file(@config.config_source)
|
|
51
|
+
elsif @config.manifest && File.exist?(@config.manifest)
|
|
52
|
+
Metanorma::Release::Config.from_file(@config.manifest)
|
|
53
|
+
else
|
|
54
|
+
Metanorma::Release::Config.defaults
|
|
55
|
+
end
|
|
56
|
+
end
|
|
49
57
|
end
|
|
50
58
|
end
|
|
51
59
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Release
|
|
7
|
+
class Config
|
|
8
|
+
def self.from_yaml(yaml_string)
|
|
9
|
+
data = YAML.safe_load(yaml_string, permitted_classes: [Symbol])
|
|
10
|
+
new(data || {})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.from_file(path)
|
|
14
|
+
unless File.exist?(path)
|
|
15
|
+
raise ArgumentError,
|
|
16
|
+
"Config file not found: #{path}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
from_yaml(File.read(path))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.defaults
|
|
23
|
+
new({})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(data)
|
|
27
|
+
@data = data
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def channels
|
|
31
|
+
@data.fetch("channels", [])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def routing
|
|
35
|
+
@data.fetch("routing", {})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def routing_default
|
|
39
|
+
routing.fetch("default", ["public"])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def routing_rules
|
|
43
|
+
routing.fetch("rules", [])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def slug_config
|
|
47
|
+
@data.fetch("slug", {})
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def slug_default_strategy
|
|
51
|
+
slug_config.fetch("default", "edition")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def slug_strategies
|
|
55
|
+
slug_config.fetch("strategies", {})
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def documents
|
|
59
|
+
@data.fetch("documents", [])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def defaults
|
|
63
|
+
@data.fetch("defaults", {})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def default_channels
|
|
67
|
+
list = defaults.fetch("channels", nil)
|
|
68
|
+
return ["public"] unless list
|
|
69
|
+
|
|
70
|
+
list
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def resolve_channels(publication)
|
|
74
|
+
manifest_channels = resolve_manifest_channels(publication)
|
|
75
|
+
return manifest_channels if manifest_channels
|
|
76
|
+
|
|
77
|
+
rule_channels = resolve_routing_rules(publication)
|
|
78
|
+
return rule_channels if rule_channels
|
|
79
|
+
|
|
80
|
+
default_channels
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def resolve_manifest_channels(publication)
|
|
86
|
+
documents.each do |entry|
|
|
87
|
+
next unless entry["source"] && publication.source_path&.end_with?(entry["source"])
|
|
88
|
+
return entry["channels"] if entry["channels"]
|
|
89
|
+
end
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def resolve_routing_rules(publication)
|
|
94
|
+
routing_rules.each do |rule|
|
|
95
|
+
match = true
|
|
96
|
+
match &&= Array(rule["stage"]).map(&:to_s).include?(publication.stage.to_s) if rule["stage"]
|
|
97
|
+
match &&= Array(rule["doctype"]).map(&:to_s).include?(publication.doctype.to_s) if rule["doctype"]
|
|
98
|
+
return rule["channels"] if match && rule["channels"]
|
|
99
|
+
end
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "digest"
|
|
4
4
|
|
|
5
5
|
module Metanorma
|
|
6
6
|
module Release
|
|
@@ -25,8 +25,16 @@ module Metanorma
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def self.of_directory(directory, base: nil)
|
|
28
|
-
pattern =
|
|
29
|
-
|
|
28
|
+
pattern = if base
|
|
29
|
+
File.join(directory,
|
|
30
|
+
"#{base}.*")
|
|
31
|
+
else
|
|
32
|
+
File.join(directory, "**",
|
|
33
|
+
"*")
|
|
34
|
+
end
|
|
35
|
+
files = Dir.glob(pattern).reject do |f|
|
|
36
|
+
File.directory?(f) || f.end_with?(".zip")
|
|
37
|
+
end
|
|
30
38
|
of_files(files)
|
|
31
39
|
end
|
|
32
40
|
|