From 065bf0aaf553b1757ba1ea64ce75f8a9a6d5d64f Mon Sep 17 00:00:00 2001 From: Matteo Manzoni Date: Sat, 5 Feb 2022 17:26:40 +0100 Subject: [PATCH 1/4] new feature: github app auth support (#29) * Add GH App Auth support * Add doc for GH App Auth support --- README.md | 46 +++++++++++++++++++++++++++++-------- action.yml | 29 ++++++++++++++++++++--- gh_app_credential_helper.sh | 31 +++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 13 deletions(-) create mode 100755 gh_app_credential_helper.sh diff --git a/README.md b/README.md index f512cc0..bf1fb72 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,19 @@ Since the issues and pull requests from this repository are also managed by this ## Variables -| Variable | Required | Description | -| ------------------ | -------- |----------- | -| `gh_token` | true | The GitHub token to use for the automation. | -| `user` | false | The GitHub username that owns the projectboard. Either a user or an organization must be specified. | -| `organization` | false | The GitHub organization that owns the projectboard. Either a user or an organization must be specified. | -| `project_id` | true | The projectboard id. | -| `resource_node_id` | true | The id of the resource node. | -| `status_value` | false | The status value to set. Must be one of the values defined in your project board **Status field settings**. If left unspecified, new items are added without an explicit status, and existing items are left alone. | -| `operation_mode` | false | The operation mode to use. Must be one of `custom_field`, `status`. Defaults to: `status` | -| `custom_field_values` | false | Provides the possibility to change custom fields. To be applied the **operation_mode** must be set to `custom_field`. For the json definition refer to [JSON-Definition](#JSON-Definition) | +| Variable | Required | Description | +| ------------------------ | -------- |------------ | +| `gh_token` | false | The GitHub token to use for the automation. For App instructions refer to [GH App Auth](#GH-App-Auth). (`gh_token` or `gh_app_*` must be defined) | +| `gh_app_ID` | false | The GitHub App ID used for App authentication. For App instructions refer to [GH App Auth](#GH-App-Auth). (`gh_token` or `gh_app_*` must be defined) | +| `gh_app_installation_ID` | false | The Github App installation ID binding the App to the target org. For App instructions refer to [GH App Auth](#GH-App-Auth). (`gh_token` or `gh_app_*` must be defined) | +| `gh_app_secret_key` | false | The Github App Secret key used to sign App JWT tokens. For App instructions refer to [GH App Auth](#GH-App-Auth). (`gh_token` or `gh_app_*` must be defined) | +| `user` | false | The GitHub username that owns the projectboard. Either a user or an organization must be specified. | +| `organization` | false | The GitHub organization that owns the projectboard. Either a user or an organization must be specified. | +| `project_id` | true | The projectboard id. | +| `resource_node_id` | true | The id of the resource node. | +| `status_value` | false | The status value to set. Must be one of the values defined in your project board **Status field settings**. If left unspecified, new items are added without an explicit status, and existing items are left alone. | +| `operation_mode` | false | The operation mode to use. Must be one of `custom_field`, `status`. Defaults to: `status` | +| `custom_field_values` | false | Provides the possibility to change custom fields. To be applied the **operation_mode** must be set to `custom_field`. For the json definition refer to [JSON-Definition](#JSON-Definition) | ## Getting started @@ -123,6 +126,29 @@ jobs: status_value: ${{ env.done }} # Target status ``` +## GH App Auth + +To leverage the App authentication with this action the following steps are needed: + +- Create a GitHub App under your user or organisation as described in the [GitHub documentation](https://docs.github.com/en/developers/apps/building-github-apps/creating-a-github-app), note the App ID +- Set the needed GitHub App permissions in order for the newly create App to access and manage Org Project, as described in the [GitHub documentation](https://docs.github.com/en/developers/apps/managing-github-apps/editing-a-github-apps-permissions). The minimum required permissions are: + - Repo: Actions: RW + - Repo: Checks: RO + - Repo: Contents: RO + - Repo: Environments: RO + - Repo: Metadata: RO + - Repo: PR: RW + - Repo: Commit statuses: RO + - Org: Members: RO + - Org: Projects: RW +- Create a private key to authenticate the newly created GitHub App as described in the [GitHub documentation](https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps), treat the downloaded key as sensitive material, store it in a GitHub secret accessible by this action. +- Install the app in the target organisation, note the installation ID +- Configure this action with + - `gh_app_secret_key` containing the aforementioned private key + - `gh_app_ID` containing the app ID, auto-generated by GitHub in the first step + - `gh_app_installation_ID` containing the installation ID, auto-generated by GitHub in the previous step. Binging the App to the Org + + ## JSON-Definition A single json object is defined as follows: diff --git a/action.yml b/action.yml index 6448f92..43b0b00 100644 --- a/action.yml +++ b/action.yml @@ -2,8 +2,20 @@ name: "project beta automations" description: 'GitHub beta projects status field automation for Issues and Pull Requests' inputs: gh_token: - description: 'Permission token that grants permission to the GitHub API.' - required: true + description: 'Permission token that grants permission to the GitHub API. (Toke or App config is required)' + required: false + default: "" + gh_app_secret_key: + description: 'Github App secret RSA key used to sign App JWT tokens' + required: false + default: "" + gh_app_ID: + description: 'Github App ID used to authenticate againts the API' + required: false + default: "" + gh_app_installation_ID: + description: 'Github App installation ID used to retrive installation token' + required: false default: "" organization: description: 'Organization the project is stored in' @@ -46,7 +58,18 @@ branding: runs: using: "composite" steps: - - name: "Authenticate gh cli" + - name: "Error missing auth conf" + if: inputs.gh_app_secret_key == '' && inputs.gh_app_ID == '' && inputs.gh_token == '' + shell: bash + run: echo "No GH Auth method configured, provide PAT or App ID/Key"; exit 1 + + - name: "Authenticate gh cli Github App" + if: inputs.gh_app_secret_key != '' && inputs.gh_app_ID != '' && inputs.gh_app_installation_ID + shell: bash + run: "${{ github.action_path }}/ghapp_credential_helper.sh \"${{ inputs.gh_app_secret_key }}\" \"${{ inputs.gh_app_ID }}\" \"${{ inputs.gh_app_installation_ID }}\" | gh auth login --with-token" + + - name: "Authenticate gh cli PAT" + if: inputs.gh_token != '' shell: bash run: echo "${{ inputs.gh_token }}" | gh auth login --with-token diff --git a/gh_app_credential_helper.sh b/gh_app_credential_helper.sh new file mode 100755 index 0000000..69e1caa --- /dev/null +++ b/gh_app_credential_helper.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +if [ "$DEBUG_COMMANDS" = "true" ]; then + set -ex +fi + +# DEBUG_MODE_ENABLED provides a way to enable the debug mode +# export DEBUG_MODE=true +DEBUG_MODE_ENABLED="${DEBUG_MODE:-false}" + +APP_SIGN_KEY="$1" +APP_ID="$2" +APP_INSTALLATION_ID="$3" + +TOKEN_IAT="$( date +%s )" +TOKEN_EXP="$((TOKEN_IAT + 570))" + +RAW_TOKEN_HEADER='{"alg":"RS256"}' +TOKEN_HEADER=$( echo -n "${RAW_TOKEN_HEADER}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' ) + +RAW_TOKEN_PAYLOAD="{\"iat\":${TOKEN_IAT},\"exp\":${TOKEN_EXP},\"iss\":\"${APP_ID}\"}" +TOKEN_PAYLOAD=$( echo -n "${RAW_TOKEN_PAYLOAD}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' ) + +TOKEN_BODY="${TOKEN_HEADER}.${TOKEN_PAYLOAD}" +TOKEN_SIGNATURE=$( openssl dgst -sha256 -sign <(echo -n "${APP_SIGN_KEY}") <(echo -n "${TOKEN_BODY}") | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' ) + +GH_APP_TOKEN="${TOKEN_BODY}.${TOKEN_SIGNATURE}" + +GH_APP_INSTALLATION_TOKEN=$(curl -XPOST -H "Authorization: Bearer $GH_APP_TOKEN" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/app/installations/$APP_INSTALLATION_ID/access_tokens" | jq -r .token) + +echo "$GH_APP_INSTALLATION_TOKEN" From 6894d446f6c7102b659aacf90a56ff6b6ef3d6d8 Mon Sep 17 00:00:00 2001 From: leonsteinhaeuser Date: Sat, 5 Feb 2022 17:44:27 +0100 Subject: [PATCH 2/4] fix: typo in action.yml where the wrong script was referenced --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 43b0b00..70957f0 100644 --- a/action.yml +++ b/action.yml @@ -66,7 +66,7 @@ runs: - name: "Authenticate gh cli Github App" if: inputs.gh_app_secret_key != '' && inputs.gh_app_ID != '' && inputs.gh_app_installation_ID shell: bash - run: "${{ github.action_path }}/ghapp_credential_helper.sh \"${{ inputs.gh_app_secret_key }}\" \"${{ inputs.gh_app_ID }}\" \"${{ inputs.gh_app_installation_ID }}\" | gh auth login --with-token" + run: "${{ github.action_path }}/gh_app_credential_helper.sh \"${{ inputs.gh_app_secret_key }}\" \"${{ inputs.gh_app_ID }}\" \"${{ inputs.gh_app_installation_ID }}\" | gh auth login --with-token" - name: "Authenticate gh cli PAT" if: inputs.gh_token != '' From 65d74a84b25abf07bffa7954cab9597e3532e229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Steinh=C3=A4user?= <42437185+leonsteinhaeuser@users.noreply.github.com> Date: Sun, 6 Feb 2022 17:21:50 +0100 Subject: [PATCH 3/4] docs: added badges to README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index bf1fb72..dedfca3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # project-beta-automations +[![latest release](https://img.shields.io/github/v/release/leonsteinhaeuser/project-beta-automations)](https://img.shields.io/github/v/release/leonsteinhaeuser/project-beta-automations) +[![release date](https://img.shields.io/github/release-date/leonsteinhaeuser/project-beta-automations)](https://img.shields.io/github/release-date/leonsteinhaeuser/project-beta-automations) +[![commits since release](https://img.shields.io/github/commits-since/leonsteinhaeuser/project-beta-automations/latest)](https://img.shields.io/github/commits-since/leonsteinhaeuser/project-beta-automations/latest) +[![open: bugs](https://img.shields.io/github/issues/leonsteinhaeuser/project-beta-automations/bug)](https://img.shields.io/github/issues/leonsteinhaeuser/project-beta-automations/bug) +[![open: feature requests](https://img.shields.io/github/issues/leonsteinhaeuser/project-beta-automations/feature%20request)](https://img.shields.io/github/issues/leonsteinhaeuser/project-beta-automations/feature%20request) +[![issues closed](https://img.shields.io/github/issues-closed/leonsteinhaeuser/project-beta-automations)](https://img.shields.io/github/issues-closed/leonsteinhaeuser/project-beta-automations) +[![license](https://img.shields.io/github/license/leonsteinhaeuser/project-beta-automations)](https://img.shields.io/github/license/leonsteinhaeuser/project-beta-automations) + This repository provides the ability to automate GitHub issues and pull requests for [Github Projects (Beta)](https://docs.github.com/en/issues/trying-out-the-new-projects-experience/about-projects). To do this, it automates the **Status** and user-defined fields to put issues and pull requests into the desired status, and therefore the desired column in the Board view. If the issue or pull request does not already exist in the project, it will be added. Note: GITHUB_TOKEN does not have the necessary scopes to access projects (beta). From 07cb88f81a846e70afe6f869b9860a757cbfd246 Mon Sep 17 00:00:00 2001 From: leonsteinhaeuser Date: Thu, 10 Feb 2022 17:03:20 +0100 Subject: [PATCH 4/4] docs: bump version to v1.2.0 --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index dedfca3..8141387 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ jobs: if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened') steps: - name: Move issue to ${{ env.todo }} - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 with: gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} user: sample-user @@ -96,7 +96,7 @@ jobs: if: github.event_name == 'issues' && github.event.action == 'closed' steps: - name: Moved issue to ${{ env.done }} - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 with: gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} user: sample-user @@ -110,7 +110,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 ${{ env.in_progress }} - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 with: gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} user: sample-user @@ -124,7 +124,7 @@ jobs: if: github.event_name == 'pull_request' && github.event.action == 'closed' steps: - name: Move PR to ${{ env.done }} - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 with: gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} user: sample-user @@ -230,7 +230,7 @@ jobs: if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened') steps: - name: 'Move issue to ${{ env.status_todo }}' - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 env: DEBUG_LOG: "true" with: @@ -248,7 +248,7 @@ jobs: - issue_opened_or_reopened steps: - name: 'Modify custom fields' - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 env: DEBUG_LOG: "true" with: @@ -265,7 +265,7 @@ jobs: if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened') steps: - name: 'Move PR to ${{ env.status_in_progress }}' - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 env: DEBUG_LOG: "true" with: @@ -283,7 +283,7 @@ jobs: - pr_opened_or_reopened steps: - name: 'Modify custom fields' - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 env: DEBUG_LOG: "true" with: @@ -324,7 +324,7 @@ jobs: if: github.event_name == 'issues' && github.event.action == 'opened' steps: - name: Move issue to ${{ env.todo }} - uses: leonsteinhaeuser/project-beta-automations@v1.1.1 + uses: leonsteinhaeuser/project-beta-automations@v1.2.0 env: DEBUG_COMMANDS: true DEBUG_LOG: true