diff --git a/Tasks/PowerShellV2/task.json b/Tasks/PowerShellV2/task.json index cd977ef0b70f..42414b262ad4 100644 --- a/Tasks/PowerShellV2/task.json +++ b/Tasks/PowerShellV2/task.json @@ -262,4 +262,4 @@ "PS_InvalidTargetType": "Invalid target type '{0}'. The value must be one of: 'filepath' or 'inline'", "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } -} \ No newline at end of file +} diff --git a/ci/build-all-steps.yml b/ci/build-all-steps.yml index 6dd421579624..ca032b3f04d8 100644 --- a/ci/build-all-steps.yml +++ b/ci/build-all-steps.yml @@ -106,6 +106,15 @@ steps: ) displayName: Push release branch +- script: node ./ci/check-dependencies.js + displayName: Check dependencies for updates + condition: | + and( + succeeded(), + eq(variables['build.reason'], 'PullRequest'), + eq(variables['System.PullRequest.TargetBranch'], 'master') + ) + - script: node ./ci/check-downgrading.js --task "$(task_pattern_fordowngradingcheck)" --sprint $(currentSprint) --week $(currentSprintWeek) displayName: Check for downgrading tasks # remove SourceBranch condition after merging users/merlynop/node20merge-2 ; see https://github.com/microsoft/azure-pipelines-tasks/pull/20819 diff --git a/ci/check-dependencies.js b/ci/check-dependencies.js new file mode 100644 index 000000000000..51855b41fa8b --- /dev/null +++ b/ci/check-dependencies.js @@ -0,0 +1,62 @@ +const { execSync } = require('node:child_process'); +const { readFileSync } = require('node:fs'); +const { get } = require('node:https'); +const { resolve } = require('node:path'); +const { styleText } = require('node:util'); + +const TASKS_PATH = 'Tasks/'; +const skippedDependencies = [ + "@types/", + "typescript" +]; + +async function checkAvailableDependencies() { + const tasks = new Set(execSync('git diff --name-only origin/master').toString('utf-8').split('\n').filter(x => x.includes(TASKS_PATH)).map(x => x.split('/')[1])); + const outdatedDependencies = []; + + console.log('Found tasks:', tasks.size); + let i = 0; + + for (const task of tasks) { + i++; + console.log(`\x1b[A\x1b[KChecking task ${i}/${tasks.size}: ${task}`); + + const { dependencies = {}, devDependencies = {} } = JSON.parse(readFileSync(resolve(TASKS_PATH, task, "package.json"))); + const allDeps = { ...dependencies, ...devDependencies }; + + for (const [dependency, currentVersion] of Object.entries(allDeps)) { + if (skippedDependencies.find(x => dependency.includes(x))) continue; + + const latestVersion = await getLatestVersion(dependency); + + if (latestVersion && latestVersion !== currentVersion.replace(/^[^\d]*/, '')) { + outdatedDependencies.push({ + task, + dependency, + currentVersion, + latestVersion + }); + } + } + } + + if (process.env["TF_BUILD"] == "True" && outdatedDependencies.length > 0) { + console.log('##vso[task.logissue type=warning]Found the outdated dependencies in changed tasks. Please see the warnings in the "Check dependencies for updates" task'); + } + + outdatedDependencies.forEach(x => { + if (process.env["TF_BUILD"] == "True") { + console.log(`##vso[task.logissue type=warning]The task "${x.task}" has dependency "${x.dependency}" that can be updated from ${x.currentVersion} to ${x.latestVersion}`); + } else { + console.warn(styleText('yellow', `Warning: The task "${x.task}" has dependency "${x.dependency}" that can be updated from ${styleText('red', x.currentVersion)} to ${styleText('greenBright', x.latestVersion)}`)); + } + }); +} + +async function getLatestVersion(dependency) { + const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(dependency)}/latest`); + const json = await response.json(); + return json.version; +} + +checkAvailableDependencies(); \ No newline at end of file