Skip to content

Commit bf39851

Browse files
colincaseyschneems
andauthored
Warn when deploying with End-of-Life Node.js versions (#1621)
* feat: display EOL warning and track metric when deploying with unsupported Node.js * feat: add eol field to resolve-version JSON output * chore: rebuild resolve-version binary and update changelog * test: add EOL warning assertions to hatchet specs for all Node.js versions * fix: add node_version_eol metric assertion to functional build metadata tests * refactor: move EOL check into Resolution struct Move the EOL boolean from inline JSON serialization into the Resolution struct so tests verify actual production logic instead of re-deriving it. * test: match full EOL warning in hatchet specs and rely on baseline blocks for non-EOL * fix: use assertCapturedSuccessWithWarnings for tests using EOL Node versions Functional tests using EOL Node fixtures now produce stderr output from the EOL warning. Switch from assertCapturedSuccess (which requires empty stderr) to assertCapturedSuccessWithWarnings (which only checks exit code). * chore: rebuild resolve-version binary after rebase onto main * Update test/run-general Co-authored-by: Richard Schneeman <richard.schneeman+foo@gmail.com> Signed-off-by: Colin Casey <casey.colin@gmail.com> --------- Signed-off-by: Colin Casey <casey.colin@gmail.com> Co-authored-by: Richard Schneeman <richard.schneeman+foo@gmail.com>
1 parent f1d1761 commit bf39851

22 files changed

Lines changed: 269 additions & 99 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Updated resolve-version binary to use shared `nodejs-data` crate. ([#1617](https://github.com/heroku/heroku-buildpack-nodejs/pull/1617))
77
- Improve resolve-version logic for detecting highest LTS to use when a wide version range is requested. ([#1618](https://github.com/heroku/heroku-buildpack-nodejs/pull/1618))
88
- Default Node.js version is now derived from the `nodejs-data` crate instead of being hardcoded in shell scripts. ([#1620](https://github.com/heroku/heroku-buildpack-nodejs/pull/1620))
9+
- Warn when deploying with End-of-Life Node.js versions. ([#1621](https://github.com/heroku/heroku-buildpack-nodejs/pull/1621))
910

1011
## [v342] - 2026-04-16
1112

lib/binaries.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ install_nodejs() {
8383
uses_wide_range=$(echo "$resolve_result" | jq .uses_wide_range)
8484
lts_upper_bound_enforced=$(echo "$resolve_result" | jq .lts_upper_bound_enforced)
8585
lts_version=$(echo "$resolve_result" | jq -r .lts_version)
86+
eol=$(echo "$resolve_result" | jq .eol)
87+
88+
build_data::set_raw "node_version_eol" "$eol"
8689

8790
if [[ "$uses_wide_range" == "true" ]]; then
8891
output::warning <<-EOF
@@ -100,6 +103,19 @@ install_nodejs() {
100103
EOF
101104
fi
102105

106+
if [[ "$eol" == "true" ]]; then
107+
output::warning <<-EOF
108+
Node.js $version is now End-of-Life (EOL). It no longer receives security
109+
updates, bug fixes, or support from the Node.js project and is no longer
110+
supported on Heroku.
111+
112+
In a future buildpack release, this warning will become a build error. Please
113+
upgrade to a supported version as soon as possible to avoid build failures.
114+
115+
https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
116+
EOF
117+
fi
118+
103119
echo "Downloading and installing node $version..."
104120

105121
if [[ "$version" == "22.5.0" ]]; then

lib/vendor/resolve-version-linux

-3.84 KB
Binary file not shown.

resolve-version/src/main.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use clap::{Command, arg, value_parser};
22
use libherokubuildpack::inventory::artifact::{Arch, Os};
33
use nodejs_data::{
4-
NodejsArtifact, NodejsInventory, RECOMMENDED_LTS_VERSION, Version, VersionError, VersionRange,
4+
NodejsArtifact, NodejsInventory, RECOMMENDED_LTS_VERSION, SUPPORTED_NODEJS_VERSIONS, Version,
5+
VersionError, VersionRange,
56
};
67
use std::env::consts;
78

@@ -14,6 +15,7 @@ struct Resolution<'a> {
1415
artifact: &'a NodejsArtifact,
1516
uses_wide_range: bool,
1617
lts_upper_bound_enforced: bool,
18+
eol: bool,
1719
}
1820

1921
fn main() {
@@ -99,6 +101,7 @@ fn main() {
99101
"lts_upper_bound_enforced": resolution.lts_upper_bound_enforced,
100102
"default": default,
101103
"lts_version": RECOMMENDED_LTS_VERSION.to_string(),
104+
"eol": resolution.eol,
102105
})
103106
);
104107
} else {
@@ -173,8 +176,11 @@ fn resolve_node_artifact<'a>(
173176
)
174177
};
175178

179+
let artifact = lts_artifact.unwrap_or(resolved_artifact);
180+
176181
Some(Resolution {
177-
artifact: lts_artifact.unwrap_or(resolved_artifact),
182+
eol: !SUPPORTED_NODEJS_VERSIONS.contains(&artifact.version.major()),
183+
artifact,
178184
uses_wide_range,
179185
lts_upper_bound_enforced: lts_artifact.is_some(),
180186
})
@@ -470,6 +476,42 @@ mod tests {
470476
assert_eq!(result.unwrap().to_string(), "22.x");
471477
}
472478

479+
// --- EOL detection tests ---
480+
481+
#[test]
482+
fn eol_true_for_unsupported_version() {
483+
let inventory = create_inventory();
484+
let requirement = VersionRange::parse("18.x").unwrap();
485+
let resolution = resolve_node_artifact(
486+
&inventory,
487+
Os::Linux,
488+
Arch::Amd64,
489+
&requirement,
490+
TEST_LTS_MAJOR_VERSION,
491+
DISALLOW_WIDE_RANGE,
492+
)
493+
.expect("expected resolution to succeed");
494+
assert_eq!(resolution.artifact.version.major(), 18);
495+
assert!(resolution.eol);
496+
}
497+
498+
#[test]
499+
fn eol_false_for_supported_version() {
500+
let inventory = create_inventory();
501+
let requirement = VersionRange::parse("24.x").unwrap();
502+
let resolution = resolve_node_artifact(
503+
&inventory,
504+
Os::Linux,
505+
Arch::Amd64,
506+
&requirement,
507+
TEST_LTS_MAJOR_VERSION,
508+
DISALLOW_WIDE_RANGE,
509+
)
510+
.expect("expected resolution to succeed");
511+
assert_eq!(resolution.artifact.version.major(), 24);
512+
assert!(!resolution.eol);
513+
}
514+
473515
fn create_inventory() -> NodejsInventory {
474516
let contents = r#"
475517
[[artifacts]]
@@ -492,6 +534,13 @@ mod tests {
492534
arch = "amd64"
493535
url = "https://nodejs.org/download/release/v22.21.0/node-v22.21.0-linux-x64.tar.gz"
494536
checksum = "sha256:262b84b02f7e2bc017d4bdb81fec85ca0d6190a5cd0781d2d6e84317c08871f8"
537+
538+
[[artifacts]]
539+
version = "18.20.8"
540+
os = "linux"
541+
arch = "amd64"
542+
url = "https://nodejs.org/download/release/v18.20.8/node-v18.20.8-linux-x64.tar.gz"
543+
checksum = "sha256:27a9f3f14d5e99ad05a07ed3524ba3ee92f8ff8b6db5ff80b00f9feb5ec8097a"
495544
"#;
496545
toml::from_str(contents).unwrap()
497546
}

spec/ci/node_10_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-10")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 10.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 10.x...
17+
remote:
18+
remote: ! Node.js 10.24.1 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 10.24.1...
1928
remote: Validating checksum
2029
remote: Using default npm version: 6.14.12
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434

spec/ci/node_12_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-12")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 12.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 12.x...
17+
remote:
18+
remote: ! Node.js 12.22.12 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 12.22.12...
1928
remote: Validating checksum
2029
remote: Using default npm version: 6.14.16
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434

spec/ci/node_14_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-14")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 14.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 14.x...
17+
remote:
18+
remote: ! Node.js 14.21.3 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 14.21.3...
1928
remote: Validating checksum
2029
remote: Using default npm version: 6.14.18
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434

spec/ci/node_15_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-15")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 15.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 15.x...
17+
remote:
18+
remote: ! Node.js 15.14.0 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 15.14.0...
1928
remote: Validating checksum
2029
remote: Using default npm version: 7.7.6
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434

spec/ci/node_16_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-16")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 16.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 16.x...
17+
remote:
18+
remote: ! Node.js 16.20.2 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 16.20.2...
1928
remote: Validating checksum
2029
remote: Using default npm version: 8.19.4
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434
end

spec/ci/node_17_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66
Hatchet::Runner.new("spec/fixtures/repos/node-17")
77
}
88

9-
it "should deploy successfully" do
9+
it "should deploy successfully with EOL warning" do
1010
app.deploy do |app|
11-
expect(successful_body(app).strip).to eq("Hello, world!")
1211
expect(clean_output(app.output)).to include(<<~OUTPUT)
1312
remote: -----> Installing binaries
1413
remote: engines.node (package.json): 17.x
1514
remote: engines.npm (package.json): unspecified (use default)
1615
remote:
1716
remote: Resolving node version 17.x...
17+
remote:
18+
remote: ! Node.js 17.9.1 is now End-of-Life (EOL). It no longer receives security
19+
remote: ! updates, bug fixes, or support from the Node.js project and is no longer
20+
remote: ! supported on Heroku.
21+
remote: !
22+
remote: ! In a future buildpack release, this warning will become a build error. Please
23+
remote: ! upgrade to a supported version as soon as possible to avoid build failures.
24+
remote: !
25+
remote: ! https://devcenter.heroku.com/articles/nodejs-support#supported-node-js-versions
26+
remote:
1827
remote: Downloading and installing node 17.9.1...
1928
remote: Validating checksum
2029
remote: Using default npm version: 8.11.0
2130
OUTPUT
31+
expect(successful_body(app).strip).to eq("Hello, world!")
2232
end
2333
end
2434
end

0 commit comments

Comments
 (0)