feat(ci): add multi-platform CI testing via Docker
Add CI infrastructure that tests the Ansible role on every distro declared in meta/main.yml (EL 8/9, Debian bullseye/bookworm, Ubuntu focal/jammy/noble) using Docker containers. - ci/build_matrix.py: parse meta/main.yml platforms into JSON matrix - ci/test_playbook.yml: test playbook (state=present + validate) - ci/Dockerfile.el, ci/Dockerfile.debian: per-family Docker images - Makefile: orchestrator (make test, make test-<slug>, make lint) - .gitea/workflows/ci.yml: Gitea Actions with dynamic matrix - .gitlab-ci.yml: GitLab CI pipeline - Jenkinsfile: Jenkins pipeline with parallel stages - .yamllint.yml: linter configuration - .dockerignore: exclude .git and CI configs from Docker context
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
.git
|
||||||
|
.gitea
|
||||||
|
.gitlab-ci.yml
|
||||||
|
Jenkinsfile
|
||||||
|
docs/
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["*"]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install linters
|
||||||
|
run: pip install ansible-lint yamllint
|
||||||
|
- name: Lint
|
||||||
|
run: make lint
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.build.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install PyYAML
|
||||||
|
run: pip install pyyaml
|
||||||
|
- name: Build matrix
|
||||||
|
id: build
|
||||||
|
run: echo "matrix=$(python3 ci/build_matrix.py)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
test:
|
||||||
|
needs: [lint, matrix]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install PyYAML
|
||||||
|
run: pip install pyyaml
|
||||||
|
- name: Test ${{ matrix.slug }}
|
||||||
|
run: make test-${{ matrix.slug }}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
stages:
|
||||||
|
- lint
|
||||||
|
- test
|
||||||
|
|
||||||
|
lint:
|
||||||
|
stage: lint
|
||||||
|
image: python:3.11-slim
|
||||||
|
before_script:
|
||||||
|
- pip install ansible-lint yamllint pyyaml
|
||||||
|
script:
|
||||||
|
- make lint
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
image: docker:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
variables:
|
||||||
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
before_script:
|
||||||
|
- apk add --no-cache python3 py3-pip py3-yaml make bash
|
||||||
|
script:
|
||||||
|
- make test
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
extends: default
|
||||||
|
|
||||||
|
rules:
|
||||||
|
line-length:
|
||||||
|
max: 120
|
||||||
|
truthy:
|
||||||
|
check-keys: false
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Lint') {
|
||||||
|
steps {
|
||||||
|
sh 'pip install ansible-lint yamllint pyyaml'
|
||||||
|
sh 'make lint'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Test Matrix') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
def matrixJson = sh(script: 'python3 ci/build_matrix.py', returnStdout: true).trim()
|
||||||
|
def matrix = readJSON(text: matrixJson)
|
||||||
|
def parallelStages = [:]
|
||||||
|
matrix.each { entry ->
|
||||||
|
def slug = entry.slug
|
||||||
|
parallelStages[slug] = {
|
||||||
|
sh "make test-${slug}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parallel parallelStages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
sh 'make clean || true'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
.PHONY: help matrix lint test clean
|
||||||
|
|
||||||
|
SHELL := /bin/bash
|
||||||
|
IMAGE_PREFIX := remote-users-fact-test
|
||||||
|
|
||||||
|
help: ## Show this help
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*##' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*##"}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
matrix: ## Print the test matrix as JSON
|
||||||
|
@python3 ci/build_matrix.py
|
||||||
|
|
||||||
|
lint: ## Run yamllint and ansible-lint
|
||||||
|
yamllint roles/
|
||||||
|
ansible-lint
|
||||||
|
|
||||||
|
test: ## Run tests on all distros from the matrix
|
||||||
|
@python3 ci/build_matrix.py | python3 -c "\
|
||||||
|
import sys, json; \
|
||||||
|
matrix = json.load(sys.stdin); \
|
||||||
|
[print(e['slug'] + ' ' + e['image'] + ' ' + e['platform']) for e in matrix]" | \
|
||||||
|
while read slug image platform; do \
|
||||||
|
$(MAKE) _test-one SLUG=$$slug IMAGE=$$image PLATFORM=$$platform; \
|
||||||
|
done
|
||||||
|
|
||||||
|
test-%: ## Run test on a single distro (e.g. make test-el9)
|
||||||
|
@python3 ci/build_matrix.py | python3 -c "\
|
||||||
|
import sys, json; \
|
||||||
|
slug = '$(*)';\
|
||||||
|
matrix = json.load(sys.stdin); \
|
||||||
|
matches = [e for e in matrix if e['slug'] == slug]; \
|
||||||
|
assert matches, f'Unknown slug: {slug}. Valid: {[e[\"slug\"] for e in matrix]}'; \
|
||||||
|
e = matches[0]; print(e['slug'] + ' ' + e['image'] + ' ' + e['platform'])" | \
|
||||||
|
while read slug image platform; do \
|
||||||
|
$(MAKE) _test-one SLUG=$$slug IMAGE=$$image PLATFORM=$$platform; \
|
||||||
|
done
|
||||||
|
|
||||||
|
_test-one:
|
||||||
|
@echo "=== Testing $(SLUG) ($(IMAGE)) ==="
|
||||||
|
@dockerfile=ci/Dockerfile.el; \
|
||||||
|
if [ "$(PLATFORM)" = "Debian" ] || [ "$(PLATFORM)" = "Ubuntu" ]; then \
|
||||||
|
dockerfile=ci/Dockerfile.debian; \
|
||||||
|
fi; \
|
||||||
|
docker build \
|
||||||
|
--build-arg "IMAGE=$(IMAGE)" \
|
||||||
|
-t "$(IMAGE_PREFIX)-$(SLUG)" \
|
||||||
|
-f "$$dockerfile" . \
|
||||||
|
&& docker run --rm "$(IMAGE_PREFIX)-$(SLUG)"
|
||||||
|
@echo "=== $(SLUG) OK ==="
|
||||||
|
|
||||||
|
clean: ## Remove test Docker images
|
||||||
|
@python3 ci/build_matrix.py | python3 -c "\
|
||||||
|
import sys, json; \
|
||||||
|
[print('$(IMAGE_PREFIX)-' + e['slug']) for e in json.load(sys.stdin)]" | \
|
||||||
|
while read img; do \
|
||||||
|
docker rmi $$img 2>/dev/null || true; \
|
||||||
|
done
|
||||||
|
@echo "Clean done."
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ARG IMAGE
|
||||||
|
FROM ${IMAGE}
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ansible iproute2 procps bash sudo \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
COPY . /workspace
|
||||||
|
|
||||||
|
CMD ["ansible-playbook", "-i", "localhost,", "ci/test_playbook.yml"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
ARG IMAGE
|
||||||
|
FROM ${IMAGE}
|
||||||
|
|
||||||
|
RUN dnf install -y epel-release \
|
||||||
|
&& dnf install -y ansible-core iproute procps-ng bash sudo \
|
||||||
|
&& dnf clean all
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
COPY . /workspace
|
||||||
|
|
||||||
|
CMD ["ansible-playbook", "-i", "localhost,", "ci/test_playbook.yml"]
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Parse meta/main.yml platforms and output a Docker test matrix as JSON."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
print("ERROR: PyYAML is required. Install with: pip install pyyaml", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Mapping Galaxy platform name -> Docker image prefix
|
||||||
|
PLATFORM_IMAGE_MAP = {
|
||||||
|
"EL": "rockylinux",
|
||||||
|
"Debian": "debian",
|
||||||
|
"Ubuntu": "ubuntu",
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_matrix(meta_path: str = "roles/remote_users_fact/meta/main.yml") -> list[dict]:
|
||||||
|
meta_file = Path(meta_path)
|
||||||
|
if not meta_file.exists():
|
||||||
|
print(f"ERROR: {meta_file} not found", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(meta_file) as f:
|
||||||
|
meta = yaml.safe_load(f)
|
||||||
|
|
||||||
|
platforms = meta.get("galaxy_info", {}).get("platforms", [])
|
||||||
|
if not platforms:
|
||||||
|
print("ERROR: No platforms found in meta/main.yml", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
matrix = []
|
||||||
|
for platform in platforms:
|
||||||
|
name = platform["name"]
|
||||||
|
if name not in PLATFORM_IMAGE_MAP:
|
||||||
|
print(f"ERROR: Unknown platform '{name}'. Known: {list(PLATFORM_IMAGE_MAP.keys())}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
image_prefix = PLATFORM_IMAGE_MAP[name]
|
||||||
|
for version in platform.get("versions", []):
|
||||||
|
version_str = str(version)
|
||||||
|
slug = f"{name.lower()}{version_str}" if name == "EL" else f"{name.lower()}-{version_str}"
|
||||||
|
matrix.append({
|
||||||
|
"slug": slug,
|
||||||
|
"image": f"{image_prefix}:{version_str}",
|
||||||
|
"platform": name,
|
||||||
|
"version": version_str,
|
||||||
|
})
|
||||||
|
|
||||||
|
return matrix
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(json.dumps(build_matrix(), indent=2))
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
# ci/test_playbook.yml — Playbook de test CI
|
||||||
|
# Execute le role avec state=present et validation complete
|
||||||
|
|
||||||
|
- name: Test remote_users_fact
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
remote_users_fact_state: present
|
||||||
|
remote_users_fact_validate: true
|
||||||
|
remote_users_fact_display_summary: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: remote_users_fact
|
||||||
Reference in New Issue
Block a user