tebako-release 0.3.0 → 0.4.0

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: da808fc0759260d4b29d60ffb1d51622cb07c3f8cf3125b4ea01025f1db4f927
4
- data.tar.gz: 8d21389348ae2bb086aaf17f3901f386e62598be12bee027e653b76b361e8d1b
3
+ metadata.gz: 6e91116fb240ffb5e0cb7cec20a1753fbd4177b7bd8f3c78adb8c3e260acf718
4
+ data.tar.gz: c5c886163f60c1ef021829015d95a1f3c50b6fc239a22a39cb890324041353d1
5
5
  SHA512:
6
- metadata.gz: d73130a2cbd8d1de0df2bb1cf1d801bfe260db9b5569cc04e736eca3324d945576cf8ee9c13c22eb31000872e9229cfb5931c553a491e72188bf58d793206cba
7
- data.tar.gz: c111f4d684714a0b03fbc92891b9e2f6640f8cc05369b77870e73bc04f34847367692c89cbaf06c48138fbf8d6172fe675c4124add72be2da7ba8c2bcd674915
6
+ metadata.gz: 78d61711350a83bc076b26472fab4b468e68368ca6f16f9f4c7bb613149d0a7c9be68821e6646e7469958e24424f59b3b88afd3d45c7b1f01db0b806550d9c69
7
+ data.tar.gz: 44d1ee55d6bf22f2aa8d6dfdacaa27f26a77170faa2d26a45669425879d6b5ece851d0d5e1072855c6f9fa6a7b2269578f1b112a0d62b34fecf2510bd82fa6a8
data/README.md CHANGED
@@ -23,7 +23,12 @@ through a small adapter file and pin this gem — they never carry copies.
23
23
  in-bundle SHA256SUMS; fixed member order, mtime=0 — identical inputs
24
24
  yield identical bytes). A factory opts into the bundle-era publish shape
25
25
  through its adapter (`bundle_publish? → true`); the per-file shape stays
26
- the default for pre-bundle lines.
26
+ the default for pre-bundle lines. Bundle-era lines whose consumers
27
+ include the lazy arm additionally opt into co-publish
28
+ (`per_file_alongside_bundle? → true`, spec 36 §3): the staged members
29
+ are served as standalone assets beside the bundle, the shard carries
30
+ the `per_file_assets` witness (runtime-manifest MINOR 2), the facet
31
+ signature declarations return, and the audit expects the union.
27
32
  - **`TebakoRelease::Blksum`** — the spec 39 §3 block-group digest sidecar
28
33
  (`<image>.blksum.json`: one sha256 per 4 MiB group of env-image bytes,
29
34
  the lazy mount's range-GET trust anchor). The uploader derives it
@@ -55,5 +55,18 @@ module TebakoRelease
55
55
  def bundle_publish?
56
56
  false
57
57
  end
58
+
59
+ # Spec 36 §3's co-publish: when true (and bundle_publish? holds), each
60
+ # leg ALSO serves the per-file assets — the exe under its historical
61
+ # spelling, the env image, the windows DLL — as standalone release
62
+ # assets beside the bundle, each with its .sha256 sidecar. The lazy
63
+ # arm (spec 39 §7) range-fetches the image's groups over HTTP, which
64
+ # is impossible inside the gzip stream; the shard gains the additive
65
+ # per_file_assets witness (runtime-manifest MINOR 2) that gates the
66
+ # loaders' lazy arm on bundle-declaring shards. Default off: a
67
+ # factory opts in when its consumers include the lazy arm.
68
+ def per_file_alongside_bundle?
69
+ false
70
+ end
58
71
  end
59
72
  end
@@ -437,7 +437,7 @@ module TebakoRelease
437
437
  nil
438
438
  end
439
439
 
440
- def manifest_entry(package, image = nil, dll = nil, bundle: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
440
+ def manifest_entry(package, image = nil, dll = nil, bundle: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
441
441
  runtime_version, platform = parse_package_filename(package.basename.to_s)
442
442
  contract = contract_sidecar(package)
443
443
  filename = package.basename.to_s
@@ -470,6 +470,11 @@ module TebakoRelease
470
470
  entry[:image] = image_entry(image, blksum) if image
471
471
  entry[:dll] = dll_entry(dll, runtime_version, platform) if dll
472
472
  entry[:bundle] = bundle_entry(bundle) if bundle
473
+ # Spec 36 §3's co-publish witness (runtime-manifest MINOR 2):
474
+ # true declares the per-file assets are served standalone beside
475
+ # the bundle — the lazy arm's gate on bundle-declaring shards
476
+ # (spec 39 §7). Never emitted on bundle-only lines.
477
+ entry[:per_file_assets] = true if bundle && copublish?
473
478
  declare_signatures(entry)
474
479
  end
475
480
  end
@@ -530,7 +535,13 @@ module TebakoRelease
530
535
  return unless signing_enabled?
531
536
 
532
537
  keyid = signing_keyid
533
- return declare_bundle_signature(entry, keyid) if entry[:bundle]
538
+ declare_bundle_signature(entry, keyid) if entry[:bundle]
539
+ # Bundle-only lines serve no other payload asset — declaring the
540
+ # members' .ascs there would be an invalid signing state (spec 09
541
+ # §4). A co-published line (spec 36 §3, the shard's per_file_assets
542
+ # witness) serves the members standalone, so the every-served-name
543
+ # rule covers the exe/image/dll set again.
544
+ return if entry[:bundle] && !entry[:per_file_assets]
534
545
 
535
546
  entry[:signature] = { keyid: keyid, asc: "#{entry[:filename]}.asc" }
536
547
  %i[image dll].each do |facet|
@@ -539,10 +550,10 @@ module TebakoRelease
539
550
  end
540
551
  end
541
552
 
542
- # Bundle-era (spec 36 §3): the only served payload is the bundle —
543
- # three signatures per leg (bundle, its sidecar, the shard). The
544
- # exe/image/dll member pins are NOT served assets; declaring their
545
- # .asc would be an invalid signing state (spec 09 §4).
553
+ # Bundle-era (spec 36 §3): the bundle's own signature declaration.
554
+ # On bundle-only lines it is the ONLY payload signature — three
555
+ # signatures per leg (bundle, its sidecar, the shard); co-published
556
+ # lines add the per-file declarations on top (declare_signatures).
546
557
  def declare_bundle_signature(entry, keyid)
547
558
  entry[:bundle][:signature] = { keyid: keyid, asc: "#{entry[:bundle][:filename]}.asc" }
548
559
  end
@@ -832,11 +843,16 @@ module TebakoRelease
832
843
  # feeds the entry's bundle block and the idempotent skip), upload the
833
844
  # bundles as the legs' only payload assets, then the metadata (shard
834
845
  # with the bundle block + the bundle's sidecar). The member files
835
- # themselves never become release assets — the bundle is the unit.
846
+ # themselves never become release assets — the bundle is the unit —
847
+ # UNLESS the factory co-publishes (spec 36 §3): then the members are
848
+ # standalone served names beside the bundle (the lazy arm
849
+ # range-fetches the image), still ahead of the shard that witnesses
850
+ # them (a witnessed shard without its assets is the invalid publish).
836
851
  def process_bundle_release(release, packages)
837
852
  bundles = build_bundles(packages)
838
853
  entries = build_manifest_entries(packages, bundles: bundles)
839
854
  bundles.each_value { |bundle| upload_package(release, bundle) }
855
+ packages.each { |package| upload_package(release, package) } if copublish?
840
856
  upload_blksum_sidecars(release, entries)
841
857
  entries.each { |entry| ensure_package_metadata(release, entry) }
842
858
  print_settled_summary
@@ -877,6 +893,14 @@ module TebakoRelease
877
893
  @config.adapter.bundle_publish?
878
894
  end
879
895
 
896
+ # Spec 36 §3's co-publish mode: a bundle-era line that ALSO serves
897
+ # the per-file assets as standalone release assets (the lazy arm's
898
+ # serving requirement — spec 39 §7). Factory-declared through the
899
+ # adapter, like the era.
900
+ def copublish?
901
+ bundle_publish? && @config.adapter.per_file_alongside_bundle?
902
+ end
903
+
880
904
  # AUDIT_ONLY (the coordinator's release job / a publish dry run):
881
905
  # strictly read-only — finds the release (never creates one), needs no
882
906
  # local packages, uploads nothing, touches no notes; the release's
@@ -943,20 +967,27 @@ module TebakoRelease
943
967
  # era: the exe, its .tfs image and its .dll facet. Bundle era (spec 36
944
968
  # §3): the bundle is the ONE served payload asset — the entry's
945
969
  # exe/image/dll fields are member pins, not served names, so they earn
946
- # no sidecar. Both eras: the derived blksum sidecar (spec 39 §3) IS a
947
- # served standalone asset, so it earns its own .sha256 sidecar like
970
+ # no sidecar — UNLESS the entry carries the co-publish witness, when
971
+ # the members ARE served standalone beside the bundle and earn theirs.
972
+ # Both eras: the derived blksum sidecar (spec 39 §3) IS a served
973
+ # standalone asset, so it earns its own .sha256 sidecar like
948
974
  # every served name.
949
975
  def metadata_assets(entry)
950
- served = if entry[:bundle]
951
- { entry[:bundle][:filename] => entry[:bundle][:sha256] }
952
- else
953
- { entry[:filename] => entry[:sha256] }
954
- .merge(facet_metadata(entry, :image))
955
- .merge(facet_metadata(entry, :dll))
956
- end
976
+ served = bundle_metadata(entry)
977
+ served.merge!(per_file_metadata(entry)) unless entry[:bundle] && !entry[:per_file_assets]
957
978
  served.merge(blksum_metadata(entry))
958
979
  end
959
980
 
981
+ def bundle_metadata(entry)
982
+ entry[:bundle] ? { entry[:bundle][:filename] => entry[:bundle][:sha256] } : {}
983
+ end
984
+
985
+ def per_file_metadata(entry)
986
+ { entry[:filename] => entry[:sha256] }
987
+ .merge(facet_metadata(entry, :image))
988
+ .merge(facet_metadata(entry, :dll))
989
+ end
990
+
960
991
  def facet_metadata(entry, facet)
961
992
  block = entry[facet]
962
993
  block ? { block[:filename] => block[:sha256] } : {}
@@ -1183,7 +1214,7 @@ module TebakoRelease
1183
1214
 
1184
1215
  exe = [name, "#{name}.exe"].find { |candidate| present.include?(candidate) }
1185
1216
  landed, missing = expected_facets(name).partition { |facet| present.include?(facet) }
1186
- landed, missing = split_blksum_expectation(release, present, name, landed, missing)
1217
+ landed, missing = split_blksum_expectation(served_shard(release, present, name), present, landed, missing)
1187
1218
  missing.unshift(name) unless exe
1188
1219
  return missing if exe.nil?
1189
1220
 
@@ -1194,12 +1225,17 @@ module TebakoRelease
1194
1225
  # asset; a landed bundle owes its sidecar and the package's shard
1195
1226
  # (plus an .asc of each on signing-enabled audits). The declared
1196
1227
  # blksum sidecar (spec 39 §3) is a standalone served asset in this
1197
- # era too — expected, with its own metadata, when the shard pins it.
1228
+ # era too — expected, with its own metadata, when the shard pins it —
1229
+ # and a shard carrying the co-publish witness (spec 36 §3's
1230
+ # per_file_assets) owes the standalone per-file assets beside the
1231
+ # bundle.
1198
1232
  def missing_bundle_assets(release, present, name, require_signatures: false)
1199
1233
  bundle = "#{name}#{Bundler::BUNDLE_SUFFIX}"
1200
1234
  return [bundle] unless present.include?(bundle)
1201
1235
 
1202
- landed, missing = split_blksum_expectation(release, present, name, [bundle], [])
1236
+ shard = served_shard(release, present, name)
1237
+ landed, missing = split_blksum_expectation(shard, present, [bundle], [])
1238
+ landed, missing = split_copublish_expectation(shard, present, name, landed, missing)
1203
1239
  missing + missing_metadata(present, name, landed, require_signatures: require_signatures)
1204
1240
  end
1205
1241
 
@@ -1212,8 +1248,8 @@ module TebakoRelease
1212
1248
  # the shard's own gap is the gate's business). A DECLARED sidecar that
1213
1249
  # never landed reports its own name only — its sidecar/.asc cascade
1214
1250
  # rides the landed set, one error per gap.
1215
- def split_blksum_expectation(release, present, name, landed, missing)
1216
- declared = declared_blksum_name(release, present, name)
1251
+ def split_blksum_expectation(shard, present, landed, missing)
1252
+ declared = shard&.dig("image", "blksum", "filename")
1217
1253
  return [landed, missing] unless declared
1218
1254
 
1219
1255
  if present.include?(declared)
@@ -1223,17 +1259,35 @@ module TebakoRelease
1223
1259
  end
1224
1260
  end
1225
1261
 
1226
- # The sidecar name the package's shard declares (`image.blksum`),
1227
- # nil when undeclared, when the shard never landed, or when it cannot
1228
- # be read (loudly — the audit never guesses an expectation).
1229
- def declared_blksum_name(release, present, name)
1262
+ # The shard-witnessed co-publish expectation (spec 36 §3): a shard
1263
+ # carrying per_file_assets: true owes the standalone per-file assets —
1264
+ # the exe (either spelling), the image, the windows DLL — beside the
1265
+ # bundle; each landed member earns its metadata through the landed
1266
+ # set. An unwitnessed shard expects nothing (bundle-only lines audit
1267
+ # green), and a missing exe reports its own gap only, never a
1268
+ # cascade (the one-error-per-gap rule).
1269
+ def split_copublish_expectation(shard, present, name, landed, missing)
1270
+ return [landed, missing] unless shard && shard["per_file_assets"] == true
1271
+
1272
+ exe = [name, "#{name}.exe"].find { |candidate| present.include?(candidate) }
1273
+ facets_landed, facets_missing = expected_facets(name).partition { |facet| present.include?(facet) }
1274
+ gaps = facets_missing
1275
+ gaps = [name] + gaps unless exe
1276
+ [landed + [exe, *facets_landed].compact, missing + gaps]
1277
+ end
1278
+
1279
+ # The package's served shard as parsed JSON — the declarations' only
1280
+ # authority (the additive keys gate the audit's expectations). nil
1281
+ # when the shard never landed (its own gap is the gate's business) or
1282
+ # cannot be read (loudly — the audit never guesses an expectation).
1283
+ def served_shard(release, present, name)
1230
1284
  shard = "#{name}#{SHARD_SUFFIX}"
1231
1285
  return nil unless present.include?(shard)
1232
1286
 
1233
- download_asset_json(find_asset(release, shard)).dig("image", "blksum", "filename")
1287
+ download_asset_json(find_asset(release, shard))
1234
1288
  rescue StandardError => e
1235
- puts "::warning::could not read #{shard} for the blksum expectation (#{e.class}: #{e.message}) — " \
1236
- "no blksum sidecar is expected of this package"
1289
+ puts "::warning::could not read #{shard} for the additive expectations (#{e.class}: #{e.message}) — " \
1290
+ "no additive assets are expected of this package"
1237
1291
  nil
1238
1292
  end
1239
1293
 
@@ -5,5 +5,5 @@
5
5
  # This file is a part of tamatebako
6
6
 
7
7
  module TebakoRelease
8
- VERSION = "0.3.0"
8
+ VERSION = "0.4.0"
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tebako-release
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc