Skip to content

Commit cddb5df

Browse files
dguidoclaude
andauthored
Add comprehensive pre-commit hooks for code quality (#14831)
* 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 <noreply@anthropic.com> * Remove redundant DEVELOPMENT.md and update CONTRIBUTING.md - Removed docs/DEVELOPMENT.md as it was redundant with existing documentation - Added pre-commit hooks setup instruction to CONTRIBUTING.md for contributors - Consolidated development guidance into a single location 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4bb13a5 commit cddb5df

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

+809
-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.

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
* Clone the repository: `git clone https://github.com/trailofbits/algo.git`
1919
* Run Algo: `./algo` (dependencies installed automatically via uv)
20+
* Install pre-commit hooks: `uv run pre-commit install` (optional, for contributors)
2021
* For local testing, consider using Docker or a cloud provider test instance
2122

2223
Thanks!

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+
}

docs/aws-credentials.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ If Algo isn't finding your credentials:
5858

5959
If credentials are found but authentication fails:
6060
- Ensure your IAM user has the required permissions (see [EC2 deployment guide](deploy-from-ansible.md))
61-
- Check if you need session tokens for temporary credentials
61+
- Check if you need session tokens for temporary credentials

docs/client-apple-ipsec.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ On iOS, connect to the VPN by opening **Settings** and clicking the toggle next
1212

1313
If you enable "Connect On Demand", the VPN will connect automatically whenever it is able. Most Apple users will want to enable "Connect On Demand", but if you do then simply disabling the VPN will not cause it to stay disabled; it will just "Connect On Demand" again. To disable the VPN you'll need to disable "Connect On Demand".
1414

15-
On iOS, you can turn off "Connect On Demand" in **Settings** by clicking the (i) next to the entry for your Algo VPN and toggling off "Connect On Demand." On macOS, you can turn off "Connect On Demand" by opening **System Settings** -> **Network** (or **VPN** on macOS Sequoia 15.0+), finding the Algo VPN in the left column, unchecking the box for "Connect on demand", and clicking Apply.
15+
On iOS, you can turn off "Connect On Demand" in **Settings** by clicking the (i) next to the entry for your Algo VPN and toggling off "Connect On Demand." On macOS, you can turn off "Connect On Demand" by opening **System Settings** -> **Network** (or **VPN** on macOS Sequoia 15.0+), finding the Algo VPN in the left column, unchecking the box for "Connect on demand", and clicking Apply.

0 commit comments

Comments
 (0)