diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml new file mode 100644 index 00000000..626800bf --- /dev/null +++ b/.github/workflows/blank.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: + tags: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + php: [7.1, 7.2, 7.3, 7.4] + steps: + - uses: actions/checkout@v1 + - name: Debug if needed + run: | + export DEBUG=${DEBUG:-false} + if [[ "$DEBUG" == "true" ]]; then + env + fi + env: + DEBUG: ${{secrets.DEBUG}} + - name: Setup php + uses: shivammathur/setup-php@1.7.0 + with: + php-version: ${{ matrix.php }} + extensions: dom, mbstring + coverage: xdebug + - name: Get Composer Cache Directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: Cache dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + - name: Install dependencies + run: composer install + - name: Run composer script + run: | + composer install-dev-tools; + composer sca diff --git a/README.md b/README.md index 5f32566d..db748afc 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,19 @@ In the "Configure your environment variables" section: COVERALLS_REPO_TOKEN=your_token ``` +## GitHub Actions + +Add a new step after phpunit generate coverage report. + +```yaml +- name: Upload coverage results to Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer global require php-coveralls/php-coveralls + php-coveralls --coverage_clover=build/logs/clover.xml -v +``` + ## From local environment If you would like to call Coveralls API from your local environment, you can set `COVERALLS_RUN_LOCALLY` environment variable. This configuration requires `repo_token` to specify which project on Coveralls your project maps to. This can be done by configuring `.coveralls.yml` or `COVERALLS_REPO_TOKEN` environment variable. @@ -265,6 +278,12 @@ Coveralls provides the ability to combine coverage result from multiple parallel COVERALLS_PARALLEL=true ``` +To distinguish your job name, please set the `COVERALLS_FLAG_NAME` environment variable. + +```sh +COVERALLS_FLAG_NAME=$YOUR_PHP_VERSION +``` + Bear in mind that you will need to configure your build to send a webhook after all the parallel builds are done in order for Coveralls to merge the results. Refer to [Parallel Builds Webhook](https://docs.coveralls.io/parallel-build-webhook) for more information for setup on your environment. diff --git a/src/Bundle/CoverallsBundle/Collector/CiEnvVarsCollector.php b/src/Bundle/CoverallsBundle/Collector/CiEnvVarsCollector.php index e9d9578d..af3f15c0 100644 --- a/src/Bundle/CoverallsBundle/Collector/CiEnvVarsCollector.php +++ b/src/Bundle/CoverallsBundle/Collector/CiEnvVarsCollector.php @@ -63,6 +63,7 @@ public function collect(array $env) ->fillCircleCi() ->fillAppVeyor() ->fillJenkins() + ->fillGithubActions() ->fillLocal() ->fillRepoToken() ->fillParallel(); @@ -113,6 +114,46 @@ protected function fillTravisCi() return $this; } + /** + * Fill Github Actions environment variables. + * + * @return $this + */ + protected function fillGithubActions() + { + if (!isset($this->env['GITHUB_ACTIONS'])) { + return $this; + } + $this->env['CI_NAME'] = 'github'; + + $githubEventName = $this->env['GITHUB_EVENT_NAME']; + $githubRef = $this->env['GITHUB_REF']; + + if (strpos($githubRef, 'refs/heads/') !== false) { + $githubRef = str_replace('refs/heads/', '', $githubRef); + } elseif ($githubEventName === 'pull_request') { + $refParts = explode('/', $githubRef); + $prNumber = $refParts[2]; + $this->env['CI_PULL_REQUEST'] = $prNumber; + $this->readEnv['CI_PULL_REQUEST'] = $this->env['CI_PULL_REQUEST']; + } elseif (strpos($githubRef, 'refs/tags/') !== false) { + $githubRef = str_replace('refs/tags/', '', $githubRef); + } + + // Same as coverallsapp/github-action + // @link https://github.com/coverallsapp/github-action/blob/5984097c6e76d873ef1d8e8e1836b0914d307c3c/src/run.ts#L47 + $this->env['CI_JOB_ID'] = $this->env['GITHUB_RUN_ID']; + $this->env['CI_BRANCH'] = $githubRef; + + $this->readEnv['GITHUB_ACTIONS'] = $this->env['GITHUB_ACTIONS']; + $this->readEnv['GITHUB_REF'] = $this->env['GITHUB_REF']; + $this->readEnv['CI_NAME'] = $this->env['CI_NAME']; + $this->readEnv['CI_JOB_ID'] = $this->env['CI_JOB_ID']; + $this->readEnv['CI_BRANCH'] = $this->env['CI_BRANCH']; + + return $this; + } + /** * Fill CircleCI environment variables. * @@ -241,6 +282,10 @@ protected function fillParallel() $this->readEnv['COVERALLS_PARALLEL'] = $this->env['COVERALLS_PARALLEL']; } + if (isset($this->env['COVERALLS_FLAG_NAME'])) { + $this->readEnv['COVERALLS_FLAG_NAME'] = $this->env['COVERALLS_FLAG_NAME']; + } + return $this; } } diff --git a/src/Bundle/CoverallsBundle/Collector/GitInfoCollector.php b/src/Bundle/CoverallsBundle/Collector/GitInfoCollector.php index 8ebd9f2c..9050449b 100644 --- a/src/Bundle/CoverallsBundle/Collector/GitInfoCollector.php +++ b/src/Bundle/CoverallsBundle/Collector/GitInfoCollector.php @@ -74,9 +74,9 @@ protected function collectBranch() foreach ($branchesResult as $result) { if (strpos($result, '* ') === 0) { - $exploded = explode('* ', $result, 2); + preg_match('~^\* (?:\(HEAD detached at )?([\w/]+)\)?~', $result, $matches); - return $exploded[1]; + return $matches[1]; } } diff --git a/src/Bundle/CoverallsBundle/Entity/Exception/RequirementsNotSatisfiedException.php b/src/Bundle/CoverallsBundle/Entity/Exception/RequirementsNotSatisfiedException.php index c743818a..a732e11b 100644 --- a/src/Bundle/CoverallsBundle/Entity/Exception/RequirementsNotSatisfiedException.php +++ b/src/Bundle/CoverallsBundle/Entity/Exception/RequirementsNotSatisfiedException.php @@ -73,6 +73,12 @@ public function getHelpMessage() - APPVEYOR - APPVEYOR_BUILD_NUMBER +For Githib Actions users: + - GITHUB_REF + - GITHUB_ACTIONS + - GITHUB_RUN_ID + - GITHUB_EVENT_NAME + From local environment: - COVERALLS_RUN_LOCALLY diff --git a/src/Bundle/CoverallsBundle/Entity/JsonFile.php b/src/Bundle/CoverallsBundle/Entity/JsonFile.php index 7f3bf40d..80131504 100644 --- a/src/Bundle/CoverallsBundle/Entity/JsonFile.php +++ b/src/Bundle/CoverallsBundle/Entity/JsonFile.php @@ -10,6 +10,7 @@ * Data represents "json_file" of Coveralls API. * * @author Kitamura Satoshi + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class JsonFile extends Coveralls { @@ -107,6 +108,14 @@ class JsonFile extends Coveralls */ protected $parallel; + /** + * If this is set, the job being reported will be named in the view and have + * it’s own independent status reported to your VCS provider. + * + * @var string + */ + protected $flagName; + // API /** @@ -129,6 +138,7 @@ public function toArray() 'service_event_type' => 'serviceEventType', 'repo_token' => 'repoToken', 'parallel' => 'parallel', + 'flag_name' => 'flagName', 'git' => 'git', 'run_at' => 'runAt', 'source_files' => 'sourceFiles', @@ -331,6 +341,30 @@ public function getParallel() return $this->parallel; } + /** + * Set flag name. + * + * @param string $flagName flag name + * + * @return $this + */ + public function setFlagName($flagName) + { + $this->flagName = $flagName; + + return $this; + } + + /** + * Return flag name. + * + * @return null|string + */ + public function getFlagName() + { + return $this->flagName; + } + /** * Set service job id. * @@ -541,6 +575,7 @@ protected function fillStandardizedEnvVars(array $env) 'serviceEventType' => 'COVERALLS_EVENT_TYPE', 'repoToken' => 'COVERALLS_REPO_TOKEN', 'parallel' => 'COVERALLS_PARALLEL', + 'flagName' => 'COVERALLS_FLAG_NAME', ]; foreach ($map as $propName => $envName) { @@ -581,6 +616,10 @@ protected function ensureJobs() return $this; } + if ($this->requireGithubActions()) { + return $this; + } + if ($this->isUnsupportedServiceJob()) { return $this; } @@ -636,6 +675,16 @@ protected function requireRepoToken() && $this->repoToken !== null; } + /** + * Return whether the job requires "service_number", "service_job_id" and "repo_token" (for GithubActions). + * + * @return bool + */ + protected function requireGithubActions() + { + return $this->serviceName === 'github' && $this->serviceJobId !== null && $this->repoToken !== null; + } + /** * Return whether the job is running on unsupported service. * diff --git a/tests/Bundle/CoverallsBundle/Collector/CiEnvVarsCollectorTest.php b/tests/Bundle/CoverallsBundle/Collector/CiEnvVarsCollectorTest.php index 55e54fcb..fdaabc3c 100644 --- a/tests/Bundle/CoverallsBundle/Collector/CiEnvVarsCollectorTest.php +++ b/tests/Bundle/CoverallsBundle/Collector/CiEnvVarsCollectorTest.php @@ -139,6 +139,97 @@ public function shouldCollectJenkinsEnvVars() return $object; } + /** + * @test + */ + public function shouldCollectGithubActionsEnvVars() + { + $serviceName = 'github'; + $jobId = '275038505'; + + $env = []; + $env['COVERALLS_REPO_TOKEN'] = 'token'; + $env['GITHUB_ACTIONS'] = true; + $env['GITHUB_EVENT_NAME'] = 'push'; + $env['GITHUB_REF'] = 'refs/heads/master'; + $env['GITHUB_RUN_ID'] = '275038505'; + + $object = $this->createCiEnvVarsCollector(); + + $actual = $object->collect($env); + + $this->assertArrayHasKey('CI_NAME', $actual); + $this->assertSame($serviceName, $actual['CI_NAME']); + + $this->assertArrayHasKey('CI_JOB_ID', $actual); + $this->assertSame($jobId, $actual['CI_JOB_ID']); + + $this->assertArrayHasKey('CI_BRANCH', $actual); + $this->assertSame('master', $actual['CI_BRANCH']); + + return $object; + } + + /** + * @test + */ + public function shouldCollectGithubActionsEnvVarsForPullRequest() + { + $serviceName = 'github'; + + $env = []; + $env['COVERALLS_REPO_TOKEN'] = 'token'; + $env['GITHUB_ACTIONS'] = true; + $env['GITHUB_EVENT_NAME'] = 'pull_request'; + $env['GITHUB_REF'] = 'refs/pull/1/merge'; + $env['GITHUB_RUN_ID'] = '275038505'; + + $object = $this->createCiEnvVarsCollector(); + + $actual = $object->collect($env); + + $this->assertArrayHasKey('CI_NAME', $actual); + $this->assertSame($serviceName, $actual['CI_NAME']); + + $this->assertArrayHasKey('CI_JOB_ID', $actual); + $this->assertSame('275038505', $actual['CI_JOB_ID']); + + $this->assertArrayHasKey('CI_BRANCH', $actual); + $this->assertSame('refs/pull/1/merge', $actual['CI_BRANCH']); + + return $object; + } + + /** + * @test + */ + public function shouldCollectGithubActionsEnvVarsForTag() + { + $serviceName = 'github'; + + $env = []; + $env['COVERALLS_REPO_TOKEN'] = 'token'; + $env['GITHUB_ACTIONS'] = true; + $env['GITHUB_EVENT_NAME'] = 'push'; + $env['GITHUB_REF'] = 'refs/tags/v2.3.0'; + $env['GITHUB_RUN_ID'] = '275038505'; + + $object = $this->createCiEnvVarsCollector(); + + $actual = $object->collect($env); + + $this->assertArrayHasKey('CI_NAME', $actual); + $this->assertSame($serviceName, $actual['CI_NAME']); + + $this->assertArrayHasKey('CI_JOB_ID', $actual); + $this->assertSame('275038505', $actual['CI_JOB_ID']); + + $this->assertArrayHasKey('CI_BRANCH', $actual); + $this->assertSame('v2.3.0', $actual['CI_BRANCH']); + + return $object; + } + /** * @test */ @@ -215,9 +306,11 @@ public function shouldCollectUnsupportedEnvVars() public function shouldCollectParallel() { $parallel = true; + $flagName = 'php-7.4'; $env = []; $env['COVERALLS_PARALLEL'] = $parallel; + $env['COVERALLS_FLAG_NAME'] = $flagName; $object = $this->createCiEnvVarsCollector(); @@ -226,6 +319,9 @@ public function shouldCollectParallel() $this->assertArrayHasKey('COVERALLS_PARALLEL', $actual); $this->assertSame($parallel, $actual['COVERALLS_PARALLEL']); + $this->assertArrayHasKey('COVERALLS_FLAG_NAME', $actual); + $this->assertSame($flagName, $actual['COVERALLS_FLAG_NAME']); + return $object; } @@ -258,6 +354,25 @@ public function shouldHaveReadEnvAfterCollectTravisCiEnvVars(CiEnvVarsCollector $this->assertArrayHasKey('CI_BUILD_NUMBER', $readEnv); } + /** + * @test + * @depends shouldCollectGithubActionsEnvVars + * + * @param CiEnvVarsCollector $object + */ + public function shouldHaveReadEnvAfterCollectGithubActionsEnvVars(CiEnvVarsCollector $object) + { + $readEnv = $object->getReadEnv(); + + $this->assertCount(6, $readEnv); + $this->assertArrayHasKey('GITHUB_REF', $readEnv); + $this->assertArrayHasKey('CI_NAME', $readEnv); + $this->assertArrayHasKey('CI_JOB_ID', $readEnv); + $this->assertArrayHasKey('GITHUB_ACTIONS', $readEnv); + $this->assertArrayHasKey('CI_BRANCH', $readEnv); + $this->assertArrayHasKey('COVERALLS_REPO_TOKEN', $readEnv); + } + /** * @test * @depends shouldCollectTravisProEnvVars diff --git a/tests/Bundle/CoverallsBundle/Collector/GitInfoCollectorTest.php b/tests/Bundle/CoverallsBundle/Collector/GitInfoCollectorTest.php index 3a267cbf..2e4e7c0a 100644 --- a/tests/Bundle/CoverallsBundle/Collector/GitInfoCollectorTest.php +++ b/tests/Bundle/CoverallsBundle/Collector/GitInfoCollectorTest.php @@ -74,6 +74,23 @@ public function shouldCollect() $this->assertGit($git); } + /** + * @test + */ + public function shouldCollectDetachedRef() + { + $gitCommand = $this->createGitCommandStubWith( + ['* (HEAD detached at pull/1/merge)'], + $this->getHeadCommitValue, + $this->getRemotesValue + ); + $object = new GitInfoCollector($gitCommand); + + $git = $object->collect(); + + $this->assertSame('pull/1/merge', $git->getBranch()); + } + // collectBranch() exception /** diff --git a/tests/Bundle/CoverallsBundle/Entity/JsonFileTest.php b/tests/Bundle/CoverallsBundle/Entity/JsonFileTest.php index 028a2a08..8bc6a296 100644 --- a/tests/Bundle/CoverallsBundle/Entity/JsonFileTest.php +++ b/tests/Bundle/CoverallsBundle/Entity/JsonFileTest.php @@ -215,6 +215,23 @@ public function shouldSetParallel() return $this->object; } + // setFlagName() + + /** + * @test + */ + public function shouldSetFlagName() + { + $expected = 'php-7.4'; + + $obj = $this->object->setFlagName($expected); + + $this->assertSame($expected, $this->object->getFlagName()); + $this->assertSame($obj, $this->object); + + return $this->object; + } + // setServiceJobId() /** @@ -577,6 +594,29 @@ public function shouldFillJobsForServiceEventType() $this->assertSame($serviceEventType, $object->getServiceEventType()); } + /** + * @test + */ + public function shouldFillJobsForGithubActions() + { + $repoToken = 'token'; + $serviceName = 'github'; + $serviceJobId = '1.1'; + + $env = []; + $env['CI_NAME'] = $serviceName; + $env['CI_JOB_ID'] = $serviceJobId; + $env['COVERALLS_REPO_TOKEN'] = $repoToken; + + $object = $this->collectJsonFile(); + + $same = $object->fillJobs($env); + + $this->assertSame($same, $object); + $this->assertSame($serviceName, $object->getServiceName()); + $this->assertSame($serviceJobId, $object->getServiceJobId()); + } + /** * @test */