Skip to content

Commit f476846

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 f476846

File tree

3 files changed

+59
-79
lines changed

3 files changed

+59
-79
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: 55 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,91 +7,76 @@
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;
37-
}
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);
3836

39-
/** The list of users selected to be members of the caretaker group. */
40-
const selectedPrimaryAndSecondary = await Prompt.checkbox<string>({
41-
choices: roster.map((member) => ({
42-
value: member,
43-
checked: current.has(member),
44-
})),
45-
message:
46-
'Select 2 caretakers for the upcoming rotation (primary and secondary, http://go/ng-caretaker-schedule):',
47-
validate: (value) => {
48-
if (value.length !== 2) {
49-
return 'Please select exactly 2 caretakers for the upcoming rotation.';
50-
}
51-
return true;
52-
},
53-
});
37+
if (roster.length === 0) {
38+
return Log.error(` ✘ Unable to retrieve members of the group: ${caretakerGroupRoster}`);
39+
}
5440

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) => ({
41+
/** The set of users selected to be members of the caretaker group. */
42+
const selected = new Set(
43+
await Prompt.checkbox<string>({
44+
choices: roster.map((member) => ({
6145
value: member,
62-
name: `${member} (EMEA)`,
6346
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-
}
47+
})),
48+
message:
49+
'Select 2 caretakers for the upcoming rotation (primary and secondary, http://go/ng-caretakers):',
50+
validate: (value) => {
51+
if (value.length !== 2) {
52+
return 'Please select exactly 2 caretakers for the upcoming rotation.';
53+
}
54+
return true;
55+
},
56+
}),
57+
);
58+
59+
if (config.caretaker.hasEmeaCaretaker) {
60+
selected.add(
61+
await Prompt.select<string>({
62+
choices: emeaRoster.map((value) => ({value})),
63+
message: 'Select EMEA caretaker (http://go/ng-caretaker-schedule-emea)',
64+
}),
65+
);
8066
}
8167

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

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

9076
try {
91-
await setCaretakerGroup(caretakerGroup, selectedSorted);
77+
await setCaretakerGroup(caretakerGroup, Array.from(selected));
9278
} catch {
93-
Log.error(' ✘ Failed to update caretaker group.');
94-
return;
79+
return Log.error(' ✘ Failed to update caretaker group.');
9580
}
9681
Log.info(green(' ✔ Successfully updated caretaker group'));
9782
}
@@ -101,17 +86,15 @@ async function getGroupMembers(group: string) {
10186
/** The authenticated GitClient instance. */
10287
const git = await AuthenticatedGitClient.get();
10388
try {
104-
return (
105-
await git.github.teams.listMembersInOrg({
89+
return await git.github.teams
90+
.listMembersInOrg({
10691
org: git.remoteConfig.owner,
10792
team_slug: group,
10893
})
109-
).data
110-
.filter((_) => !!_)
111-
.map((member) => member!.login);
94+
.then(({data}) => data.map((member) => member.login));
11295
} catch (e) {
11396
Log.debug(e);
114-
return null;
97+
return [];
11598
}
11699
}
117100

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)