#!/usr/bin/env php localBranch = exec('git rev-parse --abbrev-ref HEAD'); $this->targetBranch = getenv('TARGET_BRANCH'); if (!$this->targetBranch) { die('No target branch specified. Aborting.'); } $this->remoteBranch = exec('git rev-parse --abbrev-ref --symbolic-full-name @{u}'); $this->remoteBranch = str_replace('origin/', '', $this->remoteBranch); } public function __invoke(): void { $this->confirmCiStatusIsPassing(); // TODO: Check that the current branch has a tracking branch. $this->fetchOrigin(); $this->ensureFeatureBranchInSync(); $this->ensureTargetBranchInSync(); $this->checkoutTargetBranch(); $this->mergeLocalBranch(); $this->pushTargetBranch(); $this->deleteRemoteBranch(); $this->deleteLocalBranch(); } private function confirmCiStatusIsPassing(): void { echo 'Confirming ci-status on PR is green...' . PHP_EOL; // TODO: Check for failures, or skip if there is no CI. $errors = [ self::CI_ERROR => 'Aborting: CI error', self::CI_PENDING => 'Aborting: CI pending', ]; if (array_key_exists($status = exec('hub ci-status'), $errors)) { die($errors[$status]); } } private function fetchOrigin(): void { print 'Fetching origin to confirm local and remote in sync...' . PHP_EOL; exec("git fetch origin"); } private function ensureTargetBranchInSync(): void { $this->ensureBranchInSyncWithUpstream( $this->targetBranch, $this->targetBranch ); } private function ensureFeatureBranchInSync(): void { $this->ensureBranchInSyncWithUpstream( $this->localBranch, $this->remoteBranch ); } private function ensureBranchInSyncWithUpstream( string $localBranch, string $remoteBranch ): void { echo sprintf( 'Ensuring that %s is in sync with its upstream...', $localBranch ) . PHP_EOL; $localCommitTip = $this->tipCommitOfBranch($localBranch); $remoteCommitTip = $this->tipCommitOfBranch(sprintf( 'origin/%s', $remoteBranch )); if ($localCommitTip != $remoteCommitTip) { die(sprintf( 'Branch %s was out of date, needs rebasing. Aborting.', $localBranch )); } } private function tipCommitOfBranch(string $branchName): string { return exec(sprintf('git rev-parse %s', $branchName)); } private function checkoutTargetBranch(): void { print sprintf('Checking out %s...' . PHP_EOL, $this->targetBranch); exec(sprintf('git checkout %s', $this->targetBranch)); } private function mergeLocalBranch(): void { echo sprintf( 'Merging %s into %s...' . PHP_EOL, $this->localBranch, $this->targetBranch ); exec(sprintf('git merge --ff-only %s', $this->localBranch)); } public function pushTargetBranch(): void { print(sprintf('Pushing updated %s branch...', $this->targetBranch)); exec(sprintf('git push origin %s', $this->targetBranch)); } public function deleteRemoteBranch(): void { echo 'Deleting remote branch...' . PHP_EOL; exec(sprintf('git push origin :%s', $this->remoteBranch)); } public function deleteLocalBranch(): void { echo 'Deleting local branch...' . PHP_EOL; exec(sprintf('git branch -d %s', $this->localBranch)); } } (new ClosesPullRequests())->__invoke();