Compare commits

..

1 Commits

Author SHA1 Message Date
Matthew Costabile
89cc223d97 empty 2022-03-24 20:49:49 +00:00
36 changed files with 1344 additions and 2155 deletions

View File

@@ -20,10 +20,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Set Node.js 12.x - name: Set Node.js 12.x
uses: actions/setup-node@v3 uses: actions/setup-node@v2.5.1
with: with:
node-version: 12.x node-version: 12.x
cache: npm cache: npm

View File

@@ -1,70 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '18 2 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -13,7 +13,7 @@ jobs:
build: # make sure build/ci work properly build: # make sure build/ci work properly
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- run: | - run: |
npm install npm install
- run: | - run: |

View File

@@ -1,5 +1,5 @@
--- ---
name: '@actions/core' name: "@actions/core"
version: 1.6.0 version: 1.6.0
type: npm type: npm
summary: Actions core lib summary: Actions core lib

View File

@@ -1,5 +1,5 @@
--- ---
name: '@actions/github' name: "@actions/github"
version: 5.0.0 version: 5.0.0
type: npm type: npm
summary: Actions github lib summary: Actions github lib

View File

@@ -1,5 +1,5 @@
--- ---
name: '@actions/http-client' name: "@actions/http-client"
version: 1.0.11 version: 1.0.11
type: npm type: npm
summary: Actions Http Client summary: Actions Http Client

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/auth-token' name: "@octokit/auth-token"
version: 2.5.0 version: 2.5.0
type: npm type: npm
summary: GitHub API token authentication for browsers and Node.js summary: GitHub API token authentication for browsers and Node.js
@@ -30,5 +30,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/core' name: "@octokit/core"
version: 3.5.1 version: 3.5.1
type: npm type: npm
summary: Extendable client for GitHub's REST & GraphQL APIs summary: Extendable client for GitHub's REST & GraphQL APIs
@@ -30,5 +30,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/endpoint' name: "@octokit/endpoint"
version: 6.0.12 version: 6.0.12
type: npm type: npm
summary: Turns REST API endpoints into generic request options summary: Turns REST API endpoints into generic request options
@@ -30,5 +30,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/graphql' name: "@octokit/graphql"
version: 4.8.0 version: 4.8.0
type: npm type: npm
summary: GitHub GraphQL API client for browsers and Node summary: GitHub GraphQL API client for browsers and Node
@@ -30,5 +30,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/openapi-types' name: "@octokit/openapi-types"
version: 11.2.0 version: 11.2.0
type: npm type: npm
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
@@ -16,5 +16,5 @@ licenses:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/plugin-paginate-rest' name: "@octokit/plugin-paginate-rest"
version: 2.17.0 version: 2.17.0
type: npm type: npm
summary: Octokit plugin to paginate REST API endpoint responses summary: Octokit plugin to paginate REST API endpoint responses
@@ -16,5 +16,5 @@ licenses:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/plugin-rest-endpoint-methods' name: "@octokit/plugin-rest-endpoint-methods"
version: 5.13.0 version: 5.13.0
type: npm type: npm
summary: Octokit plugin adding one method for all of api.github.com REST API endpoints summary: Octokit plugin adding one method for all of api.github.com REST API endpoints
@@ -16,5 +16,5 @@ licenses:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/request-error' name: "@octokit/request-error"
version: 2.1.0 version: 2.1.0
type: npm type: npm
summary: Error class for Octokit request errors summary: Error class for Octokit request errors
@@ -30,5 +30,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/request' name: "@octokit/request"
version: 5.6.3 version: 5.6.3
type: npm type: npm
summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers
@@ -31,5 +31,5 @@ licenses:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -1,5 +1,5 @@
--- ---
name: '@octokit/types' name: "@octokit/types"
version: 6.34.0 version: 6.34.0
type: npm type: npm
summary: Shared TypeScript definitions for Octokit projects summary: Shared TypeScript definitions for Octokit projects
@@ -16,5 +16,5 @@ licenses:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- sources: README.md - sources: README.md
text: '[MIT](LICENSE)' text: "[MIT](LICENSE)"
notices: [] notices: []

View File

@@ -210,5 +210,5 @@ licenses:
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
- sources: README.md - sources: README.md
text: '[Apache 2.0](LICENSE)' text: "[Apache 2.0](LICENSE)"
notices: [] notices: []

View File

@@ -24,5 +24,5 @@ licenses:
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- sources: README.md - sources: README.md
text: '[ISC](LICENSE)' text: "[ISC](LICENSE)"
notices: [] notices: []

View File

@@ -16,5 +16,5 @@ licenses:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- sources: README.md - sources: README.md
text: '[ISC](LICENSE.md)' text: "[ISC](LICENSE.md)"
notices: [] notices: []

108
README.md
View File

@@ -1,25 +1,16 @@
# actions/add-to-project # actions/add-to-project
Use this action to automatically add the current issue or pull request to a GitHub Project. 🚨 **This action is a work in progress. Please do not use it except for
Note that this is for [GitHub Projects experimentation until a release has been prepared.** 🚨
Use this action to automatically add issues to a GitHub Project. Note that this
is for [GitHub Projects
(beta)](https://docs.github.com/en/issues/trying-out-the-new-projects-experience/about-projects), (beta)](https://docs.github.com/en/issues/trying-out-the-new-projects-experience/about-projects),
not the original GitHub Projects. not the original GitHub Projects.
## Current Status
[![build-test](https://github.com/actions/add-to-project/actions/workflows/test.yml/badge.svg)](https://github.com/actions/add-to-project/actions/workflows/test.yml)
🚨 **This action is in beta, however the API is stable. Some breaking changes might occur between versions, but it is not likely to break as long as you use a specific SHA or version number** 🚨
## Usage
_See [action.yml](action.yml) for [metadata](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions) that defines the inputs, outputs, and runs configuration for this action._
_For more information about workflows, see [Using workflows](https://docs.github.com/en/actions/using-workflows)._
To use the action, create a workflow that runs when issues are opened in your 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 repository. Run this action in a step, optionally configuring any filters you
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`. may want to add, such as only adding issues with certain labels.
```yaml ```yaml
name: Add bugs to bugs project name: Add bugs to bugs project
@@ -34,90 +25,25 @@ jobs:
name: Add issue to project name: Add issue to project
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# Pointing to a branch name generally isn't the safest way to refer to an action,
# but this is how you can use this action now before we've begun creating releases.
# Another option would be to point to a full commit SHA.
- uses: actions/add-to-project@main - uses: actions/add-to-project@main
with: with:
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, new labeled: bug
label-operator: AND
``` ```
#### Further reading and additional resources
- [Inputs](#inputs)
- [Supported Events](#supported-events)
- [How to point the action to a specific branch or commit sha](#how-to-point-the-action-to-a-specific-branch-or-commit-sha)
- [Creating a PAT and adding it to your repository](#creating-a-pat-and-adding-it-to-your-repository)
- [Development](#development)
- [Publish to a distribution branch](#publish-to-a-distribution-branch)
## Inputs ## Inputs
- <a name="project-url">`project-url`</a> **(required)** is the URL of the GitHub Project to add issues to. - `project-url` is the URL of the GitHub Project to add issues to.
_eg: `https://github.com/orgs|users/<ownerName>/projects/<projectNumber>`_ - `github-token` is a [personal access
- <a name="github-token">`github-token`</a> **(required)** is a [personal access
token](https://github.com/settings/tokens/new) with the `repo`, `write:org` and token](https://github.com/settings/tokens/new) with the `repo`, `write:org` and
`read:org` scopes. `read:org` scopes.
_See [Creating a PAT and adding it to your repository](#creating-a-pat-and-adding-it-to-your-repository) for more details_ - `labeled` is a comma-separated list of labels. For an issue to be added to the
- <a name="labeled">`labeled`</a> **(optional)** is a comma-separated list of labels used to filter applicable issues. When this key is provided, an issue must have _one_ of the labels in the list to be added to the project. Omitting this key means that any issue will be added. project, it must have _one_ of the labels in the list. Omitting this key means
- <a name="labeled">`label-operator`</a> **(optional)** 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`. that all issues will be added.
## Supported Events
> **NOTE:** This Action (currently) only supports auto-adding Issues to a Project which lives in the same organization as your target repo.
Currently this action supports the following [`issues` events](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues):
- `opened`
- `transferred`
- `labeled`
and the following [`pull_request` events](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request):
- `opened`
- `labeled`
Using these events ensure that a given issue or pull request, in the workflow's repo, is added to the [specified project](#project-url). If [labeled input(s)](#labeled) are defined, then issues will only be added if they contain at least _one_ of the labels in the list.
## How to point the action to a specific branch or commit sha
Pointing to a branch name generally isn't the safest way to refer to an action, but this is how you can use this action now before we've begun creating releases.
```yaml
jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/<orgName>/projects/<projectNumber>
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
```
Another option would be to point to a full [commit SHA](https://docs.github.com/en/get-started/quickstart/github-glossary#commit):
```yaml
jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@<commitSHA>
with:
project-url: https://github.com/orgs/<orgName>/projects/<projectNumber>
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
```
## Creating a PAT and adding it to your repository
- create a new [personal access
token](https://github.com/settings/tokens/new) with `repo`, `write:org` and
`read:org` scopes
_See [Creating a personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) for more information_
- add the newly created PAT as a repository secret, this secret will be referenced by the [github-token input](#github-token)
_See [Encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) for more information_
## Development ## Development
@@ -150,7 +76,3 @@ the "dist/" directory.
``` ```
Now, a release can be created from the branch containing the built action. Now, a release can be created from the branch containing the built action.
# License
The scripts and documentation in this project are released under the [MIT License](LICENSE)

View File

@@ -1,6 +1,6 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as github from '@actions/github' import * as github from '@actions/github'
import {addToProject, mustGetOwnerTypeQuery} from '../src/add-to-project' import {addToProject} from '../src/add-to-project'
describe('addToProject', () => { describe('addToProject', () => {
let outputs: Record<string, string> let outputs: Record<string, string>
@@ -52,11 +52,11 @@ describe('addToProject', () => {
expect(outputs.itemId).toEqual('project-next-item-id') expect(outputs.itemId).toEqual('project-next-item-id')
}) })
test('adds matching issues with a label filter without label-operator', async () => { test('adds matching issues with a label filter', async () => {
mockGetInput({ mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1', 'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token', 'github-token': 'gh_token',
labeled: 'bug, new' labeled: 'bug'
}) })
github.context.payload = { github.context.payload = {
@@ -94,50 +94,7 @@ describe('addToProject', () => {
expect(outputs.itemId).toEqual('project-next-item-id') expect(outputs.itemId).toEqual('project-next-item-id')
}) })
test('adds matching pull-requests with a label filter without label-operator', async () => { test('does not add un-matching issues with a label filter', async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token',
labeled: 'bug, new'
})
github.context.payload = {
// eslint-disable-next-line camelcase
pull_request: {
number: 1,
labels: [{name: 'bug'}]
}
}
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 a label filter without label-operator', async () => {
mockGetInput({ mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1', 'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token', 'github-token': 'gh_token',
@@ -157,341 +114,6 @@ describe('addToProject', () => {
expect(infoSpy).toHaveBeenCalledWith(`Skipping issue 1 because it does not have one of the labels: bug`) expect(infoSpy).toHaveBeenCalledWith(`Skipping issue 1 because it does not have one of the labels: bug`)
expect(gqlMock).not.toHaveBeenCalled() 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()
})
test('adds matching issues with multiple label filters', async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token',
labeled: 'accessibility,backend,bug'
})
github.context.payload = {
issue: {
number: 1,
labels: [{name: 'accessibility'}, {name: 'backend'}]
}
}
const gqlMock = mockGraphQL(
{
test: /getProject/,
return: {
organization: {
projectNext: {
id: 'project-next-id'
}
}
}
},
{
test: /addProjectNextItem/,
return: {
addProjectNextItem: {
projectNextItem: {
id: 'project-next-item-id'
}
}
}
}
)
const infoSpy = jest.spyOn(core, 'info')
await addToProject()
expect(gqlMock).toHaveBeenCalled()
expect(infoSpy).not.toHaveBeenCalled()
expect(outputs.itemId).toEqual('project-next-item-id')
})
test('does not add un-matching issues with multiple label filters', async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token',
labeled: 'accessibility, backend, bug'
})
github.context.payload = {
issue: {
number: 1,
labels: [{name: 'data'}, {name: 'frontend'}, {name: 'improvement'}]
}
}
const infoSpy = jest.spyOn(core, 'info')
const gqlMock = mockGraphQL()
await addToProject()
expect(infoSpy).toHaveBeenCalledWith(
`Skipping issue 1 because it does not have one of the labels: accessibility, backend, bug`
)
expect(gqlMock).not.toHaveBeenCalled()
})
test('handles spaces and extra commas gracefully in label filter input', async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token',
labeled: 'accessibility , backend ,, . , bug'
})
github.context.payload = {
issue: {
number: 1,
labels: [{name: 'accessibility'}, {name: 'backend'}, {name: 'bug'}],
'label-operator': 'AND'
}
}
const gqlMock = mockGraphQL(
{
test: /getProject/,
return: {
organization: {
projectNext: {
id: 'project-next-id'
}
}
}
},
{
test: /addProjectNextItem/,
return: {
addProjectNextItem: {
projectNextItem: {
id: 'project-next-item-id'
}
}
}
}
)
const infoSpy = jest.spyOn(core, 'info')
await addToProject()
expect(gqlMock).toHaveBeenCalled()
expect(infoSpy).not.toHaveBeenCalled()
expect(outputs.itemId).toEqual('project-next-item-id')
})
test(`throws an error when url isn't a valid project url`, async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/repositories',
'github-token': 'gh_token'
})
github.context.payload = {
issue: {
number: 1,
labels: []
}
}
const infoSpy = jest.spyOn(core, 'info')
const gqlMock = mockGraphQL()
await expect(addToProject()).rejects.toThrow(
'https://github.com/orgs/github/repositories. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>'
)
expect(infoSpy).not.toHaveBeenCalled()
expect(gqlMock).not.toHaveBeenCalled()
})
test(`throws an error when url isn't under the github.com domain`, async () => {
mockGetInput({
'project-url': 'https://notgithub.com/orgs/github/projects/1',
'github-token': 'gh_token'
})
github.context.payload = {
issue: {
number: 1,
labels: []
}
}
const infoSpy = jest.spyOn(core, 'info')
const gqlMock = mockGraphQL()
await expect(addToProject()).rejects.toThrow(
'https://notgithub.com/orgs/github/projects/1. Project URL should match the format https://github.com/<orgs-or-users>/<ownerName>/projects/<projectNumber>'
)
expect(infoSpy).not.toHaveBeenCalled()
expect(gqlMock).not.toHaveBeenCalled()
})
test('constructs the correct graphQL query given an organization owner', async () => {
mockGetInput({
'project-url': 'https://github.com/orgs/github/projects/1',
'github-token': 'gh_token',
labeled: 'bug, new'
})
github.context.payload = {
issue: {
number: 1,
labels: [{name: 'bug'}]
}
}
const gqlMock = mockGraphQL(
{
test: /getProject/,
return: {
organization: {
projectNext: {
id: 'project-next-id'
}
}
}
},
{
test: /addProjectNextItem/,
return: {
addProjectNextItem: {
projectNextItem: {
id: 'project-next-item-id'
}
}
}
}
)
await addToProject()
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('organization(login: $ownerName)'), {
ownerName: 'github',
projectNumber: 1
})
})
test('constructs the correct graphQL query given a user owner', async () => {
mockGetInput({
'project-url': 'https://github.com/users/monalisa/projects/1',
'github-token': 'gh_token',
labeled: 'bug, new'
})
github.context.payload = {
issue: {
number: 1,
labels: [{name: 'bug'}]
}
}
const gqlMock = mockGraphQL(
{
test: /getProject/,
return: {
organization: {
projectNext: {
id: 'project-next-id'
}
}
}
},
{
test: /addProjectNextItem/,
return: {
addProjectNextItem: {
projectNextItem: {
id: 'project-next-item-id'
}
}
}
}
)
await addToProject()
expect(gqlMock).toHaveBeenNthCalledWith(1, expect.stringContaining('user(login: $ownerName)'), {
ownerName: 'monalisa',
projectNumber: 1
})
})
})
describe('mustGetOwnerTypeQuery', () => {
test('returns organization for orgs ownerType', async () => {
const ownerTypeQuery = mustGetOwnerTypeQuery('orgs')
expect(ownerTypeQuery).toEqual('organization')
})
test('returns user for users ownerType', async () => {
const ownerTypeQuery = mustGetOwnerTypeQuery('users')
expect(ownerTypeQuery).toEqual('user')
})
test('throws an error when an unsupported ownerType is set', async () => {
expect(() => {
mustGetOwnerTypeQuery('unknown')
}).toThrow(`Unsupported ownerType: unknown. Must be one of 'orgs' or 'users'`)
})
}) })
function mockGetInput(mocks: Record<string, string>): jest.SpyInstance { function mockGetInput(mocks: Record<string, string>): jest.SpyInstance {

View File

@@ -1,8 +1,6 @@
name: Add To GitHub Projects Beta name: Add to Project
description: Automatically add issues and PRs to GitHub projects beta description: Automatically add issues and PRs to GitHub projects
author: GitHub author: GitHub
branding:
icon: table
inputs: inputs:
project-url: project-url:
required: true required: true
@@ -13,9 +11,6 @@ inputs:
labeled: labeled:
required: false required: false
description: A comma-separated list of labels to use as a filter for issue to be added 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: runs:
using: 'node16' using: 'node16'
main: 'dist/index.js' main: 'dist/index.js'

29
dist/index.js generated vendored
View File

@@ -8,11 +8,7 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k); Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
@@ -39,7 +35,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.mustGetOwnerTypeQuery = exports.addToProject = void 0; exports.addToProject = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const github = __importStar(__nccwpck_require__(5438)); const github = __importStar(__nccwpck_require__(5438));
// TODO: Ensure this (and the Octokit client) works for non-github.com URLs, as well. // TODO: Ensure this (and the Octokit client) works for non-github.com URLs, as well.
@@ -55,20 +51,14 @@ function addToProject() {
.split(',') .split(',')
.map(l => l.trim()) .map(l => l.trim())
.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 octokit = github.getOctokit(ghToken); const octokit = github.getOctokit(ghToken);
const urlMatch = projectUrl.match(urlParse); 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); 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 based on the label-operator. // Ensure the issue matches our `labeled` filter, if provided.
if (labelOperator === 'and') { if (labeled.length > 0) {
if (!labeled.every(l => issueLabels.includes(l))) { const hasLabel = issueLabels.some(l => labeled.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(', ')}`); if (!hasLabel) {
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(', ')}`); 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; return;
} }
@@ -123,7 +113,6 @@ function mustGetOwnerTypeQuery(ownerType) {
} }
return ownerTypeQuery; return ownerTypeQuery;
} }
exports.mustGetOwnerTypeQuery = mustGetOwnerTypeQuery;
/***/ }), /***/ }),
@@ -135,11 +124,7 @@ exports.mustGetOwnerTypeQuery = mustGetOwnerTypeQuery;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k); Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

2
dist/sourcemap-register.js generated vendored

File diff suppressed because one or more lines are too long

2039
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,19 +13,19 @@
}, },
"devDependencies": { "devDependencies": {
"@github/prettier-config": "^0.0.4", "@github/prettier-config": "^0.0.4",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.0",
"@types/node": "~16.11.26", "@types/node": "^12.12.6",
"@typescript-eslint/parser": "^5.17.0", "@typescript-eslint/parser": "^5.10.2",
"@vercel/ncc": "^0.33.3", "@vercel/ncc": "^0.33.1",
"concurrently": "^7.1.0", "concurrently": "^7.0.0",
"eslint": "^8.12.0", "eslint": "^8.8.0",
"eslint-plugin-github": "^4.3.6", "eslint-plugin-github": "^4.3.5",
"eslint-plugin-jest": "^26.1.3", "eslint-plugin-jest": "^26.0.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"jest": "^27.5.1", "jest": "^27.4.7",
"prettier": "2.6.2", "prettier": "2.5.1",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.3",
"typescript": "^4.6.3" "typescript": "^4.5.5"
}, },
"keywords": [ "keywords": [
"actions", "actions",

View File

@@ -37,22 +37,18 @@ export async function addToProject(): Promise<void> {
.split(',') .split(',')
.map(l => l.trim()) .map(l => l.trim())
.filter(l => l.length > 0) ?? [] .filter(l => l.length > 0) ?? []
const labelOperator = core.getInput('label-operator').trim().toLocaleLowerCase()
const octokit = github.getOctokit(ghToken) const octokit = github.getOctokit(ghToken)
const urlMatch = projectUrl.match(urlParse) 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) const issueLabels: string[] = (issue?.labels ?? []).map((l: {name: string}) => l.name)
// Ensure the issue matches our `labeled` filter based on the label-operator. // Ensure the issue matches our `labeled` filter, if provided.
if (labelOperator === 'and') { if (labeled.length > 0) {
if (!labeled.every(l => issueLabels.includes(l))) { const hasLabel = issueLabels.some(l => labeled.includes(l))
core.info(`Skipping issue ${issue?.number} because it doesn't match all the labels: ${labeled.join(', ')}`)
return if (!hasLabel) {
}
} 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(', ')}`) core.info(`Skipping issue ${issue?.number} because it does not have one of the labels: ${labeled.join(', ')}`)
return return
} }
} }
@@ -115,7 +111,7 @@ export async function addToProject(): Promise<void> {
core.setOutput('itemId', addResp.addProjectNextItem.projectNextItem.id) core.setOutput('itemId', addResp.addProjectNextItem.projectNextItem.id)
} }
export function mustGetOwnerTypeQuery(ownerType?: string): 'organization' | 'user' { function mustGetOwnerTypeQuery(ownerType?: string): 'organization' | 'user' {
const ownerTypeQuery = ownerType === 'orgs' ? 'organization' : ownerType === 'users' ? 'user' : null const ownerTypeQuery = ownerType === 'orgs' ? 'organization' : ownerType === 'users' ? 'user' : null
if (!ownerTypeQuery) { if (!ownerTypeQuery) {