├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml └── entrypoint.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # name: Auto Assign to Project(s) 2 | 3 | # on: 4 | # issues: 5 | # types: [opened, labeled] 6 | # pull_request: 7 | # types: [opened, labeled] 8 | # env: 9 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 10 | 11 | # jobs: 12 | # assign_one_project: 13 | # runs-on: ubuntu-latest 14 | # name: Assign to One Project 15 | # steps: 16 | # - name: Assign NEW issues and NEW pull requests to project 2 17 | # uses: srggrs/assign-one-project-github-action@1.2.0 18 | # if: github.event.action == 'opened' 19 | # with: 20 | # project: 'https://github.com/srggrs/assign-one-project-github-action/projects/2' 21 | 22 | # - name: Assign issues and pull requests with `bug` label to project 3 23 | # uses: srggrs/assign-one-project-github-action@1.2.0 24 | # if: | 25 | # contains(github.event.issue.labels.*.name, 'bug') || 26 | # contains(github.event.pull_request.labels.*.name, 'bug') 27 | # with: 28 | # project: 'https://github.com/srggrs/assign-one-project-github-action/projects/3' 29 | # column_name: 'Labeled' 30 | name: Debug JSON 31 | 32 | on: 33 | issues: 34 | types: [opened, labeled, unlabeled] 35 | pull_request: 36 | types: [opened, labeled, unlabeled] 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | jobs: 41 | debugging: 42 | runs-on: ubuntu-latest 43 | name: Debugging 44 | steps: 45 | - name: Debugging JSON 46 | run: | 47 | echo "Event Type: ${{ github.event_name }}" 48 | env 49 | cat $GITHUB_EVENT_PATH 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime* 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), with an added `List of PRs` section and links to the relevant PRs on the individal updates. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased](https://github.com/srggrs/assign-one-project-github-action/compare/master...HEAD) 9 | 10 | #### Added 11 | 12 | TBC 13 | 14 | #### Changed 15 | 16 | TBC 17 | 18 | #### Fixed 19 | 20 | TBC 21 | 22 | #### Removed 23 | 24 | TBC 25 | 26 | ## [1.3.1](https://github.com/srggrs/assign-one-project-github-action/compare/1.3.1...HEAD) (2021-10-04) 27 | 28 | #### Added 29 | 30 | * Added `issue_comment` as trigger [#76](https://github.com/srggrs/assign-one-project-github-action/pull/76). 31 | 32 | #### Fixed 33 | 34 | * Better error messages [#1dbdca2](https://github.com/srggrs/assign-one-project-github-action/commit/1dbdca2) 35 | 36 | ## [1.3.0](https://github.com/srggrs/assign-one-project-github-action/compare/1.3.0...HEAD) (2021-09-26) 37 | 38 | #### Added 39 | * Added pagination in project finding [#69](https://github.com/srggrs/assign-one-project-github-action/pull/69). 40 | 41 | #### Fixed 42 | * Change documentation: `event` to `event_name` [#75](https://github.com/srggrs/assign-one-project-github-action/pull/75). 43 | 44 | 45 | ## [1.2.1](https://github.com/srggrs/assign-one-project-github-action/compare/prep-release-1.2.1...HEAD) (2021-03-07) 46 | 47 | #### Added 48 | * Added support for repository forks with `pull request target` event [#62](https://github.com/srggrs/assign-one-project-github-action/pull/62). 49 | * Added Changelog [#66](https://github.com/srggrs/assign-one-project-github-action/pull/66). 50 | * Added Code of Conduct [#66](https://github.com/srggrs/assign-one-project-github-action/pull/66). 51 | 52 | #### Changed 53 | 54 | * Changed README with latest changes [#66](https://github.com/srggrs/assign-one-project-github-action/pull/66). 55 | * Change action to use local Docker image [#68](https://github.com/srggrs/assign-one-project-github-action/pull/68). 56 | 57 | ## [1.2.0](https://github.com/srggrs/assign-one-project-github-action/compare/prep-release-1.2.0...HEAD) () 58 | 59 | Last release to start tracking changes. 60 | 61 | Features: 62 | * add issue or PR to a project column 63 | * specify the name of the column [#35](https://github.com/srggrs/assign-one-project-github-action/pull/35) 64 | 65 | #### List of PRs 66 | 67 | - [#68](https://github.com/srggrs/assign-one-project-github-action/pull/68) fix: change action to use local dockerfile. 68 | - [#66](https://github.com/srggrs/assign-one-project-github-action/pull/66) doc: added Changelog and code of conduct, change readme. 69 | - [#62](https://github.com/srggrs/assign-one-project-github-action/pull/62) feat: added support for Pull request target for forks of repository. 70 | - [#59](https://github.com/srggrs/assign-one-project-github-action/pull/59) doc: fix typo. 71 | - [#35](https://github.com/srggrs/assign-one-project-github-action/pull/35) feat: add support for custom column name. 72 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Code Of Conduct 3 | 4 | This project is for a community based on openness, as well as friendly and didactic discussions. 5 | 6 | Such community should aspire to treat everybody equally, and value their contributions. 7 | 8 | Decisions are made based on technical merit and consensus. 9 | 10 | Code is not the only way to help the project. Reviewing pull requests, answering questions to help others on mailing lists or issues, organizing and teaching tutorials, working on the website, improving the documentation, are all priceless contributions. 11 | 12 | Such community should abide by the principles of openness, respect, and consideration of others of the Python Software Foundation: https://www.python.org/psf/codeofconduct/ 13 | 14 | ## Extra Guidelines 15 | Please follow these guidelines in all your interactions: 16 | 17 | - __Behave professionally__. Harassment and sexist, racist, or exclusionary comments or jokes are not appropriate. Harassment includes sustained disruption of talks or other events, inappropriate physical contact, sexual attention or innuendo, deliberate intimidation, stalking, and photography or recording of an individual without consent. It also includes offensive comments related to gender, sexual orientation, disability, physical appearance, body size, race or religion. 18 | - __All communication should be appropriate__ for a professional audience including people of many different backgrounds. Sexual language and imagery is not appropriate. 19 | - __Be kind to others__. Do not insult or put down other GitHub users. 20 | - Participants asked to stop any inappropriate behaviour are expected to comply immediately. People violating these rules may be blocked at the discretion of the mantainers. 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Container image that runs your code 2 | FROM alpine:3.10 3 | 4 | RUN apk add --no-cache --no-progress curl jq 5 | 6 | # Copies your code file from your action repository to the filesystem path `/` of the container 7 | COPY entrypoint.sh /entrypoint.sh 8 | # Code file to execute when the docker container starts up (`entrypoint.sh`) 9 | ENTRYPOINT ["/entrypoint.sh"] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sergio Pintaldi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Action for Assign to One Project 2 | 3 | [![Docker Cloud Automated build](https://img.shields.io/docker/cloud/automated/srggrs/assign-one-project-github-action)][docker] 4 | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/srggrs/assign-one-project-github-action)][docker] 5 | [![Docker Pulls](https://img.shields.io/docker/pulls/srggrs/assign-one-project-github-action)][docker] 6 | [![GitHub license](https://img.shields.io/github/license/srggrs/assign-one-project-github-action.svg)][license] 7 | ![Latest Version](https://img.shields.io/github/v/release/srggrs/assign-one-project-github-action?color=orange&label=latest%20release) 8 | 9 | [docker]: https://hub.docker.com/r/srggrs/assign-one-project-github-action 10 | [license]: https://github.com/srggrs/assign-one-project-github-action/blob/master/LICENSE 11 | 12 | Automatically add an issue or pull request to specific [GitHub Project](https://help.github.com/articles/about-project-boards/) when you __create__ and/or __label__ them. By default, the issues are assigned to the __`To do`__ column and the pull requests to the __`In progress`__ one, so make sure you have those columns in your project dashboard. But the workflow __allowed you to specify the column name as input__, so you can assign the issues/PRs based on a set of conditions to a specific column of a specific project. 13 | 14 | ## Latest features: 15 | 16 | * included `issue_comment` as trigger for this action. 17 | * added project pagination for searching 100+ GitHub projects. 18 | 19 | ## Acknowledgment & Motivations 20 | 21 | This action has been modified from the original action from [masutaka](https://github.com/masutaka/github-actions-all-in-one-project). I needed to fix it as the original docker container would not build. Also I think the GitHub Action syntax changed a bit. 22 | 23 | I would like to thank @SunRunAway for adding the labelling functionality and custom column input. 24 | 25 | ## Inputs 26 | 27 | ### `project` 28 | 29 | **Required** The url of the project to be assigned to. 30 | 31 | ### `column_name` 32 | 33 | The column name of the project, defaults to `'To do'` for issues and `'In progress'` for pull requests. 34 | 35 | ## Example usage 36 | 37 | Examples of action: 38 | 39 | ### Repository project 40 | 41 | ```yaml 42 | name: Auto Assign to Project(s) 43 | 44 | on: 45 | issues: 46 | types: [opened, labeled] 47 | pull_request: 48 | types: [opened, labeled] 49 | issue_comment: 50 | types: [created] 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | 54 | jobs: 55 | assign_one_project: 56 | runs-on: ubuntu-latest 57 | name: Assign to One Project 58 | steps: 59 | - name: Assign NEW issues and NEW pull requests to project 2 60 | uses: srggrs/assign-one-project-github-action@1.2.1 61 | if: github.event.action == 'opened' 62 | with: 63 | project: 'https://github.com/srggrs/assign-one-project-github-action/projects/2' 64 | 65 | - name: Assign issues and pull requests with `bug` label to project 3 66 | uses: srggrs/assign-one-project-github-action@1.2.1 67 | if: | 68 | contains(github.event.issue.labels.*.name, 'bug') || 69 | contains(github.event.pull_request.labels.*.name, 'bug') 70 | with: 71 | project: 'https://github.com/srggrs/assign-one-project-github-action/projects/3' 72 | column_name: 'Labeled' 73 | ``` 74 | 75 | #### __Notes__ 76 | Be careful of using the conditions above (opened and labeled issues/PRs) because in such workflow, if the issue/PR is opened and labeled at the same time, it will be assigned to __both__ projects! 77 | 78 | 79 | You can use any combination of conditions. For example, to assign new issues or issues labeled with 'mylabel' to a project column, use: 80 | ```yaml 81 | ... 82 | 83 | if: | 84 | github.event_name == 'issues' && 85 | ( 86 | github.event.action == 'opened' || 87 | contains(github.event.issue.labels.*.name, 'mylabel') 88 | ) 89 | ... 90 | ``` 91 | 92 | ### Organisation or User project 93 | 94 | Generate a token from the Organisation settings or User Settings and add it as a secret in the repository secrets as `MY_GITHUB_TOKEN` 95 | 96 | ```yaml 97 | name: Auto Assign to Project(s) 98 | 99 | on: 100 | issues: 101 | types: [opened, labeled] 102 | pull_request_target: 103 | types: [opened, labeled] 104 | issue_comment: 105 | types: [created] 106 | env: 107 | MY_GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} 108 | 109 | jobs: 110 | assign_one_project: 111 | runs-on: ubuntu-latest 112 | name: Assign to One Project 113 | steps: 114 | - name: Assign NEW issues and NEW pull requests to project 2 115 | uses: srggrs/assign-one-project-github-action@1.2.1 116 | if: github.event.action == 'opened' 117 | with: 118 | project: 'https://github.com/srggrs/assign-one-project-github-action/projects/2' 119 | 120 | - name: Assign issues and pull requests with `bug` label to project 3 121 | uses: srggrs/assign-one-project-github-action@1.2.1 122 | if: | 123 | contains(github.event.issue.labels.*.name, 'bug') || 124 | contains(github.event.pull_request.labels.*.name, 'bug') 125 | with: 126 | project: 'https://github.com/srggrs/assign-one-project-github-action/projects/3' 127 | column_name: 'Labeled' 128 | ``` 129 | 130 | ## [Change Log](./CHANGELOG.md) 131 | 132 | Please refer to the list of changes [here](./CHANGELOG.md) 133 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: 'Assign to One Project' 3 | description: 'Assign new/labeled Issue or Pull Request to a specific project dashboard column' 4 | author: srggrs 5 | inputs: 6 | project: 7 | description: 'The url of the project to be assigned to.' 8 | required: true 9 | column_name: 10 | description: 'The column name of the project, defaults to "To do" for issues and "In progress" for pull requests.' 11 | required: false 12 | 13 | runs: 14 | using: 'docker' 15 | image: 'Dockerfile' 16 | args: 17 | - ${{ inputs.project }} 18 | - ${{ inputs.column_name }} 19 | 20 | branding: 21 | icon: 'box' 22 | color: 'red' 23 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | 3 | PROJECT_URL="$INPUT_PROJECT" 4 | if [ -z "$PROJECT_URL" ]; then 5 | echo "Project input variable is not defined." >&2 6 | exit 1 7 | fi 8 | 9 | get_project_type() { 10 | _PROJECT_URL="$1" 11 | 12 | case "$_PROJECT_URL" in 13 | https://github.com/orgs/*) 14 | echo "org" 15 | ;; 16 | https://github.com/users/*) 17 | echo "user" 18 | ;; 19 | https://github.com/*/projects/*) 20 | echo "repo" 21 | ;; 22 | *) 23 | echo "Invalid Project URL: '$_PROJECT_URL' . Please pass a valid Project URL in the project input variable" >&2 24 | exit 1 25 | ;; 26 | esac 27 | 28 | unset _PROJECT_URL 29 | } 30 | 31 | get_next_url_from_headers() { 32 | _HEADERS_FILE=$1 33 | grep -i '^link' "$_HEADERS_FILE" | tr ',' '\n'| grep \"next\" | sed 's/.*<\(.*\)>.*/\1/' 34 | } 35 | 36 | find_project_id() { 37 | _PROJECT_TYPE="$1" 38 | _PROJECT_URL="$2" 39 | 40 | case "$_PROJECT_TYPE" in 41 | org) 42 | _ORG_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/orgs/\([^/]\+\)/projects/[0-9]\+@\1@') 43 | _ENDPOINT="https://api.github.com/orgs/$_ORG_NAME/projects?per_page=100" 44 | ;; 45 | user) 46 | _USER_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/users/\([^/]\+\)/projects/[0-9]\+@\1@') 47 | _ENDPOINT="https://api.github.com/users/$_USER_NAME/projects?per_page=100" 48 | ;; 49 | repo) 50 | _ENDPOINT="https://api.github.com/repos/$GITHUB_REPOSITORY/projects?per_page=100" 51 | ;; 52 | esac 53 | 54 | _NEXT_URL="$_ENDPOINT" 55 | 56 | while : ; do 57 | 58 | _PROJECTS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ 59 | -H 'Accept: application/vnd.github.inertia-preview+json' \ 60 | -D /tmp/headers \ 61 | "$_NEXT_URL") 62 | 63 | _PROJECTID=$(echo "$_PROJECTS" | jq -r ".[] | select(.html_url == \"$_PROJECT_URL\").id") 64 | _NEXT_URL=$(get_next_url_from_headers '/tmp/headers') 65 | 66 | if [ "$_PROJECTID" != "" ]; then 67 | echo "$_PROJECTID" 68 | elif [ "$_NEXT_URL" == "" ]; then 69 | echo "No project was found." >&2 70 | exit 1 71 | fi 72 | done 73 | 74 | unset _PROJECT_TYPE _PROJECT_URL _ORG_NAME _USER_NAME _ENDPOINT _PROJECTS _PROJECTID _NEXT_URL 75 | } 76 | 77 | find_column_id() { 78 | _PROJECT_ID="$1" 79 | _INITIAL_COLUMN_NAME="$2" 80 | 81 | _COLUMNS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ 82 | -H 'Accept: application/vnd.github.inertia-preview+json' \ 83 | "https://api.github.com/projects/$_PROJECT_ID/columns") 84 | 85 | 86 | echo "$_COLUMNS" | jq -r ".[] | select(.name == \"$_INITIAL_COLUMN_NAME\").id" 87 | unset _PROJECT_ID _INITIAL_COLUMN_NAME _COLUMNS 88 | } 89 | 90 | PROJECT_TYPE=$(get_project_type "${PROJECT_URL:? required this environment variable}") 91 | 92 | if [ "$PROJECT_TYPE" = org ] || [ "$PROJECT_TYPE" = user ]; then 93 | if [ -z "$MY_GITHUB_TOKEN" ]; then 94 | echo "MY_GITHUB_TOKEN not defined" >&2 95 | exit 1 96 | fi 97 | 98 | TOKEN="$MY_GITHUB_TOKEN" # It's User's personal access token. It should be secret. 99 | else 100 | if [ -z "$GITHUB_TOKEN" ]; then 101 | echo "GITHUB_TOKEN not defined" >&2 102 | exit 1 103 | fi 104 | 105 | TOKEN="$GITHUB_TOKEN" # GitHub sets. The scope in only the repository containing the workflow file. 106 | fi 107 | 108 | INITIAL_COLUMN_NAME="$INPUT_COLUMN_NAME" 109 | if [ -z "$INITIAL_COLUMN_NAME" ]; then 110 | # assing the column name by default 111 | INITIAL_COLUMN_NAME='To do' 112 | if [ "$GITHUB_EVENT_NAME" == "pull_request" ] || [ "$GITHUB_EVENT_NAME" == "pull_request_target" ]; then 113 | echo "changing column name for PR event" 114 | INITIAL_COLUMN_NAME='In progress' 115 | fi 116 | fi 117 | 118 | 119 | PROJECT_ID=$(find_project_id "$PROJECT_TYPE" "$PROJECT_URL") 120 | INITIAL_COLUMN_ID=$(find_column_id "$PROJECT_ID" "${INITIAL_COLUMN_NAME:? required this environment variable}") 121 | 122 | if [ -z "$INITIAL_COLUMN_ID" ]; then 123 | echo "Column name '$INITIAL_COLUMN_ID' is not found." >&2 124 | exit 1 125 | fi 126 | 127 | case "$GITHUB_EVENT_NAME" in 128 | issues|issue_comment) 129 | ISSUE_ID=$(jq -r '.issue.id' < "$GITHUB_EVENT_PATH") 130 | 131 | # Add this issue to the project column 132 | curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ 133 | -H 'Accept: application/vnd.github.inertia-preview+json' \ 134 | -d "{\"content_type\": \"Issue\", \"content_id\": $ISSUE_ID}" \ 135 | "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards" 136 | ;; 137 | pull_request|pull_request_target) 138 | PULL_REQUEST_ID=$(jq -r '.pull_request.id' < "$GITHUB_EVENT_PATH") 139 | 140 | # Add this pull_request to the project column 141 | curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ 142 | -H 'Accept: application/vnd.github.inertia-preview+json' \ 143 | -d "{\"content_type\": \"PullRequest\", \"content_id\": $PULL_REQUEST_ID}" \ 144 | "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards" 145 | ;; 146 | *) 147 | echo "Nothing to be done on this action: '$GITHUB_EVENT_NAME'" >&2 148 | exit 1 149 | ;; 150 | esac 151 | --------------------------------------------------------------------------------