Compare commits

..

No commits in common. "main" and "5.2.1" have entirely different histories.
main ... 5.2.1

35 changed files with 765 additions and 331 deletions

View File

@ -37,116 +37,293 @@ variables:
- name: coverageBranches - name: coverageBranches
value: main value: main
- name: entryPoint - name: entryPoint
value: tests/utils/shippable/nox.sh value: tests/utils/shippable/shippable.sh
- name: prepareEntryPoint
value: tests/utils/shippable/nox-prepare.sh
- name: fetchDepth - name: fetchDepth
value: 0 value: 0
resources: resources:
containers: containers:
- container: default - container: default
image: quay.io/ansible/azure-pipelines-test-container:8.0.0 image: quay.io/ansible/azure-pipelines-test-container:7.0.0
pool: Standard pool: Standard
stages: stages:
- stage: remote_2_19
displayName: Remote 2.19 ### Sanity & units
- stage: Ansible_devel
displayName: Sanity & Units devel
dependsOn: [] dependsOn: []
variables:
entryPoint: tests/utils/shippable/nox.sh
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
targets: targets:
- name: Ubuntu 22.04 + group 1 - name: Sanity
test: ansible-test-integration-2.19-ubuntu-22.04-azp-1 test: 'devel/sanity/1'
- name: Ubuntu 22.04 + group 2 - name: Units
test: ansible-test-integration-2.19-ubuntu-22.04-azp-2 test: 'devel/units/1'
- name: Ubuntu 22.04 + group 3 - stage: Ansible_2_21
test: ansible-test-integration-2.19-ubuntu-22.04-azp-3 displayName: Sanity & Units 2.21
- name: Ubuntu 22.04 + group 4
test: ansible-test-integration-2.19-ubuntu-22.04-azp-4
- name: Ubuntu 22.04 + group 5
test: ansible-test-integration-2.19-ubuntu-22.04-azp-5
- stage: remote_2_20
displayName: Remote 2.20
dependsOn: [] dependsOn: []
variables:
entryPoint: tests/utils/shippable/nox.sh
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
targets: targets:
- name: RHEL 9.7 + group 1 - name: Sanity
test: ansible-test-integration-2.20-rhel-9.7-azp-1 test: '2.21/sanity/1'
- name: RHEL 9.7 + group 2 - name: Units
test: ansible-test-integration-2.20-rhel-9.7-azp-2 test: '2.21/units/1'
- name: RHEL 9.7 + group 3 - stage: Ansible_2_20
test: ansible-test-integration-2.20-rhel-9.7-azp-3 displayName: Sanity & Units 2.20
- name: RHEL 9.7 + group 4
test: ansible-test-integration-2.20-rhel-9.7-azp-4
- name: RHEL 9.7 + group 5
test: ansible-test-integration-2.20-rhel-9.7-azp-5
- stage: remote_2_21
displayName: Remote 2.21
dependsOn: [] dependsOn: []
variables:
entryPoint: tests/utils/shippable/nox.sh
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
targets: targets:
- name: RHEL 10.1 + group 1 - name: Sanity
test: ansible-test-integration-2.21-rhel-10.1-azp-1 test: '2.20/sanity/1'
- name: RHEL 10.1 + group 2 - name: Units
test: ansible-test-integration-2.21-rhel-10.1-azp-2 test: '2.20/units/1'
- name: RHEL 10.1 + group 3 - stage: Ansible_2_19
test: ansible-test-integration-2.21-rhel-10.1-azp-3 displayName: Sanity & Units 2.19
- name: RHEL 10.1 + group 4 dependsOn: []
test: ansible-test-integration-2.21-rhel-10.1-azp-4 jobs:
- name: RHEL 10.1 + group 5 - template: templates/matrix.yml
test: ansible-test-integration-2.21-rhel-10.1-azp-5 parameters:
targets:
- name: Sanity
test: '2.19/sanity/1'
- name: Units
test: '2.19/units/1'
- stage: Ansible_2_18
displayName: Sanity & Units 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
targets:
- name: Sanity
test: '2.18/sanity/1'
- name: Units
test: '2.18/units/1'
- stage: remote_devel ### Docker
- stage: Docker_devel
displayName: Docker devel
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: devel/linux/{0}
targets:
- name: Fedora 44
test: fedora44
- name: Ubuntu 26.04
test: ubuntu2604
- name: Ubuntu 24.04
test: ubuntu2404
- name: Alpine 3.23
test: alpine323
groups:
- 4
- 5
- stage: Docker_2_21
displayName: Docker 2.21
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.21/linux/{0}
targets:
- name: Fedora 43
test: fedora43
- name: Ubuntu 22.04
test: ubuntu2204
- name: Ubuntu 24.04
test: ubuntu2404
# - name: Alpine 3.23
# test: alpine323
groups:
- 4
- 5
- stage: Docker_2_20
displayName: Docker 2.20
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.20/linux/{0}
targets:
- name: Fedora 42
test: fedora42
- name: Alpine 3.22
test: alpine322
groups:
- 4
- 5
- stage: Docker_2_19
displayName: Docker 2.19
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.19/linux/{0}
targets:
- name: Fedora 41
test: fedora41
- name: Alpine 3.21
test: alpine321
groups:
- 4
- 5
- stage: Docker_2_18
displayName: Docker 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/linux/{0}
targets:
- name: Fedora 40
test: fedora40
- name: Ubuntu 22.04
test: ubuntu2204
- name: Alpine 3.20
test: alpine320
groups:
- 4
- 5
### Community Docker
- stage: Docker_community_devel
displayName: Docker (community images) devel
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: devel/linux-community/{0}
targets:
- name: Debian 11 Bullseye
test: debian-bullseye/3.9
- name: Debian 12 Bookworm
test: debian-bookworm/3.11
- name: Debian 13 Trixie
test: debian-13-trixie/3.13
- name: ArchLinux
test: archlinux/3.14
groups:
- 4
- 5
### Remote
- stage: Remote_devel
displayName: Remote devel displayName: Remote devel
dependsOn: [] dependsOn: []
variables:
entryPoint: tests/utils/shippable/nox.sh
jobs: jobs:
- template: templates/matrix.yml - template: templates/matrix.yml
parameters: parameters:
testFormat: devel/{0}
targets: targets:
- name: RHEL 10.1 + group 1 + sdk-dev-latest - name: RHEL 10.1 with Docker SDK, urllib3, requests from sources
test: ansible-test-integration-devel-rhel-10.1-azp-1 test: rhel/10.1-dev-latest
- name: RHEL 10.1 + group 2 + sdk-dev-latest - name: RHEL 9.7
test: ansible-test-integration-devel-rhel-10.1-azp-2 test: rhel/9.7
- name: RHEL 10.1 + group 3 + sdk-dev-latest # For some reason, Ubuntu 24.04 is *extremely* slower than RHEL 9.6
test: ansible-test-integration-devel-rhel-10.1-azp-3 # - name: Ubuntu 24.04
- name: RHEL 10.1 + group 4 + sdk-dev-latest # test: ubuntu/24.04
test: ansible-test-integration-devel-rhel-10.1-azp-4 groups:
- name: RHEL 10.1 + group 5 + sdk-dev-latest - 1
test: ansible-test-integration-devel-rhel-10.1-azp-5 - 2
- name: RHEL 9.7 + group 1 - 3
test: ansible-test-integration-devel-rhel-9.7-azp-1 - 4
- name: RHEL 9.7 + group 2 - 5
test: ansible-test-integration-devel-rhel-9.7-azp-2 - stage: Remote_2_21
- name: RHEL 9.7 + group 3 displayName: Remote 2.21
test: ansible-test-integration-devel-rhel-9.7-azp-3 dependsOn: []
- name: RHEL 9.7 + group 4 jobs:
test: ansible-test-integration-devel-rhel-9.7-azp-4 - template: templates/matrix.yml
- name: RHEL 9.7 + group 5 parameters:
test: ansible-test-integration-devel-rhel-9.7-azp-5 testFormat: 2.21/{0}
targets:
- name: RHEL 10.1
test: rhel/10.1
# - name: RHEL 9.7
# test: rhel/9.7
groups:
- 1
- 2
- 3
- 4
- 5
- stage: Remote_2_20
displayName: Remote 2.20
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.20/{0}
targets:
- name: RHEL 9.7
test: rhel/9.7
groups:
- 1
- 2
- 3
- 4
- 5
- stage: Remote_2_19
displayName: Remote 2.19
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.19/{0}
targets:
- name: Ubuntu 22.04
test: ubuntu/22.04
groups:
- 1
- 2
- 3
- 4
- 5
- stage: Remote_2_18
displayName: Remote 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/{0}
targets:
- name: RHEL 9.7
test: rhel/9.7
groups:
- 1
- 2
- 3
- 4
- 5
## Finally
- stage: Summary - stage: Summary
condition: succeededOrFailed() condition: succeededOrFailed()
dependsOn: dependsOn:
- remote_2_19 - Ansible_devel
- remote_2_20 - Ansible_2_21
- remote_2_21 - Ansible_2_20
- remote_devel - Ansible_2_19
- Ansible_2_18
- Remote_devel
- Remote_2_21
- Remote_2_20
- Remote_2_19
- Remote_2_18
- Docker_devel
- Docker_2_21
- Docker_2_20
- Docker_2_19
- Docker_2_18
- Docker_community_devel
jobs: jobs:
- template: templates/coverage.yml - template: templates/coverage.yml

View File

@ -1,10 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Aggregate code coverage results for later processing.
# Copyright (c) Ansible Project # Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# Aggregate code coverage results for later processing.
set -o pipefail -eu set -o pipefail -eu
agent_temp_directory="$1" agent_temp_directory="$1"
@ -13,6 +13,10 @@ PATH="${PWD}/bin:${PATH}"
mkdir "${agent_temp_directory}/coverage/" mkdir "${agent_temp_directory}/coverage/"
if [[ "$(ansible --version)" =~ \ 2\.9\. ]]; then
exit
fi
options=(--venv --venv-system-site-packages --color -v) options=(--venv --venv-system-site-packages --color -v)
ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}" ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}"

View File

@ -11,7 +11,8 @@ Keep in mind that Azure Pipelines does not enforce unique job display names (onl
It is up to pipeline authors to avoid name collisions when deviating from the recommended format. It is up to pipeline authors to avoid name collisions when deviating from the recommended format.
""" """
from __future__ import annotations from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os import os
import re import re
@ -23,12 +24,12 @@ def main():
"""Main program entry point.""" """Main program entry point."""
source_directory = sys.argv[1] source_directory = sys.argv[1]
if "/ansible_collections/" in os.getcwd(): if '/ansible_collections/' in os.getcwd():
output_path = "tests/output" output_path = "tests/output"
else: else:
output_path = "test/results" output_path = "test/results"
destination_directory = os.path.join(output_path, "coverage") destination_directory = os.path.join(output_path, 'coverage')
if not os.path.exists(destination_directory): if not os.path.exists(destination_directory):
os.makedirs(destination_directory) os.makedirs(destination_directory)
@ -37,27 +38,27 @@ def main():
count = 0 count = 0
for name in os.listdir(source_directory): for name in os.listdir(source_directory):
match = re.search("^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$", name) match = re.search('^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$', name)
label = match.group("label") label = match.group('label')
attempt = int(match.group("attempt")) attempt = int(match.group('attempt'))
jobs[label] = max(attempt, jobs.get(label, 0)) jobs[label] = max(attempt, jobs.get(label, 0))
for label, attempt in jobs.items(): for label, attempt in jobs.items():
name = f"Coverage {attempt} {label}" name = 'Coverage {attempt} {label}'.format(label=label, attempt=attempt)
source = os.path.join(source_directory, name) source = os.path.join(source_directory, name)
source_files = os.listdir(source) source_files = os.listdir(source)
for source_file in source_files: for source_file in source_files:
source_path = os.path.join(source, source_file) source_path = os.path.join(source, source_file)
destination_path = os.path.join(destination_directory, source_file + "." + label) destination_path = os.path.join(destination_directory, source_file + '.' + label)
print(f'"{source_path}" -> "{destination_path}"') print('"%s" -> "%s"' % (source_path, destination_path))
shutil.copyfile(source_path, destination_path) shutil.copyfile(source_path, destination_path)
count += 1 count += 1
print(f"Coverage file count: {count}") print('Coverage file count: %d' % count)
print(f"##vso[task.setVariable variable=coverageFileCount]{count}") print('##vso[task.setVariable variable=coverageFileCount]%d' % count)
print(f"##vso[task.setVariable variable=outputPath]{output_path}") print('##vso[task.setVariable variable=outputPath]%s' % output_path)
if __name__ == "__main__": if __name__ == '__main__':
main() main()

View File

@ -1,10 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Check the test results and set variables for use in later steps.
# Copyright (c) Ansible Project # Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# Check the test results and set variables for use in later steps.
set -o pipefail -eu set -o pipefail -eu
if [[ "$PWD" =~ /ansible_collections/ ]]; then if [[ "$PWD" =~ /ansible_collections/ ]]; then

View File

@ -15,6 +15,7 @@ import pathlib
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
import typing as t
import urllib.request import urllib.request
@ -22,7 +23,7 @@ import urllib.request
class CoverageFile: class CoverageFile:
name: str name: str
path: pathlib.Path path: pathlib.Path
flags: list[str] flags: t.List[str]
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@ -33,8 +34,8 @@ class Args:
def parse_args() -> Args: def parse_args() -> Args:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("-n", "--dry-run", action="store_true") parser.add_argument('-n', '--dry-run', action='store_true')
parser.add_argument("path", type=pathlib.Path) parser.add_argument('path', type=pathlib.Path)
args = parser.parse_args() args = parser.parse_args()
@ -45,36 +46,32 @@ def parse_args() -> Args:
return Args(**kwargs) return Args(**kwargs)
def process_files(directory: pathlib.Path) -> 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'):
name = file.stem.replace("coverage=", "") name = file.stem.replace('coverage=', '')
# Get flags from name # Get flags from name
flags = name.replace("-powershell", "").split("=") # Drop '-powershell' suffix flags = name.replace('-powershell', '').split('=') # Drop '-powershell' suffix
flags = [ flags = [flag if not flag.startswith('stub') else flag.split('-')[0] for flag in flags] # Remove "-01" from stub files
flag if not flag.startswith("stub") else flag.split("-")[0] for flag in flags
] # Remove "-01" from stub files
processed.append(CoverageFile(name, file, flags)) processed.append(CoverageFile(name, file, flags))
return tuple(processed) return tuple(processed)
def upload_files(codecov_bin: pathlib.Path, files: tuple[CoverageFile, ...], dry_run: bool = False) -> None: def upload_files(codecov_bin: 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", '--name', file.name,
file.name, '--file', str(file.path),
"--file",
str(file.path),
] ]
for flag in file.flags: for flag in file.flags:
cmd.extend(["--flags", flag]) cmd.extend(['--flags', flag])
if dry_run: if dry_run:
print(f"DRY-RUN: Would run command: {cmd}") print(f'DRY-RUN: Would run command: {cmd}')
continue continue
subprocess.run(cmd, check=True) subprocess.run(cmd, check=True)
@ -82,11 +79,11 @@ def upload_files(codecov_bin: pathlib.Path, files: tuple[CoverageFile, ...], dry
def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None: def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None:
if dry_run: if dry_run:
print(f"DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}") print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
return return
with urllib.request.urlopen(url) as resp: with urllib.request.urlopen(url) as resp:
with dest.open("w+b") as f: with dest.open('w+b') as f:
# Read data in chunks rather than all at once # Read data in chunks rather than all at once
shutil.copyfileobj(resp, f, 64 * 1024) shutil.copyfileobj(resp, f, 64 * 1024)
@ -95,14 +92,14 @@ def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = Fals
def main(): def main():
args = parse_args() args = parse_args()
url = "https://ansible-ci-files.s3.amazonaws.com/codecov/linux/codecov" url = 'https://ansible-ci-files.s3.amazonaws.com/codecov/linux/codecov'
with tempfile.TemporaryDirectory(prefix="codecov-") as tmpdir: with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
codecov_bin = pathlib.Path(tmpdir) / "codecov" codecov_bin = pathlib.Path(tmpdir) / 'codecov'
download_file(url, codecov_bin, 0o755, args.dry_run) download_file(url, codecov_bin, 0o755, args.dry_run)
files = process_files(args.path) files = process_files(args.path)
upload_files(codecov_bin, files, args.dry_run) upload_files(codecov_bin, files, args.dry_run)
if __name__ == "__main__": if __name__ == '__main__':
main() main()

View File

@ -1,14 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Generate code coverage reports for uploading to Azure Pipelines and codecov.io.
# Copyright (c) Ansible Project # Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# Generate code coverage reports for uploading to Azure Pipelines and codecov.io.
set -o pipefail -eu set -o pipefail -eu
PATH="${PWD}/bin:${PATH}" PATH="${PWD}/bin:${PATH}"
if [[ "$(ansible --version)" =~ \ 2\.9\. ]]; then
exit
fi
if ! ansible-test --help >/dev/null 2>&1; then if ! ansible-test --help >/dev/null 2>&1; then
# Install the devel version of ansible-test for generating code coverage reports. # Install the devel version of ansible-test for generating code coverage reports.
# This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs). # This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs).

View File

@ -1,22 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Configure the test environment and run the tests.
# Copyright (c) Ansible Project # Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# Configure the test environment and run the tests.
set -o pipefail -eu set -o pipefail -eu
entry_point="$1" entry_point="$1"
test="$2" test="$2"
read -r -a coverage_branches <<< "$3" # space separated list of branches to run code coverage on for scheduled builds read -r -a coverage_branches <<< "$3" # space separated list of branches to run code coverage on for scheduled builds
agent_temp_directory="$4"
export COMMIT_MESSAGE export COMMIT_MESSAGE
export COMPLETE export COMPLETE
export COVERAGE export COVERAGE
export IS_PULL_REQUEST export IS_PULL_REQUEST
export COVERAGE_DESTINATION_DIRECTORY="${agent_temp_directory}/coverage"
if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then
IS_PULL_REQUEST=true IS_PULL_REQUEST=true

View File

@ -5,7 +5,8 @@
"""Prepends a relative timestamp to each input line from stdin and writes it to stdout.""" """Prepends a relative timestamp to each input line from stdin and writes it to stdout."""
from __future__ import annotations from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import sys import sys
import time import time
@ -15,14 +16,14 @@ def main():
"""Main program entry point.""" """Main program entry point."""
start = time.time() start = time.time()
sys.stdin.reconfigure(errors="surrogateescape") sys.stdin.reconfigure(errors='surrogateescape')
sys.stdout.reconfigure(errors="surrogateescape") sys.stdout.reconfigure(errors='surrogateescape')
for line in sys.stdin: for line in sys.stdin:
seconds = int(time.time() - start) seconds = time.time() - start
sys.stdout.write(f"{seconds // 60:02}:{seconds % 60:02} {line}") sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line))
sys.stdout.flush() sys.stdout.flush()
if __name__ == "__main__": if __name__ == '__main__':
main() main()

View File

@ -23,16 +23,14 @@ jobs:
- checkout: self - checkout: self
fetchDepth: $(fetchDepth) fetchDepth: $(fetchDepth)
path: $(checkoutPath) path: $(checkoutPath)
- bash: .azure-pipelines/scripts/run-tests.sh "$(prepareEntryPoint)" "${{ job.test }}" "$(coverageBranches)" "$(Agent.TempDirectory)" - bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
displayName: Prepare Tests
- bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)" "$(Agent.TempDirectory)"
displayName: Run Tests displayName: Run Tests
- bash: .azure-pipelines/scripts/process-results.sh - bash: .azure-pipelines/scripts/process-results.sh
condition: succeededOrFailed() condition: succeededOrFailed()
displayName: Process Results displayName: Process Results
# - bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)" - bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
# condition: eq(variables.haveCoverageData, 'true') condition: eq(variables.haveCoverageData, 'true')
# displayName: Aggregate Coverage Data displayName: Aggregate Coverage Data
- task: PublishTestResults@2 - task: PublishTestResults@2
condition: eq(variables.haveTestResults, 'true') condition: eq(variables.haveTestResults, 'true')
inputs: inputs:

View File

@ -45,7 +45,7 @@ jobs:
steps: steps:
- name: Check out repository - name: Check out repository
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
persist-credentials: false persist-credentials: false

View File

@ -15,11 +15,6 @@ name: nox
- cron: '0 9 * * *' - cron: '0 9 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
# Make sure there is at most one active run per PR, but do not cancel any non-PR runs
group: ${{ github.workflow }}-${{ (github.head_ref && github.event.number) || github.run_id }}
cancel-in-progress: true
jobs: jobs:
nox: nox:
uses: ansible-community/antsibull-nox/.github/workflows/reusable-nox-run.yml@main uses: ansible-community/antsibull-nox/.github/workflows/reusable-nox-run.yml@main
@ -35,6 +30,29 @@ jobs:
upload-codecov-pr: false upload-codecov-pr: false
upload-codecov-push: false upload-codecov-push: false
upload-codecov-schedule: true upload-codecov-schedule: true
allow-coverage-cd-override: true max-ansible-core: "2.17"
# For some reason GitHub decided to bump Docker on the ubuntu-24.04 image,
# which is not compatible with podman and various other things.
pre-test-cmd-integration: |-
# See https://docs.docker.com/engine/install/ubuntu/.
# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt-get install -y --allow-downgrades docker-ce-cli=5:28.0.4-1~ubuntu.24.04~noble docker-ce=5:28.0.4-1~ubuntu.24.04~noble
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@ -9,10 +9,6 @@
"community.internal_test_tools" = "git+https://github.com/ansible-collections/community.internal_test_tools.git,main" "community.internal_test_tools" = "git+https://github.com/ansible-collections/community.internal_test_tools.git,main"
"community.library_inventory_filtering_v1" = "git+https://github.com/ansible-collections/community.library_inventory_filtering.git,stable-1" "community.library_inventory_filtering_v1" = "git+https://github.com/ansible-collections/community.library_inventory_filtering.git,stable-1"
[collection_sources_per_ansible.'2.17']
# community.general's main branch needs ansible-core >= 2.18
"community.general" = "git+https://github.com/ansible-collections/community.general.git,stable-12"
[vcs] [vcs]
vcs = "git" vcs = "git"
development_branch = "main" development_branch = "main"
@ -103,28 +99,13 @@ include_devel = true
[sessions.ansible_test_integration] [sessions.ansible_test_integration]
session_name_template = "ansible-test-integration-{ansible_core}{dash_docker_short}{dash_remote}{dash_python_version}{dash_target_dashized}" session_name_template = "ansible-test-integration-{ansible_core}{dash_docker_short}{dash_remote}{dash_python_version}{dash_target_dashized}"
display_name_template = "Ⓐ{ansible_core}{plus_docker_nice}{plus_remote_nice}{plus_py_python_version}{plus_target_nice}{plus_force_docker_sdk_for_python_dev}{plus_force_docker_sdk_for_python_pypi}" display_name_template = "Ⓐ{ansible_core}{plus_docker_short}{plus_remote}{plus_py_python_version}{plus_target}{plus_force_docker_sdk_for_python_dev}{plus_force_docker_sdk_for_python_pypi}"
description_template = "Run integration tests with ansible-core {ansible_core}{comma_docker_nice}{comma_remote_nice}{comma_py_python_version}{comma_target_nice}{comma_force_docker_sdk_for_python_dev}{comma_force_docker_sdk_for_python_pypi}" description_template = "Run integration tests with ansible-core {ansible_core}{comma_docker_short}{comma_remote}{comma_py_python_version}{comma_target}{comma_force_docker_sdk_for_python_dev}{comma_force_docker_sdk_for_python_pypi}"
retry_on_error = "in-ci"
[sessions.ansible_test_integration.ansible_vars] [sessions.ansible_test_integration.ansible_vars]
force_docker_sdk_for_python_dev = { type = "value", value = false, template_value = "" } force_docker_sdk_for_python_dev = { type = "value", value = false, template_value = "" }
force_docker_sdk_for_python_pypi = { type = "value", value = false, template_value = "" } force_docker_sdk_for_python_pypi = { type = "value", value = false, template_value = "" }
[sessions.ansible_test_integration.nice_target_names]
"azp/1/" = "group 1"
"azp/2/" = "group 2"
"azp/3/" = "group 3"
"azp/4/" = "group 4"
"azp/5/" = "group 5"
[sessions.ansible_test_integration.nice_docker_names]
"quay.io/ansible-community/test-image:debian-bullseye" = "Debian 11"
"quay.io/ansible-community/test-image:debian-bookworm" = "Debian 12"
"quay.io/ansible-community/test-image:debian-13-trixie" = "Debian 13"
"quay.io/ansible-community/test-image:archlinux" = "Arch Linux"
"quay.io/ansible-community/test-image:opensuse-tumbleweed" = "OpenSuSE Tumbleweed"
################################################################################################## ##################################################################################################
# Ansible-core 2.17: # Ansible-core 2.17:
@ -236,12 +217,6 @@ target = [ "azp/4/", "azp/5/" ]
python_version = "3.13" python_version = "3.13"
docker = "quay.io/ansible-community/test-image:debian-13-trixie" docker = "quay.io/ansible-community/test-image:debian-13-trixie"
[[sessions.ansible_test_integration.groups.sessions]]
ansible_core = "devel"
target = [ "azp/4/", "azp/5/" ]
python_version = "3.13"
docker = "quay.io/ansible-community/test-image:opensuse-tumbleweed"
[[sessions.ansible_test_integration.groups.sessions]] [[sessions.ansible_test_integration.groups.sessions]]
ansible_core = "devel" ansible_core = "devel"
target = [ "azp/4/", "azp/5/" ] target = [ "azp/4/", "azp/5/" ]
@ -268,14 +243,14 @@ remote = [
[sessions.ansible_lint] [sessions.ansible_lint]
[[sessions.ee_check.execution_environments]] [[sessions.ee_check.execution_environments]]
name = "devel-ubi-10" name = "devel-ubi-9"
description = "ansible-core devel @ RHEL UBI 10" description = "ansible-core devel @ RHEL UBI 9"
test_playbooks = ["tests/ee/all.yml"] test_playbooks = ["tests/ee/all.yml"]
config.images.base_image.name = "docker.io/redhat/ubi10:latest" config.images.base_image.name = "docker.io/redhat/ubi9:latest"
config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/devel.tar.gz" config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/devel.tar.gz"
config.dependencies.ansible_runner.package_pip = "ansible-runner" config.dependencies.ansible_runner.package_pip = "ansible-runner"
config.dependencies.python_interpreter.package_system = "python3.14 python3.14-pip python3.14-cryptography" config.dependencies.python_interpreter.package_system = "python3.12 python3.12-pip python3.12-wheel python3.12-cryptography"
config.dependencies.python_interpreter.python_path = "/usr/bin/python3.14" config.dependencies.python_interpreter.python_path = "/usr/bin/python3.12"
runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"} runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"}
runtime_container_options = [ runtime_container_options = [
# Mount Docker socket into the container so we can talk to Docker outside the container # Mount Docker socket into the container so we can talk to Docker outside the container

View File

@ -7,7 +7,7 @@
namespace: community namespace: community
name: docker name: docker
version: 5.3.0 version: 5.2.1
readme: README.md readme: README.md
authors: authors:
- Ansible Docker Working Group - Ansible Docker Working Group

View File

@ -12,7 +12,6 @@ import nox
try: try:
import antsibull_nox import antsibull_nox
from antsibull_nox.cli import run as run_antsibull_nox
except ImportError: except ImportError:
print("You need to install antsibull-nox in the same Python environment as nox.") print("You need to install antsibull-nox in the same Python environment as nox.")
sys.exit(1) sys.exit(1)
@ -21,24 +20,6 @@ except ImportError:
antsibull_nox.load_antsibull_nox_toml() antsibull_nox.load_antsibull_nox_toml()
@nox.session(name="update-azp-config", python=False)
def update_azp_config(session: nox.Session) -> None:
command = [
"antsibull-nox",
"update-azp-config",
"--exclude-tags",
"docker",
"--min-ansible-core",
"2.19",
]
if antsibull_nox.IN_CI:
command.append("--fail-on-change")
session.debug(" ".join(command))
result = run_antsibull_nox(command)
if result != 0:
session.error(f"Execution failed with status code {result}")
# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar. # Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar.
# Requires nox >= 2025.02.09 # Requires nox >= 2025.02.09
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -7,3 +7,4 @@
ansible.builtin.file: ansible.builtin.file:
path: "{{ remote_tmp_dir }}" path: "{{ remote_tmp_dir }}"
state: absent state: absent
no_log: true

View File

@ -20,7 +20,7 @@ from ansible_collections.community.docker.plugins.inventory.docker_containers im
) )
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from collections.abc import Callable # pragma: no cover from collections.abc import Callable
@pytest.fixture(scope="module", name="templar") @pytest.fixture(scope="module", name="templar")

View File

@ -43,7 +43,7 @@ from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.c
from .. import fake_api from .. import fake_api
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from ansible_collections.community.docker.plugins.module_utils._api.auth import ( # pragma: no cover from ansible_collections.community.docker.plugins.module_utils._api.auth import (
AuthConfig, AuthConfig,
) )

View File

@ -18,7 +18,7 @@ from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.c
from . import fake_stat from . import fake_stat
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from collections.abc import Callable # pragma: no cover from collections.abc import Callable
CURRENT_VERSION = f"v{DEFAULT_DOCKER_API_VERSION}" CURRENT_VERSION = f"v{DEFAULT_DOCKER_API_VERSION}"

View File

@ -13,9 +13,9 @@ from ansible_collections.community.docker.plugins.module_utils._copy import (
) )
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from collections.abc import Sequence # pragma: no cover from collections.abc import Sequence
T = t.TypeVar("T") # pragma: no cover T = t.TypeVar("T")
def _simple_generator(sequence: Sequence[T]) -> t.Generator[T]: def _simple_generator(sequence: Sequence[T]) -> t.Generator[T]:

View File

@ -61,10 +61,12 @@ def test_archived_image_manifest_extracts_nothing_when_file_not_present(
def test_archived_image_manifest_raises_when_file_not_a_tar() -> None: def test_archived_image_manifest_raises_when_file_not_a_tar() -> None:
with pytest.raises(ImageArchiveInvalidException, match=__file__) as exc_info: try:
archived_image_manifest(__file__) archived_image_manifest(__file__)
assert isinstance(exc_info.value.__cause__, tarfile.ReadError) raise AssertionError()
assert str(__file__) in str(exc_info.value) except ImageArchiveInvalidException as e:
assert isinstance(e.__cause__, tarfile.ReadError)
assert str(__file__) in str(e)
def test_archived_image_manifest_raises_when_tar_missing_manifest( def test_archived_image_manifest_raises_when_tar_missing_manifest(
@ -72,10 +74,12 @@ def test_archived_image_manifest_raises_when_tar_missing_manifest(
) -> None: ) -> None:
write_irrelevant_tar(tar_file_name) write_irrelevant_tar(tar_file_name)
with pytest.raises(ImageArchiveInvalidException) as exc_info: try:
archived_image_manifest(tar_file_name) archived_image_manifest(tar_file_name)
assert isinstance(exc_info.value.__cause__, KeyError) raise AssertionError()
assert "manifest.json" in str(exc_info.value.__cause__) except ImageArchiveInvalidException as e:
assert isinstance(e.__cause__, KeyError)
assert "manifest.json" in str(e.__cause__)
def test_archived_image_manifest_raises_when_manifest_missing_id( def test_archived_image_manifest_raises_when_manifest_missing_id(
@ -85,7 +89,9 @@ def test_archived_image_manifest_raises_when_manifest_missing_id(
write_imitation_archive_with_manifest(tar_file_name, manifest) write_imitation_archive_with_manifest(tar_file_name, manifest)
with pytest.raises(ImageArchiveInvalidException) as exc_info: try:
archived_image_manifest(tar_file_name) archived_image_manifest(tar_file_name)
assert isinstance(exc_info.value.__cause__, KeyError) raise AssertionError()
assert "Config" in str(exc_info.value.__cause__) except ImageArchiveInvalidException as e:
assert isinstance(e.__cause__, KeyError)
assert "Config" in str(e.__cause__)

View File

@ -17,12 +17,12 @@ from ansible_collections.community.docker.plugins.module_utils._util import (
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
class DAMSpec(t.TypedDict): # pragma: no cover class DAMSpec(t.TypedDict):
av: dict[str, t.Any] av: dict[str, t.Any]
bv: dict[str, t.Any] bv: dict[str, t.Any]
result: bool result: bool
class Spec(t.TypedDict): # pragma: no cover class Spec(t.TypedDict):
a: t.Any a: t.Any
b: t.Any b: t.Any
method: t.Literal["strict", "ignore", "allow_more_present"] method: t.Literal["strict", "ignore", "allow_more_present"]

View File

@ -21,13 +21,11 @@ from ..test_support.docker_image_archive_stubbing import (
) )
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from collections.abc import Callable # pragma: no cover from collections.abc import Callable
def assert_no_logging(msg: str) -> t.NoReturn: def assert_no_logging(msg: str) -> t.NoReturn:
raise AssertionError( raise AssertionError(f"Should not have logged anything but logged {msg}")
f"Should not have logged anything but logged {msg}"
) # pragma: no cover
def capture_logging(messages: list[str]) -> Callable[[str], None]: def capture_logging(messages: list[str]) -> Callable[[str], None]:

View File

@ -12,8 +12,7 @@ from ansible_collections.community.docker.plugins.modules import (
docker_swarm_service, docker_swarm_service,
) )
docker_errors = pytest.importorskip("docker.errors") APIError = pytest.importorskip("docker.errors.APIError")
APIError = docker_errors.APIError
def test_retry_on_out_of_sequence_error(mocker: t.Any) -> None: def test_retry_on_out_of_sequence_error(mocker: t.Any) -> None:

View File

@ -0,0 +1 @@
remote.sh

View File

@ -0,0 +1 @@
remote.sh

View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
declare -a args
IFS='/:' read -ra args <<< "$1"
image="${args[1]}"
python="${args[2]}"
if [ "${#args[@]}" -gt 3 ]; then
target="azp/${args[3]}/"
else
target="azp/"
fi
if [[ "${python}" =~ -pypi-latest$ ]]; then
python="${python/-pypi-latest}"
echo 'force_docker_sdk_for_python_pypi: true' >> tests/integration/integration_config.yml
fi
if [[ "${python}" =~ -dev-latest$ ]]; then
python="${python/-dev-latest}"
echo 'force_docker_sdk_for_python_dev: true' >> tests/integration/integration_config.yml
fi
# shellcheck disable=SC2086
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
--docker "quay.io/ansible-community/test-image:${image}" --python "${python}"

30
tests/utils/shippable/linux.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
declare -a args
IFS='/:' read -ra args <<< "$1"
image="${args[1]}"
if [ "${#args[@]}" -gt 2 ]; then
target="azp/${args[2]}/"
else
target="azp/"
fi
if [[ "${image}" =~ -pypi-latest$ ]]; then
image="${image/-pypi-latest}"
echo 'force_docker_sdk_for_python_pypi: true' >> tests/integration/integration_config.yml
fi
if [[ "${image}" =~ -dev-latest$ ]]; then
image="${image/-dev-latest}"
echo 'force_docker_sdk_for_python_dev: true' >> tests/integration/integration_config.yml
fi
# shellcheck disable=SC2086
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
--docker "${image}"

View File

@ -1,53 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
nox_session="$1"
docker images ansible/ansible
docker images quay.io/ansible/*
docker ps
for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do
docker rm -f "${container}" || true # ignore errors
done
docker ps
command -v python
python -V
function retry
{
# shellcheck disable=SC2034
for repetition in 1 2 3; do
set +e
"$@"
result=$?
set -e
if [ ${result} == 0 ]; then
return ${result}
fi
echo "@* -> ${result}"
done
echo "Command '@*' failed 3 times!"
exit 255
}
command -v pip
pip --version
pip list --disable-pip-version-check
retry pip install https://github.com/ansible-community/antsibull-nox/archive/main.tar.gz --disable-pip-version-check
export PYTHONIOENCODING='utf-8'
export FORCE_COLOR=1
export ANTSIBULL_NOX_IGNORE_INSTALLED_COLLECTIONS="true"
if [ "${nox_session}" == "extra-sanity-tests" ]; then
nox --verbose --install-only
else
nox --verbose --install-only -e "${nox_session}"
fi

View File

@ -1,64 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
nox_session="$1"
export PYTHONIOENCODING='utf-8'
if [ -n "${COVERAGE:-}" ]; then
# on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value
export COVERAGE="--coverage"
elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then
# on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message
export COVERAGE="--coverage"
else
# on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled)
export COVERAGE="--coverage-check"
fi
if [ -n "${COMPLETE:-}" ]; then
# disable change detection triggered by setting the COMPLETE environment variable to a non-empty value
export ANTSIBULL_CHANGE_DETECTION=""
elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then
# disable change detection triggered by having 'ci_complete' in the latest commit message
export ANTSIBULL_CHANGE_DETECTION=""
elif [ "${IS_PULL_REQUEST:-}" == "true" ]; then
# enable change detection for PRs (default behavior)
export ANTSIBULL_CHANGE_DETECTION="true"
export ANTSIBULL_BASE_BRANCH="${SYSTEM_PULLREQUEST_TARGETBRANCH}"
# Create a branch for the current HEAD, which happens to be a merge commit
git checkout -b "pull-request-branch"
# Name the target branch
git branch "${SYSTEM_PULLREQUEST_TARGETBRANCH}" --track "origin/${SYSTEM_PULLREQUEST_TARGETBRANCH}"
# Show branches
git branch -vv
else
# disable change detection for pushes and scheduled runs
export ANTSIBULL_CHANGE_DETECTION=""
fi
if [[ "${COVERAGE:-}" == "--coverage" ]]; then
export ANTSIBULL_NOX_TIMEOUT=60
else
export ANTSIBULL_NOX_TIMEOUT=50
fi
if [ "${IS_PULL_REQUEST:-}" == "true" ]; then
export ANTSIBULL_NOX_INTEGRATION_ALLOW_UNSTABLE_CHANGED="true"
fi
export FORCE_COLOR=1
export ANTSIBULL_NOX_IGNORE_INSTALLED_COLLECTIONS="true"
export ANTSIBULL_NOX_COVERAGE_DESTINATION="${COVERAGE_DESTINATION_DIRECTORY}"
export ANTSIBULL_NOX_COVERAGE_ANALYSIS_FILE="${COVERAGE_DESTINATION_DIRECTORY}/coverage-analyze-targets.json"
export ANTSIBULL_NOX_COVERAGE_NO_XML="true"
if [ "${nox_session}" == "extra-sanity-tests" ]; then
nox --reuse-existing-virtualenvs --no-install
else
nox --reuse-existing-virtualenvs --no-install -e "${nox_session}" -- ${COVERAGE}
fi

48
tests/utils/shippable/remote.sh Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
declare -a args
IFS='/:' read -ra args <<< "$1"
platform="${args[0]}"
version="${args[1]}"
pyver=default
# check for explicit python version like 8.3@3.8
declare -a splitversion
IFS='@' read -ra splitversion <<< "$version"
if [ "${#splitversion[@]}" -gt 1 ]; then
version="${splitversion[0]}"
pyver="${splitversion[1]}"
fi
if [ "${#args[@]}" -gt 2 ]; then
target="azp/${args[2]}/"
else
target="azp/"
fi
if [[ "${version}" =~ -pypi-latest$ ]]; then
version="${version/-pypi-latest}"
echo 'force_docker_sdk_for_python_pypi: true' >> tests/integration/integration_config.yml
fi
if [[ "${version}" =~ -dev-latest$ ]]; then
version="${version/-dev-latest}"
echo 'force_docker_sdk_for_python_dev: true' >> tests/integration/integration_config.yml
fi
stage="${S:-prod}"
provider="${P:-default}"
if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8\. ]]; then
echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt
fi
# shellcheck disable=SC2086
ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
--python "${pyver}" --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"

View File

@ -0,0 +1 @@
remote.sh

17
tests/utils/shippable/sanity.sh Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
if [ "${BASE_BRANCH:-}" ]; then
base_branch="origin/${BASE_BRANCH}"
else
base_branch=""
fi
# shellcheck disable=SC2086
ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
--docker --base-branch "${base_branch}" \
--allow-disabled

View File

@ -0,0 +1,234 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
declare -a args
IFS='/:' read -ra args <<< "$1"
ansible_version="${args[0]}"
script="${args[1]}"
after_script="${args[2]}"
function join {
local IFS="$1";
shift;
echo "$*";
}
# Ensure we can write other collections to this dir
sudo chown -R "$(whoami)" "${PWD}/../../../"
test="$(join / "${args[@]:1}")"
docker images ansible/ansible
docker images quay.io/ansible/*
docker ps
for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do
docker rm -f "${container}" || true # ignore errors
done
docker ps
if [ -d /home/shippable/cache/ ]; then
ls -la /home/shippable/cache/
fi
command -v python
python -V
function retry
{
# shellcheck disable=SC2034
for repetition in 1 2 3; do
set +e
"$@"
result=$?
set -e
if [ ${result} == 0 ]; then
return ${result}
fi
echo "@* -> ${result}"
done
echo "Command '@*' failed 3 times!"
exit 255
}
command -v pip
pip --version
pip list --disable-pip-version-check
if [ "${ansible_version}" == "devel" ]; then
retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
else
retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check
fi
export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../"
# START: HACK
COMMUNITY_CRYPTO_BRANCH=main
if [ "${ansible_version}" == "2.16" ]; then
COMMUNITY_CRYPTO_BRANCH=stable-2
fi
if [ "${script}" == "linux" ] && [ "$after_script" == "ubuntu2004" ]; then
COMMUNITY_CRYPTO_BRANCH=stable-2
fi
retry git clone --depth=1 --single-branch --branch stable-1 https://github.com/ansible-collections/community.library_inventory_filtering.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/library_inventory_filtering_v1"
# NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
# retry ansible-galaxy -vvv collection install community.library_inventory_filtering_v1
if [ "${test}" == "units/1" ]; then
# Nothing further should be added to this list.
# This is to prevent modules or plugins in this collection having a runtime dependency on other collections.
retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/internal_test_tools"
# NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
# retry ansible-galaxy -vvv collection install community.internal_test_tools
fi
if [ "${script}" != "sanity/1" ] && [ "${script}" != "units/1" ]; then
# To prevent Python dependencies on other collections only install other collections for integration tests
retry git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/ansible/posix"
retry git clone --depth=1 --single-branch --branch "${COMMUNITY_CRYPTO_BRANCH}" https://github.com/ansible-collections/community.crypto.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/crypto"
retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.general.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general"
# NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
# retry ansible-galaxy -vvv collection install ansible.posix
# retry ansible-galaxy -vvv collection install community.crypto
# retry ansible-galaxy -vvv collection install community.general
fi
# END: HACK
export PYTHONIOENCODING='utf-8'
if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then
COVERAGE=yes
COMPLETE=yes
fi
if [ -n "${COVERAGE:-}" ]; then
# on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value
export COVERAGE="--coverage"
elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then
# on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message
export COVERAGE="--coverage"
else
# on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled)
export COVERAGE="--coverage-check"
fi
if [ -n "${COMPLETE:-}" ]; then
# disable change detection triggered by setting the COMPLETE environment variable to a non-empty value
export CHANGED=""
elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then
# disable change detection triggered by having 'ci_complete' in the latest commit message
export CHANGED=""
else
# enable change detection (default behavior)
export CHANGED="--changed"
fi
if [ "${IS_PULL_REQUEST:-}" == "true" ]; then
# run unstable tests which are targeted by focused changes on PRs
export UNSTABLE="--allow-unstable-changed"
else
# do not run unstable tests outside PRs
export UNSTABLE=""
fi
# remove empty core/extras module directories from PRs created prior to the repo-merge
find plugins -type d -empty -print -delete
function cleanup
{
# for complete on-demand coverage generate a report for all files with no coverage on the "sanity/5" job so we only have one copy
if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/5" ]; then
stub="--stub"
# trigger coverage reporting for stubs even if no other coverage data exists
mkdir -p tests/output/coverage/
else
stub=""
fi
if [ -d tests/output/coverage/ ]; then
if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then
process_coverage='yes' # process existing coverage files
elif [ "${stub}" ]; then
process_coverage='yes' # process coverage when stubs are enabled
else
process_coverage=''
fi
if [ "${process_coverage}" ]; then
# use python 3.7 for coverage to avoid running out of memory during coverage xml processing
# only use it for coverage to avoid the additional overhead of setting up a virtual environment for a potential no-op job
virtualenv --python /usr/bin/python3.7 ~/ansible-venv
set +ux
. ~/ansible-venv/bin/activate
set -ux
# shellcheck disable=SC2086
ansible-test coverage xml --color -v --requirements --group-by command --group-by version ${stub:+"$stub"}
cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/"
if [ "${ansible_version}" != "2.9" ]; then
# analyze and capture code coverage aggregated by integration test target
ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json"
fi
# upload coverage report to codecov.io only when using complete on-demand coverage
if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then
for file in tests/output/reports/coverage=*.xml; do
flags="${file##*/coverage=}"
flags="${flags%-powershell.xml}"
flags="${flags%.xml}"
# remove numbered component from stub files when converting to tags
flags="${flags//stub-[0-9]*/stub}"
flags="${flags//=/,}"
flags="${flags//[^a-zA-Z0-9_,]/_}"
bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \
-f "${file}" \
-F "${flags}" \
-n "${test}" \
-t 8450ed26-4e94-4d07-8831-d2023d6d20a3 \
-X coveragepy \
-X gcov \
-X fix \
-X search \
-X xcode \
|| echo "Failed to upload code coverage report to codecov.io: ${file}"
done
fi
fi
fi
if [ -d tests/output/junit/ ]; then
cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/"
fi
if [ -d tests/output/data/ ]; then
cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/"
fi
if [ -d tests/output/bot/ ]; then
cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/"
fi
}
if [ "${SHIPPABLE_BUILD_ID:-}" ]; then trap cleanup EXIT; fi
if [[ "${COVERAGE:-}" == "--coverage" ]]; then
timeout=60
else
timeout=50
fi
ansible-test env --dump --show --timeout "${timeout}" --color -v
if [ "${SHIPPABLE_BUILD_ID:-}" ]; then "tests/utils/shippable/check_matrix.py"; fi
"tests/utils/shippable/${script}.sh" "${test}"

View File

@ -0,0 +1 @@
remote.sh

29
tests/utils/shippable/units.sh Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -o pipefail -eux
declare -a args
IFS='/:' read -ra args <<< "$1"
group="${args[1]}"
if [[ "${COVERAGE:-}" == "--coverage" ]]; then
timeout=90
else
timeout=30
fi
group1=()
case "${group}" in
1) options=("${group1[@]:+${group1[@]}}") ;;
esac
ansible-test env --timeout "${timeout}" --color -v
# shellcheck disable=SC2086
ansible-test units --color -v --docker default ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
"${options[@]:+${options[@]}}" \