Skip to content
Bitcoin Roots

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. The two line-number columns refer to before and after.

ci/release/runner-notices.py CHANGED
@@ -47,14 +47,17 @@ def source_notices(archive, expected_hash, destination):
47
47
  if not member.isfile():
48
48
  continue
49
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()
50
+ scan_path = PurePosixPath(member.name.replace('\\', '/'))
51
+ name = scan_path.name.lower()
53
52
  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')
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')
56
55
  if not selected and not header:
57
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}')
58
61
  data = package.extractfile(member).read()
59
62
  if header:
60
63
  data = data.split(b'*/', 1)[0] + b'*/\n'
@@ -80,9 +83,16 @@ def acquire(url, checksum, destination):
80
83
 
81
84
 
82
85
  def depends_notices(depends, host, destination):
83
- def variables(names):
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}')
84
94
  output = run('make', '--no-print-directory', '-C', str(depends), f'HOST={host}',
85
- *[f'print-{name}' for name in names])
95
+ *bindings, *[f'print-{name}' for name in names])
86
96
  return dict(line.split('=', 1) for line in output.splitlines() if '=' in line)
87
97
  selected = variables(['packages', 'host_prefix', 'SOURCES_PATH'])
88
98
  if not (Path(selected['host_prefix']) / 'toolchain.cmake').is_file():
@@ -90,7 +100,7 @@ def depends_notices(depends, host, destination):
90
100
  packages = selected['packages'].split()
91
101
  names = [f'{p}_{suffix}' for p in packages for suffix in
92
102
  ('version', 'source', 'sha256_hash', 'download_path', 'download_file', 'extra_sources')]
93
- values = variables(names)
103
+ values = variables(names, packages)
94
104
  result = {}
95
105
  for package in packages:
96
106
  source = Path(values[f'{package}_source'])
@@ -100,7 +110,7 @@ def depends_notices(depends, host, destination):
100
110
  # Qt translations/tools are separately fetched and bundled by the recipe.
101
111
  if package == 'qt':
102
112
  extras = variables(['qt_qttranslations_file_name', 'qt_qttranslations_sha256_hash',
103
- 'qt_qttools_file_name', 'qt_qttools_sha256_hash'])
113
+ 'qt_qttools_file_name', 'qt_qttools_sha256_hash'], ['qt'])
104
114
  for part in ('qttranslations', 'qttools'):
105
115
  filename = extras[f'qt_{part}_file_name']
106
116
  extra_hash = extras[f'qt_{part}_sha256_hash']