djaploy 0.1.0__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.
- djaploy-0.1.0/LICENSE +21 -0
- djaploy-0.1.0/PKG-INFO +310 -0
- djaploy-0.1.0/README.md +247 -0
- djaploy-0.1.0/djaploy/__init__.py +14 -0
- djaploy-0.1.0/djaploy/artifact.py +124 -0
- djaploy-0.1.0/djaploy/bin/__init__.py +1 -0
- djaploy-0.1.0/djaploy/bin/django_pyinfra.py +49 -0
- djaploy-0.1.0/djaploy/certificates.py +423 -0
- djaploy-0.1.0/djaploy/config.py +224 -0
- djaploy-0.1.0/djaploy/deploy.py +357 -0
- djaploy-0.1.0/djaploy/management/__init__.py +3 -0
- djaploy-0.1.0/djaploy/management/commands/__init__.py +3 -0
- djaploy-0.1.0/djaploy/management/commands/configureserver.py +56 -0
- djaploy-0.1.0/djaploy/management/commands/deploy.py +100 -0
- djaploy-0.1.0/djaploy/management/commands/sync_certs.py +75 -0
- djaploy-0.1.0/djaploy/management/commands/update_certs.py +151 -0
- djaploy-0.1.0/djaploy/management/commands/verify.py +363 -0
- djaploy-0.1.0/djaploy/management/utils.py +131 -0
- djaploy-0.1.0/djaploy/modules/__init__.py +13 -0
- djaploy-0.1.0/djaploy/modules/base.py +152 -0
- djaploy-0.1.0/djaploy/modules/core.py +418 -0
- djaploy-0.1.0/djaploy/modules/litestream.py +166 -0
- djaploy-0.1.0/djaploy/modules/loader.py +129 -0
- djaploy-0.1.0/djaploy/modules/nginx.py +109 -0
- djaploy-0.1.0/djaploy/modules/rclone.py +387 -0
- djaploy-0.1.0/djaploy/modules/sync_certs.py +107 -0
- djaploy-0.1.0/djaploy/modules/systemd.py +66 -0
- djaploy-0.1.0/djaploy/utils.py +84 -0
- djaploy-0.1.0/djaploy/version.py +1 -0
- djaploy-0.1.0/djaploy.egg-info/PKG-INFO +310 -0
- djaploy-0.1.0/djaploy.egg-info/SOURCES.txt +35 -0
- djaploy-0.1.0/djaploy.egg-info/dependency_links.txt +1 -0
- djaploy-0.1.0/djaploy.egg-info/entry_points.txt +2 -0
- djaploy-0.1.0/djaploy.egg-info/requires.txt +9 -0
- djaploy-0.1.0/djaploy.egg-info/top_level.txt +1 -0
- djaploy-0.1.0/pyproject.toml +61 -0
- djaploy-0.1.0/setup.cfg +4 -0
djaploy-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 djaploy contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
djaploy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djaploy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modular Django deployment system based on pyinfra
|
|
5
|
+
Author-email: Johanna Mae Dimayuga <johanna@techco.fi>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 djaploy contributors
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/techco-fi/djaploy
|
|
28
|
+
Project-URL: Repository, https://github.com/techco-fi/djaploy
|
|
29
|
+
Project-URL: Issues, https://github.com/techco-fi/djaploy/issues
|
|
30
|
+
Project-URL: Documentation, https://github.com/techco-fi/djaploy#readme
|
|
31
|
+
Keywords: django,deployment,pyinfra,automation,infrastructure
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Framework :: Django
|
|
35
|
+
Classifier: Framework :: Django :: 3.2
|
|
36
|
+
Classifier: Framework :: Django :: 4.0
|
|
37
|
+
Classifier: Framework :: Django :: 4.1
|
|
38
|
+
Classifier: Framework :: Django :: 4.2
|
|
39
|
+
Classifier: Framework :: Django :: 5.0
|
|
40
|
+
Classifier: Intended Audience :: Developers
|
|
41
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
42
|
+
Classifier: Operating System :: OS Independent
|
|
43
|
+
Classifier: Programming Language :: Python :: 3
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
47
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
49
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
50
|
+
Classifier: Topic :: System :: Installation/Setup
|
|
51
|
+
Classifier: Topic :: System :: Systems Administration
|
|
52
|
+
Requires-Python: >=3.9
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
License-File: LICENSE
|
|
55
|
+
Requires-Dist: pyinfra>=3.4
|
|
56
|
+
Requires-Dist: django>=3.2
|
|
57
|
+
Requires-Dist: build<2.0.0,>=1.3.0
|
|
58
|
+
Provides-Extra: certificates
|
|
59
|
+
Requires-Dist: certbot>=2.0; extra == "certificates"
|
|
60
|
+
Provides-Extra: bunny
|
|
61
|
+
Requires-Dist: certbot-dns-bunny>=0.1.0; extra == "bunny"
|
|
62
|
+
Dynamic: license-file
|
|
63
|
+
|
|
64
|
+
# djaploy
|
|
65
|
+
|
|
66
|
+
A modular Django deployment system based on pyinfra, designed to standardize and simplify infrastructure management across Django projects.
|
|
67
|
+
|
|
68
|
+
## Features
|
|
69
|
+
|
|
70
|
+
- **Modular Architecture**: Extensible plugin system for deployment components
|
|
71
|
+
- **Django Integration**: Seamless integration via Django management commands
|
|
72
|
+
- **Python Compilation Support**: Compile Python from source for specific versions
|
|
73
|
+
- **Multiple Deployment Modes**: Support for `--local`, `--latest`, and `--release` deployments
|
|
74
|
+
- **Infrastructure as Code**: Define infrastructure using Python with pyinfra
|
|
75
|
+
- **Git-based Artifacts**: Automated artifact creation from git repository
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
### From PyPI (once published)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install djaploy
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or with Poetry:
|
|
86
|
+
```bash
|
|
87
|
+
poetry add djaploy
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Development Installation
|
|
91
|
+
|
|
92
|
+
For testing djaploy locally without publishing to PyPI:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Clone the repository
|
|
96
|
+
git clone https://github.com/techco-fi/djaploy.git
|
|
97
|
+
cd djaploy
|
|
98
|
+
|
|
99
|
+
# Install in editable mode
|
|
100
|
+
pip install -e .
|
|
101
|
+
|
|
102
|
+
# Or with Poetry
|
|
103
|
+
poetry install
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Then in your Django project:
|
|
107
|
+
```bash
|
|
108
|
+
# Using pip
|
|
109
|
+
pip install -e /path/to/djaploy
|
|
110
|
+
|
|
111
|
+
# Or add as a local dependency in pyproject.toml
|
|
112
|
+
[tool.poetry.dependencies]
|
|
113
|
+
djaploy = {path = "../djaploy", develop = true}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Quick Start
|
|
117
|
+
|
|
118
|
+
### 1. Project Structure
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
your-django-project/
|
|
122
|
+
├── manage.py
|
|
123
|
+
├── your_app/
|
|
124
|
+
│ └── settings.py
|
|
125
|
+
└── djaploy/ # Deployment configuration
|
|
126
|
+
├── config.py # Main configuration
|
|
127
|
+
├── inventory/ # Host definitions
|
|
128
|
+
│ ├── production.py
|
|
129
|
+
│ └── staging.py
|
|
130
|
+
└── deploy_files/ # Environment-specific files
|
|
131
|
+
├── production/
|
|
132
|
+
│ └── etc/systemd/system/app.service
|
|
133
|
+
└── staging/
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2. Django Settings
|
|
137
|
+
|
|
138
|
+
Add to your Django `settings.py`:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# Required: Set project paths
|
|
142
|
+
BASE_DIR = '/path/to/django'
|
|
143
|
+
PROJECT_DIR = BASE_DIR # folder containing manage.py
|
|
144
|
+
DJAPLOY_CONFIG_DIR = BASE_DIR + '/djaploy'
|
|
145
|
+
GIT_DIR = BASE_DIR.parent # Git repository root
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 3. Create Configuration
|
|
149
|
+
|
|
150
|
+
Create `djaploy/config.py`:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from djaploy.config import DjaployConfig
|
|
154
|
+
from pathlib import Path
|
|
155
|
+
|
|
156
|
+
config = DjaployConfig(
|
|
157
|
+
# Required fields
|
|
158
|
+
project_name="myapp",
|
|
159
|
+
djaploy_dir=Path(__file__).parent, # REQUIRED: This djaploy directory
|
|
160
|
+
manage_py_path=Path("manage.py"), # REQUIRED: Path to manage.py (relative to project root)
|
|
161
|
+
|
|
162
|
+
# Python settings
|
|
163
|
+
python_version="3.11",
|
|
164
|
+
python_compile=False, # Set True to compile from source
|
|
165
|
+
|
|
166
|
+
# Server settings
|
|
167
|
+
app_user="app",
|
|
168
|
+
ssh_user="deploy",
|
|
169
|
+
|
|
170
|
+
# Modules to use
|
|
171
|
+
modules=[
|
|
172
|
+
"djaploy.modules.core", # Core setup (required)
|
|
173
|
+
"djaploy.modules.nginx", # Web server
|
|
174
|
+
"djaploy.modules.systemd", # Service management
|
|
175
|
+
],
|
|
176
|
+
|
|
177
|
+
# Services to manage
|
|
178
|
+
services=["myapp", "myapp-worker"],
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 4. Define Inventory
|
|
183
|
+
|
|
184
|
+
Create `djaploy/inventory/production.py`:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from djaploy.config import HostConfig
|
|
188
|
+
|
|
189
|
+
hosts = [
|
|
190
|
+
HostConfig(
|
|
191
|
+
name="web-1",
|
|
192
|
+
ssh_host="192.168.1.100",
|
|
193
|
+
ssh_user="deploy",
|
|
194
|
+
app_user="app",
|
|
195
|
+
env="production",
|
|
196
|
+
services=["myapp", "myapp-worker"],
|
|
197
|
+
),
|
|
198
|
+
]
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 5. Deploy Files
|
|
202
|
+
|
|
203
|
+
Place service files in `djaploy/deploy_files/production/`:
|
|
204
|
+
|
|
205
|
+
```ini
|
|
206
|
+
# etc/systemd/system/myapp.service
|
|
207
|
+
[Unit]
|
|
208
|
+
Description=My Django App
|
|
209
|
+
After=network.target
|
|
210
|
+
|
|
211
|
+
[Service]
|
|
212
|
+
Type=simple
|
|
213
|
+
User=app
|
|
214
|
+
WorkingDirectory=/home/app/apps/myapp
|
|
215
|
+
ExecStart=/home/app/.local/bin/poetry run gunicorn config.wsgi
|
|
216
|
+
Restart=on-failure
|
|
217
|
+
|
|
218
|
+
[Install]
|
|
219
|
+
WantedBy=multi-user.target
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Usage
|
|
223
|
+
|
|
224
|
+
### Configure Server
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
python manage.py configureserver --env production
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
This will:
|
|
231
|
+
- Create application user
|
|
232
|
+
- Install/compile Python
|
|
233
|
+
- Install Poetry
|
|
234
|
+
- Set up directory structure
|
|
235
|
+
|
|
236
|
+
### Deploy Application
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Deploy local changes (for development)
|
|
240
|
+
python manage.py deploy --env production --local
|
|
241
|
+
|
|
242
|
+
# Deploy latest git commit
|
|
243
|
+
python manage.py deploy --env production --latest
|
|
244
|
+
|
|
245
|
+
# Deploy specific release
|
|
246
|
+
python manage.py deploy --env production --release v1.0.0
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Deployment process:
|
|
250
|
+
1. Creates tar.gz artifact from git
|
|
251
|
+
2. Uploads to servers
|
|
252
|
+
3. Extracts application code
|
|
253
|
+
4. Installs dependencies
|
|
254
|
+
5. Runs migrations
|
|
255
|
+
6. Collects static files
|
|
256
|
+
7. Restarts services
|
|
257
|
+
|
|
258
|
+
## Module System
|
|
259
|
+
|
|
260
|
+
Djaploy uses a modular architecture where each component (nginx, systemd, backups, etc.) is a separate module that can be enabled or disabled per project.
|
|
261
|
+
|
|
262
|
+
### Available Modules
|
|
263
|
+
|
|
264
|
+
- `nginx`: NGINX web server configuration
|
|
265
|
+
- `systemd`: Systemd service management
|
|
266
|
+
- `litestream`: Litestream database backups
|
|
267
|
+
- `rclone`: Rclone-based backup system
|
|
268
|
+
- `tailscale`: Tailscale networking
|
|
269
|
+
- `ssl`: SSL certificate management
|
|
270
|
+
- `python_build`: Python compilation from source
|
|
271
|
+
|
|
272
|
+
### Creating Custom Modules
|
|
273
|
+
|
|
274
|
+
Projects can create their own modules by extending the base module class:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from djaploy.modules.base import BaseModule
|
|
278
|
+
|
|
279
|
+
class MyCustomModule(BaseModule):
|
|
280
|
+
def configure_server(self, host):
|
|
281
|
+
# Server configuration logic
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
def deploy(self, host, artifact_path):
|
|
285
|
+
# Deployment logic
|
|
286
|
+
pass
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Project Customization
|
|
290
|
+
|
|
291
|
+
### prepare.py
|
|
292
|
+
|
|
293
|
+
Projects can include a `prepare.py` file for local build steps before deployment:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
# prepare.py
|
|
297
|
+
from djaploy.prepare import run_command
|
|
298
|
+
|
|
299
|
+
def prepare():
|
|
300
|
+
run_command("npm run build")
|
|
301
|
+
run_command("python manage.py collectstatic --noinput")
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Custom Deploy Files
|
|
305
|
+
|
|
306
|
+
Projects can include environment-specific configuration files in a `deploy_files/` directory that will be copied to the server during deployment.
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
MIT
|
djaploy-0.1.0/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# djaploy
|
|
2
|
+
|
|
3
|
+
A modular Django deployment system based on pyinfra, designed to standardize and simplify infrastructure management across Django projects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Modular Architecture**: Extensible plugin system for deployment components
|
|
8
|
+
- **Django Integration**: Seamless integration via Django management commands
|
|
9
|
+
- **Python Compilation Support**: Compile Python from source for specific versions
|
|
10
|
+
- **Multiple Deployment Modes**: Support for `--local`, `--latest`, and `--release` deployments
|
|
11
|
+
- **Infrastructure as Code**: Define infrastructure using Python with pyinfra
|
|
12
|
+
- **Git-based Artifacts**: Automated artifact creation from git repository
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### From PyPI (once published)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install djaploy
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or with Poetry:
|
|
23
|
+
```bash
|
|
24
|
+
poetry add djaploy
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Development Installation
|
|
28
|
+
|
|
29
|
+
For testing djaploy locally without publishing to PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Clone the repository
|
|
33
|
+
git clone https://github.com/techco-fi/djaploy.git
|
|
34
|
+
cd djaploy
|
|
35
|
+
|
|
36
|
+
# Install in editable mode
|
|
37
|
+
pip install -e .
|
|
38
|
+
|
|
39
|
+
# Or with Poetry
|
|
40
|
+
poetry install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then in your Django project:
|
|
44
|
+
```bash
|
|
45
|
+
# Using pip
|
|
46
|
+
pip install -e /path/to/djaploy
|
|
47
|
+
|
|
48
|
+
# Or add as a local dependency in pyproject.toml
|
|
49
|
+
[tool.poetry.dependencies]
|
|
50
|
+
djaploy = {path = "../djaploy", develop = true}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### 1. Project Structure
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
your-django-project/
|
|
59
|
+
├── manage.py
|
|
60
|
+
├── your_app/
|
|
61
|
+
│ └── settings.py
|
|
62
|
+
└── djaploy/ # Deployment configuration
|
|
63
|
+
├── config.py # Main configuration
|
|
64
|
+
├── inventory/ # Host definitions
|
|
65
|
+
│ ├── production.py
|
|
66
|
+
│ └── staging.py
|
|
67
|
+
└── deploy_files/ # Environment-specific files
|
|
68
|
+
├── production/
|
|
69
|
+
│ └── etc/systemd/system/app.service
|
|
70
|
+
└── staging/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Django Settings
|
|
74
|
+
|
|
75
|
+
Add to your Django `settings.py`:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Required: Set project paths
|
|
79
|
+
BASE_DIR = '/path/to/django'
|
|
80
|
+
PROJECT_DIR = BASE_DIR # folder containing manage.py
|
|
81
|
+
DJAPLOY_CONFIG_DIR = BASE_DIR + '/djaploy'
|
|
82
|
+
GIT_DIR = BASE_DIR.parent # Git repository root
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. Create Configuration
|
|
86
|
+
|
|
87
|
+
Create `djaploy/config.py`:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from djaploy.config import DjaployConfig
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
|
|
93
|
+
config = DjaployConfig(
|
|
94
|
+
# Required fields
|
|
95
|
+
project_name="myapp",
|
|
96
|
+
djaploy_dir=Path(__file__).parent, # REQUIRED: This djaploy directory
|
|
97
|
+
manage_py_path=Path("manage.py"), # REQUIRED: Path to manage.py (relative to project root)
|
|
98
|
+
|
|
99
|
+
# Python settings
|
|
100
|
+
python_version="3.11",
|
|
101
|
+
python_compile=False, # Set True to compile from source
|
|
102
|
+
|
|
103
|
+
# Server settings
|
|
104
|
+
app_user="app",
|
|
105
|
+
ssh_user="deploy",
|
|
106
|
+
|
|
107
|
+
# Modules to use
|
|
108
|
+
modules=[
|
|
109
|
+
"djaploy.modules.core", # Core setup (required)
|
|
110
|
+
"djaploy.modules.nginx", # Web server
|
|
111
|
+
"djaploy.modules.systemd", # Service management
|
|
112
|
+
],
|
|
113
|
+
|
|
114
|
+
# Services to manage
|
|
115
|
+
services=["myapp", "myapp-worker"],
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. Define Inventory
|
|
120
|
+
|
|
121
|
+
Create `djaploy/inventory/production.py`:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from djaploy.config import HostConfig
|
|
125
|
+
|
|
126
|
+
hosts = [
|
|
127
|
+
HostConfig(
|
|
128
|
+
name="web-1",
|
|
129
|
+
ssh_host="192.168.1.100",
|
|
130
|
+
ssh_user="deploy",
|
|
131
|
+
app_user="app",
|
|
132
|
+
env="production",
|
|
133
|
+
services=["myapp", "myapp-worker"],
|
|
134
|
+
),
|
|
135
|
+
]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 5. Deploy Files
|
|
139
|
+
|
|
140
|
+
Place service files in `djaploy/deploy_files/production/`:
|
|
141
|
+
|
|
142
|
+
```ini
|
|
143
|
+
# etc/systemd/system/myapp.service
|
|
144
|
+
[Unit]
|
|
145
|
+
Description=My Django App
|
|
146
|
+
After=network.target
|
|
147
|
+
|
|
148
|
+
[Service]
|
|
149
|
+
Type=simple
|
|
150
|
+
User=app
|
|
151
|
+
WorkingDirectory=/home/app/apps/myapp
|
|
152
|
+
ExecStart=/home/app/.local/bin/poetry run gunicorn config.wsgi
|
|
153
|
+
Restart=on-failure
|
|
154
|
+
|
|
155
|
+
[Install]
|
|
156
|
+
WantedBy=multi-user.target
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Usage
|
|
160
|
+
|
|
161
|
+
### Configure Server
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python manage.py configureserver --env production
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This will:
|
|
168
|
+
- Create application user
|
|
169
|
+
- Install/compile Python
|
|
170
|
+
- Install Poetry
|
|
171
|
+
- Set up directory structure
|
|
172
|
+
|
|
173
|
+
### Deploy Application
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Deploy local changes (for development)
|
|
177
|
+
python manage.py deploy --env production --local
|
|
178
|
+
|
|
179
|
+
# Deploy latest git commit
|
|
180
|
+
python manage.py deploy --env production --latest
|
|
181
|
+
|
|
182
|
+
# Deploy specific release
|
|
183
|
+
python manage.py deploy --env production --release v1.0.0
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Deployment process:
|
|
187
|
+
1. Creates tar.gz artifact from git
|
|
188
|
+
2. Uploads to servers
|
|
189
|
+
3. Extracts application code
|
|
190
|
+
4. Installs dependencies
|
|
191
|
+
5. Runs migrations
|
|
192
|
+
6. Collects static files
|
|
193
|
+
7. Restarts services
|
|
194
|
+
|
|
195
|
+
## Module System
|
|
196
|
+
|
|
197
|
+
Djaploy uses a modular architecture where each component (nginx, systemd, backups, etc.) is a separate module that can be enabled or disabled per project.
|
|
198
|
+
|
|
199
|
+
### Available Modules
|
|
200
|
+
|
|
201
|
+
- `nginx`: NGINX web server configuration
|
|
202
|
+
- `systemd`: Systemd service management
|
|
203
|
+
- `litestream`: Litestream database backups
|
|
204
|
+
- `rclone`: Rclone-based backup system
|
|
205
|
+
- `tailscale`: Tailscale networking
|
|
206
|
+
- `ssl`: SSL certificate management
|
|
207
|
+
- `python_build`: Python compilation from source
|
|
208
|
+
|
|
209
|
+
### Creating Custom Modules
|
|
210
|
+
|
|
211
|
+
Projects can create their own modules by extending the base module class:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from djaploy.modules.base import BaseModule
|
|
215
|
+
|
|
216
|
+
class MyCustomModule(BaseModule):
|
|
217
|
+
def configure_server(self, host):
|
|
218
|
+
# Server configuration logic
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
def deploy(self, host, artifact_path):
|
|
222
|
+
# Deployment logic
|
|
223
|
+
pass
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Project Customization
|
|
227
|
+
|
|
228
|
+
### prepare.py
|
|
229
|
+
|
|
230
|
+
Projects can include a `prepare.py` file for local build steps before deployment:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
# prepare.py
|
|
234
|
+
from djaploy.prepare import run_command
|
|
235
|
+
|
|
236
|
+
def prepare():
|
|
237
|
+
run_command("npm run build")
|
|
238
|
+
run_command("python manage.py collectstatic --noinput")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Custom Deploy Files
|
|
242
|
+
|
|
243
|
+
Projects can include environment-specific configuration files in a `deploy_files/` directory that will be copied to the server during deployment.
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
djaploy - Modular Django deployment system based on pyinfra
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .config import DjaployConfig
|
|
6
|
+
from .deploy import deploy_project, configure_server
|
|
7
|
+
from .version import __version__
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"DjaployConfig",
|
|
11
|
+
"deploy_project",
|
|
12
|
+
"configure_server",
|
|
13
|
+
"__version__",
|
|
14
|
+
]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Artifact creation for deployments
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from .config import DjaployConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_artifact(config: DjaployConfig,
|
|
15
|
+
mode: str = "latest",
|
|
16
|
+
release_tag: Optional[str] = None) -> Path:
|
|
17
|
+
"""
|
|
18
|
+
Create deployment artifact based on mode
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
config: DjaployConfig instance
|
|
22
|
+
mode: Deployment mode ("local", "latest", "release")
|
|
23
|
+
release_tag: Release tag if mode is "release"
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Path to created artifact
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# Ensure artifact directory exists
|
|
30
|
+
artifact_dir = config.git_dir / config.artifact_dir
|
|
31
|
+
artifact_dir.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
if mode == "local":
|
|
34
|
+
return _create_local_artifact(config, artifact_dir)
|
|
35
|
+
elif mode == "latest":
|
|
36
|
+
return _create_latest_artifact(config, artifact_dir)
|
|
37
|
+
elif mode == "release":
|
|
38
|
+
if not release_tag:
|
|
39
|
+
raise ValueError("release_tag is required when mode is 'release'")
|
|
40
|
+
return _create_release_artifact(config, artifact_dir, release_tag)
|
|
41
|
+
else:
|
|
42
|
+
raise ValueError(f"Invalid deployment mode: {mode}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _create_local_artifact(config: DjaployConfig, artifact_dir: Path) -> Path:
|
|
46
|
+
"""Create artifact from local uncommitted files"""
|
|
47
|
+
|
|
48
|
+
artifact_file = artifact_dir / f"{config.project_name}.local.tar.gz"
|
|
49
|
+
|
|
50
|
+
# Change to git directory
|
|
51
|
+
original_dir = os.getcwd()
|
|
52
|
+
os.chdir(config.git_dir)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# Create artifact from git files (including uncommitted changes)
|
|
56
|
+
subprocess.run(
|
|
57
|
+
"git ls-files --others --exclude-standard --cached | tar Tzcf - {}".format(artifact_file),
|
|
58
|
+
shell=True,
|
|
59
|
+
check=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return artifact_file
|
|
63
|
+
finally:
|
|
64
|
+
os.chdir(original_dir)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _create_latest_artifact(config: DjaployConfig, artifact_dir: Path) -> Path:
|
|
68
|
+
"""Create artifact from latest git commit"""
|
|
69
|
+
|
|
70
|
+
# Get git hash
|
|
71
|
+
git_hash = subprocess.run(
|
|
72
|
+
["git", "rev-parse", "--short", "HEAD"],
|
|
73
|
+
capture_output=True,
|
|
74
|
+
check=True,
|
|
75
|
+
text=True,
|
|
76
|
+
cwd=config.git_dir,
|
|
77
|
+
).stdout.strip()
|
|
78
|
+
|
|
79
|
+
return _create_git_artifact(config, artifact_dir, git_hash)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _create_release_artifact(config: DjaployConfig, artifact_dir: Path, release_tag: str) -> Path:
|
|
83
|
+
"""Create artifact from a specific release tag"""
|
|
84
|
+
|
|
85
|
+
# Verify the tag exists
|
|
86
|
+
try:
|
|
87
|
+
subprocess.run(
|
|
88
|
+
["git", "rev-parse", release_tag],
|
|
89
|
+
capture_output=True,
|
|
90
|
+
check=True,
|
|
91
|
+
cwd=config.git_dir,
|
|
92
|
+
)
|
|
93
|
+
except subprocess.CalledProcessError:
|
|
94
|
+
raise ValueError(f"Release tag '{release_tag}' does not exist")
|
|
95
|
+
|
|
96
|
+
return _create_git_artifact(config, artifact_dir, release_tag)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _create_git_artifact(config: DjaployConfig, artifact_dir: Path, git_ref: str) -> Path:
|
|
100
|
+
"""Create artifact from a git reference"""
|
|
101
|
+
|
|
102
|
+
artifact_tar = artifact_dir / f"{config.project_name}.{git_ref}.tar"
|
|
103
|
+
artifact_file = artifact_dir / f"{config.project_name}.{git_ref}.tar.gz"
|
|
104
|
+
|
|
105
|
+
# Change to git directory
|
|
106
|
+
original_dir = os.getcwd()
|
|
107
|
+
os.chdir(config.git_dir)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Create tar archive from git
|
|
111
|
+
subprocess.run(
|
|
112
|
+
["git", "archive", "--format=tar", "-o", str(artifact_tar), git_ref],
|
|
113
|
+
check=True,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Compress the tar file
|
|
117
|
+
subprocess.run(
|
|
118
|
+
["gzip", "-f", str(artifact_tar)],
|
|
119
|
+
check=True,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return artifact_file
|
|
123
|
+
finally:
|
|
124
|
+
os.chdir(original_dir)
|