diff --git a/composer.json b/composer.json index 9e2815cf..2ad42776 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/orm": "^2.7", "incenteev/composer-parameter-handler": "~2.0", - "knplabs/github-api": "^3.2", + "knplabs/github-api": "^3.3", "nyholm/psr7": "^1.3", "sensio/framework-extra-bundle": "^5.2", "symfony/console": "^5.2", diff --git a/composer.lock b/composer.lock index d99d374e..1e34ac5a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f34db7d7fb4797fdbbeb7229fbd8371", + "content-hash": "fe2120fb8824c43df8973c103778f137", "packages": [ { "name": "clue/stream-filter", @@ -1626,16 +1626,16 @@ }, { "name": "knplabs/github-api", - "version": "v3.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/KnpLabs/php-github-api.git", - "reference": "1dce0d33292eb2c4d33e2f9354b4043caf14588e" + "reference": "48f1df227d597336ae794c0c54a4db0e61da1dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/1dce0d33292eb2c4d33e2f9354b4043caf14588e", - "reference": "1dce0d33292eb2c4d33e2f9354b4043caf14588e", + "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/48f1df227d597336ae794c0c54a4db0e61da1dfe", + "reference": "48f1df227d597336ae794c0c54a4db0e61da1dfe", "shasum": "" }, "require": { @@ -1665,11 +1665,12 @@ "symfony/cache": "^5.1.8", "symfony/phpunit-bridge": "^5.2" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { "dev-2.x": "2.20.x-dev", - "dev-master": "3.2.x-dev" + "dev-master": "3.3.x-dev" } }, "autoload": { @@ -1702,7 +1703,7 @@ ], "support": { "issues": "https://github.com/KnpLabs/php-github-api/issues", - "source": "https://github.com/KnpLabs/php-github-api/tree/v3.2.0" + "source": "https://github.com/KnpLabs/php-github-api/tree/master" }, "funding": [ { @@ -1710,7 +1711,7 @@ "type": "github" } ], - "time": "2021-04-28T18:12:19+00:00" + "time": "2021-05-20T19:56:43+00:00" }, { "name": "laminas/laminas-code", @@ -8747,7 +8748,9 @@ ], "aliases": [], "minimum-stability": "RC", - "stability-flags": [], + "stability-flags": { + "knplabs/github-api": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/config/packages/github_api.yaml b/config/packages/github_api.yaml index f100dfae..f8a36904 100644 --- a/config/packages/github_api.yaml +++ b/config/packages/github_api.yaml @@ -69,3 +69,6 @@ services: Github\Api\Search: factory: ['@Github\Client', api] arguments: [search] + + Github\Api\Repository\Actions\WorkflowRuns: + factory: ['@Github\Api\Repo', workflowRuns] diff --git a/config/services.yaml b/config/services.yaml index 5cc04f74..a4183f8f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -15,6 +15,7 @@ parameters: - 'App\Subscriber\UnsupportedBranchSubscriber' - 'App\Subscriber\RemoveStalledLabelOnCommentSubscriber' - 'App\Subscriber\AllowEditFromMaintainerSubscriber' + - 'App\Subscriber\ApproveCiForNonContributors' secret: '%env(SYMFONY_SECRET)%' symfony/symfony-docs: @@ -31,6 +32,7 @@ parameters: - 'App\Subscriber\RemoveStalledLabelOnCommentSubscriber' - 'App\Subscriber\UpdateMilestoneWhenLabeledWaitingCodeMergeSubscriber' - 'App\Subscriber\AllowEditFromMaintainerSubscriber' + - 'App\Subscriber\ApproveCiForNonContributors' secret: '%env(SYMFONY_DOCS_SECRET)%' # used in a functional test @@ -51,6 +53,7 @@ parameters: - 'App\Subscriber\RemoveStalledLabelOnCommentSubscriber' - 'App\Subscriber\UpdateMilestoneWhenLabeledWaitingCodeMergeSubscriber' - 'App\Subscriber\AllowEditFromMaintainerSubscriber' + - 'App\Subscriber\ApproveCiForNonContributors' services: _defaults: @@ -75,12 +78,12 @@ services: resource: '../src/Subscriber/*' autoconfigure: false - App\Api\Issue\IssueApi: '@App\Api\Issue\GithubIssueApi' App\Api\Label\LabelApi: '@App\Api\Label\GithubLabelApi' App\Api\Milestone\MilestoneApi: '@App\Api\Milestone\GithubMilestoneApi' App\Api\PullRequest\PullRequestApi: '@App\Api\PullRequest\GithubPullRequestApi' App\Api\Status\StatusApi: '@App\Api\Status\GitHubStatusApi' + App\Api\Workflow\WorkflowApi: '@App\Api\Workflow\GithubWorkflowApi' App\Service\RepositoryProvider: arguments: [ '%repositories%' ] diff --git a/config/services_test.yaml b/config/services_test.yaml index 2467a470..bd9b73e2 100644 --- a/config/services_test.yaml +++ b/config/services_test.yaml @@ -8,3 +8,4 @@ services: App\Api\Milestone\MilestoneApi: '@App\Api\Milestone\StaticMilestoneApi' App\Api\PullRequest\PullRequestApi: '@App\Api\PullRequest\NullPullRequestApi' App\Api\Status\StatusApi: '@App\Api\Status\NullStatusApi' + App\Api\Workflow\WorkflowApi: '@App\Api\Workflow\NullWorkflowApi' diff --git a/src/Api/Workflow/GithubWorkflowApi.php b/src/Api/Workflow/GithubWorkflowApi.php new file mode 100644 index 00000000..bd3d3b80 --- /dev/null +++ b/src/Api/Workflow/GithubWorkflowApi.php @@ -0,0 +1,39 @@ + + */ +class GithubWorkflowApi implements WorkflowApi +{ + private ResultPager $resultPager; + private WorkflowRuns $workflowApi; + private LoggerInterface $logger; + + public function __construct(ResultPager $resultPager, WorkflowRuns $workflowApi, LoggerInterface $logger) + { + $this->resultPager = $resultPager; + $this->workflowApi = $workflowApi; + $this->logger = $logger; + } + + public function approveWorkflowsForPullRequest(Repository $repository, string $headRepository, string $headBranch): void + { + $runs = $this->resultPager->fetchAllLazy($this->workflowApi, 'all', [$repository->getVendor(), $repository->getName(), [ + 'branch' => $headBranch, + 'status' => 'action_required', + ]]); + + foreach ($runs as $run) { + if ($headRepository === $run['head_repository']['full_name']) { + $this->workflowApi->approve($repository->getVendor(), $repository->getName(), $run['id']); + } + } + } +} diff --git a/src/Api/Workflow/NullWorkflowApi.php b/src/Api/Workflow/NullWorkflowApi.php new file mode 100644 index 00000000..af8c0850 --- /dev/null +++ b/src/Api/Workflow/NullWorkflowApi.php @@ -0,0 +1,12 @@ + + */ +interface WorkflowApi +{ + /** + * Find workflow runs related to the PR and approve them. + */ + public function approveWorkflowsForPullRequest(Repository $repository, string $headRepository, string $headBranch): void; +} diff --git a/src/Subscriber/ApproveCiForNonContributors.php b/src/Subscriber/ApproveCiForNonContributors.php new file mode 100644 index 00000000..90ae8442 --- /dev/null +++ b/src/Subscriber/ApproveCiForNonContributors.php @@ -0,0 +1,45 @@ + + */ +class ApproveCiForNonContributors implements EventSubscriberInterface +{ + private WorkflowApi $workflowApi; + + public function __construct(WorkflowApi $workflowApi) + { + $this->workflowApi = $workflowApi; + } + + public function onPullRequest(GitHubEvent $event) + { + $data = $event->getData(); + if (!in_array($data['action'], ['opened', 'reopened', 'synchronize'])) { + return; + } + + $repository = $event->getRepository(); + $headRepository = $data['pull_request']['head']['repo']['full_name']; + $headBranch = $data['pull_request']['head']['ref']; + $this->workflowApi->approveWorkflowsForPullRequest($repository, $headRepository, $headBranch); + + $event->setResponseData(['approved_run' => true]); + } + + public static function getSubscribedEvents() + { + return [ + GitHubEvents::PULL_REQUEST => 'onPullRequest', + ]; + } +} diff --git a/tests/Controller/WebhookControllerTest.php b/tests/Controller/WebhookControllerTest.php index 70b0f358..e2809685 100644 --- a/tests/Controller/WebhookControllerTest.php +++ b/tests/Controller/WebhookControllerTest.php @@ -67,12 +67,12 @@ public function getTests() 'On pull request opened' => [ 'pull_request', 'pull_request.opened.json', - ['pull_request' => 3, 'status_change' => 'needs_review', 'pr_labels' => ['Console', 'Bug'], 'unsupported_branch' => '2.5'], + ['pull_request' => 3, 'status_change' => 'needs_review', 'pr_labels' => ['Console', 'Bug'], 'unsupported_branch' => '2.5', 'approved_run' => true], ], 'On draft pull request opened' => [ 'pull_request', 'pull_request.opened_draft.json', - ['pull_request' => 3, 'draft_comment' => true], + ['pull_request' => 3, 'draft_comment' => true, 'approved_run' => true], ], 'On pull request draft to ready' => [ 'pull_request', @@ -87,7 +87,7 @@ public function getTests() 'On pull request opened with target branch' => [ 'pull_request', 'pull_request.opened_target_branch.json', - ['pull_request' => 3, 'status_change' => 'needs_review', 'pr_labels' => ['Bug'], 'milestone' => '4.4'], + ['pull_request' => 3, 'status_change' => 'needs_review', 'pr_labels' => ['Bug'], 'milestone' => '4.4', 'approved_run' => true], ], 'On issue labeled bug' => [ 'issues', @@ -102,7 +102,7 @@ public function getTests() 'Welcome first users' => [ 'pull_request', 'pull_request.new_contributor.json', - ['pull_request' => 4, 'status_change' => 'needs_review', 'pr_labels' => [], 'new_contributor' => true, 'squash_comment' => true], + ['pull_request' => 4, 'status_change' => 'needs_review', 'pr_labels' => [], 'new_contributor' => true, 'squash_comment' => true, 'approved_run' => true], ], 'Waiting Code Merge' => [ 'pull_request', diff --git a/tests/webhook_examples/pull_request.opened.json b/tests/webhook_examples/pull_request.opened.json index 11f44996..2e785783 100644 --- a/tests/webhook_examples/pull_request.opened.json +++ b/tests/webhook_examples/pull_request.opened.json @@ -70,7 +70,7 @@ "repo": { "id": 21151556, "name": "symfony", - "full_name": "carsonbot-playground/symfony", + "full_name": "weaverryan/symfony", "owner": { "login": "weaverryan", "id": 121003,