diff --git a/extension/tasks/dependabotV2/index.ts b/extension/tasks/dependabotV2/index.ts index 94c4c87f..99df8445 100644 --- a/extension/tasks/dependabotV2/index.ts +++ b/extension/tasks/dependabotV2/index.ts @@ -7,13 +7,14 @@ import { parseProjectDependencyListProperty, parsePullRequestProperties, } from './utils/dependabot-cli/DependabotOutputProcessor'; +import { IDependabotUpdateOperationResult } from './utils/dependabot-cli/interfaces/IDependabotUpdateOperationResult'; import { IDependabotUpdate } from './utils/dependabot/interfaces/IDependabotConfig'; import parseDependabotConfigFile from './utils/dependabot/parseConfigFile'; import parseTaskInputConfiguration from './utils/getSharedVariables'; async function run() { let dependabot: DependabotCli = undefined; - let failedJobs: number = 0; + let failedTasks: number = 0; try { // Check if required tools are installed debug('Checking for `docker` install...'); @@ -99,38 +100,38 @@ async function run() { const existingPullRequestDependencies = Object.entries(existingPullRequests).map(([id, deps]) => deps); // Run an update job for "all dependencies"; this will create new pull requests for dependencies that need updating - const allDependenciesJob = DependabotJobBuilder.newUpdateAllJob( - taskInputs, - updateId, - update, - dependabotConfig.registries, - dependencyList?.['dependencies'], - existingPullRequestDependencies, + failedTasks += handleUpdateOperationResults( + await dependabot.update( + DependabotJobBuilder.newUpdateAllJob( + taskInputs, + updateId, + update, + dependabotConfig.registries, + dependencyList?.['dependencies'], + existingPullRequestDependencies, + ), + dependabotUpdaterOptions, + ), ); - const allDependenciesUpdateOutputs = await dependabot.update(allDependenciesJob, dependabotUpdaterOptions); - if (!allDependenciesUpdateOutputs || allDependenciesUpdateOutputs.filter((u) => !u.success).length > 0) { - allDependenciesUpdateOutputs?.filter((u) => !u.success)?.forEach((u) => exception(u.error)); - failedJobs++; - } // If there are existing pull requests, run an update job for each one; this will resolve merge conflicts and close pull requests that are no longer needed const numberOfPullRequestsToUpdate = Object.keys(existingPullRequests).length; if (numberOfPullRequestsToUpdate > 0) { if (!taskInputs.skipPullRequests) { for (const pullRequestId in existingPullRequests) { - const updatePullRequestJob = DependabotJobBuilder.newUpdatePullRequestJob( - taskInputs, - pullRequestId, - update, - dependabotConfig.registries, - existingPullRequestDependencies, - existingPullRequests[pullRequestId], + failedTasks += handleUpdateOperationResults( + await dependabot.update( + DependabotJobBuilder.newUpdatePullRequestJob( + taskInputs, + pullRequestId, + update, + dependabotConfig.registries, + existingPullRequestDependencies, + existingPullRequests[pullRequestId], + ), + dependabotUpdaterOptions, + ), ); - const updatePullRequestOutputs = await dependabot.update(updatePullRequestJob, dependabotUpdaterOptions); - if (!updatePullRequestOutputs || updatePullRequestOutputs.filter((u) => !u.success).length > 0) { - updatePullRequestOutputs?.filter((u) => !u.success)?.forEach((u) => exception(u.error)); - failedJobs++; - } } } else { warning( @@ -141,10 +142,10 @@ async function run() { } setResult( - failedJobs ? TaskResult.Failed : TaskResult.Succeeded, - failedJobs - ? `${failedJobs} update job(s) failed, check logs for more information` - : `All update jobs completed successfully`, + failedTasks ? TaskResult.Failed : TaskResult.Succeeded, + failedTasks + ? `${failedTasks} update tasks(s) failed, check logs for more information` + : `All update tasks completed successfully`, ); } catch (e) { setResult(TaskResult.Failed, e?.message); @@ -154,6 +155,33 @@ async function run() { } } +/** + * Handles the results of an update operation. + * @param outputs The processed outputs of the update operation. + * @returns The number of failed tasks (i.e. outputs that could not be processed). + * @remarks + * If the update operation completed with all outputs processed successfully, it will return 0. + * If the update operation completed with no outputs, it will return 1. + * If the update operation completed with some outputs processed unsuccessfully, it will return the number of failed outputs. + */ +function handleUpdateOperationResults(outputs: IDependabotUpdateOperationResult[] | undefined) { + let failedTasks = 0; // assume success, initially + if (outputs) { + // The update operation completed, but some output tasks may have failed + const failedUpdateTasks = outputs.filter((u) => !u.success); + if (failedUpdateTasks.length > 0) { + // At least one output task failed to process + failedUpdateTasks.forEach((u) => exception(u.error)); + failedTasks += failedUpdateTasks.length; + } + } else { + // The update operation critically failed, it produced no output + failedTasks++; + } + + return failedTasks; +} + function exception(e: Error) { if (e) { error(`An unhandled exception occurred: ${e}`); diff --git a/extension/tasks/dependabotV2/utils/azure-devops/AzureDevOpsWebApiClient.ts b/extension/tasks/dependabotV2/utils/azure-devops/AzureDevOpsWebApiClient.ts index a01362c2..3d1c644d 100644 --- a/extension/tasks/dependabotV2/utils/azure-devops/AzureDevOpsWebApiClient.ts +++ b/extension/tasks/dependabotV2/utils/azure-devops/AzureDevOpsWebApiClient.ts @@ -499,7 +499,7 @@ export class AzureDevOpsWebApiClient { projectId: string, name: string, valueBuilder: (existingValue: string) => string, - ): Promise { + ): Promise { try { // Get the existing project property value const core = await this.connection.getCoreApi(); @@ -514,9 +514,12 @@ export class AzureDevOpsWebApiClient { value: valueBuilder(propertyValue || ''), }, ]); + + return true; } catch (e) { error(`Failed to update project property '${name}': ${e}`); console.debug(e); // Dump the error stack trace to help with debugging + return false; } } } diff --git a/extension/tasks/dependabotV2/utils/dependabot-cli/DependabotOutputProcessor.ts b/extension/tasks/dependabotV2/utils/dependabot-cli/DependabotOutputProcessor.ts index 97ab9d00..0415b3b9 100644 --- a/extension/tasks/dependabotV2/utils/dependabot-cli/DependabotOutputProcessor.ts +++ b/extension/tasks/dependabotV2/utils/dependabot-cli/DependabotOutputProcessor.ts @@ -60,8 +60,8 @@ export class DependabotOutputProcessor implements IDependabotUpdateOutputProcess case 'update_dependency_list': // Store the dependency list snapshot in project properties, if configured if (this.taskInputs.storeDependencyList) { - console.info(`Storing the dependency list snapshot for project '${project}'...`); - await this.prAuthorClient.updateProjectProperty( + console.info(`Updating the dependency list snapshot for project '${project}'...`); + return await this.prAuthorClient.updateProjectProperty( this.taskInputs.projectId, DependabotOutputProcessor.PROJECT_PROPERTY_NAME_DEPENDENCY_LIST, function (existingValue: string) { @@ -76,7 +76,6 @@ export class DependabotOutputProcessor implements IDependabotUpdateOutputProcess return JSON.stringify(repoDependencyLists); }, ); - console.info(`Dependency list snapshot was updated for project '${project}'`); } return true;