Skip to content

Commit

Permalink
feature: user based project support (#13)
Browse files Browse the repository at this point in the history
* implemented user based project beta support

* Update action.yml

* Added user field
* Removed organization requirement
* Added precondition checks for user and organization field
* Added user based project beta execution

* added documentation related to user based project automation
  • Loading branch information
leonsteinhaeuser committed Nov 14, 2021
1 parent 35944de commit e71cc18
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 7 deletions.
97 changes: 92 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Note: GITHUB_TOKEN does not have the necessary scopes to access projects (beta).
You must create a token with ***org:write*** scope and save it as a secret in your repository or organization.
In the following workflow, replace ***YOUR_TOKEN*** with the name of the secret. For more information, see [Creating a personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).

Organization based:

```yaml
name: Project automations
on:
Expand All @@ -30,7 +32,7 @@ jobs:
if: github.event_name == 'issues' && github.event.action == 'opened' || github.event.action == 'reopened'
steps:
- name: 'Move issue to "Todo"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.1
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
organization: sample-org
Expand All @@ -43,7 +45,7 @@ jobs:
if: github.event_name == 'issues' && github.event.action == 'closed'
steps:
- name: 'Moved issue to "Done"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.1
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
organization: sample-org
Expand All @@ -56,7 +58,7 @@ jobs:
if: github.event_name == 'pull_request' && github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'review_requested'
steps:
- name: 'Move PR to "In Progress"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.1
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
organization: sample-org
Expand All @@ -69,7 +71,7 @@ jobs:
if: github.event_name == 'pull_request' && github.event.action == 'ready_for_review'
steps:
- name: 'Move PR to "Ready for Review"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.1
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
organization: sample-org
Expand All @@ -82,11 +84,96 @@ jobs:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
steps:
- name: 'Move PR to "Closed"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.1
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
organization: sample-org
project_id: 1
resource_node_id: ${{ github.event.pull_request.node_id }}
status_value: "Done"
```

User based:

```yaml
name: Project automations
on:
issues:
types:
- opened
- reopened
- closed
pull_request:
types:
- opened
- ready_for_review
- reopened
- review_requested
- closed
jobs:
issue_opened_and_reopened:
name: issue_opened_and_reopened
runs-on: ubuntu-latest
if: github.event_name == 'issues' && github.event.action == 'opened' || github.event.action == 'reopened'
steps:
- name: 'Move issue to "Todo"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
user: sample-user
project_id: 1
resource_node_id: ${{ github.event.issue.node_id }}
status_value: "Todo"
issue_closed:
name: issue_closed
runs-on: ubuntu-latest
if: github.event_name == 'issues' && github.event.action == 'closed'
steps:
- name: 'Moved issue to "Done"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
user: sample-user
project_id: 1
resource_node_id: ${{ github.event.issue.node_id }}
status_value: "Done"
pr_opened_reopened_review_requested:
name: pr_opened_reopened_review_requested
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'review_requested'
steps:
- name: 'Move PR to "In Progress"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
user: sample-user
project_id: 1
resource_node_id: ${{ github.event.pull_request.node_id }}
status_value: "In Progress"
pr_ready_for_review:
name: pr_ready_for_review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'ready_for_review'
steps:
- name: 'Move PR to "Ready for Review"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
user: sample-user
project_id: 1
resource_node_id: ${{ github.event.pull_request.node_id }}
status_value: "Ready for Review"
pr_closed:
name: pr_closed
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'closed'
steps:
- name: 'Move PR to "Closed"'
uses: leonsteinhaeuser/project-beta-automations@v1.0.2
with:
gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
user: sample-user
project_id: 1
resource_node_id: ${{ github.event.pull_request.node_id }}
status_value: "Done"
```
31 changes: 29 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ inputs:
default: ""
organization:
description: 'Organization the project is stored in'
required: true
required: false
default: ""
user:
description: 'User the project is stored in'
required: false
default: ""
project_id:
description: 'Project ID owned by the organization'
Expand All @@ -28,9 +32,32 @@ runs:
shell: bash
run: echo "${{ inputs.gh_token }}" | gh auth login --with-token

- name: "Execute automation"
- name: "Check if User or Organization is set"
if: inputs.organization == '' && inputs.user == ''
uses: actions/github-script@v3
with:
script: |
core.setFailed('either user or organization is not specified.')
- name: "Check if User and Organization are set"
if: inputs.organization != '' && inputs.user != ''
uses: actions/github-script@v3
with:
script: |
core.setFailed('user and organization field is set. Only one is supported.')
- name: "Execute Organization automation"
if: inputs.organization != ''
working-directory: ${{ github.action_path }}
env:
GITHUB_TOKEN: ${{ inputs.gh_token }}
shell: bash
run: "${{ github.action_path }}/entrypoint.sh \"${{ inputs.organization }}\" \"${{ inputs.project_id }}\" \"${{ inputs.resource_node_id }}\" \"${{ inputs.status_value }}\""

- name: "Execute User automation"
if: inputs.user != ''
working-directory: ${{ github.action_path }}
env:
GITHUB_TOKEN: ${{ inputs.gh_token }}
shell: bash
run: "${{ github.action_path }}/entrypoint_user.sh \"${{ inputs.user }}\" \"${{ inputs.project_id }}\" \"${{ inputs.resource_node_id }}\" \"${{ inputs.status_value }}\""
28 changes: 28 additions & 0 deletions entrypoint_user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

organization_name=$1 # organization name
organization_project_id=$2 # organization id
resource_id=$3 # pr or issue node id
status_value=$4 # text representation of the card

# load lib bash functions
source gh_api_lib_user.sh

# request gh api and returns the project settings
getProject $organization_name $organization_project_id

PROJECT_ID=$(extractProjectID)

ITEM_ID=$(getItemID $PROJECT_ID $resource_id)

STATUS_FIELD_ID=$(extractStatusFieldID)
# select field values
status_value_OPTION_ID=$(extractStatusFieldNodeSettingsByValue "$status_value")

echo "PROJECT_ID: $PROJECT_ID"
echo "ITEM_ID: $ITEM_ID"
echo "STATUS_FIELD_ID: $STATUS_FIELD_ID"
echo "status_value_OPTION_ID: $status_value_OPTION_ID"

# update single select field
updateSingleSelectField "$PROJECT_ID" "$ITEM_ID" "$STATUS_FIELD_ID" "$status_value_OPTION_ID"
125 changes: 125 additions & 0 deletions gh_api_lib_user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash

TMP_STORE_LOCATION=/tmp/api_response.json

# getProject queries the github api for the specific project
function getProject() {
local USER=$1
local PROJECT_NUMBER=$2
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
query($user: String!, $number: Int!) {
user(login: $user){
projectNext(number: $number) {
id
fields(first:20) {
nodes {
id
name
settings
}
}
}
}
}' -f user=$USER -F number=$PROJECT_NUMBER > $TMP_STORE_LOCATION
}

# extractProjectID returns the project id
function extractProjectID() {
echo $(jq '.data.user.projectNext.id' $TMP_STORE_LOCATION | sed -e "s+\"++g")
}

# extractStatusFieldID returns the status field id
function extractStatusFieldID() {
echo $(jq '.data.user.projectNext.fields.nodes[] | select(.name== "Status") | .id' $TMP_STORE_LOCATION | sed -e "s+\"++g")
}

# extractStatusFieldNodeSettingsByValue returns a list of available settings
function extractStatusFieldNodeSettingsByValue() {
local STATUS_NAME=$1
jq ".data.user.projectNext.fields.nodes[] | select(.name== \"Status\") | .settings | fromjson.options[] | select(.name==\"$STATUS_NAME\") |.id" $TMP_STORE_LOCATION | sed -e "s+\"++g"
}

# getItemID queries the github api for the specific item_id
# Required arguments:
# 1: project id
# 2: resource node id
function getItemID() {
local project_id=$1
local resource_id=$2
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation($project:ID!, $resource_id:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $resource_id}) {
projectNextItem {
id
}
}
}' -f project=$project_id -f resource_id=$resource_id --jq '.data.addProjectNextItem.projectNextItem.id' | sed -e "s+\"++g"
}

# updateSingleSelectField updates the given item field with the defined value
# Required arguments:
# 1: project id
# 2: project item id
# 3: field id
# 4: field option id
function updateSingleSelectField() {
local project_id=$1
local item_id=$2
local field_id=$3
local field_option=$4
echo $(gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation (
$project: ID!
$item: ID!
$fieldid: ID!
$fieldOption: ID!
) {
updateProjectNextItemField(
input: {
projectId: $project
itemId: $item
fieldId: $fieldid
value: $fieldOption
}
)
{
projectNextItem {
id
}
}
}' -f project=$project_id -f item=$item_id -f fieldid=$field_id -f fieldOption=$field_option | sed -e "s+\"++g")
}

# updateNonSingleSelectField updates the given item field with the defined value
# Required arguments:
# 1: project id
# 2: project item id
# 3: field id
# 4: field value
function updateNonSingleSelectField() {
local PROJECT_ID=$1
local ITEM_ID=$2
local FIELD_ID=$3
local FIELD_VALUE$4
echo $(gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation (
$project: ID!
$item: ID!
$fieldid: ID!
$fieldOption: String!
) {
updateProjectNextItemField(
input: {
projectId: $project
itemId: $item
fieldId: $fieldid
value: $fieldOption
}
)
{
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f fieldid=$FIELD_ID -f fieldOption=$FIELD_VALUE | sed -e "s+\"++g")
}

0 comments on commit e71cc18

Please sign in to comment.