Skip to content

Commit

Permalink
Add label exclusion support. (#86)
Browse files Browse the repository at this point in the history
Co-authored-by: Sara Tan <saratan@mac.com>
  • Loading branch information
chinthakagodawita and anarast authored Jan 18, 2021
1 parent c812799 commit e467e67
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ All configuration values, except `GITHUB_TOKEN`, are optional.

* `PR_LABELS`: Controls which labels _autoupdate_ will look for when monitoring PRs. Only used if `PR_FILTER="labelled"`. This can be either a single label or a comma-separated list of labels.

* `EXCLUDED_LABELS`: Controls which labels _autoupdate_ will ignore when evaluating otherwise-included PRs. This option works with all `PR_FILTER` options and can be either a single label or a comma-separated list of labels.

* `MERGE_MSG`: A custom message to use when creating the merge commit from the destination branch to your pull request's branch.

* `RETRY_COUNT`: The number of times a branch update should be attempted before _autoupdate_ gives up (default: `"5"`).
Expand Down Expand Up @@ -77,9 +79,11 @@ jobs:
DRY_RUN: "false"
PR_FILTER: "labelled"
PR_LABELS: "autoupdate,keep up-to-date,integration"
EXCLUDED_LABELS: "dependencies,wontfix"
MERGE_MSG: "Branch was auto-updated."
RETRY_COUNT: "5"
RETRY_SLEEP: "300"
MERGE_CONFLICT_ACTION: "fail"
```

## Examples
Expand Down
16 changes: 16 additions & 0 deletions src/autoupdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,28 @@ export class AutoUpdater {
return false;
}

// First check if this PR has an excluded label on it and skip further
// processing if so.
const excludedLabels = this.config.excludedLabels();
if (excludedLabels.length > 0) {
for (const label of pull.labels) {
if (excludedLabels.includes(label.name)) {
ghCore.info(
`Pull request has excluded label '${label.name}', skipping update.`,
);
return false;
}
}
}

const prFilter = this.config.pullRequestFilter();

ghCore.info(
`PR_FILTER=${prFilter}, checking if this PR's branch needs to be updated.`,
);

// If PR_FILTER=labelled, check that this PR has _any_ of the labels
// specified in that configuration option.
if (prFilter === 'labelled') {
const labels = this.config.pullRequestLabels();
if (labels.length === 0) {
Expand Down
10 changes: 10 additions & 0 deletions src/config-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ export class ConfigLoader {
return rawLabels.split(',').map((label: string) => label.trim());
}

excludedLabels(): Array<string> {
const rawLabels = this.getValue('EXCLUDED_LABELS', false, '')
.toString()
.trim();
if (rawLabels === '') {
return [];
}
return rawLabels.split(',').map((label: string) => label.trim());
}

mergeMsg(): string {
const msg = this.getValue('MERGE_MSG', false, '').toString().trim();
return msg === '' ? null : msg;
Expand Down
67 changes: 67 additions & 0 deletions test/autoupdate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,64 @@ describe('test `prNeedsUpdate`', () => {
expect(scope.isDone()).toEqual(true);
});

test('excluded labels were configured but not found', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('all');
(config.excludedLabels as jest.Mock).mockReturnValue(['label']);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
.reply(200, {
behind_by: 1,
});

const updater = new AutoUpdater(config, {});
const needsUpdate = await updater.prNeedsUpdate(validPull);

expect(needsUpdate).toEqual(true);
expect(scope.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('excluded labels exist', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('all');
(config.pullRequestLabels as jest.Mock).mockReturnValue([]);
(config.excludedLabels as jest.Mock).mockReturnValue(['dependencies']);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
.reply(200, {
behind_by: 1,
});

const updater = new AutoUpdater(config, {});
const pull = clonePull();
pull.labels = [
{
id: 3,
name: 'autoupdate',
},
{
id: 4,
name: 'dependencies',
},
];
const needsUpdate = await updater.prNeedsUpdate(pull);

expect(needsUpdate).toEqual(false);
expect(scope.isDone()).toEqual(true);
expect(config.excludedLabels).toHaveBeenCalled();

// The excluded labels check happens before we check any filters so these
// functions should never be called.
expect(config.pullRequestFilter).toHaveBeenCalledTimes(0);
expect(config.pullRequestLabels).toHaveBeenCalledTimes(0);
});

test('no pull request labels were configured', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('labelled');
(config.pullRequestLabels as jest.Mock).mockReturnValue([]);
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -116,11 +171,13 @@ describe('test `prNeedsUpdate`', () => {
expect(scope.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.pullRequestLabels).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('pull request has no labels', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('labelled');
(config.pullRequestLabels as jest.Mock).mockReturnValue(['one', 'two']);
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -137,11 +194,13 @@ describe('test `prNeedsUpdate`', () => {
expect(scope.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.pullRequestLabels).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('pull request labels do not match', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('labelled');
(config.pullRequestLabels as jest.Mock).mockReturnValue(['three', 'four']);
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -156,11 +215,13 @@ describe('test `prNeedsUpdate`', () => {
expect(scope.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.pullRequestLabels).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('pull request labels do match', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('labelled');
(config.pullRequestLabels as jest.Mock).mockReturnValue(['three', 'four']);
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const scope = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -184,6 +245,7 @@ describe('test `prNeedsUpdate`', () => {

test('pull request is against protected branch', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('protected');
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const comparePr = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -204,10 +266,12 @@ describe('test `prNeedsUpdate`', () => {
expect(comparePr.isDone()).toEqual(true);
expect(getBranch.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('pull request is not against protected branch', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('protected');
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const comparePr = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -228,10 +292,12 @@ describe('test `prNeedsUpdate`', () => {
expect(comparePr.isDone()).toEqual(true);
expect(getBranch.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});

test('no filters configured', async () => {
(config.pullRequestFilter as jest.Mock).mockReturnValue('all');
(config.excludedLabels as jest.Mock).mockReturnValue([]);

const comparePr = nock('https://api.github.com:443')
.get(`/repos/${owner}/${repo}/compare/${head}...${base}`)
Expand All @@ -245,6 +311,7 @@ describe('test `prNeedsUpdate`', () => {
expect(needsUpdate).toEqual(true);
expect(comparePr.isDone()).toEqual(true);
expect(config.pullRequestFilter).toHaveBeenCalled();
expect(config.excludedLabels).toHaveBeenCalled();
});
});

Expand Down
7 changes: 7 additions & 0 deletions test/config-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ const tests = [
default: [],
type: 'list',
},
{
name: 'excludedLabels',
envVar: 'EXCLUDED_LABELS',
required: false,
default: [],
type: 'list',
},
{
name: 'mergeMsg',
envVar: 'MERGE_MSG',
Expand Down

0 comments on commit e467e67

Please sign in to comment.