|
| 1 | +import readline from 'node:readline/promises' |
| 2 | + |
1 | 3 | /** |
2 | 4 | * Onboards a user to an organization. |
3 | 5 | * @param {{ client: import('../github-api.js').default, logger: import('pino').Logger }} deps |
4 | | - * @param {{ org: string, username: string, dryRun: boolean }} options |
| 6 | + * @param {{ org: string, username: string, joiningTeams: Set, dryRun: boolean }} options |
5 | 7 | * @returns {Promise<void>} |
6 | 8 | */ |
7 | | -export default async function onboard ({ client, logger }, { org, username, dryRun }) { |
8 | | - const orgId = await client.getOrgId(org) |
9 | | - logger.info('Organization ID %s', orgId) |
| 9 | +export default async function onboard ({ client, logger }, { org, username, joiningTeams, dryRun }) { |
| 10 | + const joiningUser = await client.getUserInfo(username) |
| 11 | + if (!await confirm(`Are you sure you want to onboard ${joiningUser.login} [${joiningUser.name}] to ${org}?`)) { |
| 12 | + logger.warn('Aborting onboarding') |
| 13 | + process.exit(0) |
| 14 | + } |
| 15 | + |
| 16 | + const orgData = await client.getOrgData(org) |
| 17 | + logger.info('Organization ID %s', orgData.id) |
10 | 18 |
|
11 | | - const orgChart = await client.getOrgChart(org) |
| 19 | + const orgTeams = await client.getOrgChart(orgData) |
| 20 | + const destinationTeams = orgTeams.filter(t => joiningTeams.has(t.slug)) |
| 21 | + |
| 22 | + const teamSlugs = new Set(orgTeams.map(t => t.slug)) |
| 23 | + const wrongInputTeams = joiningTeams.difference(teamSlugs) |
| 24 | + if (wrongInputTeams.size) { |
| 25 | + logger.error('Team %s not found in organization %s', [...wrongInputTeams], org) |
| 26 | + process.exit(1) |
| 27 | + } |
12 | 28 |
|
13 | | - // TODO Implement onboarding logic here |
14 | 29 | if (dryRun) { |
15 | | - logger.info(`[DRY RUN] Would onboard user: ${username}`) |
| 30 | + logger.info('[DRY-RUN] This user %s should be added to team %s', joiningUser.login, [...joiningTeams]) |
16 | 31 | } else { |
17 | | - logger.info(`Onboarding user: ${username}`) |
| 32 | + for (const targetTeam of destinationTeams) { |
| 33 | + await client.addUserToTeam(org, targetTeam.slug, joiningUser.login) |
| 34 | + logger.info('Added %s to team %s', joiningUser.login, targetTeam.slug) |
| 35 | + } |
18 | 36 | } |
| 37 | + |
| 38 | + logger.info('GitHub onboarding completed for user %s ✅ ', joiningUser.login) |
| 39 | + |
| 40 | + logger.warn('To complete the NPM onboarding, please following these steps:') |
| 41 | + // This step cannot be automated, there are no API to add members to an org on NPM |
| 42 | + logger.info('1. Invite the user to the organization on NPM: https://www.npmjs.com/org/%s/invite?track=existingOrgAddMembers', org) |
| 43 | + logger.info('2. Add the user to the relevant teams by using the commands:'); |
| 44 | + [ |
| 45 | + { slug: 'developers' }, // NPM has a default team for every org |
| 46 | + ...destinationTeams |
| 47 | + ].forEach(team => { |
| 48 | + logger.info('npm team add @%s:%s %s', org, team.slug, joiningUser.login) |
| 49 | + }) |
| 50 | + logger.info('When it will be done, the NPM onboarding will be completed for user %s ✅ ', joiningUser.login) |
| 51 | +} |
| 52 | + |
| 53 | +async function confirm (q) { |
| 54 | + const rl = readline.createInterface({ |
| 55 | + input: process.stdin, |
| 56 | + output: process.stdout |
| 57 | + }) |
| 58 | + const answer = await rl.question(`${q} (y/N)`) |
| 59 | + rl.close() |
| 60 | + return answer.trim().toLowerCase() === 'y' |
19 | 61 | } |
0 commit comments