Skip to content

Commit

Permalink
Merge pull request #18 from joshjohanning/v2
Browse files Browse the repository at this point in the history
Updating to v2 of the action. 

- use gh api instead of curl #9 
- fix pagination #12 
- remove dependency on app action #13 
- remove deprecated set-output references #14 
- update comment action #15 
- add approve command as input #16 
- pin action versions #17
  • Loading branch information
joshjohanning authored Mar 7, 2023
2 parents 849b5f6 + 216735f commit 70770ae
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 137 deletions.
86 changes: 0 additions & 86 deletions .github/workflows/approveops.yml

This file was deleted.

80 changes: 65 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,83 @@ See the following guide on this action: https://josh-ops.com/posts/github-approv
## Usage

```yml
- name: ApproveOps - Approvals in IssueOps
uses: joshjohanning/approveops@v1
id: check-approval
with:
app-id: 170284 # The GitHub App ID; ie: 170284
app-private-key: ${{ secrets.PRIVATE_KEY }} # Private key for the GitHub App that is installed on the repo; e.g.: ${{ secrets.PRIVATE_KEY }}
team-name: approver-team # The name of the team in GitHub to check for the approval command; e.g.: approver-team
fail-if-approval-not-found: false # Fail the action (show the action run as red) if the command is not found in the comments from someone in the approver team"
post-successful-approval-comment: true # Boolean whether to post successful approval comment
successful-approval-comment: ':tada: You were able to run the workflow because someone left an approval in the comments!! :tada:' # Comment to post if there is an approval is found
name: ApproveOps
on:
issue_comment:
types: [created]

env:
approver_team_name: 'approver-team'
approval_command: '/approve'

jobs:
approveops:
runs-on: ubuntu-latest
if: contains(github.event.comment.body, '/do-stuff')

steps:
# get the app's installation token
- uses: tibdex/github-app-token@v1
id: get_installation_token
with:
app_id: 170284
private_key: ${{ secrets.PRIVATE_KEY }}

- name: ApproveOps - Approvals in IssueOps
uses: joshjohanning/approveops@v2
id: check-approval
with:
token: ${{ steps.get_installation_token.outputs.token }} # use a github app token or a PAT
approve-command: '${{ env.approval_command }}' # Optional, defaults to '/approve', the command to look for in the comments
team-name: ${{ env.approver_team_name }} # The name of the team in GitHub to check for the approval command; e.g.: approver-team
fail-if-approval-not-found: false # Optional, defaults to true, fail the action (show the action run as red) if the command is not found in the comments from someone in the approver team"
post-successful-approval-comment: true # Optional, defaults to true, whether to post successful approval comment
successful-approval-comment: ':tada: You were able to run the workflow because someone left an approval in the comments!! :tada:' # Optional, comment to post if an approval is found
```
## Prerequisites
1. Create a GitHub team and add at least one member
1. You will need a Github App with the following permissions:
- **read-only** on `Organization / Members` to list the members of the team
- **read & write** on `Repository / Issues` to create the comment
1. Generate a `PRIVATE_KEY` for the GitHub app and store it as a repo or organizational secret
1. Capture the `APP ID` to use as an input for this action
2. Authentication options:
- GitHub App
- If you are using a GitHub Github App, it will need following permissions:
- **read & write** on `Repository / Issues` to create the comment
- **read-only** on `Organization / Members` to list the members of the team
- Generate a `PRIVATE_KEY` for the GitHub app and store it as a repo or organizational secret
- Note the `APP ID` to use as an input for an action like `tibdex/github-app-token@v1`
- Classic PAT
- If you are using a classic PAT, it will need the following scopes:
- `repo` - to create the comment
- `read:org` - to list the members of the team
- Fine-grained PAT
- If you are using a fine-grained PAT, it will need following permissions (same as GitHub App):
- **read & write** on `Repository / Issues` to create the comment
- **read-only** on `Organization / Members` to list the members of the team

See the following guide on creating a GitHub app: https://josh-ops.com/posts/github-apps/

Notes:
- A Personal Access Token (PAT) is not used since we want the comment to show as from a bot
- The `github.token` is not used since the token can't provide hyperlinks for @ mentions since it doesn't have the scope for org teams, only repository data

## Breaking Changes

### v1 to v2

Extracting the logic for generating a GitHub App's installation token so that you can either use an alternative action or method to retrieve the token or to be able use a GitHub PAT instead.

Added/removed the following inputs:

| Input | Action | Required | Note |
|-------------------|---------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `token` | Added | Yes | GitHub App installation token or PAT that has access to read+write comments and list the org team's membership; ie `${{ steps.get_installation_token.outputs.token }}` |
| `approve-command` | Added | No | Optional, defaults to `/approve`, the command to look for in the comments |
| `app-id` | Removed | Yes | The app ID for a GitHub App ie `170284` |
| `app-private-key` | Removed | Yes | The private key for a GitHub App, ie: `${{ secrets.APP_PRIVATE_KEY }}` |

Removed the following dependency:
- `tibdex/github-app-token@v1`

## Screenshots

![approveops](https://user-images.githubusercontent.com/19912012/154545687-8d64a775-eec2-4ec7-90dc-901b2d6d39a5.png)
Expand Down
67 changes: 31 additions & 36 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ branding:
color: "blue"

inputs:
approve-command:
description: "The command to look for in the comments; e.g.: /approve"
required: true
default: '/approve'
team-name:
description: "The name of the team in GitHub to check for the approval command; e.g.: approver-team"
required: true
app-private-key:
description: "Private key for the GitHub App that is installed on the repo; e.g.: secrets.PRIVATE_KEY"
required: true
app-id:
description: "The GitHub App ID; ie: 170284"
token:
description: "GitHub App installation token or PAT that has access to read the comments and check the org team's membership"
required: true
fail-if-approval-not-found:
description: "Fail the action (i.e. show the action run as red) if the command is not found in the comments from someone in the approver team"
Expand All @@ -24,37 +25,29 @@ inputs:
required: true
default: 'true'
successful-approval-comment:
description: "Comment to post if there is an approval is found"
description: "Comment to post if an approval is found"
required: true
default: ":tada: You were able to run the workflow because someone left an approval in the comments!! :tada:"

outputs:
approved:
description: "Returns 'true' if the approval command was found in a comment from someone in the approver team, otherwise 'false'"
value: ${{ steps.check-approval.outputs.approved }}

runs:
using: "composite"
steps:
- uses: tibdex/github-app-token@v1
id: get_installation_token
with:
app_id: ${{ inputs.app-id }}
private_key: ${{ inputs.app-private-key }}

- id: check-approval
name: check if there is an approve command from authorized party
env:
GH_TOKEN: ${{ inputs.token }}
shell: bash
run: |
# "checking for a /approve command in the comments from someone in the approver team"
users=$(curl -sLX GET 'https://api.github.com/orgs/${{ github.repository_owner }}/teams/${{ inputs.team-name }}/members' \
--header "Accept: application/vnd.github.v3+json" \
--header "Authorization: Bearer ${{ steps.get_installation_token.outputs.token }}" | jq -c '.[].login')
approveCommand="/approve"
comments=$(curl -sLX GET '${{ github.event.comment.issue_url }}/comments?&per_page=100' \
--header "Accept: application/vnd.github.v3+json" \
--header "Authorization: Bearer ${{ steps.get_installation_token.outputs.token }}")
# "checking for a ${{ inputs.approve-command }} command in the comments from someone in the approver team"
users=$(gh api --paginate '/orgs/${{ github.repository_owner }}/teams/${{ inputs.team-name }}/members' | jq -c '.[].login')
approveCommand="${{ inputs.approve-command }}"
authorized=false
comments=$(gh api --paginate '${{ github.event.comment.issue_url }}/comments')
for comment in $(echo $comments | jq -r '.[] | @base64'); do
body=$(echo $comment | base64 --decode | jq -r '.body' | tr -d ' ' | tr -d '\r\n')
actor=$(echo $comment | base64 --decode | jq -r '.user.login')
Expand All @@ -69,42 +62,44 @@ runs:
done
if $authorized; then
echo "Approval authorized by $actor"
echo "::set-output name=approved::true"
echo "approved=true" >> $GITHUB_OUTPUT
else
echo "Approval not found or not authorized"
echo "::set-output name=approved::false"
echo "approved=false" >> $GITHUB_OUTPUT
if !(${{ inputs.fail-if-approval-not-found }}); then
echo "::notice title=Not Approved::There is no /approve command in the comments from someone in the ${{ github.repository_owner }}/${{ inputs.team-name }} team"
echo "::notice title=Not Approved::There is no ${{ inputs.approve-command }} command in the comments from someone in the ${{ github.repository_owner }}/${{ inputs.team-name }} team"
fi
fi
- if: ${{ steps.check-approval.outputs.approved == 'false' && inputs.fail-if-approval-not-found == 'true' }}
name: Create completed comment
uses: peter-evans/create-or-update-comment@v1
uses: peter-evans/create-or-update-comment@v2.1.1
with:
token: ${{ steps.get_installation_token.outputs.token }}
token: ${{ inputs.token }}
issue-number: ${{ github.event.issue.number }}
body: |
Hey, @${{ github.event.comment.user.login }}!
:cry: No one approved your run yet! Have someone from the @${{ github.repository_owner }}/${{ inputs.team-name }} team run `/approve` and then try your command again
:no_entry_sign: :no_entry: Marking the [workflow run](${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}) as failed
:cry: No one approved your run yet! Have someone from the @${{ github.repository_owner }}/${{ inputs.team-name }} team run `${{ inputs.approve-command }}` and then try your command again
_:no_entry_sign: :no_entry: Marking the [workflow run](${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}) as failed_
- if: ${{ steps.check-approval.outputs.approved == 'false' && inputs.fail-if-approval-not-found == 'false' }}
name: Create completed comment
uses: peter-evans/create-or-update-comment@v1
uses: peter-evans/create-or-update-comment@v2.1.1
with:
token: ${{ steps.get_installation_token.outputs.token }}
token: ${{ inputs.token }}
issue-number: ${{ github.event.issue.number }}
body: |
Hey, @${{ github.event.comment.user.login }}!
:cry: No one approved your run yet! Have someone from the @${{ github.repository_owner }}/${{ inputs.team-name }} team run `/approve` and then try your command again
:warning: :pause_button: The [workflow run](${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}) wasn't marked as failed
:cry: No one approved your run yet! Have someone from the @${{ github.repository_owner }}/${{ inputs.team-name }} team run `${{ inputs.approve-command }}` and then try your command again
_:warning: :pause_button: See [workflow run](${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}) for reference_
- if: ${{ steps.check-approval.outputs.approved == 'true' && inputs.post-successful-approval-comment == 'true' }}
name: Create completed comment
uses: peter-evans/create-or-update-comment@v1
uses: peter-evans/create-or-update-comment@v2.1.1
with:
token: ${{ steps.get_installation_token.outputs.token }}
token: ${{ inputs.token }}
issue-number: ${{ github.event.issue.number }}
body: |
Hey, @${{ github.event.comment.user.login }}!
Expand All @@ -113,7 +108,7 @@ runs:
# if specified, exit with an error if approval is not found
- name: exit and fail workflow if not approved
if: ${{ inputs.fail-if-approval-not-found == 'true' && steps.check-approval.outputs.approved == 'false' }}
uses: actions/github-script@v6
uses: actions/github-script@v6.4.0
with:
script: |
core.setFailed("There is no /approve command in the comments from someone in the ${{ github.repository_owner }}/${{ inputs.team-name }} team");
core.setFailed("There is no ${{ inputs.approve-command }} command in the comments from someone in the ${{ github.repository_owner }}/${{ inputs.team-name }} team");

0 comments on commit 70770ae

Please sign in to comment.