1- # typed: true # rubocop:todo Sorbet/StrictSigil
1+ # typed: strict
22# frozen_string_literal: true
33
44require "utils/curl"
@@ -16,8 +16,8 @@ class GitHubPackages
1616 include Utils ::Output ::Mixin
1717
1818 URL_DOMAIN = "ghcr.io"
19- URL_PREFIX = "https://#{ URL_DOMAIN } /v2/" . freeze
20- DOCKER_PREFIX = "docker://#{ URL_DOMAIN } /" . freeze
19+ URL_PREFIX = T . let ( "https://#{ URL_DOMAIN } /v2/" . freeze , String )
20+ DOCKER_PREFIX = T . let ( "docker://#{ URL_DOMAIN } /" . freeze , String )
2121 public_constant :URL_DOMAIN
2222 private_constant :URL_PREFIX
2323 private_constant :DOCKER_PREFIX
@@ -30,16 +30,22 @@ class GitHubPackages
3030 INVALID_OCI_TAG_CHARS_REGEX = /[^a-zA-Z0-9._-]/
3131
3232 # Translate Homebrew tab.arch to OCI platform.architecture
33- TAB_ARCH_TO_PLATFORM_ARCHITECTURE = {
34- "arm64" => "arm64" ,
35- "x86_64" => "amd64" ,
36- } . freeze
33+ TAB_ARCH_TO_PLATFORM_ARCHITECTURE = T . let (
34+ {
35+ "arm64" => "arm64" ,
36+ "x86_64" => "amd64" ,
37+ } . freeze ,
38+ T ::Hash [ String , String ] ,
39+ )
3740
3841 # Translate Homebrew built_on.os to OCI platform.os
39- BUILT_ON_OS_TO_PLATFORM_OS = {
40- "Linux" => "linux" ,
41- "Macintosh" => "darwin" ,
42- } . freeze
42+ BUILT_ON_OS_TO_PLATFORM_OS = T . let (
43+ {
44+ "Linux" => "linux" ,
45+ "Macintosh" => "darwin" ,
46+ } . freeze ,
47+ T ::Hash [ String , String ] ,
48+ )
4349
4450 sig {
4551 params (
@@ -79,6 +85,7 @@ def upload_bottles(bottles_hash, keep_old:, dry_run:, warn_on_error:)
7985 # rubocop:enable Style/CombinableLoops
8086 end
8187
88+ sig { params ( version : String , rebuild : String , bottle_tag : T . nilable ( String ) ) . returns ( String ) }
8289 def self . version_rebuild ( version , rebuild , bottle_tag = nil )
8390 bottle_tag = ( ".#{ bottle_tag } " if bottle_tag . present? )
8491
@@ -93,18 +100,21 @@ def self.version_rebuild(version, rebuild, bottle_tag = nil)
93100 "#{ version } #{ bottle_tag } #{ rebuild } "
94101 end
95102
103+ sig { params ( repo : String ) . returns ( String ) }
96104 def self . repo_without_prefix ( repo )
97105 # Remove redundant repository prefix for a shorter name.
98106 repo . delete_prefix ( "homebrew-" )
99107 end
100108
109+ sig { params ( org : String , repo : String , prefix : String ) . returns ( String ) }
101110 def self . root_url ( org , repo , prefix = URL_PREFIX )
102111 # `docker`/`skopeo` insist on lowercase organisation (“repository name”).
103112 org = org . downcase
104113
105114 "#{ prefix } #{ org } /#{ repo_without_prefix ( repo ) } "
106115 end
107116
117+ sig { params ( url : T . nilable ( String ) ) . returns ( T . nilable ( String ) ) }
108118 def self . root_url_if_match ( url )
109119 return if url . blank?
110120
@@ -114,6 +124,7 @@ def self.root_url_if_match(url)
114124 root_url ( org , repo )
115125 end
116126
127+ sig { params ( formula_name : String ) . returns ( String ) }
117128 def self . image_formula_name ( formula_name )
118129 # Invalid docker name characters:
119130 # - `/` makes sense because we already use it to separate repository/formula.
@@ -122,6 +133,7 @@ def self.image_formula_name(formula_name)
122133 . tr ( "+" , "x" )
123134 end
124135
136+ sig { params ( version_rebuild : String ) . returns ( String ) }
125137 def self . image_version_rebuild ( version_rebuild )
126138 unless version_rebuild . match? ( VALID_OCI_TAG_REGEX )
127139 raise ArgumentError , "GitHub Packages versions must match #{ VALID_OCI_TAG_REGEX . source } !"
@@ -141,6 +153,7 @@ def self.image_version_rebuild(version_rebuild)
141153 private_constant :IMAGE_CONFIG_SCHEMA_URI , :IMAGE_INDEX_SCHEMA_URI , :IMAGE_LAYOUT_SCHEMA_URI ,
142154 :IMAGE_MANIFEST_SCHEMA_URI , :GITHUB_PACKAGE_TYPE
143155
156+ sig { void }
144157 def load_schemas!
145158 schema_uri ( "content-descriptor" ,
146159 "https://opencontainers.org/schema/image/content-descriptor.json" )
@@ -168,6 +181,7 @@ def load_schemas!
168181 schema_uri ( "image-manifest-schema" , IMAGE_MANIFEST_SCHEMA_URI )
169182 end
170183
184+ sig { params ( basename : String , uris : T . any ( String , T ::Array [ String ] ) ) . void }
171185 def schema_uri ( basename , uris )
172186 # The current `main` version has an invalid JSON schema.
173187 # Going forward, this should probably be pinned to tags.
@@ -176,18 +190,20 @@ def schema_uri(basename, uris)
176190 out = Utils ::Curl . curl_output ( url ) . stdout
177191 json = JSON . parse ( out )
178192
179- @schema_json ||= { }
193+ @schema_json ||= T . let ( { } , T . nilable ( T :: Hash [ String , T :: Hash [ String , T . untyped ] ] ) )
180194 Array ( uris ) . each do |uri |
181195 @schema_json [ uri ] = json
182196 end
183197 end
184198
199+ sig { params ( uri : URI ::Generic ) . returns ( T . nilable ( T ::Hash [ String , T . untyped ] ) ) }
185200 def schema_resolver ( uri )
186- @schema_json [ uri . to_s . gsub ( /#.*/ , "" ) ]
201+ @schema_json &. fetch ( uri . to_s . gsub ( /#.*/ , "" ) )
187202 end
188203
204+ sig { params ( schema_uri : String , json : T ::Hash [ String , T . untyped ] ) . void }
189205 def validate_schema! ( schema_uri , json )
190- schema = JSONSchemer . schema ( @schema_json [ schema_uri ] , ref_resolver : method ( :schema_resolver ) )
206+ schema = JSONSchemer . schema ( @schema_json &. fetch ( schema_uri ) , ref_resolver : method ( :schema_resolver ) )
191207 json = json . deep_stringify_keys
192208 return if schema . valid? ( json )
193209
@@ -200,6 +216,7 @@ def validate_schema!(schema_uri, json)
200216 exit 1
201217 end
202218
219+ sig { params ( user : String , token : String , skopeo : Pathname , image_uri : String , root : Pathname , dry_run : T ::Boolean ) . void }
203220 def download ( user , token , skopeo , image_uri , root , dry_run :)
204221 puts
205222 args = [ "copy" , "--all" , image_uri . to_s , "oci:#{ root } " ]
@@ -211,6 +228,14 @@ def download(user, token, skopeo, image_uri, root, dry_run:)
211228 end
212229 end
213230
231+ sig {
232+ params (
233+ user : String , token : String , skopeo : Pathname , _formula_full_name : String ,
234+ bottle_hash : T ::Hash [ String , T . untyped ] , keep_old : T ::Boolean , dry_run : T ::Boolean , warn_on_error : T ::Boolean
235+ ) . returns (
236+ T . nilable ( [ String , String , String , String , String , String , String , String , T ::Boolean ] ) ,
237+ )
238+ }
214239 def preupload_check ( user , token , skopeo , _formula_full_name , bottle_hash , keep_old :, dry_run :, warn_on_error :)
215240 formula_name = bottle_hash [ "formula" ] [ "name" ]
216241
@@ -260,6 +285,12 @@ def preupload_check(user, token, skopeo, _formula_full_name, bottle_hash, keep_o
260285 [ formula_name , org , repo , version , rebuild , version_rebuild , image_name , image_uri , keep_old ]
261286 end
262287
288+ sig {
289+ params (
290+ user : String , token : String , skopeo : Pathname , formula_full_name : String ,
291+ bottle_hash : T ::Hash [ String , T . untyped ] , keep_old : T ::Boolean , dry_run : T ::Boolean , warn_on_error : T ::Boolean
292+ ) . void
293+ }
263294 def upload_bottle ( user , token , skopeo , formula_full_name , bottle_hash , keep_old :, dry_run :, warn_on_error :)
264295 # We run the preupload check twice to prevent TOCTOU bugs.
265296 result = preupload_check ( user , token , skopeo , formula_full_name , bottle_hash ,
@@ -479,19 +510,22 @@ def upload_bottle(user, token, skopeo, formula_full_name, bottle_hash, keep_old:
479510 end
480511 end
481512
513+ sig { params ( root : Pathname ) . returns ( [ String , Integer ] ) }
482514 def write_image_layout ( root )
483515 image_layout = { imageLayoutVersion : "1.0.0" }
484516 validate_schema! ( IMAGE_LAYOUT_SCHEMA_URI , image_layout )
485517 write_hash ( root , image_layout , "oci-layout" )
486518 end
487519
520+ sig { params ( local_file : String , blobs : Pathname ) . returns ( String ) }
488521 def write_tar_gz ( local_file , blobs )
489522 tar_gz_sha256 = Digest ::SHA256 . file ( local_file )
490523 . hexdigest
491524 FileUtils . ln local_file , blobs /tar_gz_sha256 , force : true
492525 tar_gz_sha256
493526 end
494527
528+ sig { params ( platform_hash : T ::Hash [ String , T . untyped ] , tar_sha256 : String , blobs : Pathname ) . returns ( [ String , Integer ] ) }
495529 def write_image_config ( platform_hash , tar_sha256 , blobs )
496530 image_config = platform_hash . merge ( {
497531 rootfs : {
@@ -503,6 +537,7 @@ def write_image_config(platform_hash, tar_sha256, blobs)
503537 write_hash ( blobs , image_config )
504538 end
505539
540+ sig { params ( manifests : T ::Array [ T ::Hash [ String , T . untyped ] ] , blobs : Pathname , annotations : T ::Hash [ String , String ] ) . returns ( [ String , Integer ] ) }
506541 def write_image_index ( manifests , blobs , annotations )
507542 image_index = {
508543 schemaVersion : 2 ,
@@ -513,6 +548,7 @@ def write_image_index(manifests, blobs, annotations)
513548 write_hash ( blobs , image_index )
514549 end
515550
551+ sig { params ( index_json_sha256 : String , index_json_size : Integer , root : Pathname , annotations : T ::Hash [ String , String ] ) . void }
516552 def write_index_json ( index_json_sha256 , index_json_size , root , annotations )
517553 index_json = {
518554 schemaVersion : 2 ,
@@ -527,6 +563,7 @@ def write_index_json(index_json_sha256, index_json_size, root, annotations)
527563 write_hash ( root , index_json , "index.json" )
528564 end
529565
566+ sig { params ( directory : Pathname , hash : T ::Hash [ String , T . untyped ] , filename : T . nilable ( String ) ) . returns ( [ String , Integer ] ) }
530567 def write_hash ( directory , hash , filename = nil )
531568 json = JSON . pretty_generate ( hash )
532569 sha256 = Digest ::SHA256 . hexdigest ( json )
0 commit comments