Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
81291cc
chore(ci): add CI workflow and Dependabot hardening
t0kubetsu Jun 9, 2026
707e99b
fix(ci): split workflow into ansible-lint + conditional python jobs
t0kubetsu Jun 9, 2026
443de07
fix(ci): fix python job condition and relax ansible-lint profile
t0kubetsu Jun 9, 2026
55e6c92
fix(ci): expand ansible-lint skip_list for bundles (syntax-check, nam…
t0kubetsu Jun 9, 2026
413fa3d
fix(ci): replace unskippable syntax-check with mock_roles for cross-r…
t0kubetsu Jun 9, 2026
18e15f9
fix(ci): add missing catalog roles to mock_roles + skip jinja[spacing…
t0kubetsu Jun 9, 2026
0582420
fix(ci): add software.install.wazuh-agent to mock_roles
t0kubetsu Jun 9, 2026
4d7d973
ci(workflow): fix push branch triggers — feat/** + fix/** replace fea…
t0kubetsu Jun 9, 2026
f9f5f51
chore(ci): bump actions/checkout to v6
t0kubetsu Jun 9, 2026
cde1440
chore(ci): upgrade ruff check to --select ALL
t0kubetsu Jun 9, 2026
90d37d8
chore(ci): scope ruff to r42playbooks/ — exclude tests from --select ALL
t0kubetsu Jun 9, 2026
8811eb2
fix(lint): normalize bundle YAML to pass ansible-lint basic profile
t0kubetsu Aug 11, 2026
3ac2d68
chore(ci): pin ansible-lint, default ruff rule set, document mock_roles
t0kubetsu Aug 11, 2026
b13bf4a
Merge remote-tracking branch 'origin/dev' into feat/ci-hardening
t0kubetsu Aug 11, 2026
beb2f38
fix(lint): normalize new admin/generic/ctf bundle YAML via ansible-li…
t0kubetsu Aug 11, 2026
eaf5271
chore(ci): stub new runtime vars and catalog roles for the lint gate
t0kubetsu Aug 11, 2026
8812e45
fix(ci): install ansible.posix collection before lint
t0kubetsu Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .ansible-lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
profile: basic

skip_list:
# SCREAMING_SNAKE is the documented convention for infrastructure-wide vars
- var-naming[pattern]
# noun.verb role naming is the documented convention
- role-name
# generated scenario playbooks do not require play-level names
- name[play]
# pre-existing task naming convention: lowercase names throughout bundles
- name[casing]
# generated bundles include tasks without explicit names
- name[missing]
# Jinja template syntax in task names is intentional (loop items, vars)
- name[template]
# pre-existing {{ item.ID}} spacing in generated bundles — cosmetic only
- jinja[spacing]

warn_list:
- yaml[line-length]
- yaml[empty-lines]
- yaml[trailing-spaces]
- risky-shell-pipe

# catalog and cross-repo roles not checked out in CI — stub so syntax-check passes.
# Hand-maintained: when a bundle starts using a new catalog role, add it here,
# and when a catalog role is renamed, update it here too — otherwise CI fails
# with an unresolved-role error.
mock_roles:
- range42-ansible_roles-proxmox_controller
- ansible.utils
- indexer
- software.configure.docker-compose
- software.configure.firewalls
- software.configure.tailscale_disable_nftables
- software.install.nodejs_app_systemd
- software.install.symlinks_farm
- software.install.tailscale
- software.install.warmup.basic_packages
- software.install.warmup.dot_files
- software.install.wazuh-dashboard
- software.install.wazuh-filebeat-oss
- software.install.wazuh-indexer
- software.install.wazuh-manager
- systems.configure.add_user
- software.install.wazuh-agent
- software.install.kunai_official_workshop
- systems.configure.authorized_keys
- systems.configure.os_auto_updates
- systems.configure.ssh_keypair
- systems.configure.sudo

# vars defined at runtime via inventory/group_vars — stub for CI syntax-check
extra_vars:
global_vm_ssh_name: ci_mock
target_group: ci_mock_group
TARGET_GROUP: ci_mock_group
wazuh_clients_group: ci_mock_group
GITCLONE_KUNAI_TARGET_GROUP: ci_mock_group
KUNAI_TARGET_GROUP: ci_mock_group
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
labels: [dependencies]
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
labels: [dependencies, ci]
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI
on:
push:
branches: [main, dev, 'feat/**', 'fix/**']
pull_request:
branches: [main, dev]
jobs:
ansible-lint:
name: Ansible Lint
runs-on: ubuntu-latest
container:
image: python:3.13-slim
steps:
- run: apt-get update && apt-get install -y --no-install-recommends git
- uses: actions/checkout@v6
- run: pip install --no-cache-dir ansible-lint==26.6.0
# bundles use ansible.posix (synchronize) — not shipped with ansible-core
- run: ansible-galaxy collection install ansible.posix
- run: ansible-lint bundles/
env:
# bundles/generic/repo.clone.kunai_workshop resolves its inner
# import_playbook path from this env var
RANGE42_BUNDLE_DIR: ${{ github.workspace }}/bundles

python:
name: Ruff + Pytest
runs-on: ubuntu-latest
container:
image: python:3.13-slim
steps:
- run: apt-get update && apt-get install -y --no-install-recommends git
- uses: actions/checkout@v6
- id: pycheck
run: test -f pyproject.toml && echo "exists=true" >> "$GITHUB_OUTPUT" || echo "exists=false" >> "$GITHUB_OUTPUT"
- if: "steps.pycheck.outputs.exists == 'true'"
run: pip install --no-cache-dir -e ".[dev]"
- if: "steps.pycheck.outputs.exists == 'true'"
run: ruff check r42playbooks/
- if: "steps.pycheck.outputs.exists == 'true'"
run: pytest tests/ --tb=short -q
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
**/secret
CLAUDE.md
**/CLAUDE.md
.claude
.claude

# ansible-lint runtime cache (mock roles)
.ansible/
6 changes: 6 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# CI gate: syntax errors (E9) and pyflakes (F) — undefined names, unused
# imports, broken f-strings. Same baseline as the range42 bootstrapper repo.
# Tighten (or move into pyproject.toml [tool.ruff]) when the r42playbooks
# package lands and owns its own lint policy.
[lint]
select = ["E9", "F"]
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ optional: true
install_flag: INSTALL_DEPLOYER_API_BACKEND

params:

- name: global_vm_ssh_name
target: true
type: string
Expand Down Expand Up @@ -61,7 +60,7 @@ params:
type: string
required: false
default_where: bundle-inline
default: '^https?://r42\.admin-deployer-ui(:\d+)?$'
default: "^https?://r42\\.admin-deployer-ui(:\\d+)?$"
description: Anchored CORS origin regex written to the backend .env so the deployer-ui browser Origin is accepted.

- name: INSTALL_TAILSCALE
Expand Down
9 changes: 5 additions & 4 deletions bundles/admin/software.install.deployer_api_backend/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# ============================================================================
# bundles/admin/software.install.deployer_api_backend/main.yml
# ============================================================================
Expand Down Expand Up @@ -164,15 +165,15 @@
tailscale_authkey: "{{ infrastructure_tailscale_authkey }}"
tasks:
- name: Install + configure tailscale on the backend VM (opt-in via INSTALL_TAILSCALE=YES)
when: INSTALL_TAILSCALE | default('NO') | upper == "YES"

# ============================================================================

block:
- include_role:
name: software.install.tailscale
- include_role:
name: software.configure.tailscale_disable_nftables
when: INSTALL_TAILSCALE | default('NO') | upper == "YES"

# ============================================================================

- name: install deployer-api-backend - workspace + playbooks + source sync + .env + compose up
hosts: "{{ global_vm_ssh_name }}"
become: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
bundle: admin/software.install.deployer_ui

description: Deploy range42-deployer-ui as a Docker container (multi-stage build then nginx SPA on UI_PORT) with firewall, optional tailscale, and a /health probe.
description: Deploy range42-deployer-ui as a Docker container (multi-stage build then nginx SPA on UI_PORT) with firewall, optional tailscale, and a /health
probe.

optional: true

install_flag: INSTALL_DEPLOYER_UI

params:

- name: global_vm_ssh_name
target: true
type: string
Expand Down Expand Up @@ -47,7 +47,8 @@ params:
type: string
required: false
default_where: none
description: Backend-api base URL rendered into public/config.json before the Docker build, so the SPA registers this deployment's backend on first launch. Unset means no config.json is rendered and the operator sets the backend by hand in the Settings modal.
description: Backend-api base URL rendered into public/config.json before the Docker build, so the SPA registers this deployment's backend on first launch.
Unset means no config.json is rendered and the operator sets the backend by hand in the Settings modal.

- name: PROXMOX_NODE_NAME
type: string
Expand Down
9 changes: 5 additions & 4 deletions bundles/admin/software.install.deployer_ui/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# ============================================================================
# bundles/admin/software.install.deployer_ui/main.yml
# ============================================================================
Expand Down Expand Up @@ -121,15 +122,15 @@
tailscale_authkey: "{{ infrastructure_tailscale_authkey }}"
tasks:
- name: Install + configure tailscale on the UI VM (opt-in via INSTALL_TAILSCALE=YES)
when: INSTALL_TAILSCALE | default('NO') | upper == "YES"

# ============================================================================

block:
- include_role:
name: software.install.tailscale
- include_role:
name: software.configure.tailscale_disable_nftables
when: INSTALL_TAILSCALE | default('NO') | upper == "YES"

# ============================================================================

- name: install deployer-ui - sync source + .env + docker compose up
hosts: "{{ global_vm_ssh_name }}"
become: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ optional: true
install_flag: INSTALL_GITEA

params:

- name: global_vm_ssh_name
target: true
type: string
Expand Down
35 changes: 17 additions & 18 deletions bundles/admin/software.install.gitea/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# ============================================================================
# bundles/admin/software.install.gitea/main.yml
# ============================================================================
Expand Down Expand Up @@ -34,15 +35,13 @@
# global_vm_ci_ip: "192.168.142.183"
#
# ============================================================================


- name: gitea - ensure catalog .env exists (cp .env.example -> .env if missing)
hosts: proxmox
gather_facts: false
tasks:
- name: cp .env.example -> .env if .env absent (force=false respects an existing customized .env)
ansible.builtin.copy:
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea/.env.example"
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea/.env.example"
dest: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea/.env"
force: false
remote_src: true
Expand All @@ -56,10 +55,10 @@
roles:
- software.install.warmup.basic_packages
vars:
INSTALL_PACKAGES_BASICS: "YES"
INSTALL_PACKAGES_DOCKER: "YES"
INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"
INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES"
INSTALL_PACKAGES_BASICS: "YES"
INSTALL_PACKAGES_DOCKER: "YES"
INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"
INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES"

# #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####

Expand All @@ -71,7 +70,7 @@
- software.configure.firewalls
vars:
firewall_rules:
- { ip: "all", port: 22, protocol: "tcp" }
- { ip: "all", port: 22, protocol: "tcp" }
- { ip: "all", port: 3000, protocol: "tcp" }
- { ip: "all", port: 2222, protocol: "tcp" }

Expand Down Expand Up @@ -99,19 +98,19 @@

- name: push .env on the VM (cp .env.example -> .env if .env absent on the VM)
ansible.builtin.copy:
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea/.env.example"
dest: "/home/alice/gitea/.env"
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea/.env.example"
dest: "/home/alice/gitea/.env"
owner: "alice"
group: "alice"
mode: "0600"
mode: "0600"
force: false
roles:
- software.configure.docker-compose
vars:
LABEL_PROJECT_TYPE: "admin"
LABEL_PROJET_NAME: "gitea"
LOCAL__PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea"
REMOTE_PROJECT_DIR: "/home/alice/gitea"
OPERATOR_USER: "alice"
CLEAN_UP_DEPLOY_DIR: "NO"
SEND_POC_DIR: "NO"
LABEL_PROJECT_TYPE: "admin"
LABEL_PROJET_NAME: "gitea"
LOCAL__PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/gitea"
REMOTE_PROJECT_DIR: "/home/alice/gitea"
OPERATOR_USER: "alice"
CLEAN_UP_DEPLOY_DIR: "NO"
SEND_POC_DIR: "NO"
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ optional: true
install_flag: INSTALL_KONG

params:

- name: global_vm_ssh_name
target: true
type: string
Expand Down
1 change: 1 addition & 0 deletions bundles/admin/software.install.kong/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# ============================================================================
# bundles/admin/software.install.kong/main.yml
# ============================================================================
Expand Down
35 changes: 17 additions & 18 deletions bundles/admin/software.install.mattermost/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# ============================================================================
# bundles/admin/software.install.mattermost/main.yml
# ============================================================================
Expand Down Expand Up @@ -34,15 +35,13 @@
# global_vm_ci_ip: "192.168.142.182"
#
# ============================================================================


- name: mattermost - ensure catalog .env exists (cp .env.example -> .env if missing)
hosts: proxmox
gather_facts: false
tasks:
- name: cp .env.example -> .env if .env absent (force=false respects an existing customized .env)
ansible.builtin.copy:
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost/.env.example"
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost/.env.example"
dest: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost/.env"
force: false
remote_src: true
Expand All @@ -56,10 +55,10 @@
roles:
- software.install.warmup.basic_packages
vars:
INSTALL_PACKAGES_BASICS: "YES"
INSTALL_PACKAGES_DOCKER: "YES"
INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"
INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES"
INSTALL_PACKAGES_BASICS: "YES"
INSTALL_PACKAGES_DOCKER: "YES"
INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"
INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES"

# #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####

Expand All @@ -71,7 +70,7 @@
- software.configure.firewalls
vars:
firewall_rules:
- { ip: "all", port: 22, protocol: "tcp" }
- { ip: "all", port: 22, protocol: "tcp" }
- { ip: "all", port: 8065, protocol: "tcp" }

# #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
Expand All @@ -98,19 +97,19 @@

- name: push .env on the VM (cp .env.example -> .env if .env absent on the VM)
ansible.builtin.copy:
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost/.env.example"
dest: "/home/alice/mattermost/.env"
src: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost/.env.example"
dest: "/home/alice/mattermost/.env"
owner: "alice"
group: "alice"
mode: "0600"
mode: "0600"
force: false
roles:
- software.configure.docker-compose
vars:
LABEL_PROJECT_TYPE: "admin"
LABEL_PROJET_NAME: "mattermost"
LOCAL__PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost"
REMOTE_PROJECT_DIR: "/home/alice/mattermost"
OPERATOR_USER: "alice"
CLEAN_UP_DEPLOY_DIR: "NO"
SEND_POC_DIR: "NO"
LABEL_PROJECT_TYPE: "admin"
LABEL_PROJET_NAME: "mattermost"
LOCAL__PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/mattermost"
REMOTE_PROJECT_DIR: "/home/alice/mattermost"
OPERATOR_USER: "alice"
CLEAN_UP_DEPLOY_DIR: "NO"
SEND_POC_DIR: "NO"
Loading