1818
1919import yaml
2020
21- from ..features import CName
2221from .bucket import Bucket
2322
2423
@@ -120,26 +119,90 @@ def upload_from_directory(
120119
121120 release_file = artifacts_dir .joinpath (f"{ base_name } .release" )
122121
123- cname_object = CName .new_from_release_file (release_file )
122+ if not release_file .exists ():
123+ raise RuntimeError (f"Release file not found: { release_file } " )
124124
125- if cname_object .version_and_commit_id is None :
126- raise RuntimeError (
127- "Version information could not be determined from release file"
128- )
125+ # RegEx for S3 supported characters
126+ re_object = re .compile ("[^a-zA-Z0-9\\ s+\\ -=.\\ _:/@]" )
127+
128+ def _sanitize (value : Optional [str ]) -> Optional [str ]:
129+ if value is None :
130+ return None
131+ return re_object .sub ("+" , str (value ))
132+
133+ def _read_kv_file (path : Path ) -> ConfigParser :
134+ cfg = ConfigParser (allow_unnamed_section = True , interpolation = None )
135+ with path .open ("r" , encoding = "utf-8" ) as fp :
136+ cfg .read_file (fp )
137+ return cfg
138+
139+ def _get_required (cfg : ConfigParser , key : str , what : Path ) -> str :
140+ if not cfg .has_option (UNNAMED_SECTION , key ):
141+ raise RuntimeError (f"Missing required field { key } in { what } " )
142+ v = cfg .get (UNNAMED_SECTION , key ).strip ()
143+ if len (v ) >= 2 and ((v [0 ] == '"' and v [- 1 ] == '"' ) or (v [0 ] == "'" and v [- 1 ] == "'" )):
144+ v = v [1 :- 1 ]
145+ if v == "" :
146+ raise RuntimeError (f"Empty required field { key } in { what } " )
147+ return v
148+
149+ def _get_optional (cfg : ConfigParser , key : str ) -> Optional [str ]:
150+ if not cfg .has_option (UNNAMED_SECTION , key ):
151+ return None
152+ v = cfg .get (UNNAMED_SECTION , key ).strip ()
153+ if len (v ) >= 2 and ((v [0 ] == '"' and v [- 1 ] == '"' ) or (v [0 ] == "'" and v [- 1 ] == "'" )):
154+ v = v [1 :- 1 ]
155+ if v == "" :
156+ return None
157+ return v
158+
159+ release_config = _read_kv_file (release_file )
160+
161+ # Backwards compatibility: platform fallback
162+ cname = _get_optional (release_config , "GARDENLINUX_CNAME" )
163+ platform = _get_optional (release_config , "GARDENLINUX_PLATFORM" )
164+ if platform is None :
165+ if cname is None :
166+ cname = base_name
167+ platform = cname .split ("-" , 1 )[0 ]
168+
169+ # Hard guard: frankenstein images must never be published
170+ if platform == "frankenstein" :
171+ raise RuntimeError ("frankenstein images must not be published" )
172+
173+ version = _get_required (release_config , "GARDENLINUX_VERSION" , release_file )
174+ commit_id_long = _get_required (
175+ release_config , "GARDENLINUX_COMMIT_ID_LONG" , release_file
176+ )
177+
178+ platform_variant = _get_optional (
179+ release_config , "GARDENLINUX_PLATFORM_VARIANT"
180+ )
129181
130- arch = cname_object .arch
131- feature_set_list = cname_object .feature_set_list
132- release_timestamp = stat (release_file ).st_ctime
182+ features = _get_optional (release_config , "GARDENLINUX_FEATURES" )
183+ feature_set_list : list [str ] = []
184+ if features is not None :
185+ feature_set_list = [x .strip () for x in features .split ("," )]
186+ feature_set_list = [x for x in feature_set_list if x ]
187+
188+ gardenlinux_epoch = None
189+ try :
190+ gardenlinux_epoch = int (version .split ("." , 1 )[0 ])
191+ except Exception :
192+ gardenlinux_epoch = None
193+
194+ release_timestamp = stat (release_file ).st_mtime
133195 requirements_file = artifacts_dir .joinpath (f"{ base_name } .requirements" )
134- require_uefi = None
135- secureboot = None
196+ require_uefi = False
197+ secureboot = False
198+ tpm2 = False
199+ arch = None
136200
137201 if requirements_file .exists ():
138- requirements_config = ConfigParser (allow_unnamed_section = True )
139- requirements_config .read (requirements_file )
202+ requirements_config = _read_kv_file (requirements_file )
140203
141204 if requirements_config .has_option (UNNAMED_SECTION , "arch" ):
142- arch = requirements_config .get (UNNAMED_SECTION , "arch" )
205+ arch = requirements_config .get (UNNAMED_SECTION , "arch" ). strip ()
143206
144207 if requirements_config .has_option (UNNAMED_SECTION , "uefi" ):
145208 require_uefi = requirements_config .getboolean (UNNAMED_SECTION , "uefi" )
@@ -149,56 +212,55 @@ def upload_from_directory(
149212 UNNAMED_SECTION , "secureboot"
150213 )
151214
152- if arch is None :
153- raise RuntimeError (
154- "Architecture could not be determined from release or requirements file"
155- )
156-
157- if require_uefi is None :
158- require_uefi = "_usi" in feature_set_list
159-
160- if secureboot is None :
161- secureboot = "_trustedboot" in feature_set_list
162-
163- # RegEx for S3 supported characters
164- re_object = re .compile ("[^a-zA-Z0-9\\ s+\\ -=.\\ _:/@]" )
165-
166- arch = re_object .sub ("+" , arch )
167- commit_id_or_hash = cname_object .commit_hash
215+ if requirements_config .has_option (UNNAMED_SECTION , "tpm2" ):
216+ tpm2 = requirements_config .getboolean (UNNAMED_SECTION , "tpm2" )
217+
218+ # Backwards compatibility: arch fallback
219+ if not arch :
220+ if cname is None :
221+ cname = base_name
222+ cname_parts = cname .split ("-" )
223+ if len (cname_parts ) < 2 :
224+ raise RuntimeError (
225+ "Architecture could not be determined from requirements file or cname"
226+ )
227+ arch = cname_parts [- 2 ]
168228
169- if commit_id_or_hash is None :
170- commit_id_or_hash = cname_object .commit_id
229+ arch = _sanitize (arch )
171230
172231 metadata = {
173- "platform" : cname_object .feature_set_platform ,
232+ "platform" : platform ,
233+ "platform_variant" : platform_variant ,
174234 "architecture" : arch ,
175- "base_image" : None ,
176- "build_committish" : commit_id_or_hash ,
235+ "version" : version ,
236+ "gardenlinux_epoch" : gardenlinux_epoch ,
237+ "build_committish" : commit_id_long ,
177238 "build_timestamp" : datetime .fromtimestamp (release_timestamp ),
178- "logs" : None ,
179239 "modifiers" : feature_set_list ,
180240 "require_uefi" : require_uefi ,
181241 "secureboot" : secureboot ,
182- "published_image_metadata" : None ,
242+ "tpm2" : tpm2 ,
243+ "paths" : [],
183244 "s3_bucket" : self ._bucket .name ,
184245 "s3_key" : f"meta/singles/{ base_name } " ,
185- "test_result" : None ,
186- "version" : cname_object .version ,
187- "paths" : [],
188246 }
189247
190- if cname_object . version_epoch is not None :
191- metadata ["gardenlinux_epoch" ] = cname_object . version_epoch
248+ if metadata [ "platform_variant" ] is None :
249+ del metadata ["platform_variant" ]
192250
193- platform_variant = cname_object .platform_variant
194-
195- if platform_variant is not None :
196- metadata ["platform_variant" ] = platform_variant
251+ if metadata ["gardenlinux_epoch" ] is None :
252+ del metadata ["gardenlinux_epoch" ]
197253
198254 base_name_length = len (base_name )
199255
200256 for artifact in artifacts_dir .iterdir ():
201- if not artifact .match (f"{ base_name } *" ):
257+ if artifact .is_dir ():
258+ continue
259+
260+ if artifact .name == f"{ base_name } .release" :
261+ continue
262+
263+ if artifact .name == f"{ base_name } .requirements" :
202264 continue
203265
204266 s3_key = f"objects/{ base_name } /{ artifact .name } "
@@ -207,20 +269,25 @@ def upload_from_directory(
207269 md5sum = file_digest (fp , "md5" ).hexdigest ()
208270 sha256sum = file_digest (fp , "sha256" ).hexdigest ()
209271
272+ if artifact .name .startswith (base_name ):
273+ suffix = artifact .name [base_name_length :]
274+ else :
275+ suffix = artifact .suffix
276+
210277 artifact_metadata = {
211278 "name" : artifact .name ,
212279 "s3_bucket_name" : self ._bucket .name ,
213280 "s3_key" : s3_key ,
214- "suffix" : re_object . sub ( "+" , artifact . name [ base_name_length :] ),
281+ "suffix" : _sanitize ( suffix ),
215282 "md5sum" : md5sum ,
216283 "sha256sum" : sha256sum ,
217284 }
218285
219286 s3_tags = {
220287 "architecture" : arch ,
221- "platform" : re_object . sub ( "+" , cname_object . platform ),
222- "version" : re_object . sub ( "+" , cname_object . version ), # type: ignore[arg-type]
223- "committish" : commit_id_or_hash ,
288+ "platform" : _sanitize ( platform ),
289+ "version" : _sanitize ( version ),
290+ "committish" : commit_id_long ,
224291 "md5sum" : md5sum ,
225292 "sha256sum" : sha256sum ,
226293 }
@@ -246,7 +313,7 @@ def upload_from_directory(
246313 )
247314
248315 with TemporaryFile (mode = "wb+" ) as fp :
249- fp .write (yaml .dump (metadata ).encode ("utf-8" ))
316+ fp .write (yaml .dump (metadata , sort_keys = False ).encode ("utf-8" ))
250317 fp .seek (0 )
251318
252319 self ._bucket .upload_fileobj (
0 commit comments