Skip to content

Commit

Permalink
Raise error and fail task if dependency list project property cannot …
Browse files Browse the repository at this point in the history
…be updated [when configured] (#1379)

* Report the total number of failed update "tasks" (i.e. outputs that could not be processed), not just the number of failed update jobs

* When the project properties cannot be updated, bubble up the error and fail the task;

* Reduce code duplication
  • Loading branch information
rhyskoedijk authored Oct 14, 2024
1 parent a7d39bd commit 9c91ef0
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 33 deletions.
86 changes: 57 additions & 29 deletions extension/tasks/dependabotV2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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...');
Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand All @@ -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}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ export class AzureDevOpsWebApiClient {
projectId: string,
name: string,
valueBuilder: (existingValue: string) => string,
): Promise<void> {
): Promise<boolean> {
try {
// Get the existing project property value
const core = await this.connection.getCoreApi();
Expand All @@ -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;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down

0 comments on commit 9c91ef0

Please sign in to comment.