djux 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.
- djux-0.1.0/PKG-INFO +172 -0
- djux-0.1.0/README.md +145 -0
- djux-0.1.0/djux/__init__.py +1 -0
- djux-0.1.0/djux/cli.py +30 -0
- djux-0.1.0/djux/commands/__init__.py +0 -0
- djux-0.1.0/djux/commands/add.py +207 -0
- djux-0.1.0/djux/commands/list.py +45 -0
- djux-0.1.0/djux/commands/new.py +51 -0
- djux-0.1.0/djux/commands/publish.py +64 -0
- djux-0.1.0/djux/commands/remove.py +94 -0
- djux-0.1.0/djux/core/__init__.py +0 -0
- djux-0.1.0/djux/core/downloader.py +32 -0
- djux-0.1.0/djux/core/manifest.py +40 -0
- djux-0.1.0/djux/core/patcher.py +83 -0
- djux-0.1.0/djux/core/registry.py +67 -0
- djux-0.1.0/djux/templates/project_template/.env.example +3 -0
- djux-0.1.0/djux/templates/project_template/.gitignore +11 -0
- djux-0.1.0/djux/templates/project_template/apps/.gitkeep +0 -0
- djux-0.1.0/djux/templates/project_template/config/__init__.py +0 -0
- djux-0.1.0/djux/templates/project_template/config/asgi.py +7 -0
- djux-0.1.0/djux/templates/project_template/config/settings.py +85 -0
- djux-0.1.0/djux/templates/project_template/config/urls.py +7 -0
- djux-0.1.0/djux/templates/project_template/config/wsgi.py +7 -0
- djux-0.1.0/djux/templates/project_template/djux.project.json +5 -0
- djux-0.1.0/djux/templates/project_template/manage.py +20 -0
- djux-0.1.0/djux/templates/project_template/requirements.txt +2 -0
- djux-0.1.0/djux/templates/project_template/static/.gitkeep +0 -0
- djux-0.1.0/djux/templates/project_template/templates/.gitkeep +0 -0
- djux-0.1.0/djux.egg-info/PKG-INFO +172 -0
- djux-0.1.0/djux.egg-info/SOURCES.txt +37 -0
- djux-0.1.0/djux.egg-info/dependency_links.txt +1 -0
- djux-0.1.0/djux.egg-info/entry_points.txt +2 -0
- djux-0.1.0/djux.egg-info/requires.txt +3 -0
- djux-0.1.0/djux.egg-info/top_level.txt +1 -0
- djux-0.1.0/pyproject.toml +48 -0
- djux-0.1.0/setup.cfg +4 -0
- djux-0.1.0/tests/test_add.py +209 -0
- djux-0.1.0/tests/test_new.py +69 -0
- djux-0.1.0/tests/test_patcher.py +117 -0
djux-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djux
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Django app installer and registry CLI
|
|
5
|
+
Author: Djux contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/djuxhq/djux
|
|
8
|
+
Project-URL: Repository, https://github.com/djuxhq/djux
|
|
9
|
+
Project-URL: Issues, https://github.com/djuxhq/djux/issues
|
|
10
|
+
Project-URL: Roadmap, https://github.com/djuxhq/djux/blob/main/APP_ROADMAP.md
|
|
11
|
+
Keywords: django,cli,scaffold,templates,registry
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: click>=8.1
|
|
25
|
+
Requires-Dist: requests>=2.31
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
|
|
28
|
+
# djux
|
|
29
|
+
|
|
30
|
+
**djux** is a CLI and registry for adding production-ready Django app templates to a project in one command. It handles the repetitive setup work: app files, Django settings, URL wiring, pip dependencies, and migrations.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install djux
|
|
34
|
+
|
|
35
|
+
djux new myproject
|
|
36
|
+
cd myproject
|
|
37
|
+
|
|
38
|
+
djux add auth
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Think of it as a Django app installer where the installed code is copied into your project so you can own it, edit it, and ship it like normal Django code.
|
|
42
|
+
|
|
43
|
+
## Vision
|
|
44
|
+
|
|
45
|
+
Djux aims to make reusable Django app templates easier to discover, install, inspect, and customize. The project is intentionally open to community feedback: the registry should grow around apps Django developers actually want, with a safe update story for vendored app code.
|
|
46
|
+
|
|
47
|
+
Current focus:
|
|
48
|
+
|
|
49
|
+
- Core app templates such as `auth`, `users`, `api-keys`, and `files`.
|
|
50
|
+
- AI-ready templates such as `ai-models`, `ai-prompts`, `ai-chat`, `ai-usage`, and `ai-rag`.
|
|
51
|
+
- A safer update workflow for copied app code: `djux outdated` and `djux update <app>`.
|
|
52
|
+
|
|
53
|
+
See [APP_ROADMAP.md](APP_ROADMAP.md) for the full roadmap.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
Requires Python 3.10+.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install djux
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Verify:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
djux --version
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick start
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# 1. Create a new project
|
|
73
|
+
djux new myproject
|
|
74
|
+
cd myproject
|
|
75
|
+
|
|
76
|
+
# 2. Set up your environment
|
|
77
|
+
python -m venv .venv
|
|
78
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
79
|
+
pip install -r requirements.txt
|
|
80
|
+
cp .env.example .env
|
|
81
|
+
|
|
82
|
+
# 3. Run initial migrations and start the server
|
|
83
|
+
python manage.py migrate
|
|
84
|
+
python manage.py runserver
|
|
85
|
+
|
|
86
|
+
# 4. Add apps
|
|
87
|
+
djux add auth
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See [docs/getting-started.md](docs/getting-started.md) for a full walkthrough.
|
|
91
|
+
|
|
92
|
+
## Available apps
|
|
93
|
+
|
|
94
|
+
| Name | Description | Tags |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `auth` | JWT authentication: register, login, logout, refresh, me | `auth` `jwt` `api` |
|
|
97
|
+
| `users` | User profiles, avatars, preferences | planned |
|
|
98
|
+
| `api-keys` | API keys, scopes, rotation, revocation | planned |
|
|
99
|
+
| `ai-chat` | Basic chat API with conversation history | planned `ai` |
|
|
100
|
+
| `ai-models` | Provider/model registry and model selection | planned `ai` |
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
djux list
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## How it works
|
|
107
|
+
|
|
108
|
+
`djux add <app>` currently:
|
|
109
|
+
|
|
110
|
+
1. Looks up the app in the registry.
|
|
111
|
+
2. Downloads and extracts the app zip from GitHub.
|
|
112
|
+
3. Validates the `djux.json` manifest.
|
|
113
|
+
4. Copies the `app/` folder into your project's `apps/` directory.
|
|
114
|
+
5. Patches `config/settings.py` with `INSTALLED_APPS` entries and settings blocks.
|
|
115
|
+
6. Patches `config/urls.py` with the route include.
|
|
116
|
+
7. Runs `pip install` for declared dependencies.
|
|
117
|
+
8. Runs `python manage.py migrate` if the app has migrations.
|
|
118
|
+
9. Records the installed app in `djux.project.json`.
|
|
119
|
+
|
|
120
|
+
Installed apps are vendored into the project. That means developers fully own the copied code after install.
|
|
121
|
+
|
|
122
|
+
## Project layout
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
myproject/
|
|
126
|
+
|-- config/
|
|
127
|
+
| |-- settings.py # djux patches INSTALLED_APPS here
|
|
128
|
+
| `-- urls.py # djux adds URL includes here
|
|
129
|
+
|-- apps/ # all Django apps land here
|
|
130
|
+
|-- templates/
|
|
131
|
+
|-- static/
|
|
132
|
+
|-- manage.py
|
|
133
|
+
|-- djux.project.json # tracks installed apps
|
|
134
|
+
`-- requirements.txt
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Documentation
|
|
138
|
+
|
|
139
|
+
| Document | Contents |
|
|
140
|
+
|---|---|
|
|
141
|
+
| [Getting started](docs/getting-started.md) | Install, scaffold, first app, what changed |
|
|
142
|
+
| [CLI reference](docs/cli-reference.md) | Commands, options, errors |
|
|
143
|
+
| [Creating apps](docs/creating-apps.md) | Build and publish a djux app |
|
|
144
|
+
| [Manifest spec](docs/manifest-spec.md) | `djux.json` contract and versioning rules |
|
|
145
|
+
| [App conventions](docs/app-conventions.md) | Naming, layout, settings, migrations |
|
|
146
|
+
| [Update design](docs/update-design.md) | Planned `djux outdated` and `djux update <app>` flow |
|
|
147
|
+
| [Smoke test checklist](docs/smoke-test-checklist.md) | Quality checklist for official apps |
|
|
148
|
+
| [Registry](docs/registry.md) | Registry format, caching, custom registries |
|
|
149
|
+
|
|
150
|
+
## Repositories
|
|
151
|
+
|
|
152
|
+
| Repo | Purpose |
|
|
153
|
+
|---|---|
|
|
154
|
+
| [djuxhq/djux](https://github.com/djuxhq/djux) | CLI and project template |
|
|
155
|
+
| [djuxhq/djux-registry](https://github.com/djuxhq/djux-registry) | Registry index |
|
|
156
|
+
| [djuxhq/djux-app-auth](https://github.com/djuxhq/djux-app-auth) | Official auth app |
|
|
157
|
+
|
|
158
|
+
## Contributing
|
|
159
|
+
|
|
160
|
+
Contributors and testers are welcome. Useful ways to help:
|
|
161
|
+
|
|
162
|
+
- Build official app templates.
|
|
163
|
+
- Test Djux in real Django projects.
|
|
164
|
+
- Improve the CLI and update workflow.
|
|
165
|
+
- Review app manifests and registry entries.
|
|
166
|
+
- Improve documentation.
|
|
167
|
+
|
|
168
|
+
Start with [CONTRIBUTING.md](CONTRIBUTING.md), then check the roadmap and open issues.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT
|
djux-0.1.0/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# djux
|
|
2
|
+
|
|
3
|
+
**djux** is a CLI and registry for adding production-ready Django app templates to a project in one command. It handles the repetitive setup work: app files, Django settings, URL wiring, pip dependencies, and migrations.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install djux
|
|
7
|
+
|
|
8
|
+
djux new myproject
|
|
9
|
+
cd myproject
|
|
10
|
+
|
|
11
|
+
djux add auth
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Think of it as a Django app installer where the installed code is copied into your project so you can own it, edit it, and ship it like normal Django code.
|
|
15
|
+
|
|
16
|
+
## Vision
|
|
17
|
+
|
|
18
|
+
Djux aims to make reusable Django app templates easier to discover, install, inspect, and customize. The project is intentionally open to community feedback: the registry should grow around apps Django developers actually want, with a safe update story for vendored app code.
|
|
19
|
+
|
|
20
|
+
Current focus:
|
|
21
|
+
|
|
22
|
+
- Core app templates such as `auth`, `users`, `api-keys`, and `files`.
|
|
23
|
+
- AI-ready templates such as `ai-models`, `ai-prompts`, `ai-chat`, `ai-usage`, and `ai-rag`.
|
|
24
|
+
- A safer update workflow for copied app code: `djux outdated` and `djux update <app>`.
|
|
25
|
+
|
|
26
|
+
See [APP_ROADMAP.md](APP_ROADMAP.md) for the full roadmap.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Requires Python 3.10+.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install djux
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Verify:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
djux --version
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 1. Create a new project
|
|
46
|
+
djux new myproject
|
|
47
|
+
cd myproject
|
|
48
|
+
|
|
49
|
+
# 2. Set up your environment
|
|
50
|
+
python -m venv .venv
|
|
51
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
cp .env.example .env
|
|
54
|
+
|
|
55
|
+
# 3. Run initial migrations and start the server
|
|
56
|
+
python manage.py migrate
|
|
57
|
+
python manage.py runserver
|
|
58
|
+
|
|
59
|
+
# 4. Add apps
|
|
60
|
+
djux add auth
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See [docs/getting-started.md](docs/getting-started.md) for a full walkthrough.
|
|
64
|
+
|
|
65
|
+
## Available apps
|
|
66
|
+
|
|
67
|
+
| Name | Description | Tags |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `auth` | JWT authentication: register, login, logout, refresh, me | `auth` `jwt` `api` |
|
|
70
|
+
| `users` | User profiles, avatars, preferences | planned |
|
|
71
|
+
| `api-keys` | API keys, scopes, rotation, revocation | planned |
|
|
72
|
+
| `ai-chat` | Basic chat API with conversation history | planned `ai` |
|
|
73
|
+
| `ai-models` | Provider/model registry and model selection | planned `ai` |
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
djux list
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## How it works
|
|
80
|
+
|
|
81
|
+
`djux add <app>` currently:
|
|
82
|
+
|
|
83
|
+
1. Looks up the app in the registry.
|
|
84
|
+
2. Downloads and extracts the app zip from GitHub.
|
|
85
|
+
3. Validates the `djux.json` manifest.
|
|
86
|
+
4. Copies the `app/` folder into your project's `apps/` directory.
|
|
87
|
+
5. Patches `config/settings.py` with `INSTALLED_APPS` entries and settings blocks.
|
|
88
|
+
6. Patches `config/urls.py` with the route include.
|
|
89
|
+
7. Runs `pip install` for declared dependencies.
|
|
90
|
+
8. Runs `python manage.py migrate` if the app has migrations.
|
|
91
|
+
9. Records the installed app in `djux.project.json`.
|
|
92
|
+
|
|
93
|
+
Installed apps are vendored into the project. That means developers fully own the copied code after install.
|
|
94
|
+
|
|
95
|
+
## Project layout
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
myproject/
|
|
99
|
+
|-- config/
|
|
100
|
+
| |-- settings.py # djux patches INSTALLED_APPS here
|
|
101
|
+
| `-- urls.py # djux adds URL includes here
|
|
102
|
+
|-- apps/ # all Django apps land here
|
|
103
|
+
|-- templates/
|
|
104
|
+
|-- static/
|
|
105
|
+
|-- manage.py
|
|
106
|
+
|-- djux.project.json # tracks installed apps
|
|
107
|
+
`-- requirements.txt
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Documentation
|
|
111
|
+
|
|
112
|
+
| Document | Contents |
|
|
113
|
+
|---|---|
|
|
114
|
+
| [Getting started](docs/getting-started.md) | Install, scaffold, first app, what changed |
|
|
115
|
+
| [CLI reference](docs/cli-reference.md) | Commands, options, errors |
|
|
116
|
+
| [Creating apps](docs/creating-apps.md) | Build and publish a djux app |
|
|
117
|
+
| [Manifest spec](docs/manifest-spec.md) | `djux.json` contract and versioning rules |
|
|
118
|
+
| [App conventions](docs/app-conventions.md) | Naming, layout, settings, migrations |
|
|
119
|
+
| [Update design](docs/update-design.md) | Planned `djux outdated` and `djux update <app>` flow |
|
|
120
|
+
| [Smoke test checklist](docs/smoke-test-checklist.md) | Quality checklist for official apps |
|
|
121
|
+
| [Registry](docs/registry.md) | Registry format, caching, custom registries |
|
|
122
|
+
|
|
123
|
+
## Repositories
|
|
124
|
+
|
|
125
|
+
| Repo | Purpose |
|
|
126
|
+
|---|---|
|
|
127
|
+
| [djuxhq/djux](https://github.com/djuxhq/djux) | CLI and project template |
|
|
128
|
+
| [djuxhq/djux-registry](https://github.com/djuxhq/djux-registry) | Registry index |
|
|
129
|
+
| [djuxhq/djux-app-auth](https://github.com/djuxhq/djux-app-auth) | Official auth app |
|
|
130
|
+
|
|
131
|
+
## Contributing
|
|
132
|
+
|
|
133
|
+
Contributors and testers are welcome. Useful ways to help:
|
|
134
|
+
|
|
135
|
+
- Build official app templates.
|
|
136
|
+
- Test Djux in real Django projects.
|
|
137
|
+
- Improve the CLI and update workflow.
|
|
138
|
+
- Review app manifests and registry entries.
|
|
139
|
+
- Improve documentation.
|
|
140
|
+
|
|
141
|
+
Start with [CONTRIBUTING.md](CONTRIBUTING.md), then check the roadmap and open issues.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
djux-0.1.0/djux/cli.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Ensure UTF-8 output on Windows (required for Rich/Unicode characters)
|
|
5
|
+
if sys.platform == "win32":
|
|
6
|
+
os.environ.setdefault("PYTHONUTF8", "1")
|
|
7
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
8
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
9
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
10
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
from djux.commands.new import new
|
|
14
|
+
from djux.commands.add import add
|
|
15
|
+
from djux.commands.remove import remove
|
|
16
|
+
from djux.commands.list import list_apps
|
|
17
|
+
from djux.commands.publish import publish
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.group()
|
|
21
|
+
@click.version_option(package_name="djux")
|
|
22
|
+
def main():
|
|
23
|
+
"""djux — add production-ready Django apps in one command."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
main.add_command(new)
|
|
27
|
+
main.add_command(add)
|
|
28
|
+
main.add_command(remove)
|
|
29
|
+
main.add_command(list_apps, name="list")
|
|
30
|
+
main.add_command(publish)
|
|
File without changes
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from djux.core.downloader import download_and_extract
|
|
11
|
+
from djux.core.manifest import InvalidManifestError, parse_manifest
|
|
12
|
+
from djux.core.patcher import (
|
|
13
|
+
patch_installed_apps,
|
|
14
|
+
patch_settings_block,
|
|
15
|
+
patch_urls,
|
|
16
|
+
)
|
|
17
|
+
from djux.core.registry import REGISTRY_URL, fetch_registry, get_app
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _find_project_root() -> Path | None:
|
|
23
|
+
current = Path.cwd()
|
|
24
|
+
for parent in [current, *current.parents]:
|
|
25
|
+
if (parent / "djux.project.json").exists():
|
|
26
|
+
return parent
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_project_json(root: Path) -> dict:
|
|
31
|
+
return json.loads((root / "djux.project.json").read_text(encoding="utf-8"))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _write_project_json(root: Path, data: dict) -> None:
|
|
35
|
+
(root / "djux.project.json").write_text(
|
|
36
|
+
json.dumps(data, indent=2), encoding="utf-8"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@click.command()
|
|
41
|
+
@click.argument("app_name")
|
|
42
|
+
@click.option("--registry", "registry_url", default=REGISTRY_URL, help="Custom registry URL.")
|
|
43
|
+
def add(app_name: str, registry_url: str):
|
|
44
|
+
"""Download and install a djux app into the current project."""
|
|
45
|
+
|
|
46
|
+
# 1. Find project root
|
|
47
|
+
root = _find_project_root()
|
|
48
|
+
if root is None:
|
|
49
|
+
console.print("✗ No djux project found. Run this inside a djx project.")
|
|
50
|
+
raise SystemExit(1)
|
|
51
|
+
|
|
52
|
+
# 2. Check if already installed
|
|
53
|
+
project_data = _read_project_json(root)
|
|
54
|
+
if app_name in project_data.get("installed_apps", []):
|
|
55
|
+
console.print(f"⚠ App '[bold]{app_name}[/bold]' is already installed.")
|
|
56
|
+
raise SystemExit(0)
|
|
57
|
+
|
|
58
|
+
# 3. Fetch registry
|
|
59
|
+
with console.status("Fetching registry..."):
|
|
60
|
+
try:
|
|
61
|
+
registry = fetch_registry(registry_url)
|
|
62
|
+
except RuntimeError as e:
|
|
63
|
+
console.print(f"✗ {e}")
|
|
64
|
+
raise SystemExit(1)
|
|
65
|
+
|
|
66
|
+
app_entry = get_app(registry, app_name)
|
|
67
|
+
if app_entry is None:
|
|
68
|
+
console.print(
|
|
69
|
+
f"✗ App '[bold]{app_name}[/bold]' not found in registry.\n"
|
|
70
|
+
" Run [cyan]djux list[/cyan] to see available apps."
|
|
71
|
+
)
|
|
72
|
+
raise SystemExit(1)
|
|
73
|
+
|
|
74
|
+
console.print(f"✓ Found [bold]{app_name}[/bold] v{app_entry.get('version', '?')}")
|
|
75
|
+
|
|
76
|
+
# 4-5. Download and extract
|
|
77
|
+
with console.status("Downloading..."):
|
|
78
|
+
try:
|
|
79
|
+
extracted = download_and_extract(app_entry["download"], app_name)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
console.print(f"✗ Download failed: {e}")
|
|
82
|
+
raise SystemExit(1)
|
|
83
|
+
|
|
84
|
+
console.print("✓ Downloaded")
|
|
85
|
+
|
|
86
|
+
# 6. Validate manifest
|
|
87
|
+
try:
|
|
88
|
+
manifest = parse_manifest(extracted)
|
|
89
|
+
except InvalidManifestError as e:
|
|
90
|
+
console.print(f"✗ {e}")
|
|
91
|
+
raise SystemExit(1)
|
|
92
|
+
|
|
93
|
+
# 7. Check required apps
|
|
94
|
+
requires = manifest.get("requires_apps", [])
|
|
95
|
+
installed = project_data.get("installed_apps", [])
|
|
96
|
+
for dep in requires:
|
|
97
|
+
if dep not in installed:
|
|
98
|
+
console.print(
|
|
99
|
+
f"✗ App '[bold]{app_name}[/bold]' requires "
|
|
100
|
+
f"'[bold]{dep}[/bold]' to be installed first.\n"
|
|
101
|
+
f" Run: [cyan]djux add {dep}[/cyan]"
|
|
102
|
+
)
|
|
103
|
+
raise SystemExit(1)
|
|
104
|
+
|
|
105
|
+
# 8. Copy app code
|
|
106
|
+
app_src = extracted / "app"
|
|
107
|
+
if not app_src.exists():
|
|
108
|
+
console.print("✗ App package is missing the 'app/' directory.")
|
|
109
|
+
raise SystemExit(1)
|
|
110
|
+
|
|
111
|
+
install_name = app_name
|
|
112
|
+
app_dest = root / "apps" / install_name
|
|
113
|
+
|
|
114
|
+
if app_dest.exists():
|
|
115
|
+
console.print(
|
|
116
|
+
f"\n[yellow]Warning:[/yellow] Directory 'apps/{install_name}' already exists."
|
|
117
|
+
)
|
|
118
|
+
while True:
|
|
119
|
+
new_name = click.prompt(
|
|
120
|
+
" Enter a new name to install as (or leave empty to cancel)",
|
|
121
|
+
default="",
|
|
122
|
+
show_default=False,
|
|
123
|
+
).strip()
|
|
124
|
+
|
|
125
|
+
if not new_name:
|
|
126
|
+
console.print("Cancelled.")
|
|
127
|
+
raise SystemExit(0)
|
|
128
|
+
|
|
129
|
+
import re as _re
|
|
130
|
+
if not _re.match(r"^[a-z][a-z0-9_]*$", new_name):
|
|
131
|
+
console.print(
|
|
132
|
+
" [red]Invalid name.[/red] Use lowercase letters, digits, and underscores only."
|
|
133
|
+
)
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
app_dest = root / "apps" / new_name
|
|
137
|
+
if app_dest.exists():
|
|
138
|
+
console.print(f" [red]Directory 'apps/{new_name}' also exists.[/red] Try another name.")
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
install_name = new_name
|
|
142
|
+
console.print(f" Installing as '[bold]{install_name}[/bold]'")
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
shutil.copytree(app_src, app_dest)
|
|
146
|
+
console.print(f"✓ App files copied to apps/{install_name}/")
|
|
147
|
+
|
|
148
|
+
# 9. Patch settings.py
|
|
149
|
+
settings_path = root / "config" / "settings.py"
|
|
150
|
+
patch_installed_apps(settings_path, manifest["installed_apps"])
|
|
151
|
+
|
|
152
|
+
settings_patch = manifest.get("settings_patch")
|
|
153
|
+
if settings_patch:
|
|
154
|
+
patch_settings_block(settings_path, settings_patch)
|
|
155
|
+
|
|
156
|
+
console.print("✓ settings.py updated")
|
|
157
|
+
|
|
158
|
+
# 10. Patch urls.py — use install_name so the URL include matches the folder
|
|
159
|
+
urls_path = root / "config" / "urls.py"
|
|
160
|
+
patch_urls(urls_path, install_name, manifest["url_prefix"])
|
|
161
|
+
console.print("✓ urls.py updated")
|
|
162
|
+
|
|
163
|
+
# 11. Install pip dependencies
|
|
164
|
+
deps = manifest.get("dependencies", [])
|
|
165
|
+
if deps:
|
|
166
|
+
with console.status("Installing dependencies..."):
|
|
167
|
+
result = subprocess.run(
|
|
168
|
+
[sys.executable, "-m", "pip", "install", *deps],
|
|
169
|
+
capture_output=True,
|
|
170
|
+
text=True,
|
|
171
|
+
)
|
|
172
|
+
if result.returncode != 0:
|
|
173
|
+
console.print(f"✗ pip install failed:\n{result.stderr}")
|
|
174
|
+
raise SystemExit(1)
|
|
175
|
+
console.print("✓ Dependencies installed")
|
|
176
|
+
|
|
177
|
+
# 12. Run migrations
|
|
178
|
+
if manifest.get("migrations", False):
|
|
179
|
+
with console.status("Running migrations..."):
|
|
180
|
+
result = subprocess.run(
|
|
181
|
+
[sys.executable, "manage.py", "migrate"],
|
|
182
|
+
cwd=root,
|
|
183
|
+
capture_output=True,
|
|
184
|
+
text=True,
|
|
185
|
+
)
|
|
186
|
+
if result.returncode != 0:
|
|
187
|
+
console.print(f"✗ migrate failed:\n{result.stderr}")
|
|
188
|
+
raise SystemExit(1)
|
|
189
|
+
console.print("✓ Migrations applied")
|
|
190
|
+
|
|
191
|
+
# 13. Update djux.project.json
|
|
192
|
+
project_data.setdefault("installed_apps", []).append(install_name)
|
|
193
|
+
_write_project_json(root, project_data)
|
|
194
|
+
|
|
195
|
+
# 14. Print success
|
|
196
|
+
label = f"{app_name}" if install_name == app_name else f"{app_name} (as '{install_name}')"
|
|
197
|
+
console.print(f"\n[green]✓ {label} added successfully![/green]")
|
|
198
|
+
|
|
199
|
+
env_vars = manifest.get("env_vars", [])
|
|
200
|
+
if env_vars:
|
|
201
|
+
console.print("\nRequired environment variables:")
|
|
202
|
+
for var in env_vars:
|
|
203
|
+
console.print(f" • [yellow]{var}[/yellow]")
|
|
204
|
+
|
|
205
|
+
notes = manifest.get("notes")
|
|
206
|
+
if notes:
|
|
207
|
+
console.print(f"\nNotes: {notes}")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
from djux.core.registry import REGISTRY_URL, fetch_registry
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command()
|
|
11
|
+
@click.option("--registry", "registry_url", default=REGISTRY_URL, help="Custom registry URL.")
|
|
12
|
+
@click.option("--refresh", is_flag=True, help="Bypass cache and fetch fresh registry.")
|
|
13
|
+
def list_apps(registry_url: str, refresh: bool):
|
|
14
|
+
"""Show all apps available in the djux registry."""
|
|
15
|
+
with console.status("Fetching registry..."):
|
|
16
|
+
try:
|
|
17
|
+
registry = fetch_registry(registry_url, force_refresh=refresh)
|
|
18
|
+
except RuntimeError as e:
|
|
19
|
+
console.print(f"✗ {e}")
|
|
20
|
+
raise SystemExit(1)
|
|
21
|
+
|
|
22
|
+
apps = registry.get("apps", {})
|
|
23
|
+
if not apps:
|
|
24
|
+
console.print("No apps found in registry.")
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
table = Table(title="Available djux Apps", border_style="blue")
|
|
28
|
+
table.add_column("Name", style="bold cyan", no_wrap=True)
|
|
29
|
+
table.add_column("Version", style="green")
|
|
30
|
+
table.add_column("Description")
|
|
31
|
+
table.add_column("Tags", style="dim")
|
|
32
|
+
|
|
33
|
+
for name, info in apps.items():
|
|
34
|
+
tags = ", ".join(info.get("tags", []))
|
|
35
|
+
official = " [yellow]★[/yellow]" if info.get("official") else ""
|
|
36
|
+
table.add_row(
|
|
37
|
+
f"{name}{official}",
|
|
38
|
+
info.get("version", "?"),
|
|
39
|
+
info.get("description", ""),
|
|
40
|
+
tags,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
console.print(table)
|
|
44
|
+
console.print("\n Install any app: [bold cyan]djux add <name>[/bold cyan]")
|
|
45
|
+
console.print(" [yellow]★[/yellow] = official djux app")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
TEMPLATE_DIR = Path(__file__).parent.parent / "templates" / "project_template"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _replace_in_file(path: Path, project_name: str) -> None:
|
|
14
|
+
try:
|
|
15
|
+
text = path.read_text(encoding="utf-8")
|
|
16
|
+
path.write_text(text.replace("{{project_name}}", project_name), encoding="utf-8")
|
|
17
|
+
except (UnicodeDecodeError, PermissionError):
|
|
18
|
+
pass # skip binary files
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command()
|
|
22
|
+
@click.argument("project_name")
|
|
23
|
+
def new(project_name: str):
|
|
24
|
+
"""Scaffold a new djux Django project."""
|
|
25
|
+
target = Path.cwd() / project_name
|
|
26
|
+
|
|
27
|
+
if target.exists():
|
|
28
|
+
console.print(f"✗ Directory '[bold]{project_name}[/bold]' already exists.")
|
|
29
|
+
raise SystemExit(1)
|
|
30
|
+
|
|
31
|
+
console.print(f"Creating project: [bold]{project_name}[/bold]\n")
|
|
32
|
+
|
|
33
|
+
shutil.copytree(TEMPLATE_DIR, target)
|
|
34
|
+
|
|
35
|
+
for file_path in target.rglob("*"):
|
|
36
|
+
if file_path.is_file():
|
|
37
|
+
_replace_in_file(file_path, project_name)
|
|
38
|
+
|
|
39
|
+
panel_content = (
|
|
40
|
+
f"[green]✓[/green] Project created: [bold]{project_name}/[/bold]\n\n"
|
|
41
|
+
f" [cyan]cd {project_name}[/cyan]\n"
|
|
42
|
+
f" [cyan]python -m venv .venv[/cyan]\n"
|
|
43
|
+
f" [cyan]source .venv/bin/activate[/cyan] [dim]# Windows: .venv\\Scripts\\activate[/dim]\n"
|
|
44
|
+
f" [cyan]pip install -r requirements.txt[/cyan]\n"
|
|
45
|
+
f" [cyan]cp .env.example .env[/cyan]\n"
|
|
46
|
+
f" [cyan]python manage.py migrate[/cyan]\n"
|
|
47
|
+
f" [cyan]python manage.py runserver[/cyan]\n\n"
|
|
48
|
+
f" Then add apps: [bold cyan]djux add auth[/bold cyan]"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
console.print(Panel(panel_content, title="Ready", border_style="green"))
|