Files
algo/.github/workflows/smart-tests.yml
T
984831bcab fix: add explicit bool filters for Ansible 12 jinja2_native compatibility (#14963)
* fix: add explicit bool filters for Ansible 12 jinja2_native compatibility

Ansible 12 enables jinja2_native by default, which means string values
like "true"/"false" are no longer automatically coerced to booleans in
when: conditions and Jinja2 if statements. Add | bool filters to all
boolean variable references in tasks, templates, and handlers.

Also reformats long single-line Jinja2 conditionals into multi-line for
readability, fixes GCE default() calls for native mode, adds help
command to the algo script, and updates test fixtures to register the
bool filter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: add j2lint for Jinja2 template linting

Add j2lint (aristanetworks/j2lint) to catch syntax errors, spacing
issues, and operator formatting in Jinja2 templates. Integrated into
pre-commit hooks, lint.yml CI, and smart-tests.yml.

Rules S3/S5/S6/S7/V1 are ignored — they enforce conventions
incompatible with Ansible's config-file-embedded templates.

Also fixes int+1 → int + 1 operator spacing in server.conf.j2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve all ansible-lint warnings and enforce zero-tolerance policy

Fix 18 jinja[spacing] errors across 12 files by moving Jinja2 block
delimiters to prevent YAML >- folding from introducing trailing spaces.

Fix 27 key-order[task] warnings across 17 files by reordering task keys
to canonical order (name → when → tags → environment → become → block).

Promote key-order[task] and yaml[line-length] from warn_list to hard
errors by removing warn_list entirely from .ansible-lint.

Add zero-tolerance warning policy to CLAUDE.md explaining why warnings
are unacceptable in a security tool and documenting resolution order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 11:21:56 -05:00

308 lines
9.8 KiB
YAML

---
name: Smart Test Selection
'on':
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: read
jobs:
changed-files:
name: Detect Changed Files
runs-on: ubuntu-latest
outputs:
# Define what tests to run based on changes
run_syntax_check: ${{ steps.filter.outputs.ansible }}
run_basic_tests: ${{ steps.filter.outputs.python }}
run_docker_tests: ${{ steps.filter.outputs.docker }}
run_config_tests: ${{ steps.filter.outputs.configs }}
run_template_tests: ${{ steps.filter.outputs.templates }}
run_lint: ${{ steps.filter.outputs.lint }}
run_integration: ${{ steps.filter.outputs.integration }}
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
ansible:
- '**/*.yml'
- '**/*.yaml'
- 'main.yml'
- 'playbooks/**'
- 'roles/**'
- 'library/**'
python:
- '**/*.py'
- 'pyproject.toml'
- 'uv.lock'
- 'tests/**'
docker:
- 'Dockerfile*'
- '.dockerignore'
- 'docker-compose*.yml'
configs:
- 'config.cfg*'
- 'roles/**/templates/**'
- 'roles/**/defaults/**'
templates:
- '**/*.j2'
- 'roles/**/templates/**'
lint:
- '**/*.py'
- '**/*.yml'
- '**/*.yaml'
- '**/*.sh'
- '**/*.j2'
- '.ansible-lint'
- '.yamllint'
- 'pyproject.toml'
integration:
- 'main.yml'
- 'roles/**'
- 'library/**'
- 'playbooks/**'
syntax-check:
name: Ansible Syntax Check
needs: changed-files
if: needs.changed-files.outputs.run_syntax_check == 'true'
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.11'
- name: Setup uv environment
uses: ./.github/actions/setup-uv
- name: Check Ansible playbook syntax
run: uv run ansible-playbook main.yml --syntax-check
basic-tests:
name: Basic Sanity Tests
needs: changed-files
if: needs.changed-files.outputs.run_basic_tests == 'true' || needs.changed-files.outputs.run_template_tests == 'true'
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.11'
- name: Setup uv environment
uses: ./.github/actions/setup-uv
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Run relevant tests
env:
RUN_BASIC_TESTS: ${{ needs.changed-files.outputs.run_basic_tests }}
RUN_TEMPLATE_TESTS: ${{ needs.changed-files.outputs.run_template_tests }}
run: |
# Always run basic sanity
uv run pytest tests/unit/test_basic_sanity.py -v
# Run other tests based on what changed
if [[ "${RUN_BASIC_TESTS}" == "true" ]]; then
uv run pytest \
tests/unit/test_config_validation.py \
tests/unit/test_user_management.py \
tests/unit/test_openssl_compatibility.py \
tests/unit/test_cloud_provider_configs.py \
tests/unit/test_generated_configs.py \
-v
fi
if [[ "${RUN_TEMPLATE_TESTS}" == "true" ]]; then
uv run pytest tests/unit/test_template_rendering.py -v
fi
docker-tests:
name: Docker Build Test
needs: changed-files
if: needs.changed-files.outputs.run_docker_tests == 'true'
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.11'
- name: Setup uv environment
uses: ./.github/actions/setup-uv
- name: Build Docker image
run: docker build -t local/algo:test .
- name: Test Docker image starts
run: |
docker run --rm local/algo:test /algo/algo --help
- name: Run Docker deployment tests
run: uv run pytest tests/unit/test_docker_localhost_deployment.py -v
config-tests:
name: Configuration Tests
needs: changed-files
if: needs.changed-files.outputs.run_config_tests == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.11'
- name: Setup uv environment
uses: ./.github/actions/setup-uv
- name: Test configuration generation
run: |
chmod +x tests/test-local-config.sh
./tests/test-local-config.sh
- name: Run ansible dry-run tests
run: |
# Quick dry-run for local provider only
cat > test-local.cfg << 'EOF'
users:
- testuser
cloud_providers:
local:
server: test-server
wireguard_enabled: true
ipsec_enabled: false
dns_adblocking: false
ssh_tunneling: false
algo_provider: local
algo_server_name: test-algo-vpn
server: test-server
endpoint: 10.0.0.1
EOF
uv run ansible-playbook main.yml \
-i "localhost," \
-c local \
-e @test-local.cfg \
-e "provider=local" \
--check \
--diff \
-vv \
--skip-tags "facts,tests,local,update-alternatives,cloud_api" || true
lint:
name: Linting
needs: changed-files
if: needs.changed-files.outputs.run_lint == 'true'
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.11'
- name: Setup uv environment
uses: ./.github/actions/setup-uv
- name: Install ansible dependencies
run: uv run ansible-galaxy collection install community.crypto
- name: Run relevant linters
env:
RUN_LINT: ${{ needs.changed-files.outputs.run_lint }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.sha }}
run: |
# Run linters if lint-related files changed
if [[ "${RUN_LINT}" == "true" ]]; then
echo "Running linters..."
# Run Python linter
uv run --with ruff ruff check .
# Run YAML linter
uv run --with yamllint yamllint -c .yamllint .
# Run Ansible linter
uv run --with ansible-lint ansible-lint
# Check Jinja2 templates
if git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" | grep -q '\.j2$'; then
uv run --with j2lint j2lint roles/ --ignore S3 S5 S6 S7 V1
fi
# Check shell scripts if any changed
if git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" | grep -q '\.sh$'; then
find . -name "*.sh" -type f -not -path "./.git/*" -exec shellcheck {} +
fi
fi
all-tests-required:
name: All Required Tests
needs: [syntax-check, basic-tests, docker-tests, config-tests, lint]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check test results
env:
SYNTAX_CHECK_RESULT: ${{ needs.syntax-check.result }}
BASIC_TESTS_RESULT: ${{ needs.basic-tests.result }}
DOCKER_TESTS_RESULT: ${{ needs.docker-tests.result }}
CONFIG_TESTS_RESULT: ${{ needs.config-tests.result }}
LINT_RESULT: ${{ needs.lint.result }}
run: |
# This job ensures all required tests pass
# It will fail if any dependent job failed
if [[ "${SYNTAX_CHECK_RESULT}" == "failure" ]] || \
[[ "${BASIC_TESTS_RESULT}" == "failure" ]] || \
[[ "${DOCKER_TESTS_RESULT}" == "failure" ]] || \
[[ "${CONFIG_TESTS_RESULT}" == "failure" ]] || \
[[ "${LINT_RESULT}" == "failure" ]]; then
echo "One or more required tests failed"
exit 1
fi
echo "All required tests passed!"
trigger-integration:
name: Trigger Integration Tests
needs: changed-files
if: |
needs.changed-files.outputs.run_integration == 'true' &&
github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- name: Trigger integration tests
run: |
echo "Integration tests should be triggered for this PR"
echo "Changed files indicate potential breaking changes"
echo "Run workflow manually: .github/workflows/integration-tests.yml"