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