-
Notifications
You must be signed in to change notification settings - Fork 34
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
skuba migrate node 22 #1735
base: main
Are you sure you want to change the base?
skuba migrate node 22 #1735
Changes from 2 commits
72c0a9f
c7be1a5
ca66785
14dc05c
2176a05
575eaea
ec85772
178c02f
8d2a75b
99be3a9
b165e28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,12 +14,45 @@ type SubPatch = ( | |
replace: string; | ||
}; | ||
|
||
type VersionResult = { | ||
version: string; | ||
err: string | undefined; | ||
}; | ||
|
||
type RegistryResponse = { | ||
'dist-tags': Record<string, string>; | ||
}; | ||
|
||
export const getLatestNode22Types = async (): Promise<VersionResult> => { | ||
const FALLBACK_VERSION = '22.9.0'; | ||
const url = 'https://registry.npmjs.org/@types/node'; | ||
const headers = { | ||
accept: | ||
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*', | ||
}; | ||
|
||
try { | ||
const fetchResponse = await fetch(url, { headers }); | ||
const jsonResponse = (await fetchResponse.json()) as RegistryResponse; | ||
const latestNode22 = jsonResponse['dist-tags'].node22; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does a node22 dist tag exist..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As it turns out... no 😢 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Swapped it for your suggestion of |
||
|
||
return { version: latestNode22 ?? FALLBACK_VERSION, err: undefined }; | ||
} catch { | ||
return { | ||
version: FALLBACK_VERSION, | ||
err: 'Failed to fetch latest version, using fallback version', | ||
}; | ||
} | ||
}; | ||
|
||
const SHA_REGEX = /(?<=node.*)(@sha256:[a-f0-9]{64})/gm; | ||
|
||
const subPatches: SubPatch[] = [ | ||
{ file: '.nvmrc', replace: '<%- version %>\n' }, | ||
{ | ||
files: 'Dockerfile*', | ||
test: /^FROM(.*) node:[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?( .+|)$/gm, | ||
replace: 'FROM$1 node:<%- version %>$3$4', | ||
test: /^FROM(.*) (public.ecr.aws\/docker\/library\/)?node:[0-9.]+(@sha256:[a-f0-9]{64})?(\.[^- \n]+)?(-[^ \n]+)?( .+|)$/gm, | ||
replace: 'FROM$1 $2node:<%- version %>$3$5$6', | ||
}, | ||
{ | ||
files: 'Dockerfile*', | ||
|
@@ -37,13 +70,50 @@ const subPatches: SubPatch[] = [ | |
replace: 'NODEJS_<%- version %>_X', | ||
}, | ||
{ | ||
files: '.buildkite/*', | ||
test: /image: node:[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm, | ||
replace: 'image: node:<%- version %>$2', | ||
files: '**/.buildkite/*', | ||
test: /image: (public.ecr.aws\/docker\/library\/)?node:[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm, | ||
replace: 'image: $1node:<%- version %>$3', | ||
}, | ||
{ | ||
files: '.node-version*', | ||
test: /(v)?\d+\.\d+\.\d+(.+)?/gm, | ||
replace: '$1<%- version %>$2', | ||
}, | ||
{ | ||
files: '**/package.json', | ||
test: /"@types\/node": "(\^)?[0-9.]+"/gm, | ||
replace: '"@types/node": "$1<%- version %>"', | ||
}, | ||
{ | ||
files: '**/package.json', | ||
test: /("engines":\s*{[^}]*"node":\s*">=)(\d+)("[^}]*})(?![^}]*"skuba":\s*{[^}]*"type":\s*"package")/gm, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This regex is doing what |
||
replace: '$1<%- version %>$3', | ||
}, | ||
{ | ||
files: '**/tsconfig.json', | ||
test: /("target":\s*")(ES?:[0-9]+|Next|[A-Za-z]+[0-9]*)"/gim, | ||
replace: '$1<%- version %>"', | ||
}, | ||
{ | ||
files: '**/docker-compose*.y*ml', | ||
test: /image: (public.ecr.aws\/docker\/library\/)?node:[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm, | ||
replace: 'image: $1node:<%- version %>$3', | ||
}, | ||
]; | ||
|
||
const runSubPatch = async (version: number, dir: string, patch: SubPatch) => { | ||
const removeNodeShas = (content: string): string => | ||
content.replace(SHA_REGEX, ''); | ||
|
||
type Versions = { | ||
nodeVersion: number; | ||
nodeTypesVersion: string; | ||
}; | ||
|
||
const runSubPatch = async ( | ||
{ nodeVersion, nodeTypesVersion }: Versions, | ||
dir: string, | ||
patch: SubPatch, | ||
) => { | ||
const readFile = createDestinationFileReader(dir); | ||
const paths = patch.file | ||
? [patch.file] | ||
|
@@ -60,22 +130,44 @@ const runSubPatch = async (version: number, dir: string, patch: SubPatch) => { | |
return; | ||
} | ||
|
||
const templated = patch.replace.replaceAll( | ||
'<%- version %>', | ||
version.toString(), | ||
); | ||
const unPinnedContents = removeNodeShas(contents); | ||
|
||
let templated: string; | ||
if ( | ||
path.includes('package.json') && | ||
patch.replace.includes('@types/node') | ||
) { | ||
templated = patch.replace.replaceAll( | ||
'<%- version %>', | ||
nodeTypesVersion, | ||
); | ||
} else if (path.includes('tsconfig.json')) { | ||
templated = patch.replace.replaceAll('<%- version %>', 'ES2024'); | ||
} else { | ||
templated = patch.replace.replaceAll( | ||
'<%- version %>', | ||
nodeVersion.toString(), | ||
); | ||
} | ||
|
||
await fs.promises.writeFile( | ||
path, | ||
patch.test ? contents.replaceAll(patch.test, templated) : templated, | ||
patch.test | ||
? unPinnedContents.replaceAll(patch.test, templated) | ||
: templated, | ||
); | ||
}), | ||
); | ||
}; | ||
|
||
const upgrade = async (version: number, dir: string) => { | ||
const upgrade = async ( | ||
{ nodeVersion, nodeTypesVersion }: Versions, | ||
dir: string, | ||
) => { | ||
await Promise.all( | ||
subPatches.map((subPatch) => runSubPatch(version, dir, subPatch)), | ||
subPatches.map((subPatch) => | ||
runSubPatch({ nodeVersion, nodeTypesVersion }, dir, subPatch), | ||
), | ||
); | ||
}; | ||
|
||
|
@@ -85,7 +177,11 @@ export const nodeVersionMigration = async ( | |
) => { | ||
log.ok(`Upgrading to Node.js ${version}`); | ||
try { | ||
await upgrade(version, dir); | ||
const { version: nodeTypesVersion, err } = await getLatestNode22Types(); | ||
if (err) { | ||
log.warn(err); | ||
} | ||
await upgrade({ nodeVersion: version, nodeTypesVersion }, dir); | ||
log.ok('Upgraded to Node.js', version); | ||
} catch (err) { | ||
log.err('Failed to upgrade'); | ||
|
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.
I feel like it could be fine to keep 20 around just for purposes of people doing a 2-step upgrade? 🤷 maybe not