v30.3-roots.1 · Commit 66: [PATCH 66/68] fix(release): validate notices and reuse proven promotion CI
ci/release/runner-notices.py
Before: ci/release/runner-notices.py · After: ci/release/runner-notices.py
File metadata
index 4876f59c3fb520b360d662b0a1c5b7a3c2082e3f..c4d73139b6d2d8a2bcd8eabf7df70e5a6bf02c78 100755
Download original patch · Release notes
Diff rows 1–56 of 56. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
ci/release/runner-notices.py
CHANGED
|
@@ -47,14 +47,17 @@ def source_notices(archive, expected_hash, destination):
|
|
| 47 |
if not member.isfile():
|
| 48 |
continue
|
| 49 |
path = PurePosixPath(member.name)
|
| 50 |
-
if path.is_absolute() or '..' in path.parts or '\\' in member.name or ':' in member.name:
|
| 51 |
-
raise ValueError('Unsafe source archive member')
|
| 52 |
-
name = path.name.lower()
|
| 53 |
selected = bool(re.fullmatch(r'(?:copying|licen[cs]e|copyright|notice)(?:[._-].*)?', name))
|
| 54 |
-
selected |= 'LICENSES' in path.parts or name in {'ftl.txt', 'gplv2.txt'}
|
| 55 |
-
header = name == 'sqlite3.c' or path.as_posix().endswith('/keysyms/keysyms.c')
|
| 56 |
if not selected and not header:
|
| 57 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
data = package.extractfile(member).read()
|
| 59 |
if header:
|
| 60 |
data = data.split(b'*/', 1)[0] + b'*/\n'
|
|
@@ -80,9 +83,16 @@ def acquire(url, checksum, destination):
|
|
| 80 |
|
| 81 |
|
| 82 |
def depends_notices(depends, host, destination):
|
| 83 |
-
def variables(names):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
output = run('make', '--no-print-directory', '-C', str(depends), f'HOST={host}',
|
| 85 |
-
*[f'print-{name}' for name in names])
|
| 86 |
return dict(line.split('=', 1) for line in output.splitlines() if '=' in line)
|
| 87 |
selected = variables(['packages', 'host_prefix', 'SOURCES_PATH'])
|
| 88 |
if not (Path(selected['host_prefix']) / 'toolchain.cmake').is_file():
|
|
@@ -90,7 +100,7 @@ def depends_notices(depends, host, destination):
|
|
| 90 |
packages = selected['packages'].split()
|
| 91 |
names = [f'{p}_{suffix}' for p in packages for suffix in
|
| 92 |
('version', 'source', 'sha256_hash', 'download_path', 'download_file', 'extra_sources')]
|
| 93 |
-
values = variables(names)
|
| 94 |
result = {}
|
| 95 |
for package in packages:
|
| 96 |
source = Path(values[f'{package}_source'])
|
|
@@ -100,7 +110,7 @@ def depends_notices(depends, host, destination):
|
|
| 100 |
# Qt translations/tools are separately fetched and bundled by the recipe.
|
| 101 |
if package == 'qt':
|
| 102 |
extras = variables(['qt_qttranslations_file_name', 'qt_qttranslations_sha256_hash',
|
| 103 |
-
'qt_qttools_file_name', 'qt_qttools_sha256_hash'])
|
| 104 |
for part in ('qttranslations', 'qttools'):
|
| 105 |
filename = extras[f'qt_{part}_file_name']
|
| 106 |
extra_hash = extras[f'qt_{part}_sha256_hash']
|
|
|
|
| 47 |
if not member.isfile():
|
| 48 |
continue
|
| 49 |
path = PurePosixPath(member.name)
|
| 50 |
+
scan_path = PurePosixPath(member.name.replace('\\', '/'))
|
| 51 |
+
name = scan_path.name.lower()
|
|
|
|
| 52 |
selected = bool(re.fullmatch(r'(?:copying|licen[cs]e|copyright|notice)(?:[._-].*)?', name))
|
| 53 |
+
selected |= 'LICENSES' in scan_path.parts or name in {'ftl.txt', 'gplv2.txt'}
|
| 54 |
+
header = name == 'sqlite3.c' or scan_path.as_posix().endswith('/keysyms/keysyms.c')
|
| 55 |
if not selected and not header:
|
| 56 |
continue
|
| 57 |
+
# Source tarballs can contain irrelevant manpage names such as
|
| 58 |
+
# SystemTap's function::HZ.3stap. Never write those members.
|
| 59 |
+
if path.is_absolute() or '..' in path.parts or '\\' in member.name or ':' in member.name:
|
| 60 |
+
raise ValueError(f'Unsafe source archive notice: {archive.name}: {member.name}')
|
| 61 |
data = package.extractfile(member).read()
|
| 62 |
if header:
|
| 63 |
data = data.split(b'*/', 1)[0] + b'*/\n'
|
|
|
|
| 83 |
|
| 84 |
|
| 85 |
def depends_notices(depends, host, destination):
|
| 86 |
+
def variables(names, packages=()):
|
| 87 |
+
bindings = []
|
| 88 |
+
for name in names:
|
| 89 |
+
package = next((p for p in sorted(packages, key=len, reverse=True)
|
| 90 |
+
if name.startswith(p + '_')), None)
|
| 91 |
+
if package:
|
| 92 |
+
# Recipes defer $(package)-based expansion until a target runs.
|
| 93 |
+
bindings.append(f'--eval=print-{name}: package={package}')
|
| 94 |
output = run('make', '--no-print-directory', '-C', str(depends), f'HOST={host}',
|
| 95 |
+
*bindings, *[f'print-{name}' for name in names])
|
| 96 |
return dict(line.split('=', 1) for line in output.splitlines() if '=' in line)
|
| 97 |
selected = variables(['packages', 'host_prefix', 'SOURCES_PATH'])
|
| 98 |
if not (Path(selected['host_prefix']) / 'toolchain.cmake').is_file():
|
|
|
|
| 100 |
packages = selected['packages'].split()
|
| 101 |
names = [f'{p}_{suffix}' for p in packages for suffix in
|
| 102 |
('version', 'source', 'sha256_hash', 'download_path', 'download_file', 'extra_sources')]
|
| 103 |
+
values = variables(names, packages)
|
| 104 |
result = {}
|
| 105 |
for package in packages:
|
| 106 |
source = Path(values[f'{package}_source'])
|
|
|
|
| 110 |
# Qt translations/tools are separately fetched and bundled by the recipe.
|
| 111 |
if package == 'qt':
|
| 112 |
extras = variables(['qt_qttranslations_file_name', 'qt_qttranslations_sha256_hash',
|
| 113 |
+
'qt_qttools_file_name', 'qt_qttools_sha256_hash'], ['qt'])
|
| 114 |
for part in ('qttranslations', 'qttools'):
|
| 115 |
filename = extras[f'qt_{part}_file_name']
|
| 116 |
extra_hash = extras[f'qt_{part}_sha256_hash']
|