Skip to content

Release v0.3.2 - #244

Merged
vsilent merged 77 commits into
mainfrom
dev
Aug 26, 2026
Merged

Release v0.3.2#244
vsilent merged 77 commits into
mainfrom
dev

Conversation

@vsilent

@vsilent vsilent commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Release v0.3.2 — merges dev into main.

Highlights (see CHANGELOG.md [0.3.2] for the full list)

Added

  • stacker monitor — container-down alarm + basic scheduler. Configured via a new monitoring.alerts block (terminal / webhook / pipe target); edge-triggered, cron-friendly (--once). Alarm engine lives in a new dependency-free health-monitor crate.
  • Declarative pipes (IaC)pipes: block in stacker.yml, reconciled by stacker pipe diff (preview) and stacker pipe apply [--prune] [--dry-run] (create/prune, idempotent).
  • Manual pipe endpoints — non-interactive pipe create --source-endpoint/--target-endpoint/--source-fields/--target-fields/--name.
  • Pipe resiliencepipe create --retry/--retry-backoff-ms/--retry-backoff-max-ms/--on-failure/--on-success.
  • Reverse-proxy routing — Traefik (labels), Caddy (Caddyfile), Nginx Proxy Manager (proxy hosts), platform-managed and driven by proxy.domains end-to-end; local Caddyfile generation.

Fixed

  • W003 proxy/service ingress port-conflict warning; W001 host-port comparison bug.
  • SSH backup key saved during provisioning (no longer lost on interrupted watch).
  • Remote-compose orphan named-volume cleanup when stripping a platform proxy.

Also folds in the previously-unreleased entries (deployment aliases & --pinned, --from-github init, onboarding helpers).

Docs

  • CHANGELOG.md, README.md, docs/STACKER_YML_REFERENCE.md (monitoring.alerts, pipes:, W003, new CLI commands), docs/PIPING.md — all updated (additive).

Verification

  • Full lib suite + health-monitor crate tests green.
  • stacker monitor, pipe diff/apply --prune, manual-endpoint pipes, and all three proxy types verified live.

🤖 Generated with Claude Code

robotizeit and others added 30 commits August 14, 2026 21:51
- clone.rs: generate deployment_hash, create Project + Deployment records,
  generate SSH keypair, register on Hetzner, return hash + private key
- hetzner.rs: add add_ssh_key() to trait + client
- install_service: add post_deploy_clone() to trait, client, mock
…r_type_daily_rate

- DB migration: daily_rate/monthly_cap on stack_template,
  billing tracking columns on marketplace_install_authorization,
  server_type_daily_rate table with Hetzner cost × 1.5 pricing
- clone.rs: authorize daily_rate via user_service before server
  creation, store authorization, return authorization_id
- marketplace_billing.rs: daily sweep candidates, mark_daily_charged,
  mark_server_deleted, mark_suspended helpers
- marketplace.rs: get_approved_by_slug for one-click template lookup
- models: StackTemplate.daily_rate/monthly_cap, ServerTypeDailyRate
  with calculate_daily_rate formula
- Daily charge sweep: charge daily_rate from captured authorizations
- Monthly cap check: skip when total_charged >= monthly_cap
- Grace period: suspend after 24h of failed charges
- Auto-delete: void after 3 days of suspension
- Server deletion: void remaining hold when server_deleted_at set
- Marks authorization as server_deleted on delete
- Voids remaining hold via user_service void_install_charge
- Injects UserServiceConnector into delete endpoint
- connector trait: add daily_capture_install_charge method
- client.rs: implement daily_capture_install_charge (calls /daily-capture)
- mock.rs: add mock implementation
- sweeper: use daily_capture_install_charge for deployment_daily charges
Read the user's SSH public key from deploy.cloud.ssh_key (.pub file)
and include it in the deploy form as server.additional_public_keys.
The server-side merges these with the Vault-managed key so the Install
Service installs all keys in authorized_keys on the cloud VM.

- Add additional_public_keys field to ServerForm
- build_deploy_form() reads .pub file from deploy.cloud.ssh_key
- execute_deployment() merges additional keys into new_public_key
- Make resolve_ssh_key_path() pub(crate) for reuse
Add a Docker pre-flight check in deploy_to_intranet_server() before
rsyncing project files. If Docker or Docker Compose is not installed,
abort immediately with an actionable error message showing the exact
SSH command to install Docker.
Move persist_proxy_config_to_stacker_yml() before the agent command
so the local config is always saved, even when the remote NPM
configuration fails (e.g., Vault unavailable). The agent failure is
now a warning instead of a hard error.
…failure (#222)

#221: choose_server_for_project now accepts force_new flag. When true,
sorts servers by id (descending) and picks the newest, avoiding stale
server from a previous deploy being written to deployment-cloud.lock.

#222: fetch_server_for_project now tracks deployment_failed status.
When the deployment ends with 'failed' or 'error', the IP retry loop
runs only once instead of 6 times — no point waiting for an IP when
provisioning failed.
Hooks that reference application binaries only make sense for local
deploys. For cloud/server deploys, the app runs on the remote host —
running the hook locally would fail with "command not found" and is a
security concern (arbitrary remote execution).

- pre_build hook still runs locally (builds happen locally)
- post_deploy hook skipped for Cloud/Server with info message
- on_failure hook skipped for Cloud/Server
Remove from .gitignore so CI always has the correct RBAC policy file.
Panic if missing instead of falling back to .dist (security risk).
Use serde_yaml::Value for surgical updates to the services array
instead of re-serializing the entire StackerConfig struct. This
prevents null injection for Option fields and preserves config_contract.

Fixes #215
- marketplace_webhook.rs: add daily_rate/monthly_cap to MarketplaceWebhookPayload
- marketplace_access.rs: add deployment_hash to daily_capture_install_charge mock
- marketplace_integration.rs: add daily_rate/monthly_cap to StackTemplate fixtures
- Added local_compose_project_name(config) — derives the name from project.identity (falling back to name), sanitized the same way stack codes are elsewhere in this file.
- LocalDeploy::deploy and LocalDeploy::destroy now pass -p <project_name> to docker compose up/down.
- Threaded the same project name through the port-conflict preflight helpers (check_local_host_port_conflicts, get_own_compose_running_ports) so the docker compose ps probe used to distinguish "our own container" from "external conflict" queries the correctly-namespaced project too — otherwise it would silently keep checking the old shared "stacker" project.
…unts (paths starting with .///~, which Compose never declares at the top level), collect the distinct named volumes actually referenced across services, and render them under a top-level volumes: block — omitted entirely when none exist.
All server deploys previously wrote to /home/trydirect/project/,
causing cross-project damage. Each project now gets its own directory
/home/trydirect/{stack_code}/.

Changes:
- env_path.rs: Add dynamic _for(stack_code) path functions
- payload.rs: Add stack_code field to deploy Payload
- install_service/client.rs: Set stack_code from project name
- deployment_state.rs: Use dynamic paths, add stack_code field
- config_renderer.rs: Use dynamic paths for .env destinations
- explain.rs (CLI, services, MCP): Use dynamic paths
- TFA roles/custom: Parameterize stack_source via stack_code
- TFA post_clone_setup.yml: Use stack_code in dest path
- Contract tests: Update fixtures with per-project paths

Also includes:
- fix(#233): Prevent --force-new + --force-rebuild together
- fix(#215): Surgical YAML updates for service import/proxy add
- fix(#227): Skip post_deploy hooks for remote deploys
Code review findings:
- Fix build break: config_renderer test still called removed remote_runtime_env_path()
- Reuse sanitize_project_name instead of duplicate sanitize_stack_code
- Use sanitized name in deployment_state.rs compose/env paths
- Use sanitized name in explain.rs (CLI + MCP) remote paths
- Use sanitized name in config_renderer.rs Vault .env destination

All remote paths now go through sanitize_project_name() which lowercases
and replaces invalid chars with underscores, matching what Ansible sees.
project.name has no DB uniqueness constraint, so two projects with
the same sanitized name (e.g. 'My App' and 'my-app') would collide.
Now uses '{sanitized_name}-{id}' pattern.

Also fixes residual unsanitized config.name in config show --resolved.
config_renderer.rs Vault .env destination and mcp/tools/explain.rs
remote paths now use {sanitized_name}-{id} matching the actual
remote directory computed by install_service/client.rs.
- cli_deployment_state.rs: mock response uses remote-project-17 path
- cli_config.rs: expects resolved-test path instead of hardcoded project
- mcp.feature: partial match on path prefix (project id is dynamic)
…cli/deploy.rs, checked before is_private_host — skips the auto-switch entirely when the host is still a template string. 2 new tests
- console/commands/cli/destroy.rs: build_destroy_args now takes -p <project_name>, resolved from stacker.yml via a new shared local_compose::resolve_local_compose_project_name.
- console/commands/cli/status.rs: same fix for docker compose ps — stacker status could previously report another project's containers under the shared scope.
- Extracted the project-name resolution (config → local_compose_project_name → fallback) into one shared helper in local_compose.rs, reused by deploy, destroy, and status instead of three separate implementations.
- local_compose_project_name promoted to pub so it's reusable outside install_runner.rs.
…expired tokens now auto-refresh transparently using the stored refresh_token, with no stacker login re-run required
…terminal alert target (crate::cli::notify::notify_message — OS notification + terminal bell + stderr), so the monitor can notify in-terminal with no external webhook.
@vsilent
vsilent merged commit 84c515c into main Aug 26, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants