|
10 | 10 |
|
11 | 11 | jobs: |
12 | 12 | authorize: |
13 | | - name: Check owner or developer access |
| 13 | + name: Check repository access |
14 | 14 | runs-on: ubuntu-latest |
15 | 15 | timeout-minutes: 2 |
16 | | - # Adding some other label must not start e2e. |
17 | 16 | if: github.event.action != 'labeled' || github.event.label.name == 'ok-to-test' |
18 | 17 | permissions: |
19 | 18 | contents: read |
| 19 | + members: read |
20 | 20 | outputs: |
21 | 21 | authorized: ${{ steps.check.outputs.authorized }} |
22 | 22 | steps: |
23 | | - - name: Verify executor is owner or developer |
| 23 | + - name: Allow repo members, or ok-to-test from an owner |
24 | 24 | id: check |
25 | 25 | env: |
26 | 26 | GH_TOKEN: ${{ github.token }} |
27 | 27 | EVENT_NAME: ${{ github.event_name }} |
28 | 28 | EVENT_ACTION: ${{ github.event.action }} |
29 | 29 | USER: ${{ github.event.pull_request.user.login || github.actor }} |
30 | 30 | ACTOR: ${{ github.actor }} |
31 | | - ASSOCIATION: ${{ github.event.pull_request.author_association }} |
32 | 31 | REPOSITORY: ${{ github.repository }} |
33 | 32 | run: | |
34 | 33 | set -euo pipefail |
35 | 34 |
|
36 | 35 | allow() { echo "$1"; echo "authorized=true" >> "$GITHUB_OUTPUT"; exit 0; } |
37 | 36 | deny() { echo "::warning::$1"; echo "authorized=false" >> "$GITHUB_OUTPUT"; exit 0; } |
38 | 37 |
|
39 | | - perm() { |
40 | | - local encoded |
41 | | - encoded=$(jq -nr --arg u "$1" '$u|@uri') |
42 | | - gh api "repos/${REPOSITORY}/collaborators/${encoded}/permission" --jq .permission 2>/dev/null || echo none |
| 38 | + encode() { jq -nr --arg u "$1" '$u|@uri'; } |
| 39 | +
|
| 40 | + # True if the user is on this repository or an org owner/member. |
| 41 | + is_repo_user_or_member() { |
| 42 | + local encoded org role |
| 43 | + encoded=$(encode "$1") |
| 44 | + if gh api --silent "repos/${REPOSITORY}/collaborators/${encoded}"; then |
| 45 | + return 0 |
| 46 | + fi |
| 47 | + org="${REPOSITORY%%/*}" |
| 48 | + role=$(gh api "orgs/${org}/memberships/${encoded}" \ |
| 49 | + --jq 'if .state == "active" then .role else "none" end' 2>/dev/null || echo none) |
| 50 | + [ "$role" = "admin" ] || [ "$role" = "member" ] |
| 51 | + } |
| 52 | +
|
| 53 | + repo_permission() { |
| 54 | + gh api "repos/${REPOSITORY}/collaborators/$(encode "$1")/permission" --jq .permission 2>/dev/null || echo none |
43 | 55 | } |
44 | 56 |
|
45 | 57 | [ "$EVENT_NAME" = "schedule" ] && allow "Scheduled run." |
46 | 58 |
|
47 | | - USER_PERM=$(perm "$USER") |
48 | | - echo "executor=$USER permission=$USER_PERM association=${ASSOCIATION:-none}" |
49 | | - if [ "$USER_PERM" = "admin" ] || [ "$USER_PERM" = "write" ]; then |
50 | | - allow "$USER is an owner or developer." |
51 | | - fi |
52 | | - # Org members often only have read on this repo, so perm() is not enough. |
53 | | - if [ "$ASSOCIATION" = "OWNER" ] || [ "$ASSOCIATION" = "MEMBER" ]; then |
54 | | - allow "$USER is an organization $ASSOCIATION." |
| 59 | + echo "executor=$USER" |
| 60 | + if is_repo_user_or_member "$USER"; then |
| 61 | + allow "$USER is a user or member of ${REPOSITORY}." |
55 | 62 | fi |
56 | 63 |
|
57 | | - # ok-to-test only counts on the labeled event, so a later push cannot |
58 | | - # reuse an old owner approval against a new PR head. |
59 | | - [ "$EVENT_ACTION" = "labeled" ] || deny "$USER is not an owner, member, or developer." |
| 64 | + [ "$EVENT_ACTION" = "labeled" ] || deny "$USER is not a user or member of ${REPOSITORY}." |
60 | 65 |
|
61 | | - LABELER_PERM=$(perm "$ACTOR") |
| 66 | + LABELER_PERM=$(repo_permission "$ACTOR") |
62 | 67 | echo "ok-to-test labeler=$ACTOR permission=$LABELER_PERM" |
63 | 68 | [ "$LABELER_PERM" = "admin" ] && allow "Owner $ACTOR added ok-to-test." |
64 | 69 |
|
65 | | - deny "ok-to-test was not added by an owner." |
| 70 | + deny "ok-to-test must be added by a repository owner." |
0 commit comments