Skip to content

Commit 98a89ee

Browse files
Update Node.js Engine Inventory (#1410)
* Update Inventory for heroku/nodejs engine - Added Node.js version 24.0.0. * Add hatchet tests for Node 24 --------- Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com> Co-authored-by: Colin Casey <[email protected]>
1 parent 72c37f7 commit 98a89ee

File tree

12 files changed

+172
-2
lines changed

12 files changed

+172
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [Unreleased]
44

5+
- Added Node.js version 24.0.0.
56
- Drop all support and references to the now end-of-life `heroku-20` stack. ([#1408](https://github.com/heroku/heroku-buildpack-nodejs/pull/1408))
67

78
## [v290] - 2025-04-24

inventory/node.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6195,6 +6195,13 @@ arch = "linux-x64"
61956195
url = "https://heroku-nodebin.s3.us-east-1.amazonaws.com/node/release/linux-x64/node-v23.9.0-linux-x64.tar.gz"
61966196
etag = "097d7fe5948a070b55b3099d7209db2d-7"
61976197

6198+
[[releases]]
6199+
version = "24.0.0"
6200+
channel = "release"
6201+
arch = "linux-x64"
6202+
url = "https://heroku-nodebin.s3.us-east-1.amazonaws.com/node/release/linux-x64/node-v24.0.0-linux-x64.tar.gz"
6203+
etag = "afaa569dadb13adeaf866ed3e4726c90-7"
6204+
61986205
[[releases]]
61996206
version = "4.0.0"
62006207
channel = "release"

spec/ci/node_24_metrics_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require_relative '../spec_helper'
2+
3+
describe "Node Metrics for v24.x" do
4+
context "test metrics for Node v24x app" do
5+
let(:app) {
6+
Hatchet::Runner.new(
7+
"spec/fixtures/repos/node-24-metrics",
8+
config: {
9+
"HEROKU_METRICS_URL" => "http://localhost:3000",
10+
"METRICS_INTERVAL_OVERRIDE" => "10000"
11+
}
12+
)
13+
}
14+
15+
it "should deploy" do
16+
app.deploy do |app|
17+
data = successful_json_body(app)
18+
expect(data["gauges"]["node.eventloop.delay.ms.max"]).to be >= 2000
19+
expect(data["counters"]["node.gc.collections"]).to be >= 0
20+
expect(data["counters"]["node.gc.young.collections"]).to be >= 0
21+
expect(data["counters"]["node.gc.old.collections"]).to be >= 0
22+
end
23+
end
24+
end
25+
end

spec/ci/node_24_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require_relative '../spec_helper'
2+
3+
describe "Hello World for Node v24.x" do
4+
context "a single-process Node v24.x app" do
5+
let(:app) {
6+
Hatchet::Runner.new("spec/fixtures/repos/node-24")
7+
}
8+
9+
it "should deploy successfully" do
10+
app.deploy do |app|
11+
expect(successful_body(app).strip).to eq("Hello, world!")
12+
end
13+
end
14+
end
15+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: node index.js
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env node
2+
3+
const http = require('http');
4+
const EventEmitter = require('events');
5+
6+
const PORT = process.env.PORT || 5000;
7+
const Events = new EventEmitter();
8+
9+
// This will block the event loop for ~lengths of time
10+
function blockCpuFor(ms) {
11+
return new Promise((resolve, reject) => {
12+
setTimeout(() => {
13+
console.log(`blocking the event loop for ${ms}ms`);
14+
let now = new Date().getTime();
15+
let result = 0
16+
while(true) {
17+
result += Math.random() * Math.random();
18+
if (new Date().getTime() > now + ms)
19+
break;
20+
}
21+
resolve();
22+
}, 100);
23+
});
24+
}
25+
26+
function getNextMetricsEvent() {
27+
return new Promise((resolve, reject) => Events.once('metrics', resolve));
28+
}
29+
30+
const server = http.createServer((req, res) => {
31+
// wait for the next metrics event
32+
getNextMetricsEvent()
33+
.then(blockCpuFor(2000))
34+
.then(blockCpuFor(100))
35+
.then(blockCpuFor(100))
36+
.then(blockCpuFor(100))
37+
.then(blockCpuFor(100))
38+
.then(blockCpuFor(100))
39+
.then(blockCpuFor(100))
40+
.then(blockCpuFor(100))
41+
.then(blockCpuFor(100))
42+
.then(blockCpuFor(100))
43+
.then(blockCpuFor(100))
44+
// gather the next metrics data which should include these pauses
45+
.then(getNextMetricsEvent())
46+
.then(data => {
47+
res.setHeader('Content-Type', 'application/json');
48+
res.end(data);
49+
})
50+
.catch(() => {
51+
res.statusCode = 500;
52+
res.end("Something went wrong");
53+
});
54+
});
55+
56+
server.listen(PORT, () => console.log(`Listening on ${PORT}`));
57+
58+
// Create a second server that intercepts the HTTP requests
59+
// sent by the metrics plugin
60+
const metricsListener = http.createServer((req, res) => {
61+
if (req.method == 'POST') {
62+
let body = '';
63+
req.on('data', (data) => body += data);
64+
req.on('end', () => {
65+
res.statusCode = 200;
66+
res.end();
67+
Events.emit('metrics', body)
68+
});
69+
}
70+
});
71+
72+
metricsListener.listen(3000, () => console.log('Listening for metrics on 3000'));
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "node-metrics-test-app",
3+
"version": "1.0.0",
4+
"engines": {
5+
"node": "24.x"
6+
},
7+
"main": "index.js",
8+
"license": "MIT",
9+
"devDependencies": {},
10+
"dependencies": {}
11+
}

spec/fixtures/repos/node-24/Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: node index.js

spec/fixtures/repos/node-24/app.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "hello-world"
3+
}

spec/fixtures/repos/node-24/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env node
2+
3+
const http = require('http');
4+
5+
const PORT = process.env.PORT || 5000;
6+
7+
const server = http.createServer((_req, res) => {
8+
res.statusCode = 200;
9+
res.setHeader('Content-Type', 'text/plain');
10+
res.end("Hello, world!");
11+
})
12+
13+
server.listen(PORT, () => console.log(`Listening on ${PORT}`));
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "hello-world",
3+
"version": "1.0.0",
4+
"engines": {
5+
"node": "24.x"
6+
},
7+
"scripts": {
8+
"prettify": "prettier --single-quote --trailing-comma all --write 'bin/*' 'src/**/*.js'",
9+
"test": "jest --silent",
10+
"dev": "nodemon --watch . --watch src/* src/index.js",
11+
"build": "echo NODE_OPTIONS: $NODE_OPTIONS"
12+
},
13+
"main": "index.js",
14+
"license": "MIT",
15+
"devDependencies": {
16+
"jest": "^19.0.2",
17+
"nodemon": "^1.19.4",
18+
"prettier": "^0.22.0"
19+
},
20+
"dependencies": {}
21+
}

spec/spec_helper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def resolve_all_supported_node_versions(options = {})
7878
end
7979

8080
def version_supports_metrics(version)
81-
SemVersion.new(version).satisfies?('>= 10.0.0') && SemVersion.new(version).satisfies?('< 24.0.0')
81+
SemVersion.new(version).satisfies?('>= 10.0.0') && SemVersion.new(version).satisfies?('< 25.0.0')
8282
end
8383

8484
def get_test_versions
@@ -87,7 +87,7 @@ def get_test_versions
8787
elsif ENV['TEST_ALL_NODE_VERSIONS'] == 'true'
8888
versions = resolve_all_supported_node_versions()
8989
else
90-
versions = resolve_node_version(['18.x', '19.x', '20.x', '21.x', '22.x', '23.x'])
90+
versions = resolve_node_version(['20.x', '22.x', '23.x', '24.x'])
9191
end
9292
puts("Running tests for Node versions: #{versions.join(', ')}")
9393
versions

0 commit comments

Comments
 (0)