aayi 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
aayi-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.3
2
+ Name: aayi
3
+ Version: 0.1.1
4
+ Summary: Deploy Docker apps to any server with zero downtime and automatic HTTPS
5
+ Author: Ahmed
6
+ Author-email: Ahmed <thlurte@gmail.com>
7
+ Requires-Dist: asyncssh>=2.14.0
8
+ Requires-Dist: httpx>=0.27.0
9
+ Requires-Dist: jinja2>=3.1.0
10
+ Requires-Dist: pydantic>=2.7.0
11
+ Requires-Dist: pyyaml>=6.0.1
12
+ Requires-Dist: rich>=13.7.0
13
+ Requires-Dist: typer>=0.12.0
14
+ Requires-Python: >=3.13
15
+ Description-Content-Type: text/markdown
16
+
17
+ <p align="center">
18
+ <img src="assets/banner.jpeg" alt="Aayi - Modern Cloud Deployment Dev Tool" width="100%">
19
+ </p>
20
+
21
+ # Aayi
22
+
23
+ Deploy Docker apps to any server with zero downtime and automatic HTTPS.
24
+
25
+ Built with uv, AsyncSSH, Typer, Pydantic, and Caddy.
26
+
27
+ ---
28
+
29
+ ## Features
30
+
31
+ - **Zero-Downtime Container Swaps**: Routes traffic between container revisions via Caddy's dynamic in-memory Admin API (`:2019`).
32
+ - **Automatic HTTPS**: Full TLS certificate lifecycle via Let's Encrypt / ZeroSSL powered by Caddy.
33
+ - **Docker Compose & Multi-Container**: Deploy multi-service applications directly from `docker-compose.yml` or multi-service `deploy.yml`.
34
+ - **Public vs. Private Containers**:
35
+ - **Public**: Dedicated domain per container (e.g. `app.example.com` and `api.example.com`) with automatic Caddy TLS.
36
+ - **Private**: Background workers, databases, and microservices connected to internal `aayi-net` with no host port exposure.
37
+ - **Concurrent SSH Execution**: Asynchronous, parallel multi-server operations powered by AsyncSSH.
38
+ - **Type-Safe Declarative Config**: Strict validation of `config/deploy.yml` with friendly Pydantic v2 error messages.
39
+ - **GitHub Actions Ready**: Out-of-the-box CI/CD generator (`aayi ci init`).
40
+ - **No Ruby Required**: Built for developers who want Kamal's simplicity without installing Ruby or gem bundlers.
41
+ - **Stateful Accessories**: Spin up and manage PostgreSQL, Redis, or MySQL containers with persistent Docker volumes.
42
+ - **Instant Rollback**: Revert Caddy upstreams back to the prior container in milliseconds with `aayi rollback`.
43
+
44
+ ---
45
+
46
+ ## Architecture
47
+
48
+ ```
49
+ [ CLI: aayi ]
50
+ (Typer + Rich UI)
51
+ │
52
+ [ Config: deploy.yml ]
53
+ (Pydantic v2 Models)
54
+ │
55
+ ┌────────────────┴────────────────┐
56
+ ▼ ▼
57
+ [ Local Builder ] [ AsyncSSH Fleet ]
58
+ (docker buildx push) (Parallel Multi-Server Pool)
59
+ │
60
+ ┌─────────────────────────┴─────────────────────────┐
61
+ ▼ ▼
62
+ [ Web Server(s) ] [ Worker Server(s) ]
63
+ ┌───────────────────┐ ┌────────────────────┐
64
+ │ Caddy (Port 80/443│ │ Worker Container │
65
+ │ Admin API :2019) │ └────────────────────┘
66
+ └─────────┬─────────┘
67
+ │ (Dynamic Route)
68
+ ┌─────────┴─────────┐
69
+ │ App Container │
70
+ │ (Port: 8000/8001) │
71
+ └───────────────────┘
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Quickstart
77
+
78
+ ### 1. Installation
79
+
80
+ **One-line standalone installer (Zero dependencies, no Python or pip needed):**
81
+ ```bash
82
+ curl -fsSL https://raw.githubusercontent.com/thlurte/aayi/main/install.sh | bash
83
+ ```
84
+
85
+ **Or run instantly with uvx (no installation required):**
86
+ ```bash
87
+ uvx aayi --help
88
+ ```
89
+
90
+ **Or install via uv tool or pip:**
91
+ ```bash
92
+ uv tool install aayi
93
+ # or
94
+ pip install aayi
95
+ ```
96
+
97
+ ### 2. Initialize Your Project
98
+
99
+ Generate starter `config/deploy.yml` and an optimized multi-stage `Dockerfile`:
100
+ ```bash
101
+ aayi init
102
+ ```
103
+
104
+ ### 3. Configure `config/deploy.yml`
105
+
106
+ ```yaml
107
+ service: myapp
108
+ image: your-docker-user/myapp
109
+
110
+ servers:
111
+ web:
112
+ - 192.168.1.10
113
+ workers:
114
+ - 192.168.1.11
115
+ user: root
116
+
117
+ registry:
118
+ server: docker.io
119
+ username: your-docker-user
120
+ password: ${DOCKER_REGISTRY_PASSWORD}
121
+
122
+ proxy:
123
+ domain: app.example.com
124
+ app_port: 8000
125
+ healthcheck_path: /healthz
126
+ drain_timeout: 10
127
+ tls: true
128
+ email: admin@example.com
129
+
130
+ env:
131
+ clear:
132
+ PORT: "8000"
133
+ ENVIRONMENT: production
134
+ secret:
135
+ - SECRET_KEY
136
+ - DATABASE_URL
137
+ ```
138
+
139
+ ### 4. Setup Target Servers
140
+
141
+ Ensures official Docker CE and Caddy are running on all target hosts:
142
+ ```bash
143
+ aayi setup
144
+ ```
145
+
146
+ ### 5. Deploy with Zero Downtime
147
+
148
+ Builds the image with `docker buildx`, pushes to your registry, boots a new container, runs health checks, and smoothly switches Caddy upstream traffic:
149
+ ```bash
150
+ aayi deploy
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Command Reference
156
+
157
+ | Command | Description |
158
+ | :--- | :--- |
159
+ | `aayi init` | Scaffolds `config/deploy.yml`, `Dockerfile`, and `.dockerignore`. |
160
+ | `aayi ci init` | Generates `.github/workflows/deploy.yml` for automated GitHub Actions CI/CD. |
161
+ | `aayi setup` | Installs Docker CE (if missing) and boots Caddy reverse proxy on all hosts. |
162
+ | `aayi deploy` | Full zero-downtime deployment (build, push, boot, healthcheck, swap Caddy traffic). |
163
+ | `aayi redeploy` | Redeploy an existing image tag on remote servers without rebuilding locally. |
164
+ | `aayi rollback` | Atomically revert Caddy upstream to the previous healthy container. |
165
+ | `aayi logs` | Real-time multi-host unified log streaming with colored host labels. |
166
+ | `aayi exec <cmd>` | Execute an ad-hoc command or interactive shell inside running container. |
167
+ | `aayi proxy status` | Inspect current live Caddy routing state and upstreams. |
168
+ | `aayi proxy reload` | Force Caddy to route traffic to a specific container port. |
169
+ | `aayi accessory boot <name>` | Start an accessory (e.g. Postgres, Redis) with persistent volumes. |
170
+ | `aayi accessory stop <name>` | Stop an accessory container. |
171
+ | `aayi accessory logs <name>` | View or follow logs for an accessory container. |
172
+
173
+ ---
174
+
175
+ ## Running Tests
176
+
177
+ ```bash
178
+ uv run pytest -v
179
+ ```
180
+
181
+ ---
182
+
183
+ ## License
184
+
185
+ MIT
aayi-0.1.1/README.md ADDED
@@ -0,0 +1,169 @@
1
+ <p align="center">
2
+ <img src="assets/banner.jpeg" alt="Aayi - Modern Cloud Deployment Dev Tool" width="100%">
3
+ </p>
4
+
5
+ # Aayi
6
+
7
+ Deploy Docker apps to any server with zero downtime and automatic HTTPS.
8
+
9
+ Built with uv, AsyncSSH, Typer, Pydantic, and Caddy.
10
+
11
+ ---
12
+
13
+ ## Features
14
+
15
+ - **Zero-Downtime Container Swaps**: Routes traffic between container revisions via Caddy's dynamic in-memory Admin API (`:2019`).
16
+ - **Automatic HTTPS**: Full TLS certificate lifecycle via Let's Encrypt / ZeroSSL powered by Caddy.
17
+ - **Docker Compose & Multi-Container**: Deploy multi-service applications directly from `docker-compose.yml` or multi-service `deploy.yml`.
18
+ - **Public vs. Private Containers**:
19
+ - **Public**: Dedicated domain per container (e.g. `app.example.com` and `api.example.com`) with automatic Caddy TLS.
20
+ - **Private**: Background workers, databases, and microservices connected to internal `aayi-net` with no host port exposure.
21
+ - **Concurrent SSH Execution**: Asynchronous, parallel multi-server operations powered by AsyncSSH.
22
+ - **Type-Safe Declarative Config**: Strict validation of `config/deploy.yml` with friendly Pydantic v2 error messages.
23
+ - **GitHub Actions Ready**: Out-of-the-box CI/CD generator (`aayi ci init`).
24
+ - **No Ruby Required**: Built for developers who want Kamal's simplicity without installing Ruby or gem bundlers.
25
+ - **Stateful Accessories**: Spin up and manage PostgreSQL, Redis, or MySQL containers with persistent Docker volumes.
26
+ - **Instant Rollback**: Revert Caddy upstreams back to the prior container in milliseconds with `aayi rollback`.
27
+
28
+ ---
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ [ CLI: aayi ]
34
+ (Typer + Rich UI)
35
+ │
36
+ [ Config: deploy.yml ]
37
+ (Pydantic v2 Models)
38
+ │
39
+ ┌────────────────┴────────────────┐
40
+ ▼ ▼
41
+ [ Local Builder ] [ AsyncSSH Fleet ]
42
+ (docker buildx push) (Parallel Multi-Server Pool)
43
+ │
44
+ ┌─────────────────────────┴─────────────────────────┐
45
+ ▼ ▼
46
+ [ Web Server(s) ] [ Worker Server(s) ]
47
+ ┌───────────────────┐ ┌────────────────────┐
48
+ │ Caddy (Port 80/443│ │ Worker Container │
49
+ │ Admin API :2019) │ └────────────────────┘
50
+ └─────────┬─────────┘
51
+ │ (Dynamic Route)
52
+ ┌─────────┴─────────┐
53
+ │ App Container │
54
+ │ (Port: 8000/8001) │
55
+ └───────────────────┘
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Quickstart
61
+
62
+ ### 1. Installation
63
+
64
+ **One-line standalone installer (Zero dependencies, no Python or pip needed):**
65
+ ```bash
66
+ curl -fsSL https://raw.githubusercontent.com/thlurte/aayi/main/install.sh | bash
67
+ ```
68
+
69
+ **Or run instantly with uvx (no installation required):**
70
+ ```bash
71
+ uvx aayi --help
72
+ ```
73
+
74
+ **Or install via uv tool or pip:**
75
+ ```bash
76
+ uv tool install aayi
77
+ # or
78
+ pip install aayi
79
+ ```
80
+
81
+ ### 2. Initialize Your Project
82
+
83
+ Generate starter `config/deploy.yml` and an optimized multi-stage `Dockerfile`:
84
+ ```bash
85
+ aayi init
86
+ ```
87
+
88
+ ### 3. Configure `config/deploy.yml`
89
+
90
+ ```yaml
91
+ service: myapp
92
+ image: your-docker-user/myapp
93
+
94
+ servers:
95
+ web:
96
+ - 192.168.1.10
97
+ workers:
98
+ - 192.168.1.11
99
+ user: root
100
+
101
+ registry:
102
+ server: docker.io
103
+ username: your-docker-user
104
+ password: ${DOCKER_REGISTRY_PASSWORD}
105
+
106
+ proxy:
107
+ domain: app.example.com
108
+ app_port: 8000
109
+ healthcheck_path: /healthz
110
+ drain_timeout: 10
111
+ tls: true
112
+ email: admin@example.com
113
+
114
+ env:
115
+ clear:
116
+ PORT: "8000"
117
+ ENVIRONMENT: production
118
+ secret:
119
+ - SECRET_KEY
120
+ - DATABASE_URL
121
+ ```
122
+
123
+ ### 4. Setup Target Servers
124
+
125
+ Ensures official Docker CE and Caddy are running on all target hosts:
126
+ ```bash
127
+ aayi setup
128
+ ```
129
+
130
+ ### 5. Deploy with Zero Downtime
131
+
132
+ Builds the image with `docker buildx`, pushes to your registry, boots a new container, runs health checks, and smoothly switches Caddy upstream traffic:
133
+ ```bash
134
+ aayi deploy
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Command Reference
140
+
141
+ | Command | Description |
142
+ | :--- | :--- |
143
+ | `aayi init` | Scaffolds `config/deploy.yml`, `Dockerfile`, and `.dockerignore`. |
144
+ | `aayi ci init` | Generates `.github/workflows/deploy.yml` for automated GitHub Actions CI/CD. |
145
+ | `aayi setup` | Installs Docker CE (if missing) and boots Caddy reverse proxy on all hosts. |
146
+ | `aayi deploy` | Full zero-downtime deployment (build, push, boot, healthcheck, swap Caddy traffic). |
147
+ | `aayi redeploy` | Redeploy an existing image tag on remote servers without rebuilding locally. |
148
+ | `aayi rollback` | Atomically revert Caddy upstream to the previous healthy container. |
149
+ | `aayi logs` | Real-time multi-host unified log streaming with colored host labels. |
150
+ | `aayi exec <cmd>` | Execute an ad-hoc command or interactive shell inside running container. |
151
+ | `aayi proxy status` | Inspect current live Caddy routing state and upstreams. |
152
+ | `aayi proxy reload` | Force Caddy to route traffic to a specific container port. |
153
+ | `aayi accessory boot <name>` | Start an accessory (e.g. Postgres, Redis) with persistent volumes. |
154
+ | `aayi accessory stop <name>` | Stop an accessory container. |
155
+ | `aayi accessory logs <name>` | View or follow logs for an accessory container. |
156
+
157
+ ---
158
+
159
+ ## Running Tests
160
+
161
+ ```bash
162
+ uv run pytest -v
163
+ ```
164
+
165
+ ---
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "aayi"
3
+ version = "0.1.1"
4
+ description = "Deploy Docker apps to any server with zero downtime and automatic HTTPS"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "asyncssh>=2.14.0",
9
+ "httpx>=0.27.0",
10
+ "jinja2>=3.1.0",
11
+ "pydantic>=2.7.0",
12
+ "pyyaml>=6.0.1",
13
+ "rich>=13.7.0",
14
+ "typer>=0.12.0",
15
+ ]
16
+
17
+ [[project.authors]]
18
+ name = "Ahmed"
19
+ email = "thlurte@gmail.com"
20
+
21
+ [project.scripts]
22
+ aayi = "aayi.cli.main:app"
23
+
24
+ [build-system]
25
+ requires = ["uv_build>=0.13.0,<0.14.0"]
26
+ build-backend = "uv_build"
27
+
28
+ [dependency-groups]
29
+ dev = [
30
+ "pytest>=9.2.0",
31
+ "pytest-asyncio>=1.4.1",
32
+ ]
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "aayi"
3
+ version = "0.1.1"
4
+ description = "Deploy Docker apps to any server with zero downtime and automatic HTTPS"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Ahmed", email = "thlurte@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "asyncssh>=2.14.0",
12
+ "httpx>=0.27.0",
13
+ "jinja2>=3.1.0",
14
+ "pydantic>=2.7.0",
15
+ "pyyaml>=6.0.1",
16
+ "rich>=13.7.0",
17
+ "typer>=0.12.0",
18
+ ]
19
+
20
+ [project.scripts]
21
+ aayi = "aayi.cli.main:app"
22
+
23
+ [build-system]
24
+ requires = ["uv_build>=0.13.0,<0.14.0"]
25
+ build-backend = "uv_build"
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "pytest>=9.2.0",
30
+ "pytest-asyncio>=1.4.1",
31
+ ]
@@ -0,0 +1,3 @@
1
+ """Aayi: A Python-native container deployment tool with zero-downtime Caddy proxying."""
2
+
3
+ __version__ = "0.1.1"
@@ -0,0 +1,76 @@
1
+ """GitHub Actions CI/CD workflow generator for Aayi."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from rich.console import Console
8
+
9
+ console = Console()
10
+
11
+ GITHUB_WORKFLOW_TEMPLATE = """name: Deploy Application
12
+
13
+ on:
14
+ push:
15
+ branches:
16
+ - main
17
+
18
+ jobs:
19
+ deploy:
20
+ name: Deploy to Production
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Checkout Repository
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v5
28
+ with:
29
+ version: "latest"
30
+
31
+ - name: Set up Docker Buildx
32
+ uses: docker/setup-buildx-action@v3
33
+
34
+ - name: Log in to Docker Registry
35
+ uses: docker/login-action@v3
36
+ with:
37
+ registry: ${{ secrets.DOCKER_REGISTRY || 'docker.io' }}
38
+ username: ${{ secrets.DOCKER_USERNAME }}
39
+ password: ${{ secrets.DOCKER_PASSWORD }}
40
+
41
+ - name: Set up SSH Private Key
42
+ uses: webfactory/ssh-agent@v0.9.0
43
+ with:
44
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
45
+
46
+ - name: Add Known Hosts
47
+ run: |
48
+ mkdir -p ~/.ssh
49
+ # You can add: ssh-keyscan -H <YOUR_SERVER_IP> >> ~/.ssh/known_hosts
50
+
51
+ - name: Deploy with Aayi
52
+ env:
53
+ DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
54
+ run: |
55
+ uvx aayi deploy
56
+ """
57
+
58
+
59
+ def init_github_actions(force: bool = False) -> Path:
60
+ """Generate .github/workflows/deploy.yml for automated deployments."""
61
+ workflows_dir = Path(".github") / "workflows"
62
+ workflows_dir.mkdir(parents=True, exist_ok=True)
63
+ workflow_file = workflows_dir / "deploy.yml"
64
+
65
+ if workflow_file.exists() and not force:
66
+ console.print(f"[yellow]Skipped {workflow_file} (already exists). Use --force to overwrite.[/yellow]")
67
+ else:
68
+ workflow_file.write_text(GITHUB_WORKFLOW_TEMPLATE, encoding="utf-8")
69
+ console.print(f"[green]✓ Created GitHub Actions workflow at {workflow_file}[/green]")
70
+
71
+ console.print("\n[bold cyan]GitHub Actions setup tips:[/bold cyan]")
72
+ console.print(" Configure these repository secrets in GitHub ([bold]Settings > Secrets and variables > Actions[/bold]):")
73
+ console.print(" • [yellow]SSH_PRIVATE_KEY[/yellow]: The private SSH key authorized on your target servers.")
74
+ console.print(" • [yellow]DOCKER_USERNAME[/yellow]: Your Docker Hub or container registry username.")
75
+ console.print(" • [yellow]DOCKER_PASSWORD[/yellow]: Your container registry password or access token.\n")
76
+ return workflow_file
@@ -0,0 +1,130 @@
1
+ """Project initialization and scaffolding generator for Aayi."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from rich.console import Console
8
+
9
+ console = Console()
10
+
11
+ DEPLOY_YML_TEMPLATE = """# Aayi Deployment Configuration
12
+ service: myapp
13
+ image: your-docker-user/myapp
14
+
15
+ # Server topology
16
+ servers:
17
+ web:
18
+ - 192.168.1.10
19
+ workers:
20
+ - 192.168.1.11
21
+ user: root
22
+ ssh_port: 22
23
+
24
+ # Docker container registry
25
+ registry:
26
+ server: docker.io
27
+ username: your-docker-user
28
+ password: ${DOCKER_REGISTRY_PASSWORD}
29
+
30
+ # Caddy Reverse Proxy & Zero-Downtime Traffic Routing
31
+ proxy:
32
+ domain: app.example.com
33
+ app_port: 8000
34
+ healthcheck_path: /healthz
35
+ healthcheck_timeout: 30
36
+ drain_timeout: 10
37
+ tls: true
38
+ email: admin@example.com
39
+
40
+ # Environment variables
41
+ env:
42
+ clear:
43
+ PORT: "8000"
44
+ ENVIRONMENT: production
45
+ secret:
46
+ - SECRET_KEY_BASE
47
+ - DATABASE_URL
48
+
49
+ # Attached stateful accessories (optional)
50
+ accessories:
51
+ postgres:
52
+ image: postgres:16-alpine
53
+ host: 192.168.1.10
54
+ port: "5432:5432"
55
+ env:
56
+ POSTGRES_DB: myapp_production
57
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
58
+ volumes:
59
+ - postgres_data:/var/lib/postgresql/data
60
+
61
+ redis:
62
+ image: redis:7-alpine
63
+ host: 192.168.1.10
64
+ port: "6379:6379"
65
+ volumes:
66
+ - redis_data:/data
67
+ """
68
+
69
+ DOCKERFILE_TEMPLATE = """# Multi-stage Dockerfile optimized with uv
70
+ FROM python:3.13-slim AS builder
71
+
72
+ WORKDIR /app
73
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
74
+
75
+ COPY pyproject.toml uv.lock* ./
76
+ RUN uv sync --frozen --no-dev --no-install-project
77
+
78
+ COPY . .
79
+ RUN uv sync --frozen --no-dev
80
+
81
+ FROM python:3.13-slim
82
+
83
+ WORKDIR /app
84
+ COPY --from=builder /app/.venv /app/.venv
85
+ COPY . .
86
+
87
+ ENV PATH="/app/.venv/bin:$PATH"
88
+ EXPOSE 8000
89
+
90
+ CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
91
+ """
92
+
93
+ DOCKERIGNORE_TEMPLATE = """.git
94
+ .venv
95
+ __pycache__
96
+ *.pyc
97
+ .env
98
+ .pytest_cache
99
+ dist
100
+ build
101
+ """
102
+
103
+
104
+ def init_project(force: bool = False) -> None:
105
+ """Scaffold initial deployment configuration and Dockerfile."""
106
+ config_dir = Path("config")
107
+ config_dir.mkdir(exist_ok=True)
108
+ deploy_yml = config_dir / "deploy.yml"
109
+
110
+ if deploy_yml.exists() and not force:
111
+ console.print(f"[yellow]Skipped {deploy_yml} (already exists). Use --force to overwrite.[/yellow]")
112
+ else:
113
+ deploy_yml.write_text(DEPLOY_YML_TEMPLATE, encoding="utf-8")
114
+ console.print(f"[green]✓ Created {deploy_yml}[/green]")
115
+
116
+ dockerfile = Path("Dockerfile")
117
+ if not dockerfile.exists() or force:
118
+ dockerfile.write_text(DOCKERFILE_TEMPLATE, encoding="utf-8")
119
+ console.print(f"[green]✓ Created {dockerfile}[/green]")
120
+
121
+ dockerignore = Path(".dockerignore")
122
+ if not dockerignore.exists() or force:
123
+ dockerignore.write_text(DOCKERIGNORE_TEMPLATE, encoding="utf-8")
124
+ console.print(f"[green]✓ Created {dockerignore}[/green]")
125
+
126
+ console.print("\n[bold green]Project initialized for Aayi![/bold green]")
127
+ console.print("Next steps:")
128
+ console.print(" 1. Edit [cyan]config/deploy.yml[/cyan] with your server IP and domain.")
129
+ console.print(" 2. Set up target servers: [bold]aayi setup[/bold]")
130
+ console.print(" 3. Deploy your application: [bold]aayi deploy[/bold]\n")