-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub-sync.ts
More file actions
139 lines (121 loc) · 4.22 KB
/
github-sync.ts
File metadata and controls
139 lines (121 loc) · 4.22 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
import { Octokit } from 'octokit';
import { envVars } from '../lib/config';
import { normalizeGitHubUsername } from '../lib/utils';
import { createGoogleAuth, getWorkspaceUsers } from './google-workspace';
export type SyncResult = {
invited: SyncResultUser[];
removed: string[];
errors: string[];
};
export type SyncResultUser = {
username: string;
email: string;
};
export class WorkspaceGitHubSync {
private readonly auth;
private octokit: Octokit;
private readonly orgName: string;
private removeStopList: string[] = envVars.REMOVE_STOP_LIST;
constructor() {
this.auth = createGoogleAuth();
this.octokit = new Octokit({
auth: envVars.GITHUB_TOKEN,
});
this.orgName = envVars.GITHUB_ORG_NAME;
}
async syncMembers(): Promise<SyncResult> {
const syncResult: SyncResult = { invited: [], removed: [], errors: [] };
try {
const workspaceUsers = await getWorkspaceUsers(this.auth);
const currentMembers = await this.getGithubMemberUsernames();
const pendingInvites = await this.getGithubInvitedUsers();
// get all users with GitHub usernames
const workspaceGitHubUsernames = new Set(
workspaceUsers
.filter(
(user) =>
!!user.customSchemas && user.customSchemas['3rd-party_tools']?.GitHub_Username,
)
.map((user) => {
const githubUsername = user.customSchemas?.['3rd-party_tools']?.GitHub_Username;
return normalizeGitHubUsername(githubUsername || '');
}),
);
// add users with GitHub usernames to the org
for (const username of workspaceGitHubUsernames) {
if (!currentMembers.has(username) && !pendingInvites.has(username)) {
try {
const user = await this.octokit.rest.users.getByUsername({
username,
});
const userEmail = workspaceUsers.find((u) => {
const ghUsername = u.customSchemas?.['3rd-party_tools']?.GitHub_Username;
return ghUsername ? normalizeGitHubUsername(ghUsername) === username : false;
})?.primaryEmail;
if (!user.data.id || !userEmail) {
syncResult.errors.push(
`User ${username} with email ${userEmail} not found on GitHub`,
);
continue;
}
await this.octokit.rest.orgs.createInvitation({
org: this.orgName,
invitee_id: user.data.id,
});
syncResult.invited.push({ username, email: userEmail });
} catch (e) {
syncResult.errors.push(`Error inviting ${username}`);
console.error('Error inviting user', e);
}
}
}
// remove users without GitHub usernames from the org
for (const member of currentMembers) {
if (!workspaceGitHubUsernames.has(member) && !this.removeStopList.includes(member)) {
try {
await this.octokit.rest.orgs.removeMember({
org: this.orgName,
username: member,
});
syncResult.removed.push(member);
} catch (e) {
syncResult.errors.push(`Error removing ${member}`);
console.error('Error removing user', e);
}
}
}
} catch (e) {
console.error('Error during synchronization', e);
throw e;
}
return syncResult;
}
async getGithubMemberUsernames() {
try {
const members = await this.octokit.paginate(this.octokit.rest.orgs.listMembers, {
org: this.orgName,
per_page: 100,
});
return new Set(members.map((member) => normalizeGitHubUsername(member.login)));
} catch (error) {
console.error('Error fetching GitHub members', error);
throw error;
}
}
async getGithubInvitedUsers() {
try {
const invites = await this.octokit.paginate(this.octokit.rest.orgs.listPendingInvitations, {
org: this.orgName,
per_page: 100,
});
return new Set(
invites
.filter((invite) => invite.login)
.map((invite) => normalizeGitHubUsername(invite.login || '')),
);
} catch (error) {
console.error('Error fetching GitHub invites', error);
throw error;
}
}
}