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:
parent
87c2b794b9
commit
107018c400
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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}`);
|
||||
|
|
|
@ -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 [
|
||||
|
|
Loading…
Reference in New Issue