feat: Add `delete-branch` option to delete PR branches after closing (#190)

* feat: Add `delete-branch` option to delete PR branches after closing

* Fix branch ref
This commit is contained in:
Alex Brazier 2021-01-15 11:49:38 +00:00 committed by GitHub
parent 87c2b794b9
commit 107018c400
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 2 deletions

View File

@ -51,7 +51,8 @@ const DefaultProcessorOptions: IssueProcessorOptions = Object.freeze({
removeStaleWhenUpdated: false,
ascending: false,
skipStaleIssueMessage: false,
skipStalePrMessage: false
skipStalePrMessage: false,
deleteBranch: false
});
test('empty issue list results in 1 operation', async () => {
@ -89,6 +90,7 @@ test('processing an issue with no label will make it stale and close it, if it i
expect(processor.staleIssues.length).toEqual(1);
expect(processor.closedIssues.length).toEqual(1);
expect(processor.deletedBranchIssues.length).toEqual(0);
});
test('processing an issue with no label will make it stale and not close it if days-before-close is set to > 0', async () => {
@ -895,3 +897,59 @@ test('not providing stalePrMessage takes precedence over skipStalePrMessage', as
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
});
test('git branch is deleted when option is enabled', async () => {
const opts = {...DefaultProcessorOptions, deleteBranch: true};
const isPullRequest = true;
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should have its branch deleted',
'2020-01-01T17:00:00Z',
isPullRequest,
['Stale']
)
];
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
await processor.processIssues(1);
expect(processor.closedIssues.length).toEqual(1);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
expect(processor.deletedBranchIssues.length).toEqual(1);
});
test('git branch is not deleted when issue is not pull request', async () => {
const opts = {...DefaultProcessorOptions, deleteBranch: true};
const isPullRequest = false;
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should not have its branch deleted',
'2020-01-01T17:00:00Z',
isPullRequest,
['Stale']
)
];
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
await processor.processIssues(1);
expect(processor.closedIssues.length).toEqual(1);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
expect(processor.deletedBranchIssues.length).toEqual(0);
});

View File

@ -56,6 +56,9 @@ inputs:
skip-stale-issue-message:
description: 'Skip adding stale message when marking an issue as stale.'
default: false
delete-branch:
description: 'Delete the git branch after closing a stale pull request.'
default: false
runs:
using: 'node12'
main: 'dist/index.js'

View File

@ -14,6 +14,13 @@ export interface Issue {
locked: boolean;
}
export interface PullRequest {
number: number;
head: {
ref: string;
};
}
export interface User {
type: string;
login: string;
@ -54,6 +61,7 @@ export interface IssueProcessorOptions {
ascending: boolean;
skipStaleIssueMessage: boolean;
skipStalePrMessage: boolean;
deleteBranch: boolean;
}
/***
@ -66,6 +74,7 @@ export class IssueProcessor {
readonly staleIssues: Issue[] = [];
readonly closedIssues: Issue[] = [];
readonly deletedBranchIssues: Issue[] = [];
readonly removedLabelIssues: Issue[] = [];
constructor(
@ -250,6 +259,14 @@ export class IssueProcessor {
`Closing ${issueType} because it was last updated on ${issue.updated_at}`
);
await this.closeIssue(issue, closeMessage, closeLabel);
if (this.options.deleteBranch && issue.pull_request) {
core.info(
`Deleting branch for #${issue.number} as delete-branch option was specified`
);
await this.deleteBranch(issue);
this.deletedBranchIssues.push(issue);
}
} else {
core.info(
`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`
@ -432,6 +449,61 @@ export class IssueProcessor {
}
}
private async getPullRequest(
pullNumber: number
): Promise<PullRequest | undefined> {
this.operationsLeft -= 1;
try {
const pullRequest = await this.client.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullNumber
});
return pullRequest.data;
} catch (error) {
core.error(`Error getting pull request ${pullNumber}: ${error.message}`);
}
}
// Delete the branch on closed pull request
private async deleteBranch(issue: Issue): Promise<void> {
core.info(
`Delete branch from closed issue #${issue.number} - ${issue.title}`
);
if (this.options.debugOnly) {
return;
}
const pullRequest = await this.getPullRequest(issue.number);
if (!pullRequest) {
core.info(
`Not deleting branch as pull request not found for issue ${issue.number}`
);
return;
}
const branch = pullRequest.head.ref;
core.info(`Deleting branch ${branch} from closed issue #${issue.number}`);
this.operationsLeft -= 1;
try {
await this.client.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branch}`
});
} catch (error) {
core.error(
`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`
);
}
}
// Remove a label from an issue
private async removeLabel(issue: Issue, label: string): Promise<void> {
core.info(`Removing label from issue #${issue.number}`);

View File

@ -42,7 +42,8 @@ function getAndValidateArgs(): IssueProcessorOptions {
debugOnly: core.getInput('debug-only') === 'true',
ascending: core.getInput('ascending') === 'true',
skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true',
skipStaleIssueMessage: core.getInput('skip-stale-issue-message') === 'true'
skipStaleIssueMessage: core.getInput('skip-stale-issue-message') === 'true',
deleteBranch: core.getInput('delete-branch') === 'true'
};
for (const numberInput of [