Skip to content

Commit d6157e0

Browse files
feat(node): support node 20 in tasks (#581)
Addresses #566. Use Node 20 using microsoft guidance: https://learn.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#q-how-can-i-upgrade-my-custom-task-to-the-latest-node Keep Node 10 as a fallback. However, we are still building the node10 with esbuild to ensure backwards compatibility. In the future, we can consider building for a higher node version only. Adding support requires a hacky patch to fix shell.js, which doesn't support bundlers. Mitigates the issue described in #539 Credit to ivanduplenskikh: https://github.com/ivanduplenskikh/aws-toolkit-azure-devops/pull/1 Co-authored-by: Ivan Duplenskikh <115665590+ivanduplenskikh@users.noreply.github.com>
1 parent f4b9ae1 commit d6157e0

File tree

26 files changed

+292
-308
lines changed

26 files changed

+292
-308
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Tasks now support Node 20"
4+
}

.vscode/settings.json

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
{
2-
"files.exclude": {
3-
"build": true,
4-
"package": true,
5-
"package-lock.json": true,
6-
".vscode": true
7-
},
2+
"files.exclude": {},
83
"search.exclude": {
9-
"build": true,
10-
"package": true,
11-
".vscode": true
4+
"package": true
125
},
136
"json.schemas": [
147
{

build-scripts/packageExtension.ts

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,54 @@ interface CommandLineOptions {
2121
publisher?: string
2222
}
2323

24+
/**
25+
* Patch shelljs, because it is not compatible with esbuild
26+
* More info: https://github.com/aws/aws-toolkit-azure-devops/pull/539
27+
*/
28+
class ShelljsPatch {
29+
private static readonly shelljsRootPath = path.resolve(process.cwd(), './node_modules/shelljs/')
30+
private static readonly shelljsEntryPath = path.join(ShelljsPatch.shelljsRootPath, 'shell.js')
31+
32+
private originalContent: string | undefined
33+
34+
patch() {
35+
const sourceRequireString =
36+
"require('./commands').forEach(function (command) {\n require('./src/' + command);\n});"
37+
const originalContent = fs.readFileSync(ShelljsPatch.shelljsEntryPath, 'utf-8')
38+
39+
// eslint-disable-next-line @typescript-eslint/no-var-requires
40+
const fixedRequireString = require(path.join(ShelljsPatch.shelljsRootPath, 'commands.js'))
41+
.map((command: string) => `require('./src/${command}.js');`)
42+
.join('\n')
43+
44+
const patchedContent = originalContent.replace(sourceRequireString, fixedRequireString)
45+
if (originalContent === patchedContent) {
46+
throw new Error('Could not patch shelljs, was this npm package updated?')
47+
}
48+
49+
fs.writeFileSync(ShelljsPatch.shelljsEntryPath, patchedContent)
50+
51+
this.originalContent = originalContent
52+
}
53+
54+
unpatch() {
55+
if (this.originalContent) {
56+
fs.writeFileSync(ShelljsPatch.shelljsEntryPath, this.originalContent)
57+
this.originalContent = undefined
58+
}
59+
}
60+
}
61+
const shelljsPatch = new ShelljsPatch()
62+
2463
function findMatchingFiles(directory: string) {
2564
return fs.readdirSync(directory)
2665
}
2766

2867
function installNodePackages(directory: string) {
2968
fs.mkdirpSync(directory)
3069
const npmCmd = `npm install --prefix ${directory} azure-pipelines-task-lib --only=production`
31-
try {
32-
const output = ncp.execSync(npmCmd)
33-
console.log(output.toString('utf8'))
34-
} catch (err) {
35-
console.error(err.output ? err.output.toString() : err?.message)
36-
process.exit(1)
37-
}
70+
const output = ncp.execSync(npmCmd)
71+
console.log(output.toString('utf8'))
3872
}
3973

4074
function generateGitHashFile() {
@@ -69,6 +103,8 @@ function packagePlugin(options: CommandLineOptions) {
69103
// get required npm packages that will be copied
70104
installNodePackages(npmFolder)
71105

106+
shelljsPatch.patch()
107+
72108
// clean, dedupe and pack each task as needed
73109
findMatchingFiles(folders.sourceTasks).forEach(function(taskName) {
74110
console.log('Processing task ' + taskName)
@@ -111,22 +147,19 @@ function packagePlugin(options: CommandLineOptions) {
111147
const inputFilename = path.join(taskBuildFolder, taskName + '.runner.js')
112148

113149
console.log('packing node-based task')
114-
try {
115-
const result = esbuild.buildSync({
116-
entryPoints: [inputFilename],
117-
bundle: true,
118-
platform: 'node',
119-
target: ['node10'],
120-
minify: true,
121-
outfile: `${taskPackageFolder}/${taskName}.js`
122-
})
123-
result.warnings.forEach(warning => console.log(warning))
124-
} catch (err) {
125-
console.error(err.output ? err.output.toString() : err.message)
126-
process.exit(1)
127-
}
150+
const result = esbuild.buildSync({
151+
entryPoints: [inputFilename],
152+
bundle: true,
153+
platform: 'node',
154+
target: ['node10'],
155+
minify: true,
156+
outfile: `${taskPackageFolder}/${taskName}.js`
157+
})
158+
result.warnings.forEach(warning => console.log(warning))
128159
})
129160

161+
shelljsPatch.unpatch()
162+
130163
console.log('Creating deployment vsix')
131164

132165
const binName = os.platform() === 'win32' ? `tfx.cmd` : 'tfx'
@@ -156,5 +189,12 @@ const parsedOptions: CommandLineOptions = {}
156189
if (commandLineInput.length > 0 && commandLineInput[0].split('=')[0] === 'publisher') {
157190
parsedOptions.publisher = commandLineInput[0].split('=')[1]
158191
}
159-
packagePlugin(parsedOptions)
192+
193+
try {
194+
packagePlugin(parsedOptions)
195+
} catch (e) {
196+
shelljsPatch.unpatch()
197+
throw e
198+
}
199+
160200
console.timeEnd(timeMessage)

0 commit comments

Comments
 (0)