Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: E2E

on:
schedule:
# Every Monday at 06:00 UTC
- cron: "0 6 * * 1"
workflow_dispatch: # Allow manual triggers

permissions:
contents: read

jobs:
lifecycle:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.13

- name: Install dependencies
run: uv sync

- name: Set up SSH key
run: |
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "${{ secrets.E2E_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
touch ~/.ssh/config && chmod 600 ~/.ssh/config

- name: Set up dropkit config
run: |
mkdir -p ~/.config/dropkit
echo "${{ secrets.E2E_DROPKIT_CONFIG }}" > ~/.config/dropkit/config.yaml
chmod 600 ~/.config/dropkit/config.yaml

- name: Run E2E lifecycle test
run: ./tests/e2e/test_lifecycle.sh
env:
DROPLET_SIZE: s-1vcpu-1gb
timeout-minutes: 15
16 changes: 16 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Pre-configured cloud-init, Tailscale VPN (enabled by default), and SSH config ma
- **Never use `pip`** — always use `uv` for all Python operations
- **Always run `prek run`** before committing (or `prek install` to auto-run on commit)
- **Keep README.md in sync** when adding commands or features
- **Run E2E tests before pushing** changes that affect core workflows (create, destroy, SSH config, cloud-init, Tailscale)

## Quick Commands

Expand All @@ -26,6 +27,7 @@ dropkit/
├── dropkit/ # CLI source (Typer entry point: main.py)
│ └── templates/ # Jinja2 cloud-init templates
└── tests/ # pytest tests
└── e2e/
```

## Technology Stack
Expand Down Expand Up @@ -140,6 +142,20 @@ uv run pytest -v # Verbose

**Coverage**: Minimum 29% enforced via `--cov-fail-under=29` in pyproject.toml.

### E2E Testing

The E2E lifecycle test creates a real droplet, verifies SSH connectivity,
and destroys it. **Run before pushing changes that affect core workflows**
(create, destroy, SSH config, cloud-init, Tailscale).

```bash
./tests/e2e/test_lifecycle.sh
```

Requires a valid dropkit config (`~/.config/dropkit/config.yaml`).
Optional environment variables: `DROPLET_NAME`, `DROPLET_REGION`,
`DROPLET_SIZE`, `DROPLET_IMAGE`, `E2E_SSH_TIMEOUT`.

## Pydantic Models

- **`DropkitConfig`** — Root config with `extra='forbid'`
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: help dev lint format test audit
.PHONY: help dev lint format test e2e audit

help: ## Show available targets
@grep -E '^[a-zA-Z_-]+:.*##' $(MAKEFILE_LIST) | awk -F ':.*## ' '{printf " %-12s %s\n", $$1, $$2}'
@grep -E '^[a-zA-Z0-9_-]+:.*##' $(MAKEFILE_LIST) | awk -F ':.*## ' '{printf " %-12s %s\n", $$1, $$2}'

dev: ## Install all dependencies
uv sync --all-groups
Expand All @@ -15,5 +15,8 @@ format: ## Auto-format code
test: ## Run tests
uv run pytest

e2e: ## Run E2E lifecycle test (creates a real droplet)
./tests/e2e/test_lifecycle.sh

audit: ## Audit dependencies for vulnerabilities
uv run pip-audit
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,29 @@ The droplet might belong to someone else. List your droplets:
dropkit list
```

## Development

### Running Unit Tests

```bash
uv run pytest # All tests
uv run pytest -v # Verbose output
uv run pytest -k "pattern" # Filter by name
```

### Running E2E Tests

The E2E lifecycle test creates a real droplet, verifies SSH connectivity,
and destroys it. Run before pushing changes that affect core workflows.

```bash
./tests/e2e/test_lifecycle.sh
```

Requires a valid dropkit config (`~/.config/dropkit/config.yaml`).
Optional environment variables: `DROPLET_NAME`, `DROPLET_REGION`,
`DROPLET_SIZE`, `DROPLET_IMAGE`, `E2E_SSH_TIMEOUT`.

## Technology Stack

- **CLI Framework**: [Typer](https://typer.tiangolo.com/) - Modern CLI framework
Expand Down
2 changes: 1 addition & 1 deletion dropkit/templates/default-cloud-init.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ write_files:
defer: true
content: |
# Prompt: user@host:dir (green for normal, red after failed command)
PROMPT='%F{%(?.green.red)}%n@%m%f:%F{blue}%~%f$ '
{% raw %}PROMPT='%F{%(?.green.red)}%n@%m%f:%F{blue}%~%f$ '{% endraw %}

# History
HISTFILE=~/.zsh_history
Expand Down
Loading