[stable-2.14] 🧪 Pin codecov-cli deptree used in AZP (#85903)

* Use codecov-cli for uploads (#85386)

ci_coverage
ci_complete

(cherry picked from commit 4a03ccbd41)

* 🧪 Pin codecov-cli deptree used in AZP

PR #85888

`codecov-cli == 11.0.3` allows `click == 8.3.0` in its deps but the latter causes commit auto-discovery breakage in the former. With https://github.com/getsentry/prevent-cli/pull/95, `codecov-cli == 11.2.3` excludes this version so this patch updates the requirement to that.

To prevent this from happening again, the change also makes use of a pip constraint file that pins the entire dependency tree to concrete versions. The constraint file is managed by `pip-tools`.

Refs:
* https://github.com/getsentry/prevent-cli/pull/95
* https://github.com/pallets/click/issues/3066

ci_coverage
ci_complete

(cherry picked from commit 1e572ba5cc)

* Unignore `publish-codecov.py` sanity violations

---------

Co-authored-by: Matt Clay <matt@mystile.com>
pull/86104/head
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) 2 months ago committed by GitHub
parent 22130a2455
commit 0feee4824c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,6 @@
[tool.pip-tools]
allow-unsafe = true # weird outdated default
annotation-style = "line" # put the source tracking comments inline
generate-hashes = false # pip bug https://github.com/pypa/pip/issues/9243
resolver = "backtracking" # modern depresolver
strip-extras = true # so that output files are true pip constraints

@ -0,0 +1,18 @@
#
# This file is autogenerated by pip-compile with Python 3.13
# by the following command:
#
# pip-compile --allow-unsafe --annotation-style=line --output-file=codecov.txt --strip-extras codecov.in
#
certifi==2025.8.3 # via requests, sentry-sdk
charset-normalizer==3.4.3 # via requests
click==8.2.1 # via codecov-cli
codecov-cli==11.2.3 # via -r codecov.in
idna==3.10 # via requests
ijson==3.4.0 # via codecov-cli
pyyaml==6.0.2 # via codecov-cli
requests==2.32.5 # via responses
responses==0.21.0 # via codecov-cli
sentry-sdk==2.38.0 # via codecov-cli
test-results-parser==0.5.4 # via codecov-cli
urllib3==2.5.0 # via requests, responses, sentry-sdk

@ -8,11 +8,15 @@ Python coverage, as well as PowerShell and Python stubs can all be uploaded.
import argparse import argparse
import dataclasses import dataclasses
import pathlib import pathlib
import shutil import shlex
import subprocess import subprocess
import tempfile import tempfile
import typing as t import typing as t
import urllib.request import venv
SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve()
DEPS_DIR = SCRIPTS_DIR / 'dependencies'
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@ -42,6 +46,36 @@ def parse_args() -> Args:
return Args(**kwargs) return Args(**kwargs)
def run(*args: str | pathlib.Path) -> None:
cmd = [str(arg) for arg in args]
print(f'==> {shlex.join(cmd)}', flush=True)
subprocess.run(cmd, check=True)
def install_codecov(dest: pathlib.Path) -> pathlib.Path:
"""Populate a transitively pinned venv with ``codecov-cli``."""
requirement_file = DEPS_DIR / 'codecov.in'
constraint_file = requirement_file.with_suffix('.txt')
venv_dir = dest / 'venv'
python_bin = venv_dir / 'bin' / 'python'
codecov_bin = venv_dir / 'bin' / 'codecovcli'
venv.create(venv_dir, with_pip=True)
run(
python_bin,
'-m',
'pip',
'install',
f'--constraint={constraint_file!s}',
f'--requirement={requirement_file!s}',
'--disable-pip-version-check',
)
return codecov_bin
def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]: def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
processed = [] processed = []
for file in directory.joinpath('reports').glob('coverage*.xml'): for file in directory.joinpath('reports').glob('coverage*.xml'):
@ -56,45 +90,42 @@ def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
return tuple(processed) return tuple(processed)
def upload_files(codecov_bin: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None: def upload_files(codecov_bin: pathlib.Path, config_file: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None:
for file in files: for file in files:
cmd = [ cmd = [
str(codecov_bin), str(codecov_bin),
'--name', file.name, '--disable-telem',
'--file', str(file.path), '--codecov-yml-path',
config_file,
'upload-process',
'--disable-search',
'--disable-file-fixes',
'--plugin',
'noop',
'--name',
file.name,
'--file',
file.path,
] ]
for flag in file.flags: for flag in file.flags:
cmd.extend(['--flags', flag]) cmd.extend(['--flag', flag])
if dry_run: if dry_run:
print(f'DRY-RUN: Would run command: {cmd}') cmd.append('--dry-run')
continue
subprocess.run(cmd, check=True)
def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None: run(*cmd)
if dry_run:
print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
return
with urllib.request.urlopen(url) as resp:
with dest.open('w+b') as f:
# Read data in chunks rather than all at once
shutil.copyfileobj(resp, f, 64 * 1024)
dest.chmod(flags) def main() -> None:
def main():
args = parse_args() args = parse_args()
url = 'https://ci-files.testing.ansible.com/codecov/linux/codecov'
with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir: with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
codecov_bin = pathlib.Path(tmpdir) / 'codecov' config_file = pathlib.Path(tmpdir) / 'config.yml'
download_file(url, codecov_bin, 0o755, args.dry_run) config_file.write_text('')
codecov_bin = install_codecov(pathlib.Path(tmpdir))
files = process_files(args.path) files = process_files(args.path)
upload_files(codecov_bin, files, args.dry_run) upload_files(codecov_bin, config_file, files, args.dry_run)
if __name__ == '__main__': if __name__ == '__main__':

@ -1,4 +1,3 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
lib/ansible/cli/scripts/ansible_connection_cli_stub.py shebang lib/ansible/cli/scripts/ansible_connection_cli_stub.py shebang
lib/ansible/config/base.yml no-unwanted-files lib/ansible/config/base.yml no-unwanted-files
lib/ansible/executor/playbook_executor.py pylint:disallowed-name lib/ansible/executor/playbook_executor.py pylint:disallowed-name

Loading…
Cancel
Save