Skip to content

Commit

Permalink
Fixed HTTP 422 for Github Actions; Improved/added log output
Browse files Browse the repository at this point in the history
Details:

* The API documentation of coveralls.io states in
  https://docs.coveralls.io/api-jobs-endpoint that 'service_job_id' is
  required and is a unique ID for the job. Experiments show that
  not providing it causes result submission to fail with
  HTTP 422 "Unprocessable Entity".

  The current code of coveralls-python sets 'service_job_id' to None/null
  when submitting to coveralls.io.

  This worked until recently, and now causes HTTP 422 "Unprocessable Entity".

  This PR fixes that by setting 'service_job_id' to GITHUB_RUN_ID.

  Note that the current code sets 'service_number' to GITHUB_RUN_ID as well,
  and that is not changed by this PR.

* Added a description of how the various service* request parameters
  work, based on the API documentation of coveralls.io and experiments,
  which allowed to partly correct that documentation.

* Changed the print() for resubmission of a result to become a log.warning()
  call so it shows up in the ouutput at the right position.

* Added log.info() calls for the original result submission and for the
  submission of the finish request.

Signed-off-by: Andreas Maier <andreas.r.maier@gmx.de>
  • Loading branch information
andy-maier committed Apr 30, 2023
1 parent 8901de7 commit 093a297
Showing 1 changed file with 59 additions and 11 deletions.
70 changes: 59 additions & 11 deletions coveralls/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,23 @@ def load_config_from_github(self):
# the same -- forceibly using Github's flow seems to be more stable
self.config['repo_token'] = os.environ.get('GITHUB_TOKEN')

# TODO: Clarify default service.
# From experiments, it seems that service should always be set to
# 'github'. However, that is not yet confirmed - for discussion, see
# https://github.com/lemurheavy/coveralls-public/issues/1710.
# For compatibility, the service continues to be set to 'github-actions'
# for now. Users can specify the service name manually to override
# this.
service = 'github-actions'

job = os.environ.get('GITHUB_RUN_ID')
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]

# N.B. some users require this to be 'github' and some require it to
# be 'github-actions'. Defaulting to 'github-actions' as it seems more
# common -- users can specify the service name manually to override
# this.
return 'github-actions', None, os.environ.get('GITHUB_RUN_ID'), pr
return service, job, number, pr

@staticmethod
def load_config_from_jenkins():
Expand Down Expand Up @@ -185,9 +193,6 @@ def load_config_from_ci_environment(self):
elif os.environ.get('CIRCLECI'):
name, job, number, pr = self.load_config_from_circle()
elif os.environ.get('GITHUB_ACTIONS'):
# N.B. Github Actions fails if this is not set even when null.
# Other services fail if this is set to null. Sigh.
self.config['service_job_id'] = None
name, job, number, pr = self.load_config_from_github()
elif os.environ.get('JENKINS_HOME'):
name, job, number, pr = self.load_config_from_jenkins()
Expand All @@ -199,6 +204,34 @@ def load_config_from_ci_environment(self):
else:
name, job, number, pr = self.load_config_from_unknown()

# Documentation of some request parameters,
# from https://docs.coveralls.io/api-jobs-endpoint:
# * service_name (String, required) - The CI service or other
# environment in which the test suite was run. Can be anything, but
# certain values may trigger automatic support by certain Coveralls
# Integrations.
# * service_number (String) - The build number. Will default to
# incrementing integers, applied chronologically to the builds on
# a repo.
# * service_job_id (String, required) - A unique identifier for the
# job, assigned by the service specified in "service_name".
# * service_job_number (String) - The job number. Will default to
# incrementing integers based on the jobs in a build.
#
# Notes:
# * Other than documented, the optional 'service_number' request
# parameter defaults to the 'service_job_id' parameter. The resulting
# value for the 'service_number' parameter is treated by coveralls.io
# as a unique identifier for a build. It is used to match any parallel
# submissions, and also to match a parallel finish request via its
# 'build_num' request parameter. This unique ID is also shown on the
# coveralls.io web site as an identifier for the build.
# * It is not clear whether coveralls.io uses the 'service_job_id'
# request parameter for anything else but as a default for
# 'service_number'.
# * The optional 'service_job_number' request parameter is used by
# coveralls.io as the job number behind the unique build ID.

self.config.setdefault('service_name', name)
if job:
self.config['service_job_id'] = job
Expand Down Expand Up @@ -226,7 +259,9 @@ def load_config_from_environment(self):
}
for var, key in fields.items():
value = os.environ.get(var)
if value:
if value == 'omit' and key in self.config:
del self.config[key]
elif value:
self.config[key] = value

def load_config_from_file(self):
Expand Down Expand Up @@ -259,6 +294,12 @@ def wear(self, dry_run=False):
def submit_report(self, json_string):
endpoint = '{}/api/v1/jobs'.format(self._coveralls_host.rstrip('/'))
verify = not bool(os.environ.get('COVERALLS_SKIP_SSL_VERIFY'))
log.info('Submitting with: service_name=%r, service_number=%r, '
'service_job_id=%r, service_job_number=%r',
self.config.get('service_name'),
self.config.get('service_number'),
self.config.get('service_job_id'),
self.config.get('service_job_number'))
response = requests.post(endpoint, files={'json_file': json_string},
verify=verify)

Expand All @@ -276,9 +317,14 @@ def submit_report(self, json_string):
new_id = '{}-{}'.format(
self.config.get('service_job_id', 42),
random.randint(0, sys.maxsize))
print('resubmitting with id {}'.format(new_id))

self.config['service_job_id'] = new_id
log.warning(
'Resubmitting with changed service_job_id: service_name=%r, '
'service_number=%r, service_job_id=%r, service_job_number=%r',
self.config.get('service_name'),
self.config.get('service_number'),
self.config.get('service_job_id'),
self.config.get('service_job_number'))
self._data = None # force create_report to use updated data
json_string = self.create_report()

Expand Down Expand Up @@ -308,6 +354,8 @@ def parallel_finish(self):
# Github Actions only
payload['repo_name'] = os.environ.get('GITHUB_REPOSITORY')

log.info('Submitting parallel finish request with: build_num=%r',
payload['payload'].get('build_num'))
endpoint = '{}/webhook'.format(self._coveralls_host.rstrip('/'))
verify = not bool(os.environ.get('COVERALLS_SKIP_SSL_VERIFY'))
response = requests.post(endpoint, json=payload, verify=verify)
Expand Down

0 comments on commit 093a297

Please sign in to comment.