1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
#!/usr/bin/env python3
"""Stage the release assets for one target triple.
The release workflow runs this on Linux, macOS and Windows runners, so the
packaging cannot rely on GNU coreutils or GNU tar. Everything here uses the
standard library, and the archive is written with fixed member metadata and a
timestamp-free gzip header so that rebuilding a tag yields byte-identical
archives.
Two kinds of asset are produced in the staging directory:
``<package>-<os>-<arch>[.exe]``
The bare binary, for people who download a single file.
``<package>-<version>-<target>.tar.gz``
A ``cargo-binstall`` archive holding the binary at the archive root under
its plain name, which is what the ``bin-dir`` template in
``[package.metadata.binstall]`` resolves to.
Each asset gets a ``.sha256`` sidecar in ``sha256sum`` format, naming the asset
alone so that ``sha256sum --check`` works in the directory holding the
download.
Example:
$ python3 scripts/package_release_artifacts.py \\
--binary target/x86_64-unknown-linux-gnu/release/mdtablefix \\
--artifact-dir artifacts/linux-x86_64 \\
--version 0.5.1 --target x86_64-unknown-linux-gnu \\
--os linux --arch x86_64
artifacts/linux-x86_64/mdtablefix-linux-x86_64
artifacts/linux-x86_64/mdtablefix-linux-x86_64.sha256
artifacts/linux-x86_64/mdtablefix-0.5.1-x86_64-unknown-linux-gnu.tar.gz
artifacts/linux-x86_64/mdtablefix-0.5.1-x86_64-unknown-linux-gnu.tar.gz.sha256
"""
=
# Release binaries are executable for everybody and writable only by the owner,
# so the archive member mode cannot inherit a runner's umask.
= 0o755
= 1024 * 1024
"""Return the executable suffix Rust appends for a target.
Parameters
----------
target : str
The Rust target triple, such as ``x86_64-pc-windows-msvc``.
Returns
-------
str
``".exe"`` for Windows targets and the empty string otherwise.
Examples
--------
>>> binary_extension("x86_64-unknown-linux-gnu")
''
>>> binary_extension("x86_64-pc-windows-msvc")
'.exe'
"""
return
"""Return the SHA-256 digest of a file.
Parameters
----------
path : Path
The file to read. It is read in chunks, so its size is not bounded by
available memory.
Returns
-------
str
The digest as lowercase hexadecimal.
Raises
------
OSError
If the file cannot be read.
"""
=
return
"""Write a ``sha256sum``-format sidecar beside an asset.
The sidecar names the asset without any directory component, so it stays
valid wherever the pair is downloaded to.
Parameters
----------
path : Path
The asset to checksum.
Returns
-------
Path
The sidecar that was written, named ``<path>.sha256``.
Raises
------
OSError
If the asset cannot be read or the sidecar cannot be written.
Examples
--------
>>> import tempfile
>>> with tempfile.TemporaryDirectory() as directory:
... asset = Path(directory) / "asset.bin"
... _ = asset.write_bytes(b"")
... sidecar = write_checksum(asset)
... sidecar.read_text(encoding="utf-8").split()[1]
'asset.bin'
"""
=
return
"""Write a deterministic single-member gzip tarball.
Determinism comes from three places: fixed member metadata, a trivially
ordered single-entry layout, and a gzip header carrying neither a timestamp
nor the original file name.
Parameters
----------
binary : Path
The executable to place in the archive. It is read into memory whole.
archive_path : Path
Where to write the archive. Any existing file is overwritten.
member_name : str
The name the binary takes inside the archive, which must match what the
crate's ``bin-dir`` template renders to.
source_date_epoch : int
The modification time recorded for the member, in seconds since the
Unix epoch.
Returns
-------
Path
``archive_path``, for convenience when collecting written paths.
Raises
------
OSError
If the binary cannot be read or the archive cannot be written.
Examples
--------
>>> import tempfile
>>> with tempfile.TemporaryDirectory() as directory:
... root = Path(directory)
... binary = root / "tool"
... _ = binary.write_bytes(b"binary")
... archive = write_binstall_archive(
... binary, root / "tool.tar.gz", "tool", 1_700_000_000
... )
... with tarfile.open(archive) as opened:
... opened.getnames()
['tool']
"""
=
=
=
=
=
=
= 0
= 0
=
=
# ``filename=""`` and ``mtime=0`` keep the gzip header free of the
# run's own metadata, which would otherwise differ on every rebuild.
return
"""Copy the built binary into the staging directory under its asset name.
Parameters
----------
binary : Path
The built executable.
artifact_dir : Path
The staging directory, which must already exist.
asset_name : str
The published name of the bare-binary asset.
Returns
-------
Path
The staged copy, whose mode is normalized to ``0o755`` so it does not
inherit the runner's umask.
Raises
------
OSError
If the copy or the mode change fails.
"""
= /
return
"""Stage every release asset for one target.
Parameters
----------
binary : Path
The built executable for ``target``.
artifact_dir : Path
The staging directory. It is created if absent.
package_name : str
The crate name, used in every asset name.
version : str
The release version, without a leading ``v``.
target : str
The Rust target triple.
operating_system : str
The operating-system label in the bare-binary asset name.
arch : str
The architecture label in the bare-binary asset name.
source_date_epoch : int
The modification time recorded for the archive member.
Returns
-------
list of Path
Every file written, in the order it was written: the bare binary, its
sidecar, the archive, and the archive's sidecar.
Raises
------
OSError
If any file cannot be read or written.
"""
=
=
=
return
"""Return the version a Cargo manifest declares.
Parameters
----------
manifest : Path
The ``Cargo.toml`` to read.
Returns
-------
str
The ``package.version`` value.
Raises
------
KeyError
If the manifest declares no ``package.version``.
OSError
If the manifest cannot be read.
"""
return
"""Return the archive timestamp taken from the environment.
Returns
-------
int
``SOURCE_DATE_EPOCH`` if it is set and non-empty, otherwise zero.
"""
=
return
"""Parse the command line for the packaging entry point.
Parameters
----------
argv : list of str, optional
Arguments to parse. Defaults to ``sys.argv[1:]``.
Returns
-------
argparse.Namespace
The parsed arguments, with ``version`` and ``source_date_epoch``
resolved from the manifest and the environment when omitted.
Raises
------
SystemExit
If the arguments are invalid, as raised by :mod:`argparse`.
"""
=
=
=
=
return
"""Stage the assets described by the command line and list them.
Parameters
----------
argv : list of str, optional
Arguments to parse. Defaults to ``sys.argv[1:]``.
Returns
-------
int
Zero on success. Every staged path is printed, one per line.
Raises
------
SystemExit
If the named binary does not exist.
"""
=
return 0