Skip to content

Commit

Permalink
Update Github Actions support
Browse files Browse the repository at this point in the history
  • Loading branch information
TimoRoth committed Jun 9, 2020
1 parent b120650 commit dd87892
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 39 deletions.
69 changes: 42 additions & 27 deletions coveralls/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,12 @@ def __init__(self, token_required=True, service_name=None, **kwargs):

self.load_config_from_environment()

name, job, pr = self.load_config_from_ci_environment()
name, job, number, pr = self.load_config_from_ci_environment()
self.config['service_name'] = self.config.get('service_name', name)
if job:
# N.B. Github Actions uses a different chunk of the Coveralls
# config when running parallel builds, ie. `service_number` instead
# of `service_job_id`.
if name.startswith('github'):
self.config['service_number'] = job
else:
self.config['service_job_id'] = job
self.config['service_job_id'] = job
if number:
self.config['service_number'] = number
if pr:
self.config['service_pull_request'] = pr

Expand All @@ -68,6 +64,17 @@ def ensure_token(self):
if self.config.get('repo_token') or not self._token_required:
return

service = self.config.get('service_name') or ''
if self._token_required and service.startswith('github'):
gh_token = os.environ.get('GITHUB_TOKEN')
if gh_token:
self.config['repo_token'] = gh_token
return
raise CoverallsException(
'Running on Github Actions but GITHUB_TOKEN is not set. '
'Add "env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}" to '
'your step config.')

raise CoverallsException(
'Not on TravisCI. You have to provide either repo_token in {} or '
'set the COVERALLS_REPO_TOKEN env var.'.format(
Expand All @@ -76,67 +83,71 @@ def ensure_token(self):
@staticmethod
def load_config_from_appveyor():
pr = os.environ.get('APPVEYOR_PULL_REQUEST_NUMBER')
return 'appveyor', os.environ.get('APPVEYOR_BUILD_ID'), pr
return 'appveyor', os.environ.get('APPVEYOR_BUILD_ID'), None, pr

@staticmethod
def load_config_from_buildkite():
pr = os.environ.get('BUILDKITE_PULL_REQUEST')
if pr == 'false':
pr = None
return 'buildkite', os.environ.get('BUILDKITE_JOB_ID'), pr
return 'buildkite', os.environ.get('BUILDKITE_JOB_ID'), None, pr

@staticmethod
def load_config_from_circle():
pr = os.environ.get('CI_PULL_REQUEST', '').split('/')[-1] or None
return 'circle-ci', os.environ.get('CIRCLE_BUILD_NUM'), pr
return 'circle-ci', os.environ.get('CIRCLE_BUILD_NUM'), None, pr

@staticmethod
def load_config_from_github():
service_number = os.environ.get('GITHUB_SHA')
service = 'github'
if 'COVERALLS_REPO_TOKEN' in os.environ:
service = 'github-actions'
job = os.environ.get('GITHUB_RUN_NUMBER')
number = os.environ.get('GITHUB_RUN_ID')
pr = None
if os.environ.get('GITHUB_REF', '').startswith('refs/pull/'):
pr = os.environ.get('GITHUB_REF', '//').split('/')[2]
service_number += '-PR-{}'.format(pr)
return 'github-actions', service_number, pr
return service, job, number, pr

@staticmethod
def load_config_from_jenkins():
pr = os.environ.get('CI_PULL_REQUEST', '').split('/')[-1] or None
return 'jenkins', os.environ.get('BUILD_NUMBER'), pr
return 'jenkins', os.environ.get('BUILD_NUMBER'), None, pr

@staticmethod
def load_config_from_travis():
pr = os.environ.get('TRAVIS_PULL_REQUEST')
return 'travis-ci', os.environ.get('TRAVIS_JOB_ID'), pr
return 'travis-ci', os.environ.get('TRAVIS_JOB_ID'), None, pr

@staticmethod
def load_config_from_semaphore():
job = os.environ.get('SEMAPHORE_BUILD_NUMBER')
pr = os.environ.get('PULL_REQUEST_NUMBER')
return 'semaphore-ci', os.environ.get('SEMAPHORE_BUILD_NUMBER'), pr
return 'semaphore-ci', job, None, pr

@staticmethod
def load_config_from_unknown():
return 'coveralls-python', None, None
return 'coveralls-python', None, None, None

def load_config_from_ci_environment(self):
if os.environ.get('APPVEYOR'):
name, job, pr = self.load_config_from_appveyor()
name, job, number, pr = self.load_config_from_appveyor()
elif os.environ.get('BUILDKITE'):
name, job, pr = self.load_config_from_buildkite()
name, job, number, pr = self.load_config_from_buildkite()
elif os.environ.get('CIRCLECI'):
name, job, pr = self.load_config_from_circle()
name, job, number, pr = self.load_config_from_circle()
elif os.environ.get('GITHUB_ACTIONS'):
name, job, pr = self.load_config_from_github()
name, job, number, pr = self.load_config_from_github()
elif os.environ.get('JENKINS_HOME'):
name, job, pr = self.load_config_from_jenkins()
name, job, number, pr = self.load_config_from_jenkins()
elif os.environ.get('TRAVIS'):
self._token_required = False
name, job, pr = self.load_config_from_travis()
name, job, number, pr = self.load_config_from_travis()
elif os.environ.get('SEMAPHORE'):
name, job, pr = self.load_config_from_semaphore()
name, job, number, pr = self.load_config_from_semaphore()
else:
name, job, pr = self.load_config_from_unknown()
return (name, job, pr)
name, job, number, pr = self.load_config_from_unknown()
return (name, job, number, pr)

def load_config_from_environment(self):
coveralls_host = os.environ.get('COVERALLS_HOST')
Expand All @@ -159,6 +170,10 @@ def load_config_from_environment(self):
if flag_name:
self.config['flag_name'] = flag_name

number = os.environ.get('COVERALLS_SERVICE_JOB_NUMBER')
if number:
self.config['service_number'] = number

def load_config_from_file(self):
try:
with open(os.path.join(os.getcwd(),
Expand Down
40 changes: 34 additions & 6 deletions docs/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,41 @@ Sample ``.coveralls.yml`` file::
Github Actions Gotcha
---------------------

There's something weird with using Github Actions that we've not yet been able to entirely sort out -- if you find you're getting a 422 error on Github Actions which looks like this::

Could not submit coverage: 422 Client Error: Unprocessable Entity for url: https://coveralls.io/api/v1/jobs

Then you may be able to solve it by ensuring your ``secret`` is named ``COVERALLS_REPO_TOKEN``; it seems like Github Actions may do Magic(tm) to some environment variables based on their name. The following config block seems to work properly::
Coveralls natively supports jobs running on Github Actions. You can directly pass the default-provided secret GITHUB_TOKEN::

env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
coveralls

For parallel builds you have to specify a unique flag-name for every step/job. You can use the official coveralls action to finalize the build::

jobs:
test:
strategy:
matrix:
test-name:
- test1
- test2
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Test
run: |
./run_tests.sh ${{ matrix.test-name }}
coveralls
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_PARALLEL: true
COVERALLS_FLAG_NAME: test-${{ matrix.test-env }}
coveralls:
name: Coveralls
needs: test
runs-on: ubuntu-latest
steps:
- name: Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
3 changes: 3 additions & 0 deletions docs/usage/tox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ All variables:
- ``GITHUB_REF``
- ``GITHUB_SHA``
- ``GITHUB_HEAD_REF``
- ``GITHUB_RUN_ID``
- ``GITHUB_RUN_NUMBER``
- ``GITHUB_TOKEN``

Jenkins
-------
Expand Down
28 changes: 22 additions & 6 deletions tests/api/configuration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def test_misconfigured(self):
'Not on TravisCI. You have to provide either repo_token in '
'.coveralls.mock or set the COVERALLS_REPO_TOKEN env var.')

@mock.patch.dict(os.environ, {'GITHUB_ACTIONS': 'true'}, clear=True)
def test_misconfigured_github(self):
with pytest.raises(Exception) as excinfo:
Coveralls()

assert str(excinfo.value).startswith(
'Running on Github Actions but GITHUB_TOKEN is not set.')

@mock.patch.dict(os.environ, {'APPVEYOR': 'True',
'APPVEYOR_BUILD_ID': '1234567',
'APPVEYOR_PULL_REQUEST_NUMBER': '1234'},
Expand Down Expand Up @@ -113,26 +121,34 @@ def test_circleci_no_config(self):
{'GITHUB_ACTIONS': 'true',
'GITHUB_REF': 'refs/pull/1234/merge',
'GITHUB_SHA': 'bb0e00166b28f49db04d6a8b8cb4bddb5afa529f',
'GITHUB_HEAD_REF': 'fixup-branch'},
'GITHUB_RUN_ID': '123456789',
'GITHUB_RUN_NUMBER': '12',
'GITHUB_HEAD_REF': 'fixup-branch',
'COVERALLS_REPO_TOKEN': 'xxx'},
clear=True)
def test_github_no_config(self):
cover = Coveralls(repo_token='xxx')
cover = Coveralls()
assert cover.config['service_name'] == 'github-actions'
assert cover.config['service_pull_request'] == '1234'
assert 'service_job_id' not in cover.config
assert cover.config['service_job_id'] == '123456789'
assert cover.config['service_number'] == '12'

@mock.patch.dict(
os.environ,
{'GITHUB_ACTIONS': 'true',
'GITHUB_TOKEN': 'xxx',
'GITHUB_REF': 'refs/heads/master',
'GITHUB_SHA': 'bb0e00166b28f49db04d6a8b8cb4bddb5afa529f',
'GITHUB_RUN_ID': '987654321',
'GITHUB_RUN_NUMBER': '21',
'GITHUB_HEAD_REF': ''},
clear=True)
def test_github_no_config_no_pr(self):
cover = Coveralls(repo_token='xxx')
assert cover.config['service_name'] == 'github-actions'
cover = Coveralls()
assert cover.config['service_name'] == 'github'
assert cover.config['service_job_id'] == '987654321'
assert cover.config['service_number'] == '21'
assert 'service_pull_request' not in cover.config
assert 'service_job_id' not in cover.config

@mock.patch.dict(
os.environ,
Expand Down

0 comments on commit dd87892

Please sign in to comment.