Skip to content

Commit c261496

Browse files
authored
Move check-permissions, should-run, and determine-ref utils actions (#20)
* Move check-permissions, should-run, and determine-ref utils actions Signed-off-by: Jakub Stejskal <xstejs24@gmail.com> * Minor changes after self-review Signed-off-by: Jakub Stejskal <xstejs24@gmail.com> * Add docs and release triggers Signed-off-by: Jakub Stejskal <xstejs24@gmail.com> --------- Signed-off-by: Jakub Stejskal <xstejs24@gmail.com>
1 parent d5e3f2c commit c261496

20 files changed

Lines changed: 701 additions & 1 deletion
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Verify users rights
2+
description: "Fails the workflow run if the triggering user lacks write permission or is not in the given organisation team."
3+
4+
inputs:
5+
message:
6+
description: "Message that will be shown as comment in case of access denial"
7+
default: "⚠️ You don’t have permission to run this workflow. Please ask a maintainer to trigger it for you."
8+
team:
9+
description: "Name of the team that contains trusted contributors"
10+
default: "contributors"
11+
12+
runs:
13+
using: composite
14+
steps:
15+
- name: Check repository permission / team membership
16+
uses: actions/github-script@v8
17+
env:
18+
TEAM: ${{ inputs.team }}
19+
MESSAGE: ${{ inputs.message }}
20+
with:
21+
script: |
22+
const {owner, repo} = context.repo;
23+
// Get actor from event payload first, fallback to GITHUB_ACTOR
24+
const actor = context.payload.actor ||
25+
context.actor ||
26+
process.env.GITHUB_ACTOR;
27+
const team = process.env.TEAM
28+
const denialMessage = process.env.MESSAGE
29+
30+
//------------------------------------------------------------------
31+
// 1) Check collaborator permission (works for user *and* org repos)
32+
//------------------------------------------------------------------
33+
const perm = (await github.rest.repos.getCollaboratorPermissionLevel({
34+
owner, repo, username: actor
35+
})).data.permission; // admin | maintain | write | triage | read | none
36+
37+
if (['admin', 'maintain', 'write'].includes(perm)) {
38+
core.info(`${actor} has ${perm} permission → authorised ✅`);
39+
return;
40+
}
41+
42+
//------------------------------------------------------------------
43+
// 2) Repo is under an organisation? Then allow specific team members
44+
//------------------------------------------------------------------
45+
if (context.payload.repository.owner.type === 'Organization') {
46+
try {
47+
await github.rest.teams.getMembershipForUserInOrg({
48+
org: owner,
49+
team_slug: team,
50+
username: actor
51+
});
52+
core.info(`${actor} is in org team “${team}” → authorised ✅`);
53+
return;
54+
} catch (_) {
55+
core.info(`${actor} is not in team “${team}”.`);
56+
}
57+
}
58+
59+
let orgMember = true;
60+
try {
61+
await github.rest.orgs.getMembershipForUser({org: owner, username: actor});
62+
core.info(`${actor} is in org” → authorised ✅`);
63+
return;
64+
} catch (_) {
65+
core.info(`${actor} is not in org “${owner}”.`);
66+
}
67+
68+
//------------------------------------------------------------------
69+
// 3) Post PR comment if applicable, then fail
70+
//------------------------------------------------------------------
71+
if (context.payload.pull_request || context.payload.issue?.pull_request) {
72+
const issueNumber = (context.payload.pull_request?.number)
73+
?? (context.payload.issue.number);
74+
await github.rest.issues.createComment({
75+
owner, repo,
76+
issue_number: issueNumber,
77+
body: denialMessage
78+
});
79+
core.info(`Refusal comment posted to PR #${issueNumber}`);
80+
}
81+
82+
core.setFailed(`${actor} is not authorised ❌`);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: "Determine checkout ref"
2+
description: "Determines the correct ref to checkout based on event context"
3+
4+
outputs:
5+
ref:
6+
description: "The ref to checkout"
7+
value: ${{ steps.determine.outputs.ref }}
8+
sha:
9+
description: "The SHA to checkout"
10+
value: ${{ steps.determine.outputs.sha }}
11+
12+
runs:
13+
using: "composite"
14+
steps:
15+
- name: Determine ref and SHA
16+
id: determine
17+
uses: actions/github-script@v8
18+
with:
19+
script: |
20+
const {owner, repo} = context.repo;
21+
let ref, sha;
22+
23+
if (context.eventName === 'pull_request') {
24+
// For PR events, use merge ref to test the merged state
25+
const prNumber = context.payload.pull_request.number;
26+
ref = `refs/pull/${prNumber}/merge`;
27+
sha = context.payload.pull_request.head.sha;
28+
core.info(`PR event detected: Using merge ref for PR #${prNumber}`);
29+
core.info(`Head SHA: ${sha}`);
30+
} else if (context.eventName === 'issue_comment' && context.payload.issue?.pull_request) {
31+
// For PR comments, fetch the PR to get head SHA
32+
const prNumber = context.payload.issue.number;
33+
ref = `refs/pull/${prNumber}/merge`;
34+
35+
const pr = await github.rest.pulls.get({
36+
owner,
37+
repo,
38+
pull_number: prNumber
39+
});
40+
sha = pr.data.head.sha;
41+
42+
core.info(`PR comment detected: Using merge ref for PR #${prNumber}`);
43+
core.info(`Head SHA: ${sha}`);
44+
} else {
45+
// For workflow_dispatch and other events, use the current branch
46+
ref = context.ref;
47+
sha = context.sha;
48+
core.info(`Standard event: Using current ref ${ref}`);
49+
core.info(`SHA: ${sha}`);
50+
}
51+
52+
core.setOutput('ref', ref);
53+
core.setOutput('sha', sha);
54+
core.exportVariable('REF', ref);
55+
core.exportVariable('SHA', sha);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: "Should Run"
2+
description: "Determines whether the workflow should proceed based on event context"
3+
4+
outputs:
5+
shouldRun:
6+
description: "Flag if tests were triggered via comment or UI which leads to running the following jobs"
7+
value: ${{ steps.should_run.outputs.shouldRun }}
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
- name: Should Run
13+
id: should_run
14+
uses: actions/github-script@v8
15+
with:
16+
script: |
17+
const ev = context.eventName;
18+
const body = context.payload?.comment?.body ?? '';
19+
let shouldRun = false;
20+
21+
if (ev === 'workflow_dispatch') {
22+
shouldRun = true;
23+
} else if (ev === 'pull_request') {
24+
// Auto-run smoke tests on PR workflow changes
25+
shouldRun = true;
26+
} else if (ev === 'issue_comment' && context.payload?.issue?.pull_request) {
27+
// For issue comments on PRs, check if PR is closed first
28+
const issueState = context.payload?.issue?.state;
29+
if (issueState === 'closed') {
30+
core.info(`PR #${context.payload.issue.number} is closed, skipping execution`);
31+
shouldRun = false;
32+
} else {
33+
shouldRun = /^\s*\/gha\s+run\b/i.test(body || '');
34+
}
35+
}
36+
37+
core.setOutput('shouldRun', String(shouldRun));
38+
core.info(`Parsed shouldRun: ${shouldRun}`);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"event_name": "pull_request",
3+
"action": "opened",
4+
"actor": "frawless",
5+
"pull_request": {
6+
"number": 123,
7+
"title": "Test authorized user",
8+
"head": {
9+
"ref": "test-branch",
10+
"sha": "abc123456789",
11+
"repo": {
12+
"name": "ci-playground",
13+
"owner": { "login": "frawless" }
14+
}
15+
}
16+
},
17+
"repository": {
18+
"name": "ci-playground",
19+
"owner": {
20+
"login": "frawless",
21+
"type": "User"
22+
}
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"event_name": "pull_request",
3+
"action": "opened",
4+
"actor": "pepa",
5+
"pull_request": {
6+
"number": 66666666666,
7+
"title": "Test unauthorized user",
8+
"head": {
9+
"ref": "test-branch",
10+
"sha": "def987654321",
11+
"repo": {
12+
"name": "ci-playground",
13+
"owner": { "login": "pepa" }
14+
}
15+
}
16+
},
17+
"repository": {
18+
"name": "ci-playground",
19+
"owner": {
20+
"login": "frawless",
21+
"type": "User"
22+
}
23+
}
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"action": "workflow_dispatch",
3+
"event_name": "workflow_dispatch",
4+
"inputs": {},
5+
"repository": {
6+
"name": "test-repo",
7+
"owner": { "login": "batman" }
8+
},
9+
"ref": "refs/heads/main",
10+
"head_commit": {
11+
"id": "3333333333333333333333333333333333333333"
12+
}
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"event_name": "issue_comment",
3+
"action": "created",
4+
"comment": {
5+
"body": "/gha run profile=operators,operands"
6+
},
7+
"issue": {
8+
"number": 123,
9+
"state": "closed"
10+
},
11+
"repository": {
12+
"name": "test-repo",
13+
"owner": { "login": "batman" }
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"event_name": "issue_comment",
3+
"action": "created",
4+
"comment": {
5+
"body": "/gha run pipeline=regression,upgrade"
6+
},
7+
"issue": {
8+
"number": 42,
9+
"state": "closed",
10+
"pull_request": {
11+
"head": {
12+
"ref": "feature-123",
13+
"sha": "1111111111111111111111111111111111111111",
14+
"repo": {
15+
"name": "test-repo",
16+
"owner": { "login": "batman" }
17+
}
18+
}
19+
}
20+
},
21+
"repository": {
22+
"name": "test-repo",
23+
"owner": { "login": "batman" }
24+
}
25+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"event_name": "issue_comment",
3+
"action": "created",
4+
"comment": {
5+
"body": "This comment does not contain the trigger phrase"
6+
},
7+
"issue": {
8+
"number": 42,
9+
"pull_request": {
10+
"head": {
11+
"ref": "feature-123",
12+
"sha": "1111111111111111111111111111111111111111",
13+
"repo": {
14+
"name": "test-repo",
15+
"owner": { "login": "batman" }
16+
}
17+
}
18+
}
19+
},
20+
"repository": {
21+
"name": "test-repo",
22+
"owner": { "login": "batman" }
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"event_name": "issue_comment",
3+
"action": "created",
4+
"comment": {
5+
"body": "/gha run"
6+
},
7+
"issue": {
8+
"number": 42,
9+
"pull_request": {
10+
"head": {
11+
"ref": "feature-123",
12+
"sha": "1111111111111111111111111111111111111111",
13+
"repo": {
14+
"name": "test-repo",
15+
"owner": { "login": "batman" }
16+
}
17+
}
18+
}
19+
},
20+
"repository": {
21+
"name": "test-repo",
22+
"owner": { "login": "batman" }
23+
}
24+
}

0 commit comments

Comments
 (0)