Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

16 Commits

Repository files navigation

DDS — Daedalus Deployment System

"He who would learn to fly one day must first learn to stand and walk and run and climb and dance; one cannot fly into flying."

Version: 0.6.0 · License: MIT · Python: 3.10+

Platform-agnostic deployment tooling with a provider abstraction layer. Ships with Azure, Docker/SSH, and a Custom provider that lets you define shell commands in dds.yaml — deploy to Kubernetes, Fly.io, Railway, Dokku, bare metal, or anything else without writing Python. One config file, one CLI, every environment, any cloud.


Why DDS?

Shell scripts aren't cross-platform, testable, or maintainable at scale. DDS replaces project-embedded bash deploy scripts with a proper standalone tool:

  • One dds.yaml per project — declares all services, environments, and secrets
  • Preflight checks before every deploy — catches auth, tooling, and access issues early
  • Health verification after every deploy — auto-checks endpoints, suggests rollback on failure
  • Rollback in one command — revert to any previous Container App revision instantly
  • Secrets from anywhere — Azure Key Vault, .env files, environment variables, inline config
  • No CI required — designed for direct deploys with immediate feedback (ACR remote builds, no local Docker needed)

Installation

# From source (recommended during alpha)
pip install -e ".[dev]"# Future: PyPI
pip install dds-deploy
pipx install dds-deploy

Prerequisites

ToolRequiredNotes
Python 3.10+Runtime
Provider CLIaz for Azure, aws for AWS, gcloud for GCP
GitUsed for image tagging
DockerOnly needed for build_strategy: local

Run dds preflight to verify everything is ready.


Quick Start

# 1. Initialize a config file
dds init
# 2. Edit dds.yaml for your project (see Configuration below)# 3. Check prerequisites
dds preflight
# 4. Deploy everything to dev
dds deploy dev
# 5. Deploy a specific service
dds deploy dev -s api
# 6. Preview without executing
dds deploy dev --dry-run

Commands

dds deploy <environment>

Build, push, and deploy services. Runs preflight checks before and health verification after.

dds deploy dev # Deploy all services
dds deploy dev -s api # Deploy only 'api'
dds deploy dev -s api -s worker # Deploy multiple services
dds deploy dev --dry-run # Preview actions
dds deploy dev --skip-preflight # Skip pre-deploy checks
dds deploy dev --skip-health # Skip post-deploy verification

dds status <environment>

Show current deployment status for all services in an environment.

dds status dev

dds preflight

Validate prerequisites without deploying. Checks Azure CLI, Git, Docker, login status, and ACR access.

dds preflight

dds rollback <environment> -s <service>

Revert a Container App to a previous revision. Activates the target revision, redirects 100% traffic, and deactivates the old one.

dds rollback dev -s api # Roll back to previous revision
dds rollback dev -s api -r rev-abc # Roll back to a specific revision

dds revisions <environment> -s <service>

Show revision history for a Container App — images, traffic weights, health state, creation times.

dds revisions dev -s api

dds logs <environment> -s <service>

Tail or stream logs from a Container App.

dds logs dev -s api # Last 100 lines
dds logs dev -s api -f # Follow/stream in real-time
dds logs dev -s api -n 50 # Last 50 lines
dds logs dev -s api --system # Platform/system logs

dds health <environment> -s <service>

Run health checks on a deployed service. Verifies running state and hits the configured health_path endpoint.

dds health dev -s api

dds init

Create a dds.yaml template in the current directory.

dds init

Global Options

dds --version # Show version
dds -c path/to/dds.yaml # Use a custom config path
dds -v deploy dev # Verbose output

Configuration

DDS is configured via a dds.yaml file in your project root.

# DDS — Daedalus Deployment Systemproject: my-project# provider: azure # Cloud provider (azure|aws|gcp|docker) — default: azureregistry: myregistry.azurecr.io# key_vault: my-shared-keyvault # Optional: project-wide Key Vaultenvironments:
dev:
resource_group: my-dev-rgcontainer_env: my-dev-env# key_vault: my-dev-keyvault # Optional: env-level Key Vault# env_file: .env.dev # Optional: load vars from .env fileservices:
api:
type: container-appname: dev-apidockerfile: api/Dockerfilecontext: .build_strategy: acr # acr (default) or localport: 3000min_replicas: 1max_replicas: 3health_path: /healthbuild_args: # Build-time args (CACHE_BUST + GIT_HASH auto-added)NODE_ENV: productionenv: # Runtime environment variablesPUBLIC_URL: https://api.example.comsecrets: # Secrets from Key Vault or env vars
- name: DATABASE_URLvault_key: db-connection-string
- name: API_KEYenv: MY_LOCAL_API_KEYapp:
type: static-sitestorage_account: mydevstoragebuild_cmd: npm run buildbuild_dir: app/dist# install_deps: true # Auto-install node deps (default: true)env: # Build-time env (NEXT_PUBLIC_*, VITE_*, etc.)NEXT_PUBLIC_API_URL: https://api.example.comdb:
type: databaseserver: my-postgres-serverdatabase: mydb# charset: UTF8 # Default# collation: en_US.utf8 # Default

Docker/SSH Provider Example

project: my-forgeprovider: dockerhost: my-server # SSH host (from ~/.ssh/config or IP)environments:
prod:
project_dir: /opt/forge # Remote dir with docker-compose.ymlservices:
api:
type: container-appcompose_service: api # Service name in docker-compose.ymlhealth_path: /healthport: 3000web:
type: static-sitebuild_cmd: npm run buildbuild_dir: distremote_path: /var/www/mysitedb:
type: databasecontainer: postgres # Docker container namedatabase: mydb

Custom Provider Example (Kubernetes)

provider: customcommands:
container-app:
build: "docker build -t {registry}/{service}:{git_hash} ."deploy: "kubectl set image deployment/{service} {service}={registry}/{service}:{git_hash}"rollback: "kubectl rollout undo deployment/{service}"logs: "kubectl logs deployment/{service} --tail={tail} {follow_flag}"health: "kubectl rollout status deployment/{service} --timeout=60s"status: "kubectl get deployment/{service} -o wide"revisions: "kubectl rollout history deployment/{service}"static-site:
build: "npm run build"deploy: "aws s3 sync {build_dir} s3://{bucket}/ --delete"preflight:
checks:
- "kubectl cluster-info"
- "docker version"environments:
prod:
registry: myregistry.iobucket: my-prod-bucketservices:
api:
type: container-appweb:
type: static-sitebuild_dir: dist

Any config key becomes an interpolation variable: {host}, {port}, {registry}, {compose_file}, {bucket} — whatever you put in dds.yaml, you can use in commands. Git info ({git_hash}, {git_branch}, {build_time}) is always available.

Service Types

TypeAzureDocker/SSHCustom
container-appContainer Apps (ACR build)docker compose build/up over SSHYour commands
static-siteBlob Storage $webrsync/scp to remote web rootYour commands
swaStatic Web Apps❌ Not supported❌ Not supported
databasePostgres Flexible Serverdocker exec into Postgres containerYour commands

Build Strategies (Container Apps)

StrategyDocker RequiredDescription
acr (default)NoRemote build on Azure Container Registry
localYesLocal Docker build, then push to ACR

Secrets Resolution

Secrets resolve in priority layers (later layers override earlier):

  1. env_file — Load from a .env file (environment-level)
  2. env — Inline key-value pairs (service-level)
  3. secrets — Azure Key Vault (vault_key) or local environment variables (env)

Architecture

dds/
├── dds/
│ ├── __init__.py # Version (0.4.0)
│ ├── cli.py # Click CLI — 8 commands
│ ├── config.py # dds.yaml loader + template generator
│ ├── context.py # DeployContext dataclass (with provider resolution)
│ ├── preflight.py # Generic + provider-delegated pre-deploy validation
│ ├── secrets.py # Secret resolution (provider vaults, .env, env vars)
│ ├── health.py # Backward-compat wrapper → provider.health()
│ ├── rollback.py # Backward-compat wrapper → provider.rollback()
│ ├── logs.py # Backward-compat wrapper → provider.logs()
│ ├── providers/
│ │ ├── __init__.py # Provider registry + factory
│ │ ├── base.py # Abstract base classes (ContainerProvider, etc.)
│ │ ├── azure/ # Azure provider
│ │ │ ├── container.py # Container Apps (build, deploy, rollback, logs, health)
│ │ │ ├── static.py # Blob Storage static sites
│ │ │ ├── swa.py # Static Web Apps
│ │ │ ├── database.py # Postgres Flexible Server
│ │ │ ├── secrets.py # Key Vault
│ │ │ ├── preflight.py # az login, ACR access checks
│ │ │ └── utils.py # az(), az_json() wrappers
│ │ ├── docker/ # Docker/SSH provider
│ │ │ ├── container.py # docker compose over SSH (build, deploy, logs, health)
│ │ │ ├── static.py # rsync/scp to remote host
│ │ │ ├── database.py # docker exec into Postgres containers
│ │ │ ├── secrets.py # .env file reader
│ │ │ ├── preflight.py # SSH + remote Docker checks
│ │ │ └── utils.py # ssh() wrapper, host/compose resolution
│ │ └── custom/ # Config-driven command template provider
│ │ ├── template.py # SafeFormatter, interpolation, variable builder
│ │ ├── container.py # Command-template container lifecycle
│ │ ├── static.py # Command-template static site
│ │ ├── database.py # Command-template database provisioning
│ │ ├── secrets.py # Command-template or .env fallback
│ │ └── preflight.py # Run user-defined check commands
│ ├── deployers/
│ │ ├── __init__.py # Dispatch → resolves provider + routes
│ │ ├── container.py # Backward-compat shim
│ │ ├── static.py # Backward-compat shim
│ │ ├── swa.py # Backward-compat shim
│ │ └── database.py # Backward-compat shim
│ ├── builders/
│ │ ├── __init__.py
│ │ ├── docker.py # Generic Docker build/push (local)
│ │ └── frontend.py # Frontend builds (npm/pnpm/yarn/bun auto-detect)
│ └── utils/
│ ├── __init__.py
│ ├── shell.py # Generic subprocess runner (run_cmd)
│ ├── azure.py # Backward-compat re-exports
│ └── git.py # Git info (hash, branch, build time)
├── tests/ # 40 tests
│ ├── test_builders.py
│ ├── test_cli.py
│ ├── test_config.py
│ ├── test_deployers.py
│ ├── test_preflight.py
│ └── test_secrets.py
├── pyproject.toml # Hatchling build, Click + Rich + PyYAML deps
├── LICENSE # MIT
└── README.md

Design Principles

  • Provider abstraction — cloud-specific logic lives in providers/<name>/; adding a new cloud means implementing the base classes, not touching core. Ships with azure, docker, and custom providers.
  • No CI dependency — deploys work from any terminal with the provider CLI and git
  • Remote-first builds — ACR remote builds for Azure, docker compose build over SSH for Docker, extensible per provider
  • Preflight before, health after — catch problems on both ends of a deploy
  • Auto-injectionCACHE_BUST and GIT_HASH build args added automatically
  • Package manager detection — lockfile-based (pnpm-lock.yaml, yarn.lock, bun.lockb, or npm default)

Development

# Clone
git clone git@github.com:digitalforgeca/dds.git
cd dds
# Install with dev deps
pip install -e ".[dev]"# Run tests
pytest tests/ -v
# Lint
ruff check dds/ tests/

Origin

DDS was born from the TL4C project's deploy/scripts/dds — a 764-line bash script that grew organically. This is a clean-room rewrite as a standalone, testable, cross-platform tool.

The TL4C embedded DDS continues to work independently. This project does not modify or replace it.


Changelog

See CHANGELOG.md for version history.

License

MIT — Digital Forge Studios · 2026

About

Daedalus Deployment System — cross-platform deployment tooling for Azure Container Apps, static sites, and managed services.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages