-
-
Notifications
You must be signed in to change notification settings - Fork 109
158 lines (138 loc) · 5.37 KB
/
Copy pathrelease-actor-guard.yml
File metadata and controls
158 lines (138 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
name: GUARD — Release Actor Authorization
on:
workflow_call:
inputs:
team_slug:
description: Team slug allowed to run release/deploy/provider workflows
required: false
type: string
default: release-admins
org:
description: GitHub organization that owns the team
required: false
type: string
default: ""
secrets:
RELEASE_BOT_APP_ID:
required: false
RELEASE_BOT_PRIVATE_KEY:
required: false
permissions:
contents: read
jobs:
authorize:
runs-on: ubuntu-latest
env:
RELEASE_BOT_APP_ID: ${{ secrets.RELEASE_BOT_APP_ID }}
RELEASE_BOT_PRIVATE_KEY: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
steps:
- name: Resolve guard inputs
id: params
env:
INPUT_ORG: ${{ inputs.org }}
INPUT_TEAM_SLUG: ${{ inputs.team_slug }}
REPO_OWNER: ${{ github.repository_owner }}
run: |
set -euo pipefail
actor="${GITHUB_TRIGGERING_ACTOR:-${GITHUB_ACTOR:-}}"
if [ -z "${actor}" ]; then
echo "Unable to resolve triggering actor." >&2
exit 1
fi
team_slug="${INPUT_TEAM_SLUG:-release-admins}"
org="${INPUT_ORG:-$REPO_OWNER}"
if [ -z "${org}" ]; then
echo "Unable to resolve organization owner." >&2
exit 1
fi
echo "actor=${actor}" >> "$GITHUB_OUTPUT"
echo "org=${org}" >> "$GITHUB_OUTPUT"
echo "team_slug=${team_slug}" >> "$GITHUB_OUTPUT"
echo "Authorizing actor '${actor}' against team '${org}/${team_slug}'."
- name: Create GitHub App token for guard checks
id: app_token
if: ${{ env.RELEASE_BOT_APP_ID != '' && env.RELEASE_BOT_PRIVATE_KEY != '' }}
uses: actions/create-github-app-token@v1
with:
app-id: ${{ env.RELEASE_BOT_APP_ID }}
private-key: ${{ env.RELEASE_BOT_PRIVATE_KEY }}
owner: ${{ steps.params.outputs.org }}
- name: Verify triggering actor is in release admin team
if: ${{ env.RELEASE_BOT_APP_ID != '' && env.RELEASE_BOT_PRIVATE_KEY != '' }}
env:
ACTOR: ${{ steps.params.outputs.actor }}
ORG: ${{ steps.params.outputs.org }}
TEAM_SLUG: ${{ steps.params.outputs.team_slug }}
APP_TOKEN: ${{ steps.app_token.outputs.token }}
run: |
set -euo pipefail
if [ -z "${APP_TOKEN:-}" ]; then
echo "Missing GitHub App token for release actor guard." >&2
exit 1
fi
endpoint="https://api.github.com/orgs/${ORG}/teams/${TEAM_SLUG}/memberships/${ACTOR}"
resp_file="$(mktemp)"
status="$(curl -sS \
--connect-timeout 10 \
--max-time 30 \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${APP_TOKEN}" \
"$endpoint" \
-o "$resp_file" \
-w "%{http_code}")"
case "$status" in
200)
echo "Actor '${ACTOR}' is a member of '${ORG}/${TEAM_SLUG}'."
;;
404)
echo "Actor '${ACTOR}' is not a member of '${ORG}/${TEAM_SLUG}' (or team is not visible to the app)." >&2
cat "$resp_file" >&2 || true
rm -f "$resp_file"
exit 1
;;
401|403)
echo "Release actor guard app token is not authorized to read team memberships for '${ORG}/${TEAM_SLUG}'." >&2
cat "$resp_file" >&2 || true
rm -f "$resp_file"
exit 1
;;
*)
echo "Unexpected response from team membership check (HTTP ${status})." >&2
cat "$resp_file" >&2 || true
rm -f "$resp_file"
exit 1
;;
esac
rm -f "$resp_file"
- name: Verify triggering actor is a repo admin (fallback)
if: ${{ env.RELEASE_BOT_APP_ID == '' || env.RELEASE_BOT_PRIVATE_KEY == '' }}
env:
ACTOR: ${{ steps.params.outputs.actor }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
echo "Release guard GitHub App credentials are not configured; falling back to repo admin check for '${ACTOR}'."
endpoint="https://api.github.com/repos/${REPO}/collaborators/${ACTOR}/permission"
resp_file="$(mktemp)"
status="$(curl -sS \
--connect-timeout 10 \
--max-time 30 \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
"$endpoint" \
-o "$resp_file" \
-w "%{http_code}")"
if [ "$status" != "200" ]; then
echo "Unable to determine repo permission for '${ACTOR}' (HTTP ${status})." >&2
cat "$resp_file" >&2 || true
rm -f "$resp_file"
exit 1
fi
perm="$(node -e 'const fs=require("fs"); const body=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.stdout.write(String(body.permission||""))' "$resp_file")"
rm -f "$resp_file"
if [ "$perm" != "admin" ]; then
echo "Actor '${ACTOR}' is not a repo admin (permission='${perm}')." >&2
exit 1
fi
echo "Actor '${ACTOR}' is a repo admin; allowing release workflow."