Skip to content

npm: set npm_config_build_from_source=true to handle prebuildify packages#1392

Open
Gitjay11 wants to merge 1 commit into
hermetoproject:mainfrom
Gitjay11:issue-1015-npm-prebuildify-build-from-source
Open

npm: set npm_config_build_from_source=true to handle prebuildify packages#1392
Gitjay11 wants to merge 1 commit into
hermetoproject:mainfrom
Gitjay11:issue-1015-npm-prebuildify-build-from-source

Conversation

@Gitjay11

@Gitjay11 Gitjay11 commented Mar 12, 2026

Copy link
Copy Markdown

fixes #1015

Description

Resolves the first proposed solution from #1015.

Packages using prebuildify ship precompiled native .node binaries under a prebuilds/ directory. At install time, node-gyp-build checks for the npm_config_build_from_source environment variable — if set, it compiles native addons from source instead of using the prebuilt binaries.

Without this fix, hermetic builds silently use prebuilt platform-specific binaries, undermining the hermetic build guarantee.

This PR injects npm_config_build_from_source=true into the build configuration output for all npm packages, ensuring native addons are always compiled from source.

How to test

nox -s python-3.10 -- tests/unit/package_managers/test_npm.py -v
Screenshot 2026-03-12 182704
============================= test session starts ==============================
collected 105 items

tests/unit/package_managers/test_npm.py::test_fetch_npm_source[single_input_package] PASSED
tests/unit/package_managers/test_npm.py::test_fetch_npm_source[multiple_input_package] PASSED
...
============================= 105 passed in 1.54s ==============================

As noted in the issue, we should do either or both of the proposed solutions. This PR implements Solution 1 as a standalone fix. Solution 2 (detecting and gating prebuilt packages) can be added as a follow-up PR if the maintainers agree it's needed.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request correctly adds the npm_config_build_from_source=true environment variable to force npm to build native addons from source, which is a good step towards ensuring hermetic builds. My review includes a couple of suggestions to improve code maintainability by reducing magic strings and code duplication in tests, in line with the repository's style guide.

Comment thread hermeto/core/package_managers/npm.py Outdated
Comment thread tests/unit/package_managers/test_npm.py Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request correctly implements the proposed solution to force npm packages with native addons to be built from source by setting the npm_config_build_from_source environment variable. This is a good improvement for ensuring hermetic builds.

I've added a couple of suggestions to improve maintainability by reducing the use of magic strings and avoiding code duplication in tests, in line with the repository's style guide.

Comment thread hermeto/core/package_managers/npm.py Outdated
Comment on lines +742 to +747
env_vars = [
EnvironmentVariable(
name="npm_config_build_from_source",
value="true",
),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To improve readability and maintainability, it's better to define hardcoded strings like environment variable names as module-level constants. This avoids magic strings and makes the configuration easier to manage.

You could add the following at the top of the file:

NPM_BUILD_FROM_SOURCE_ENV_VAR = "npm_config_build_from_source"

And then use it here:

env_vars = [
    EnvironmentVariable(
        name=NPM_BUILD_FROM_SOURCE_ENV_VAR,
        value="true",
    ),
]
References
  1. The style guide emphasizes maintaining readable code. (link)

Comment thread tests/unit/package_managers/test_npm.py Outdated
Comment on lines +797 to +802
"environment_variables": [
EnvironmentVariable(
name="npm_config_build_from_source",
value="true",
),
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

There's some code duplication here and in the next test case (lines 889-894) for creating the EnvironmentVariable object. The repository style guide encourages reducing code duplication.

To follow the DRY (Don't Repeat Yourself) principle, you could define this as a constant at the top of the test file or within the test class and reuse it in both parameterized tests.

For example:

BUILD_FROM_SOURCE_ENV_VAR = EnvironmentVariable(
    name="npm_config_build_from_source",
    value="true",
)

# ... inside the test parameterization
"environment_variables": [BUILD_FROM_SOURCE_ENV_VAR],
References
  1. The style guide recommends detecting and flagging code duplication. (link)

Comment thread hermeto/core/package_managers/npm.py Outdated

env_vars = [
EnvironmentVariable(
name="npm_config_build_from_source",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When this triggers node-gyp, won't it try to fetch Node.js headers from nodejs.org at build time?
How would that work in a hermetic/offline build?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Actually when node-gyp compiles from source, it needs Node.js C/C++ headers. However node-gyp first checks for locally available headers (via the Node.js installations include/ directory or the npm_config_nodedir env var) before attempting to download from nodejs.org. In typical hermetic build environments (e.g., Konflux), Node.js is pre-installed in the base image with headers already available, so node-gyp finds them locally without needing network access.
well, happy to add a note in the code or docs if it's needed.

@farhann-saleem farhann-saleem left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does yarn / yarn-classic need the same treatment? node-gyp-build checks this env var regardless of the package manager.

@a-ovchinnikov

Copy link
Copy Markdown
Contributor

I'd say second option is necessary since it adds visibility. Users who are fine with binaries could then set the variable to true then.

@Gitjay11

Gitjay11 commented Mar 13, 2026

Copy link
Copy Markdown
Author

@farhann-saleem Yes, that's right. node-gyp-build checks this env var regardless of the package manager. I'll update this PR to add the same env var to yarn and yarn-classic as well.

edit : I have added the changes now.

@Gitjay11

Gitjay11 commented Mar 13, 2026

Copy link
Copy Markdown
Author

@a-ovchinnikov Yeah, I know solution 2 adds important visibility. This PR was scoped to Solution 1 as a minimal first step since the issue says either or both. I will implement Solution 2 soon and raise a follow-up PR for it.

edit: I have raised a follow up PR for solution 2.

@Gitjay11 Gitjay11 force-pushed the issue-1015-npm-prebuildify-build-from-source branch 2 times, most recently from bb390b7 to 222c0bc Compare March 15, 2026 04:36
@eskultety

Copy link
Copy Markdown
Member

@Gitjay11 are you going to still work on this? There are merge conflicts to resolve.

@Gitjay11

Gitjay11 commented Jul 2, 2026

Copy link
Copy Markdown
Author

@Gitjay11 are you going to still work on this? There are merge conflicts to resolve.

@eskultety yes, I have resolved the conflicts.

…ages (hermetoproject#1015)

Signed-off-by: Gitjay11 <newajay.11r@gmail.com>
Assisted-by: Gemini
@Gitjay11 Gitjay11 force-pushed the issue-1015-npm-prebuildify-build-from-source branch from 222c0bc to ddf4012 Compare July 2, 2026 01:14
import pytest

from hermeto import APP_NAME
from hermeto.core.models.output import EnvironmentVariable, RequestOutput
Comment on lines +130 to +135
@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(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this unit test is in contradiction with what we are trying to do in #1588

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Handle Javascript packages with prebuilt binaries (e.g., prebuildify)

6 participants