Skip to content

Commit 2830fa1

Browse files
committed
refactor(ng-dev): update caretaker handoff to support all three repositories
Update the logic and interaction of the caretaker handoff to support usage in components and angular-cli as well. Moves to a standard caretaker group naming convention and adds configuration for using an emea caretaker as well.
1 parent a08ba6c commit 2830fa1

File tree

3 files changed

+48
-70
lines changed

3 files changed

+48
-70
lines changed

.github/local-actions/branch-manager/main.js

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

ng-dev/caretaker/handoff/update-github-team.ts

Lines changed: 44 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,91 +7,74 @@
77
*/
88

99
import {Prompt} from '../../utils/prompt.js';
10-
import {getConfig, assertValidCaretakerConfig} from '../../utils/config.js';
10+
import {
11+
getConfig,
12+
assertValidCaretakerConfig,
13+
assertValidGithubConfig,
14+
} from '../../utils/config.js';
1115

1216
import {green, Log} from '../../utils/logging.js';
1317
import {AuthenticatedGitClient} from '../../utils/git/authenticated-git-client.js';
1418

1519
/** Update the Github caretaker group, using a prompt to obtain the new caretaker group members. */
1620
export async function updateCaretakerTeamViaPrompt() {
1721
/** Caretaker specific configuration. */
18-
const config = await getConfig([assertValidCaretakerConfig]);
19-
const {caretakerGroup} = config.caretaker;
20-
21-
if (caretakerGroup === undefined) {
22-
throw Error('`caretakerGroup` is not defined in the `caretaker` config');
23-
}
24-
25-
/** The list of current members in the group. */
22+
const config = await getConfig([assertValidCaretakerConfig, assertValidGithubConfig]);
23+
/** The github team name for the caretaker group. */
24+
const caretakerGroup = `${config.github.name}-caretaker`;
25+
/** The github team name for the group containing all possible caretakers. */
26+
const caretakerGroupRoster = `${config.github.name}-caretaker-roster`;
27+
/** The github team name for the group containing all possible emea caretakers. */
28+
const caretakerGroupEmeaRoster = `${config.github.name}-caretaker-roster-emea`;
29+
30+
/** A set of the current caretakers in the {@link caretakerGroup}} */
2631
const current = new Set(await getGroupMembers(caretakerGroup));
27-
const [roster, emeaRoster] = await Promise.all([
28-
getGroupMembers(`${caretakerGroup}-roster`),
29-
getGroupMembers(`${caretakerGroup}-roster-emea`),
30-
]);
31-
if (emeaRoster === null) {
32-
Log.debug(` Unable to retrieve members of the group: ${caretakerGroup}-roster-emea`);
33-
}
34-
if (roster === null) {
35-
Log.error(` ✘ Unable to retrieve members of the group: ${caretakerGroup}-roster`);
36-
return;
32+
/** A list of the team members in the {@link caretakerGroupRoster}} */
33+
const roster = await getGroupMembers(caretakerGroupRoster);
34+
/** A list of the team members in the {@link caretakerGroupEmeaRoster}} */
35+
const emeaRoster = await getGroupMembers(caretakerGroupEmeaRoster);
36+
37+
if (roster.length === 0) {
38+
return Log.error(` ✘ Unable to retrieve members of the group: ${caretakerGroupRoster}`);
3739
}
3840

39-
/** The list of users selected to be members of the caretaker group. */
40-
const selectedPrimaryAndSecondary = await Prompt.checkbox<string>({
41+
/** The set of users selected to be members of the caretaker group. */
42+
const selected = new Set(await Prompt.checkbox<string>({
4143
choices: roster.map((member) => ({
4244
value: member,
4345
checked: current.has(member),
4446
})),
4547
message:
46-
'Select 2 caretakers for the upcoming rotation (primary and secondary, http://go/ng-caretaker-schedule):',
48+
'Select 2 caretakers for the upcoming rotation (primary and secondary, http://go/ng-caretakers):',
4749
validate: (value) => {
4850
if (value.length !== 2) {
4951
return 'Please select exactly 2 caretakers for the upcoming rotation.';
5052
}
5153
return true;
5254
},
53-
});
54-
55-
let selectedEmea = '';
56-
if (emeaRoster !== null) {
57-
const emeaOptions = emeaRoster
58-
// Do not show members that are already selected as primary/secondary.
59-
.filter((m) => !selectedPrimaryAndSecondary.includes(m))
60-
.map((member) => ({
61-
value: member,
62-
name: `${member} (EMEA)`,
63-
checked: current.has(member),
64-
}));
65-
selectedEmea = await Prompt.select<string>({
66-
choices: emeaOptions,
67-
message: 'Select EMEA caretaker (http://go/ng-caretaker-schedule-emea)',
68-
});
69-
70-
/** Whether the user positively confirmed the selected made. */
71-
const confirmation = await Prompt.confirm({
72-
default: true,
73-
message: 'Are you sure?',
74-
});
75-
76-
if (confirmation === false) {
77-
Log.warn(' ⚠ Skipping caretaker group update.');
78-
return;
79-
}
55+
}));
56+
57+
if (config.caretaker.hasEmeaCaretaker) {
58+
selected.add(
59+
await Prompt.select<string>({
60+
choices: emeaRoster.map((value) => ({value})),
61+
message: 'Select EMEA caretaker (http://go/ng-caretaker-schedule-emea)',
62+
}),
63+
);
8064
}
8165

82-
const selectedSorted = [...selectedPrimaryAndSecondary, selectedEmea].filter((_) => !!_).sort();
83-
const currentSorted = Array.from(current).sort();
66+
if (!(await Prompt.confirm({default: true, message: 'Are you sure?'}))) {
67+
return Log.warn(' ⚠ Skipping caretaker group update.');
68+
}
8469

85-
if (JSON.stringify(selectedSorted) === JSON.stringify(currentSorted)) {
86-
Log.info(green(' ✔ Caretaker group already up to date.'));
87-
return;
70+
if (JSON.stringify(Array.from(selected).sort()) === JSON.stringify(Array.from(current).sort())) {
71+
return Log.info(green(' ✔ Caretaker group already up to date.'));
8872
}
8973

9074
try {
91-
await setCaretakerGroup(caretakerGroup, selectedSorted);
75+
await setCaretakerGroup(caretakerGroup, Array.from(selected));
9276
} catch {
93-
Log.error(' ✘ Failed to update caretaker group.');
94-
return;
77+
return Log.error(' ✘ Failed to update caretaker group.');
9578
}
9679
Log.info(green(' ✔ Successfully updated caretaker group'));
9780
}
@@ -101,17 +84,15 @@ async function getGroupMembers(group: string) {
10184
/** The authenticated GitClient instance. */
10285
const git = await AuthenticatedGitClient.get();
10386
try {
104-
return (
105-
await git.github.teams.listMembersInOrg({
87+
return await git.github.teams
88+
.listMembersInOrg({
10689
org: git.remoteConfig.owner,
10790
team_slug: group,
10891
})
109-
).data
110-
.filter((_) => !!_)
111-
.map((member) => member!.login);
92+
.then(({data}) => data.map((member) => member.login));
11293
} catch (e) {
11394
Log.debug(e);
114-
return null;
95+
return [];
11596
}
11697
}
11798

ng-dev/utils/config.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,11 @@ export interface GoogleSyncConfig {
6464
export interface CaretakerConfig {
6565
/** Github queries showing a snapshot of pulls/issues caretakers need to monitor. */
6666
githubQueries?: {name: string; query: string}[];
67-
/**
68-
* The Github group used to track current caretakers. A second group is assumed to exist with the
69-
* name "<group-name>-roster" containing a list of all users eligible for the caretaker group.
70-
* */
71-
caretakerGroup?: string;
67+
/** Whether the repository uses an additional caretaker from an emea group. */
68+
hasEmeaCaretaker?: true;
7269
/**
7370
* Project-relative path to a config file describing how the project is synced into Google.
74-
* The configuration file is expected to be valid JSONC and match {@see GoogleSyncConfig}.
71+
* The configuration file is expected to be valid JSONC and match {@link GoogleSyncConfig}.
7572
*/
7673
g3SyncConfigPath?: string;
7774
}

0 commit comments

Comments
 (0)