diff --git a/CHANGELOG.md b/CHANGELOG.md index d6129c4af..53b5f6e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## main +## v211 (2023-04-20) + +- Add metrics and tests for Node.js 20.x line + ## v210 (2023-04-20) - Added node version 20.0.0. diff --git a/plugin/download.sh b/plugin/download.sh index 76830e094..530becbf5 100755 --- a/plugin/download.sh +++ b/plugin/download.sh @@ -42,6 +42,8 @@ delete_old_plugin() { rm -f "$dir/heroku-nodejs-plugin-node-18.tar.gz" rm -f "$dir/heroku-nodejs-plugin-node-19.sha512" rm -f "$dir/heroku-nodejs-plugin-node-19.tar.gz" + rm -f "$dir/heroku-nodejs-plugin-node-20.sha512" + rm -f "$dir/heroku-nodejs-plugin-node-20.tar.gz" rm -f "$dir/version" } @@ -64,6 +66,10 @@ download_assets_for_release() { # Node 19 download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-19-$tag.sha512" "$dir/heroku-nodejs-plugin-node-19.sha512" download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-19-$tag.tar.gz" "$dir/heroku-nodejs-plugin-node-19.tar.gz" + + # Node 20 + download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-20-$tag.sha512" "$dir/heroku-nodejs-plugin-node-20.sha512" + download "https://github.com/heroku/heroku-nodejs-plugin/releases/download/$tag/heroku-nodejs-plugin-node-20-$tag.tar.gz" "$dir/heroku-nodejs-plugin-node-20.tar.gz" } test_hash() { @@ -100,5 +106,6 @@ test_hash 14 $PLUGIN_DIR test_hash 16 $PLUGIN_DIR test_hash 18 $PLUGIN_DIR test_hash 19 $PLUGIN_DIR +test_hash 20 $PLUGIN_DIR echo "Done" diff --git a/plugin/heroku-nodejs-plugin-node-14.sha512 b/plugin/heroku-nodejs-plugin-node-14.sha512 index cafa78e89..feadd34fb 100644 --- a/plugin/heroku-nodejs-plugin-node-14.sha512 +++ b/plugin/heroku-nodejs-plugin-node-14.sha512 @@ -1 +1 @@ -3a6171950d27f3b3cc82200089733f39ec962f30b774913d72a55e9e05d3c7d0d5127508b74c1fb48f775471d7550988cc556e52683ea7b0715dabee8a7fcaae heroku-nodejs-plugin-node-14-v11.tar.gz +b003962c8702e7e78e7e09a3b246d670e71224abe665d0fa832696d28de8c365904771d58f87888525875fd5a7a1c5dd262a7113f91709d973bca1dc24a761ce heroku-nodejs-plugin-node-14-v12.tar.gz diff --git a/plugin/heroku-nodejs-plugin-node-14.tar.gz b/plugin/heroku-nodejs-plugin-node-14.tar.gz index 419d2ef81..3e57f5d21 100644 Binary files a/plugin/heroku-nodejs-plugin-node-14.tar.gz and b/plugin/heroku-nodejs-plugin-node-14.tar.gz differ diff --git a/plugin/heroku-nodejs-plugin-node-16.sha512 b/plugin/heroku-nodejs-plugin-node-16.sha512 index 47d1546d1..1bc91db39 100644 --- a/plugin/heroku-nodejs-plugin-node-16.sha512 +++ b/plugin/heroku-nodejs-plugin-node-16.sha512 @@ -1 +1 @@ -f8a70f50b4df42a98f44f606137c1ed0e2f6abc3041719e61259a584a12ee31616125fb7a3479d749594838180dd5abf8036302f4c5cec283ef5ddf9bd1e2cd8 heroku-nodejs-plugin-node-16-v11.tar.gz +0c1de3760f42fa275ec7494a5a17f22000df9f22db6f0f83211db9c6f0e46bc8645b83fb9d6d4d8fa2ec66f82a760d111c94001151aec76baae045d485a74eb3 heroku-nodejs-plugin-node-16-v12.tar.gz diff --git a/plugin/heroku-nodejs-plugin-node-16.tar.gz b/plugin/heroku-nodejs-plugin-node-16.tar.gz index e3e477988..62d711acf 100644 Binary files a/plugin/heroku-nodejs-plugin-node-16.tar.gz and b/plugin/heroku-nodejs-plugin-node-16.tar.gz differ diff --git a/plugin/heroku-nodejs-plugin-node-18.sha512 b/plugin/heroku-nodejs-plugin-node-18.sha512 index ab79767fd..b379f678b 100644 --- a/plugin/heroku-nodejs-plugin-node-18.sha512 +++ b/plugin/heroku-nodejs-plugin-node-18.sha512 @@ -1 +1 @@ -573f5567fe049213bbc6764a6bd46b9a625af664e29496cb75fc825788f996e743c25290076745c6173f39fa452534e8cf07db5c83d5fcd5c9fe2fb6945f282c heroku-nodejs-plugin-node-18-v11.tar.gz +07e97b6d3a7e95a8790d328798a4904bdcc5bed86e6c0c5f921c32d5952e30e61aec250130569f66434e77134ee2b8d711dc44a47ffddd446697625138885dea heroku-nodejs-plugin-node-18-v12.tar.gz diff --git a/plugin/heroku-nodejs-plugin-node-18.tar.gz b/plugin/heroku-nodejs-plugin-node-18.tar.gz index c21b8b29f..296bd180d 100644 Binary files a/plugin/heroku-nodejs-plugin-node-18.tar.gz and b/plugin/heroku-nodejs-plugin-node-18.tar.gz differ diff --git a/plugin/heroku-nodejs-plugin-node-19.sha512 b/plugin/heroku-nodejs-plugin-node-19.sha512 index be08569c9..f22b86bbd 100644 --- a/plugin/heroku-nodejs-plugin-node-19.sha512 +++ b/plugin/heroku-nodejs-plugin-node-19.sha512 @@ -1 +1 @@ -e7bd47b0672e4b900714336bc90f1583ea54f6692d2cf0ee1ea954dac270bf5b33930cb413db26fe93b5f28ea8866b4c44d1e83bab8e7d8dc5b07fa595474188 heroku-nodejs-plugin-node-19-v11.tar.gz +5f1f5d80eb8fa578a235587d392e7fdbcb0f3d31cf564f5b7d420939ebd083372869d88c7c287d149ee5096d23655cb1a6785935268c5fde5738fde4b29b8bd6 heroku-nodejs-plugin-node-19-v12.tar.gz diff --git a/plugin/heroku-nodejs-plugin-node-19.tar.gz b/plugin/heroku-nodejs-plugin-node-19.tar.gz index 56ec238cb..e6df0ad7c 100644 Binary files a/plugin/heroku-nodejs-plugin-node-19.tar.gz and b/plugin/heroku-nodejs-plugin-node-19.tar.gz differ diff --git a/plugin/heroku-nodejs-plugin-node-20.sha512 b/plugin/heroku-nodejs-plugin-node-20.sha512 new file mode 100644 index 000000000..5e7302dfe --- /dev/null +++ b/plugin/heroku-nodejs-plugin-node-20.sha512 @@ -0,0 +1 @@ +07adc25fc5aa5100d9ee3f4018e0c746f4b35793bb7a90b5c913b4603f3ba2d565f4b77d3c734394a2e0b7b90d03db8be3a72b1a505cf75702a99eb596f757a1 heroku-nodejs-plugin-node-20-v12.tar.gz diff --git a/plugin/heroku-nodejs-plugin-node-20.tar.gz b/plugin/heroku-nodejs-plugin-node-20.tar.gz new file mode 100644 index 000000000..37a2faaec Binary files /dev/null and b/plugin/heroku-nodejs-plugin-node-20.tar.gz differ diff --git a/plugin/version b/plugin/version index 882cecb02..dae199aec 100644 --- a/plugin/version +++ b/plugin/version @@ -1 +1 @@ -v11 +v12 diff --git a/spec/ci/node_20_metrics_spec.rb b/spec/ci/node_20_metrics_spec.rb new file mode 100644 index 000000000..1f62c7c86 --- /dev/null +++ b/spec/ci/node_20_metrics_spec.rb @@ -0,0 +1,25 @@ +require_relative '../spec_helper' + +describe "Node Metrics for v20.x" do + context "test metrics for Node v20x app" do + let(:app) { + Hatchet::Runner.new( + "spec/fixtures/repos/node-20-metrics", + config: { + "HEROKU_METRICS_URL" => "http://localhost:3000", + "METRICS_INTERVAL_OVERRIDE" => "10000" + } + ) + } + + it "should deploy" do + app.deploy do |app| + data = successful_json_body(app) + expect(data["gauges"]["node.eventloop.delay.ms.max"]).to be >= 2000 + expect(data["counters"]["node.gc.collections"]).to be >= 0 + expect(data["counters"]["node.gc.young.collections"]).to be >= 0 + expect(data["counters"]["node.gc.old.collections"]).to be >= 0 + end + end + end +end diff --git a/spec/ci/node_20_spec.rb b/spec/ci/node_20_spec.rb new file mode 100644 index 000000000..661393511 --- /dev/null +++ b/spec/ci/node_20_spec.rb @@ -0,0 +1,27 @@ +require_relative '../spec_helper' + +describe "Hello World for Node v20.x" do + context "a single-process Node v20.x app" do + let(:app) { + Hatchet::Runner.new("spec/fixtures/repos/node-20") + } + + it "should deploy successfully" do + app.deploy do |app| + expect(successful_body(app).strip).to eq("Hello, world!") + end + end + end + + context "on an incompatible stack (heroku-18)" do + it "should log a stack compatibility message" do + Hatchet::Runner.new( + "spec/fixtures/repos/node-20", + stack: "heroku-18", + allow_failure: true + ).deploy do |app| + expect(app.output).to match("Node.js version is not compatible with the current stack") + end + end + end +end diff --git a/spec/fixtures/repos/node-20-metrics/Procfile b/spec/fixtures/repos/node-20-metrics/Procfile new file mode 100644 index 000000000..1da0cd6f6 --- /dev/null +++ b/spec/fixtures/repos/node-20-metrics/Procfile @@ -0,0 +1 @@ +web: node index.js diff --git a/spec/fixtures/repos/node-20-metrics/index.js b/spec/fixtures/repos/node-20-metrics/index.js new file mode 100755 index 000000000..eae912a05 --- /dev/null +++ b/spec/fixtures/repos/node-20-metrics/index.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node + +const http = require('http'); +const EventEmitter = require('events'); + +const PORT = process.env.PORT || 5000; +const Events = new EventEmitter(); + +// This will block the event loop for ~lengths of time +function blockCpuFor(ms) { + return new Promise((resolve, reject) => { + setTimeout(() => { + console.log(`blocking the event loop for ${ms}ms`); + let now = new Date().getTime(); + let result = 0 + while(true) { + result += Math.random() * Math.random(); + if (new Date().getTime() > now + ms) + break; + } + resolve(); + }, 100); + }); +} + +function getNextMetricsEvent() { + return new Promise((resolve, reject) => Events.once('metrics', resolve)); +} + +const server = http.createServer((req, res) => { + // wait for the next metrics event + getNextMetricsEvent() + .then(blockCpuFor(2000)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + .then(blockCpuFor(100)) + // gather the next metrics data which should include these pauses + .then(getNextMetricsEvent()) + .then(data => { + res.setHeader('Content-Type', 'application/json'); + res.end(data); + }) + .catch(() => { + res.statusCode = 500; + res.end("Something went wrong"); + }); +}); + +server.listen(PORT, () => console.log(`Listening on ${PORT}`)); + +// Create a second server that intercepts the HTTP requests +// sent by the metrics plugin +const metricsListener = http.createServer((req, res) => { + if (req.method == 'POST') { + let body = ''; + req.on('data', (data) => body += data); + req.on('end', () => { + res.statusCode = 200; + res.end(); + Events.emit('metrics', body) + }); + } +}); + +metricsListener.listen(3000, () => console.log('Listening for metrics on 3000')); diff --git a/spec/fixtures/repos/node-20-metrics/package.json b/spec/fixtures/repos/node-20-metrics/package.json new file mode 100644 index 000000000..bfdeddc72 --- /dev/null +++ b/spec/fixtures/repos/node-20-metrics/package.json @@ -0,0 +1,11 @@ +{ + "name": "node-metrics-test-app", + "version": "1.0.0", + "engines": { + "node": "20.x" + }, + "main": "index.js", + "license": "MIT", + "devDependencies": {}, + "dependencies": {} +} diff --git a/spec/fixtures/repos/node-20/Procfile b/spec/fixtures/repos/node-20/Procfile new file mode 100644 index 000000000..1da0cd6f6 --- /dev/null +++ b/spec/fixtures/repos/node-20/Procfile @@ -0,0 +1 @@ +web: node index.js diff --git a/spec/fixtures/repos/node-20/app.json b/spec/fixtures/repos/node-20/app.json new file mode 100644 index 000000000..63b5c96f2 --- /dev/null +++ b/spec/fixtures/repos/node-20/app.json @@ -0,0 +1,3 @@ +{ + "name": "hello-world" +} diff --git a/spec/fixtures/repos/node-20/index.js b/spec/fixtures/repos/node-20/index.js new file mode 100755 index 000000000..6a0090ff5 --- /dev/null +++ b/spec/fixtures/repos/node-20/index.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +const http = require('http'); + +const PORT = process.env.PORT || 5000; + +const server = http.createServer((_req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end("Hello, world!"); +}) + +server.listen(PORT, () => console.log(`Listening on ${PORT}`)); diff --git a/spec/fixtures/repos/node-20/package.json b/spec/fixtures/repos/node-20/package.json new file mode 100644 index 000000000..3e8ea46b8 --- /dev/null +++ b/spec/fixtures/repos/node-20/package.json @@ -0,0 +1,21 @@ +{ + "name": "hello-world", + "version": "1.0.0", + "engines": { + "node": "20.x" + }, + "scripts": { + "prettify": "prettier --single-quote --trailing-comma all --write 'bin/*' 'src/**/*.js'", + "test": "jest --silent", + "dev": "nodemon --watch . --watch src/* src/index.js", + "build": "echo NODE_OPTIONS: $NODE_OPTIONS" + }, + "main": "index.js", + "license": "MIT", + "devDependencies": { + "jest": "^19.0.2", + "nodemon": "^1.19.4", + "prettier": "^0.22.0" + }, + "dependencies": {} +} diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62b24c01b..446ea175c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -77,7 +77,7 @@ def resolve_all_supported_node_versions(options = {}) end def version_supports_metrics(version) - SemVersion.new(version).satisfies?('>= 10.0.0') && SemVersion.new(version).satisfies?('< 20.0.0') + SemVersion.new(version).satisfies?('>= 10.0.0') && SemVersion.new(version).satisfies?('< 21.0.0') end def get_test_versions @@ -86,7 +86,7 @@ def get_test_versions elsif ENV['TEST_ALL_NODE_VERSIONS'] == 'true' versions = resolve_all_supported_node_versions() else - versions = resolve_node_version(['14.x', '16.x', '18.x', '19.x']) + versions = resolve_node_version(['14.x', '16.x', '18.x', '19.x', '20.x']) end puts("Running tests for Node versions: #{versions.join(', ')}") versions