blob: 18174459372b8be9ec36c7ad24ea6b63e2f58829 [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
83 # DLC required fields.
84 parser.add_argument(
85 "--id",
86 type=str,
87 required=True,
88 help="The DLC ID",
89 )
90 parser.add_argument(
91 "--preallocated-blocks",
92 type=int,
93 required=True,
94 help="The preallocated number of blocks in 4KiB chunks",
95 )
96
97 # DLC optional fields.
98 parser.add_argument(
99 "--name",
100 type=str,
101 default="",
102 help="The name of the DLC in human friendly format",
103 )
104 parser.add_argument(
105 "--description",
106 type=str,
107 default="",
108 help="The description of the DLC in human friendly format",
109 )
110 parser.add_argument(
111 "--version",
112 type=str,
113 required=True,
114 help="The version of this DLC build",
115 )
116
117 opts = parser.parse_args(argv)
118
119 dlc_lib.ValidateDlcIdentifier(opts.id)
120
121 opts.Freeze()
122
123 return opts
124
125
126def GenerateDlcParams(
127 opts: commandline.ArgumentNamespace,
128) -> dlc_lib.EbuildParams:
129 """Generates and verifies DLC parameters based on options
130
131 Args:
132 opts: The command line arguments.
133
134 Returns:
135 The DLC ebuild parameters.
136 """
137 params = dlc_lib.EbuildParams(
138 dlc_id=opts.id,
139 dlc_package="package",
140 fs_type=dlc_lib.SQUASHFS_TYPE,
141 pre_allocated_blocks=opts.preallocated_blocks,
142 version=opts.version,
143 name=opts.name,
144 description=opts.description,
145 # Add preloading support.
146 preload=False,
147 used_by="",
148 mount_file_required=False,
149 fullnamerev="",
150 scaled=True,
151 loadpin_verity_digest=False,
152 )
153 params.VerifyDlcParameters()
154 return params
155
156
157def UploadDlcArtifacts(
158 dlcartifacts: dlc_lib.DlcArtifacts, dry_run: bool
159) -> None:
160 """Uploads the DLC artifacts based on `DlcArtifacts`
161
162 Args:
163 dlcartifacts: The DLC artifacts to upload.
164 dry_run: Dry run without actually uploading if true.
165 """
166 logging.info("Uploading DLC artifacts")
167 logging.debug(
168 "Uploading DLC image %s to %s",
169 dlcartifacts.image,
170 dlcartifacts.uri_path,
171 )
172 logging.debug(
173 "Uploading DLC meta %s to %s", dlcartifacts.meta, dlcartifacts.uri_path
174 )
175 dlcartifacts.Upload(dry_run=dry_run)
176
177
178def GenerateDlcArtifacts(opts: commandline.ArgumentNamespace) -> None:
179 """Generates the DLC artifacts
180
181 Args:
182 opts: The command line arguments.
183 """
184 params = GenerateDlcParams(opts)
Jae Hoon Kim95cbdf52023-08-10 20:22:58 +0000185 uri_path = opts.uri_path or params.GetUriPath()
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000186
187 with osutils.TempDir(prefix="dlcartifacts", sudo_rm=True) as tmpdir:
188 output_dir = opts.output_dir or tmpdir
189 os.makedirs(output_dir, exist_ok=True)
190
191 logging.info("Generating DLC artifacts")
192 artifacts = dlc_lib.DlcGenerator(
193 src_dir=opts.src_dir,
194 sysroot="",
195 board=dlc_lib.MAGIC_BOARD,
196 ebuild_params=params,
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000197 reproducible=opts.reproducible_image,
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000198 license_file=opts.license,
199 ).ExternalGenerateDLC(
Jae Hoon Kim3690a222023-09-20 18:15:27 +0000200 tmpdir, _SHORT_SALT if opts.reproducible_image else None
Jae Hoon Kimbb0ddf02023-07-21 04:23:12 +0000201 )
202 logging.debug("Generated DLC artifacts: %s", artifacts.StringJSON())
203
204 # Handle the meta.
205 meta_out = os.path.join(output_dir, _META_OUT_FILE)
206
207 logging.info("Emitting the metadata into %s", meta_out)
208 cros_build_lib.CreateTarball(
209 tarball_path=meta_out,
210 cwd=artifacts.meta,
211 compression=cros_build_lib.CompressionType.ZSTD,
212 extra_env={"ZSTD_CLEVEL": "9"},
213 )
214
215 # Handle the image.
216 image_out = os.path.join(output_dir, dlc_lib.DLC_IMAGE)
217 logging.info("Emitting the DLC image into %s", image_out)
218 shutil.move(artifacts.image, image_out)
219
220 # Handle the upload.
221 ret_artifacts = dlc_lib.DlcArtifacts(
222 uri_path=uri_path,
223 image=image_out,
224 meta=meta_out,
225 )
226 ret_artifacts_json = ret_artifacts.StringJSON()
227 logging.debug("The final DLC artifacts: %s", ret_artifacts_json)
228
229 if opts.output_metadata_dir:
230 osutils.WriteFile(
231 os.path.join(opts.output_metadata_dir, _METADATA_FILE),
232 ret_artifacts_json,
233 makedirs=True,
234 )
235
236 if opts.upload or opts.upload_dry_run:
237 UploadDlcArtifacts(ret_artifacts, opts.upload_dry_run)
238 else:
239 logging.debug("Skipping DLC artifacts upload")
240
241
242def main(argv):
243 GenerateDlcArtifacts(ParseArguments(argv))