Skip to content

Commit

Permalink
Added output (#60)
Browse files Browse the repository at this point in the history
* Added output

* removed formatting changes

* more formatting changes

* Fix merge conflicts

* Small test refactoring

* tests are passing

* Add a test

* Add a small jest mock for addLabels. Not used.

* Add tests for more cases

* get rid of an unused jest mock

* fix review points

* rebuild + minor README fix

* fix review points

* update tests

* fix formatting

* add tests, fix bug

* cleanup

---------

Co-authored-by: Daniel Shteremberg <[email protected]>
Co-authored-by: Daniel Shteremberg <[email protected]>
Co-authored-by: Patrick Ellis <[email protected]>
Co-authored-by: Andrey Lobanovich <[email protected]>
  • Loading branch information
5 people committed Jun 29, 2023
1 parent 375538a commit 0967ca8
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 16 deletions.
50 changes: 44 additions & 6 deletions README.md
Expand Up @@ -109,12 +109,12 @@ jobs:

Various inputs are defined in [`action.yml`](action.yml) to let you configure the labeler:

| Name | Description | Default |
| - | - | - |
| `repo-token` | Token to use to authorize label changes. Typically the `GITHUB_TOKEN` secret, with `contents:read` and `pull-requests:write` access | `github.token` |
| `configuration-path` | The path to the label configuration file | `.github/labeler.yml` |
| `sync-labels` | Whether or not to remove labels when matching files are reverted or no longer changed by the PR | `false` |
| `dot` | Whether or not to auto-include paths starting with dot (e.g. `.github`) | `false` |
| Name | Description | Default |
|----------------------|-------------------------------------------------------------------------------------------------|-----------------------|
| `repo-token` | Token to use to authorize label changes. Typically the GITHUB_TOKEN secret | N/A |
| `configuration-path` | The path to the label configuration file | `.github/labeler.yml` |
| `sync-labels` | Whether or not to remove labels when matching files are reverted or no longer changed by the PR | `false` |
| `dot` | Whether or not to auto-include paths starting with dot (e.g. `.github`) | `false` |

When `dot` is disabled and you want to include _all_ files in a folder:

Expand All @@ -131,6 +131,44 @@ label1:
- path/to/folder/**
```

#### Outputs

Labeler provides the following outputs:

| Name | Description |
|--------------|-----------------------------------------------------------|
| `new-labels` | A comma-separated list of all new labels |
| `all-labels` | A comma-separated list of all labels that the PR contains |

The following example performs steps based on the output of labeler:
```yml
name: "My workflow"
on:
- pull_request_target

jobs:
triage:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- id: label-the-PR
uses: actions/labeler@v3

- id: run-frontend-tests
if: contains(fromJson(steps.label-the-PR.outputs.all-labels), 'frontend')
run: |
echo "Running frontend tests..."
# Put your commands for running frontend tests here
- id: run-backend-tests
if: contains(fromJson(steps.label-the-PR.outputs.all-labels), 'backend')
run: |
echo "Running backend tests..."
# Put your commands for running backend tests here
```

## Permissions

In order to add labels to pull requests, the GitHub labeler action requires
Expand Down
30 changes: 30 additions & 0 deletions __tests__/main.test.ts
Expand Up @@ -13,6 +13,7 @@ const reposMock = jest.spyOn(gh.rest.repos, 'getContent');
const paginateMock = jest.spyOn(gh, 'paginate');
const getPullMock = jest.spyOn(gh.rest.pulls, 'get');
const coreWarningMock = jest.spyOn(core, 'warning');
const setOutputSpy = jest.spyOn(core, 'setOutput');

const yamlFixtures = {
'only_pdfs.yml': fs.readFileSync('__tests__/fixtures/only_pdfs.yml')
Expand Down Expand Up @@ -50,12 +51,21 @@ describe('run', () => {
await run();

expect(setLabelsMock).toHaveBeenCalledTimes(1);

expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['touched-a-pdf-file']
});
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file'
);
});

it('(with dot: true) adds labels to PRs that match our glob patterns', async () => {
Expand All @@ -77,6 +87,14 @@ describe('run', () => {
issue_number: 123,
labels: ['touched-a-pdf-file']
});
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file'
);
});

it('(with dot: false) does not add labels to PRs that do not match our glob patterns', async () => {
Expand All @@ -92,6 +110,8 @@ describe('run', () => {
await run();

expect(setLabelsMock).toHaveBeenCalledTimes(0);
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', '');
});

it('(with dot: true) does not add labels to PRs that do not match our glob patterns', async () => {
Expand Down Expand Up @@ -128,6 +148,8 @@ describe('run', () => {
issue_number: 123,
labels: ['manually-added']
});
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'manually-added');
});

it('(with sync-labels: false) it issues no delete calls even when there are preexisting PR labels that no longer match the glob pattern', async () => {
Expand All @@ -148,6 +170,11 @@ describe('run', () => {
await run();

expect(setLabelsMock).toHaveBeenCalledTimes(0);
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file,manually-added'
);
});

it('(with sync-labels: false) it only logs the excess labels', async () => {
Expand Down Expand Up @@ -178,6 +205,9 @@ describe('run', () => {
'Maximum of 100 labels allowed. Excess labels: touched-a-pdf-file',
{title: 'Label limit for a PR exceeded'}
);
const allLabels: string = existingLabels.map(i => i.name).join(',');
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', allLabels);
});
});

Expand Down
5 changes: 5 additions & 0 deletions action.yml
Expand Up @@ -19,6 +19,11 @@ inputs:
default: false
required: false

outputs:
new-labels:
description: 'A comma-separated list of all new labels'
all-labels:
description: 'A comma-separated list of all labels that the PR contains'
runs:
using: 'node16'
main: 'dist/index.js'
14 changes: 9 additions & 5 deletions dist/index.js
Expand Up @@ -68,8 +68,8 @@ function run() {
core.debug(`fetching changed files for pr #${prNumber}`);
const changedFiles = yield getChangedFiles(client, prNumber);
const labelGlobs = yield getLabelGlobs(client, configPath);
const prLabels = pullRequest.labels.map(label => label.name);
const allLabels = new Set(prLabels);
const preexistingLabels = pullRequest.labels.map(l => l.name);
const allLabels = new Set(preexistingLabels);
for (const [label, globs] of labelGlobs.entries()) {
core.debug(`processing ${label}`);
if (checkGlobs(changedFiles, globs, dot)) {
Expand All @@ -79,12 +79,16 @@ function run() {
allLabels.delete(label);
}
}
const labels = [...allLabels].slice(0, GITHUB_MAX_LABELS);
const labelsToAdd = [...allLabels].slice(0, GITHUB_MAX_LABELS);
const excessLabels = [...allLabels].slice(GITHUB_MAX_LABELS);
try {
if (!isListEqual(prLabels, labels)) {
yield setLabels(client, prNumber, labels);
let newLabels = [];
if (!isListEqual(labelsToAdd, preexistingLabels)) {
yield setLabels(client, prNumber, labelsToAdd);
newLabels = labelsToAdd.filter(l => !preexistingLabels.includes(l));
}
core.setOutput('new-labels', newLabels.join(','));
core.setOutput('all-labels', labelsToAdd.join(','));
if (excessLabels.length) {
core.warning(`Maximum of ${GITHUB_MAX_LABELS} labels allowed. Excess labels: ${excessLabels.join(', ')}`, { title: 'Label limit for a PR exceeded' });
}
Expand Down
16 changes: 11 additions & 5 deletions src/labeler.ts
Expand Up @@ -43,8 +43,8 @@ export async function run() {
configPath
);

const prLabels: string[] = pullRequest.labels.map(label => label.name);
const allLabels: Set<string> = new Set(prLabels);
const preexistingLabels = pullRequest.labels.map(l => l.name);
const allLabels: Set<string> = new Set<string>(preexistingLabels);

for (const [label, globs] of labelGlobs.entries()) {
core.debug(`processing ${label}`);
Expand All @@ -55,14 +55,20 @@ export async function run() {
}
}

const labels = [...allLabels].slice(0, GITHUB_MAX_LABELS);
const labelsToAdd = [...allLabels].slice(0, GITHUB_MAX_LABELS);
const excessLabels = [...allLabels].slice(GITHUB_MAX_LABELS);

try {
if (!isListEqual(prLabels, labels)) {
await setLabels(client, prNumber, labels);
let newLabels: string[] = [];

if (!isListEqual(labelsToAdd, preexistingLabels)) {
await setLabels(client, prNumber, labelsToAdd);
newLabels = labelsToAdd.filter(l => !preexistingLabels.includes(l));
}

core.setOutput('new-labels', newLabels.join(','));
core.setOutput('all-labels', labelsToAdd.join(','));

if (excessLabels.length) {
core.warning(
`Maximum of ${GITHUB_MAX_LABELS} labels allowed. Excess labels: ${excessLabels.join(
Expand Down

0 comments on commit 0967ca8

Please sign in to comment.