diff options
author | Morris Jobke <hey@morrisjobke.de> | 2018-10-29 16:20:00 +0300 |
---|---|---|
committer | Morris Jobke <hey@morrisjobke.de> | 2018-10-29 16:20:00 +0300 |
commit | e3fccc9f1b0df12c445ede6c0e10ce1245c007cc (patch) | |
tree | 01ab78dae1920a791c544a9cd37f0ed6a4d04090 /changelog | |
parent | b112035eb8152ba8ca78ce8a7f10ffb979e72731 (diff) |
Add changelog script for Android
Signed-off-by: Morris Jobke <hey@morrisjobke.de>
Diffstat (limited to 'changelog')
-rw-r--r-- | changelog/android.php | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/changelog/android.php b/changelog/android.php new file mode 100644 index 0000000..a0f6d03 --- /dev/null +++ b/changelog/android.php @@ -0,0 +1,308 @@ +<?php + +// This file is generated by Composer +require_once __DIR__ . '/vendor/autoload.php'; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Helper\ProgressBar; +# TODO +#use Cache\Adapter\Redis\RedisCachePool; + +class GenerateChangelogCommand extends Command +{ + protected function configure() + { + $this + ->setName('generate:changelog') + ->setDescription('Generates the changelog.') + ->addArgument('base', InputArgument::REQUIRED, 'The base version.') + ->addArgument('head', InputArgument::REQUIRED, 'The head version.') + ->addOption( + 'format', + 'f', + InputOption::VALUE_REQUIRED, + 'What format should the output have? (markdown, forum, html)', + 'markdown' + ); + ; + } + + /** + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // TODO iterate over all repos + $orgName = 'nextcloud'; + $repoName = 'android'; + + $reposToIterate = [ + "android", + /*"server", + "3rdparty", + "activity", + "apps", + "files_pdfviewer", + "files_texteditor", + "files_videoplayer", + "firstrunwizard", + "gallery", + "logreader", + "nextcloud_announcements", + "notifications", + "password_policy", + "serverinfo", + "survey_client",*/ + ]; + + + if (!file_exists(__DIR__ . '/../credentials.json')) { + throw new Exception('Credentials file is missing - please provide your credentials in credentials.json in the root folder.'); + } + + $credentialsData = json_decode(file_get_contents(__DIR__ . '/../credentials.json'), true); + if (!is_array($credentialsData) || !isset($credentialsData['apikey'])) { + throw new Exception('Credentials file can not be read or does not provide "apikey".'); + } + + $format = $input->getOption('format'); + if (!in_array($format, ['markdown', 'forum', 'html'])) { + throw new \Symfony\Component\Console\Exception\InvalidOptionException( + "The provided format is invalid (should be one of markdown, forum, html but was '$format')" + ); + } + $base = $input->getArgument('base'); + $head = $input->getArgument('head'); + + $output->writeln("base: $base"); + $output->writeln("head: $head"); + + $milestoneToCheck = null; + if (substr($base, 0, 7) === 'stable-') { + $version = explode('.', substr($base, 7)); + if (count($version) !== 3) { + $output->writeln('<error>Detected version does not have exactly 3 numbers separated by a dot.</error>'); + } else { + if (strpos($version[2], 'RC') !== false || strpos($version[2], 'beta') !== false) { + $version[2] = (string)((int)$version[2]); // this basically removes the beta/RC part + $milestoneToCheck = join('.', $version); + + if (strpos($milestoneToCheck, '.0.0') !== false) { + $milestoneToCheck = str_replace('.0.0', '', $milestoneToCheck); + } + } else { + $version[2] = (string)((int)$version[2] + 1); + $milestoneToCheck = join('.', $version); + } + $output->writeln("Checking milestone $milestoneToCheck for pending PRs ..."); + } + } else { + $output->writeln('<error>No version detected - the output will not contain any pending PRs. Use a git tag starting with "v" like "v13.0.5".</error>'); + } + + $prTitles = ['closed' => [], 'pending' => []]; + + # TODO + #$client = new \Redis(); + #$client->connect('127.0.0.1', 6379); + // Create a PSR6 cache pool + #$pool = new RedisCachePool($client); + + $client = new \Github\Client(); + # TODO + #$client->addCache($pool); + $client->authenticate($credentialsData['apikey'], Github\Client::AUTH_URL_TOKEN); + + $factor = 2; + if ($milestoneToCheck !== null) { + $factor = 3; + } + + $progressBar = new ProgressBar($output, count($reposToIterate) * $factor); + $progressBar->setFormat( + implode( + "\n", + [ + ' %message%', + ' %current%/%max% [%bar%] %percent:3s%%', + ' Remaining: %remaining:6s%', + ] + ) + ); + $progressBar->setMessage('Starting ...'); + $progressBar->start(); + + foreach ($reposToIterate as $repoName) { + + $pullRequests = []; + /** @var \Github\Api\Repo $repo */ + $repo = $client->api('repo'); + try { + $progressBar->setMessage("Fetching git history for $repoName..."); + $diff = $repo->commits()->compare($orgName, $repoName, $base, $head); + } catch (\Github\Exception\RuntimeException $e) { + if ($e->getMessage() === 'Not Found') { + $output->writeln('<error>Could not find base or head reference.</error>'); + return; + } + throw $e; + } + + foreach ($diff['commits'] as $commit) { + $fullMessage = $commit['commit']['message']; + list($firstLine,) = explode("\n", $fullMessage, 2); + if (substr($firstLine, 0, 20) === 'Merge pull request #') { + $firstLine = substr($firstLine, 20); + list($number,) = explode(" ", $firstLine, 2); + $pullRequests[] = $number; + } + } + $progressBar->advance(); + + if ($milestoneToCheck !== null) { + $progressBar->setMessage("Fetching pending PRs for $repoName $milestoneToCheck ..."); + + $query = "query{ + repository(owner: \"$orgName\", name: \"$repoName\") { + milestones(first: 20, states: [OPEN]) { + nodes { + title + pullRequests(states: [OPEN], first: 20) { + nodes { + number + } + } + } + } + } +}"; + + $response = $client->api('graphql')->execute($query); + foreach ($response['data']['repository']['milestones']['nodes'] as $milestone) { + if (strpos($milestone['title'], $milestoneToCheck) !== false) { + foreach ($milestone['pullRequests']['nodes'] as $pr) { + $pullRequests[] = $pr['number']; + } + } + } + $progressBar->advance(); + } + + $query = <<<'QUERY' +query { +QUERY; + $query .= ' repository(owner: "' . $orgName . '", name: "' . $repoName . '") {'; + + foreach ($pullRequests as $pullRequest) { + $query .= "pr$pullRequest: pullRequest(number: $pullRequest) { number, title, state },"; + } + + $query .= <<<'QUERY' + } +} +QUERY; + + $progressBar->setMessage("Fetching PR titles for $repoName ..."); + $response = $client->api('graphql')->execute($query); + + if (!isset($response['data']['repository'])) { + $progressBar->advance(); + continue; + } + foreach ($response['data']['repository'] as $pr) { + $title = $pr['title']; + $title = preg_replace('!^(backport\W+#\d+:?)?\W*!i', '', $title); + $title = trim($title); + $title = strtoupper(substr($title, 0, 1)) . substr($title, 1); + $id = '#' . $pr['number']; + if ($repoName !== 'server') { + $id = $repoName . $id; + } + $data = [ + 'repoName' => $repoName, + 'number' => $pr['number'], + 'title' => $title, + ]; + if ($pr['state'] === 'MERGED') { + $prTitles['closed'][$id] = $data; + } else { + $prTitles['pending'][$id] = $data; + } + } + $progressBar->advance(); + } + $progressBar->finish(); + + $output->writeln(''); + + ksort($prTitles['closed']); + ksort($prTitles['pending']); + + switch($format) { + case 'html': + foreach($prTitles['closed'] as $id => $data) { + $repoName = $data['repoName']; + $number = $data['number']; + $title = $data['title']; + $output->writeln("<li><a href=\"https://github.com/$orgName/$repoName/pull/$number\">$title ($repoName#$number)</a></li>"); + } + $count = count($prTitles['pending']); + if ($count > 0) { + $output->writeln("<error>$count pending PRs not printed - maybe the release is not ready yet</error>"); + } + break; + case 'forum': + foreach($prTitles['closed'] as $id => $data) { + $repoName = $data['repoName']; + $number = $data['number']; + $title = $data['title']; + $output->writeln("* [$title ($repoName#$number)](https://github.com/$orgName/$repoName/pull/$number)"); + } + $count = count($prTitles['pending']); + if ($count > 0) { + $output->writeln("<error>$count pending PRs not printed - maybe the release is not ready yet</error>"); + } + break; + case 'markdown': + default: + foreach($prTitles['closed'] as $id => $data) { + $repoName = $data['repoName']; + $number = $data['number']; + $title = $data['title']; + if ($repoName === 'server') { + $output->writeln("* #$number $title"); + } else { + $output->writeln("* [$repoName#$number](https://github.com/$orgName/$repoName/pull/$number) $title"); + } + } + if (count($prTitles['pending'])) { + $output->writeln("\n\nPending PRs:\n"); + } + foreach($prTitles['pending'] as $id => $data) { + $repoName = $data['repoName']; + $number = $data['number']; + $title = $data['title']; + if ($repoName === 'server') { + $output->writeln("* [ ] #$number $title"); + } else { + $output->writeln("* [ ] [$repoName#$number](https://github.com/$orgName/$repoName/pull/$number) $title"); + } + } + break; + } + + // Stop using cache + # TODO + #$client->removeCache(); + } +} + +$application = new Application(); + +$application->add(new GenerateChangelogCommand()); +$application->run(); |