Skip to content
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

Add node 20 support to task-lib for testing and pick highest node version for tests #1039

Merged
merged 6 commits into from
May 27, 2024
57 changes: 24 additions & 33 deletions node/mock-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,21 +165,17 @@ export class MockTestRunner {
// Returns a path to node.exe with the correct version for this task (based on if its node10 or node)
private async getNodePath(nodeVersion?: number): Promise<string> {
const version: number = nodeVersion || this.getNodeVersion();

let downloadVersion: string;
switch (version) {
case 6:
downloadVersion = 'v6.17.1';
break;
case 10:
downloadVersion = 'v10.21.0';
break;
case 16:
downloadVersion = 'v16.13.0';
break;
default:
throw new Error('Invalid node version, must be 6, 10, or 16 (received ' + version + ')');
}
const versions = {
20: 'v20.13.1',
16: 'v16.20.2',
10: 'v10.24.1',
6: 'v6.17.1',
};

const downloadVersion: string = versions[version];
if (!downloadVersion) {
throw new Error('Invalid node version, must be 6, 10, 16 or 20 (received ' + version + ')');
}

// Install node in home directory if it isn't already there.
const downloadDestination: string = path.join(downloadDirectory, 'node' + version);
Expand All @@ -203,31 +199,26 @@ export class MockTestRunner {
const taskJsonContents = fs.readFileSync(taskJsonPath, { encoding: 'utf-8' });
const taskJson: object = JSON.parse(taskJsonContents);

let nodeVersionFound = false;
const execution: object = (
taskJson['execution']
|| taskJson['prejobexecution']
|| taskJson['postjobexecution']
);
const keys = Object.keys(execution);
for (let i = 0; i < keys.length; i++) {
if (keys[i].toLowerCase() == 'node16') {
// Prefer node 16 and return immediately.
return 16;
} else if (keys[i].toLowerCase() == 'node10') {
// Prefer node 10 and return immediately.
return 10;
} else if (keys[i].toLowerCase() == 'node') {
nodeVersionFound = true;
let nodeVersion = 0;
const executors = ['execution', 'prejobexecution', 'postjobexecution'];
for (const executor of executors) {
if (!taskJson[executor]) continue;

for (const key of Object.keys(taskJson[executor])) {
const currExecutor = key.toLocaleLowerCase();
if (!currExecutor.startsWith('node')) continue;
const version = currExecutor.replace('node', '');
const intVersion = parseInt(version) || 6; // node handler is node v6 by default
nodeVersion = Math.max(intVersion, nodeVersion);
}
}

if (!nodeVersionFound) {
if (nodeVersion === 0) {
console.warn('Unable to determine execution type from task.json, defaulting to use Node 16');
return 16;
}

return 6;
return nodeVersion;
}

// Returns the path to the task.json for the task being tested. Returns null if unable to find it.
Expand Down
2 changes: 1 addition & 1 deletion node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "azure-pipelines-task-lib",
"version": "4.12.1",
"version": "4.13.0",
"description": "Azure Pipelines Task SDK",
"main": "./task.js",
"typings": "./task.d.ts",
Expand Down
3 changes: 3 additions & 0 deletions node/test/fakeTasks/node10task/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"id": "id",
"name": "Node10Task",
"execution": {
"Node": {
"target": "usedotnet.js"
},
"Node10": {
"target": "usedotnet.js"
}
Expand Down
6 changes: 6 additions & 0 deletions node/test/fakeTasks/node16task/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"id": "id",
"name": "Node16Task",
"execution": {
"Node": {
"target": "usedotnet.js"
},
"Node10": {
"target": "usedotnet.js"
},
"Node16": {
"target": "usedotnet.js"
}
Expand Down
18 changes: 18 additions & 0 deletions node/test/fakeTasks/node20task/task.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "id",
"name": "Node20Task",
"execution": {
"Node": {
"target": "usedotnet.js"
},
"Node10": {
"target": "usedotnet.js"
},
"Node16": {
"target": "usedotnet.js"
},
"Node20": {
"target": "usedotnet.js"
}
}
}
10 changes: 10 additions & 0 deletions node/test/mocktests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,16 @@ describe('Mock Tests', function () {
await Promise.resolve()
})

it('MockTest handles node 20 tasks correctly', async function () {
this.timeout(30000);
const runner = await (new mtm.MockTestRunner).LoadAsync(path.join(__dirname, 'fakeTasks', 'node20task', 'entry.js'));
const nodePath = runner.nodePath;
assert(nodePath, 'node path should have been correctly set');
const version = ncp.execSync(nodePath + ' -v').toString().trim();
assert(semver.satisfies(version, '20.x'), 'Downloaded node version should be Node 20 instead of ' + version);
await Promise.resolve()
})

it('MockTest handles node tasks correctly by async call', async () => {
this.timeout(30000);
const runner = await (new mtm.MockTestRunner).LoadAsync(path.join(__dirname, 'fakeTasks', 'node16task', 'entry.js'));
Expand Down