quickscale-cli 0.73.0__tar.gz → 0.74.2__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.
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/PKG-INFO +162 -10
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/README.md +160 -8
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/pyproject.toml +7 -2
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/_version.py +1 -1
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/apply_command.py +82 -16
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/development_commands.py +10 -3
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/module_config.py +111 -101
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/plan_command.py +10 -9
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/schema/config_schema.py +3 -2
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/project_manager.py +12 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/__init__.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/__init__.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/deployment_commands.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/module_commands.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/remove_command.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/status_command.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/main.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/schema/__init__.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/schema/delta.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/schema/state_schema.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/__init__.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/dependency_utils.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/docker_utils.py +0 -0
- {quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/railway_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quickscale-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.74.2
|
|
4
4
|
Summary: Command-line interface for QuickScale Django project generator
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: django,cli,project-generator,saas
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Requires-Dist: click (>=8.3.1,<9.0.0)
|
|
20
|
-
Requires-Dist: quickscale-core (>=0.
|
|
20
|
+
Requires-Dist: quickscale-core (>=0.74.2,<0.75.0)
|
|
21
21
|
Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
|
|
22
22
|
Project-URL: Homepage, https://github.com/Experto-AI/quickscale
|
|
23
23
|
Project-URL: Repository, https://github.com/Experto-AI/quickscale
|
|
@@ -25,6 +25,14 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
# 🚀 QuickScale
|
|
27
27
|
|
|
28
|
+
[](https://pypi.org/project/quickscale/)
|
|
29
|
+
[](https://github.com/Experto-AI/quickscale/actions/workflows/ci.yml)
|
|
30
|
+
[](https://github.com/Experto-AI/quickscale/actions/workflows/ci.yml)
|
|
31
|
+
[](https://www.python.org/downloads/)
|
|
32
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
33
|
+
[](https://github.com/Experto-AI/quickscale/releases)
|
|
34
|
+
[](https://github.com/astral-sh/ruff)
|
|
35
|
+
|
|
28
36
|
> **You are here**: **QuickScale README** (Project Overview)
|
|
29
37
|
> **Related docs**: [Start Here](START_HERE.md) | [Glossary](GLOSSARY.md) | [Decisions](docs/technical/decisions.md) | [User Manual](docs/technical/user_manual.md)
|
|
30
38
|
|
|
@@ -93,10 +101,11 @@ See [decisions.md - Glossary section](./docs/technical/decisions.md#document-res
|
|
|
93
101
|
|
|
94
102
|
### Development Flow
|
|
95
103
|
1. `quickscale plan myapp` → Interactive configuration wizard
|
|
96
|
-
2. `
|
|
97
|
-
3.
|
|
98
|
-
4.
|
|
99
|
-
5.
|
|
104
|
+
2. `cd myapp` → Navigate to your project directory
|
|
105
|
+
3. `quickscale apply` → Generates production-ready Django project
|
|
106
|
+
4. Add your custom Django apps and features
|
|
107
|
+
5. Build your unique client application
|
|
108
|
+
6. Deploy to Railway with `quickscale deploy railway` (or use standard Django deployment)
|
|
100
109
|
|
|
101
110
|
ℹ️ The [MVP Feature Matrix](./docs/technical/decisions.md#mvp-feature-matrix-authoritative) is the single source of truth for what's in or out.
|
|
102
111
|
|
|
@@ -104,6 +113,7 @@ See [decisions.md - Glossary section](./docs/technical/decisions.md#document-res
|
|
|
104
113
|
|
|
105
114
|
Running `quickscale plan myapp && quickscale apply` generates a **production-ready Django project** with:
|
|
106
115
|
|
|
116
|
+
- ✅ **React + shadcn/ui** frontend (TypeScript, Vite, Tailwind CSS) — **NEW in v0.74.0**
|
|
107
117
|
- ✅ **Docker** setup (development + production)
|
|
108
118
|
- ✅ **PostgreSQL** configuration
|
|
109
119
|
- ✅ **Environment-based** settings (dev/prod split)
|
|
@@ -115,6 +125,8 @@ Running `quickscale plan myapp && quickscale apply` generates a **production-rea
|
|
|
115
125
|
- ✅ **Poetry** for dependency management
|
|
116
126
|
- ✅ **One-Command Deployment**: Deploy to Railway with `quickscale deploy railway` - fully automated setup
|
|
117
127
|
|
|
128
|
+
**Alternative**: Use `quickscale plan myapp --theme showcase_html` for pure HTML/CSS (simpler projects).
|
|
129
|
+
|
|
118
130
|
**See the complete project structure:** [scaffolding.md - Generated Project Output](./docs/technical/scaffolding.md#5-generated-project-output)
|
|
119
131
|
|
|
120
132
|
The generated project is **yours to own and modify** - no vendor lock-in, just Django best practices.
|
|
@@ -132,8 +144,148 @@ See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detai
|
|
|
132
144
|
|
|
133
145
|
---
|
|
134
146
|
|
|
147
|
+
## Installation
|
|
148
|
+
|
|
149
|
+
QuickScale can be installed in two ways:
|
|
150
|
+
|
|
151
|
+
### Method 1: Install from PyPI (Recommended)
|
|
152
|
+
|
|
153
|
+
**Quick install:**
|
|
154
|
+
```bash
|
|
155
|
+
pip install quickscale
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Or with Poetry:**
|
|
159
|
+
```bash
|
|
160
|
+
poetry add quickscale
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Then use directly:**
|
|
164
|
+
```bash
|
|
165
|
+
quickscale plan myapp
|
|
166
|
+
cd myapp
|
|
167
|
+
quickscale apply
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Method 2: Install from Source
|
|
171
|
+
|
|
172
|
+
**For those who prefer building from source:**
|
|
173
|
+
```bash
|
|
174
|
+
git clone https://github.com/Experto-AI/quickscale.git
|
|
175
|
+
cd quickscale
|
|
176
|
+
./scripts/install_global.sh
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Then use directly:**
|
|
180
|
+
```bash
|
|
181
|
+
quickscale plan myapp
|
|
182
|
+
cd myapp
|
|
183
|
+
quickscale apply
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Both methods use the same command syntax:** `quickscale plan`, `quickscale apply`, etc.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### For Contributors
|
|
191
|
+
|
|
192
|
+
If you want to contribute to QuickScale development, see the [Development Guide](./docs/technical/development.md) for setup instructions.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 🚀 5-Minute Quickstart
|
|
197
|
+
|
|
198
|
+
**Want to see QuickScale in action right now?** Here's the fastest path to a working Django SaaS:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# 1. Install QuickScale
|
|
202
|
+
./scripts/install_global.sh
|
|
203
|
+
|
|
204
|
+
# 2. Create your project configuration
|
|
205
|
+
quickscale plan myapp
|
|
206
|
+
# → Accept defaults: theme (showcase_html), no modules, Docker enabled
|
|
207
|
+
|
|
208
|
+
# 3. Generate and enter the project
|
|
209
|
+
cd myapp
|
|
210
|
+
quickscale apply
|
|
211
|
+
# → Docker services auto-start! (default: docker.start=true)
|
|
212
|
+
|
|
213
|
+
# 4. Setup and verify
|
|
214
|
+
quickscale manage migrate
|
|
215
|
+
quickscale manage createsuperuser
|
|
216
|
+
quickscale ps # Check services are running
|
|
217
|
+
|
|
218
|
+
# 5. Open http://localhost:8000
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**That's it!** 🎉 You now have a production-ready Django SaaS running with Docker + PostgreSQL.
|
|
222
|
+
|
|
223
|
+
**Note**: Services started automatically during `quickscale apply`. No need to run `quickscale up` manually!
|
|
224
|
+
|
|
225
|
+
**Prefer native Python?** After step 3, run:
|
|
226
|
+
```bash
|
|
227
|
+
poetry install
|
|
228
|
+
poetry run python manage.py migrate
|
|
229
|
+
poetry run python manage.py runserver
|
|
230
|
+
# Open http://localhost:8000
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Understanding Docker Auto-Start
|
|
236
|
+
|
|
237
|
+
**When does `quickscale apply` start Docker automatically?**
|
|
238
|
+
|
|
239
|
+
✅ **Services auto-start** (no `quickscale up` needed):
|
|
240
|
+
- First-time project generation
|
|
241
|
+
- When `docker.start: true` in quickscale.yml (**default**)
|
|
242
|
+
- When `--no-docker` flag is NOT used
|
|
243
|
+
|
|
244
|
+
❌ **Manual start required** (`quickscale up` needed):
|
|
245
|
+
- You set `docker.start: false` during `quickscale plan` wizard
|
|
246
|
+
- After stopping services with `quickscale down`
|
|
247
|
+
- When running `quickscale apply --no-docker`
|
|
248
|
+
- Adding modules to existing project (incremental apply)
|
|
135
249
|
|
|
136
|
-
|
|
250
|
+
**Example - No manual start needed:**
|
|
251
|
+
```bash
|
|
252
|
+
quickscale plan myapp # Accept defaults (docker.start: true)
|
|
253
|
+
cd myapp
|
|
254
|
+
quickscale apply # ← Docker services auto-start!
|
|
255
|
+
|
|
256
|
+
# Already running - just check status:
|
|
257
|
+
quickscale ps
|
|
258
|
+
curl http://localhost:8000 # ✅ Works immediately
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Example - Manual start needed:**
|
|
262
|
+
```bash
|
|
263
|
+
quickscale plan myapp
|
|
264
|
+
# During wizard, set: "Docker start? [Y/n]: n"
|
|
265
|
+
cd myapp
|
|
266
|
+
quickscale apply # Services do NOT start
|
|
267
|
+
|
|
268
|
+
# Must start manually:
|
|
269
|
+
quickscale up
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Docker configuration options** in quickscale.yml:
|
|
273
|
+
|
|
274
|
+
```yaml
|
|
275
|
+
docker:
|
|
276
|
+
start: true # Auto-start services during apply?
|
|
277
|
+
build: true # Rebuild images? (slower but ensures latest)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- `start: true` + `build: true` → Full rebuild + start (slowest, most reliable)
|
|
281
|
+
- `start: true` + `build: false` → Start with cached images (faster)
|
|
282
|
+
- `start: false` → Manual control with `quickscale up`
|
|
283
|
+
|
|
284
|
+
**Need more details?** See [Docker Workflows Guide](./docs/technical/docker_workflows.md) for comprehensive scenarios and troubleshooting.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Full Documentation & Setup
|
|
137
289
|
|
|
138
290
|
```bash
|
|
139
291
|
# Install QuickScale globally
|
|
@@ -142,11 +294,11 @@ See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detai
|
|
|
142
294
|
# Create a configuration interactively
|
|
143
295
|
quickscale plan myapp
|
|
144
296
|
# → Select theme, modules, Docker options
|
|
145
|
-
# → Generates quickscale.yml
|
|
297
|
+
# → Generates quickscale.yml in myapp/ directory
|
|
146
298
|
|
|
147
|
-
#
|
|
148
|
-
quickscale apply
|
|
299
|
+
# Navigate to project and execute the configuration
|
|
149
300
|
cd myapp
|
|
301
|
+
quickscale apply
|
|
150
302
|
```
|
|
151
303
|
|
|
152
304
|
**Choose your development workflow:**
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# 🚀 QuickScale
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/quickscale/)
|
|
4
|
+
[](https://github.com/Experto-AI/quickscale/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/Experto-AI/quickscale/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
8
|
+
[](https://github.com/Experto-AI/quickscale/releases)
|
|
9
|
+
[](https://github.com/astral-sh/ruff)
|
|
10
|
+
|
|
3
11
|
> **You are here**: **QuickScale README** (Project Overview)
|
|
4
12
|
> **Related docs**: [Start Here](START_HERE.md) | [Glossary](GLOSSARY.md) | [Decisions](docs/technical/decisions.md) | [User Manual](docs/technical/user_manual.md)
|
|
5
13
|
|
|
@@ -68,10 +76,11 @@ See [decisions.md - Glossary section](./docs/technical/decisions.md#document-res
|
|
|
68
76
|
|
|
69
77
|
### Development Flow
|
|
70
78
|
1. `quickscale plan myapp` → Interactive configuration wizard
|
|
71
|
-
2. `
|
|
72
|
-
3.
|
|
73
|
-
4.
|
|
74
|
-
5.
|
|
79
|
+
2. `cd myapp` → Navigate to your project directory
|
|
80
|
+
3. `quickscale apply` → Generates production-ready Django project
|
|
81
|
+
4. Add your custom Django apps and features
|
|
82
|
+
5. Build your unique client application
|
|
83
|
+
6. Deploy to Railway with `quickscale deploy railway` (or use standard Django deployment)
|
|
75
84
|
|
|
76
85
|
ℹ️ The [MVP Feature Matrix](./docs/technical/decisions.md#mvp-feature-matrix-authoritative) is the single source of truth for what's in or out.
|
|
77
86
|
|
|
@@ -79,6 +88,7 @@ See [decisions.md - Glossary section](./docs/technical/decisions.md#document-res
|
|
|
79
88
|
|
|
80
89
|
Running `quickscale plan myapp && quickscale apply` generates a **production-ready Django project** with:
|
|
81
90
|
|
|
91
|
+
- ✅ **React + shadcn/ui** frontend (TypeScript, Vite, Tailwind CSS) — **NEW in v0.74.0**
|
|
82
92
|
- ✅ **Docker** setup (development + production)
|
|
83
93
|
- ✅ **PostgreSQL** configuration
|
|
84
94
|
- ✅ **Environment-based** settings (dev/prod split)
|
|
@@ -90,6 +100,8 @@ Running `quickscale plan myapp && quickscale apply` generates a **production-rea
|
|
|
90
100
|
- ✅ **Poetry** for dependency management
|
|
91
101
|
- ✅ **One-Command Deployment**: Deploy to Railway with `quickscale deploy railway` - fully automated setup
|
|
92
102
|
|
|
103
|
+
**Alternative**: Use `quickscale plan myapp --theme showcase_html` for pure HTML/CSS (simpler projects).
|
|
104
|
+
|
|
93
105
|
**See the complete project structure:** [scaffolding.md - Generated Project Output](./docs/technical/scaffolding.md#5-generated-project-output)
|
|
94
106
|
|
|
95
107
|
The generated project is **yours to own and modify** - no vendor lock-in, just Django best practices.
|
|
@@ -107,8 +119,148 @@ See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detai
|
|
|
107
119
|
|
|
108
120
|
---
|
|
109
121
|
|
|
122
|
+
## Installation
|
|
123
|
+
|
|
124
|
+
QuickScale can be installed in two ways:
|
|
125
|
+
|
|
126
|
+
### Method 1: Install from PyPI (Recommended)
|
|
127
|
+
|
|
128
|
+
**Quick install:**
|
|
129
|
+
```bash
|
|
130
|
+
pip install quickscale
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Or with Poetry:**
|
|
134
|
+
```bash
|
|
135
|
+
poetry add quickscale
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Then use directly:**
|
|
139
|
+
```bash
|
|
140
|
+
quickscale plan myapp
|
|
141
|
+
cd myapp
|
|
142
|
+
quickscale apply
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Method 2: Install from Source
|
|
146
|
+
|
|
147
|
+
**For those who prefer building from source:**
|
|
148
|
+
```bash
|
|
149
|
+
git clone https://github.com/Experto-AI/quickscale.git
|
|
150
|
+
cd quickscale
|
|
151
|
+
./scripts/install_global.sh
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Then use directly:**
|
|
155
|
+
```bash
|
|
156
|
+
quickscale plan myapp
|
|
157
|
+
cd myapp
|
|
158
|
+
quickscale apply
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Both methods use the same command syntax:** `quickscale plan`, `quickscale apply`, etc.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### For Contributors
|
|
166
|
+
|
|
167
|
+
If you want to contribute to QuickScale development, see the [Development Guide](./docs/technical/development.md) for setup instructions.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🚀 5-Minute Quickstart
|
|
172
|
+
|
|
173
|
+
**Want to see QuickScale in action right now?** Here's the fastest path to a working Django SaaS:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 1. Install QuickScale
|
|
177
|
+
./scripts/install_global.sh
|
|
178
|
+
|
|
179
|
+
# 2. Create your project configuration
|
|
180
|
+
quickscale plan myapp
|
|
181
|
+
# → Accept defaults: theme (showcase_html), no modules, Docker enabled
|
|
182
|
+
|
|
183
|
+
# 3. Generate and enter the project
|
|
184
|
+
cd myapp
|
|
185
|
+
quickscale apply
|
|
186
|
+
# → Docker services auto-start! (default: docker.start=true)
|
|
187
|
+
|
|
188
|
+
# 4. Setup and verify
|
|
189
|
+
quickscale manage migrate
|
|
190
|
+
quickscale manage createsuperuser
|
|
191
|
+
quickscale ps # Check services are running
|
|
192
|
+
|
|
193
|
+
# 5. Open http://localhost:8000
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**That's it!** 🎉 You now have a production-ready Django SaaS running with Docker + PostgreSQL.
|
|
197
|
+
|
|
198
|
+
**Note**: Services started automatically during `quickscale apply`. No need to run `quickscale up` manually!
|
|
199
|
+
|
|
200
|
+
**Prefer native Python?** After step 3, run:
|
|
201
|
+
```bash
|
|
202
|
+
poetry install
|
|
203
|
+
poetry run python manage.py migrate
|
|
204
|
+
poetry run python manage.py runserver
|
|
205
|
+
# Open http://localhost:8000
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Understanding Docker Auto-Start
|
|
211
|
+
|
|
212
|
+
**When does `quickscale apply` start Docker automatically?**
|
|
213
|
+
|
|
214
|
+
✅ **Services auto-start** (no `quickscale up` needed):
|
|
215
|
+
- First-time project generation
|
|
216
|
+
- When `docker.start: true` in quickscale.yml (**default**)
|
|
217
|
+
- When `--no-docker` flag is NOT used
|
|
218
|
+
|
|
219
|
+
❌ **Manual start required** (`quickscale up` needed):
|
|
220
|
+
- You set `docker.start: false` during `quickscale plan` wizard
|
|
221
|
+
- After stopping services with `quickscale down`
|
|
222
|
+
- When running `quickscale apply --no-docker`
|
|
223
|
+
- Adding modules to existing project (incremental apply)
|
|
110
224
|
|
|
111
|
-
|
|
225
|
+
**Example - No manual start needed:**
|
|
226
|
+
```bash
|
|
227
|
+
quickscale plan myapp # Accept defaults (docker.start: true)
|
|
228
|
+
cd myapp
|
|
229
|
+
quickscale apply # ← Docker services auto-start!
|
|
230
|
+
|
|
231
|
+
# Already running - just check status:
|
|
232
|
+
quickscale ps
|
|
233
|
+
curl http://localhost:8000 # ✅ Works immediately
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Example - Manual start needed:**
|
|
237
|
+
```bash
|
|
238
|
+
quickscale plan myapp
|
|
239
|
+
# During wizard, set: "Docker start? [Y/n]: n"
|
|
240
|
+
cd myapp
|
|
241
|
+
quickscale apply # Services do NOT start
|
|
242
|
+
|
|
243
|
+
# Must start manually:
|
|
244
|
+
quickscale up
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Docker configuration options** in quickscale.yml:
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
docker:
|
|
251
|
+
start: true # Auto-start services during apply?
|
|
252
|
+
build: true # Rebuild images? (slower but ensures latest)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- `start: true` + `build: true` → Full rebuild + start (slowest, most reliable)
|
|
256
|
+
- `start: true` + `build: false` → Start with cached images (faster)
|
|
257
|
+
- `start: false` → Manual control with `quickscale up`
|
|
258
|
+
|
|
259
|
+
**Need more details?** See [Docker Workflows Guide](./docs/technical/docker_workflows.md) for comprehensive scenarios and troubleshooting.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Full Documentation & Setup
|
|
112
264
|
|
|
113
265
|
```bash
|
|
114
266
|
# Install QuickScale globally
|
|
@@ -117,11 +269,11 @@ See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detai
|
|
|
117
269
|
# Create a configuration interactively
|
|
118
270
|
quickscale plan myapp
|
|
119
271
|
# → Select theme, modules, Docker options
|
|
120
|
-
# → Generates quickscale.yml
|
|
272
|
+
# → Generates quickscale.yml in myapp/ directory
|
|
121
273
|
|
|
122
|
-
#
|
|
123
|
-
quickscale apply
|
|
274
|
+
# Navigate to project and execute the configuration
|
|
124
275
|
cd myapp
|
|
276
|
+
quickscale apply
|
|
125
277
|
```
|
|
126
278
|
|
|
127
279
|
**Choose your development workflow:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "quickscale-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.74.2"
|
|
4
4
|
description = "Command-line interface for QuickScale Django project generator"
|
|
5
5
|
authors = ["Experto AI <victor@experto.ai>"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -25,7 +25,7 @@ packages = [{include = "quickscale_cli", from = "src"}]
|
|
|
25
25
|
[tool.poetry.dependencies]
|
|
26
26
|
python = "^3.12"
|
|
27
27
|
click = "^8.3.1"
|
|
28
|
-
quickscale-core = "^0.
|
|
28
|
+
quickscale-core = "^0.74.2"
|
|
29
29
|
|
|
30
30
|
[tool.poetry.group.dev.dependencies]
|
|
31
31
|
# Dev dependencies centralized in root pyproject.toml
|
|
@@ -49,6 +49,7 @@ addopts = [
|
|
|
49
49
|
"--cov=quickscale_cli",
|
|
50
50
|
"--cov-report=term-missing",
|
|
51
51
|
"--cov-report=html",
|
|
52
|
+
"--cov-fail-under=90",
|
|
52
53
|
"-m", "not e2e", # Skip E2E tests by default (run with: pytest -m e2e)
|
|
53
54
|
]
|
|
54
55
|
markers = [
|
|
@@ -57,6 +58,10 @@ markers = [
|
|
|
57
58
|
"e2e: end-to-end tests",
|
|
58
59
|
]
|
|
59
60
|
|
|
61
|
+
[tool.coverage.report]
|
|
62
|
+
fail_under = 90
|
|
63
|
+
# Per-file minimum target: 80% (enforced via code review)
|
|
64
|
+
|
|
60
65
|
# Ruff and mypy configurations centralized in root ruff.toml and mypy.ini
|
|
61
66
|
# Package-specific isort override
|
|
62
67
|
[tool.ruff.lint.isort]
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Auto-generated by scripts/version_tool.sh
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.74.2"
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/apply_command.py
RENAMED
|
@@ -92,18 +92,21 @@ def _generate_project(config: QuickScaleConfig, output_path: Path) -> bool:
|
|
|
92
92
|
click.echo(f"⏳ Generating project: {config.project.name}...")
|
|
93
93
|
|
|
94
94
|
# Validate theme availability
|
|
95
|
-
if config.project.theme
|
|
95
|
+
if config.project.theme == "showcase_htmx":
|
|
96
96
|
click.secho(
|
|
97
97
|
f"❌ Error: Theme '{config.project.theme}' is not yet implemented",
|
|
98
98
|
fg="red",
|
|
99
99
|
err=True,
|
|
100
100
|
)
|
|
101
101
|
click.echo(
|
|
102
|
-
|
|
102
|
+
"\n💡 The 'showcase_htmx' theme is planned for a future release:",
|
|
103
|
+
err=True,
|
|
104
|
+
)
|
|
105
|
+
click.echo(" - showcase_htmx: Coming in v0.78.0", err=True)
|
|
106
|
+
click.echo(
|
|
107
|
+
" - Use 'showcase_react' (default) or 'showcase_html' instead",
|
|
103
108
|
err=True,
|
|
104
109
|
)
|
|
105
|
-
click.echo(" - showcase_htmx: Coming in v0.70.0", err=True)
|
|
106
|
-
click.echo(" - showcase_react: Coming in v0.71.0", err=True)
|
|
107
110
|
return False
|
|
108
111
|
|
|
109
112
|
generator = ProjectGenerator(theme=config.project.theme)
|
|
@@ -197,18 +200,54 @@ def _run_migrations(project_path: Path) -> bool:
|
|
|
197
200
|
)[0]
|
|
198
201
|
|
|
199
202
|
|
|
200
|
-
def _start_docker(
|
|
201
|
-
|
|
203
|
+
def _start_docker(
|
|
204
|
+
project_path: Path, build: bool = True, verbose: bool = False
|
|
205
|
+
) -> bool:
|
|
206
|
+
"""Start Docker services using quickscale up
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
project_path: Path to the project directory
|
|
210
|
+
build: Whether to build images before starting
|
|
211
|
+
verbose: Whether to show Docker build output (useful for debugging)
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
True if Docker started successfully, False otherwise
|
|
215
|
+
"""
|
|
202
216
|
cmd = ["quickscale", "up"]
|
|
203
217
|
if build:
|
|
204
218
|
cmd.append("--build")
|
|
205
219
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
if verbose:
|
|
221
|
+
# Show Docker build output for debugging
|
|
222
|
+
click.echo("⏳ Starting Docker services (showing build output)...")
|
|
223
|
+
click.echo("=" * 50)
|
|
224
|
+
try:
|
|
225
|
+
result = subprocess.run(
|
|
226
|
+
cmd,
|
|
227
|
+
cwd=project_path,
|
|
228
|
+
text=True,
|
|
229
|
+
check=False,
|
|
230
|
+
)
|
|
231
|
+
click.echo("=" * 50)
|
|
232
|
+
if result.returncode == 0:
|
|
233
|
+
click.secho("✅ Starting Docker services", fg="green")
|
|
234
|
+
return True
|
|
235
|
+
else:
|
|
236
|
+
click.secho("❌ Starting Docker services failed", fg="red")
|
|
237
|
+
return False
|
|
238
|
+
except FileNotFoundError:
|
|
239
|
+
click.secho(f"❌ Command not found: {cmd[0]}", fg="red", err=True)
|
|
240
|
+
return False
|
|
241
|
+
except Exception as e:
|
|
242
|
+
click.secho(f"❌ Unexpected error: {e}", fg="red", err=True)
|
|
243
|
+
return False
|
|
244
|
+
else:
|
|
245
|
+
success, _ = _run_command(
|
|
246
|
+
cmd,
|
|
247
|
+
project_path,
|
|
248
|
+
"Starting Docker services",
|
|
249
|
+
)
|
|
250
|
+
return success
|
|
212
251
|
|
|
213
252
|
|
|
214
253
|
def _load_module_manifests(
|
|
@@ -646,7 +685,11 @@ def _prepare_apply_context(config_path: Path) -> ApplyContext:
|
|
|
646
685
|
|
|
647
686
|
|
|
648
687
|
def _execute_apply_steps(
|
|
649
|
-
ctx: ApplyContext,
|
|
688
|
+
ctx: ApplyContext,
|
|
689
|
+
force: bool,
|
|
690
|
+
no_docker: bool,
|
|
691
|
+
no_modules: bool,
|
|
692
|
+
verbose_docker: bool = False,
|
|
650
693
|
) -> None:
|
|
651
694
|
"""Execute the apply steps after confirmation."""
|
|
652
695
|
click.echo("\n" + "=" * 50)
|
|
@@ -685,7 +728,9 @@ def _execute_apply_steps(
|
|
|
685
728
|
|
|
686
729
|
# Start Docker
|
|
687
730
|
if not no_docker and ctx.qs_config.docker.start:
|
|
688
|
-
if not _start_docker(
|
|
731
|
+
if not _start_docker(
|
|
732
|
+
ctx.output_path, ctx.qs_config.docker.build, verbose_docker
|
|
733
|
+
):
|
|
689
734
|
click.secho("⚠️ Docker start failed, continuing...", fg="yellow")
|
|
690
735
|
|
|
691
736
|
# Save state
|
|
@@ -724,7 +769,14 @@ def _execute_apply_steps(
|
|
|
724
769
|
is_flag=True,
|
|
725
770
|
help="Skip module embedding",
|
|
726
771
|
)
|
|
727
|
-
|
|
772
|
+
@click.option(
|
|
773
|
+
"--verbose-docker",
|
|
774
|
+
is_flag=True,
|
|
775
|
+
help="Show Docker build output (useful for debugging build issues)",
|
|
776
|
+
)
|
|
777
|
+
def apply(
|
|
778
|
+
config: str, force: bool, no_docker: bool, no_modules: bool, verbose_docker: bool
|
|
779
|
+
) -> None:
|
|
728
780
|
"""
|
|
729
781
|
Execute project configuration from quickscale.yml.
|
|
730
782
|
|
|
@@ -737,6 +789,7 @@ def apply(config: str, force: bool, no_docker: bool, no_modules: bool) -> None:
|
|
|
737
789
|
quickscale apply myapp/quickscale.yml # Use specific config file
|
|
738
790
|
quickscale apply --force # Overwrite existing project
|
|
739
791
|
quickscale apply --no-docker # Skip Docker operations
|
|
792
|
+
quickscale apply --verbose-docker # Show Docker build output
|
|
740
793
|
|
|
741
794
|
\b
|
|
742
795
|
Execution Order:
|
|
@@ -760,10 +813,23 @@ def apply(config: str, force: bool, no_docker: bool, no_modules: bool) -> None:
|
|
|
760
813
|
# Check output directory
|
|
761
814
|
_check_output_directory(ctx.output_path, ctx.existing_state, force)
|
|
762
815
|
|
|
816
|
+
# Ask about Docker build output visibility if Docker will be started
|
|
817
|
+
show_docker_output = verbose_docker
|
|
818
|
+
if (
|
|
819
|
+
not no_docker
|
|
820
|
+
and ctx.qs_config.docker.start
|
|
821
|
+
and ctx.qs_config.docker.build
|
|
822
|
+
and not verbose_docker
|
|
823
|
+
):
|
|
824
|
+
show_docker_output = click.confirm(
|
|
825
|
+
"\n🐳 Show Docker build output? (useful for debugging build issues)",
|
|
826
|
+
default=False,
|
|
827
|
+
)
|
|
828
|
+
|
|
763
829
|
# Confirm before proceeding
|
|
764
830
|
if not click.confirm("\n❓ Proceed with apply?", default=True):
|
|
765
831
|
click.echo("❌ Cancelled")
|
|
766
832
|
raise click.Abort()
|
|
767
833
|
|
|
768
834
|
# Execute apply steps
|
|
769
|
-
_execute_apply_steps(ctx, force, no_docker, no_modules)
|
|
835
|
+
_execute_apply_steps(ctx, force, no_docker, no_modules, show_docker_output)
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/development_commands.py
RENAMED
|
@@ -17,6 +17,7 @@ from quickscale_cli.utils.docker_utils import (
|
|
|
17
17
|
wait_for_port_release,
|
|
18
18
|
)
|
|
19
19
|
from quickscale_cli.utils.project_manager import (
|
|
20
|
+
get_project_config,
|
|
20
21
|
get_web_container_name,
|
|
21
22
|
is_in_quickscale_project,
|
|
22
23
|
)
|
|
@@ -135,8 +136,14 @@ def up(build: bool, no_cache: bool) -> None:
|
|
|
135
136
|
"""Start Docker services for development."""
|
|
136
137
|
_validate_project_and_docker()
|
|
137
138
|
|
|
139
|
+
# Load config to check for default build behavior
|
|
140
|
+
config = get_project_config()
|
|
141
|
+
should_build = build
|
|
142
|
+
if not build and config and config.docker:
|
|
143
|
+
should_build = config.docker.build
|
|
144
|
+
|
|
138
145
|
# Check if dependencies changed and suggest rebuild
|
|
139
|
-
if not
|
|
146
|
+
if not should_build and _dependencies_changed_since_last_build():
|
|
140
147
|
click.secho(
|
|
141
148
|
"⚠️ Warning: Dependencies may have changed since last Docker build",
|
|
142
149
|
fg="yellow",
|
|
@@ -156,10 +163,10 @@ def up(build: bool, no_cache: bool) -> None:
|
|
|
156
163
|
|
|
157
164
|
try:
|
|
158
165
|
compose_cmd = get_docker_compose_command()
|
|
159
|
-
_run_docker_compose_up(compose_cmd,
|
|
166
|
+
_run_docker_compose_up(compose_cmd, should_build, no_cache)
|
|
160
167
|
|
|
161
168
|
# Update build timestamp if build was performed
|
|
162
|
-
if
|
|
169
|
+
if should_build:
|
|
163
170
|
_update_last_build_timestamp()
|
|
164
171
|
|
|
165
172
|
except subprocess.CalledProcessError as e:
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/module_config.py
RENAMED
|
@@ -7,7 +7,7 @@ including interactive configuration prompts and settings application.
|
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any, Optional
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
|
|
@@ -688,6 +688,50 @@ def configure_crm_module(non_interactive: bool = False) -> dict[str, Any]:
|
|
|
688
688
|
return config
|
|
689
689
|
|
|
690
690
|
|
|
691
|
+
def _get_dependency_version(content: str, package: str) -> Optional[str]:
|
|
692
|
+
"""Extract dependency version from pyproject.toml content."""
|
|
693
|
+
match = re.search(
|
|
694
|
+
rf'{package}\s*=\s*["\']([^"\']+)["\']',
|
|
695
|
+
content,
|
|
696
|
+
)
|
|
697
|
+
return match.group(1) if match else None
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def _update_pyproject_toml(
|
|
701
|
+
pyproject_path: Path,
|
|
702
|
+
content: str,
|
|
703
|
+
drf_ver: Optional[str],
|
|
704
|
+
filter_ver: Optional[str],
|
|
705
|
+
) -> None:
|
|
706
|
+
"""Update pyproject.toml with new dependencies."""
|
|
707
|
+
dependencies_pattern = r"(\[tool\.poetry\.dependencies\][^\[]*)"
|
|
708
|
+
match = re.search(dependencies_pattern, content, re.DOTALL)
|
|
709
|
+
if not match:
|
|
710
|
+
return
|
|
711
|
+
|
|
712
|
+
dependencies_section = match.group(1)
|
|
713
|
+
additions = ""
|
|
714
|
+
if drf_ver:
|
|
715
|
+
additions += f'\ndjangorestframework = "{drf_ver}"'
|
|
716
|
+
if filter_ver:
|
|
717
|
+
additions += f'\ndjango-filter = "{filter_ver}"'
|
|
718
|
+
|
|
719
|
+
updated_dependencies = re.sub(
|
|
720
|
+
r'(python = "[^"]*")',
|
|
721
|
+
rf"\1{additions}",
|
|
722
|
+
dependencies_section,
|
|
723
|
+
)
|
|
724
|
+
content = content.replace(dependencies_section, updated_dependencies)
|
|
725
|
+
|
|
726
|
+
with open(pyproject_path, "w") as f:
|
|
727
|
+
f.write(content)
|
|
728
|
+
|
|
729
|
+
if drf_ver:
|
|
730
|
+
click.secho(" ✅ Added djangorestframework to pyproject.toml", fg="green")
|
|
731
|
+
if filter_ver:
|
|
732
|
+
click.secho(" ✅ Added django-filter to pyproject.toml", fg="green")
|
|
733
|
+
|
|
734
|
+
|
|
691
735
|
def _add_drf_and_filter_dependencies(project_path: Path, pyproject_path: Path) -> None:
|
|
692
736
|
"""Add djangorestframework and django-filter dependencies to project's pyproject.toml."""
|
|
693
737
|
with open(pyproject_path) as f:
|
|
@@ -704,78 +748,91 @@ def _add_drf_and_filter_dependencies(project_path: Path, pyproject_path: Path) -
|
|
|
704
748
|
err=True,
|
|
705
749
|
)
|
|
706
750
|
click.echo(f"Expected file: {crm_pyproject_path}", err=True)
|
|
707
|
-
click.echo(
|
|
708
|
-
"This indicates the CRM module was not embedded correctly.",
|
|
709
|
-
err=True,
|
|
710
|
-
)
|
|
711
751
|
raise click.Abort()
|
|
712
752
|
|
|
713
753
|
try:
|
|
714
754
|
with open(crm_pyproject_path) as f:
|
|
715
755
|
crm_pyproject_content = f.read()
|
|
716
756
|
|
|
717
|
-
# Extract djangorestframework version
|
|
718
757
|
drf_version = None
|
|
719
758
|
if "djangorestframework" not in pyproject_content:
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
crm_pyproject_content,
|
|
759
|
+
drf_version = _get_dependency_version(
|
|
760
|
+
crm_pyproject_content, "djangorestframework"
|
|
723
761
|
)
|
|
724
|
-
if drf_match:
|
|
725
|
-
drf_version = drf_match.group(1)
|
|
726
762
|
|
|
727
|
-
# Extract django-filter version
|
|
728
763
|
filter_version = None
|
|
729
764
|
if "django-filter" not in pyproject_content:
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
crm_pyproject_content,
|
|
765
|
+
filter_version = _get_dependency_version(
|
|
766
|
+
crm_pyproject_content, "django-filter"
|
|
733
767
|
)
|
|
734
|
-
if filter_match:
|
|
735
|
-
filter_version = filter_match.group(1)
|
|
736
768
|
|
|
737
|
-
# Add dependencies if needed
|
|
738
769
|
if drf_version or filter_version:
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
dependencies_section = match.group(1)
|
|
743
|
-
additions = ""
|
|
744
|
-
if drf_version:
|
|
745
|
-
additions += f'\ndjangorestframework = "{drf_version}"'
|
|
746
|
-
if filter_version:
|
|
747
|
-
additions += f'\ndjango-filter = "{filter_version}"'
|
|
748
|
-
|
|
749
|
-
updated_dependencies = re.sub(
|
|
750
|
-
r'(python = "[^"]*")',
|
|
751
|
-
rf"\1{additions}",
|
|
752
|
-
dependencies_section,
|
|
753
|
-
)
|
|
754
|
-
pyproject_content = pyproject_content.replace(
|
|
755
|
-
dependencies_section, updated_dependencies
|
|
756
|
-
)
|
|
770
|
+
_update_pyproject_toml(
|
|
771
|
+
pyproject_path, pyproject_content, drf_version, filter_version
|
|
772
|
+
)
|
|
757
773
|
|
|
758
|
-
with open(pyproject_path, "w") as f:
|
|
759
|
-
f.write(pyproject_content)
|
|
760
|
-
|
|
761
|
-
if drf_version:
|
|
762
|
-
click.secho(
|
|
763
|
-
" ✅ Added djangorestframework to pyproject.toml", fg="green"
|
|
764
|
-
)
|
|
765
|
-
if filter_version:
|
|
766
|
-
click.secho(
|
|
767
|
-
" ✅ Added django-filter to pyproject.toml", fg="green"
|
|
768
|
-
)
|
|
769
774
|
except (FileNotFoundError, AttributeError) as e:
|
|
770
775
|
click.secho(
|
|
771
776
|
f"❌ Error: Failed to parse dependencies from CRM module: {e}",
|
|
772
777
|
fg="red",
|
|
773
778
|
err=True,
|
|
774
779
|
)
|
|
775
|
-
click.echo(f"File: {crm_pyproject_path}", err=True)
|
|
776
780
|
raise click.Abort()
|
|
777
781
|
|
|
778
782
|
|
|
783
|
+
def _get_crm_settings_addition(config: dict[str, Any]) -> str:
|
|
784
|
+
"""Generate settings addition for CRM module."""
|
|
785
|
+
addition = f"""
|
|
786
|
+
# CRM Module Settings
|
|
787
|
+
CRM_DEALS_PER_PAGE = {config["deals_per_page"]}
|
|
788
|
+
CRM_CONTACTS_PER_PAGE = {config["contacts_per_page"]}
|
|
789
|
+
CRM_ENABLE_API = {config["enable_api"]}
|
|
790
|
+
"""
|
|
791
|
+
if config["enable_api"]:
|
|
792
|
+
addition += """
|
|
793
|
+
# REST Framework Settings
|
|
794
|
+
REST_FRAMEWORK = {
|
|
795
|
+
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|
796
|
+
"rest_framework.authentication.SessionAuthentication",
|
|
797
|
+
],
|
|
798
|
+
"DEFAULT_PERMISSION_CLASSES": [
|
|
799
|
+
"rest_framework.permissions.IsAuthenticated",
|
|
800
|
+
],
|
|
801
|
+
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
|
802
|
+
"PAGE_SIZE": 25,
|
|
803
|
+
"DEFAULT_FILTER_BACKENDS": [
|
|
804
|
+
"django_filters.rest_framework.DjangoFilterBackend",
|
|
805
|
+
"rest_framework.filters.SearchFilter",
|
|
806
|
+
"rest_framework.filters.OrderingFilter",
|
|
807
|
+
],
|
|
808
|
+
}
|
|
809
|
+
"""
|
|
810
|
+
return addition
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
def _update_crm_urls(urls_path: Path) -> None:
|
|
814
|
+
"""Add CRM URLs to project's urls.py."""
|
|
815
|
+
if not urls_path.exists():
|
|
816
|
+
return
|
|
817
|
+
|
|
818
|
+
with open(urls_path) as f:
|
|
819
|
+
urls_content = f.read()
|
|
820
|
+
|
|
821
|
+
if "quickscale_modules_crm" in urls_content:
|
|
822
|
+
return
|
|
823
|
+
|
|
824
|
+
if "urlpatterns = [" in urls_content:
|
|
825
|
+
urls_addition = ' path("crm/", include("quickscale_modules_crm.urls")),\n'
|
|
826
|
+
urls_content = urls_content.replace(
|
|
827
|
+
"urlpatterns = [", "urlpatterns = [\n" + urls_addition
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
with open(urls_path, "w") as f:
|
|
831
|
+
f.write(urls_content)
|
|
832
|
+
|
|
833
|
+
click.secho(" ✅ Updated urls.py with CRM URLs", fg="green")
|
|
834
|
+
|
|
835
|
+
|
|
779
836
|
def apply_crm_configuration(project_path: Path, config: dict[str, Any]) -> None:
|
|
780
837
|
"""Apply CRM module configuration to project settings"""
|
|
781
838
|
# QuickScale uses settings/base.py and project_name/urls.py structure
|
|
@@ -807,10 +864,7 @@ def apply_crm_configuration(project_path: Path, config: dict[str, Any]) -> None:
|
|
|
807
864
|
required_apps = ["rest_framework", "django_filters", "quickscale_modules_crm"]
|
|
808
865
|
new_apps = _filter_new_apps(settings_content, required_apps)
|
|
809
866
|
|
|
810
|
-
if
|
|
811
|
-
click.echo("ℹ️ All required apps already in INSTALLED_APPS")
|
|
812
|
-
else:
|
|
813
|
-
# Build the INSTALLED_APPS addition with only new apps
|
|
867
|
+
if new_apps:
|
|
814
868
|
apps_list = ",\n ".join([f'"{app}"' for app in new_apps])
|
|
815
869
|
installed_apps_addition = f"""
|
|
816
870
|
# QuickScale CRM Module - Added by quickscale embed
|
|
@@ -818,64 +872,20 @@ INSTALLED_APPS += [
|
|
|
818
872
|
{apps_list},
|
|
819
873
|
]
|
|
820
874
|
"""
|
|
821
|
-
# Append INSTALLED_APPS to settings.py
|
|
822
875
|
with open(settings_path, "a") as f:
|
|
823
876
|
f.write("\n" + installed_apps_addition)
|
|
877
|
+
else:
|
|
878
|
+
click.echo("ℹ️ All required apps already in INSTALLED_APPS")
|
|
824
879
|
|
|
825
|
-
# Add CRM settings
|
|
826
|
-
settings_addition =
|
|
827
|
-
# CRM Module Settings
|
|
828
|
-
CRM_DEALS_PER_PAGE = {config["deals_per_page"]}
|
|
829
|
-
CRM_CONTACTS_PER_PAGE = {config["contacts_per_page"]}
|
|
830
|
-
CRM_ENABLE_API = {config["enable_api"]}
|
|
831
|
-
"""
|
|
832
|
-
|
|
833
|
-
# Add REST Framework settings if enabling API
|
|
834
|
-
if config["enable_api"]:
|
|
835
|
-
settings_addition += """
|
|
836
|
-
# REST Framework Settings
|
|
837
|
-
REST_FRAMEWORK = {
|
|
838
|
-
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|
839
|
-
"rest_framework.authentication.SessionAuthentication",
|
|
840
|
-
],
|
|
841
|
-
"DEFAULT_PERMISSION_CLASSES": [
|
|
842
|
-
"rest_framework.permissions.IsAuthenticated",
|
|
843
|
-
],
|
|
844
|
-
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
|
845
|
-
"PAGE_SIZE": 25,
|
|
846
|
-
"DEFAULT_FILTER_BACKENDS": [
|
|
847
|
-
"django_filters.rest_framework.DjangoFilterBackend",
|
|
848
|
-
"rest_framework.filters.SearchFilter",
|
|
849
|
-
"rest_framework.filters.OrderingFilter",
|
|
850
|
-
],
|
|
851
|
-
}
|
|
852
|
-
"""
|
|
853
|
-
|
|
854
|
-
# Append settings to settings.py
|
|
880
|
+
# Add CRM settings and REST framework settings
|
|
881
|
+
settings_addition = _get_crm_settings_addition(config)
|
|
855
882
|
with open(settings_path, "a") as f:
|
|
856
883
|
f.write(settings_addition)
|
|
857
884
|
|
|
858
885
|
click.secho(" ✅ Updated settings.py with CRM configuration", fg="green")
|
|
859
886
|
|
|
860
887
|
# Update urls.py
|
|
861
|
-
|
|
862
|
-
with open(urls_path) as f:
|
|
863
|
-
urls_content = f.read()
|
|
864
|
-
|
|
865
|
-
if "quickscale_modules_crm" not in urls_content:
|
|
866
|
-
# Find urlpatterns and add CRM URLs
|
|
867
|
-
if "urlpatterns = [" in urls_content:
|
|
868
|
-
urls_addition = (
|
|
869
|
-
' path("crm/", include("quickscale_modules_crm.urls")),\n'
|
|
870
|
-
)
|
|
871
|
-
urls_content = urls_content.replace(
|
|
872
|
-
"urlpatterns = [", "urlpatterns = [\n" + urls_addition
|
|
873
|
-
)
|
|
874
|
-
|
|
875
|
-
with open(urls_path, "w") as f:
|
|
876
|
-
f.write(urls_content)
|
|
877
|
-
|
|
878
|
-
click.secho(" ✅ Updated urls.py with CRM URLs", fg="green")
|
|
888
|
+
_update_crm_urls(urls_path)
|
|
879
889
|
|
|
880
890
|
# Show configuration summary
|
|
881
891
|
click.echo("\n📋 Configuration applied:")
|
|
@@ -20,9 +20,9 @@ from quickscale_cli.schema.state_schema import QuickScaleState, StateManager
|
|
|
20
20
|
|
|
21
21
|
# Available themes for selection
|
|
22
22
|
AVAILABLE_THEMES = [
|
|
23
|
-
("
|
|
24
|
-
("
|
|
25
|
-
("
|
|
23
|
+
("showcase_react", "React + TypeScript + shadcn/ui (default, production-ready)"),
|
|
24
|
+
("showcase_html", "Pure HTML + CSS (simpler alternative)"),
|
|
25
|
+
("showcase_htmx", "HTMX + Alpine.js (coming in v0.78.0)"),
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
# Available modules for selection
|
|
@@ -53,12 +53,12 @@ def _get_theme_by_name(name: str) -> str | None:
|
|
|
53
53
|
|
|
54
54
|
def _confirm_unimplemented_theme(theme_id: str) -> bool:
|
|
55
55
|
"""Confirm use of unimplemented theme"""
|
|
56
|
-
if theme_id
|
|
56
|
+
if theme_id in ("showcase_html", "showcase_react"):
|
|
57
57
|
return True
|
|
58
58
|
|
|
59
59
|
click.secho(
|
|
60
60
|
f"\n⚠️ Theme '{theme_id}' is not yet implemented. "
|
|
61
|
-
"It will be available in a future release.",
|
|
61
|
+
"It will be available in a future release (v0.78.0).",
|
|
62
62
|
fg="yellow",
|
|
63
63
|
)
|
|
64
64
|
return click.confirm("Use this theme anyway?", default=False)
|
|
@@ -68,7 +68,7 @@ def _select_theme() -> str:
|
|
|
68
68
|
"""Interactive theme selection"""
|
|
69
69
|
click.echo("\n🎨 Select a theme for your project:")
|
|
70
70
|
for i, (tid, description) in enumerate(AVAILABLE_THEMES, start=1):
|
|
71
|
-
status = " ⚠️ not yet implemented" if tid
|
|
71
|
+
status = " ⚠️ not yet implemented" if tid == "showcase_htmx" else ""
|
|
72
72
|
click.echo(f" {i}. {tid} - {description}{status}")
|
|
73
73
|
|
|
74
74
|
while True:
|
|
@@ -611,15 +611,16 @@ def _validate_new_project_name(name: str | None) -> str:
|
|
|
611
611
|
)
|
|
612
612
|
raise click.Abort()
|
|
613
613
|
|
|
614
|
-
|
|
614
|
+
package_name = name.replace("-", "_")
|
|
615
|
+
if not package_name.isidentifier():
|
|
615
616
|
click.secho(
|
|
616
617
|
f"\n❌ Error: '{name}' is not a valid project name",
|
|
617
618
|
fg="red",
|
|
618
619
|
err=True,
|
|
619
620
|
)
|
|
620
621
|
click.echo(
|
|
621
|
-
" Project name must
|
|
622
|
-
"(letters, numbers, underscores, not starting with a number)",
|
|
622
|
+
" Project name must check out as a valid Python identifier when hyphens are replaced with underscores "
|
|
623
|
+
"(letters, numbers, underscores, hyphens, not starting with a number)",
|
|
623
624
|
err=True,
|
|
624
625
|
)
|
|
625
626
|
raise click.Abort()
|
|
@@ -159,12 +159,13 @@ def _validate_project_section(data: dict, yaml_content: str) -> tuple[str, str]:
|
|
|
159
159
|
"'project.name' must be a non-empty string", line=line
|
|
160
160
|
)
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
package_name = project_name.replace("-", "_")
|
|
163
|
+
if not package_name.isidentifier():
|
|
163
164
|
line = _find_line_number(yaml_content, "name")
|
|
164
165
|
raise ConfigValidationError(
|
|
165
166
|
f"Invalid project name '{project_name}'",
|
|
166
167
|
line=line,
|
|
167
|
-
suggestion="Project name must be a valid Python identifier (letters, numbers, underscores, not starting with a number)",
|
|
168
|
+
suggestion="Project name must be a valid Python identifier (letters, numbers, underscores, hyphens, not starting with a number)",
|
|
168
169
|
)
|
|
169
170
|
|
|
170
171
|
theme = project_data.get("theme", "showcase_html")
|
|
@@ -3,9 +3,21 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from quickscale_cli.schema.config_schema import QuickScaleConfig, validate_config
|
|
6
7
|
from .docker_utils import find_docker_compose, get_running_containers
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
def get_project_config() -> QuickScaleConfig | None:
|
|
11
|
+
"""Load and validate quickscale.yml from current directory if it exists."""
|
|
12
|
+
config_path = Path.cwd() / "quickscale.yml"
|
|
13
|
+
if not config_path.exists():
|
|
14
|
+
return None
|
|
15
|
+
try:
|
|
16
|
+
return validate_config(config_path.read_text())
|
|
17
|
+
except Exception:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
|
|
9
21
|
def get_project_state() -> dict[str, Any]:
|
|
10
22
|
"""Get comprehensive project state including directory and containers."""
|
|
11
23
|
try:
|
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/deployment_commands.py
RENAMED
|
File without changes
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/module_commands.py
RENAMED
|
File without changes
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/remove_command.py
RENAMED
|
File without changes
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/commands/status_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.73.0 → quickscale_cli-0.74.2}/src/quickscale_cli/utils/dependency_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|