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 +185 -0
- aayi-0.1.1/README.md +169 -0
- aayi-0.1.1/pyproject.toml +32 -0
- aayi-0.1.1/pyproject.toml.orig +31 -0
- aayi-0.1.1/src/aayi/__init__.py +3 -0
- aayi-0.1.1/src/aayi/cli/ci.py +76 -0
- aayi-0.1.1/src/aayi/cli/init.py +130 -0
- aayi-0.1.1/src/aayi/cli/main.py +276 -0
- aayi-0.1.1/src/aayi/compose/parser.py +198 -0
- aayi-0.1.1/src/aayi/config/loader.py +84 -0
- aayi-0.1.1/src/aayi/config/models.py +155 -0
- aayi-0.1.1/src/aayi/docker/builder.py +63 -0
- aayi-0.1.1/src/aayi/docker/remote.py +204 -0
- aayi-0.1.1/src/aayi/docker/remote_builder.py +82 -0
- aayi-0.1.1/src/aayi/docker/tar_builder.py +121 -0
- aayi-0.1.1/src/aayi/orchestrator/accessories.py +91 -0
- aayi-0.1.1/src/aayi/orchestrator/deployer.py +284 -0
- aayi-0.1.1/src/aayi/orchestrator/rollback.py +63 -0
- aayi-0.1.1/src/aayi/proxy/caddy.py +133 -0
- aayi-0.1.1/src/aayi/ssh/runner.py +163 -0
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,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")
|