diff --git a/README.md b/README.md index 640dc2f2..27fcedff 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ It's the nickname for our Special Projects team at Automattic. Be well, be kind, make things and set them free. +## Dependencies +- Composer (instructions below) + ## Installation 1. Open the Terminal on your Mac and clone this repository by running: - `git clone git@github.com:Automattic/team51-cli.git` @@ -59,6 +62,9 @@ If you get the error `./install-osx: line 2: composer: command not found`, you c ### `brew: command not found` If you don't have [brew](https://brew.sh/) yet, install it by executing this from your Terminal: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` — Tip: you can use brew to install all sort of apps on your Mac. Give it [a try](https://formulae.brew.sh/cask/zoom) +### `gh: command not found` +If you're onboarding/offboarding users, you will need access to Github CLI. Please refer to the [installation instructions](https://cli.github.com/), and then follow the instructions on the Terminal for authentication. + ### `Warning: file_get_contents: failed to open stream` ``` Warning: file_get_contents(/Users/.../team51-cli/config.json): failed to open stream: No such file or directory in /Users/.../team51-cli/src/helpers/config-loader.php on line 5 diff --git a/load-application.php b/load-application.php index 960c4eb5..76a9d48e 100644 --- a/load-application.php +++ b/load-application.php @@ -31,6 +31,7 @@ $application->add( new Team51\Command\Plugin_List() ); $application->add( new Team51\Command\Pressable_Generate_Token() ); $application->add( new Team51\Command\Pressable_Grant_Access() ); - +$application->add( new Team51\Command\Github_Team_Add_User() ); +$application->add( new Team51\Command\Github_Team_Add_Repos() ); $application->run(); diff --git a/src/commands/create-repository.php b/src/commands/create-repository.php index 46bd19bd..5ff53112 100644 --- a/src/commands/create-repository.php +++ b/src/commands/create-repository.php @@ -3,6 +3,7 @@ namespace Team51\Command; use Team51\Helper\API_Helper; +use Team51\Helper\DRY_Helper; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputOption; @@ -35,6 +36,7 @@ protected function execute( InputInterface $input, OutputInterface $output ) { $filesystem = new Filesystem(); $api_helper = new API_Helper(); + $dry_helper = new DRY_Helper(); if ( empty( $input->getOption( 'repo-slug' ) ) ) { $output->writeln( 'You must pass a repository slug with --repo-slug.' ); @@ -167,6 +169,14 @@ protected function execute( InputInterface $input, OutputInterface $output ) { //exit; } + // Adding repo to all required teams + // TODO: TEST + $output->writeln( "Adding '{$slug}' to the corresponding Github Teams." ); + $repo_names = array( $slug ); + $dry_helper->populate_team_with_repos( $repo_names, $dry_helper->GH_ACCESS_1['team_slug'], $dry_helper->GH_ACCESS_1['team_permission'] ); + $dry_helper->populate_team_with_repos( $repo_names, $dry_helper->GH_ACCESS_2['team_slug'], $dry_helper->GH_ACCESS_2['team_permission'] ); + $dry_helper->populate_team_with_repos( $repo_names, $dry_helper->GH_ACCESS_3['team_slug'], $dry_helper->GH_ACCESS_3['team_permission'] ); + $ssh_url = $response->ssh_url; $html_url = $response->html_url; diff --git a/src/commands/github-repos-to-teams.php b/src/commands/github-repos-to-teams.php new file mode 100644 index 00000000..44abfd3f --- /dev/null +++ b/src/commands/github-repos-to-teams.php @@ -0,0 +1,61 @@ +setDescription( 'Add all Repositories to all GitHub Teams in the organization, with the respective repo permission.' ) + ->setHelp( 'Add all repos to our Github teams. Most-likely to be run only once, for the creation of the teams' ); + } + + protected function execute( InputInterface $input, OutputInterface $output ) { + $this->api_helper = new API_Helper(); + $this->dry_helper = new DRY_Helper(); + $this->output = $output; + + $output->writeln( "Pulling all repositories from our Github organization." ); + $repos = array(); + $repos_page = 1; + $more_repos = true; + + while( $more_repos ) { + $this->output->writeln( "..." ); + $tmp_repos = $this->api_helper->call_github_api( + sprintf( 'orgs/%s/repos?type=private&per_page=100&page=%s', GITHUB_API_OWNER, $repos_page ), + '', + 'GET' + ); + + if ( empty($tmp_repos) ) { + $more_repos = false; + break; + } + + $repos = array_merge($repos, $tmp_repos); + $repos_page++; + } + + $repo_names = array_column( $repos, 'name' ); + $total_repos = count($repo_names); + $output->writeln( "{$total_repos} repositories will be added to each of our Github Teams." ); + + // Populate Teams with Repositories + $this->dry_helper->populate_team_with_repos( $repo_names, $this->dry_helper->GH_ACCESS_1['team_slug'], $this->dry_helper->GH_ACCESS_1['team_permission'] ); + $this->dry_helper->populate_team_with_repos( $repo_names, $this->dry_helper->GH_ACCESS_2['team_slug'], $this->dry_helper->GH_ACCESS_2['team_permission'] ); + $this->dry_helper->populate_team_with_repos( $repo_names, $this->dry_helper->GH_ACCESS_3['team_slug'], $this->dry_helper->GH_ACCESS_3['team_permission'] ); + + $output->writeln( "All done." ); + } +} diff --git a/src/commands/github-team-add-user.php b/src/commands/github-team-add-user.php new file mode 100644 index 00000000..a4c47860 --- /dev/null +++ b/src/commands/github-team-add-user.php @@ -0,0 +1,76 @@ +setDescription( 'Adds collaborator to Github Team and Pressable sites. Collaborator can be either a11n or contractor' ) + ->setHelp( 'This command allows you to bulk add a collaborator to all Pressable sites and a Github team.' ) + ->addOption( 'username', null, InputOption::VALUE_REQUIRED, "Collaborator's Github username." ) + ->addOption( 'team', null, InputOption::VALUE_REQUIRED, sprintf('Github Team can be: %s, %s, or %s.', self::ACCESS_1, self::ACCESS_2, self::ACCESS_3) ); + } + + protected function execute( InputInterface $input, OutputInterface $output ) { + $this->api_helper = new API_Helper(); + $this->output = $output; + + $github_user = $input->getOption( 'github_username' ); + if ( empty( $github_user ) ) { + $github_user = trim( readline( "Please provide the collaborator's Github username: " ) ); + if ( empty( $github_user ) ) { + $github_user->writeln( "Missing collaborator's Github username (eg: --username=their_username)." ); + exit; + } + } + + $github_team = $input->getOption( 'github_team' ); + if ( empty( $github_team ) || ! in_array($github_team, [self::ACCESS_1, self::ACCESS_2, self::ACCESS_3]) ) { + $github_team = trim( readline( sprintf("Please provide the collaborator's Github team. Values can be: %s, %s, or %s: ", self::ACCESS_1, self::ACCESS_2, self::ACCESS_3) ) ); + if ( empty( $github_team ) || ! in_array($github_team, [self::ACCESS_1, self::ACCESS_2, self::ACCESS_3]) ) { + $output->writeln( 'Missing collaborator github_team (eg: --team='.self::ACCESS_1.').' ); + exit; + } + } + + // Start process + $this->onboard_github( $github_user, $github_team ); + + $output->writeln( 'All done!' ); + } + + + private function onboard_github( $gh_username, $gh_team ) { + $this->output->writeln( "Granting access to our a8cteam51 organization..." ); + + // Associate Github username with Github Team + $team_put = $this->api_helper->call_github_api( + sprintf( 'orgs/%s/teams/%s/memberships/%s', GITHUB_API_OWNER, $gh_team, $gh_username ), + array( + 'role' => 'member' + ), + 'PUT' + ); + + if ( ! empty( $team_put->message ) ) { + $this->output->writeln( "Something went wrong. Github says: {$team_put->message}" ); + } else { + $this->output->writeln( "The user '{$gh_username}' has been added to Team '{$gh_team}'!" ); + } + } +} diff --git a/src/helpers/dry-helper.php b/src/helpers/dry-helper.php new file mode 100644 index 00000000..299c8a59 --- /dev/null +++ b/src/helpers/dry-helper.php @@ -0,0 +1,42 @@ + 'triage', + 'team_permission' => 'triage', + ); + public $GH_ACCESS_2 = array( + 'team_slug' => 'deploy', + 'team_permission' => 'push', + ); + public $GH_ACCESS_3 = array( + 'team_slug' => 'admin', + 'team_permission' => 'admin', + ); + + function __construct() { + $this->api_helper = new API_Helper(); + } + + public function populate_team_with_repos( $repo_names, $team_slug, $team_permission ) { + echo "Adding repos to team '{$team_slug}'. This might take a while...\n"; + foreach( $repo_names as $repo_name ) { + $github_response = $this->api_helper->call_github_api( + sprintf( 'orgs/%s/teams/%s/repos/%s/%s', GITHUB_API_OWNER, $team_slug, GITHUB_API_OWNER, $repo_name ), + array( + 'permission' => $team_permission + ), + 'PUT' + ); + if ( ! empty($github_response->message) ) { + echo "Something went wrong when adding the repo '{$repo_name}' to the team '{$team_slug}'. Message: {$github_response->message}\n"; + } + } + } + +}