Skip to content

Commit 0b9fc8c

Browse files
authored
Allow participating buildpacks to opt-out of Node.js build scripts (#928)
* Allow participating buildpacks to opt-out of Node.js build scripts The buildpacks that performs installation for each of the supported package managers (npm, pnpm, Yarn) will execute a preset list of scripts from `package.json` if present: - `heroku-prebuild` - `heroku-build` or `build` - `heroku-postbuild` This PR adds a new Buildpack Plan called `node_build_scripts` to the package manager installation buildpacks that can be configured by later buildpacks that require it with the following: ```toml [[requires]] name = "node_build_scripts" [requires.metadata] enabled = <bool> ``` * Allow participating buildpacks to opt-out of Node.js build scripts The buildpacks that performs installation for each of the supported package managers (npm, pnpm, Yarn) will execute a preset list of scripts from `package.json` if present: - `heroku-prebuild` - `heroku-build` or `build` - `heroku-postbuild` This PR adds a new Buildpack Plan called `node_build_scripts` to the package manager installation buildpacks that can be configured by later buildpacks that require it with the following: ```toml [[requires]] name = "node_build_scripts" [requires.metadata] enabled = <bool> ``` * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update README.md * Added yarn-project which for some reason didn't get added in the previous commits * Update Cargo.lock after merge
1 parent 2fec1f4 commit 0b9fc8c

29 files changed

Lines changed: 759 additions & 57 deletions

File tree

Cargo.lock

Lines changed: 80 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

buildpacks/nodejs-npm-install/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Allow configuration of build script behavior through the `node_build_scripts` build plan. ([#928](https://github.com/heroku/buildpacks-nodejs/pull/928))
13+
1014
## [3.2.15] - 2024-10-04
1115

1216
- No changes.

buildpacks/nodejs-npm-install/README.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,37 @@ added that executes `npm start`.
3636

3737
## Build Plan
3838

39+
### Provides
40+
41+
| Name | Description |
42+
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------|
43+
| `node_modules` | Allows other buildpacks to depend on the Node modules provided by this buildpack. |
44+
| `node_build_scripts` | Allows other buildpacks to depend on the [build script execution](#step-3-execute-build-scripts) behavior provided by this buildpack. |
45+
3946
### Requires
4047

41-
| Name | Description |
42-
|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
43-
| `node` | To execute `npm` a [Node.js][Node.js] runtime is required. It can be provided by the [`heroku/nodejs-engine`][heroku/nodejs-engine] buildpack. |
44-
| `npm` | To install node modules, the [npm][npm] package manager is required. It can be provided by either the [`heroku/nodejs-engine`][heroku/nodejs-engine] or [`heroku/nodejs-npm-engine`][heroku/nodejs-npm-engine] buildpacks. |
45-
| `node_modules` | This is not a strict requirement of the buildpack. Requiring `node_modules` ensures that this buildpack can be used even when no other buildpack requires `node_modules`. |
48+
| Name | Description |
49+
|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
50+
| `node` | To execute `npm` a [Node.js][Node.js] runtime is required. It can be provided by the [`heroku/nodejs-engine`][heroku/nodejs-engine] buildpack. |
51+
| `npm` | To install node modules, the [npm][npm] package manager is required. It can be provided by either the [`heroku/nodejs-engine`][heroku/nodejs-engine] or [`heroku/nodejs-npm-engine`][heroku/nodejs-npm-engine] buildpacks. |
52+
| `node_modules` | This is not a strict requirement of the buildpack. Requiring `node_modules` ensures that this buildpack can be used even when no other buildpack requires `node_modules`. |
53+
| `node_build_scripts` | This is not a strict requirement of the buildpack. Requiring `node_build_scripts` ensures that this buildpack will perform [build script execution](#step-3-execute-build-scripts) even when no other buildpack requires `node_build_scripts`. | |
4654

55+
#### Build Plan Metadata Schemas
4756

48-
### Provides
57+
##### `node_build_scripts`
58+
59+
* `enabled` ([boolean][toml_type_boolean], optional)
60+
61+
###### Example
62+
63+
```toml
64+
[[requires]]
65+
name = "node_build_scripts"
4966

50-
| Name | Description |
51-
|----------------|-----------------------------------------------------------------------------------|
52-
| `node_modules` | Allows other buildpacks to depend on the Node modules provided by this buildpack. |
67+
[requires.metadata]
68+
enabled = false # this will prevent build scripts from running
69+
```
5370

5471
## License
5572

@@ -63,3 +80,4 @@ See [LICENSE](../../LICENSE) file.
6380
[npm]: https://docs.npmjs.com/
6481
[heroku/nodejs-engine]: ../nodejs-engine/README.md
6582
[heroku/nodejs-npm-engine]: ../nodejs-npm-engine/README.md
83+
[toml_type_boolean]: https://toml.io/en/v1.0.0#boolean

buildpacks/nodejs-npm-install/src/errors.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use commons::output::fmt;
55
use commons::output::fmt::DEBUG_INFO;
66
use fun_run::CmdError;
77
use heroku_nodejs_utils::application;
8+
use heroku_nodejs_utils::buildplan::{
9+
NodeBuildScriptsMetadataError, NODE_BUILD_SCRIPTS_BUILD_PLAN_NAME,
10+
};
811
use heroku_nodejs_utils::package_json::PackageJsonError;
912
use indoc::formatdoc;
1013
use std::fmt::Display;
@@ -27,6 +30,7 @@ pub(crate) enum NpmInstallBuildpackError {
2730
NpmSetCacheDir(CmdError),
2831
NpmVersion(npm::VersionError),
2932
PackageJson(PackageJsonError),
33+
NodeBuildScriptsMetadata(NodeBuildScriptsMetadataError),
3034
}
3135

3236
pub(crate) fn on_error(error: libcnb::Error<NpmInstallBuildpackError>) {
@@ -44,13 +48,36 @@ fn on_buildpack_error(error: NpmInstallBuildpackError, logger: Box<dyn StartedLo
4448
NpmInstallBuildpackError::Application(e) => on_application_error(&e, logger),
4549
NpmInstallBuildpackError::BuildScript(e) => on_build_script_error(&e, logger),
4650
NpmInstallBuildpackError::Detect(e) => on_detect_error(&e, logger),
51+
NpmInstallBuildpackError::NodeBuildScriptsMetadata(e) => {
52+
on_node_build_scripts_metadata_error(e, logger);
53+
}
4754
NpmInstallBuildpackError::NpmInstall(e) => on_npm_install_error(&e, logger),
4855
NpmInstallBuildpackError::NpmSetCacheDir(e) => on_set_cache_dir_error(&e, logger),
4956
NpmInstallBuildpackError::NpmVersion(e) => on_npm_version_error(e, logger),
5057
NpmInstallBuildpackError::PackageJson(e) => on_package_json_error(e, logger),
5158
}
5259
}
5360

61+
fn on_node_build_scripts_metadata_error(
62+
error: NodeBuildScriptsMetadataError,
63+
logger: Box<dyn StartedLogger>,
64+
) {
65+
let NodeBuildScriptsMetadataError::InvalidEnabledValue(value) = error;
66+
let value_type = value.type_str();
67+
logger.announce().error(&formatdoc! { "
68+
A participating buildpack has set invalid `[requires.metadata]` for the build plan \
69+
named `{NODE_BUILD_SCRIPTS_BUILD_PLAN_NAME}`.
70+
71+
Expected metadata format:
72+
[requires.metadata]
73+
enabled = <bool>
74+
75+
But was:
76+
[requires.metadata]
77+
enabled = <{value_type}>
78+
"});
79+
}
80+
5481
fn on_package_json_error(error: PackageJsonError, logger: Box<dyn StartedLogger>) {
5582
match error {
5683
PackageJsonError::AccessError(e) => {

buildpacks/nodejs-npm-install/src/main.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use commons::output::section_log::{log_step, log_step_stream};
1212
use commons::output::warn_later::WarnGuard;
1313
use fun_run::{CommandWithName, NamedOutput};
1414
use heroku_nodejs_utils::application;
15+
use heroku_nodejs_utils::buildplan::{
16+
read_node_build_scripts_metadata, NodeBuildScriptsMetadata, NODE_BUILD_SCRIPTS_BUILD_PLAN_NAME,
17+
};
1518
use heroku_nodejs_utils::package_json::PackageJson;
1619
use heroku_nodejs_utils::package_manager::PackageManager;
1720
use heroku_nodejs_utils::vrs::Version;
@@ -53,9 +56,11 @@ impl Buildpack for NpmInstallBuildpack {
5356
.build_plan(
5457
BuildPlanBuilder::new()
5558
.provides("node_modules")
59+
.provides(NODE_BUILD_SCRIPTS_BUILD_PLAN_NAME)
60+
.requires("node")
5661
.requires("npm")
5762
.requires("node_modules")
58-
.requires("node")
63+
.requires(NODE_BUILD_SCRIPTS_BUILD_PLAN_NAME)
5964
.build(),
6065
)
6166
.build()
@@ -74,6 +79,8 @@ impl Buildpack for NpmInstallBuildpack {
7479
let app_dir = &context.app_dir;
7580
let package_json = PackageJson::read(app_dir.join("package.json"))
7681
.map_err(NpmInstallBuildpackError::PackageJson)?;
82+
let node_build_scripts_metadata = read_node_build_scripts_metadata(&context.buildpack_plan)
83+
.map_err(NpmInstallBuildpackError::NodeBuildScriptsMetadata)?;
7784

7885
run_application_checks(app_dir, &warn_later)?;
7986

@@ -84,7 +91,12 @@ impl Buildpack for NpmInstallBuildpack {
8491
let logger = section.end_section();
8592

8693
let section = logger.section("Running scripts");
87-
run_build_scripts(&package_json, &env, section.as_ref())?;
94+
run_build_scripts(
95+
&package_json,
96+
&node_build_scripts_metadata,
97+
&env,
98+
section.as_ref(),
99+
)?;
88100
let logger = section.end_section();
89101

90102
let section = logger.section("Configuring default processes");
@@ -154,6 +166,7 @@ fn run_npm_install(
154166

155167
fn run_build_scripts(
156168
package_json: &PackageJson,
169+
node_build_scripts_metadata: &NodeBuildScriptsMetadata,
157170
env: &Env,
158171
_section_logger: &dyn SectionLogger,
159172
) -> Result<(), NpmInstallBuildpackError> {
@@ -162,16 +175,23 @@ fn run_build_scripts(
162175
log_step("No build scripts found");
163176
} else {
164177
for script in build_scripts {
165-
let mut npm_run = npm::RunScript { env, script }.into_command();
166-
log_step_stream(
167-
format!("Running {}", fmt::command(npm_run.name())),
168-
|stream| {
169-
npm_run
170-
.stream_output(stream.io(), stream.io())
171-
.and_then(NamedOutput::nonzero_captured)
172-
.map_err(NpmInstallBuildpackError::BuildScript)
173-
},
174-
)?;
178+
if let Some(false) = node_build_scripts_metadata.enabled {
179+
log_step(format!(
180+
"Not running {} as it was disabled by a participating buildpack",
181+
fmt::value(script)
182+
));
183+
} else {
184+
let mut npm_run = npm::RunScript { env, script }.into_command();
185+
log_step_stream(
186+
format!("Running {}", fmt::command(npm_run.name())),
187+
|stream| {
188+
npm_run
189+
.stream_output(stream.io(), stream.io())
190+
.and_then(NamedOutput::nonzero_captured)
191+
.map_err(NpmInstallBuildpackError::BuildScript)
192+
},
193+
)?;
194+
}
175195
}
176196
};
177197
Ok(())

0 commit comments

Comments
 (0)