Skip to content

CI/CD Pipeline

CI/CD Pipeline #8

Workflow file for this run

---
name: CI/CD Pipeline
on:
push:
branches:
- main
- develop
paths-ignore:
- "**.md"
- "docs/**"
- "LICENSE"
pull_request:
branches:
- main
- develop
paths-ignore:
- "**.md"
- "docs/**"
- "LICENSE"
schedule:
# Run weekly security scans
- cron: "0 2 * * 1"
workflow_dispatch:
env:
ANSIBLE_FORCE_COLOR: "1"
ANSIBLE_VERBOSITY: "1"
PY_COLORS: "1"
PYTHON_VERSIONS: "3.8, 3.9, 3.10, 3.11"
ANSIBLE_VERSIONS: "core 2.12, 2.13, 2.14, 2.15"
UBUNTU_VERSIONS: "20.04, 22.04, 24.04"
jobs:
# Job to determine which files have changed to optimize CI execution
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
any: ${{ steps.changes.outputs.any_changed }}
roles: ${{ steps.changes.outputs.roles_any_changed }}
molecule: ${{ steps.changes.outputs.molecule_any_changed }}
docs: ${{ steps.changes.outputs.docs_any_changed }}
workflow: ${{ steps.changes.outputs.workflow_any_changed }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for Changes
id: changes
uses: dorny/paths-filter@v3
with:
filters: |
roles:
- 'roles/**'
molecule:
- 'molecule/**'
docs:
- '**/*.md'
- 'docs/**'
workflow:
- '.github/workflows/**'
- 'requirements.yml'
- 'ansible.cfg'
- '.ansible-lint'
# Linting job to validate code quality and syntax
lint:
name: Linting
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.any == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
python-version: [3.9]
ansible-core-version: ["2.15"]
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install "ansible-core==${{ matrix.ansible-core-version }}" ansible-lint yamllint
ansible-galaxy collection install -r requirements.yml
- name: Ansible Syntax Check
run: ansible-playbook site.yml --syntax-check
- name: YAML Lint
run: yamllint .
- name: Ansible Lint
run: ansible-lint .
- name: Role Linting
run: |
for role in roles/*/; do
echo "Linting $role"
ansible-lint "$role" || echo "Linting failed for $role"
done
# Security scanning job for vulnerabilities and secrets
security:
name: Security Scanning
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.any == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
python-version: [3.9]
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-security-${{ matrix.python-version }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-security-${{ matrix.python-version }}-
- name: Install Security Tools
run: |
python -m pip install --upgrade pip
pip install "ansible-core==2.15" bandit safety
- name: Secret Detection with GitLeaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dependency Vulnerability Scanning
run: |
# Check for known vulnerabilities in Python dependencies
safety check || echo "Safety check completed with findings"
- name: Ansible Security Analysis
run: |
# Check for common security issues in Ansible playbooks
ansible-playbook site.yml --check --diff -e "ansible_check_mode=true"
- name: Configuration Security Validation
run: |
# Validate that security configurations are properly set
echo "Checking for security best practices..."
# Add specific security checks here based on project requirements
# Molecule testing job for role-level testing
molecule:
name: Molecule Testing
runs-on: ubuntu-latest
needs: [changes, lint]
if: needs.changes.outputs.roles == 'true' || needs.changes.outputs.molecule == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
python-version: [3.8, 3.9, 3.10, 3.11]
ansible-core-version: ["2.12", "2.13", "2.14", "2.15"]
scenario: [default, docker, rust, common]
include:
# Include specific combinations for comprehensive testing
- python-version: 3.11
ansible-core-version: "2.15"
scenario: default
- python-version: 3.8
ansible-core-version: "2.12"
scenario: default
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-molecule-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-molecule-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install "ansible-core==${{ matrix.ansible-core-version }}" molecule[docker] pytest
ansible-galaxy collection install -r requirements.yml
- name: Cache Docker Images
uses: actions/cache@v4
with:
path: /tmp/docker-images
key: ${{ runner.os }}-docker-images-${{ matrix.scenario }}
restore-keys: |
${{ runner.os }}-docker-images-
- name: Run Molecule Tests
run: |
cd molecule/${{ matrix.scenario }}
molecule test
env:
PY_COLORS: "1"
ANSIBLE_FORCE_COLOR: "1"
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: molecule-test-results-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ matrix.scenario }}
path: |
reports/
logs/
coverage.xml
if-no-files-found: ignore
# Integration testing job for full playbook testing
integration:
name: Integration Testing
runs-on: ubuntu-latest
needs: [changes, lint]
if: needs.changes.outputs.any == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
python-version: [3.9, 3.10, 3.11]
ansible-core-version: ["2.14", "2.15"]
ubuntu-version: ["20.04", "22.04", "24.04"]
include:
# Include specific combinations for comprehensive testing
- python-version: 3.11
ansible-core-version: "2.15"
ubuntu-version: "22.04"
- python-version: 3.9
ansible-core-version: "2.13"
ubuntu-version: "20.04"
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-integration-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-integration-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install "ansible-core==${{ matrix.ansible-core-version }}"
ansible-galaxy collection install -r requirements.yml
- name: Create Test Inventory Directory
run: |
mkdir -p inventory/test
cat > inventory/test.ini << EOF
[test]
localhost ansible_connection=local
EOF
- name: Create Test Vault Directory
run: |
mkdir -p group_vars/all
cat > group_vars/all/test-vault.yml << EOF
# Test vault values for CI
tailscale_auth_key: "test-key"
atuin_user: "test-user"
atuin_pass: "test-pass"
atuin_key: "test-key"
EOF
- name: Run Integration Tests
run: |
ansible-playbook -i inventory/test.ini test-docker.yml --check --diff
ansible-playbook -i inventory/test.ini test-rust.yml --check --diff
ansible-playbook -i inventory/test.ini test-atuin.yml --check --diff
- name: Run Full Playbook in Check Mode
run: |
ansible-playbook -i inventory/test.ini site.yml --check --diff
- name: Upload Integration Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-test-results-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-ubuntu${{ matrix.ubuntu-version }}
path: |
reports/
logs/
coverage.xml
if-no-files-found: ignore
# Performance testing job for benchmarking critical operations
performance:
name: Performance Testing
runs-on: ubuntu-latest
needs: [changes, lint]
if: needs.changes.outputs.any == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
python-version: [3.9, 3.11]
ansible-core-version: ["2.15"]
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-performance-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-performance-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install "ansible-core==${{ matrix.ansible-core-version }}"
ansible-galaxy collection install -r requirements.yml
- name: Performance Benchmark
run: |
echo "Running performance tests..."
# Add performance testing commands here
# This could include timing ansible runs, measuring resource usage, etc.
- name: Upload Performance Results
uses: actions/upload-artifact@v4
if: always()
with:
name: performance-results-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}
path: |
performance-reports/
benchmarks/
if-no-files-found: ignore
# Deployment validation job for different environments
deployment:
name: Deployment Validation
runs-on: ubuntu-latest
needs: [changes, lint]
if: needs.changes.outputs.any == 'true' || needs.changes.outputs.workflow == 'true'
strategy:
matrix:
environment: [development, staging, production]
python-version: [3.9, 3.11]
ansible-core-version: ["2.14", "2.15"]
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache Python Dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-deployment-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ matrix.environment }}-${{ hashFiles('**/requirements.yml') }}
restore-keys: |
${{ runner.os }}-pip-deployment-${{ matrix.python-version }}-${{ matrix.ansible-core-version }}-${{ matrix.environment }}-
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install "ansible-core==${{ matrix.ansible-core-version }}"
ansible-galaxy collection install -r requirements.yml
- name: Validate Environment Configuration
run: |
echo "Validating configuration for ${{ matrix.environment }} environment"
# Add environment-specific validation here
- name: Run Environment-Specific Tests
run: |
echo "Running tests for ${{ matrix.environment }} environment"
# Add environment-specific tests here
# Job to collect and report test results
report:
name: Test Results & Reporting
runs-on: ubuntu-latest
needs: [lint, security, molecule, integration, performance, deployment]
if: always()
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Create Test Summary
run: |
echo "## CI/CD Pipeline Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Linting | ${{ needs.lint.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security | ${{ needs.security.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Molecule | ${{ needs.molecule.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Integration | ${{ needs.integration.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Performance | ${{ needs.performance.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Deployment | ${{ needs.deployment.result }} |" >> $GITHUB_STEP_SUMMARY
- name: Quality Gate Check
run: |
# Check if all required jobs passed
if [[ "${{ needs.lint.result }}" == "success" && "${{ needs.security.result }}" == "success" ]]; then
echo "Quality gates passed"
else
echo "Quality gates failed"
exit 1
fi
- name: Notify on Failure
if: failure()
run: |
echo "Pipeline failed! Notifying team..."
# Add notification logic here (Slack, email, etc.)
- name: Notify on Success
if: success()
run: |
echo "Pipeline succeeded! All tests passed."
# Add success notification logic here if needed
- name: Generate Coverage Report
run: |
echo "Generating coverage report..."
# Add coverage report generation logic here
- name: Comment on Pull Request
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const summary = `## CI/CD Pipeline Results\n\n| Job | Status |\n|-----|--------|\n| Linting | ${{ needs.lint.result }} |\n| Security | ${{ needs.security.result }} |\n| Molecule | ${{ needs.molecule.result }} |\n| Integration | ${{ needs.integration.result }} |\n| Performance | ${{ needs.performance.result }} |\n| Deployment | ${{ needs.deployment.result }} |`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: summary
});
- name: Create Issue on Failure
if: failure() && github.ref == 'refs/heads/main'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `CI/CD Pipeline Failure on ${context.ref}`,
body: `The CI/CD pipeline failed on ${context.ref}.\n\nCheck the workflow run: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
});
# Conditional execution based on changed files
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true