feat: liveness probes, recovery strategies, and skip_if rename #488
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUSTFLAGS: "-D warnings" | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| commits: | |
| name: Conventional Commits | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Validate PR commits follow conventional format | |
| run: | | |
| set -e | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| HEAD="${{ github.event.pull_request.head.sha }}" | |
| PATTERN='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+' | |
| FAILED=0 | |
| while IFS= read -r sha; do | |
| MSG=$(git log --format='%s' -1 "$sha") | |
| if ! echo "$MSG" | grep -qE "$PATTERN"; then | |
| echo "❌ $sha: $MSG" | |
| FAILED=1 | |
| else | |
| echo "✅ $sha: $MSG" | |
| fi | |
| done < <(git rev-list "$BASE".."$HEAD") | |
| if [ "$FAILED" -eq 1 ]; then | |
| echo "" | |
| echo "Some commits do not follow conventional commit format." | |
| echo "Expected: type(scope)?: description" | |
| echo "Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert" | |
| exit 1 | |
| fi | |
| website: | |
| name: Website Docker Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build website container | |
| run: docker build -t veld-website website/ | |
| schema: | |
| name: Validate JSON Schema | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Validate schema and configs | |
| run: | | |
| chmod +x tests/validate-schema.sh | |
| ./tests/validate-schema.sh --install | |
| go-test: | |
| name: Go Module Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.22' | |
| - name: Test caddy/inject module | |
| working-directory: caddy/inject | |
| run: go test -v ./... | |
| check: | |
| name: Check & Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: crates/veld-daemon/frontend/package-lock.json | |
| - name: Install frontend dependencies | |
| run: cd crates/veld-daemon/frontend && npm ci | |
| - name: cargo fmt --check | |
| run: cargo fmt --all -- --check | |
| - name: cargo clippy | |
| run: cargo clippy --workspace -- -D warnings | |
| - name: cargo test | |
| run: cargo test --workspace | |
| frontend: | |
| name: Frontend (TypeScript) | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: crates/veld-daemon/frontend | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: crates/veld-daemon/frontend/package-lock.json | |
| - run: npm ci | |
| - name: Type-check | |
| run: npm run typecheck | |
| - name: Test | |
| run: npm test | |
| integration: | |
| name: Integration Tests (macOS) | |
| runs-on: macos-latest | |
| needs: check | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: crates/veld-daemon/frontend/package-lock.json | |
| - name: Install frontend dependencies | |
| run: cd crates/veld-daemon/frontend && npm ci | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.22' | |
| - name: Build all binaries | |
| run: cargo build | |
| # Build Caddy with the veld_inject plugin from this branch. | |
| # install_caddy() just checks the binary exists — no markers needed. | |
| - name: Build Caddy with local veld_inject | |
| run: | | |
| export PATH="$(go env GOPATH)/bin:$PATH" | |
| XCADDY_VERSION=$(cat .xcaddy-version) | |
| go install "github.com/caddyserver/xcaddy/cmd/xcaddy@v${XCADDY_VERSION}" | |
| mkdir -p "$HOME/.local/lib/veld" | |
| xcaddy build \ | |
| --with github.com/prosperity-solutions/veld/caddy/inject=./caddy/inject \ | |
| --output "$HOME/.local/lib/veld/caddy" | |
| chmod +x "$HOME/.local/lib/veld/caddy" | |
| # Stash a copy so we can restore after cleanup steps. | |
| cp "$HOME/.local/lib/veld/caddy" /tmp/caddy-preserve | |
| - name: Verify CLI basics | |
| run: | | |
| ./target/debug/veld --version | |
| ./target/debug/veld --help | |
| - name: Test config parsing (testproject) | |
| working-directory: testproject | |
| run: | | |
| ../target/debug/veld nodes | |
| ../target/debug/veld nodes --json | |
| ../target/debug/veld graph frontend:local | |
| - name: Verify setup subcommands exist | |
| run: | | |
| set -e | |
| ./target/debug/veld setup --help | grep -q "unprivileged" | |
| ./target/debug/veld setup --help | grep -q "privileged" | |
| ./target/debug/veld setup --help | grep -q "hammerspoon" | |
| ./target/debug/veld doctor --help | grep -q "json" | |
| echo "Setup subcommands and doctor verified." | |
| - name: Test Python servers directly | |
| run: | | |
| set -e | |
| python3 testproject/backend/server.py 19555 & | |
| PID_B=$! | |
| python3 testproject/frontend/server.py 19556 & | |
| PID_F=$! | |
| sleep 2 | |
| curl -sf http://localhost:19555/health | |
| curl -sf http://localhost:19555/ | |
| curl -sf http://localhost:19556/ | |
| kill $PID_B $PID_F | |
| wait $PID_B $PID_F 2>/dev/null || true | |
| - name: Run full integration tests | |
| run: | | |
| chmod +x tests/integration.sh | |
| sudo ./tests/integration.sh \ | |
| --veld-bin "$(pwd)/target/debug/veld" \ | |
| --project-dir "$(pwd)/testproject" | |
| - name: "Cleanup after integration tests" | |
| run: | | |
| sudo "$(pwd)/target/debug/veld" uninstall 2>/dev/null || true | |
| curl -s -X POST http://localhost:2019/stop 2>/dev/null || true | |
| sudo rm -rf "$HOME/.veld" "$HOME/.local/lib/veld" /usr/local/lib/veld 2>/dev/null || true | |
| sudo chown -R "$(whoami)" "$HOME/.local" 2>/dev/null || true | |
| sudo launchctl bootout system/dev.veld.helper 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.helper" 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.daemon" 2>/dev/null || true | |
| sleep 2 | |
| # Restore the Caddy binary for subsequent tests. | |
| mkdir -p "$HOME/.local/lib/veld" | |
| cp /tmp/caddy-preserve "$HOME/.local/lib/veld/caddy" | |
| chmod +x "$HOME/.local/lib/veld/caddy" | |
| - name: "Test: unprivileged setup lifecycle" | |
| run: | | |
| set -e | |
| VELD="$(pwd)/target/debug/veld" | |
| echo "=== Unprivileged setup ===" | |
| # Setup may fail on CA trust in CI (no keychain / Caddy timing) — allow partial success | |
| $VELD setup unprivileged || echo "WARNING: setup returned non-zero (CA trust may have failed in CI)" | |
| echo "" | |
| echo "=== Run doctor (diagnostics) ===" | |
| $VELD doctor || echo "WARNING: doctor reported issues (expected in CI — Caddy may not start)" | |
| $VELD doctor --json > /tmp/doctor-unpriv.json 2>/dev/null || true | |
| cat /tmp/doctor-unpriv.json | |
| echo "" | |
| echo "=== Verify setup.json ===" | |
| cat ~/.veld/setup.json | |
| grep -q '"unprivileged"' ~/.veld/setup.json | |
| echo "" | |
| echo "=== Verify helper is running on user socket ===" | |
| test -S ~/.veld/helper.sock | |
| echo "Helper socket OK" | |
| echo "" | |
| echo "=== Check if Caddy is running ===" | |
| CADDY_OK=0 | |
| for i in 1 2 3 4 5; do | |
| if curl -sf http://localhost:2019/id/veld-sentinel 2>/dev/null | grep -q "veld"; then | |
| CADDY_OK=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$CADDY_OK" = "1" ]; then | |
| echo "Caddy is running (sentinel verified)" | |
| curl -sf http://localhost:2019/config/apps/http/servers/veld/listen | grep -q "18443" | |
| echo "Caddy listening on port 18443" | |
| echo "" | |
| echo "=== Start a service in unprivileged mode ===" | |
| cd testproject | |
| $VELD start frontend:local --name unpriv-test | |
| sleep 2 | |
| echo "" | |
| echo "=== Verify URLs have :18443 ===" | |
| $VELD urls --name unpriv-test --json | grep -q "18443" | |
| echo "" | |
| echo "=== Stop ===" | |
| $VELD stop --name unpriv-test | |
| cd .. | |
| else | |
| echo "WARNING: Caddy did not start in CI (timing issue), skipping HTTPS tests" | |
| fi | |
| echo "" | |
| echo "=== Uninstall (unprivileged) ===" | |
| $VELD uninstall || echo "WARNING: uninstall returned non-zero" | |
| echo "" | |
| echo "=== Verify cleanup ===" | |
| sleep 2 | |
| if ! curl -sf http://localhost:2019/config/ >/dev/null 2>&1; then | |
| echo "Caddy stopped OK" | |
| else | |
| echo "WARNING: Caddy still running (may need manual cleanup)" | |
| fi | |
| if [ ! -f ~/.veld/setup.json ]; then | |
| echo "setup.json removed OK" | |
| else | |
| echo "WARNING: setup.json still exists" | |
| fi | |
| echo "" | |
| echo "=== Unprivileged lifecycle PASSED ===" | |
| - name: "Cleanup between lifecycle tests (unpriv → priv)" | |
| if: always() | |
| run: | | |
| curl -s -X POST http://localhost:2019/stop 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.helper" 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.daemon" 2>/dev/null || true | |
| rm -rf "$HOME/.veld" 2>/dev/null || true | |
| rm -f "$HOME/.local/lib/veld/caddy-data" 2>/dev/null || true | |
| # Privileged setup runs as root. sudo on macOS may resolve | |
| # lib_dir() to /usr/local/lib/veld. Ensure Caddy is there too. | |
| sudo mkdir -p /usr/local/lib/veld | |
| sudo cp /tmp/caddy-preserve /usr/local/lib/veld/caddy | |
| sudo chmod +x /usr/local/lib/veld/caddy | |
| sleep 1 | |
| - name: "Test: privileged setup lifecycle" | |
| run: | | |
| set -e | |
| VELD="$(pwd)/target/debug/veld" | |
| echo "=== Privileged setup ===" | |
| sudo $VELD setup privileged | |
| echo "" | |
| echo "=== Run doctor (diagnostics) ===" | |
| $VELD doctor || echo "WARNING: doctor reported issues (expected in CI)" | |
| $VELD doctor --json > /tmp/doctor-priv.json 2>/dev/null || true | |
| cat /tmp/doctor-priv.json | |
| echo "" | |
| echo "=== Verify setup.json ===" | |
| cat ~/.veld/setup.json | |
| grep -q '"privileged"' ~/.veld/setup.json | |
| echo "" | |
| echo "=== Verify helper is running on system socket ===" | |
| test -S /var/run/veld-helper.sock | |
| echo "System socket OK" | |
| echo "" | |
| echo "=== Check Caddy ===" | |
| CADDY_OK=0 | |
| for i in 1 2 3 4 5; do | |
| if curl -sf http://localhost:2019/id/veld-sentinel 2>/dev/null | grep -q "veld"; then | |
| CADDY_OK=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$CADDY_OK" = "1" ]; then | |
| echo "Caddy running (sentinel verified)" | |
| curl -sf http://localhost:2019/config/apps/http/servers/veld/listen | grep -q '":443"' | |
| echo "Caddy on port 443" | |
| echo "" | |
| echo "=== Start a service in privileged mode ===" | |
| cd testproject | |
| if $VELD start frontend:local --name priv-test 2>&1; then | |
| sleep 2 | |
| URLS=$($VELD urls --name priv-test --json 2>/dev/null || true) | |
| if echo "$URLS" | grep -q "18443"; then | |
| echo "FAIL: URLs contain :18443 in privileged mode" | |
| exit 1 | |
| fi | |
| echo "Clean URLs OK" | |
| $VELD stop --name priv-test 2>/dev/null || true | |
| else | |
| echo "WARNING: veld start failed in CI (non-fatal)" | |
| fi | |
| cd .. | |
| else | |
| echo "WARNING: Caddy did not start, skipping service tests" | |
| fi | |
| echo "" | |
| echo "=== Uninstall (privileged) ===" | |
| sudo $VELD uninstall || echo "WARNING: uninstall returned non-zero" | |
| echo "" | |
| echo "=== Verify cleanup ===" | |
| sleep 2 | |
| if ! curl -sf http://localhost:2019/config/ >/dev/null 2>&1; then | |
| echo "Caddy stopped OK" | |
| else | |
| echo "WARNING: Caddy still running" | |
| fi | |
| echo "=== Privileged lifecycle PASSED ===" | |
| - name: "Cleanup between lifecycle tests (priv → auto)" | |
| if: always() | |
| run: | | |
| sudo curl -s -X POST http://localhost:2019/stop 2>/dev/null || true | |
| sudo launchctl bootout system/dev.veld.helper 2>/dev/null || true | |
| sudo rm -rf "$HOME/.veld" "$HOME/.local/lib/veld" /usr/local/lib/veld 2>/dev/null || true | |
| sudo chown -R "$(whoami)" "$HOME/.local" 2>/dev/null || true | |
| sleep 1 | |
| # Restore the Caddy binary for subsequent tests. | |
| mkdir -p "$HOME/.local/lib/veld" | |
| cp /tmp/caddy-preserve "$HOME/.local/lib/veld/caddy" | |
| chmod +x "$HOME/.local/lib/veld/caddy" | |
| - name: "Test: auto-bootstrap (no setup)" | |
| run: | | |
| set -e | |
| VELD="$(pwd)/target/debug/veld" | |
| echo "=== Auto-bootstrap: start without any setup ===" | |
| cd testproject | |
| # Auto-bootstrap may fail in CI due to Caddy startup issues — allow soft failure | |
| if $VELD start frontend:local --name auto-test 2>&1; then | |
| sleep 2 | |
| echo "" | |
| echo "=== Verify it auto-bootstrapped ===" | |
| cat ~/.veld/setup.json | |
| grep -q '"auto"' ~/.veld/setup.json | |
| echo "" | |
| echo "=== Verify URLs have :18443 ===" | |
| $VELD urls --name auto-test --json | grep -q "18443" | |
| echo "" | |
| echo "=== Stop ===" | |
| $VELD stop --name auto-test | |
| else | |
| echo "WARNING: auto-bootstrap veld start failed in CI (Caddy may not start in this environment)" | |
| # Still verify the setup.json was written | |
| if [ -f ~/.veld/setup.json ]; then | |
| grep -q '"auto"' ~/.veld/setup.json && echo "setup.json mode=auto OK" | |
| fi | |
| fi | |
| cd .. | |
| echo "" | |
| echo "=== Cleanup auto-bootstrap state ===" | |
| curl -s http://localhost:2019/stop >/dev/null 2>&1 || true | |
| rm -rf "$HOME/.veld" | |
| sleep 1 | |
| echo "=== Auto-bootstrap PASSED ===" | |
| # ── Release build matrix — catch build failures before merge ────── | |
| release-build: | |
| name: Release Build (${{ matrix.suffix }}) | |
| runs-on: ${{ matrix.os }} | |
| needs: check | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: aarch64-apple-darwin | |
| os: macos-latest | |
| suffix: macos-arm64 | |
| goos: darwin | |
| goarch: arm64 | |
| - target: x86_64-apple-darwin | |
| os: macos-latest | |
| suffix: macos-amd64 | |
| goos: darwin | |
| goarch: amd64 | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| suffix: linux-amd64 | |
| goos: linux | |
| goarch: amd64 | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| suffix: linux-arm64 | |
| cross: true | |
| goos: linux | |
| goarch: arm64 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: crates/veld-daemon/frontend/package-lock.json | |
| - name: Install frontend dependencies | |
| run: cd crates/veld-daemon/frontend && npm ci | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.22' | |
| - name: Install cross-compilation tools | |
| if: matrix.cross | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml | |
| echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml | |
| - name: Build release binaries | |
| run: cargo build --release --target ${{ matrix.target }} | |
| - name: Build Caddy with veld_inject | |
| run: | | |
| export PATH="$(go env GOPATH)/bin:$PATH" | |
| XCADDY_VERSION=$(cat .xcaddy-version) | |
| go install "github.com/caddyserver/xcaddy/cmd/xcaddy@v${XCADDY_VERSION}" | |
| GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} xcaddy build \ | |
| --with github.com/prosperity-solutions/veld/caddy/inject=./caddy/inject \ | |
| --output ./caddy-build-test | |
| rm -f ./caddy-build-test | |
| injection-test: | |
| name: Injection E2E (macOS) | |
| runs-on: macos-latest | |
| needs: check | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: crates/veld-daemon/frontend/package-lock.json | |
| - name: Install frontend dependencies | |
| run: cd crates/veld-daemon/frontend && npm ci | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.22' | |
| - name: Build veld | |
| run: cargo build | |
| - name: Install Next.js deps | |
| working-directory: testproject-nextjs | |
| run: npm install | |
| - name: Build Caddy with local veld_inject | |
| run: | | |
| export PATH="$(go env GOPATH)/bin:$PATH" | |
| XCADDY_VERSION=$(cat .xcaddy-version) | |
| go install "github.com/caddyserver/xcaddy/cmd/xcaddy@v${XCADDY_VERSION}" | |
| mkdir -p "$HOME/.local/lib/veld" | |
| xcaddy build \ | |
| --with github.com/prosperity-solutions/veld/caddy/inject=./caddy/inject \ | |
| --output "$HOME/.local/lib/veld/caddy" | |
| chmod +x "$HOME/.local/lib/veld/caddy" | |
| - name: Setup veld (unprivileged) | |
| run: | | |
| ./target/debug/veld setup unprivileged || echo "WARNING: setup returned non-zero (CA trust may have failed)" | |
| - name: Wait for Caddy | |
| run: | | |
| for i in $(seq 1 15); do | |
| if curl -sf http://localhost:2019/id/veld-sentinel 2>/dev/null | grep -q "veld"; then | |
| echo "Caddy is running" | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| - name: Run injection E2E tests | |
| run: | | |
| chmod +x tests/test-injection.sh | |
| ./tests/test-injection.sh \ | |
| --veld-bin "$(pwd)/target/debug/veld" \ | |
| --project-dir "$(pwd)/testproject-nextjs" | |
| - name: Cleanup | |
| if: always() | |
| run: | | |
| ./target/debug/veld uninstall 2>/dev/null || true | |
| curl -s -X POST http://localhost:2019/stop 2>/dev/null || true | |
| rm -rf "$HOME/.veld" "$HOME/.local/lib/veld" 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.helper" 2>/dev/null || true | |
| launchctl bootout "gui/$(id -u)/dev.veld.daemon" 2>/dev/null || true |