Skip to content

Commit ba72972

Browse files
dguidoclaude
andauthored
Simplify codebase: modernize loops, split templates, improve CI (#14889)
* Simplify codebase: modernize loops, split templates, improve CI This PR consolidates several simplification phases: ## Ansible Modernization - Modernize `with_items` to `loop` across ~50 task files - Add OS detection facts (is_ubuntu, os_family_lowercase) - Condense inline YAML syntax where appropriate ## Template Splitting - Split 568-line dnscrypt-proxy.toml.j2 into focused partials: - global.toml.j2 (core settings) - sources.toml.j2 (resolver sources) - filters.toml.j2 (blocking rules) - cache.toml.j2 (caching config) ## CI Workflow Improvements - Create setup-algo composite action for shared CI setup - Re-enable integration tests with health checks - Fix smart-tests.yml silent lint failures (remove || true) - Use env variables for GitHub SHAs (security) ## server.yml Async Simplification - Reorganize VPN service configuration with clear sections - Add performance_parallel_services toggle - Simplify status display from json_query to inline conditionals - Keep services explicit for readability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix with_items to loop conversion: preserve list flattening with_items automatically flattens nested lists, but loop does NOT. The mechanical conversion broke iteration over list variables. Wrong: loop: - "{{ users }}" # ['alice', 'bob'] treated as ONE item Fixed: loop: "{{ users }}" # Iterates over alice, bob correctly For combined lists (users + server): loop: "{{ users + [IP_subject_alt_name] }}" Fixes IPsec certificate generation creating files named literally '['alice', 'bob'].key' instead of separate alice.key and bob.key. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix integration test: use strongswan-starter service name on Ubuntu 20.04+ The StrongSwan service is named 'strongswan-starter' on Ubuntu 20.04+, not 'strongswan'. The test was checking the wrong service name, causing false failures even when StrongSwan was actually running. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix IPsec path issues: remove trailing slashes and fix test paths 1. Remove trailing slashes from ipsec_config_path and ipsec_pki_path in roles/strongswan/defaults/main.yml (causes double slashes) 2. Fix integration test to check correct subdirectories: - .p12 files are in ipsec/manual/ - .mobileconfig files are in ipsec/apple/ 3. Fix strongswan service name check (strongswan-starter on Ubuntu 20.04+) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 0d5793d commit ba72972

File tree

34 files changed

+422
-766
lines changed

34 files changed

+422
-766
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
name: 'Setup Algo Environment'
3+
description: 'Setup Python, uv, and dependencies for Algo VPN CI'
4+
inputs:
5+
python-version:
6+
description: 'Python version to use'
7+
required: false
8+
default: '3.11'
9+
install-shellcheck:
10+
description: 'Install shellcheck for shell script linting'
11+
required: false
12+
default: 'false'
13+
install-ansible-collections:
14+
description: 'Install Ansible Galaxy collections'
15+
required: false
16+
default: 'false'
17+
runs:
18+
using: composite
19+
steps:
20+
- name: Setup Python
21+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
22+
with:
23+
python-version: ${{ inputs.python-version }}
24+
25+
- name: Setup uv environment
26+
uses: ./.github/actions/setup-uv
27+
28+
- name: Install shellcheck
29+
if: inputs.install-shellcheck == 'true'
30+
run: sudo apt-get update && sudo apt-get install -y shellcheck
31+
shell: bash
32+
33+
- name: Install Ansible collections
34+
if: inputs.install-ansible-collections == 'true'
35+
run: uv run ansible-galaxy collection install -r requirements.yml
36+
shell: bash

.github/workflows/integration-tests.yml

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
name: Localhost VPN Deployment Test
2222
runs-on: ubuntu-22.04
2323
timeout-minutes: 30
24-
if: false # Disabled until we fix the ansible issues
2524
strategy:
25+
fail-fast: false
2626
matrix:
2727
vpn_type: ['wireguard', 'ipsec', 'both']
2828
steps:
@@ -78,7 +78,6 @@ jobs:
7878
tests: true
7979
no_log: false
8080
ansible_connection: local
81-
ansible_python_interpreter: /usr/bin/python3
8281
dns_encryption: true
8382
algo_dns_adblocking: true
8483
algo_ssh_tunneling: false
@@ -88,11 +87,15 @@ jobs:
8887
pki_in_tmpfs: true
8988
endpoint: 127.0.0.1
9089
ssh_port: 4160
90+
local_service_ip: 172.16.0.1
91+
local_service_ipv6: "fd00::1"
9192
EOF
9293
9394
- name: Run Algo deployment
9495
run: |
95-
sudo ansible-playbook main.yml \
96+
# Run ansible-playbook via uv - become: true in playbook handles root
97+
# GitHub runners have passwordless sudo for become escalation
98+
uv run ansible-playbook main.yml \
9699
-i "localhost," \
97100
-c local \
98101
-e @integration-test.cfg \
@@ -112,11 +115,11 @@ jobs:
112115
echo "✓ WireGuard is running"
113116
fi
114117
115-
# Check StrongSwan
118+
# Check StrongSwan (service name is strongswan-starter on Ubuntu 20.04+)
116119
if [[ "${{ matrix.vpn_type }}" == "ipsec" || "${{ matrix.vpn_type }}" == "both" ]]; then
117120
echo "Checking StrongSwan..."
118121
sudo ipsec statusall
119-
if ! sudo systemctl is-active --quiet strongswan; then
122+
if ! sudo systemctl is-active --quiet strongswan-starter; then
120123
echo "✗ StrongSwan service not running"
121124
exit 1
122125
fi
@@ -130,6 +133,21 @@ jobs:
130133
echo "✓ dnsmasq is running"
131134
fi
132135
136+
# Check dnscrypt-proxy
137+
if sudo systemctl is-active --quiet dnscrypt-proxy; then
138+
echo "✓ dnscrypt-proxy is running"
139+
else
140+
echo "⚠️ dnscrypt-proxy not running"
141+
fi
142+
143+
# DNS health check - verify DNS resolution works
144+
echo "Testing DNS resolution via local_service_ip (172.16.0.1)..."
145+
if dig @172.16.0.1 google.com +short +timeout=5 | grep -q .; then
146+
echo "✓ DNS resolution working"
147+
else
148+
echo "⚠️ DNS resolution failed (service may still be starting)"
149+
fi
150+
133151
- name: Verify generated configs
134152
run: |
135153
echo "Checking generated configuration files..."
@@ -149,14 +167,14 @@ jobs:
149167
echo "✓ All WireGuard configs generated"
150168
fi
151169
152-
# IPsec configs
170+
# IPsec configs (p12 in manual/, mobileconfig in apple/)
153171
if [[ "${{ matrix.vpn_type }}" == "ipsec" || "${{ matrix.vpn_type }}" == "both" ]]; then
154172
for user in alice bob; do
155-
if [ ! -f "configs/localhost/ipsec/${user}.p12" ]; then
173+
if [ ! -f "configs/localhost/ipsec/manual/${user}.p12" ]; then
156174
echo "✗ Missing IPsec certificate for ${user}"
157175
exit 1
158176
fi
159-
if [ ! -f "configs/localhost/ipsec/${user}.mobileconfig" ]; then
177+
if [ ! -f "configs/localhost/ipsec/apple/${user}.mobileconfig" ]; then
160178
echo "✗ Missing IPsec mobile config for ${user}"
161179
exit 1
162180
fi
@@ -196,12 +214,22 @@ jobs:
196214
- name: Upload logs on failure
197215
if: failure()
198216
run: |
199-
echo "=== Ansible Log ==="
200-
sudo journalctl -u ansible --no-pager || true
217+
echo "=== Network Interfaces ==="
218+
ip addr || true
219+
echo "=== Listening Ports ==="
220+
sudo ss -tulnp || true
221+
echo "=== WireGuard Status ==="
222+
sudo wg show || true
223+
echo "=== IPsec Status ==="
224+
sudo ipsec statusall || true
225+
echo "=== DNS Services ==="
226+
sudo systemctl status dnscrypt-proxy dnscrypt-proxy.socket dnsmasq --no-pager || true
201227
echo "=== WireGuard Log ==="
202-
sudo journalctl -u wg-quick@wg0 --no-pager || true
228+
sudo journalctl -u wg-quick@wg0 -n 50 --no-pager || true
203229
echo "=== StrongSwan Log ==="
204-
sudo journalctl -u strongswan --no-pager || true
230+
sudo journalctl -u strongswan -n 50 --no-pager || true
231+
echo "=== dnscrypt-proxy Log ==="
232+
sudo journalctl -u dnscrypt-proxy -n 50 --no-pager || true
205233
echo "=== System Log (last 100 lines) ==="
206234
sudo journalctl -n 100 --no-pager || true
207235

.github/workflows/lint.yml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,11 @@ jobs:
1414
- uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1
1515
with:
1616
persist-credentials: false
17-
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
18-
with:
19-
python-version: '3.11'
2017

21-
- name: Setup uv environment
22-
uses: ./.github/actions/setup-uv
23-
24-
- name: Install Ansible collections
25-
run: uv run --with ansible-lint --with ansible ansible-galaxy collection install -r requirements.yml
18+
- name: Setup Algo environment
19+
uses: ./.github/actions/setup-algo
20+
with:
21+
install-ansible-collections: 'true'
2622

2723
- name: Run ansible-lint
2824
run: |
@@ -59,12 +55,9 @@ jobs:
5955
- uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1
6056
with:
6157
persist-credentials: false
62-
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
63-
with:
64-
python-version: '3.11'
6558

66-
- name: Setup uv environment
67-
uses: ./.github/actions/setup-uv
59+
- name: Setup Algo environment
60+
uses: ./.github/actions/setup-algo
6861

6962
- name: Run ruff
7063
run: |
@@ -79,9 +72,13 @@ jobs:
7972
with:
8073
persist-credentials: false
8174

75+
- name: Setup Algo environment
76+
uses: ./.github/actions/setup-algo
77+
with:
78+
install-shellcheck: 'true'
79+
8280
- name: Run shellcheck
8381
run: |
84-
sudo apt-get update && sudo apt-get install -y shellcheck
8582
# Check all shell scripts, not just algo and install.sh
8683
find . -type f -name "*.sh" -not -path "./.git/*" -exec shellcheck {} \;
8784

.github/workflows/smart-tests.yml

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ jobs:
6161
- '**/*.sh'
6262
- '.ansible-lint'
6363
- '.yamllint'
64-
- 'ruff.toml'
6564
- 'pyproject.toml'
6665
integration:
6766
- 'main.yml'
@@ -239,17 +238,25 @@ jobs:
239238
- name: Run relevant linters
240239
env:
241240
RUN_LINT: ${{ needs.changed-files.outputs.run_lint }}
241+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
242+
HEAD_SHA: ${{ github.sha }}
242243
run: |
243-
# Always run if lint files changed
244+
# Run linters if lint-related files changed
244245
if [[ "${RUN_LINT}" == "true" ]]; then
245-
# Run all linters
246-
uv run --with ruff ruff check . || true
247-
uv run --with yamllint yamllint . || true
248-
uv run --with ansible-lint ansible-lint || true
246+
echo "Running linters..."
247+
248+
# Run Python linter
249+
uv run --with ruff ruff check .
250+
251+
# Run YAML linter
252+
uv run --with yamllint yamllint -c .yamllint .
253+
254+
# Run Ansible linter
255+
uv run --with ansible-lint ansible-lint
249256
250257
# Check shell scripts if any changed
251-
if git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -q '\.sh$'; then
252-
find . -name "*.sh" -type f -exec shellcheck {} + || true
258+
if git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" | grep -q '\.sh$'; then
259+
find . -name "*.sh" -type f -not -path "./.git/*" -exec shellcheck {} +
253260
fi
254261
fi
255262

roles/client/tasks/main.yml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
- name: Install prerequisites
88
package: name="{{ item }}" state=present
9-
with_items:
10-
- "{{ prerequisites }}"
9+
loop: "{{ prerequisites }}"
1110
register: result
1211
until: result is succeeded
1312
retries: 10
@@ -25,8 +24,6 @@
2524
src: roles/strongswan/templates/client_ipsec.conf.j2
2625
dest: "{{ configs_prefix }}/ipsec.{{ IP_subject_alt_name }}.conf"
2726
mode: "0644"
28-
with_items:
29-
- "{{ vpn_user }}"
3027
notify:
3128
- restart strongswan
3229

@@ -35,8 +32,6 @@
3532
src: roles/strongswan/templates/client_ipsec.secrets.j2
3633
dest: "{{ configs_prefix }}/ipsec.{{ IP_subject_alt_name }}.secrets"
3734
mode: "0600"
38-
with_items:
39-
- "{{ vpn_user }}"
4035
notify:
4136
- restart strongswan
4237

@@ -46,7 +41,7 @@
4641
line: "{{ item.line }}"
4742
create: true
4843
mode: "{{ item.mode }}"
49-
with_items:
44+
loop:
5045
- dest: "{{ configs_prefix }}/ipsec.conf"
5146
line: include ipsec.{{ IP_subject_alt_name }}.conf
5247
mode: '0644'
@@ -69,7 +64,7 @@
6964
src: "{{ item.src }}"
7065
dest: "{{ item.dest }}"
7166
mode: "{{ item.mode }}"
72-
with_items:
67+
loop:
7368
- src: configs/{{ IP_subject_alt_name }}/ipsec/.pki/certs/{{ vpn_user }}.crt
7469
dest: "{{ configs_prefix }}/ipsec.d/certs/{{ vpn_user }}.crt"
7570
mode: '0644'

roles/cloud-cloudstack/tasks/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
start_port: "{{ item.start_port }}"
2424
end_port: "{{ item.end_port }}"
2525
cidr: "{{ item.range }}"
26-
with_items:
26+
loop:
2727
- { proto: tcp, start_port: "{{ ssh_port }}", end_port: "{{ ssh_port }}", range: 0.0.0.0/0 }
2828
- { proto: udp, start_port: 4500, end_port: 4500, range: 0.0.0.0/0 }
2929
- { proto: udp, start_port: 500, end_port: 500, range: 0.0.0.0/0 }

roles/cloud-openstack/tasks/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
port_range_min: "{{ item.port_min }}"
2424
port_range_max: "{{ item.port_max }}"
2525
remote_ip_prefix: "{{ item.range }}"
26-
with_items:
26+
loop:
2727
- { proto: tcp, port_min: "{{ ssh_port }}", port_max: "{{ ssh_port }}", range: 0.0.0.0/0 }
2828
- { proto: icmp, port_min: -1, port_max: -1, range: 0.0.0.0/0 }
2929
- { proto: udp, port_min: 4500, port_max: 4500, range: 0.0.0.0/0 }
@@ -58,7 +58,7 @@
5858
- item['router:external']|default(omit)
5959
- item['admin_state_up']|default(omit)
6060
- item['status'] == 'ACTIVE'
61-
with_items: "{{ os_network.openstack_networks }}"
61+
loop: "{{ os_network.openstack_networks }}"
6262

6363
- name: Set facts
6464
set_fact:

roles/cloud-vultr/tasks/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
ip_type: "{{ item.ip }}"
2020
subnet: "{{ item.cidr.split('/')[0] }}"
2121
subnet_size: "{{ item.cidr.split('/')[1] }}"
22-
with_items:
22+
loop:
2323
- { protocol: tcp, port: "{{ ssh_port }}", ip: v4, cidr: 0.0.0.0/0 }
2424
- { protocol: tcp, port: "{{ ssh_port }}", ip: v6, cidr: "::/0" }
2525
- { protocol: udp, port: 500, ip: v4, cidr: 0.0.0.0/0 }

roles/common/tasks/facts.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
---
2+
- name: Set OS platform facts
3+
set_fact:
4+
is_debian_based: "{{ ansible_distribution in ['Debian', 'Ubuntu'] }}"
5+
uses_systemd_socket: "{{ ansible_distribution in ['Debian', 'Ubuntu'] }}"
6+
is_ubuntu_22_plus: "{{ ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('22.04', '>=') }}"
7+
tags: always
8+
29
- name: Define facts
310
set_fact:
411
p12_export_password: "{{ p12_password | default(lookup('password', '/dev/null length=9 chars=ascii_letters,digits,_,@')) }}"

roles/common/tasks/iptables.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
owner: root
77
group: root
88
mode: '0640'
9-
with_items:
9+
loop:
1010
- { src: rules.v4.j2, dest: /etc/iptables/rules.v4 }
1111
notify:
1212
- restart iptables
@@ -19,7 +19,7 @@
1919
group: root
2020
mode: '0640'
2121
when: ipv6_support
22-
with_items:
22+
loop:
2323
- { src: rules.v6.j2, dest: /etc/iptables/rules.v6 }
2424
notify:
2525
- restart iptables

0 commit comments

Comments
 (0)