mirror of
https://github.com/actions/add-to-project.git
synced 2025-12-10 20:21:17 +00:00
Allow users to add an issue/PR to a board in a different organization
At present, this Action only supports adding an issue or pull request to a project in the *same organization* as the issue or pull request itself. This removes that limitation. If the issue/pull request and project have different owners, then instead of directly creating a project item, we will instead create a "draft issue" which will be added to the board. Fixes #74.
This commit is contained in:
@@ -9,8 +9,6 @@ not the original GitHub projects.
|
|||||||
|
|
||||||
[](https://github.com/actions/add-to-project/actions/workflows/test.yml)
|
[](https://github.com/actions/add-to-project/actions/workflows/test.yml)
|
||||||
|
|
||||||
> **NOTE:** This Action (currently) only supports auto-adding Issues/Pull Requests to a Project which lives in the same organization as your target Repository.
|
|
||||||
|
|
||||||
> **NOTE:** ⚠️ This action no longer uses the deprecated ProjectNext API. If you are looking for the old version of that action, use version [v0.0.5](https://github.com/actions/add-to-project/releases/tag/v0.0.5).
|
> **NOTE:** ⚠️ This action no longer uses the deprecated ProjectNext API. If you are looking for the old version of that action, use version [v0.0.5](https://github.com/actions/add-to-project/releases/tag/v0.0.5).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -42,6 +40,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@RELEASE_VERSION
|
- uses: actions/add-to-project@RELEASE_VERSION
|
||||||
with:
|
with:
|
||||||
|
# You can target a repository in a different organization
|
||||||
|
# to the issue
|
||||||
project-url: https://github.com/orgs/<orgName>/projects/<projectNumber>
|
project-url: https://github.com/orgs/<orgName>/projects/<projectNumber>
|
||||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||||
labeled: bug, needs-triage
|
labeled: bug, needs-triage
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token'
|
'github-token': 'gh_token'
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -24,7 +24,22 @@ describe('addToProject', () => {
|
|||||||
jest.restoreAllMocks()
|
jest.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('adds an issue to the project', async () => {
|
test('adds an issue from the same organization to the project', async () => {
|
||||||
|
github.context.payload = {
|
||||||
|
issue: {
|
||||||
|
number: 1,
|
||||||
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mockGraphQL(
|
mockGraphQL(
|
||||||
{
|
{
|
||||||
test: /getProject/,
|
test: /getProject/,
|
||||||
@@ -53,9 +68,53 @@ describe('addToProject', () => {
|
|||||||
expect(outputs.itemId).toEqual('project-item-id')
|
expect(outputs.itemId).toEqual('project-item-id')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('adds an issue from a different organization to the project', async () => {
|
||||||
|
github.context.payload = {
|
||||||
|
issue: {
|
||||||
|
number: 2221,
|
||||||
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/octokit/octokit.js/issues/2221'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'octokit.js',
|
||||||
|
owner: {
|
||||||
|
login: 'octokit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGraphQL(
|
||||||
|
{
|
||||||
|
test: /getProject/,
|
||||||
|
return: {
|
||||||
|
organization: {
|
||||||
|
projectV2: {
|
||||||
|
id: 'project-id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /addProjectV2DraftIssue/,
|
||||||
|
return: {
|
||||||
|
addProjectV2DraftIssue: {
|
||||||
|
projectItem: {
|
||||||
|
id: 'project-item-id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await addToProject()
|
||||||
|
|
||||||
|
expect(outputs.itemId).toEqual('project-item-id')
|
||||||
|
})
|
||||||
|
|
||||||
test('adds matching issues with a label filter without label-operator', async () => {
|
test('adds matching issues with a label filter without label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new'
|
labeled: 'bug, new'
|
||||||
})
|
})
|
||||||
@@ -63,7 +122,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}]
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +164,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('adds matching pull-requests with a label filter without label-operator', async () => {
|
test('adds matching pull-requests with a label filter without label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new'
|
labeled: 'bug, new'
|
||||||
})
|
})
|
||||||
@@ -106,7 +173,15 @@ describe('addToProject', () => {
|
|||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
pull_request: {
|
pull_request: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}]
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/pull/136'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +215,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('does not add un-matching issues with a label filter without label-operator', async () => {
|
test('does not add un-matching issues with a label filter without label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug'
|
labeled: 'bug'
|
||||||
})
|
})
|
||||||
@@ -148,7 +223,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: []
|
labels: [],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +244,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('adds matching issues with labels filter with AND label-operator', async () => {
|
test('adds matching issues with labels filter with AND label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new',
|
labeled: 'bug, new',
|
||||||
'label-operator': 'AND'
|
'label-operator': 'AND'
|
||||||
@@ -170,7 +253,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}, {name: 'new'}]
|
labels: [{name: 'bug'}, {name: 'new'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +295,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('does not add un-matching issues with labels filter with AND label-operator', async () => {
|
test('does not add un-matching issues with labels filter with AND label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new',
|
labeled: 'bug, new',
|
||||||
'label-operator': 'AND'
|
'label-operator': 'AND'
|
||||||
@@ -213,7 +304,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}, {name: 'other'}]
|
labels: [{name: 'bug'}, {name: 'other'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +325,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('does not add matching issues with labels filter with NOT label-operator', async () => {
|
test('does not add matching issues with labels filter with NOT label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new',
|
labeled: 'bug, new',
|
||||||
'label-operator': 'NOT'
|
'label-operator': 'NOT'
|
||||||
@@ -235,7 +334,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}]
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +355,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('adds issues that do not have labels present in the label list with NOT label-operator', async () => {
|
test('adds issues that do not have labels present in the label list with NOT label-operator', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new',
|
labeled: 'bug, new',
|
||||||
'label-operator': 'NOT'
|
'label-operator': 'NOT'
|
||||||
@@ -257,7 +364,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'other'}]
|
labels: [{name: 'other'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +406,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('adds matching issues with multiple label filters', async () => {
|
test('adds matching issues with multiple label filters', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'accessibility,backend,bug'
|
labeled: 'accessibility,backend,bug'
|
||||||
})
|
})
|
||||||
@@ -299,7 +414,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'accessibility'}, {name: 'backend'}]
|
labels: [{name: 'accessibility'}, {name: 'backend'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +460,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('does not add un-matching issues with multiple label filters', async () => {
|
test('does not add un-matching issues with multiple label filters', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'accessibility, backend, bug'
|
labeled: 'accessibility, backend, bug'
|
||||||
})
|
})
|
||||||
@@ -345,7 +468,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'data'}, {name: 'frontend'}, {name: 'improvement'}]
|
labels: [{name: 'data'}, {name: 'frontend'}, {name: 'improvement'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +491,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('handles spaces and extra commas gracefully in label filter input', async () => {
|
test('handles spaces and extra commas gracefully in label filter input', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'accessibility , backend ,, . , bug'
|
labeled: 'accessibility , backend ,, . , bug'
|
||||||
})
|
})
|
||||||
@@ -369,7 +500,15 @@ describe('addToProject', () => {
|
|||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'accessibility'}, {name: 'backend'}, {name: 'bug'}],
|
labels: [{name: 'accessibility'}, {name: 'backend'}, {name: 'bug'}],
|
||||||
'label-operator': 'AND'
|
'label-operator': 'AND',
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +553,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: []
|
labels: [],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +583,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: []
|
labels: [],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,7 +606,7 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
test('constructs the correct graphQL query given an organization owner', async () => {
|
test('constructs the correct graphQL query given an organization owner', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'bug, new'
|
labeled: 'bug, new'
|
||||||
})
|
})
|
||||||
@@ -459,7 +614,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}]
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,8 +651,8 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
await addToProject()
|
await addToProject()
|
||||||
|
|
||||||
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('organization(login: $ownerName)'), {
|
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('organization(login: $projectOwnerName)'), {
|
||||||
ownerName: 'github',
|
projectOwnerName: 'actions',
|
||||||
projectNumber: 1
|
projectNumber: 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -504,7 +667,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'bug'}]
|
labels: [{name: 'bug'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/monalisa/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'monalisa'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,15 +704,15 @@ describe('addToProject', () => {
|
|||||||
|
|
||||||
await addToProject()
|
await addToProject()
|
||||||
|
|
||||||
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('user(login: $ownerName)'), {
|
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('user(login: $projectOwnerName)'), {
|
||||||
ownerName: 'monalisa',
|
projectOwnerName: 'monalisa',
|
||||||
projectNumber: 1
|
projectNumber: 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('compares labels case-insensitively', async () => {
|
test('compares labels case-insensitively', async () => {
|
||||||
mockGetInput({
|
mockGetInput({
|
||||||
'project-url': 'https://github.com/orgs/github/projects/1',
|
'project-url': 'https://github.com/orgs/actions/projects/1',
|
||||||
'github-token': 'gh_token',
|
'github-token': 'gh_token',
|
||||||
labeled: 'FOO, Bar, baz',
|
labeled: 'FOO, Bar, baz',
|
||||||
'label-operator': 'AND'
|
'label-operator': 'AND'
|
||||||
@@ -550,7 +721,15 @@ describe('addToProject', () => {
|
|||||||
github.context.payload = {
|
github.context.payload = {
|
||||||
issue: {
|
issue: {
|
||||||
number: 1,
|
number: 1,
|
||||||
labels: [{name: 'foo'}, {name: 'BAR'}, {name: 'baz'}]
|
labels: [{name: 'foo'}, {name: 'BAR'}, {name: 'baz'}],
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
html_url: 'https://github.com/actions/add-to-project/issues/74'
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
name: 'add-to-project',
|
||||||
|
owner: {
|
||||||
|
login: 'actions'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
dist/index.js
generated
vendored
68
dist/index.js
generated
vendored
@@ -46,7 +46,7 @@ const github = __importStar(__nccwpck_require__(5438));
|
|||||||
// https://github.com/orgs|users/<ownerName>/projects/<projectNumber>
|
// https://github.com/orgs|users/<ownerName>/projects/<projectNumber>
|
||||||
const urlParse = /^(?:https:\/\/)?github\.com\/(?<ownerType>orgs|users)\/(?<ownerName>[^/]+)\/projects\/(?<projectNumber>\d+)/;
|
const urlParse = /^(?:https:\/\/)?github\.com\/(?<ownerType>orgs|users)\/(?<ownerName>[^/]+)\/projects\/(?<projectNumber>\d+)/;
|
||||||
function addToProject() {
|
function addToProject() {
|
||||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const projectUrl = core.getInput('project-url', { required: true });
|
const projectUrl = core.getInput('project-url', { required: true });
|
||||||
const ghToken = core.getInput('github-token', { required: true });
|
const ghToken = core.getInput('github-token', { required: true });
|
||||||
@@ -57,9 +57,10 @@ function addToProject() {
|
|||||||
.filter(l => l.length > 0)) !== null && _a !== void 0 ? _a : [];
|
.filter(l => l.length > 0)) !== null && _a !== void 0 ? _a : [];
|
||||||
const labelOperator = core.getInput('label-operator').trim().toLocaleLowerCase();
|
const labelOperator = core.getInput('label-operator').trim().toLocaleLowerCase();
|
||||||
const octokit = github.getOctokit(ghToken);
|
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 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.toLowerCase());
|
const issueLabels = ((_c = issue === null || issue === void 0 ? void 0 : issue.labels) !== null && _c !== void 0 ? _c : []).map((l) => l.name.toLowerCase());
|
||||||
|
const issueOwnerName = (_d = github.context.payload.repository) === null || _d === void 0 ? void 0 : _d.owner.login;
|
||||||
|
core.debug(`Issue/PR owner: ${issueOwnerName}`);
|
||||||
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
||||||
if (labelOperator === 'and') {
|
if (labelOperator === 'and') {
|
||||||
if (!labeled.every(l => issueLabels.includes(l))) {
|
if (!labeled.every(l => issueLabels.includes(l))) {
|
||||||
@@ -80,45 +81,68 @@ function addToProject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.debug(`Project URL: ${projectUrl}`);
|
core.debug(`Project URL: ${projectUrl}`);
|
||||||
|
const urlMatch = projectUrl.match(urlParse);
|
||||||
if (!urlMatch) {
|
if (!urlMatch) {
|
||||||
throw new Error(`Invalid project URL: ${projectUrl}. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>`);
|
throw new Error(`Invalid project URL: ${projectUrl}. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>`);
|
||||||
}
|
}
|
||||||
const ownerName = (_d = urlMatch.groups) === null || _d === void 0 ? void 0 : _d.ownerName;
|
const projectOwnerName = (_e = urlMatch.groups) === null || _e === void 0 ? void 0 : _e.ownerName;
|
||||||
const projectNumber = parseInt((_f = (_e = urlMatch.groups) === null || _e === void 0 ? void 0 : _e.projectNumber) !== null && _f !== void 0 ? _f : '', 10);
|
const projectNumber = parseInt((_g = (_f = urlMatch.groups) === null || _f === void 0 ? void 0 : _f.projectNumber) !== null && _g !== void 0 ? _g : '', 10);
|
||||||
const ownerType = (_g = urlMatch.groups) === null || _g === void 0 ? void 0 : _g.ownerType;
|
const ownerType = (_h = urlMatch.groups) === null || _h === void 0 ? void 0 : _h.ownerType;
|
||||||
const ownerTypeQuery = mustGetOwnerTypeQuery(ownerType);
|
const ownerTypeQuery = mustGetOwnerTypeQuery(ownerType);
|
||||||
core.debug(`Org name: ${ownerName}`);
|
core.debug(`Project owner: ${projectOwnerName}`);
|
||||||
core.debug(`Project number: ${projectNumber}`);
|
core.debug(`Project number: ${projectNumber}`);
|
||||||
core.debug(`Owner type: ${ownerType}`);
|
core.debug(`Project owner type: ${ownerType}`);
|
||||||
// First, use the GraphQL API to request the project's node ID.
|
// First, use the GraphQL API to request the project's node ID.
|
||||||
const idResp = yield octokit.graphql(`query getProject($ownerName: String!, $projectNumber: Int!) {
|
const idResp = yield octokit.graphql(`query getProject($projectOwnerName: String!, $projectNumber: Int!) {
|
||||||
${ownerTypeQuery}(login: $ownerName) {
|
${ownerTypeQuery}(login: $projectOwnerName) {
|
||||||
projectV2(number: $projectNumber) {
|
projectV2(number: $projectNumber) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`, {
|
}`, {
|
||||||
ownerName,
|
projectOwnerName,
|
||||||
projectNumber
|
projectNumber
|
||||||
});
|
});
|
||||||
const projectId = (_h = idResp[ownerTypeQuery]) === null || _h === void 0 ? void 0 : _h.projectV2.id;
|
const projectId = (_j = idResp[ownerTypeQuery]) === null || _j === void 0 ? void 0 : _j.projectV2.id;
|
||||||
const contentId = issue === null || issue === void 0 ? void 0 : issue.node_id;
|
const contentId = issue === null || issue === void 0 ? void 0 : issue.node_id;
|
||||||
core.debug(`Project node ID: ${projectId}`);
|
core.debug(`Project node ID: ${projectId}`);
|
||||||
core.debug(`Content ID: ${contentId}`);
|
core.debug(`Content ID: ${contentId}`);
|
||||||
// Next, use the GraphQL API to add the issue to the project.
|
// Next, use the GraphQL API to add the issue to the project.
|
||||||
const addResp = yield octokit.graphql(`mutation addIssueToProject($input: AddProjectV2ItemByIdInput!) {
|
// If the issue has the same owner as the project, we can directly
|
||||||
addProjectV2ItemById(input: $input) {
|
// add a project item. Otherwise, we add a draft issue.
|
||||||
item {
|
if (issueOwnerName === projectOwnerName) {
|
||||||
id
|
core.info('Creating project item');
|
||||||
|
const addResp = yield octokit.graphql(`mutation addIssueToProject($input: AddProjectV2ItemByIdInput!) {
|
||||||
|
addProjectV2ItemById(input: $input) {
|
||||||
|
item {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}`, {
|
||||||
}`, {
|
input: {
|
||||||
input: {
|
projectId,
|
||||||
|
contentId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
core.setOutput('itemId', addResp.addProjectV2ItemById.item.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info('Creating draft issue in project');
|
||||||
|
const addResp = yield octokit.graphql(`mutation addDraftIssueToProject($projectId: ID!, $title: String!) {
|
||||||
|
addProjectV2DraftIssue(input: {
|
||||||
|
projectId: $projectId,
|
||||||
|
title: $title
|
||||||
|
}) {
|
||||||
|
projectItem {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`, {
|
||||||
projectId,
|
projectId,
|
||||||
contentId
|
title: issue === null || issue === void 0 ? void 0 : issue.html_url
|
||||||
}
|
});
|
||||||
});
|
core.setOutput('itemId', addResp.addProjectV2DraftIssue.projectItem.id);
|
||||||
core.setOutput('itemId', addResp.addProjectV2ItemById.item.id);
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.addToProject = addToProject;
|
exports.addToProject = addToProject;
|
||||||
|
|||||||
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
@@ -28,6 +28,14 @@ interface ProjectAddItemResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProjectV2AddDraftIssueResponse {
|
||||||
|
addProjectV2DraftIssue: {
|
||||||
|
projectItem: {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function addToProject(): Promise<void> {
|
export async function addToProject(): Promise<void> {
|
||||||
const projectUrl = core.getInput('project-url', {required: true})
|
const projectUrl = core.getInput('project-url', {required: true})
|
||||||
const ghToken = core.getInput('github-token', {required: true})
|
const ghToken = core.getInput('github-token', {required: true})
|
||||||
@@ -41,9 +49,11 @@ export async function addToProject(): Promise<void> {
|
|||||||
|
|
||||||
const octokit = github.getOctokit(ghToken)
|
const octokit = github.getOctokit(ghToken)
|
||||||
|
|
||||||
const urlMatch = projectUrl.match(urlParse)
|
|
||||||
const issue = github.context.payload.issue ?? github.context.payload.pull_request
|
const issue = github.context.payload.issue ?? github.context.payload.pull_request
|
||||||
const issueLabels: string[] = (issue?.labels ?? []).map((l: {name: string}) => l.name.toLowerCase())
|
const issueLabels: string[] = (issue?.labels ?? []).map((l: {name: string}) => l.name.toLowerCase())
|
||||||
|
const issueOwnerName = github.context.payload.repository?.owner.login
|
||||||
|
|
||||||
|
core.debug(`Issue/PR owner: ${issueOwnerName}`)
|
||||||
|
|
||||||
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
// Ensure the issue matches our `labeled` filter based on the label-operator.
|
||||||
if (labelOperator === 'and') {
|
if (labelOperator === 'and') {
|
||||||
@@ -65,32 +75,34 @@ export async function addToProject(): Promise<void> {
|
|||||||
|
|
||||||
core.debug(`Project URL: ${projectUrl}`)
|
core.debug(`Project URL: ${projectUrl}`)
|
||||||
|
|
||||||
|
const urlMatch = projectUrl.match(urlParse)
|
||||||
|
|
||||||
if (!urlMatch) {
|
if (!urlMatch) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid project URL: ${projectUrl}. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>`
|
`Invalid project URL: ${projectUrl}. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ownerName = urlMatch.groups?.ownerName
|
const projectOwnerName = urlMatch.groups?.ownerName
|
||||||
const projectNumber = parseInt(urlMatch.groups?.projectNumber ?? '', 10)
|
const projectNumber = parseInt(urlMatch.groups?.projectNumber ?? '', 10)
|
||||||
const ownerType = urlMatch.groups?.ownerType
|
const ownerType = urlMatch.groups?.ownerType
|
||||||
const ownerTypeQuery = mustGetOwnerTypeQuery(ownerType)
|
const ownerTypeQuery = mustGetOwnerTypeQuery(ownerType)
|
||||||
|
|
||||||
core.debug(`Org name: ${ownerName}`)
|
core.debug(`Project owner: ${projectOwnerName}`)
|
||||||
core.debug(`Project number: ${projectNumber}`)
|
core.debug(`Project number: ${projectNumber}`)
|
||||||
core.debug(`Owner type: ${ownerType}`)
|
core.debug(`Project owner type: ${ownerType}`)
|
||||||
|
|
||||||
// First, use the GraphQL API to request the project's node ID.
|
// First, use the GraphQL API to request the project's node ID.
|
||||||
const idResp = await octokit.graphql<ProjectNodeIDResponse>(
|
const idResp = await octokit.graphql<ProjectNodeIDResponse>(
|
||||||
`query getProject($ownerName: String!, $projectNumber: Int!) {
|
`query getProject($projectOwnerName: String!, $projectNumber: Int!) {
|
||||||
${ownerTypeQuery}(login: $ownerName) {
|
${ownerTypeQuery}(login: $projectOwnerName) {
|
||||||
projectV2(number: $projectNumber) {
|
projectV2(number: $projectNumber) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
{
|
{
|
||||||
ownerName,
|
projectOwnerName,
|
||||||
projectNumber
|
projectNumber
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -102,23 +114,50 @@ export async function addToProject(): Promise<void> {
|
|||||||
core.debug(`Content ID: ${contentId}`)
|
core.debug(`Content ID: ${contentId}`)
|
||||||
|
|
||||||
// Next, use the GraphQL API to add the issue to the project.
|
// Next, use the GraphQL API to add the issue to the project.
|
||||||
const addResp = await octokit.graphql<ProjectAddItemResponse>(
|
// If the issue has the same owner as the project, we can directly
|
||||||
`mutation addIssueToProject($input: AddProjectV2ItemByIdInput!) {
|
// add a project item. Otherwise, we add a draft issue.
|
||||||
addProjectV2ItemById(input: $input) {
|
if (issueOwnerName === projectOwnerName) {
|
||||||
item {
|
core.info('Creating project item')
|
||||||
id
|
|
||||||
|
const addResp = await octokit.graphql<ProjectAddItemResponse>(
|
||||||
|
`mutation addIssueToProject($input: AddProjectV2ItemByIdInput!) {
|
||||||
|
addProjectV2ItemById(input: $input) {
|
||||||
|
item {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
projectId,
|
||||||
|
contentId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`,
|
)
|
||||||
{
|
|
||||||
input: {
|
|
||||||
projectId,
|
|
||||||
contentId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
core.setOutput('itemId', addResp.addProjectV2ItemById.item.id)
|
core.setOutput('itemId', addResp.addProjectV2ItemById.item.id)
|
||||||
|
} else {
|
||||||
|
core.info('Creating draft issue in project')
|
||||||
|
|
||||||
|
const addResp = await octokit.graphql<ProjectV2AddDraftIssueResponse>(
|
||||||
|
`mutation addDraftIssueToProject($projectId: ID!, $title: String!) {
|
||||||
|
addProjectV2DraftIssue(input: {
|
||||||
|
projectId: $projectId,
|
||||||
|
title: $title
|
||||||
|
}) {
|
||||||
|
projectItem {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
{
|
||||||
|
projectId,
|
||||||
|
title: issue?.html_url
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
core.setOutput('itemId', addResp.addProjectV2DraftIssue.projectItem.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mustGetOwnerTypeQuery(ownerType?: string): 'organization' | 'user' {
|
export function mustGetOwnerTypeQuery(ownerType?: string): 'organization' | 'user' {
|
||||||
|
|||||||
Reference in New Issue
Block a user