From ddf40121800d5e0dd7225c2b0e9f49b6aea0f33b Mon Sep 17 00:00:00 2001 From: Gitjay11 Date: Thu, 12 Mar 2026 10:40:54 +0530 Subject: [PATCH] npm: set npm_config_build_from_source=true to handle prebuildify packages (#1015) Signed-off-by: Gitjay11 Assisted-by: Gemini --- .../package_managers/javascript/npm/main.py | 11 +++- .../package_managers/javascript/yarn/main.py | 1 + .../javascript/yarn_classic/main.py | 1 + .../javascript/npm/test_main.py | 50 ++++++++++++++++++- .../javascript/yarn/test_main.py | 1 + .../javascript/yarn_classic/test_main.py | 1 + 6 files changed, 62 insertions(+), 3 deletions(-) diff --git a/hermeto/core/package_managers/javascript/npm/main.py b/hermeto/core/package_managers/javascript/npm/main.py index a4005de5c..fb1c6a3ee 100644 --- a/hermeto/core/package_managers/javascript/npm/main.py +++ b/hermeto/core/package_managers/javascript/npm/main.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-3.0-only from hermeto.core.models.input import Request -from hermeto.core.models.output import ProjectFile, RequestOutput +from hermeto.core.models.output import EnvironmentVariable, ProjectFile, RequestOutput from hermeto.core.models.property_semantics import PropertySet from hermeto.core.models.sbom import Component, create_backend_annotation from hermeto.core.package_managers.javascript.npm import project as npm_project @@ -57,13 +57,20 @@ def fetch_npm_source(request: Request) -> RequestOutput: for projectfile in info["projectfiles"]: project_files.append(projectfile) + env_vars = [ + EnvironmentVariable( + name="npm_config_build_from_source", + value="true", + ), + ] + components = _generate_component_list(component_info) annotations = [] if backend_annotation := create_backend_annotation(components, "npm"): annotations.append(backend_annotation) return RequestOutput.from_obj_list( components=components, - environment_variables=[], + environment_variables=env_vars, project_files=project_files, annotations=annotations, ) diff --git a/hermeto/core/package_managers/javascript/yarn/main.py b/hermeto/core/package_managers/javascript/yarn/main.py index f5248747a..191ac649b 100644 --- a/hermeto/core/package_managers/javascript/yarn/main.py +++ b/hermeto/core/package_managers/javascript/yarn/main.py @@ -323,6 +323,7 @@ def _generate_environment_variables() -> list[EnvironmentVariable]: "YARN_ENABLE_IMMUTABLE_CACHE": "false", "YARN_ENABLE_MIRROR": "true", "YARN_GLOBAL_FOLDER": "${output_dir}/deps/yarn", + "npm_config_build_from_source": "true", } return [EnvironmentVariable(name=key, value=value) for key, value in env_vars.items()] diff --git a/hermeto/core/package_managers/javascript/yarn_classic/main.py b/hermeto/core/package_managers/javascript/yarn_classic/main.py index 8e4896d24..ed07a5379 100644 --- a/hermeto/core/package_managers/javascript/yarn_classic/main.py +++ b/hermeto/core/package_managers/javascript/yarn_classic/main.py @@ -189,6 +189,7 @@ def _generate_build_environment_variables() -> list[EnvironmentVariable]: env_vars = { "YARN_YARN_OFFLINE_MIRROR": "${output_dir}/deps/yarn-classic", "YARN_YARN_OFFLINE_MIRROR_PRUNING": "false", + "npm_config_build_from_source": "true", } return [EnvironmentVariable(name=key, value=value) for key, value in env_vars.items()] diff --git a/tests/unit/package_managers/javascript/npm/test_main.py b/tests/unit/package_managers/javascript/npm/test_main.py index 58d653a13..8e7cf706a 100644 --- a/tests/unit/package_managers/javascript/npm/test_main.py +++ b/tests/unit/package_managers/javascript/npm/test_main.py @@ -1,11 +1,16 @@ # SPDX-License-Identifier: GPL-3.0-only from pathlib import Path +from unittest import mock import pytest from hermeto import APP_NAME +from hermeto.core.models.output import EnvironmentVariable, RequestOutput from hermeto.core.models.sbom import Component, Property -from hermeto.core.package_managers.javascript.npm.main import _generate_component_list +from hermeto.core.package_managers.javascript.npm.main import ( + _generate_component_list, + fetch_npm_source, +) from hermeto.core.package_managers.javascript.npm.project import NpmComponentInfo @@ -120,3 +125,46 @@ def test_generate_component_list( """Test _generate_component_list with different NpmComponentInfo inputs.""" merged_components = _generate_component_list(components) assert merged_components == expected_components + + +@mock.patch( + "hermeto.core.package_managers.javascript.npm.main.create_backend_annotation", + return_value=None, +) +@mock.patch("hermeto.core.package_managers.javascript.npm.main._resolve_npm") +def test_fetch_npm_source_sets_build_from_source_env_var( + mock_resolve_npm: mock.Mock, + mock_create_annotation: mock.Mock, + rooted_tmp_path: "RootedPath", +) -> None: + """fetch_npm_source must set npm_config_build_from_source=true so that + prebuildify packages compile from source instead of using prebuilt binaries. + See https://github.com/hermetoproject/hermeto/issues/1015.""" + from hermeto.core.models.input import Request + from hermeto.core.rooted_path import RootedPath + + (rooted_tmp_path.path / ".").mkdir(exist_ok=True) + request = Request( + source_dir=rooted_tmp_path, + output_dir=rooted_tmp_path.join_within_root("output"), + packages=[{"type": "npm", "path": "."}], + ) + mock_resolve_npm.return_value = { + "package": { + "name": "foo", + "version": "1.0.0", + "purl": "pkg:npm/foo@1.0.0", + "bundled": False, + "dev": False, + "missing_hash_in_file": None, + "external_refs": None, + }, + "dependencies": [], + "projectfiles": [], + } + + output = fetch_npm_source(request) + + assert EnvironmentVariable(name="npm_config_build_from_source", value="true") in ( + output.build_config.environment_variables + ) diff --git a/tests/unit/package_managers/javascript/yarn/test_main.py b/tests/unit/package_managers/javascript/yarn/test_main.py index ca382a35f..ea7594739 100644 --- a/tests/unit/package_managers/javascript/yarn/test_main.py +++ b/tests/unit/package_managers/javascript/yarn/test_main.py @@ -37,6 +37,7 @@ def yarn_env_variables() -> list[EnvironmentVariable]: EnvironmentVariable(name="YARN_ENABLE_IMMUTABLE_CACHE", value="false"), EnvironmentVariable(name="YARN_ENABLE_MIRROR", value="true"), EnvironmentVariable(name="YARN_GLOBAL_FOLDER", value="${output_dir}/deps/yarn"), + EnvironmentVariable(name="npm_config_build_from_source", value="true"), ] diff --git a/tests/unit/package_managers/javascript/yarn_classic/test_main.py b/tests/unit/package_managers/javascript/yarn_classic/test_main.py index bd459399f..bbf7cfb18 100644 --- a/tests/unit/package_managers/javascript/yarn_classic/test_main.py +++ b/tests/unit/package_managers/javascript/yarn_classic/test_main.py @@ -44,6 +44,7 @@ def yarn_classic_env_variables() -> list[EnvironmentVariable]: name="YARN_YARN_OFFLINE_MIRROR", value="${output_dir}/deps/yarn-classic" ), EnvironmentVariable(name="YARN_YARN_OFFLINE_MIRROR_PRUNING", value="false"), + EnvironmentVariable(name="npm_config_build_from_source", value="true"), ]