Adding in Ansible (still a WIP).

This commit is contained in:
2025-01-16 16:12:35 -05:00
parent 91af95090e
commit a28e806b8d
4271 changed files with 606629 additions and 0 deletions
@@ -0,0 +1,78 @@
---
name: Upload Ansible coverage reports with flags
description: Does separate codecov uploads with flags taken from ansible-test's --group-by options
inputs:
additional-flags:
description: Additional custom flags (comma separated) to be added to all reports.
required: false
directory-flag-pattern:
description: |
A pattern to infer flags from directory names. For example this pattern:
{ansible-%}=python-{py%}={%}
applied to a directory name like:
stable-2.11=python-3.9=rando
Results in the flags:
ansible-stable-2.11,py3.9,rando
required: false
file-flag-pattern:
description: |
A pattern to infer flags from coverage reports. For example this pattern:
coverage={%}={target_%}={env_%}=python-{py%}.xml
applied to a file name like:
coverage=integration=lookup_some_plugin=docker-default=python-3.9.xml
Results in the flags:
integration,target_lookup_some_plugin,env_docker-default,py3.9
required: false
default: coverage={%}={target_%}={env_%}.xml
directory:
description: The directory to scan recursively. Defaults to current working directory.
required: false
codecov-uploader-version:
description: |
The version of the codecov uploader to use. 'latest' (default) always gets the latest.
See https://uploader.codecov.io/linux for the versions available.
required: false
default: latest
fail-on-error:
description: |
If 'true' then codecov will be called with '-Z', which will fail the build on an error.
Any value other than 'true' will be treated as false.
required: false
default: 'true'
runs:
using: composite
steps:
- name: Download and verify codecov uploader
shell: bash
run: |
echo "::group::Installing codecov uploader"
mkdir -p /tmp/ccbin
cd /tmp/ccbin
echo "/tmp/ccbin" >> ${GITHUB_PATH}
if command -v codecov ; then
./codecov --version
else
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --import # One-time step
curl -Os https://uploader.codecov.io/${{ inputs.codecov-uploader-version }}/linux/codecov
curl -Os https://uploader.codecov.io/${{ inputs.codecov-uploader-version }}/linux/codecov.SHA256SUM
curl -Os https://uploader.codecov.io/${{ inputs.codecov-uploader-version }}/linux/codecov.SHA256SUM.sig
gpg --verify codecov.SHA256SUM.sig codecov.SHA256SUM
shasum -a 256 -c codecov.SHA256SUM
chmod +x codecov
./codecov --version
fi
echo "::endgroup::"
- shell: bash
run: >-
python -u "${{ github.action_path }}/process.py"
--directory "${{ inputs.directory }}"
--directory-flag-pattern "${{ inputs.file-flag-pattern }}"
--file-flag-pattern "${{ inputs.file-flag-pattern }}"
--additional-flags "${{ inputs.additional-flags }}"
${{ inputs.fail-on-error == 'true' && '--fail-on-error' || '' }}
@@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Brian Scholer (@briantist)
# 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
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import sys
import subprocess
import re
import getopt
from pathlib import Path
def get_flags(pattern, input):
patpat = r'\{([^\}]+)\}'
pats = re.findall(patpat, pattern)
matcher = re.sub(patpat, r'(.*?)', pattern)
match = re.search(matcher, input)
if match:
return [pats[i].replace('%', result) for i, result in enumerate(match.groups())]
return None
def main(argv):
additional_flags = file_flag_pattern = directory_flag_pattern = directory = fail_on_error = None
opts, args = getopt.getopt(argv, '', [
'directory=',
'directory-flag-pattern=',
'file-flag-pattern=',
'additional-flags=',
'fail-on-error',
])
for opt, arg in opts:
if opt == '--directory':
directory = arg
elif opt == '--directory-flag-pattern':
directory_flag_pattern = arg
elif opt == '--file-flag-pattern':
file_flag_pattern = arg
elif opt == '--additional-flags':
additional_flags = arg
elif opt == '--fail-on-error':
fail_on_error = True
extra_flags = additional_flags.split(',') if additional_flags else []
flags = {}
directory = Path(directory) if directory else Path.cwd()
for f in directory.rglob('*'):
if f.is_file():
iflags = set()
if directory_flag_pattern:
for part in f.parent.parts:
dflags = get_flags(directory_flag_pattern, part)
if dflags:
iflags.update(dflags)
fflags = get_flags(file_flag_pattern, str(f.name))
if fflags:
iflags.update(fflags)
for flag in iflags:
flags.setdefault(flag, []).append(str(f.resolve()))
logextra = ' (+%r)' % extra_flags if extra_flags else ''
for flag, files in flags.items():
cmd = ['codecov', '-F', flag]
[cmd.extend(['-F', extra]) for extra in extra_flags]
[cmd.extend(['-f', file]) for file in files]
if fail_on_error:
cmd.append('-Z')
print('::group::Flag: %s%s' % (flag, logextra))
print('Executing: %r' % cmd)
subprocess.run(cmd, stderr=subprocess.STDOUT, check=True)
print('::endgroup::')
if __name__ == '__main__':
main(sys.argv[1:])
@@ -0,0 +1,50 @@
---
name: Ansible Collection via GitHub
description: Install Ansible collections direct from GitHub repositories without using ansible-galaxy.
branding:
icon: git-branch
color: yellow
inputs:
collection:
description: The name of the collection in namespace.collection_name form.
required: true
ref:
description: The git ref to install. Defaults to the latest release as listed in GitHub releases. Only supports branches and tags.
required: false
path:
description: The path to clone it to. Defaults to ansible_collections/namespace/collection_name.
required: false
runs:
using: composite
steps:
- shell: bash
run: |
COLLECTION="${{ inputs.collection }}"
P_PATH="${{ inputs.path }}"
P_REF="${{ inputs.ref }}"
NS="${COLLECTION%.*}"
CN="${COLLECTION#*.}"
# only collections in the ansible-collections organization are supported right now
URLBASE="https://github.com/ansible-collections/${COLLECTION}"
URLCLONE="${URLBASE}.git"
URLLATEST="${URLBASE}/releases/latest"
if [[ -n "${P_PATH}" ]]
then
OUTPATH="${P_PATH}"
else
OUTPATH="ansible_collections/${NS}/${CN}"
fi
if [[ -n "${P_REF}" ]]
then
REF="${P_REF}"
else
# credit to https://gist.github.com/lukechilds/a83e1d7127b78fef38c2914c4ececc3c#gistcomment-3294173
latest=$(curl -fs -o/dev/null -w %{redirect_url} "${URLLATEST}")
REF=$(basename ${latest})
fi
git clone --depth=1 --branch "${REF}" "${URLCLONE}" "${OUTPATH}"
@@ -0,0 +1,46 @@
---
name: Get a list of docker image versions
description: Gets a list of docker image versions (via tags), limited to a specified number of major, minor, and micro combinations.
outputs:
versions:
description: JSON encoded list of versions.
value: ${{ steps.versions.outputs.versions }}
inputs:
image:
description: The docker image name.
required: true
num_major_versions:
description: Number of unique major versions to return.
required: false
default: '1'
num_minor_versions:
description: Number of unique minor versions to return.
required: false
default: '1'
num_micro_versions:
description: Number of unique micro versions to return.
required: false
default: '1'
include_prerelease:
description: If 'true' then pre-release versions are included. Any value other than 'true' will be treated as false.
required: false
include_postrelease:
description: If 'true' then post-release versions are included. Any value other than 'true' will be treated as false.
required: false
runs:
using: composite
steps:
- name: Requirements
shell: bash
run: pip install -r "${{ github.action_path }}/requirements.txt"
- shell: bash
id: versions
run: >-
python -u "${{ github.action_path }}/versions.py"
--image "${{ inputs.image }}"
--num_major_versions "${{ inputs.num_major_versions }}"
--num_minor_versions "${{ inputs.num_minor_versions }}"
--num_micro_versions "${{ inputs.num_micro_versions }}"
${{ inputs.include_prerelease == 'true' && '--include_prerelease' || '' }}
${{ inputs.include_postrelease == 'true' && '--include_postrelease' || '' }}
@@ -0,0 +1,3 @@
requests
packaging
urllib3 >= 1.15
@@ -0,0 +1,126 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Brian Scholer (@briantist)
# 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
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import sys
import getopt
import json
import requests
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
from warnings import warn
from packaging import version
TAG_URI = 'https://registry.hub.docker.com/v2/repositories/%s/%s/tags?page_size=1024'
class WarningRetry(Retry):
def new(self, **kwargs):
if self.total > 0:
warn('Error on request. Retries remaining: %i' % (self.total,))
return super().new(**kwargs)
def main(argv):
image = None
include_prerelease = include_postrelease = False
num_major_versions = 1
num_minor_versions = 3
num_micro_versions = 1
opts, args = getopt.getopt(argv, '', [
'image=',
'num_major_versions=',
'num_minor_versions=',
'num_micro_versions=',
'include_prerelease',
'include_postrelease',
])
for opt, arg in opts:
if opt == '--image':
image = image_name = arg
elif opt == '--num_major_versions':
num_major_versions = int(arg)
elif opt == '--num_minor_versions':
num_minor_versions = int(arg)
elif opt == '--num_micro_versions':
num_micro_versions = int(arg)
elif opt == '--include_prerelease':
include_prerelease = True
elif opt == '--include_postrelease':
include_postrelease = True
if image is None:
raise ValueError('image must be supplied.')
if '/' in image:
org, image_name = image.split('/')
else:
org = 'library'
tag_url = TAG_URI % (org, image_name)
sess = requests.Session()
retry = WarningRetry(total=5, backoff_factor=0.2, respect_retry_after_header=False)
adapter = HTTPAdapter(max_retries=retry)
sess.mount('https://', adapter)
response = sess.get(tag_url)
versions = []
for tag in response.json()['results']:
vobj = None
try:
vobj = version.parse(tag['name'])
except Exception:
continue
else:
if not isinstance(vobj, version.Version):
continue
if vobj.is_prerelease is include_prerelease and vobj.is_postrelease is include_postrelease:
versions.append(vobj)
majors = set()
minors = set()
micros = set()
keep = []
for ver in sorted(versions, reverse=True):
if ver.major not in majors:
if len(majors) == num_major_versions:
break
majors.add(ver.major)
minors.clear()
micros.clear()
if ver.minor not in minors:
if len(minors) == num_minor_versions:
continue
minors.add(ver.minor)
micros.clear()
if ver.micro not in micros:
if len(micros) == num_micro_versions:
continue
micros.add(ver.micro)
keep.append(str(ver))
with open(os.environ.get('GITHUB_OUTPUT', '/dev/stdout'), 'a') as f:
f.write('versions=')
json.dump(keep, f)
if __name__ == '__main__':
main(sys.argv[1:])
@@ -0,0 +1,52 @@
---
name: Pull ansible-test docker images
description: Performs a docker pull against ansible-test docker image aliases
inputs:
working-directory:
description: The working directory to operate under. This should be the collection's directory.
required: false
ansible-test-invocation:
description: The options that will be passed to ansible-test.
required: true
runs:
using: composite
steps:
- shell: bash
run: pip install packaging
- shell: python
run: |
import os
import sys
from packaging import version
from ansible.release import __version__ as ansible_version
ver = version.parse(ansible_version)
cutoff = version.parse('2.12')
nwd = r'${{ inputs.working-directory }}'
if nwd:
os.chdir(nwd)
# not using ver >= cutoff because of pre-release/dev comparison logic
if ver.major > cutoff.major or (ver.major == cutoff.major and ver.minor >= cutoff.minor):
invo = r'${{ inputs.ansible-test-invocation }}'
sys.exit(os.system('ansible-test %s --prime-containers' % invo))
try:
from ansible_test._internal.util_common import get_docker_completion
except ImportError:
# 2.9
from ansible_test._internal.util import get_docker_completion
context = 'collection'
wanted = ['default']
dockers = get_docker_completion()
for alias, data in dockers.items():
if alias in wanted:
if 'context' not in data or data['context'] == context:
image = data['name']
print('pulling %s' % image)
os.system('docker pull %s' % image)