* Add E2E lifecycle test for create/destroy workflow
Bash script that exercises the full droplet lifecycle: create (no
Tailscale), verify SSH config, run commands over SSH, destroy, and
verify cleanup
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dm <alexis.challande@trailofbits.com>
* Update default Ubuntu image from 25.04 to 25.10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Extract default region/size/image slugs into constants
Replace duplicated hardcoded strings across config.py, main.py, and
tests with DEFAULT_REGION, DEFAULT_SIZE, and DEFAULT_IMAGE constants
defined in config.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Remove escaped chars in comments
* Add Jinja2 template validation test and pre-commit hook
Catch template syntax errors early with a unit test that parses and
renders the default cloud-init template, and a pre-commit hook that
validates all templates in dropkit/templates/ on change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* allow resizing hibernated droplets by modifying the size tag
* Prevent removal of protected tags (owner, firewall) in untag_resource
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix macOS Tailscale CLI discovery for App Store installs
On macOS, Tailscale installed via the App Store places its CLI at
/Applications/Tailscale.app/Contents/MacOS/Tailscale, which is not in
PATH. Add find_tailscale_cli() helper that checks PATH first, then
falls back to the macOS App Store location on darwin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address PR feedback: better error message, remove excessive test
Distinguish "Tailscale not found" from "not running" when the local
check fails, so users know whether to install or start Tailscale.
Remove the overly comprehensive binary_vanishes race condition test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Generalize email domain handling in documentation and tests
- Add .worktrees/ to .gitignore for git worktree support
- Update _sanitize_email_for_username docstring to be domain-agnostic
- Change CLAUDE.md example from trailofbits.com to example.com
- Add tests for google.com, gmail.com, corporate, and plus addressing
The code already supported any email domain; this updates docs and
tests to reflect that capability.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address PR review comments
- Remove redundant trailofbits.com regex, simplify to split("@")[0]
- Rename test to clarify backwards compatibility intent
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Dan Guido <dan@trailofbits.com>
Replace manual file parsing with ssh-keygen -R which properly handles
both hashed (|1|...) and unhashed entries. macOS and many Linux systems
use HashKnownHosts by default, making the previous implementation unable
to remove entries.
Changes:
- Use ssh-keygen -R for each hostname instead of parsing the file
- Detect successful removal via "updated" in stdout
- Backup files are now .old (ssh-keygen default) instead of .bak
- Bracketed entries ([host]:port) now require exact format
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Rename tobcloud references to dropkit in tests
Update test data and test method names to use the new project name.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Format test_ssh_config.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>