Skip to content

Commit

Permalink
feat(api): support generic CI env vars (TheKevJames#299)
Browse files Browse the repository at this point in the history
The official docs for Coveralls define a set of generic environment
variables that once defined should support any CI environment:

https://docs.coveralls.io/supported-ci-services (bottom of the page).

The changes implemented in this PR attempt to read those variables
but allows the values to be overwritten by any CI-specific configuration
currently supported. They were also inspired by the official client
(coveralls-ruby - lib/coveralls/configuration.rb -
set_standard_service_params_for_generic_ci).
  • Loading branch information
abravalheri committed Jun 3, 2021
1 parent 5799354 commit 243778c
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
30 changes: 29 additions & 1 deletion coveralls/api.py
Expand Up @@ -16,6 +16,8 @@

log = logging.getLogger('coveralls.api')

NUMBER_REGEX = re.compile(r'(\d+)$', re.IGNORECASE)


class Coveralls:
# pylint: disable=too-many-public-methods
Expand Down Expand Up @@ -144,7 +146,33 @@ def load_config_from_semaphore():
def load_config_from_unknown():
return 'coveralls-python', None, None, None

def load_config_from_generic_ci_environment(self):
# Inspired by the official client:
# coveralls-ruby in lib/coveralls/configuration.rb
# (set_standard_service_params_for_generic_ci)

config = {
'service_name': os.environ.get('CI_NAME'),
'service_number': os.environ.get('CI_BUILD_NUMBER'),
'service_build_url': os.environ.get('CI_BUILD_URL'),
'service_job_id': os.environ.get('CI_JOB_ID'),
'service_branch': os.environ.get('CI_BRANCH'),
}

pr_match = NUMBER_REGEX.findall(os.environ.get('CI_PULL_REQUEST', ''))
if pr_match:
config['service_pull_request'] = pr_match[-1]

non_empty = {key: value for key, value in config.items() if value}
self.config.update(non_empty)

def load_config_from_ci_environment(self):
# As defined at the bottom of
# https://docs.coveralls.io/supported-ci-services
# there are a few env vars that should support any arbitrary CI.
# We load them first and allow more specific vars to overwrite
self.load_config_from_generic_ci_environment()

if os.environ.get('APPVEYOR'):
name, job, number, pr = self.load_config_from_appveyor()
elif os.environ.get('BUILDKITE'):
Expand All @@ -166,7 +194,7 @@ def load_config_from_ci_environment(self):
else:
name, job, number, pr = self.load_config_from_unknown()

self.config['service_name'] = name
self.config.setdefault('service_name', name)
if job:
self.config['service_job_id'] = job
if number:
Expand Down
24 changes: 24 additions & 0 deletions docs/usage/configuration.rst
Expand Up @@ -156,3 +156,27 @@ you can copy from the coveralls.io website).
As per `#245 <https://github.com/TheKevJames/coveralls-python/issues/245>`_,
our users suggest leaving "keep this value secret" unchecked -- this may be
secure enough as-is, in that a user making a PR cannot access this variable.

Other CI systems
----------------

As specified in the Coveralls `official docs
<https://docs.coveralls.io/supported-ci-services>`
other CI systems can be supported if the following environment variables are
defined::

CI_NAME
# Name of the CI service being used
CI_BUILD_NUMBER
# Number (counter) relative to the current build
CI_BUILD_URL
# URL to a webpage showing the build information/output
CI_BRANCH
# For pull requests this is the name of the branch targeted by the PR,
# otherwise it corresponds to the name of the current branch or tag
CI_JOB_ID (optional)
# Unique identifier of the job in the CI service.
# When missing, CI_BUILD_NUMBER is used
CI_PULL_REQUEST (optional)
# If given, corresponds to the number of the pull request, as specified
# in the supported repository hosting service (GitHub, GitLab, etc)
34 changes: 34 additions & 0 deletions tests/api/configuration_test.py
Expand Up @@ -198,6 +198,40 @@ def test_semaphore_20_no_config(self):
assert cover.config['service_number'] == 'b86b3adf'
assert cover.config['service_pull_request'] == '9999'

@mock.patch.dict(
os.environ,
{'CI_NAME': 'generic-ci',
'CI_PULL_REQUEST': 'pull/1234',
'CI_JOB_ID': 'bb0e00166',
'CI_BUILD_NUMBER': '3',
'CI_BUILD_URL': 'https://generic-ci.local/build/123456789',
'CI_BRANCH': 'fixup-branch',
'COVERALLS_REPO_TOKEN': 'xxx'},
clear=True)
def test_generic_no_config(self):
cover = Coveralls()
assert cover.config['service_name'] == 'generic-ci'
assert cover.config['service_job_id'] == 'bb0e00166'
assert cover.config['service_branch'] == 'fixup-branch'
assert cover.config['service_pull_request'] == '1234'

@mock.patch.dict(
os.environ,
{'CI_NAME': 'generic-ci',
'CI_PULL_REQUEST': '',
'CI_JOB_ID': 'bb0e00166',
'CI_BUILD_NUMBER': '3',
'CI_BUILD_URL': 'https://generic-ci.local/build/123456789',
'CI_BRANCH': 'fixup-branch',
'COVERALLS_REPO_TOKEN': 'xxx'},
clear=True)
def test_generic_no_config_no_pr(self):
cover = Coveralls()
assert cover.config['service_name'] == 'generic-ci'
assert cover.config['service_job_id'] == 'bb0e00166'
assert cover.config['service_branch'] == 'fixup-branch'
assert 'service_pull_request' not in cover.config

@mock.patch.dict(os.environ, {'COVERALLS_SERVICE_NAME': 'xxx'}, clear=True)
def test_service_name_from_env(self):
cover = Coveralls(repo_token='yyy')
Expand Down

0 comments on commit 243778c

Please sign in to comment.