* Migrate Azure module to azure.azcollection
Replace deprecated azure_rm_deployment module with FQCN:
- azure_rm_deployment → azure.azcollection.azure_rm_deployment
Changes:
- Add azure.azcollection (>=3.0.0) to requirements.yml
- Update roles/cloud-azure/tasks/main.yml to use FQCN
No parameter changes needed - the collection module supports all
existing parameters via aliases (deployment_name, resource_group_name,
client_id, secret, tenant, subscription_id).
Closes#14904🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix trailing comma in Azure deployment template
Remove invalid trailing comma in outputs section (line 222).
This was a pre-existing JSON syntax error.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Replace ignore_errors: true with failed_when: false in 5 files
(main.yml, users.yml, ubuntu.yml, umount.yml, test-wireguard-real-async.yml)
- Add pytest.ini configuration for test discovery
- Add tests/conftest.py with shared fixtures and mock helpers
The failed_when: false pattern is preferred by ansible-lint as it
explicitly indicates expected failure handling rather than silently
ignoring all errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Refactor CLAUDE.md: add quality gates, reduce redundancy
Analyzed the last 25 PRs to identify patterns where better guidance
could have prevented iteration. Key findings:
- PRs had lint failures caught after submission
- with_items→loop conversions broke list flattening
- Missing input validation and explicit file modes
- Duplicate DNS documentation in 3 places
Changes:
- Add Quality Gates section with mandatory pre-submission checks
- Add Design Requirements checklist (validate inputs, explicit modes)
- Add Ansible Pitfalls section (with_items vs loop, path variables)
- Add Self-Review Checklist
- Consolidate DNS architecture from 3 sections into 1
- Remove redundant sections (Final Notes, User Profiles, duplicate philosophy)
- Condense linting rules into table format
Result: 496 → 307 lines (-38%) while adding actionable new guidance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add Jinja2 native mode guidance (Ansible 12 compatibility)
Analysis of PRs 26-50 revealed that 28% (7 PRs) were fixing Ansible 12
compatibility issues caused by jinja2_native mode being always enabled.
Added guidance for:
- Boolean conditionals requiring actual booleans
- No nested templates in lookup()
- JSON files needing explicit | from_json parsing
- default() not triggering on empty strings (need true param)
- Complex Jinja loops breaking in set_fact
Also added "Undocumented workarounds" to What to Avoid section based on
Scaleway/Vultr upstream module bug patterns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix Ansible version and add OpenSSL explanation
- Update Ansible version from "v9.x" to "v12+" for consistency
with Jinja2 native mode guidance
- Add one-line explanation to OpenSSL snippet for clarity
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Soften language and add troubleshooting cross-reference
- Remove "CRITICAL" and "Critical Gotcha" labels
- Change "90% of routing issues are DNS" to "Many routing issues"
- Add link to docs/troubleshooting.md for comprehensive diagnostics
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Clarify 'verify before encoding' philosophy statement
Change ambiguous "Verify before encoding" (encoding could mean data
encoding) to clearer "Test assumptions - run the code before committing"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix Docker --cap-drop=all and add multi-arch support
Fixes#14899
## Bug Fix: CAP_DROP_ALL Permission Denied
The `chown -R algo:algo /algo` line added in commit 2ab57c3 broke
`--cap-drop=all` functionality. When running as root with all
capabilities dropped, root loses CAP_DAC_OVERRIDE and cannot write
to files owned by other users.
The fix removes the unnecessary chown since:
- Container runs as USER root
- algo-docker.sh writes to /algo/config.cfg at runtime
- /algo must be root-owned for --cap-drop=all to work
## Multi-arch Support
The Docker image was only built for linux/amd64. Added:
- QEMU setup for ARM emulation
- Docker Buildx for multi-platform builds
- platforms: linux/amd64,linux/arm64
This enables native support for Apple Silicon Macs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update Docker actions to latest versions
- setup-qemu-action: v3.6.0 → v3.7.0
- setup-buildx-action: v3.10.0 → v3.11.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Enhance the local installation warning to require typing 'yes' to
proceed, making risks impossible to miss. Users following external
tutorials often don't see the documentation warning until after
their server is already modified.
Changes:
- Replace press-ENTER prompt with explicit 'yes' confirmation
- List specific consequences (firewall, DNS, no uninstall)
- Add local_install_confirmed variable for automation
- Document skip method in deploy-to-ubuntu.md
Closes#14362🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Remove obsolete troubleshooting entries
Remove 3 troubleshooting entries that are no longer relevant:
1. DigitalOcean: error tagging resource
- Fixed in 2018 when tagging was moved to droplet creation
- The separate tagging step that caused this error no longer exists
2. Docker: Failed to connect to the host via ssh
- 6+ years old workaround for control_path_dir
- Modern Docker deployments don't exhibit this issue
- The workaround was never added to ansible.cfg
3. Ubuntu Error: unable to write 'random state'
- OpenSSL 3.x (Ubuntu 22.04) doesn't use .rnd files
- Algo now uses Ansible community.crypto modules instead of
shell openssl commands
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Reorganize Installation Problems section by category
Group related troubleshooting entries for easier navigation:
- General Setup: Python, Ansible, SSL certs, SSH permissions
- Cloud Providers: Region availability, AWS (3), Azure (2), Linode (1)
- Windows: WSL keyData issue, VPN connection error
- Local Deployment: Symlinks, WireGuard configs
- Network: Firewall timeout
Moves Linode stackscript issue to be grouped with other cloud providers
instead of at the end of the section.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add a new 'Diagnostic Commands' section to help users investigate issues
themselves. Includes:
- Config options to enable verbose logging (algo_no_log, strongswan_log_level,
privacy_enhancements_enabled)
- Server-side diagnostics for WireGuard, IPsec/StrongSwan, and DNS
- Log viewing commands (journalctl, dmesg)
- Network and firewall diagnostic commands
- Client-side diagnostics for macOS, Linux, and Windows
Closes#514🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Simplify codebase: remove dead venv files, consolidate loops, modernize patterns
- Delete 8 empty venv.yml files that were remnants of pyproject.toml migration
- Remove corresponding import_tasks references from cloud provider main.yml files
- Consolidate server.yml async polling from 4 copy-paste blocks to 1 loop
- Wrap sequential VPN service imports in a block with single when condition
- Replace deprecated with_indexed_items with modern loop/loop_control syntax
These changes reduce maintenance burden by:
- Eliminating 8 files that served no purpose
- Reducing server.yml by ~20 lines while improving readability
- Removing Ansible deprecation warnings for with_indexed_items
- Making the parallel/sequential execution paths easier to maintain
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix template variable and undefined job variable bugs
- Fix client.conf.j2 to use `item` instead of `item.1` to match modern
loop syntax (loop with index_var instead of with_indexed_items)
- Fix server.yml _vpn_jobs to use `| default({})` for job variables
that may be undefined when running with tags (e.g., IPsec-only)
- Update test_template_rendering.py to pass item as string instead of
tuple, matching the new loop behavior
Fixes CI failures from PR #14891 that were correctly identified by the
Claude Code bot review.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Addresses a long-standing user question about whether existing VPN
clients can be migrated when deploying a new Algo server. The answer:
technically possible but rarely worth it, since servers auto-update
their VPN software anyway.
Closes#1538🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The wait_for_connection task hangs in certain Docker environments when
using the local provider because Ansible's local connection plugin
doesn't implement the reset method, causing the warning "Reset is not
implemented for this connection" and potential indefinite hangs.
This change adds a condition to skip the task for localhost connections,
following the same pattern used elsewhere in the codebase (e.g., the SSH
wait task on line 40 and the wait_for_connection in ubuntu.yml).
The task is unnecessary for localhost since it always completes instantly
(elapsed: 0) - localhost is always reachable.
Fixes#14627🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Fix ansible-lint failures and improve linting configuration
This PR eliminates all ansible-lint failures (9 → 0) and reduces warnings
(25 → 20). The remaining warnings are yaml[line-length] which are
intentionally in the warn_list.
Changes:
1. GitHub workflow YAML compliance
- Add document start markers (---)
- Quote 'on:' key (YAML truthy value)
2. Replace ignore_errors with failed_when
- privacy/clear_history.yml: Use failed_when: false instead of
ignore_errors for tasks that may fail on clean systems
3. Use apt module instead of shell
- privacy/auto_cleanup.yml: Replace apt-get shell command with
proper apt module (autoclean: true)
4. Add changed_when to handlers and tasks
- Handlers always run due to notifications, so changed_when: false
is appropriate for read-only operations
- Check commands that only read state get changed_when: false
- Reboot task gets changed_when: true (it actually changes state)
5. Configure ansible-lint for project structure
- Exclude CloudFormation templates (roles/cloud-*/files/) which use
AWS-specific YAML tags (!Equals, !GetAtt) that ansible-lint
cannot parse
- Add mock_modules for custom modules in library/ directory that
ansible-lint cannot auto-discover during static analysis
See: https://github.com/ansible/ansible-lint/discussions/1353
6. Fix Jinja2 spacing
- Remove extraneous whitespace in multi-line Jinja expressions
- Fix spacing before closing parentheses
References:
- ansible-lint mock_modules: https://ansible.readthedocs.io/projects/lint/configuring/
- Custom module discovery issue: https://github.com/ansible/ansible-lint/discussions/1353
- Ansible local modules: https://docs.ansible.com/ansible/latest/dev_guide/developing_locally.html🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Workaround sticky comment bug in claude-code-action
The use_sticky_comment feature is broken in claude-code-action v1.
This adds instructions for Claude to minimize its old comments as
OUTDATED before posting new reviews, preventing comment clutter.
Also adds gh api to allowed tools for the GraphQL mutation.
See: https://github.com/anthropics/claude-code-action/issues/419🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Algo focuses exclusively on Ubuntu LTS. Remove dead code for BSD support
that was never fully implemented:
- Remove roles/common/tasks/bsd_ipv6_facts.yml (unused file)
- Remove BSD handlers (restart ipfw, restart loopback bsd)
- Remove FreeBSD compatibility claim from privacy README
- Remove BSD loopback mock from integration tests
The FAQ entries about OpenBSD are kept as they explain why BSD is NOT
supported (historical context about IPSEC).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Fix DNS IP changing after running update-users
The local_service_ip variable is generated using a deterministic random
formula seeded by algo_server_name + ansible_fqdn. While algo_server_name
is persisted in .config.yml, ansible_fqdn is a runtime fact gathered from
the target host that can change if:
- Server's hostname configuration changes
- DNS resolution changes
- User runs ansible from a different control machine
When ansible_fqdn differs between initial deployment and users.yml, a
different local_service_ip is generated, causing new client configs to
point to a DNS IP that doesn't exist on the server.
This fix persists local_service_ip and local_service_ipv6 in .config.yml
during initial deployment, ensuring users.yml uses the correct DNS IP.
Fixes#14614🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Skip apt upgrade and reboot during update-users
Previously, running `./algo update-users` would trigger a full apt upgrade
and potentially reboot the server because ubuntu.yml was tagged with
`update-users`. This caused unexpected disconnections for all VPN clients
(gaming, downloads, etc.) during what should be a simple user management
operation.
Now only facts.yml runs during update-users, which provides the necessary
password facts for IPsec certificate generation without running apt upgrade
or triggering reboots.
Fixes#14518🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add keys_clean_all support to WireGuard (parity with IPsec)
Previously, WireGuard had no option to force credential regeneration for
existing users. The keys_clean_all option only affected IPsec certificates.
Now WireGuard respects keys_clean_all the same way IPsec does:
- keys_clean_all: false (default) - preserve existing keys, only generate
for new users
- keys_clean_all: true - delete all keys and regenerate for all users
Also improved the config.cfg documentation to clarify this option affects
both WireGuard and IPsec credentials.
Fixes#14610🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add permission check for local deployment update-users
For local deployments, file ownership must be consistent between initial
deployment and subsequent update-users runs. When there's a mismatch
(e.g., initial deployment without sudo, update with sudo), files get
mixed ownership causing permission errors.
This adds a pre-flight check that:
- Detects local deployments (localhost or algo_provider: local)
- Compares config directory owner with current user
- Displays a warning with guidance if there's a mismatch
- Provides the exact chown command to fix permissions
Addresses #14551🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review feedback: explicit directory modes and clearer docs
- Add explicit mode to WireGuard directory creation in main.yml:
- PKI directories (preshared, private, public): 0700
- Config directories (apple/ios, apple/macos): 0755
- Enhance config.cfg keys_clean_all comment to clarify:
- When false: new users added (not just preserved)
- When true: ALL CLIENTS MUST RECONFIGURE (explicit impact warning)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review: fail on permission mismatch, clean up directory creation
1. Permission check now FAILS instead of warning
- Prevents continuing with mismatched permissions
- Provides clear fix command: sudo chown -R <user> configs/<server>/
- Simpler condition: just compare owner with current user
2. Clean separation of directory creation
- main.yml: Only config directories (apple/ios, apple/macos) with 0755
- keys.yml: Only PKI directories (preshared, private, public) with 0700
- Eliminates duplication and clarifies responsibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review: add empty users validation and improve docs
1. Add empty users list validation
- Fails early with clear message if no users defined in config.cfg
- Prevents confusing downstream errors
2. Improve config.cfg keys_clean_all documentation
- Add example use cases: key compromise, removing untrusted users, security audit
3. Rename block for clarity
- "Check local deployment permissions" → "Local deployment permission validation"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix jinja2 spacing lint warning
Remove extra spaces inside brackets in Jinja2 expression per ansible-lint
jinja[spacing] rule.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Remove pyopenssl from core dependencies: community.crypto 3.0.3
removed pyopenssl backend from ALL modules, now uses only
cryptography library
- Remove boto from AWS optional dependencies: Algo only uses
boto3/botocore (library/lightsail_region_facts.py confirms this),
and amazon.aws collection requires only boto3>=1.34.0
Verified by:
- Code search confirming no imports of pyopenssl or old boto
- Runtime tests in isolated environments without these packages
- All 101 unit tests passing
- Ansible playbook syntax checks passing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
When running ./algo update-users, if the server is unreachable, users
previously saw cryptic Ansible SSH retry errors like:
ssh_retry: attempt: 3, ssh return code is 255
This adds a pre-flight check that tests SSH connectivity before running
the full playbook. If the connection fails, it provides a helpful error
message with:
- Clear explanation of what failed
- Possible causes (server not running, IP changed, firewall blocking)
- Diagnostic commands to run (nc, ssh -vvv)
Fixes#14878🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The ansible.utils.ipmath filter is used in config.cfg and WireGuard
templates but the collection was not listed in requirements.yml.
This causes 'No filter named ipmath' errors for users who install
ansible-core separately rather than the full ansible package.
Fixes#14879🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Fix Vultr startup script JSON serialization error
The startup_script module was failing with "Object of type 'bytes' is not
JSON serializable" because the lookup('template', ...) was returning bytes
instead of a string.
Added | string filter to explicitly convert the template result to a
string, matching the pattern used by the DigitalOcean cloud provider.
Also simplified from multiline block format to inline format for
consistency with other cloud providers.
Fixes the error: "Object of type 'bytes' is not JSON serializable by the
'tagless' profile."
* Add string filters to template lookups in cloud-init base template
Added | string filters to lookup() calls in the cloud-init base template
to ensure consistent string handling across all cloud providers.
The Vultr startup_script module requires all values to be JSON-serializable
strings, and lookup() can return bytes in some contexts. This change ensures
that both the SSH config template lookup and the SSH public key file lookup
explicitly return strings.
This is a defensive fix that improves compatibility with strict JSON
serialization requirements in some Ansible modules, while remaining
backward compatible with existing cloud providers.
Related to: vultr.cloud.startup_script JSON serialization requirements
* Use two-step fact assignment for Vultr startup script
Changed to set the cloud-init script as a fact first, then reference
that fact in the startup_script module. This follows the pattern used
by the Linode provider and avoids JSON serialization issues with nested
template lookups.
This approach ensures the template is fully evaluated and stored as a
string before being passed to the vultr.cloud.startup_script module,
which then base64-encodes it for the API.
Related to: JSON serialization with "tagless" profile in Ansible 2.19+
Hetzner deprecated cx11 and cpx11 server types. The smallest available
types are now cx22/cpx22. This fixes compatibility with hcloud 2.11.1.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* "Claude PR Assistant workflow"
* "Claude Code Review workflow"
* Add Algo-specific allowed tools to Claude Code workflows
Enable ansible-lint, ruff, yamllint, shellcheck, and pytest in both
workflows so Claude can run the project's standard linting and testing
tools during reviews and interactive assistance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Enable sticky comments for PR reviews
Updates a single comment instead of posting new comments on each push.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The existing workflows use anthropics/claude-code-action@beta which has
been superseded by v1.0 GA. The v1.0 release introduced breaking changes
that make the current configuration invalid:
- direct_prompt replaced by prompt
- custom_instructions replaced by claude_args: --system-prompt
- allowed_tools replaced by claude_args: --allowedTools
Removing these files to allow fresh installation via /install-github-app
which will create properly configured v1.0 workflows.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The "Format regions" task was converting region dictionaries to strings
using {{ v }} in a Jinja2 loop, which caused the subsequent sort() to
fail with "object of type 'str' has no attribute 'country'".
Changed to directly assign the regions array from the API response,
preserving the dictionary objects with their country, city, id, and
other attributes.
Fixes the error: "object of type 'str' has no attribute 'country'"
when provisioning Vultr servers.
This commit fixes the integration test failures that have been occurring
since Ansible 12 was introduced.
## Root Cause
Dependabot was configured to use the `pip` ecosystem, which only updates
`pyproject.toml` without updating `uv.lock`. This caused the Docker build
to fail because `uv sync --locked` requires an exact match between the
two files.
The lockfile was stuck at ansible 12.0.0 while pyproject.toml had been
bumped to 12.2.0 through multiple Dependabot PRs.
## Changes
1. **Updated uv.lock**: Synced lockfile with current pyproject.toml
- ansible: 12.0.0 → 12.2.0
- ansible-core: 2.19.2 → 2.19.4
2. **Changed Dependabot to use `uv` ecosystem**: This ensures future
dependency updates modify both pyproject.toml AND uv.lock together,
preventing this mismatch from recurring.
## References
- uv Dependabot docs: https://docs.astral.sh/uv/guides/integration/dependency-bots/🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This commit addresses two issues affecting AWS deployments in Ansible 12:
1. **EC2 credentials not being passed correctly**
- The `default()` filter chain wasn't handling empty strings properly
- In Ansible 12 with mandatory jinja2_native, `lookup('env', 'VAR')` returns
an empty string (not undefined) when the env var is not set
- Empty strings are "defined" values, so `default()` doesn't trigger
- Fix: Add `true` as second parameter to `default()` to also handle falsy values
2. **CloudFormation deprecated template parameter**
- The `template` parameter is deprecated (removal after 2026-05-01)
- Migrate to `template_body` with file lookup as recommended
- Applied to both EC2 and Lightsail cloudformation tasks
These changes ensure AWS deployments work correctly with Ansible 12's
stricter type handling while also addressing deprecation warnings.
Fixes#14842🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
In Ansible 12, jinja2_native mode is always enabled, which changes how
JSON strings are handled. The file lookup returns JSON content as a
string, but it's no longer automatically converted to a dict.
This caused GCE deployments to fail with "Invalid resource field value"
because project_id was empty - accessing .project_id on a string returns
undefined, which then defaulted to an empty env var.
Add the `| from_json` filter to explicitly parse the JSON credentials
file into a dict, as recommended by the Ansible 12 porting guide.
Fixes#14854🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Fix Scaleway deployment by replacing broken organization_info module (fixes#14846)
The scaleway_organization_info Ansible module is broken and returns empty
data due to using a deprecated API endpoint (upstream issue
ansible-collections/community.general#3782). This causes deployments to
fail with "'NoneType' object has no attribute 'get'" error.
Changes:
- Replace broken scaleway_organization_info module with user prompt for
Organization/Project ID
- Use Scaleway Marketplace API for image lookup instead of broken
scaleway_image_info module
- Migrate from deprecated 'organization' parameter to modern 'project'
parameter in scaleway_compute calls
- Add support for SCW_DEFAULT_ORGANIZATION_ID environment variable
- Provide clear instructions for finding Organization ID in console
Technical details:
- Scaleway's default project ID equals the organization ID
- Marketplace API is public and doesn't require authentication
- The 'project' parameter was added in community.general 4.3.0
Testing:
- Added 4 new unit tests validating the fix
- All 91 unit tests pass
- Passes ansible-lint, yamllint, ruff, and shellcheck
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Suppress CodeQL false positives in Scaleway tests
The CodeQL scanner flags domain string assertions as "incomplete URL
substring sanitization" but these are test assertions checking that
Ansible role files reference the correct URLs, not URL validation logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Revert "Suppress CodeQL false positives in Scaleway tests"
This reverts commit c75a73e3f5.
---------
Co-authored-by: Claude <noreply@anthropic.com>