blob: d6d9f653366eac4fba5f997be1e7149fa817d938 [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 Kima5a73ac2023-09-29 04:29:33 +0000161 use_logical_volume=True,
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000162 )
163 params.VerifyDlcParameters()
164 return params
165
166
167def UploadDlcArtifacts(
168 dlcartifacts: dlc_lib.DlcArtifacts, dry_run: bool
169) -> None:
170 """Uploads the DLC artifacts based on `DlcArtifacts`
171
172 Args:
173 dlcartifacts: The DLC artifacts to upload.
174 dry_run: Dry run without actually uploading if true.
175 """
176 logging.info("Uploading DLC artifacts")
177 logging.debug(
178 "Uploading DLC image %s to %s",
179 dlcartifacts.image,
180 dlcartifacts.uri_path,
181 )
182 logging.debug(
183 "Uploading DLC meta %s to %s", dlcartifacts.meta, dlcartifacts.uri_path
184 )
185 dlcartifacts.Upload(dry_run=dry_run)
186
187
188def GenerateDlcArtifacts(opts: commandline.ArgumentNamespace) -> None:
189 """Generates the DLC artifacts
190
191 Args:
192 opts: The command line arguments.
193 """
194 params = GenerateDlcParams(opts)
Jae Hoon Kim95cbdf52023-08-10 20:22:58 +0000195 uri_path = opts.uri_path or params.GetUriPath()
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000196
197 with osutils.TempDir(prefix="dlcartifacts", sudo_rm=True) as tmpdir:
198 output_dir = opts.output_dir or tmpdir
199 os.makedirs(output_dir, exist_ok=True)
200
201 logging.info("Generating DLC artifacts")
202 artifacts = dlc_lib.DlcGenerator(
203 src_dir=opts.src_dir,
204 sysroot="",
205 board=dlc_lib.MAGIC_BOARD,
206 ebuild_params=params,
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000207 reproducible=opts.reproducible_image,
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000208 license_file=opts.license,
209 ).ExternalGenerateDLC(
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000210 tmpdir, _SHORT_SALT if opts.reproducible_image else None
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000211 )
212 logging.debug("Generated DLC artifacts: %s", artifacts.StringJSON())
213
214 # Handle the meta.
215 meta_out = os.path.join(output_dir, _META_OUT_FILE)
216
217 logging.info("Emitting the metadata into %s", meta_out)
218 cros_build_lib.CreateTarball(
219 tarball_path=meta_out,
220 cwd=artifacts.meta,
221 compression=cros_build_lib.CompressionType.ZSTD,
222 extra_env={"ZSTD_CLEVEL": "9"},
223 )
224
225 # Handle the image.
226 image_out = os.path.join(output_dir, dlc_lib.DLC_IMAGE)
227 logging.info("Emitting the DLC image into %s", image_out)
228 shutil.move(artifacts.image, image_out)
229
230 # Handle the upload.
231 ret_artifacts = dlc_lib.DlcArtifacts(
232 uri_path=uri_path,
233 image=image_out,
234 meta=meta_out,
235 )
236 ret_artifacts_json = ret_artifacts.StringJSON()
237 logging.debug("The final DLC artifacts: %s", ret_artifacts_json)
238
239 if opts.output_metadata_dir:
240 osutils.WriteFile(
241 os.path.join(opts.output_metadata_dir, _METADATA_FILE),
242 ret_artifacts_json,
243 makedirs=True,
244 )
245
246 if opts.upload or opts.upload_dry_run:
247 UploadDlcArtifacts(ret_artifacts, opts.upload_dry_run)
248 else:
249 logging.debug("Skipping DLC artifacts upload")
250
251
252def main(argv):
253 GenerateDlcArtifacts(ParseArguments(argv))