Skip to content

Commit

Permalink
Add Pr check for agent-base (#20195)
Browse files Browse the repository at this point in the history
* Add Pr check for agent-base

- added pr checks for agent-base
- added troubleshooting section to the docs

* Add Pr check for agent-base

- Merged two method in one
- Fixed powershell test error
  • Loading branch information
DmitriiBobreshev authored Jul 29, 2024
1 parent 70b4018 commit ec83c35
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 21 deletions.
90 changes: 70 additions & 20 deletions ci/after-build-check-tasks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const util = require('./ci-util');
const fs = require('fs');
const path = require('path');
var crypto = require('crypto');
const semver = require('semver');

const fileToJson = util.fileToJson;
const buildTasksPath = util.buildTasksPath;
const GITHUB_LINK = 'https://github.com/microsoft/azure-pipelines-tasks/blob/master/docs/migrateNode16.md';
const logToPipeline = util.logToPipeline;
const GITHUB_LINK = 'https://github.com/microsoft/azure-pipelines-tasks/blob/master/docs/validation-errors.md';

/**
* Function walking through directories and looking for
Expand Down Expand Up @@ -37,17 +38,18 @@ function findLib(dirPath, libRegExp) {

/**
* Function iterates over the given array to find
* which tasks have multiple task lib packages
* @param {Array} scanningTask
* which tasks have package in node_modules
* @param {Array} scanningTask - array of tasks
* @param {RegExp} regExp - regular expression to find package
* @param {Boolean} includeAll - flag to include all founded packages
* @returns Array<Object>
*/
function findWithFsFromPaths(scanningTask) {
function findPackageUsingRegExp(scanningTask, regExp, includeAll = false) {
const foundedTasks = [];
for (let task of scanningTask) {
const taskPath = task.taskPath
const reg = new RegExp('azure-pipelines-task-lib')
const result = findLib(path.join(taskPath, 'node_modules'), reg);
if (result.length > 1) {
const result = findLib(path.join(taskPath, 'node_modules'), regExp);
if ((!includeAll && result.length > 1) || includeAll) {
const foundedPaths = result.map((path) => path.replace(buildTasksPath, ''));
foundedTasks.push({
task: task.taskName,
Expand Down Expand Up @@ -75,11 +77,10 @@ function isNodeHandlerExists(taskDefinition) {
}

/**
* Function looking for multiple azure-pipelines-task-lib versions
* in builded tasks, in case if package found multiple times throw error
* Note: now function compares only for tasks which have Node10 and Node16 in their task.json
* Function to get all built tasks
* @returns {Array<Tasks>} - array of tasks with path and versions
*/
function findNonUniqueTaskLib() {
function getBuiltTasks() {
const taskPaths = fs.readdirSync(buildTasksPath, { encoding: 'utf-8' })
const scanningTasks = [];
for (let taskName of taskPaths) {
Expand All @@ -105,33 +106,82 @@ function findNonUniqueTaskLib() {
});
}

const haveDependencies = findWithFsFromPaths(scanningTasks);
return scanningTasks;
}

/**
* Function looking for multiple azure-pipelines-task-lib versions
* in builded tasks, in case if package found multiple times throw error
* Note: now function compares only for tasks which have Node10 and Node16 in their task.json
*/
function findNonUniqueTaskLib() {
const taskLibSection = "#findnonuniquetasklib-section"
const scanningTasks = getBuiltTasks();
const reg = new RegExp('azure-pipelines-task-lib')
const haveDependencies = findPackageUsingRegExp(scanningTasks, reg, false);
if (haveDependencies.length > 0) {
console.log(`##vso[task.logissue type=error;sourcepath=ci/check-tasks.js;linenumber=109;]The following tasks have duplicate azure-pipelines-task-lib:
${JSON.stringify(haveDependencies, null, 2)}
Please examine the following link: ${GITHUB_LINK}`);
logToPipeline('error', `The following tasks have duplicate azure-pipelines-task-lib:\n${JSON.stringify(haveDependencies, null, 2)}`);
logToPipeline('error', `Please examine the following link: ${GITHUB_LINK + taskLibSection}`);
process.exit(1);
}

console.log('No duplicates found.');
logToPipeline('info', 'No duplicates found');
return null;
}

function analyzePowershellTasks() {
let output = '';
if (process.platform !== 'win32') {
console.log('The powershell check is only supported on Windows. Skipping...');
logToPipeline('info', 'The powershell check is only supported on Windows. Skipping...');
return;
}

try {
const pwshScriptPath = path.join(__dirname, 'check-powershell-syntax.ps1');
output = util.run(`powershell -NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted ${pwshScriptPath} ${buildTasksPath}`, true);
} catch (e) {
console.log(`##vso[task.logissue type=error;sourcepath=ci/check-tasks.js;linenumber=123;]Please check the tasks, seems like they have invalid PowerShell syntax.`)
logToPipeline('error', 'Please check the tasks, seems like they have invalid PowerShell syntax.');
process.exit(1);
}
}

function findIncompatibleAgentBase() {
const minAgentBaseVersion = '6.0.2';
const agentBaseSection = "#findincompatibleagentbase-section"
const scanningTasks = getBuiltTasks();
const reg = new RegExp('agent-base')
const agentBaseTasks = findPackageUsingRegExp(scanningTasks, reg, true);
const errors = [];

for (const { task, locations } of agentBaseTasks) {
if (!locations.length) continue;

for (const agentBasePath of locations) {
const packagePath = path.join(buildTasksPath, agentBasePath, 'package.json');
if (!fs.existsSync(packagePath)) {
logToPipeline('warning', `The following task has no package.json file: ${task}`);
continue;
}

const agentBaseVersion = fileToJson(packagePath).version;
if (semver.lt(agentBaseVersion, minAgentBaseVersion)) {
errors.push({ task, agentBasePath, agentBaseVersion });
}
}
}

if (errors.length) {
logToPipeline('warning', `The following tasks have incompatible agent-base versions, please use agent-base >= ${minAgentBaseVersion}:\n${JSON.stringify(errors, null, 2)}`);
logToPipeline('error', `Please examine the following link: ${GITHUB_LINK + agentBaseSection}`);
process.exit(1);
}
}



// logToPipeline("section", "Start findNonUniqueTaskLib")
// findNonUniqueTaskLib();
analyzePowershellTasks();
// logToPipeline("section", "Start analyzePowershellTasks")
// analyzePowershellTasks();
logToPipeline("section", "Start findIncompatibleAgentBase")
findIncompatibleAgentBase();
2 changes: 1 addition & 1 deletion ci/build-all-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ steps:
condition: and(succeeded(), ne(variables['numTasks'], 0))

- script: node ./ci/after-build-check-tasks.js
displayName: Check that tasks has no duplicated libs
displayName: After build tasks validation
condition: |
and(
succeeded(),
Expand Down
5 changes: 5 additions & 0 deletions ci/check-powershell-syntax.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ function main() {
(Check-PowershellHandler $_.FullName) -eq $true
}

if ($tasks.Count -eq 0) {
Write-Host "No PowerShell handler found in the tasks."
exit 0;
}

$analyzerResults = Check-Tasks -taskPaths $tasks.FullName;

return $analyzerResults;
Expand Down
2 changes: 2 additions & 0 deletions ci/ci-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@ function logToPipeline(type, payload) {
case 'debug':
console.log(`##vso[task.debug]${payload}`);
break;
case 'section':
console.log(`##[section]${payload}`);
default:
console.log(payload);
}
Expand Down
43 changes: 43 additions & 0 deletions docs/validation-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Validation Errors in the pipeline

## findNonUniqueTaskLib section
The check is looking for duplicate usage of [azure-pipelines-task-lib](https://www.npmjs.com/package/azure-pipelines-task-lib).\
The `azure-pipelines-task-lib` was designed as a singleton and there might be errors if the task uses different package versions.

If you have common npm packages as the task dependency, make sure the all dependencies have same version of `azure-pipelines-task-lib` in the task.\
As a possible solution you also can remove these package versions through the `make.json` file, for example:

```json
{
"rm": [
{
"items": [
"node_modules/azure-pipelines-tasks-java-common/node_modules/azure-pipelines-task-lib",
],
"options": "-Rf"
}
]
}
```

## findIncompatibleAgentBase section
The checks will throws error if the [agent-base](https://www.npmjs.com/package/agent-base) package with version below 6.0.2 was found. \
Usually this package comes with `azure-pipelines-tasks-azure-arm-rest` package.

The `agent-base` package below 6.0.2 does not work with node 10+ and the task will fail if the cx will try to use it with proxy. \
To fix the check you need to upgrade it on 6.0.2 at least, for it, please, upgrade your common packages for a new version(if avaiable). \
Another option is to install agent-base v6+ in the task and remove existing one using the `make.json` file using path from the error.

```json
{
"rm": [
{
"items": [
"node_modules/https-proxy-agent/node_modules/agent-base",
"node_modules/azure-pipelines-tasks-azure-arm-rest/node_modules/agent-base"
],
"options": "-Rf"
}
]
}
```

0 comments on commit ec83c35

Please sign in to comment.