metanorma-release 0.2.6 → 0.2.8

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: fd5a3df2d9c64ed9d7d70d8f4c3fd3646d8c932a4cbffc39c07490e0276e37d9
4
- data.tar.gz: f688bc55c1dff3751cdc1903e420e64c7503f7716710655c123e034dd0a3a3fb
3
+ metadata.gz: 33318ed83e806c7f0f7e4d16c3095762cf64f3558214a6775549c2dd5bd74302
4
+ data.tar.gz: 3d72e6618afb935951e22b19284299dad2adc3c7940cd87735fb47bb3f13b650
5
5
  SHA512:
6
- metadata.gz: 8127051648875b3ea36b988542c9ae82954bf68caf4ad91b785543d8ff6518ef95e1caa7d55334a777c22567806fbfa6cd32014812e6b2885388f42997f881d1
7
- data.tar.gz: 77c9e3f867de101f3e962d674c8ddcabc630b9ba45557afe4c6338af0669a572dee37432b8231887cde7964f143817ea4ee1b50c9549bf2875dcdc76542152fe
6
+ metadata.gz: eff37e2e0d375443613b578fabf7f47a51b007557ff602995105c020fe8a67034b755514d5f75d9e915189b8a189b1b32b3439ea632e478413556bd2bebc4575
7
+ data.tar.gz: fa1201421381243f2e7b1ef363c621afe636b4d684493a29b53a333f44417093f32bae0cc1ee0c99a4a9f6722177b28e6e457811909680ca631dd871d4ac0143
@@ -23,7 +23,10 @@ module Metanorma
23
23
  end
24
24
 
25
25
  def matches?(filter_channels)
26
- filter_channels.any? { |c| eql?(Channel.new(c)) }
26
+ filter_channels.any? do |c|
27
+ fc = Channel.new(c)
28
+ eql?(fc) || @name.start_with?("#{fc.name}/")
29
+ end
27
30
  end
28
31
 
29
32
  def self.parse(channel_string)
@@ -18,7 +18,7 @@ module Metanorma
18
18
  return true if @all_channels
19
19
 
20
20
  parsed = manifest_channels.map { |c| Channel.new(c) }
21
- parsed.any? { |mc| @channels.any? { |fc| fc.eql?(mc) } }
21
+ parsed.any? { |mc| mc.matches?(@channels) }
22
22
  end
23
23
 
24
24
  private
@@ -29,7 +29,7 @@ module Metanorma
29
29
  release_channels = (release_metadata["channels"] || []).map do |c|
30
30
  Channel.new(c)
31
31
  end
32
- release_channels.any? { |rc| @channels.any? { |fc| fc.eql?(rc) } }
32
+ release_channels.any? { |rc| rc.matches?(@channels) }
33
33
  end
34
34
 
35
35
  def stage_match?(release_metadata)
@@ -9,7 +9,7 @@ module Metanorma
9
9
  :source, :organizations, :topic, :repos, :repo_pattern, :local_path,
10
10
  :channels, :stages, :output_dir, :file_routing, :cache_dir,
11
11
  :data_dir, :include_drafts, :concurrency, :min_documents, :token,
12
- :create_zip,
12
+ :create_zip, :org,
13
13
  keyword_init: true
14
14
  )
15
15
 
@@ -18,9 +18,11 @@ module Metanorma
18
18
 
19
19
  def initialize(config)
20
20
  @config = config
21
+ @org_config = nil
21
22
  end
22
23
 
23
24
  def call
25
+ load_org_config
24
26
  result = run_aggregation
25
27
  return result unless result.publications.any?
26
28
 
@@ -54,6 +56,7 @@ module Metanorma
54
56
  min_documents: merged[:min_documents],
55
57
  token: merged[:token],
56
58
  create_zip: merged[:create_zip],
59
+ org: merged[:org],
57
60
  )
58
61
  end
59
62
 
@@ -82,6 +85,7 @@ module Metanorma
82
85
  min_documents: cli_options[:min_documents] || file_data["min_documents"],
83
86
  token: cli_options[:token],
84
87
  create_zip: cli_options[:create_zip],
88
+ org: file_data["org"],
85
89
  }
86
90
  end
87
91
 
@@ -163,6 +167,27 @@ module Metanorma
163
167
  rescue LoadError
164
168
  warn " (relaton gem not available — bibliography skipped)"
165
169
  end
170
+
171
+ def load_org_config
172
+ return unless @config.org
173
+
174
+ ref = OrgConfig.parse_ref(@config.org)
175
+ local_path = OrgConfig.remote_path(ref)
176
+ @org_config = File.exist?(local_path) ? OrgConfig.from_file(local_path) : fetch_org_config_from_github(ref)
177
+ end
178
+
179
+ def fetch_org_config_from_github(ref)
180
+ require "octokit"
181
+ token = @config.token || ENV.fetch("GITHUB_TOKEN", nil)
182
+ client = token ? Octokit::Client.new(access_token: token) : Octokit::Client.new
183
+ remote = OrgConfig.remote_path(ref)
184
+ contents = client.contents("#{ref.owner}/#{ref.repo}", path: remote)
185
+ decoded = Base64.decode64(contents[:content])
186
+ OrgConfig.from_yaml(decoded)
187
+ rescue StandardError => e
188
+ warn " (org config not loaded from #{ref.owner}/#{ref.repo}: #{e.message})"
189
+ OrgConfig.defaults
190
+ end
166
191
  end
167
192
  end
168
193
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
4
+
3
5
  module Metanorma
4
6
  module Release
5
7
  class ReleaseCommand
@@ -14,7 +16,8 @@ module Metanorma
14
16
  end
15
17
 
16
18
  def call
17
- config = load_config
19
+ org_config = load_org_config
20
+ config = load_config(org_config: org_config)
18
21
  options = { token: @config.token }
19
22
  publisher = PlatformFactory.build_publisher(@config.platform, options)
20
23
  channel_override = Channel.parse_list(@config.channels) if @config.channels
@@ -45,15 +48,59 @@ module Metanorma
45
48
 
46
49
  private
47
50
 
48
- def load_config
51
+ def load_config(org_config: nil)
49
52
  if @config.config_source && File.exist?(@config.config_source)
50
- Metanorma::Release::Config.from_file(@config.config_source)
53
+ Metanorma::Release::Config.from_file(@config.config_source, org_config: org_config)
51
54
  elsif @config.manifest && File.exist?(@config.manifest)
52
- Metanorma::Release::Config.from_file(@config.manifest)
55
+ Metanorma::Release::Config.from_file(@config.manifest, org_config: org_config)
53
56
  else
54
- Metanorma::Release::Config.defaults
57
+ Metanorma::Release::Config.defaults(org_config: org_config)
55
58
  end
56
59
  end
60
+
61
+ def load_org_config
62
+ path = find_config_path
63
+ return nil unless path
64
+
65
+ org_ref = extract_org_ref(path)
66
+ return nil unless org_ref
67
+
68
+ resolve_org_config(org_ref)
69
+ end
70
+
71
+ def find_config_path
72
+ if @config.config_source && File.exist?(@config.config_source)
73
+ @config.config_source
74
+ elsif @config.manifest && File.exist?(@config.manifest)
75
+ @config.manifest
76
+ end
77
+ end
78
+
79
+ def extract_org_ref(path)
80
+ raw = YAML.safe_load_file(path, permitted_classes: [Symbol]) || {}
81
+ raw["org"]
82
+ end
83
+
84
+ def resolve_org_config(org_ref)
85
+ ref = OrgConfig.parse_ref(org_ref)
86
+ local_path = OrgConfig.remote_path(ref)
87
+ return OrgConfig.from_file(local_path) if File.exist?(local_path)
88
+
89
+ fetch_org_config_from_github(ref)
90
+ end
91
+
92
+ def fetch_org_config_from_github(ref)
93
+ require "octokit"
94
+ token = @config.token || ENV.fetch("GITHUB_TOKEN", nil)
95
+ client = token ? Octokit::Client.new(access_token: token) : Octokit::Client.new
96
+ remote = OrgConfig.remote_path(ref)
97
+ contents = client.contents("#{ref.owner}/#{ref.repo}", path: remote)
98
+ decoded = Base64.decode64(contents[:content])
99
+ OrgConfig.from_yaml(decoded)
100
+ rescue StandardError => e
101
+ warn " (org config not loaded from #{ref.owner}/#{ref.repo}: #{e.message})"
102
+ OrgConfig.defaults
103
+ end
57
104
  end
58
105
  end
59
106
  end
@@ -5,26 +5,31 @@ require "yaml"
5
5
  module Metanorma
6
6
  module Release
7
7
  class Config
8
- def self.from_yaml(yaml_string)
8
+ def self.from_yaml(yaml_string, org_config: nil)
9
9
  data = YAML.safe_load(yaml_string, permitted_classes: [Symbol])
10
- new(data || {})
10
+ new(data || {}, org_config: org_config)
11
11
  end
12
12
 
13
- def self.from_file(path)
13
+ def self.from_file(path, org_config: nil)
14
14
  unless File.exist?(path)
15
15
  raise ArgumentError,
16
16
  "Config file not found: #{path}"
17
17
  end
18
18
 
19
- from_yaml(File.read(path))
19
+ from_yaml(File.read(path), org_config: org_config)
20
20
  end
21
21
 
22
- def self.defaults
23
- new({})
22
+ def self.defaults(org_config: nil)
23
+ new({}, org_config: org_config)
24
24
  end
25
25
 
26
- def initialize(data)
26
+ def initialize(data, org_config: nil)
27
27
  @data = data
28
+ @org_config = org_config
29
+ end
30
+
31
+ def org
32
+ @data["org"]
28
33
  end
29
34
 
30
35
  def channels
@@ -77,6 +82,15 @@ module Metanorma
77
82
  rule_channels = resolve_routing_rules(publication)
78
83
  return rule_channels if rule_channels
79
84
 
85
+ org_rule_channels = resolve_org_routing_rules(publication)
86
+ return org_rule_channels if org_rule_channels
87
+
88
+ local_default = routing_default
89
+ return local_default unless local_default == ["public"] && @org_config
90
+
91
+ org_default = @org_config&.routing_default
92
+ return org_default unless org_default.nil? || org_default.empty?
93
+
80
94
  default_channels
81
95
  end
82
96
 
@@ -99,6 +113,18 @@ module Metanorma
99
113
  end
100
114
  nil
101
115
  end
116
+
117
+ def resolve_org_routing_rules(publication)
118
+ return nil unless @org_config
119
+
120
+ @org_config.routing_rules.each do |rule|
121
+ match = true
122
+ match &&= Array(rule["stage"]).map(&:to_s).include?(publication.stage.to_s) if rule["stage"]
123
+ match &&= Array(rule["doctype"]).map(&:to_s).include?(publication.doctype.to_s) if rule["doctype"]
124
+ return rule["channels"] if match && rule["channels"]
125
+ end
126
+ nil
127
+ end
102
128
  end
103
129
  end
104
130
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Metanorma
6
+ module Release
7
+ class OrgConfig
8
+ Ref = Struct.new(:owner, :repo, :name, keyword_init: true)
9
+
10
+ def self.parse_ref(org_string)
11
+ parts = org_string.to_s.split("#", 2)
12
+ slug = parts[0].to_s.strip
13
+ segments = slug.split("/", 2)
14
+ raise ArgumentError, "Invalid org reference: #{org_string}" unless segments.length == 2
15
+
16
+ Ref.new(owner: segments[0], repo: segments[1], name: parts[1]&.strip)
17
+ end
18
+
19
+ def self.default_config_name
20
+ "channels"
21
+ end
22
+
23
+ def self.remote_path(ref)
24
+ name = ref.name || default_config_name
25
+ ".metanorma/#{name}.yml"
26
+ end
27
+
28
+ def self.from_yaml(yaml_string)
29
+ data = YAML.safe_load(yaml_string, permitted_classes: [Symbol])
30
+ new(data || {})
31
+ end
32
+
33
+ def self.from_file(path)
34
+ raise ArgumentError, "Org config file not found: #{path}" unless File.exist?(path)
35
+
36
+ from_yaml(File.read(path))
37
+ end
38
+
39
+ def self.defaults
40
+ new({})
41
+ end
42
+
43
+ def initialize(data)
44
+ @data = data
45
+ end
46
+
47
+ def channels
48
+ @data.fetch("channels", [])
49
+ end
50
+
51
+ def routing_default
52
+ dig_defaults_routing("default") || []
53
+ end
54
+
55
+ def routing_rules
56
+ dig_defaults_routing("rules") || []
57
+ end
58
+
59
+ def valid_channel?(name)
60
+ return true if channels.empty?
61
+
62
+ ch = Channel.new(name)
63
+ channels.any? do |valid|
64
+ valid_ch = Channel.new(valid)
65
+ ch.eql?(valid_ch) || ch.name.start_with?("#{valid_ch.name}/") || valid_ch.name.start_with?("#{ch.name}/")
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def dig_defaults_routing(key)
72
+ @data.dig("defaults", "routing", key)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Metanorma
4
4
  module Release
5
- VERSION = "0.2.6"
5
+ VERSION = "0.2.8"
6
6
  end
7
7
  end
@@ -11,6 +11,7 @@ module Metanorma
11
11
  autoload :Index, "metanorma/release/index"
12
12
  autoload :Site, "metanorma/release/site"
13
13
  autoload :Channel, "metanorma/release/channel"
14
+ autoload :OrgConfig, "metanorma/release/org_config"
14
15
  autoload :Config, "metanorma/release/config"
15
16
  autoload :ContentHash, "metanorma/release/content_hash"
16
17
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-release
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -102,6 +102,7 @@ files:
102
102
  - lib/metanorma/release/file_routing.rb
103
103
  - lib/metanorma/release/index.rb
104
104
  - lib/metanorma/release/interfaces.rb
105
+ - lib/metanorma/release/org_config.rb
105
106
  - lib/metanorma/release/platform.rb
106
107
  - lib/metanorma/release/platform/github.rb
107
108
  - lib/metanorma/release/platform/github/manifest_reader.rb