tebako-release 0.2.0 → 0.3.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 +4 -4
- data/README.md +10 -2
- data/lib/tebako_release/blksum.rb +98 -0
- data/lib/tebako_release/uploader.rb +152 -40
- data/lib/tebako_release/version.rb +1 -1
- data/lib/tebako_release.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da808fc0759260d4b29d60ffb1d51622cb07c3f8cf3125b4ea01025f1db4f927
|
|
4
|
+
data.tar.gz: 8d21389348ae2bb086aaf17f3901f386e62598be12bee027e653b76b361e8d1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d73130a2cbd8d1de0df2bb1cf1d801bfe260db9b5569cc04e736eca3324d945576cf8ee9c13c22eb31000872e9229cfb5931c553a491e72188bf58d793206cba
|
|
7
|
+
data.tar.gz: c111f4d684714a0b03fbc92891b9e2f6640f8cc05369b77870e73bc04f34847367692c89cbaf06c48138fbf8d6172fe675c4124add72be2da7ba8c2bcd674915
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# tebako-release
|
|
1
|
+
# tebako-release
|
|
2
2
|
|
|
3
3
|
The tebako factories' release machinery — the **single owner** of the per-leg
|
|
4
4
|
publish uploader and the no-fold OpenPGP release signer that every runtime
|
|
@@ -24,6 +24,14 @@ through a small adapter file and pin this gem — they never carry copies.
|
|
|
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
26
|
the default for pre-bundle lines.
|
|
27
|
+
- **`TebakoRelease::Blksum`** — the spec 39 §3 block-group digest sidecar
|
|
28
|
+
(`<image>.blksum.json`: one sha256 per 4 MiB group of env-image bytes,
|
|
29
|
+
the lazy mount's range-GET trust anchor). The uploader derives it
|
|
30
|
+
in-process from the staged image at entry-build time — a pure function
|
|
31
|
+
of the served bytes, the same class as the `.sha256` sidecars — pins it
|
|
32
|
+
in the shard (`image.blksum {filename, sha256}`), and uploads it as a
|
|
33
|
+
standalone asset in both publish eras. The render is byte-exact with
|
|
34
|
+
`tpkg::lazy::Blksum::render`, golden-pinned in `spec/blksum_spec.rb`.
|
|
27
35
|
- **`TebakoRelease::Platform`** — the release-side platform vocabulary
|
|
28
36
|
(the `(os, arch) → host_id` table; mirrors `tpkg::Platform`, drift fails
|
|
29
37
|
loudly in factory CI).
|
|
@@ -35,7 +43,7 @@ A factory adds the gem, pinned through its `contract.yml` SSOT:
|
|
|
35
43
|
```ruby
|
|
36
44
|
# Gemfile
|
|
37
45
|
gem "tebako-release",
|
|
38
|
-
git: "https://github.com/tamatebako/tebako-release
|
|
46
|
+
git: "https://github.com/tamatebako/tebako-release.git",
|
|
39
47
|
tag: YAML.load_file("contract.yml").fetch("release_tooling")
|
|
40
48
|
```
|
|
41
49
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 [Ribose Inc](https://www.ribose.com).
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
# This file is a part of tamatebako
|
|
6
|
+
|
|
7
|
+
require "digest"
|
|
8
|
+
require "json"
|
|
9
|
+
require "pathname"
|
|
10
|
+
|
|
11
|
+
module TebakoRelease
|
|
12
|
+
# The spec 39 §3 block-group digest sidecar (`<image>.blksum.json`): one
|
|
13
|
+
# sha256 per 4 MiB group of image bytes plus the whole-image sha256,
|
|
14
|
+
# authored by the PUBLISHER in-process (a resolver never derives one).
|
|
15
|
+
# For the factories the publisher invocation is `tebako-release upload`,
|
|
16
|
+
# so the derivation lives here — a pure function of the staged image
|
|
17
|
+
# bytes, the same derivable-metadata class as the `.sha256` sidecars.
|
|
18
|
+
#
|
|
19
|
+
# The render is byte-exact with tpkg::lazy::Blksum::render (the product's
|
|
20
|
+
# single owner of the grammar): compact machine JSON, keys in schema
|
|
21
|
+
# order, lowercase hex, no trailing newline. spec/blksum_spec.rb pins
|
|
22
|
+
# tpkg-minted golden vectors against this render — a drift fails the
|
|
23
|
+
# suite, never ships.
|
|
24
|
+
class Blksum
|
|
25
|
+
# The locked fetch unit (spec 39 §11.3): 4 MiB of image bytes; the
|
|
26
|
+
# final group is short. Tunable only by a measured product follow-up.
|
|
27
|
+
GROUP_SIZE = 4 * 1024 * 1024
|
|
28
|
+
|
|
29
|
+
# `schema_version` of the v1 document.
|
|
30
|
+
SCHEMA_VERSION = 1
|
|
31
|
+
|
|
32
|
+
# The served asset name is the image's name plus this suffix.
|
|
33
|
+
SIDECAR_SUFFIX = ".blksum.json"
|
|
34
|
+
|
|
35
|
+
# The sidecar asset name for an image name (or an image path).
|
|
36
|
+
def self.sidecar_name(image)
|
|
37
|
+
"#{Pathname.new(image).basename}#{SIDECAR_SUFFIX}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.blksum_file?(path)
|
|
41
|
+
path.basename.to_s.end_with?(SIDECAR_SUFFIX)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Derive the sidecar of a staged image: streams the file in GROUP_SIZE
|
|
45
|
+
# chunks (the runtime env images are 50–200 MB — never held whole),
|
|
46
|
+
# one sha256 per group plus the whole-image sha256. An absent or empty
|
|
47
|
+
# image is a named refusal (spec 39 §3: size_bytes >= 1).
|
|
48
|
+
def self.for_image(image_path)
|
|
49
|
+
path = Pathname.new(image_path)
|
|
50
|
+
assert_derivable!(path)
|
|
51
|
+
whole, groups = hash_stream(path)
|
|
52
|
+
new(size_bytes: path.size, sha256: whole.hexdigest, groups: groups)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.assert_derivable!(path)
|
|
56
|
+
raise Error, "the blksum sidecar cannot be derived: #{path} does not exist" unless path.file?
|
|
57
|
+
raise Error, "the blksum sidecar cannot be derived: #{path} is empty (size_bytes >= 1)" if path.empty?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.hash_stream(path)
|
|
61
|
+
whole = Digest::SHA256.new
|
|
62
|
+
groups = []
|
|
63
|
+
path.open("rb") do |io|
|
|
64
|
+
while (chunk = io.read(GROUP_SIZE))
|
|
65
|
+
whole.update(chunk)
|
|
66
|
+
groups << Digest::SHA256.hexdigest(chunk)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
[whole, groups]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
attr_reader :size_bytes, :sha256, :groups
|
|
73
|
+
|
|
74
|
+
def initialize(size_bytes:, sha256:, groups:)
|
|
75
|
+
@size_bytes = size_bytes
|
|
76
|
+
@sha256 = sha256
|
|
77
|
+
@groups = groups
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The machine JSON form (keys in schema order) — byte-exact with
|
|
81
|
+
# tpkg::lazy::Blksum::render, golden-pinned in spec/blksum_spec.rb.
|
|
82
|
+
def render
|
|
83
|
+
JSON.generate(
|
|
84
|
+
schema_version: SCHEMA_VERSION,
|
|
85
|
+
group_size: GROUP_SIZE,
|
|
86
|
+
size_bytes: size_bytes,
|
|
87
|
+
sha256: sha256,
|
|
88
|
+
groups: groups
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# The sidecar document's own sha256 (the manifest's
|
|
93
|
+
# `image.blksum.sha256` pin — spec 39 §3).
|
|
94
|
+
def digest
|
|
95
|
+
Digest::SHA256.hexdigest(render)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -442,7 +442,8 @@ module TebakoRelease
|
|
|
442
442
|
contract = contract_sidecar(package)
|
|
443
443
|
filename = package.basename.to_s
|
|
444
444
|
sha256 = Digest::SHA256.file(package).hexdigest
|
|
445
|
-
|
|
445
|
+
blksum = derive_blksum(image) if image
|
|
446
|
+
register_current_shas(package, image, dll, bundle, blksum)
|
|
446
447
|
{
|
|
447
448
|
tebako_version: @version,
|
|
448
449
|
contract_era: contract.fetch("contract_era"),
|
|
@@ -466,7 +467,7 @@ module TebakoRelease
|
|
|
466
467
|
# selector axis. Sourced from the factory's adapter (the same truth
|
|
467
468
|
# its boot smoke asserts), so manifest and smoke can never disagree.
|
|
468
469
|
entry[:capabilities] = @config.adapter.capabilities(version: runtime_version, platform_id: platform)
|
|
469
|
-
entry[:image] = image_entry(image) if image
|
|
470
|
+
entry[:image] = image_entry(image, blksum) if image
|
|
470
471
|
entry[:dll] = dll_entry(dll, runtime_version, platform) if dll
|
|
471
472
|
entry[:bundle] = bundle_entry(bundle) if bundle
|
|
472
473
|
declare_signatures(entry)
|
|
@@ -486,12 +487,36 @@ module TebakoRelease
|
|
|
486
487
|
end
|
|
487
488
|
|
|
488
489
|
# The idempotent upload skip reads these (same name + same sha = kept):
|
|
489
|
-
# the exe, its facets,
|
|
490
|
-
|
|
490
|
+
# the exe, its facets, the derived blksum sidecar (spec 39 §3), and the
|
|
491
|
+
# bundle (spec 36's bundle-era asset).
|
|
492
|
+
def register_current_shas(package, image, dll, bundle, blksum = nil) # rubocop:disable Metrics/AbcSize
|
|
491
493
|
current_shas[package.basename.to_s] = Digest::SHA256.file(package).hexdigest
|
|
492
494
|
current_shas[image_name_for(package)] = Digest::SHA256.file(image).hexdigest if image
|
|
493
495
|
current_shas[dll_name_for(package)] = Digest::SHA256.file(dll).hexdigest if dll
|
|
494
496
|
current_shas[bundle.basename.to_s] = Digest::SHA256.file(bundle).hexdigest if bundle
|
|
497
|
+
current_shas[Blksum.sidecar_name(image)] = blksum.digest if blksum
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# The spec 39 §3 derivation: the publisher authors the block-group
|
|
501
|
+
# digest sidecar in-process from the staged image bytes (a pure
|
|
502
|
+
# function of them — the same derivable-metadata class as the .sha256
|
|
503
|
+
# sidecars), stages it BESIDE the image so the leg's attestation glob
|
|
504
|
+
# (runtime-packages/*) covers it, and the entry's `image.blksum` block
|
|
505
|
+
# pins the document's own sha256. support_file? keeps a staged sidecar
|
|
506
|
+
# from classifying as a package on a re-run validation.
|
|
507
|
+
def derive_blksum(image)
|
|
508
|
+
blksum = Blksum.for_image(image)
|
|
509
|
+
path = Pathname.new("#{image}#{Blksum::SIDECAR_SUFFIX}")
|
|
510
|
+
path.write(blksum.render)
|
|
511
|
+
staged_blksums[path.basename.to_s] = path
|
|
512
|
+
blksum
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# The derived sidecar paths this invocation staged, keyed by asset
|
|
516
|
+
# name — the upload leg's source (entries are built before uploads in
|
|
517
|
+
# every publish shape, so a declared sidecar is always staged).
|
|
518
|
+
def staged_blksums
|
|
519
|
+
@staged_blksums ||= {}
|
|
495
520
|
end
|
|
496
521
|
|
|
497
522
|
# Spec 13 §2a / spec 09 §5: on signing-enabled lines every artifact the
|
|
@@ -573,12 +598,19 @@ module TebakoRelease
|
|
|
573
598
|
end
|
|
574
599
|
|
|
575
600
|
# The additive image metadata: name, sha256, size (consumers ignoring the
|
|
576
|
-
# `image` key keep working; item 30's compat rule)
|
|
577
|
-
|
|
601
|
+
# `image` key keep working; item 30's compat rule) — plus the additive
|
|
602
|
+
# `blksum` pin (spec 39 §3: {filename, sha256} of the derived block-group
|
|
603
|
+
# digest sidecar; consumers that predate the key ignore it, and the
|
|
604
|
+
# loader's lazy arm falls back to eager loudly when it is absent).
|
|
605
|
+
def image_entry(image, blksum)
|
|
578
606
|
{
|
|
579
607
|
filename: image.basename.to_s,
|
|
580
608
|
sha256: Digest::SHA256.file(image).hexdigest,
|
|
581
|
-
size_bytes: image.size
|
|
609
|
+
size_bytes: image.size,
|
|
610
|
+
blksum: {
|
|
611
|
+
filename: Blksum.sidecar_name(image),
|
|
612
|
+
sha256: blksum.digest
|
|
613
|
+
}
|
|
582
614
|
}
|
|
583
615
|
end
|
|
584
616
|
|
|
@@ -805,6 +837,7 @@ module TebakoRelease
|
|
|
805
837
|
bundles = build_bundles(packages)
|
|
806
838
|
entries = build_manifest_entries(packages, bundles: bundles)
|
|
807
839
|
bundles.each_value { |bundle| upload_package(release, bundle) }
|
|
840
|
+
upload_blksum_sidecars(release, entries)
|
|
808
841
|
entries.each { |entry| ensure_package_metadata(release, entry) }
|
|
809
842
|
print_settled_summary
|
|
810
843
|
end
|
|
@@ -867,10 +900,32 @@ module TebakoRelease
|
|
|
867
900
|
# the release notes are written once at creation.
|
|
868
901
|
def publish_release(release, packages, entries)
|
|
869
902
|
packages.each { |package| upload_package(release, package) }
|
|
903
|
+
upload_blksum_sidecars(release, entries)
|
|
870
904
|
entries.each { |entry| ensure_package_metadata(release, entry) }
|
|
871
905
|
print_settled_summary
|
|
872
906
|
end
|
|
873
907
|
|
|
908
|
+
# The spec 39 §3 sidecars ride along in BOTH publish eras as STANDALONE
|
|
909
|
+
# assets — never bundle members: the lazy resolver fetches the sidecar
|
|
910
|
+
# with a plain GET against its own release-asset URL
|
|
911
|
+
# (`{dir_url}/{image.blksum.filename}`), in the bundle era too. Derived
|
|
912
|
+
# at entry-build time and staged beside the images; uploaded before the
|
|
913
|
+
# metadata so the shard that pins them never precedes them.
|
|
914
|
+
def upload_blksum_sidecars(release, entries)
|
|
915
|
+
entries.each do |entry|
|
|
916
|
+
name = entry.dig(:image, :blksum, :filename)
|
|
917
|
+
next unless name
|
|
918
|
+
|
|
919
|
+
path = staged_blksums[name]
|
|
920
|
+
unless path
|
|
921
|
+
raise Error, "the blksum sidecar #{name} was declared but never staged — " \
|
|
922
|
+
"the derivation runs at entry-build time; this is a machinery bug"
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
upload_package(release, path)
|
|
926
|
+
end
|
|
927
|
+
end
|
|
928
|
+
|
|
874
929
|
# The package's metadata assets, byte-truthful for the SERVED bytes: a
|
|
875
930
|
# settled package (the byte-immutable keep, or a wedged recovery
|
|
876
931
|
# replace) publishes the PREVIOUS entry's shard and sidecars — the
|
|
@@ -888,13 +943,18 @@ module TebakoRelease
|
|
|
888
943
|
# era: the exe, its .tfs image and its .dll facet. Bundle era (spec 36
|
|
889
944
|
# §3): the bundle is the ONE served payload asset — the entry's
|
|
890
945
|
# exe/image/dll fields are member pins, not served names, so they earn
|
|
891
|
-
# no sidecar.
|
|
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
|
|
948
|
+
# every served name.
|
|
892
949
|
def metadata_assets(entry)
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
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
|
|
957
|
+
served.merge(blksum_metadata(entry))
|
|
898
958
|
end
|
|
899
959
|
|
|
900
960
|
def facet_metadata(entry, facet)
|
|
@@ -902,6 +962,14 @@ module TebakoRelease
|
|
|
902
962
|
block ? { block[:filename] => block[:sha256] } : {}
|
|
903
963
|
end
|
|
904
964
|
|
|
965
|
+
# The blksum sidecar's name -> sha256 pair when the entry declares it
|
|
966
|
+
# (spec 39 §3's `image.blksum` pin); a pre-blksum previous entry
|
|
967
|
+
# declares nothing and earns nothing (byte-truthful metadata).
|
|
968
|
+
def blksum_metadata(entry)
|
|
969
|
+
block = entry.dig(:image, :blksum)
|
|
970
|
+
block ? { block[:filename] => block[:sha256] } : {}
|
|
971
|
+
end
|
|
972
|
+
|
|
905
973
|
def shard_name_for(entry)
|
|
906
974
|
"#{package_stem(entry[:filename])}#{SHARD_SUFFIX}"
|
|
907
975
|
end
|
|
@@ -910,7 +978,8 @@ module TebakoRelease
|
|
|
910
978
|
# with the previous entry's voice; a never-published settle has nothing
|
|
911
979
|
# truthful to keep and never reaches here (upload_package re-raised).
|
|
912
980
|
def effective_entry(entry)
|
|
913
|
-
names = [entry[:filename], entry.dig(:image, :filename), entry.dig(:dll, :filename)
|
|
981
|
+
names = [entry[:filename], entry.dig(:image, :filename), entry.dig(:dll, :filename),
|
|
982
|
+
entry.dig(:image, :blksum, :filename)].compact
|
|
914
983
|
return entry unless names.any? { |name| settled?(name) }
|
|
915
984
|
|
|
916
985
|
previous_entry_for(entry[:filename]) || entry
|
|
@@ -1095,7 +1164,9 @@ module TebakoRelease
|
|
|
1095
1164
|
|
|
1096
1165
|
def missing_assets(release, packages, require_signatures: false)
|
|
1097
1166
|
present = all_assets(release).map(&:name)
|
|
1098
|
-
packages.flat_map
|
|
1167
|
+
packages.flat_map do |name|
|
|
1168
|
+
missing_package_assets(release, present, name, require_signatures: require_signatures)
|
|
1169
|
+
end
|
|
1099
1170
|
end
|
|
1100
1171
|
|
|
1101
1172
|
# One expected package's gaps. Windows executables may or may not carry
|
|
@@ -1103,11 +1174,16 @@ module TebakoRelease
|
|
|
1103
1174
|
# expectation matches both. Metadata expectations ride on what actually
|
|
1104
1175
|
# landed — a package whose exe never landed reports its own name only,
|
|
1105
1176
|
# never a cascade of secondary sidecar/shard gaps (one error per gap).
|
|
1106
|
-
|
|
1107
|
-
|
|
1177
|
+
# The blksum sidecar (spec 39 §3) is expected ONLY when the package's
|
|
1178
|
+
# shard declares the `image.blksum` pin — a pre-blksum release's audit
|
|
1179
|
+
# stays green (the additive-key compat rule), and a missing shard
|
|
1180
|
+
# reports its own gap only.
|
|
1181
|
+
def missing_package_assets(release, present, name, require_signatures: false)
|
|
1182
|
+
return missing_bundle_assets(release, present, name, require_signatures: require_signatures) if bundle_publish?
|
|
1108
1183
|
|
|
1109
1184
|
exe = [name, "#{name}.exe"].find { |candidate| present.include?(candidate) }
|
|
1110
1185
|
landed, missing = expected_facets(name).partition { |facet| present.include?(facet) }
|
|
1186
|
+
landed, missing = split_blksum_expectation(release, present, name, landed, missing)
|
|
1111
1187
|
missing.unshift(name) unless exe
|
|
1112
1188
|
return missing if exe.nil?
|
|
1113
1189
|
|
|
@@ -1116,12 +1192,49 @@ module TebakoRelease
|
|
|
1116
1192
|
|
|
1117
1193
|
# Spec 36's completeness shape: the bundle is the leg's ONE payload
|
|
1118
1194
|
# asset; a landed bundle owes its sidecar and the package's shard
|
|
1119
|
-
# (plus an .asc of each on signing-enabled audits).
|
|
1120
|
-
|
|
1195
|
+
# (plus an .asc of each on signing-enabled audits). The declared
|
|
1196
|
+
# 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.
|
|
1198
|
+
def missing_bundle_assets(release, present, name, require_signatures: false)
|
|
1121
1199
|
bundle = "#{name}#{Bundler::BUNDLE_SUFFIX}"
|
|
1122
1200
|
return [bundle] unless present.include?(bundle)
|
|
1123
1201
|
|
|
1124
|
-
|
|
1202
|
+
landed, missing = split_blksum_expectation(release, present, name, [bundle], [])
|
|
1203
|
+
missing + missing_metadata(present, name, landed, require_signatures: require_signatures)
|
|
1204
|
+
end
|
|
1205
|
+
|
|
1206
|
+
# The shard-declared blksum expectation (spec 39 §3): the shard is the
|
|
1207
|
+
# declaration's only authority (the additive `image.blksum` key), so
|
|
1208
|
+
# the audit reads it. A shard that declares no pin expects nothing (a
|
|
1209
|
+
# pre-blksum release audits green); a shard that never landed already
|
|
1210
|
+
# reports its own gap, so it adds no secondary blksum gaps; an
|
|
1211
|
+
# unreadable shard warns and expects nothing (never a silent failure —
|
|
1212
|
+
# the shard's own gap is the gate's business). A DECLARED sidecar that
|
|
1213
|
+
# never landed reports its own name only — its sidecar/.asc cascade
|
|
1214
|
+
# 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)
|
|
1217
|
+
return [landed, missing] unless declared
|
|
1218
|
+
|
|
1219
|
+
if present.include?(declared)
|
|
1220
|
+
[landed + [declared], missing]
|
|
1221
|
+
else
|
|
1222
|
+
[landed, missing + [declared]]
|
|
1223
|
+
end
|
|
1224
|
+
end
|
|
1225
|
+
|
|
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)
|
|
1230
|
+
shard = "#{name}#{SHARD_SUFFIX}"
|
|
1231
|
+
return nil unless present.include?(shard)
|
|
1232
|
+
|
|
1233
|
+
download_asset_json(find_asset(release, shard)).dig("image", "blksum", "filename")
|
|
1234
|
+
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"
|
|
1237
|
+
nil
|
|
1125
1238
|
end
|
|
1126
1239
|
|
|
1127
1240
|
# The non-executable artifacts a package is expected to carry: the
|
|
@@ -1448,12 +1561,13 @@ module TebakoRelease
|
|
|
1448
1561
|
end
|
|
1449
1562
|
|
|
1450
1563
|
# The package stem an asset name belongs to: the exe (with or without
|
|
1451
|
-
# .exe), its .tfs image, its .dll facet
|
|
1452
|
-
#
|
|
1453
|
-
#
|
|
1454
|
-
# would be a
|
|
1564
|
+
# .exe), its .tfs image, its .dll facet, the derived blksum sidecar
|
|
1565
|
+
# (spec 39 §3) and its .tar.gz bundle (spec 36) share one stem —
|
|
1566
|
+
# settling is package-scoped so a wedged exe also stands down its
|
|
1567
|
+
# facets (a fresh facet over previous package bytes would be a
|
|
1568
|
+
# mixed-version package). Longest suffix first.
|
|
1455
1569
|
def package_stem(filename)
|
|
1456
|
-
filename.sub(/\.tar\.gz\z/, "").sub(/\.(exe|tfs|dll)\z/, "")
|
|
1570
|
+
filename.sub(/\.tar\.gz\z/, "").sub(/\.tfs\.blksum\.json\z/, "").sub(/\.(exe|tfs|dll)\z/, "")
|
|
1457
1571
|
end
|
|
1458
1572
|
|
|
1459
1573
|
def settled?(filename)
|
|
@@ -1586,18 +1700,13 @@ module TebakoRelease
|
|
|
1586
1700
|
|
|
1587
1701
|
# The sha256 the previous manifest records for this asset: the entry's
|
|
1588
1702
|
# own sha for the executable, the image/dll facet block's sha for a
|
|
1589
|
-
# facet (facets key under their package's entry), the
|
|
1590
|
-
#
|
|
1703
|
+
# facet (facets key under their package's entry), the image's blksum
|
|
1704
|
+
# pin for the derived sidecar (spec 39 §3), the bundle block's sha for
|
|
1705
|
+
# the bundle (spec 36 — the bundle-era served asset).
|
|
1591
1706
|
def previous_sha_for(entry, filename)
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
entry.dig(:dll, :sha256)
|
|
1596
|
-
elsif entry.dig(:bundle, :filename) == filename
|
|
1597
|
-
entry.dig(:bundle, :sha256)
|
|
1598
|
-
else
|
|
1599
|
-
entry[:sha256]
|
|
1600
|
-
end
|
|
1707
|
+
blocks = [entry.dig(:image, :blksum), entry[:image], entry[:dll], entry[:bundle]]
|
|
1708
|
+
block = blocks.find { |candidate| candidate && candidate[:filename] == filename }
|
|
1709
|
+
block ? block[:sha256] : entry[:sha256]
|
|
1601
1710
|
end
|
|
1602
1711
|
|
|
1603
1712
|
def validate_environment
|
|
@@ -1624,13 +1733,16 @@ module TebakoRelease
|
|
|
1624
1733
|
end
|
|
1625
1734
|
|
|
1626
1735
|
# `.abi` sidecars (the runtime's platform string), `.contract.yaml`
|
|
1627
|
-
# sidecars (the era-2 release card provenance)
|
|
1736
|
+
# sidecars (the era-2 release card provenance), `.sha256` markers
|
|
1628
1737
|
# (the image's store-layout trust anchor, spec 22 §6 — the boot
|
|
1629
|
-
# smoke's image-key input)
|
|
1630
|
-
#
|
|
1738
|
+
# smoke's image-key input) and `.blksum.json` sidecars (the spec 39 §3
|
|
1739
|
+
# derivation staged beside its image at entry-build time — still in
|
|
1740
|
+
# the directory when a re-run validates) are manifest inputs / build
|
|
1741
|
+
# outputs read in place — never packages of their own.
|
|
1631
1742
|
def support_file?(path)
|
|
1632
1743
|
path.extname == ".abi" || path.extname == ".sha256" ||
|
|
1633
|
-
path.basename.to_s.end_with?(CONTRACT_SIDECAR_SUFFIX)
|
|
1744
|
+
path.basename.to_s.end_with?(CONTRACT_SIDECAR_SUFFIX) ||
|
|
1745
|
+
Blksum.blksum_file?(path)
|
|
1634
1746
|
end
|
|
1635
1747
|
end
|
|
1636
1748
|
end
|
data/lib/tebako_release.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative "tebako_release/adapter"
|
|
|
10
10
|
require_relative "tebako_release/config"
|
|
11
11
|
require_relative "tebako_release/platform"
|
|
12
12
|
require_relative "tebako_release/bundler"
|
|
13
|
+
require_relative "tebako_release/blksum"
|
|
13
14
|
require_relative "tebako_release/uploader"
|
|
14
15
|
require_relative "tebako_release/signer"
|
|
15
16
|
|
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- exe/tebako-release
|
|
40
40
|
- lib/tebako_release.rb
|
|
41
41
|
- lib/tebako_release/adapter.rb
|
|
42
|
+
- lib/tebako_release/blksum.rb
|
|
42
43
|
- lib/tebako_release/bundler.rb
|
|
43
44
|
- lib/tebako_release/config.rb
|
|
44
45
|
- lib/tebako_release/error.rb
|
|
@@ -46,7 +47,7 @@ files:
|
|
|
46
47
|
- lib/tebako_release/signer.rb
|
|
47
48
|
- lib/tebako_release/uploader.rb
|
|
48
49
|
- lib/tebako_release/version.rb
|
|
49
|
-
homepage: https://github.com/tamatebako/tebako-release
|
|
50
|
+
homepage: https://github.com/tamatebako/tebako-release
|
|
50
51
|
licenses:
|
|
51
52
|
- BSD-2-Clause
|
|
52
53
|
metadata:
|