Skip to content

Commit c7eb13d

Browse files
committed
first version of github action
1 parent d008ecd commit c7eb13d

9 files changed

Lines changed: 4112 additions & 1045 deletions

File tree

action.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ description: "Execute a GitHub Action only if the (triggering) actor is authoris
33
author: "Arky Asmal"
44
runs:
55
using: "node20"
6-
main: "dist/index.js"
6+
main: "dist/action/index.js"
77
branding:
88
icon: "briefcase"
99
color: "black"
1010
inputs:
11+
githubOrg:
12+
required: false
13+
description: "GitHub organization name, that you want to use to control access"
1114
githubToken:
12-
required: true
15+
required: false
1316
description: "GitHub access token with `repo` scope"
1417
actor:
1518
required: false

action/index.ts

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,36 @@
11
import * as core from "@actions/core";
2-
import { z } from "zod";
3-
import { GroupValues, InputValues } from "./types";
4-
const InputZodSchema = z.object({
5-
token: z
6-
.string()
7-
.optional()
8-
.transform((val) => val || process.env.GITHUB_TOKEN),
9-
actor: z
10-
.string()
11-
.optional()
12-
.transform((val) => val || process.env.GITHUB_ACTOR || ""),
13-
authorizedGroups: z
14-
.string()
15-
.optional()
16-
.or(z.array(z.nativeEnum(GroupValues)))
17-
.transform((val) => {
18-
if (typeof val === "string") {
19-
try {
20-
const parsed = JSON.parse(val);
21-
return parsed;
22-
} catch (error) {
23-
return [val];
24-
}
25-
}
26-
return val;
27-
})
28-
.refine((val) => {
29-
if (!val) return val;
30-
return Array.isArray(val);
31-
}),
32-
authorizedActors: z
33-
.string()
34-
.optional()
35-
.or(z.array(z.string()))
36-
.transform((val) => {
37-
if (typeof val === "string") {
38-
try {
39-
const parsed = JSON.parse(val);
40-
return parsed;
41-
} catch (error) {
42-
return [val];
43-
}
44-
}
45-
return val;
46-
})
47-
.refine((val) => {
48-
if (!val) return val;
49-
return Array.isArray(val);
50-
}),
51-
failSilently: z.boolean().default(false),
52-
failureMessage: z
53-
.string()
54-
.default("Actor is not authorised to trigger this Workflow."),
55-
});
56-
const getInput = (): InputValues => {
2+
import { InputZodSchema } from "./types";
3+
import { Octokit } from "@octokit/rest";
4+
5+
async function getMembers(org: string, token: string) {
6+
const octokit = new Octokit({
7+
auth: token,
8+
});
9+
const membersList = await octokit.orgs.listMembers({
10+
org,
11+
});
12+
const memberData: Promise<{
13+
login: string;
14+
role: string;
15+
}>[] = membersList.data.map(async (member: any) => {
16+
const membership = await octokit.orgs.getMembershipForUser({
17+
org,
18+
username: member.login,
19+
});
20+
return {
21+
login: member.login as string,
22+
role: membership.data.role as string,
23+
};
24+
});
25+
return Promise.all(memberData);
26+
}
27+
const getInput = () => {
5728
const results = {
29+
githubToken: core.getInput("githubToken"),
30+
githubOrg: core.getInput("githubOrg"),
5831
actor: core.getInput("actor"),
59-
authorizedActors: core.getInput("authorizedActors", { required: true }),
32+
authorizedGroups: core.getInput("authorizedGroups"),
33+
authorizedActors: core.getInput("authorizedActors"),
6034
failSilently: core.getInput("failSilently") === "true" || false,
6135
failureMessage:
6236
core.getInput("failureMessage") ||
@@ -77,8 +51,9 @@ async function run(): Promise<void> {
7751

7852
try {
7953
core.debug(`Reading input ...`);
80-
8154
const {
55+
githubOrg,
56+
githubToken,
8257
actor,
8358
authorizedActors,
8459
failSilently,
@@ -88,11 +63,21 @@ async function run(): Promise<void> {
8863

8964
core.debug(`Got actor: ${actor}`);
9065
core.debug(`Got a list of authorised actors ${authorizedActors}`);
66+
core.debug(`Got a list of authorised groups ${authorizedGroups}`);
67+
core.debug(`Got a token ${githubToken}`);
68+
core.debug(`Got an org ${githubOrg}`);
9169
const newAuthorizedActors = authorizedActors || [];
92-
70+
//handle access by organization
71+
if (authorizedGroups && githubToken && githubOrg) {
72+
const members = await getMembers(githubOrg, githubToken);
73+
const users = members
74+
.filter((m) => authorizedGroups.includes(m.role))
75+
.map((m) => m.login);
76+
newAuthorizedActors.push(...users);
77+
}
9378
const isAuthorisedActor = newAuthorizedActors.includes(actor);
94-
core.setOutput("isAuthorisedActor", isAuthorisedActor);
9579

80+
core.setOutput("isAuthorisedActor", isAuthorisedActor);
9681
core.debug(`isAuthorisedActor: ${isAuthorisedActor ? "Yes" : "No"}.`);
9782
core.debug(`Fail silently? ${failSilently ? "Yes" : "No"}!`);
9883

action/types.ts

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,65 @@
1-
export enum GroupValues{
1+
import { z } from "zod";
2+
export enum GroupValues {
23
ADMIN = "admin",
3-
OWNER = 'owner',
4-
MEMBER = 'member'
4+
OWNER = "owner",
5+
MEMBER = "member",
56
}
67
export type Input = {
78
actor?: string;
89
authorisedActors: string[];
910
failSilently?: string;
1011
failureMessage?: string;
1112
};
12-
13-
export type InputValues = {
14-
actor: string;
15-
authorizedGroups?: GroupValues[]
16-
authorizedActors?: string[];
17-
failSilently: boolean;
18-
failureMessage: string;
19-
};
13+
export const InputZodSchema = z.object({
14+
githubOrg: z.string().optional(),
15+
githubToken: z
16+
.string()
17+
.optional()
18+
.transform((val) => val || process.env.GITHUB_TOKEN),
19+
actor: z
20+
.string()
21+
.optional()
22+
.transform((val) => val || process.env.GITHUB_ACTOR || ""),
23+
authorizedGroups: z
24+
.string()
25+
.optional()
26+
.or(z.array(z.nativeEnum(GroupValues)))
27+
.transform((val) => {
28+
if (typeof val === "string") {
29+
try {
30+
const parsed = JSON.parse(val);
31+
return parsed;
32+
} catch (error) {
33+
return [val];
34+
}
35+
}
36+
return val;
37+
})
38+
.refine((val) => {
39+
if (!val) return val;
40+
return Array.isArray(val);
41+
}),
42+
authorizedActors: z
43+
.string()
44+
.optional()
45+
.or(z.array(z.string()))
46+
.transform((val) => {
47+
if (typeof val === "string") {
48+
try {
49+
const parsed = JSON.parse(val);
50+
return parsed;
51+
} catch (error) {
52+
return [val];
53+
}
54+
}
55+
return val;
56+
})
57+
.refine((val) => {
58+
if (!val) return val;
59+
return Array.isArray(val);
60+
}),
61+
failSilently: z.boolean().default(false),
62+
failureMessage: z
63+
.string()
64+
.default("Actor is not authorised to trigger this Workflow."),
65+
});

0 commit comments

Comments
 (0)