Made "publish:github:pull-request" action idempotent

Signed-off-by: Bogdan Nechyporenko <bnechyporenko@bol.com>
This commit is contained in:
Bogdan Nechyporenko
2025-03-17 22:20:05 +01:00
parent 5f66007d58
commit 0ae0c774e9
4 changed files with 54 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-github': patch
---
Made "publish:github:pull-request" action idempotent
@@ -49,6 +49,7 @@
"@backstage/errors": "workspace:^",
"@backstage/integration": "workspace:^",
"@backstage/plugin-scaffolder-node": "workspace:^",
"@backstage/types": "workspace:^",
"@octokit/webhooks": "^10.9.2",
"libsodium-wrappers": "^0.7.11",
"octokit": "^3.0.0",
@@ -35,6 +35,7 @@ import {
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
import { Config } from '@backstage/config';
import { JsonValue } from '@backstage/types';
export type Encoding = 'utf-8' | 'base64';
@@ -425,18 +426,33 @@ export const createPublishGithubPullRequestAction = (
if (targetBranchName) {
createOptions.base = targetBranchName;
}
const response = await client.createPullRequest(createOptions);
if (createWhenEmpty === false && !response) {
const pr = await ctx.checkpoint({
key: `create.pr.${owner}.${repo}.${branchName}`,
fn: async () => {
const response = await client.createPullRequest(createOptions);
if (!response) {
return null;
}
return {
base: response?.data.base,
html_url: response?.data.html_url,
number: response?.data.number,
};
},
});
if (createWhenEmpty === false && !pr) {
ctx.logger.info('No changes to commit, pull request was not created');
return;
}
if (!response) {
if (!pr) {
throw new GithubResponseError('null response from Github');
}
const pullRequestNumber = response.data.number;
const pullRequestNumber = pr.number;
if (reviewers || teamReviewers) {
const pullRequest = { owner, repo, number: pullRequestNumber };
await requestReviewersOnPullRequest(
@@ -445,12 +461,13 @@ export const createPublishGithubPullRequestAction = (
teamReviewers,
client,
ctx.logger,
ctx.checkpoint,
);
}
const targetBranch = response.data.base.ref;
const targetBranch = pr.base.ref;
ctx.output('targetBranchName', targetBranch);
ctx.output('remoteUrl', response.data.html_url);
ctx.output('remoteUrl', pr.html_url);
ctx.output('pullRequestNumber', pullRequestNumber);
} catch (e) {
throw new GithubResponseError('Pull request creation failed', e);
@@ -464,20 +481,33 @@ export const createPublishGithubPullRequestAction = (
teamReviewers: string[] | undefined,
client: Octokit,
logger: LoggerService,
checkpoint: <T extends JsonValue | void>(opts: {
key: string;
fn: () => Promise<T> | T;
}) => Promise<T>,
) {
try {
const result = await client.rest.pulls.requestReviewers({
owner: pr.owner,
repo: pr.repo,
pull_number: pr.number,
reviewers,
team_reviewers: teamReviewers ? [...new Set(teamReviewers)] : undefined,
await checkpoint({
key: `request.reviewers.${pr.owner}.${pr.repo}.${pr.number}`,
fn: async () => {
const result = await client.rest.pulls.requestReviewers({
owner: pr.owner,
repo: pr.repo,
pull_number: pr.number,
reviewers,
team_reviewers: teamReviewers
? [...new Set(teamReviewers)]
: undefined,
});
const addedUsers = result.data.requested_reviewers?.join(', ') ?? '';
const addedTeams = result.data.requested_teams?.join(', ') ?? '';
logger.info(
`Added users [${addedUsers}] and teams [${addedTeams}] as reviewers to Pull request ${pr.number}`,
);
},
});
const addedUsers = result.data.requested_reviewers?.join(', ') ?? '';
const addedTeams = result.data.requested_teams?.join(', ') ?? '';
logger.info(
`Added users [${addedUsers}] and teams [${addedTeams}] as reviewers to Pull request ${pr.number}`,
);
} catch (e) {
logger.error(
`Failure when adding reviewers to Pull request ${pr.number}`,
+1
View File
@@ -7623,6 +7623,7 @@ __metadata:
"@backstage/integration": "workspace:^"
"@backstage/plugin-scaffolder-node": "workspace:^"
"@backstage/plugin-scaffolder-node-test-utils": "workspace:^"
"@backstage/types": "workspace:^"
"@octokit/webhooks": ^10.9.2
"@types/libsodium-wrappers": ^0.7.10
fs-extra: ^11.2.0