Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -575,13 +575,32 @@ jobs:
napi_args=(--platform --release --target "${{ matrix.target }}")
if [[ "${RUNNER_OS}" == Linux ]]; then
# The published linux-*-gnu package names carry no distro floor.
# Link the addons with napi-rs' locked glibc 2.17 toolchain rather
# than inheriting the Ubuntu 24.04 runner's glibc 2.39 ABI.
# aws-lc-sys treats a same-architecture target as a native build,
# so route its host C and C++ compilers through napi-rs' locked
# glibc 2.17 toolchain too. Otherwise it inherits the Ubuntu 24.04
# headers even though Rust links with the older toolchain.
export HOST_CC="${{ matrix.target }}-gcc"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required DCO sign-off

Commit e9b789715c1fe3578ad74b3a04e963b0f4f362f1 has no Signed-off-by trailer, so the repository's DCO workflow will reject the contribution; recreate the commit with git commit -s or otherwise add a valid sign-off before submission.

AGENTS.md reference: AGENTS.md:L274-L274

Useful? React with 👍 / 👎.

export HOST_CXX="${{ matrix.target }}-g++"
napi_args+=(--use-napi-cross)
fi
(cd "${client_dir}" && ./node_modules/.bin/napi build "${napi_args[@]}")
addon="${client_dir}/${client}-client.${{ matrix.napi_platform }}.node"
if [[ "${RUNNER_OS}" == Linux ]]; then
# The version table misses unversioned imports. Candidate
# packaging rejects strong ones except the Node-API imports that
# the host intentionally resolves; weak imports remain optional.
# Rebuild with the pinned compilers is recovery, until the build
# host itself enforces the ABI floor.
unversioned_imports="$(
readelf --wide --dyn-syms "${addon}" \
| awk '$7 == "UND" && $5 != "WEAK" && $8 !~ /@/ && $8 !~ /^(napi_|node_api_)/ { print $8 }' \
| sort -u
)"
if [[ -n "${unversioned_imports}" ]]; then
printf 'native addon has strong unversioned imports:\n%s\n' \
"${unversioned_imports}" >&2
exit 1
fi
highest_glibc="$(
readelf --version-info "${addon}" \
| grep -oE 'GLIBC_[0-9]+\.[0-9]+(\.[0-9]+)?' \
Expand Down
78 changes: 78 additions & 0 deletions release/scripts/test_release_workflow_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,84 @@ def test_builds_and_smokes_stable_native_client_packages(self) -> None:
node = step_run(document, "clients", "Build Node client packages")
self.assertIn("--use-napi-cross", node)
self.assertIn('--target "${{ matrix.target }}"', node)
self.assertIn(
'export HOST_CC="${{ matrix.target }}-gcc"\n'
' export HOST_CXX="${{ matrix.target }}-g++"\n'
" napi_args+=(--use-napi-cross)",
node,
)
napi_build = (
'(cd "${client_dir}" && ./node_modules/.bin/napi build '
'"${napi_args[@]}")'
)
self.assertLess(node.index("export HOST_CC="), node.index(napi_build))
self.assertLess(node.index("export HOST_CXX="), node.index(napi_build))
self.assertIn(
'unversioned_imports="$(\n'
' readelf --wide --dyn-syms "${addon}" \\\n'
" | awk '$7 == \"UND\" && $5 != \"WEAK\" && "
"$8 !~ /@/ && $8 !~ /^(napi_|node_api_)/ { print $8 }' \\\n"
" | sort -u\n"
" )\"",
node,
)
self.assertIn(
'if [[ -n "${unversioned_imports}" ]]; then\n'
" printf 'native addon has strong unversioned imports:"
"\\n%s\\n' \\\n"
' "${unversioned_imports}" >&2\n'
" exit 1",
node,
)
guard_start = node.index('unversioned_imports="$(')
self.assertLess(node.index(napi_build), guard_start)
self.assertLess(
guard_start,
node.index('(cd "${client_dir}" && npm pack'),
)
predicate_marker = "| awk '"
predicate_start = node.index(predicate_marker, guard_start) + len(
predicate_marker
)
predicate_end = node.index("' \\", predicate_start)
predicate = node[predicate_start:predicate_end]
dynsym_fixtures = {
"observed ISO C23 import": (
" 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND "
"__isoc23_sscanf\n",
["__isoc23_sscanf"],
),
"generic strong unversioned import": (
" 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc\n",
["malloc"],
),
"intentional Node-API imports": (
" 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND "
"napi_create_function\n"
" 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND "
"node_api_get_module_file_name\n",
[],
),
"versioned import": (
" 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND "
"malloc@GLIBC_2.2.5\n",
[],
),
"weak import": (
" 6: 0000000000000000 0 FUNC WEAK DEFAULT UND getrandom\n",
[],
),
}
for fixture_name, (dynsym, expected) in dynsym_fixtures.items():
with self.subTest(fixture=fixture_name):
guard = subprocess.run(
["awk", predicate],
input=dynsym,
capture_output=True,
text=True,
check=True,
)
self.assertEqual(guard.stdout.splitlines(), expected)
self.assertIn("readelf --version-info", node)
self.assertIn("GLIBC_2.17", node)
self.assertIn(
Expand Down