Skip to content

Commit c72e5e1

Browse files
dguidoclaude
andcommitted
Add comprehensive pre-commit hooks for code quality
- Set up pre-commit framework with hooks for Python, YAML, Ansible, and shell - Configure ruff for Python linting and formatting - Add yamllint for YAML validation - Include ansible-lint and syntax checks - Add shellcheck for shell scripts - Create development documentation - Auto-fix trailing whitespace and file endings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 938cb0b commit c72e5e1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+940
-238
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
.github/
44
.gitignore
55

6-
# Development environment
6+
# Development environment
77
.env
88
.venv/
99
.ruff_cache/

.pre-commit-config.yaml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# See https://pre-commit.com for more information
2+
---
3+
# Apply to all files without committing:
4+
# pre-commit run --all-files
5+
# Update this file:
6+
# pre-commit autoupdate
7+
8+
repos:
9+
# General file checks
10+
- repo: https://github.com/pre-commit/pre-commit-hooks
11+
rev: v5.0.0
12+
hooks:
13+
- id: check-yaml
14+
args: [--allow-multiple-documents]
15+
exclude: '(files/cloud-init/base\.yml|roles/cloud-.*/files/stack\.yaml)'
16+
- id: end-of-file-fixer
17+
- id: trailing-whitespace
18+
- id: check-added-large-files
19+
args: ['--maxkb=500']
20+
- id: check-merge-conflict
21+
- id: mixed-line-ending
22+
args: [--fix=lf]
23+
24+
# Python linting with ruff (fast, replaces many tools)
25+
- repo: https://github.com/astral-sh/ruff-pre-commit
26+
rev: v0.8.6
27+
hooks:
28+
- id: ruff
29+
args: [--fix, --exit-non-zero-on-fix]
30+
- id: ruff-format
31+
32+
# YAML linting
33+
- repo: https://github.com/adrienverge/yamllint
34+
rev: v1.35.1
35+
hooks:
36+
- id: yamllint
37+
args: [-c=.yamllint]
38+
exclude: '.git/.*'
39+
40+
# Shell script linting
41+
- repo: https://github.com/shellcheck-py/shellcheck-py
42+
rev: v0.10.0.1
43+
hooks:
44+
- id: shellcheck
45+
exclude: '.git/.*'
46+
47+
# Local hooks that use the project's installed tools
48+
- repo: local
49+
hooks:
50+
- id: ansible-lint
51+
name: Ansible-lint
52+
entry: bash -c 'uv run ansible-lint --force-color || echo "Ansible-lint had issues - check output"'
53+
language: system
54+
types: [yaml]
55+
files: \.(yml|yaml)$
56+
exclude: '^(.git/|.github/|requirements\.yml)'
57+
pass_filenames: false
58+
59+
- id: ansible-syntax
60+
name: Ansible syntax check
61+
entry: bash -c 'uv run ansible-playbook main.yml --syntax-check'
62+
language: system
63+
files: 'main\.yml|server\.yml|users\.yml'
64+
pass_filenames: false
65+
66+
# Configuration for the pre-commit tool itself
67+
default_language_version:
68+
python: python3.11
69+
70+
# Files to exclude globally
71+
exclude: |
72+
(?x)^(
73+
.env/.*|
74+
.venv/.*|
75+
.git/.*|
76+
__pycache__/.*|
77+
.*\.egg-info/.*
78+
)$

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ def test_regression_openssl_inline_comments():
157157
# This pattern SHOULD fail (has inline comments)
158158
problematic = "{{ ['DNS:' + id, # comment ] }}"
159159
assert not validate(problematic), "Should detect inline comments"
160-
161-
# This pattern SHOULD pass (no inline comments)
160+
161+
# This pattern SHOULD pass (no inline comments)
162162
fixed = "{{ ['DNS:' + id] }}"
163163
assert validate(fixed), "Should pass without comments"
164164
```
@@ -492,4 +492,4 @@ When working on Algo:
492492
4. **Be Conservative**: This is critical infrastructure
493493
5. **Respect Privacy**: No tracking, minimal logging
494494

495-
Remember: People trust Algo with their privacy and security. Every line of code matters.
495+
Remember: People trust Algo with their privacy and security. Every line of code matters.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,4 +658,4 @@ specific requirements.
658658
You should also get your employer (if you work as a programmer) or school,
659659
if any, to sign a "copyright disclaimer" for the program, if necessary.
660660
For more information on this, and how to apply and follow the GNU AGPL, see
661-
<https://www.gnu.org/licenses/>.
661+
<https://www.gnu.org/licenses/>.

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ The easiest way to get an Algo server running is to run it on your local system
4545
3. **Set your configuration options.** Open `config.cfg` in your favorite text editor. Specify the users you want to create in the `users` list. Create a unique user for each device you plan to connect to your VPN. You should also review the other options before deployment, as changing your mind about them later [may require you to deploy a brand new server](https://github.com/trailofbits/algo/blob/master/docs/faq.md#i-deployed-an-algo-server-can-you-update-it-with-new-features).
4646

4747
4. **Start the deployment.** Return to your terminal. In the Algo directory, run the appropriate script for your platform:
48-
48+
4949
**macOS/Linux:**
5050
```bash
5151
./algo
5252
```
53-
53+
5454
**Windows:**
5555
```powershell
5656
.\algo.ps1
5757
```
58-
58+
5959
The first time you run the script, it will automatically install the required Python environment (Python 3.11+). On subsequent runs, it starts immediately and works on all platforms (macOS, Linux, Windows via WSL). The Windows PowerShell script automatically uses WSL when needed, since Ansible requires a Unix-like environment. There are several optional features available, none of which are required for a fully functional VPN server. These optional features are described in the [deployment documentation](docs/deploy-from-ansible.md).
6060

6161
That's it! You can now set up clients to connect to your VPN. Proceed to [Configure the VPN Clients](#configure-the-vpn-clients) below.
@@ -264,6 +264,14 @@ If you've read all the documentation and have further questions, [create a new d
264264
265265
-- [Thorin Klosowski](https://twitter.com/kingthor) for [Lifehacker](http://lifehacker.com/how-to-set-up-your-own-completely-free-vpn-in-the-cloud-1794302432)
266266
267+
## Contributing
268+
269+
See our [Development Guide](docs/DEVELOPMENT.md) for information on:
270+
* Setting up your development environment
271+
* Using pre-commit hooks for code quality
272+
* Running tests and linters
273+
* Contributing code via pull requests
274+
267275
## Support Algo VPN
268276
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CYZZD39GXUJ3E)
269277
[![Patreon](https://img.shields.io/badge/back_on-patreon-red.svg)](https://www.patreon.com/algovpn)

algo

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ UV_INSTALL_METHOD=""
88
# Function to install uv via package managers (most secure)
99
install_uv_via_package_manager() {
1010
echo "Attempting to install uv via system package manager..."
11-
11+
1212
if command -v brew &> /dev/null; then
1313
echo "Using Homebrew..."
1414
brew install uv && UV_INSTALL_METHOD="Homebrew" && return 0
@@ -31,7 +31,7 @@ install_uv_via_package_manager() {
3131
echo "Using scoop..."
3232
scoop install uv && UV_INSTALL_METHOD="scoop" && return 0
3333
fi
34-
34+
3535
return 1
3636
}
3737

@@ -41,7 +41,7 @@ install_uv_ubuntu_alternatives() {
4141
if ! command -v lsb_release &> /dev/null || [[ "$(lsb_release -si)" != "Ubuntu" ]]; then
4242
return 1 # Not Ubuntu, skip these options
4343
fi
44-
44+
4545
echo ""
4646
echo "Ubuntu detected. Additional trusted installation options available:"
4747
echo ""
@@ -54,7 +54,7 @@ install_uv_ubuntu_alternatives() {
5454
echo ""
5555
echo "3. Continue to official installer script download"
5656
echo ""
57-
57+
5858
while true; do
5959
read -r -p "Choose installation method (1/2/3): " choice
6060
case $choice in
@@ -104,14 +104,14 @@ install_uv_via_download() {
104104
echo " 3. Verify checksums and install manually"
105105
echo " 4. Then run: ./algo"
106106
echo ""
107-
107+
108108
read -p "Continue with script download? (y/N): " -n 1 -r
109109
echo
110110
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
111111
echo "Installation cancelled. Please install uv manually and retry."
112112
exit 1
113113
fi
114-
114+
115115
echo "Downloading uv installation script..."
116116
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "linux-gnu" && -n "${WSL_DISTRO_NAME:-}" ]] || uname -s | grep -q "MINGW\|MSYS"; then
117117
# Windows (Git Bash/WSL/MINGW) - use versioned installer
@@ -127,7 +127,7 @@ install_uv_via_download() {
127127
# Check if uv is installed, if not, install it securely
128128
if ! command -v uv &> /dev/null; then
129129
echo "uv (Python package manager) not found. Installing..."
130-
130+
131131
# Try package managers first (most secure)
132132
if ! install_uv_via_package_manager; then
133133
# Try Ubuntu-specific alternatives if available
@@ -136,26 +136,26 @@ if ! command -v uv &> /dev/null; then
136136
install_uv_via_download
137137
fi
138138
fi
139-
139+
140140
# Reload PATH to find uv (includes pipx, cargo, and snap paths)
141141
# Note: This PATH change only affects the current shell session.
142142
# Users may need to restart their terminal for subsequent runs.
143143
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:/snap/bin:$PATH"
144-
144+
145145
# Verify installation worked
146146
if ! command -v uv &> /dev/null; then
147147
echo "Error: uv installation failed. Please restart your terminal and try again."
148148
echo "Or install manually from: https://docs.astral.sh/uv/getting-started/installation/"
149149
exit 1
150150
fi
151-
151+
152152
echo "✓ uv installed successfully via ${UV_INSTALL_METHOD}!"
153153
fi
154154

155155
# Run the appropriate playbook
156156
case "$1" in
157-
update-users)
157+
update-users)
158158
uv run ansible-playbook users.yml "${@:2}" -t update-users ;;
159-
*)
159+
*)
160160
uv run ansible-playbook main.yml "${@}" ;;
161161
esac

algo.ps1

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ function Test-RunningInWSL {
1313
# Function to run Algo in WSL
1414
function Invoke-AlgoInWSL {
1515
param($Arguments)
16-
16+
1717
Write-Host "NOTICE: Ansible requires a Unix-like environment and cannot run natively on Windows."
1818
Write-Host "Attempting to run Algo via Windows Subsystem for Linux (WSL)..."
1919
Write-Host ""
20-
20+
2121
if (-not (Get-Command wsl -ErrorAction SilentlyContinue)) {
2222
Write-Host "ERROR: WSL (Windows Subsystem for Linux) is not installed." -ForegroundColor Red
2323
Write-Host ""
@@ -38,7 +38,7 @@ function Invoke-AlgoInWSL {
3838
Write-Host "https://github.com/trailofbits/algo/blob/master/docs/deploy-from-windows.md"
3939
exit 1
4040
}
41-
41+
4242
# Check if any WSL distributions are installed and running
4343
Write-Host "Checking for WSL Linux distributions..."
4444
$wslList = wsl -l -v 2>$null
@@ -52,13 +52,13 @@ function Invoke-AlgoInWSL {
5252
Write-Host "Then restart your computer and try again."
5353
exit 1
5454
}
55-
55+
5656
Write-Host "Successfully found WSL. Launching Algo..." -ForegroundColor Green
5757
Write-Host ""
58-
58+
5959
# Get current directory name for WSL path mapping
6060
$currentDir = Split-Path -Leaf (Get-Location)
61-
61+
6262
try {
6363
if ($Arguments.Count -gt 0 -and $Arguments[0] -eq "update-users") {
6464
$remainingArgs = $Arguments[1..($Arguments.Count-1)] -join " "
@@ -67,7 +67,7 @@ function Invoke-AlgoInWSL {
6767
$allArgs = $Arguments -join " "
6868
wsl bash -c "cd /mnt/c/$currentDir 2>/dev/null || (echo 'Error: Cannot access directory in WSL. Make sure you are running from a Windows drive (C:, D:, etc.)' && exit 1) && ./algo $allArgs"
6969
}
70-
70+
7171
if ($LASTEXITCODE -ne 0) {
7272
Write-Host ""
7373
Write-Host "Algo finished with exit code: $LASTEXITCODE" -ForegroundColor Yellow
@@ -93,7 +93,7 @@ try {
9393
# Check if we're actually running inside WSL
9494
if (Test-RunningInWSL) {
9595
Write-Host "Detected WSL environment. Running Algo using standard Unix approach..."
96-
96+
9797
# Verify bash is available (should be in WSL)
9898
if (-not (Get-Command bash -ErrorAction SilentlyContinue)) {
9999
Write-Host "ERROR: Running in WSL but bash is not available." -ForegroundColor Red
@@ -102,15 +102,15 @@ try {
102102
Write-Host " wsl" -ForegroundColor Cyan
103103
exit 1
104104
}
105-
105+
106106
# Run the standard Unix algo script
107107
& bash -c "./algo $($Arguments -join ' ')"
108108
exit $LASTEXITCODE
109109
}
110-
110+
111111
# We're on native Windows - need to use WSL
112112
Invoke-AlgoInWSL $Arguments
113-
113+
114114
} catch {
115115
Write-Host ""
116116
Write-Host "UNEXPECTED ERROR:" -ForegroundColor Red
@@ -121,4 +121,4 @@ try {
121121
Write-Host "2. See troubleshooting guide: https://github.com/trailofbits/algo/blob/master/docs/deploy-from-windows.md"
122122
Write-Host "3. Or use WSL directly: open Ubuntu and run './algo'"
123123
exit 1
124-
}
124+
}

0 commit comments

Comments
 (0)