mirror of
https://github.com/actions/add-to-project.git
synced 2025-12-11 04:32:47 +00:00
Merge pull request #41 from actions/add-lable-operator
Add label operator input support
This commit is contained in:
@@ -10,7 +10,7 @@ not the original GitHub Projects.
|
||||
|
||||
To use the action, create a workflow that runs when issues are opened in your
|
||||
repository. Run this action in a step, optionally configuring any filters you
|
||||
may want to add, such as only adding issues with certain labels.
|
||||
may want to add, such as only adding issues with certain labels. If you want to match all the labels, add `label-operator` input to be `AND`.
|
||||
|
||||
```yaml
|
||||
name: Add bugs to bugs project
|
||||
@@ -32,7 +32,8 @@ jobs:
|
||||
with:
|
||||
project-url: https://github.com/orgs/<orgName>/projects/<projectNumber>
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
labeled: bug
|
||||
labeled: bug, new
|
||||
label-operator: AND
|
||||
```
|
||||
|
||||
## Inputs
|
||||
@@ -42,8 +43,9 @@ jobs:
|
||||
token](https://github.com/settings/tokens/new) with the `repo`, `write:org` and
|
||||
`read:org` scopes.
|
||||
- `labeled` is a comma-separated list of labels. For an issue to be added to the
|
||||
project, it must have _one_ of the labels in the list. Omitting this key means
|
||||
project, it must have _one_ of the labels in the list if the `label-operator` doesn't exist or not `AND`. Omitting this key means
|
||||
that all issues will be added.
|
||||
- `label-operator` is the behavior of the labels filter, either `AND` or `OR` that controls if the issue should be matched with `all` `labeled` input or any of them, default is `OR`.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -52,11 +52,11 @@ describe('addToProject', () => {
|
||||
expect(outputs.itemId).toEqual('project-next-item-id')
|
||||
})
|
||||
|
||||
test('adds matching issues with a label filter', async () => {
|
||||
test('adds matching issues with a label filter without label-operator', async () => {
|
||||
mockGetInput({
|
||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
||||
'github-token': 'gh_token',
|
||||
labeled: 'bug'
|
||||
labeled: 'bug, new'
|
||||
})
|
||||
|
||||
github.context.payload = {
|
||||
@@ -94,7 +94,7 @@ describe('addToProject', () => {
|
||||
expect(outputs.itemId).toEqual('project-next-item-id')
|
||||
})
|
||||
|
||||
test('does not add un-matching issues with a label filter', async () => {
|
||||
test('does not add un-matching issues with a label filter without label-operator', async () => {
|
||||
mockGetInput({
|
||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
||||
'github-token': 'gh_token',
|
||||
@@ -114,6 +114,71 @@ describe('addToProject', () => {
|
||||
expect(infoSpy).toHaveBeenCalledWith(`Skipping issue 1 because it does not have one of the labels: bug`)
|
||||
expect(gqlMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('adds matching issues with labels filter with AND label-operator', async () => {
|
||||
mockGetInput({
|
||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
||||
'github-token': 'gh_token',
|
||||
labeled: 'bug, new',
|
||||
'label-operator': 'AND'
|
||||
})
|
||||
|
||||
github.context.payload = {
|
||||
issue: {
|
||||
number: 1,
|
||||
labels: [{name: 'bug'}, {name: 'new'}]
|
||||
}
|
||||
}
|
||||
|
||||
mockGraphQL(
|
||||
{
|
||||
test: /getProject/,
|
||||
return: {
|
||||
organization: {
|
||||
projectNext: {
|
||||
id: 'project-next-id'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /addProjectNextItem/,
|
||||
return: {
|
||||
addProjectNextItem: {
|
||||
projectNextItem: {
|
||||
id: 'project-next-item-id'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await addToProject()
|
||||
|
||||
expect(outputs.itemId).toEqual('project-next-item-id')
|
||||
})
|
||||
|
||||
test('does not add un-matching issues with labels filter with AND label-operator', async () => {
|
||||
mockGetInput({
|
||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
||||
'github-token': 'gh_token',
|
||||
labeled: 'bug, new',
|
||||
'label-operator': 'AND'
|
||||
})
|
||||
|
||||
github.context.payload = {
|
||||
issue: {
|
||||
number: 1,
|
||||
labels: [{name: 'bug'}, {name: 'other'}]
|
||||
}
|
||||
}
|
||||
|
||||
const infoSpy = jest.spyOn(core, 'info')
|
||||
const gqlMock = mockGraphQL()
|
||||
await addToProject()
|
||||
expect(infoSpy).toHaveBeenCalledWith(`Skipping issue 1 because it doesn't match all the labels: bug, new`)
|
||||
expect(gqlMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
function mockGetInput(mocks: Record<string, string>): jest.SpyInstance {
|
||||
|
||||
@@ -11,6 +11,9 @@ inputs:
|
||||
labeled:
|
||||
required: false
|
||||
description: A comma-separated list of labels to use as a filter for issue to be added
|
||||
label-operator:
|
||||
required: false
|
||||
description: The behavior of the labels filter, AND to match all labels, OR to match any label (default is OR)
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
|
||||
14
dist/index.js
generated
vendored
14
dist/index.js
generated
vendored
@@ -51,14 +51,20 @@ function addToProject() {
|
||||
.split(',')
|
||||
.map(l => l.trim())
|
||||
.filter(l => l.length > 0)) !== null && _a !== void 0 ? _a : [];
|
||||
const labelOperator = core.getInput('label-operator').trim().toLocaleLowerCase();
|
||||
const octokit = github.getOctokit(ghToken);
|
||||
const urlMatch = projectUrl.match(urlParse);
|
||||
const issue = (_b = github.context.payload.issue) !== null && _b !== void 0 ? _b : github.context.payload.pull_request;
|
||||
const issueLabels = ((_c = issue === null || issue === void 0 ? void 0 : issue.labels) !== null && _c !== void 0 ? _c : []).map((l) => l.name);
|
||||
// Ensure the issue matches our `labeled` filter, if provided.
|
||||
if (labeled.length > 0) {
|
||||
const hasLabel = issueLabels.some(l => labeled.includes(l));
|
||||
if (!hasLabel) {
|
||||
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
||||
if (labelOperator === 'and') {
|
||||
if (!labeled.every(l => issueLabels.includes(l))) {
|
||||
core.info(`Skipping issue ${issue === null || issue === void 0 ? void 0 : issue.number} because it doesn't match all the labels: ${labeled.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (labeled.length > 0 && !issueLabels.some(l => labeled.includes(l))) {
|
||||
core.info(`Skipping issue ${issue === null || issue === void 0 ? void 0 : issue.number} because it does not have one of the labels: ${labeled.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -37,18 +37,22 @@ export async function addToProject(): Promise<void> {
|
||||
.split(',')
|
||||
.map(l => l.trim())
|
||||
.filter(l => l.length > 0) ?? []
|
||||
const labelOperator = core.getInput('label-operator').trim().toLocaleLowerCase()
|
||||
|
||||
const octokit = github.getOctokit(ghToken)
|
||||
const urlMatch = projectUrl.match(urlParse)
|
||||
const issue = github.context.payload.issue ?? github.context.payload.pull_request
|
||||
const issueLabels: string[] = (issue?.labels ?? []).map((l: {name: string}) => l.name)
|
||||
|
||||
// Ensure the issue matches our `labeled` filter, if provided.
|
||||
if (labeled.length > 0) {
|
||||
const hasLabel = issueLabels.some(l => labeled.includes(l))
|
||||
|
||||
if (!hasLabel) {
|
||||
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
||||
if (labelOperator === 'and') {
|
||||
if (!labeled.every(l => issueLabels.includes(l))) {
|
||||
core.info(`Skipping issue ${issue?.number} because it doesn't match all the labels: ${labeled.join(', ')}`)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (labeled.length > 0 && !issueLabels.some(l => labeled.includes(l))) {
|
||||
core.info(`Skipping issue ${issue?.number} because it does not have one of the labels: ${labeled.join(', ')}`)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user