create-odoo-module 1.0.0
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.
- package/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/bin/create-odoo-module.js +147 -0
- package/package.json +84 -0
- package/src/cli/args-parser.js +36 -0
- package/src/cli/index.js +272 -0
- package/src/cli/prompts.js +151 -0
- package/src/cli/validator.js +72 -0
- package/src/config/defaults.js +41 -0
- package/src/config/pro-config.js +55 -0
- package/src/generators/api-generator.js +340 -0
- package/src/generators/deploy-generator.js +390 -0
- package/src/generators/flutter-generator.js +1695 -0
- package/src/generators/odoo-generator.js +794 -0
- package/src/generators/ui-generator.js +329 -0
- package/src/utils/file-system.js +67 -0
- package/src/utils/license-check.js +57 -0
- package/src/utils/logger.js +32 -0
- package/src/utils/spinner.js +32 -0
- package/src/utils/string-utils.js +65 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { writeFile, mkdirp } = require('../utils/file-system');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate deploy scripts: Linux bash, Windows PowerShell, Docker Compose.
|
|
9
|
+
*/
|
|
10
|
+
async function generateDeployScripts(targetDir, config) {
|
|
11
|
+
const s = config.moduleNameSnake;
|
|
12
|
+
const L = config.moduleNameLabel;
|
|
13
|
+
const M = config.moduleNameOdoo;
|
|
14
|
+
|
|
15
|
+
logger.verbose('Generating deploy scripts...');
|
|
16
|
+
|
|
17
|
+
const scriptsDir = path.join(targetDir, 'scripts');
|
|
18
|
+
await mkdirp(scriptsDir);
|
|
19
|
+
|
|
20
|
+
const files = [
|
|
21
|
+
[path.join(scriptsDir, 'deploy.sh'), genDeployBash(s, L, M, config)],
|
|
22
|
+
[path.join(scriptsDir, 'deploy.ps1'), genDeployPowershell(s, L, M, config)],
|
|
23
|
+
[path.join(scriptsDir, 'docker-compose.yml'), genDockerCompose(s, config)],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
if (config.withCi) {
|
|
27
|
+
await mkdirp(path.join(targetDir, '.github', 'workflows'));
|
|
28
|
+
files.push([
|
|
29
|
+
path.join(targetDir, '.github', 'workflows', 'ci.yml'),
|
|
30
|
+
genGithubActions(s, L, M, config),
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await Promise.all(files.map(([f, c]) => writeFile(f, c)));
|
|
35
|
+
|
|
36
|
+
// Make bash scripts executable (best effort)
|
|
37
|
+
try {
|
|
38
|
+
const { execa } = require('execa');
|
|
39
|
+
await execa('chmod', ['+x', path.join(scriptsDir, 'deploy.sh')]);
|
|
40
|
+
} catch (_) {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── Bash deploy ─────────────────────────────────────────────────────────────
|
|
44
|
+
function genDeployBash(s, L, M, config) {
|
|
45
|
+
return `#!/usr/bin/env bash
|
|
46
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
47
|
+
# ${L} — Deploy Script (Linux / macOS)
|
|
48
|
+
# Generated by create-odoo-module — https://create-odoo-module.dev
|
|
49
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
50
|
+
set -euo pipefail
|
|
51
|
+
|
|
52
|
+
# ── Load environment ────────────────────────────────────────────────────────
|
|
53
|
+
if [[ -f ".env" ]]; then
|
|
54
|
+
set -a && source .env && set +a
|
|
55
|
+
else
|
|
56
|
+
echo "⚠ .env file not found. Copy .env.example to .env and fill in your credentials."
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
MODULE_NAME="${s}"
|
|
61
|
+
ODOO_URL="\${ODOO_URL:-http://localhost:8069}"
|
|
62
|
+
ODOO_DB="\${ODOO_DB:-odoo}"
|
|
63
|
+
ODOO_USER="\${ODOO_USER:-admin}"
|
|
64
|
+
ODOO_PASSWORD="\${ODOO_PASSWORD:-admin}"
|
|
65
|
+
ADDONS_PATH="\${ODOO_ADDONS_PATH:-/opt/odoo/custom-addons}"
|
|
66
|
+
SERVICE="\${ODOO_SERVICE:-odoo}"
|
|
67
|
+
|
|
68
|
+
# ── Colors ──────────────────────────────────────────────────────────────────
|
|
69
|
+
RED='\\033[0;31m'; GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'
|
|
70
|
+
CYAN='\\033[0;36m'; BOLD='\\033[1m'; RESET='\\033[0m'
|
|
71
|
+
|
|
72
|
+
info() { echo -e " \${CYAN}ℹ\${RESET} \$1"; }
|
|
73
|
+
success() { echo -e " \${GREEN}✔\${RESET} \$1"; }
|
|
74
|
+
warn() { echo -e " \${YELLOW}⚠\${RESET} \$1"; }
|
|
75
|
+
error() { echo -e " \${RED}✗\${RESET} \$1"; exit 1; }
|
|
76
|
+
header() { echo -e "\\n\${BOLD}\${CYAN}\$1\${RESET}\\n"; }
|
|
77
|
+
|
|
78
|
+
header "Deploying \${MODULE_NAME} → \${ODOO_URL}"
|
|
79
|
+
|
|
80
|
+
# ── Step 1: Validate source ──────────────────────────────────────────────────
|
|
81
|
+
if [[ ! -d "./odoo_module" ]]; then
|
|
82
|
+
error "odoo_module/ directory not found. Run this script from the project root."
|
|
83
|
+
fi
|
|
84
|
+
success "Source validated"
|
|
85
|
+
|
|
86
|
+
# ── Step 2: Copy files ───────────────────────────────────────────────────────
|
|
87
|
+
info "Copying module to \${ADDONS_PATH}/\${MODULE_NAME} ..."
|
|
88
|
+
if [[ -w "\${ADDONS_PATH}" ]]; then
|
|
89
|
+
cp -r ./odoo_module "\${ADDONS_PATH}/\${MODULE_NAME}"
|
|
90
|
+
else
|
|
91
|
+
sudo cp -r ./odoo_module "\${ADDONS_PATH}/\${MODULE_NAME}"
|
|
92
|
+
fi
|
|
93
|
+
success "Module files copied"
|
|
94
|
+
|
|
95
|
+
# ── Step 3: Restart Odoo ─────────────────────────────────────────────────────
|
|
96
|
+
info "Restarting Odoo service (\${SERVICE}) ..."
|
|
97
|
+
if systemctl is-active --quiet "\${SERVICE}" 2>/dev/null; then
|
|
98
|
+
sudo systemctl restart "\${SERVICE}"
|
|
99
|
+
sleep 5
|
|
100
|
+
success "Odoo restarted"
|
|
101
|
+
else
|
|
102
|
+
warn "Service '\${SERVICE}' not found or not running — skipping restart"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# ── Step 4: Install / upgrade module ─────────────────────────────────────────
|
|
106
|
+
info "Upgrading module in Odoo database '\${ODOO_DB}' ..."
|
|
107
|
+
python3 - <<PYTHON
|
|
108
|
+
import sys
|
|
109
|
+
try:
|
|
110
|
+
import xmlrpc.client as rpc
|
|
111
|
+
url, db, user, pwd = "${'"'}${M}${'"'}".split(':')[0], '${M}'.split(':')[0], '${s}', '${s}'
|
|
112
|
+
url, db = "\${ODOO_URL}", "\${ODOO_DB}"
|
|
113
|
+
user, pwd = "\${ODOO_USER}", "\${ODOO_PASSWORD}"
|
|
114
|
+
|
|
115
|
+
common = rpc.ServerProxy(f'{url}/xmlrpc/2/common')
|
|
116
|
+
uid = common.authenticate(db, user, pwd, {})
|
|
117
|
+
if not uid:
|
|
118
|
+
print(" ✗ Authentication failed. Check ODOO_USER / ODOO_PASSWORD in .env")
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
|
|
121
|
+
models = rpc.ServerProxy(f'{url}/xmlrpc/2/object')
|
|
122
|
+
mod_ids = models.execute_kw(db, uid, pwd,
|
|
123
|
+
'ir.module.module', 'search',
|
|
124
|
+
[[['name', '=', '${s}']]]
|
|
125
|
+
)
|
|
126
|
+
if mod_ids:
|
|
127
|
+
models.execute_kw(db, uid, pwd,
|
|
128
|
+
'ir.module.module', 'button_immediate_upgrade',
|
|
129
|
+
[mod_ids]
|
|
130
|
+
)
|
|
131
|
+
print(" ✔ Module upgraded successfully")
|
|
132
|
+
else:
|
|
133
|
+
models.execute_kw(db, uid, pwd,
|
|
134
|
+
'ir.module.module', 'update_list', []
|
|
135
|
+
)
|
|
136
|
+
mod_ids = models.execute_kw(db, uid, pwd,
|
|
137
|
+
'ir.module.module', 'search',
|
|
138
|
+
[[['name', '=', '${s}']]]
|
|
139
|
+
)
|
|
140
|
+
if mod_ids:
|
|
141
|
+
models.execute_kw(db, uid, pwd,
|
|
142
|
+
'ir.module.module', 'button_immediate_install',
|
|
143
|
+
[mod_ids]
|
|
144
|
+
)
|
|
145
|
+
print(" ✔ Module installed successfully")
|
|
146
|
+
else:
|
|
147
|
+
print(" ⚠ Module not found in Odoo registry — check addons path config")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
print(f" ✗ {e}")
|
|
150
|
+
sys.exit(1)
|
|
151
|
+
PYTHON
|
|
152
|
+
|
|
153
|
+
# ── Done ─────────────────────────────────────────────────────────────────────
|
|
154
|
+
header "Deployment Complete"
|
|
155
|
+
echo -e " Module : \${BOLD}\${MODULE_NAME}\${RESET}"
|
|
156
|
+
echo -e " Odoo : \${CYAN}\${ODOO_URL}\${RESET}"
|
|
157
|
+
echo -e " DB : \${ODOO_DB}\\n"
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── PowerShell deploy ───────────────────────────────────────────────────────
|
|
162
|
+
function genDeployPowershell(s, L, M, config) {
|
|
163
|
+
return `# ═══════════════════════════════════════════════════════════════════════════
|
|
164
|
+
# ${L} — Deploy Script (Windows PowerShell)
|
|
165
|
+
# Generated by create-odoo-module — https://create-odoo-module.dev
|
|
166
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
167
|
+
param(
|
|
168
|
+
[switch]$Force
|
|
169
|
+
)
|
|
170
|
+
Set-StrictMode -Version Latest
|
|
171
|
+
$ErrorActionPreference = "Stop"
|
|
172
|
+
|
|
173
|
+
# ── Load .env ──────────────────────────────────────────────────────────────
|
|
174
|
+
$envFile = Join-Path $PSScriptRoot "..\\.env"
|
|
175
|
+
if (-not (Test-Path $envFile)) {
|
|
176
|
+
Write-Error "ERROR: .env file not found. Copy .env.example → .env"
|
|
177
|
+
exit 1
|
|
178
|
+
}
|
|
179
|
+
Get-Content $envFile | ForEach-Object {
|
|
180
|
+
if ($_ -match "^([^#][^=]*)=(.*)$") {
|
|
181
|
+
[System.Environment]::SetEnvironmentVariable($Matches[1].Trim(), $Matches[2].Trim())
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
$ModuleName = "${s}"
|
|
186
|
+
$OdooUrl = $Env:ODOO_URL ?? "http://localhost:8069"
|
|
187
|
+
$OdooDb = $Env:ODOO_DB ?? "odoo"
|
|
188
|
+
$OdooUser = $Env:ODOO_USER ?? "admin"
|
|
189
|
+
$OdooPassword = $Env:ODOO_PASSWORD ?? "admin"
|
|
190
|
+
$AddonsPath = $Env:ODOO_ADDONS_PATH ?? "C:\\odoo\\custom-addons"
|
|
191
|
+
|
|
192
|
+
Write-Host ""
|
|
193
|
+
Write-Host " Deploying $ModuleName → $OdooUrl" -ForegroundColor Cyan
|
|
194
|
+
Write-Host ""
|
|
195
|
+
|
|
196
|
+
# ── Copy files ─────────────────────────────────────────────────────────────
|
|
197
|
+
$src = Join-Path $PSScriptRoot "..\\odoo_module"
|
|
198
|
+
$dest = Join-Path $AddonsPath $ModuleName
|
|
199
|
+
Write-Host " Copying module to $dest..." -ForegroundColor Yellow
|
|
200
|
+
Copy-Item -Path $src -Destination $dest -Recurse -Force
|
|
201
|
+
Write-Host " ✔ Module files copied" -ForegroundColor Green
|
|
202
|
+
|
|
203
|
+
# ── Upgrade via XML-RPC ────────────────────────────────────────────────────
|
|
204
|
+
Write-Host " Upgrading module in Odoo..." -ForegroundColor Yellow
|
|
205
|
+
$pyScript = @"
|
|
206
|
+
import xmlrpc.client as rpc, sys
|
|
207
|
+
url, db, user, pwd = '$OdooUrl', '$OdooDb', '$OdooUser', '$OdooPassword'
|
|
208
|
+
try:
|
|
209
|
+
uid = rpc.ServerProxy(f'{url}/xmlrpc/2/common').authenticate(db, user, pwd, {})
|
|
210
|
+
if not uid:
|
|
211
|
+
print('Auth failed'); sys.exit(1)
|
|
212
|
+
m = rpc.ServerProxy(f'{url}/xmlrpc/2/object')
|
|
213
|
+
ids = m.execute_kw(db,uid,pwd,'ir.module.module','search',[[['name','=','$ModuleName']]])
|
|
214
|
+
m.execute_kw(db,uid,pwd,'ir.module.module','button_immediate_upgrade',[ids])
|
|
215
|
+
print('OK')
|
|
216
|
+
except Exception as e:
|
|
217
|
+
print(f'ERR: {e}'); sys.exit(1)
|
|
218
|
+
"@
|
|
219
|
+
$result = python3 -c $pyScript
|
|
220
|
+
if ($result -eq "OK") {
|
|
221
|
+
Write-Host " ✔ Module upgraded successfully" -ForegroundColor Green
|
|
222
|
+
} else {
|
|
223
|
+
Write-Warning " ⚠ $result"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
Write-Host ""
|
|
227
|
+
Write-Host " ✔ Deployment complete!" -ForegroundColor Green
|
|
228
|
+
Write-Host " Odoo: $OdooUrl" -ForegroundColor Cyan
|
|
229
|
+
Write-Host ""
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── Docker Compose ──────────────────────────────────────────────────────────
|
|
234
|
+
function genDockerCompose(s, config) {
|
|
235
|
+
return `# Docker Compose — Local Odoo development environment
|
|
236
|
+
# Generated by create-odoo-module — https://create-odoo-module.dev
|
|
237
|
+
#
|
|
238
|
+
# Usage:
|
|
239
|
+
# docker-compose up -d Start services
|
|
240
|
+
# docker-compose down Stop services
|
|
241
|
+
# docker-compose logs -f odoo Follow Odoo logs
|
|
242
|
+
# docker exec -it odoo_dev bash Enter Odoo container
|
|
243
|
+
|
|
244
|
+
version: '3.8'
|
|
245
|
+
|
|
246
|
+
services:
|
|
247
|
+
|
|
248
|
+
# ── PostgreSQL ────────────────────────────────────────────────────────────
|
|
249
|
+
db:
|
|
250
|
+
image: postgres:15-alpine
|
|
251
|
+
container_name: ${s}_db
|
|
252
|
+
restart: unless-stopped
|
|
253
|
+
environment:
|
|
254
|
+
POSTGRES_USER: odoo
|
|
255
|
+
POSTGRES_PASSWORD: odoo
|
|
256
|
+
POSTGRES_DB: odoo
|
|
257
|
+
volumes:
|
|
258
|
+
- pgdata:/var/lib/postgresql/data
|
|
259
|
+
ports:
|
|
260
|
+
- "5432:5432"
|
|
261
|
+
healthcheck:
|
|
262
|
+
test: ["CMD-SHELL", "pg_isready -U odoo"]
|
|
263
|
+
interval: 10s
|
|
264
|
+
timeout: 5s
|
|
265
|
+
retries: 5
|
|
266
|
+
|
|
267
|
+
# ── Odoo ─────────────────────────────────────────────────────────────────
|
|
268
|
+
odoo:
|
|
269
|
+
image: odoo:${config.odooVersion}
|
|
270
|
+
container_name: ${s}_odoo
|
|
271
|
+
restart: unless-stopped
|
|
272
|
+
depends_on:
|
|
273
|
+
db:
|
|
274
|
+
condition: service_healthy
|
|
275
|
+
ports:
|
|
276
|
+
- "8069:8069"
|
|
277
|
+
- "8072:8072" # Long-polling
|
|
278
|
+
volumes:
|
|
279
|
+
- ./odoo_module:/mnt/extra-addons/${s}
|
|
280
|
+
- odoo-data:/var/lib/odoo
|
|
281
|
+
- ./scripts/odoo.conf:/etc/odoo/odoo.conf:ro
|
|
282
|
+
environment:
|
|
283
|
+
HOST: db
|
|
284
|
+
USER: odoo
|
|
285
|
+
PASSWORD: odoo
|
|
286
|
+
command: >
|
|
287
|
+
odoo
|
|
288
|
+
--db_host=db
|
|
289
|
+
--db_port=5432
|
|
290
|
+
--db_user=odoo
|
|
291
|
+
--db_password=odoo
|
|
292
|
+
--addons-path=/usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
|
|
293
|
+
--dev=all
|
|
294
|
+
--log-level=info
|
|
295
|
+
|
|
296
|
+
volumes:
|
|
297
|
+
pgdata:
|
|
298
|
+
odoo-data:
|
|
299
|
+
`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ─── GitHub Actions CI ───────────────────────────────────────────────────────
|
|
303
|
+
function genGithubActions(s, L, M, config) {
|
|
304
|
+
return `name: ${L} CI
|
|
305
|
+
|
|
306
|
+
on:
|
|
307
|
+
push:
|
|
308
|
+
branches: [ main, develop ]
|
|
309
|
+
pull_request:
|
|
310
|
+
branches: [ main ]
|
|
311
|
+
|
|
312
|
+
jobs:
|
|
313
|
+
# ── Odoo Python tests ─────────────────────────────────────────────────────
|
|
314
|
+
odoo-test:
|
|
315
|
+
name: Odoo Module Tests (Python)
|
|
316
|
+
runs-on: ubuntu-22.04
|
|
317
|
+
|
|
318
|
+
services:
|
|
319
|
+
postgres:
|
|
320
|
+
image: postgres:15
|
|
321
|
+
env:
|
|
322
|
+
POSTGRES_USER: odoo
|
|
323
|
+
POSTGRES_PASSWORD: odoo
|
|
324
|
+
POSTGRES_DB: odoo_test
|
|
325
|
+
ports: ["5432:5432"]
|
|
326
|
+
options: >-
|
|
327
|
+
--health-cmd pg_isready
|
|
328
|
+
--health-interval 10s
|
|
329
|
+
--health-timeout 5s
|
|
330
|
+
--health-retries 5
|
|
331
|
+
|
|
332
|
+
steps:
|
|
333
|
+
- uses: actions/checkout@v4
|
|
334
|
+
|
|
335
|
+
- name: Set up Python
|
|
336
|
+
uses: actions/setup-python@v5
|
|
337
|
+
with:
|
|
338
|
+
python-version: '3.11'
|
|
339
|
+
|
|
340
|
+
- name: Install Python dependencies
|
|
341
|
+
run: |
|
|
342
|
+
pip install pytest flake8
|
|
343
|
+
|
|
344
|
+
- name: Lint Python code
|
|
345
|
+
run: |
|
|
346
|
+
flake8 odoo_module/ --max-line-length=120 --exclude=__pycache__
|
|
347
|
+
|
|
348
|
+
- name: Run Odoo tests
|
|
349
|
+
run: |
|
|
350
|
+
echo "Odoo integration tests require a running Odoo instance."
|
|
351
|
+
echo "Configure your Odoo test environment here."
|
|
352
|
+
# Uncomment and configure for full Odoo testing:
|
|
353
|
+
# run: |
|
|
354
|
+
# ./odoo-bin test -d odoo_test --test-tags ${s} --stop-after-init
|
|
355
|
+
|
|
356
|
+
${config.withUi ? `
|
|
357
|
+
# ── Flutter tests ──────────────────────────────────────────────────────────
|
|
358
|
+
flutter-test:
|
|
359
|
+
name: Flutter App Tests (Dart)
|
|
360
|
+
runs-on: ubuntu-latest
|
|
361
|
+
|
|
362
|
+
steps:
|
|
363
|
+
- uses: actions/checkout@v4
|
|
364
|
+
|
|
365
|
+
- name: Set up Flutter
|
|
366
|
+
uses: subosito/flutter-action@v2
|
|
367
|
+
with:
|
|
368
|
+
flutter-version: '3.19.0'
|
|
369
|
+
channel: 'stable'
|
|
370
|
+
|
|
371
|
+
- name: Install Flutter dependencies
|
|
372
|
+
working-directory: flutter_app
|
|
373
|
+
run: flutter pub get
|
|
374
|
+
|
|
375
|
+
- name: Analyze Dart code
|
|
376
|
+
working-directory: flutter_app
|
|
377
|
+
run: flutter analyze
|
|
378
|
+
|
|
379
|
+
- name: Run Flutter tests
|
|
380
|
+
working-directory: flutter_app
|
|
381
|
+
run: flutter test --coverage
|
|
382
|
+
|
|
383
|
+
- name: Build APK (debug)
|
|
384
|
+
working-directory: flutter_app
|
|
385
|
+
run: flutter build apk --debug
|
|
386
|
+
` : ''}
|
|
387
|
+
`;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
module.exports = { generateDeployScripts };
|