suma 0.5.19 → 0.5.20
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/lib/suma/staged_collection_builder.rb +105 -45
- data/lib/suma/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 467b9bcc21844daaac8a68fdb76b15b9e165b6fd2243d469282f44d15f69cb97
|
|
4
|
+
data.tar.gz: 4714dc0a4e2b9b8f63f4cdb5a88db33401662633f15fb7669c3c18908dc77492
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a2a42ab57f4d4cec318dd6232e604ae9081402d66372437abd3854efa435ad87fddd43e91fd0da24156584bfc652174b36930cc7d30b00fb727857b9cd3e66a
|
|
7
|
+
data.tar.gz: 69822cae107459997e8ebb80499fc6f8058f8e396949a33e8971b2cc6802ffbabe1f221f51c3b132b5c32170f965920363c5f61da3d6f7d3ae49e27a1a5b0ecb
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "metanorma"
|
|
4
4
|
require "yaml"
|
|
5
5
|
require "fileutils"
|
|
6
|
+
require "pathname"
|
|
6
7
|
|
|
7
8
|
module Suma
|
|
8
9
|
# Memory-bounded, resumable staged collection build (metanorma/suma#94).
|
|
@@ -73,7 +74,7 @@ module Suma
|
|
|
73
74
|
staged = shard_members(members)
|
|
74
75
|
Utils.log shard_log(staged.size, members.size)
|
|
75
76
|
if @workers == 1
|
|
76
|
-
staged.each_with_index { |member, i| stage_member(member, i) }
|
|
77
|
+
unstaged_members(staged).each_with_index { |member, i| stage_member(member, i) }
|
|
77
78
|
else
|
|
78
79
|
stage_parallel(staged)
|
|
79
80
|
end
|
|
@@ -104,6 +105,19 @@ module Suma
|
|
|
104
105
|
"skipping reinflate"
|
|
105
106
|
end
|
|
106
107
|
|
|
108
|
+
# Members already present in the store (their docref-slug artefact
|
|
109
|
+
# exists) are not re-rendered: an interrupted or repeated build over
|
|
110
|
+
# a warm store goes straight to reinflate, so the assembly pass costs
|
|
111
|
+
# minutes instead of re-rendering the whole collection.
|
|
112
|
+
def unstaged_members(members)
|
|
113
|
+
members.reject do |member|
|
|
114
|
+
identifier = member["identifier"]
|
|
115
|
+
stored = identifier && stored_semantic(identifier)
|
|
116
|
+
Utils.log "[staged] stored, skipping #{identifier}" if stored
|
|
117
|
+
stored
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
107
121
|
# Batch the members and run +@workers+ renderer child processes
|
|
108
122
|
# concurrently. Each batch file carries fully-rendered single-member
|
|
109
123
|
# manifests; children claim batches through O_EXCL markers, so a slow
|
|
@@ -113,7 +127,7 @@ module Suma
|
|
|
113
127
|
# identifier-keyed store entries, so concurrent children cannot race;
|
|
114
128
|
# sectionsplit only happens at reinflate (a single sequential pass).
|
|
115
129
|
def stage_parallel(members)
|
|
116
|
-
paths, count = write_batch_files(members)
|
|
130
|
+
paths, count = write_batch_files(unstaged_members(members))
|
|
117
131
|
run_batch_workers(count)
|
|
118
132
|
ensure
|
|
119
133
|
remove_batch_files(paths, count)
|
|
@@ -201,15 +215,13 @@ module Suma
|
|
|
201
215
|
# identifier; members whose document docid differs from the
|
|
202
216
|
# docref (docrefs carrying metanorma's GUID marker) were
|
|
203
217
|
# therefore unrecoverable ("no stored artefact"). Alias the
|
|
204
|
-
# artefacts
|
|
205
|
-
# Shape: single_member_manifest =>
|
|
206
|
-
#
|
|
218
|
+
# artefacts keyed by the render's own bibdata keys under the
|
|
219
|
+
# docref slug too. Shape: single_member_manifest =>
|
|
220
|
+
# {"manifest" => {"manifest" => [{"docref" => [dr]}]}}.
|
|
207
221
|
member_id = manifest.dig("manifest", "manifest", 0, "docref", 0, "identifier")
|
|
208
222
|
id_slug = member_id.to_s
|
|
209
223
|
.gsub(/[^A-Za-z0-9._-]+/, "-").gsub(/-{2,}/, "-")
|
|
210
224
|
.gsub(/\A-|-\z/, "")
|
|
211
|
-
semantics_before = Dir.children(store_dir)
|
|
212
|
-
.select { |f| f.end_with?(".semantic.xml") }
|
|
213
225
|
compile = { install_fonts: false }
|
|
214
226
|
compile[:novalid] = true if no_valid == "1"
|
|
215
227
|
collection = Metanorma::Collection.parse(one_path)
|
|
@@ -219,37 +231,25 @@ module Suma
|
|
|
219
231
|
compile: compile,
|
|
220
232
|
)
|
|
221
233
|
unless id_slug.empty?
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
%r{/#{Regexp.escape(src_slug)}.},
|
|
242
|
-
"/#{id_slug}.",
|
|
243
|
-
)
|
|
244
|
-
FileUtils.cp(f, alias_name)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
produced.each do |f|
|
|
249
|
-
if (m = f.match(/\A(.*).([0-9a-f]+).semantic.xml\z/))
|
|
250
|
-
alias_name = "#{id_slug}.#{m[2]}.semantic.xml"
|
|
251
|
-
alias_path = File.join(store_dir, alias_name)
|
|
252
|
-
FileUtils.cp(File.join(store_dir, f), alias_path)
|
|
234
|
+
# Alias strictly from the collection's own bibdata keys:
|
|
235
|
+
# they are the docids this member's render stored under.
|
|
236
|
+
# A produced-files delta is not safe here - with
|
|
237
|
+
# concurrent workers another child's render lands in the
|
|
238
|
+
# shared store mid-flight, and a delta would alias other
|
|
239
|
+
# members' documents under this docref (suma#167).
|
|
240
|
+
(collection.bibdatas || {}).keys.each do |key|
|
|
241
|
+
src_slug = key.to_s
|
|
242
|
+
.gsub(/[^A-Za-z0-9._-]+/, "-").gsub(/-{2,}/, "-")
|
|
243
|
+
.gsub(/\A-|-\z/, "")
|
|
244
|
+
next if src_slug.empty? || src_slug == id_slug
|
|
245
|
+
|
|
246
|
+
Dir[File.join(store_dir,
|
|
247
|
+
"#{src_slug}.*.semantic.xml")].each do |f|
|
|
248
|
+
alias_name = f.sub(
|
|
249
|
+
%r{/#{Regexp.escape(src_slug)}.},
|
|
250
|
+
"/#{id_slug}.",
|
|
251
|
+
)
|
|
252
|
+
FileUtils.cp(f, alias_name)
|
|
253
253
|
end
|
|
254
254
|
end
|
|
255
255
|
end
|
|
@@ -313,21 +313,81 @@ module Suma
|
|
|
313
313
|
end
|
|
314
314
|
|
|
315
315
|
# Assemble a manifest of the stored stubs and reinflate them into the site.
|
|
316
|
+
# The original manifest tree is preserved (nested collections,
|
|
317
|
+
# attachments, per-member sectionsplit) with each member entry swapped
|
|
318
|
+
# for its stored stub; the render resolves paths against the
|
|
319
|
+
# reinflate manifest's own directory, so every path the render reads
|
|
320
|
+
# is absolutized against the collection root (suma#168, suma#169).
|
|
316
321
|
def reinflate(members)
|
|
317
322
|
reinf_dir = File.join(@output_directory, "_staged_reinflate")
|
|
318
323
|
FileUtils.mkdir_p(reinf_dir)
|
|
319
324
|
docrefs = stored_docrefs(members, reinf_dir)
|
|
325
|
+
write_reinflate_manifest(docrefs, reinf_dir)
|
|
326
|
+
run_render(manifest_path: File.join(reinf_dir, "reinflate.yml"),
|
|
327
|
+
opts: reinflate_render_options)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def write_reinflate_manifest(docrefs, reinf_dir)
|
|
331
|
+
rebuilt = with_stubbed_members(manifest["manifest"], docrefs, [0])
|
|
320
332
|
reinf_path = File.join(reinf_dir, "reinflate.yml")
|
|
321
333
|
File.write(reinf_path,
|
|
322
|
-
base_manifest.merge("manifest" =>
|
|
334
|
+
base_manifest.merge("manifest" => rebuilt).to_yaml)
|
|
323
335
|
Utils.log "[staged] reinflating #{docrefs.size} stored members -> " \
|
|
324
336
|
"#{@output_directory}"
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def reinflate_render_options
|
|
340
|
+
{
|
|
341
|
+
format: @formats,
|
|
342
|
+
output_folder: File.expand_path(@output_directory),
|
|
343
|
+
reinflate: true,
|
|
344
|
+
coverpage: @coverpage && File.expand_path(@coverpage, @config_dir),
|
|
345
|
+
compile: compile_flags,
|
|
346
|
+
}.compact
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Deep-copy the manifest tree, replacing each member entry (same
|
|
350
|
+
# predicate and pre-order as collection_members) with its stub docref,
|
|
351
|
+
# and absolutizing non-member filerefs (attachments) against the
|
|
352
|
+
# collection root.
|
|
353
|
+
def with_stubbed_members(node, docrefs, counter)
|
|
354
|
+
case node
|
|
355
|
+
when Array
|
|
356
|
+
node.map { |child| with_stubbed_members(child, docrefs, counter) }
|
|
357
|
+
when Hash
|
|
358
|
+
with_stubbed_members_hash(node, docrefs, counter)
|
|
359
|
+
else
|
|
360
|
+
node
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def with_stubbed_members_hash(node, docrefs, counter)
|
|
365
|
+
rewritten = node.dup
|
|
366
|
+
rewritten.each_key do |key|
|
|
367
|
+
rewritten[key] = rewrite_entry_value(key, rewritten[key], docrefs, counter)
|
|
368
|
+
end
|
|
369
|
+
stub_for(rewritten, docrefs, counter) || rewritten
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def rewrite_entry_value(key, value, docrefs, counter)
|
|
373
|
+
if %w[file fileref].include?(key) && value &&
|
|
374
|
+
!Pathname.new(value).absolute?
|
|
375
|
+
File.expand_path(value, @config_dir)
|
|
376
|
+
else
|
|
377
|
+
with_stubbed_members(value, docrefs, counter)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def stub_for(node, docrefs, counter)
|
|
382
|
+
return unless member_entry?(node)
|
|
383
|
+
|
|
384
|
+
index = counter[0]
|
|
385
|
+
counter[0] += 1
|
|
386
|
+
docrefs[index]
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def member_entry?(node)
|
|
390
|
+
(node["fileref"] || node["file"]) && !truthy?(node["attachment"])
|
|
331
391
|
end
|
|
332
392
|
|
|
333
393
|
def compile_flags
|
data/lib/suma/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: suma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: expressir
|