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