blob: 29e92015dd647c394b88a18cf6bbb3d45b704e32 [file] [log] [blame]
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +00001# Copyright 2023 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Script to generate (+ upload) DLC artifacts."""
6
7import logging
8import os
9import shutil
10from typing import List
11
12from chromite.lib import commandline
13from chromite.lib import cros_build_lib
14from chromite.lib import dlc_lib
15from chromite.lib import osutils
16
17
18# Predefined salts.
19_SHORT_SALT = "1337D00D"
20
21# Tarball extension with correct compression.
22_TAR_COMP_EXT = ".tar.zst"
23_META_OUT_FILE = dlc_lib.DLC_TMP_META_DIR + _TAR_COMP_EXT
24
25# Filenames.
26_METADATA_FILE = "metadata"
27
28
29def ParseArguments(argv: List[str]) -> commandline.ArgumentNamespace:
30 """Returns a namespace for the CLI arguments."""
31 parser = commandline.ArgumentParser(description=__doc__)
32 parser.add_argument(
33 "--src-dir",
34 type="dir_exists",
35 required=True,
36 help="The directory to package as a DLC",
37 )
38 # Support license addition here. For now, have users explicitly pass in a
39 # stub license path.
40 parser.add_argument(
41 "--license",
42 type="file_exists",
43 required=True,
44 help="The path to license, this should be the same license as the one"
45 " used within the package",
46 )
47 parser.add_argument(
48 "--output-dir",
49 type="path",
50 help="The optional output directory to put artifacts into",
51 )
52 parser.add_argument(
53 "--output-metadata-dir",
54 type="path",
55 help="The optional output directory to put metadata into",
56 )
57
Jae Hoon Kim3690a222023-09-20 18:15:27 +000058 parser.add_bool_argument(
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000059 "--upload",
Jae Hoon Kim3690a222023-09-20 18:15:27 +000060 default=False,
61 enabled_desc="Upload the DLC artifacts to google buckets",
62 disabled_desc="Do not upload the DLC artifacts to google buckets",
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000063 )
64 parser.add_argument(
65 "--uri-path",
66 type="gs_path",
67 help="The override for DLC image URI, check dlc_lib for default",
68 )
Jae Hoon Kim3690a222023-09-20 18:15:27 +000069 parser.add_bool_argument(
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000070 "--upload-dry-run",
Jae Hoon Kim3690a222023-09-20 18:15:27 +000071 default=False,
72 enabled_desc="Dry run without actual upload",
73 disabled_desc="Ignored",
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000074 )
75
Jae Hoon Kim3690a222023-09-20 18:15:27 +000076 parser.add_bool_argument(
77 "--reproducible-image",
78 default=False,
79 enabled_desc="To generate reproducible DLC images",
80 disabled_desc="To generate randomized DLC images",
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000081 )
82
Jae Hoon Kim553a5d02023-09-20 04:38:40 +000083 # Enable powerwash safety for this DLC.
84 parser.add_bool_argument(
85 "--powerwash-safety",
86 default=False,
87 enabled_desc="Enable powerwash safety feature for this DLC",
88 disabled_desc="Disable powerwash safety feature for this DLC",
89 )
90
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +000091 # DLC required fields.
92 parser.add_argument(
93 "--id",
94 type=str,
95 required=True,
96 help="The DLC ID",
97 )
98 parser.add_argument(
99 "--preallocated-blocks",
100 type=int,
101 required=True,
102 help="The preallocated number of blocks in 4KiB chunks",
103 )
104
105 # DLC optional fields.
106 parser.add_argument(
107 "--name",
108 type=str,
109 default="",
110 help="The name of the DLC in human friendly format",
111 )
112 parser.add_argument(
113 "--description",
114 type=str,
115 default="",
116 help="The description of the DLC in human friendly format",
117 )
118 parser.add_argument(
119 "--version",
120 type=str,
121 required=True,
122 help="The version of this DLC build",
123 )
124
125 opts = parser.parse_args(argv)
126
127 dlc_lib.ValidateDlcIdentifier(opts.id)
128
129 opts.Freeze()
130
131 return opts
132
133
134def GenerateDlcParams(
135 opts: commandline.ArgumentNamespace,
136) -> dlc_lib.EbuildParams:
137 """Generates and verifies DLC parameters based on options
138
139 Args:
140 opts: The command line arguments.
141
142 Returns:
143 The DLC ebuild parameters.
144 """
145 params = dlc_lib.EbuildParams(
146 dlc_id=opts.id,
147 dlc_package="package",
148 fs_type=dlc_lib.SQUASHFS_TYPE,
149 pre_allocated_blocks=opts.preallocated_blocks,
150 version=opts.version,
151 name=opts.name,
152 description=opts.description,
153 # Add preloading support.
154 preload=False,
155 used_by="",
156 mount_file_required=False,
157 fullnamerev="",
158 scaled=True,
159 loadpin_verity_digest=False,
Jae Hoon Kim553a5d02023-09-20 04:38:40 +0000160 powerwash_safe=opts.powerwash_safety,
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000161 )
162 params.VerifyDlcParameters()
163 return params
164
165
166def UploadDlcArtifacts(
167 dlcartifacts: dlc_lib.DlcArtifacts, dry_run: bool
168) -> None:
169 """Uploads the DLC artifacts based on `DlcArtifacts`
170
171 Args:
172 dlcartifacts: The DLC artifacts to upload.
173 dry_run: Dry run without actually uploading if true.
174 """
175 logging.info("Uploading DLC artifacts")
176 logging.debug(
177 "Uploading DLC image %s to %s",
178 dlcartifacts.image,
179 dlcartifacts.uri_path,
180 )
181 logging.debug(
182 "Uploading DLC meta %s to %s", dlcartifacts.meta, dlcartifacts.uri_path
183 )
184 dlcartifacts.Upload(dry_run=dry_run)
185
186
187def GenerateDlcArtifacts(opts: commandline.ArgumentNamespace) -> None:
188 """Generates the DLC artifacts
189
190 Args:
191 opts: The command line arguments.
192 """
193 params = GenerateDlcParams(opts)
Jae Hoon Kim95cbdf52023-08-10 20:22:58 +0000194 uri_path = opts.uri_path or params.GetUriPath()
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000195
196 with osutils.TempDir(prefix="dlcartifacts", sudo_rm=True) as tmpdir:
197 output_dir = opts.output_dir or tmpdir
198 os.makedirs(output_dir, exist_ok=True)
199
200 logging.info("Generating DLC artifacts")
201 artifacts = dlc_lib.DlcGenerator(
202 src_dir=opts.src_dir,
203 sysroot="",
204 board=dlc_lib.MAGIC_BOARD,
205 ebuild_params=params,
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000206 reproducible=opts.reproducible_image,
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000207 license_file=opts.license,
208 ).ExternalGenerateDLC(
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000209 tmpdir, _SHORT_SALT if opts.reproducible_image else None
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000210 )
211 logging.debug("Generated DLC artifacts: %s", artifacts.StringJSON())
212
213 # Handle the meta.
214 meta_out = os.path.join(output_dir, _META_OUT_FILE)
215
216 logging.info("Emitting the metadata into %s", meta_out)
217 cros_build_lib.CreateTarball(
218 tarball_path=meta_out,
219 cwd=artifacts.meta,
220 compression=cros_build_lib.CompressionType.ZSTD,
221 extra_env={"ZSTD_CLEVEL": "9"},
222 )
223
224 # Handle the image.
225 image_out = os.path.join(output_dir, dlc_lib.DLC_IMAGE)
226 logging.info("Emitting the DLC image into %s", image_out)
227 shutil.move(artifacts.image, image_out)
228
229 # Handle the upload.
230 ret_artifacts = dlc_lib.DlcArtifacts(
231 uri_path=uri_path,
232 image=image_out,
233 meta=meta_out,
234 )
235 ret_artifacts_json = ret_artifacts.StringJSON()
236 logging.debug("The final DLC artifacts: %s", ret_artifacts_json)
237
238 if opts.output_metadata_dir:
239 osutils.WriteFile(
240 os.path.join(opts.output_metadata_dir, _METADATA_FILE),
241 ret_artifacts_json,
242 makedirs=True,
243 )
244
245 if opts.upload or opts.upload_dry_run:
246 UploadDlcArtifacts(ret_artifacts, opts.upload_dry_run)
247 else:
248 logging.debug("Skipping DLC artifacts upload")
249
250
251def main(argv):
252 GenerateDlcArtifacts(ParseArguments(argv))