Repository navigation
feat!: add Node.js 24 support and remove Node.js 20 #565
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
ab254f9
feat: declare Node.js 24 support in engines
AndreLars 95a385d
feat: remove Node.js 20 from supported versions
AndreLars 722398c
feat: warn on deploy when configured runtime is unsupported
AndreLars a8c4b2a
refactor: use a deny list of deprecated Node.js versions
AndreLars 5962840
docs: note the deny list approach in the changeset
AndreLars File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| '@twilio-labs/serverless-twilio-runtime': major | ||
| 'create-twilio-function': major | ||
| '@twilio-labs/plugin-serverless': major | ||
| '@twilio-labs/plugin-assets': major | ||
| 'twilio-run': major | ||
| --- | ||
|
|
||
| **WHAT**: Add Node.js 24 to supported versions and remove Node.js 20. | ||
|
|
||
| **ALSO**: `twilio-run deploy` now warns when a project is configured to deploy to a deprecated runtime such as `node20`, pointing at the `runtime` field in `.twilioserverlessrc`. It is a warning, not a failure — the deploy still proceeds and Twilio Serverless has the final say. Both this check and the local Node.js version check use a deny list of deprecated versions rather than an allow list of supported ones, so an older release of the toolkit never claims a newer Node.js version is invalid. `printVersionWarning` drops its unused `expectedVersion` parameter as part of this. | ||
|
|
||
| **WHY**: `node24` was added as a deploy runtime and made the default in #555, but `engines.node` was never widened, so the toolkit defaulted to deploying to node24 while refusing to declare support for running on Node 24 (#563). Node.js 20 is deprecated on Twilio Serverless and can no longer be used for new builds, so it is removed at the same time. | ||
|
|
||
| **BREAKING CHANGE**: Projects using `create-twilio-function`, `@twilio-labs/plugin-assets`, `@twilio-labs/plugin-serverless`, `@twilio-labs/serverless-twilio-runtime` or `twilio-run` will have to migrate to Node.js 22 or 24. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 20 | ||
| 22 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
packages/twilio-run/__tests__/checks/deprecated-runtime.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import { mocked } from 'jest-mock'; | ||
| import { | ||
| checkForDeprecatedRuntime, | ||
| DEPRECATED_RUNTIMES, | ||
| } from '../../src/checks/deprecated-runtime'; | ||
| import { logger } from '../../src/utils/logger'; | ||
|
|
||
| jest.mock('../../src/utils/logger', () => { | ||
| return { | ||
| logger: { | ||
| warn: jest.fn(), | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| describe('checkForDeprecatedRuntime', () => { | ||
| beforeEach(() => { | ||
| mocked(logger.warn).mockClear(); | ||
| }); | ||
|
|
||
| test('should warn for every deprecated runtime', () => { | ||
| DEPRECATED_RUNTIMES.forEach((runtime) => { | ||
| expect(checkForDeprecatedRuntime(runtime)).toEqual(false); | ||
| }); | ||
| expect(logger.warn).toHaveBeenCalledTimes(DEPRECATED_RUNTIMES.length); | ||
| }); | ||
|
|
||
| test('should list every even major up to and including 20', () => { | ||
| expect(DEPRECATED_RUNTIMES).toEqual([ | ||
| 'node4', | ||
| 'node6', | ||
| 'node8', | ||
| 'node10', | ||
| 'node12', | ||
| 'node14', | ||
| 'node16', | ||
| 'node18', | ||
| 'node20', | ||
| ]); | ||
| }); | ||
|
|
||
| test('should name the configured runtime in the warning', () => { | ||
| checkForDeprecatedRuntime('node20'); | ||
| expect(mocked(logger.warn).mock.calls[0][0]).toContain('node20'); | ||
| }); | ||
|
|
||
| test('should not warn for currently supported runtimes', () => { | ||
| ['node22', 'node24'].forEach((runtime) => { | ||
| expect(checkForDeprecatedRuntime(runtime)).toEqual(true); | ||
| }); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('should not warn for runtimes newer than this release knows about', () => { | ||
| // An older toolkit must not claim a future runtime is invalid. | ||
| ['node26', 'node28', 'node30'].forEach((runtime) => { | ||
| expect(checkForDeprecatedRuntime(runtime)).toEqual(true); | ||
| }); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('should not warn when no runtime is configured', () => { | ||
| expect(checkForDeprecatedRuntime(undefined)).toEqual(true); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('should leave unrecognised values to the platform', () => { | ||
| expect(checkForDeprecatedRuntime('nope')).toEqual(true); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('should not exit the process', () => { | ||
| const exit = jest.spyOn(process, 'exit').mockImplementation(); | ||
| checkForDeprecatedRuntime('node20'); | ||
| expect(exit).not.toHaveBeenCalled(); | ||
| exit.mockRestore(); | ||
| }); | ||
| }); |
57 changes: 57 additions & 0 deletions
57
packages/twilio-run/__tests__/checks/nodejs-version.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { mocked } from 'jest-mock'; | ||
| import checkNodejsVersion from '../../src/checks/nodejs-version'; | ||
| import { logger } from '../../src/utils/logger'; | ||
|
|
||
| jest.mock('../../src/utils/logger', () => { | ||
| return { | ||
| logger: { | ||
| warn: jest.fn(), | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| const originalVersion = process.versions.node; | ||
|
|
||
| function setNodeVersion(version: string) { | ||
| Object.defineProperty(process.versions, 'node', { | ||
| value: version, | ||
| configurable: true, | ||
| }); | ||
| } | ||
|
|
||
| describe('checkNodejsVersion', () => { | ||
| beforeEach(() => { | ||
| mocked(logger.warn).mockClear(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| setNodeVersion(originalVersion); | ||
| }); | ||
|
|
||
| test('should warn on deprecated Node.js versions', () => { | ||
| ['20.11.1', '18.19.0', '16.20.2'].forEach((version) => { | ||
| mocked(logger.warn).mockClear(); | ||
| setNodeVersion(version); | ||
| checkNodejsVersion(); | ||
| expect(logger.warn).toHaveBeenCalledTimes(1); | ||
| expect(mocked(logger.warn).mock.calls[0][0]).toContain(version); | ||
| }); | ||
| }); | ||
|
|
||
| test('should not warn on currently supported Node.js versions', () => { | ||
| ['22.11.0', '24.0.0'].forEach((version) => { | ||
| setNodeVersion(version); | ||
| checkNodejsVersion(); | ||
| }); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('should not warn on Node.js versions newer than this release knows about', () => { | ||
| // An older toolkit must not tell users a newer Node.js version is invalid. | ||
| ['26.0.0', '28.1.0'].forEach((version) => { | ||
| setNodeVersion(version); | ||
| checkNodejsVersion(); | ||
| }); | ||
| expect(logger.warn).not.toHaveBeenCalled(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { stripIndent } from 'common-tags'; | ||
| import { logger } from '../utils/logger'; | ||
|
|
||
| /** | ||
| * Node.js major versions that are no longer accepted by Twilio Serverless. | ||
| * | ||
| * This is deliberately a deny list rather than an allow list of supported | ||
| * versions: a project pinned to an older version of the toolkit must not warn | ||
| * about a runtime that became valid after it was published. A deprecation is a | ||
| * settled fact, so an outdated deny list stays correct — at worst it misses a | ||
| * newer deprecation, in which case the platform still rejects the build. | ||
| */ | ||
| export const DEPRECATED_NODE_MAJORS = [4, 6, 8, 10, 12, 14, 16, 18, 20]; | ||
|
|
||
| export const DEPRECATED_RUNTIMES = DEPRECATED_NODE_MAJORS.map( | ||
| (major) => `node${major}` | ||
| ); | ||
|
|
||
| /** | ||
| * Warns if a project is configured to deploy to a runtime that Twilio | ||
| * Serverless no longer accepts for new builds, pointing at the field to | ||
| * change. Twilio Serverless remains the authority on which runtimes it | ||
| * accepts, so this only surfaces the problem earlier and never blocks a | ||
| * deploy. Unknown or future runtime values are left alone. | ||
| */ | ||
| export function checkForDeprecatedRuntime(runtime?: string): boolean { | ||
| if (typeof runtime !== 'string' || !DEPRECATED_RUNTIMES.includes(runtime)) { | ||
| return true; | ||
| } | ||
|
|
||
| const title = 'Deprecated Node.js Runtime Configured'; | ||
| const msg = stripIndent` | ||
| Your project is configured to deploy to "${runtime}", which is deprecated and is no longer accepted for new builds. | ||
|
|
||
| Please update the "runtime" field in your .twilioserverlessrc (or pass the --runtime flag) to a version that is still supported. | ||
| `; | ||
|
|
||
| logger.warn(msg, title); | ||
|
|
||
| return false; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't there be a risk of divergence if someone is using toolkit programmatically and doesn't update to the latest version where supported runtimes are changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like it should be checked server-side?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. when we add node26, the toolkit will tell users their valid runtime "is not supported" Worth double-checking this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can invert and use a denylist instead of allowlist, we don't need to keep it updated in order to allow new runtimes, if a runtime is deprecated it will be checked server-side anyways, so keeping it updated is less critical