@brezel/installer 1.0.1 ā 1.0.2
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/package.json +9 -2
- package/.releaserc.json +0 -20
- package/install.sh +0 -41
- package/src/index.ts +0 -538
- package/tsconfig.json +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brezel/installer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Installer for Brezel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
"start": "node dist/index.js",
|
|
12
12
|
"dev": "ts-node src/index.ts"
|
|
13
13
|
},
|
|
14
|
-
"keywords": [
|
|
14
|
+
"keywords": [
|
|
15
|
+
"brezel",
|
|
16
|
+
"installer",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
15
19
|
"author": "flynamic",
|
|
16
20
|
"license": "MIT",
|
|
17
21
|
"repository": {
|
|
@@ -19,6 +23,9 @@
|
|
|
19
23
|
"url": "git+https://github.com/brezelio/installer.git"
|
|
20
24
|
},
|
|
21
25
|
"type": "commonjs",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
22
29
|
"devDependencies": {
|
|
23
30
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
24
31
|
"@semantic-release/git": "^10.0.1",
|
package/.releaserc.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"branches": [
|
|
3
|
-
"main"
|
|
4
|
-
],
|
|
5
|
-
"plugins": [
|
|
6
|
-
"@semantic-release/commit-analyzer",
|
|
7
|
-
"@semantic-release/release-notes-generator",
|
|
8
|
-
"@semantic-release/npm",
|
|
9
|
-
"@semantic-release/github",
|
|
10
|
-
[
|
|
11
|
-
"@semantic-release/git",
|
|
12
|
-
{
|
|
13
|
-
"assets": [
|
|
14
|
-
"package.json"
|
|
15
|
-
],
|
|
16
|
-
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
]
|
|
20
|
-
}
|
package/install.sh
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
# Colors for output
|
|
5
|
-
RED='\033[0;31m'
|
|
6
|
-
GREEN='\033[0;32m'
|
|
7
|
-
BLUE='\033[0;34m'
|
|
8
|
-
NC='\033[0m' # No Color
|
|
9
|
-
|
|
10
|
-
echo "${BLUE}š„Ø Welcome to the Brezel Installer!${NC}"
|
|
11
|
-
|
|
12
|
-
# Check for Node.js
|
|
13
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
14
|
-
echo "${RED}ā Node.js is required but not found.${NC}"
|
|
15
|
-
echo "Please install Node.js 18+ and try again."
|
|
16
|
-
exit 1
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
# Check for npm
|
|
20
|
-
if ! command -v npm >/dev/null 2>&1; then
|
|
21
|
-
echo "${RED}ā npm is required but not found.${NC}"
|
|
22
|
-
exit 1
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# In production, we would use npx -y @kibro/brezel-installer@latest
|
|
26
|
-
# For development/demo purposes in this repo:
|
|
27
|
-
if [ -f "package.json" ] && [ -d "src" ]; then
|
|
28
|
-
echo "š¦ Preparing installer..."
|
|
29
|
-
npm install --silent
|
|
30
|
-
npm run build --silent
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
if [ -f "./dist/index.js" ]; then
|
|
34
|
-
echo "š Launching installer..."
|
|
35
|
-
# Pass all arguments to the TS installer
|
|
36
|
-
node ./dist/index.js "$@"
|
|
37
|
-
else
|
|
38
|
-
# Fallback to npx if no local build is found
|
|
39
|
-
echo "š Fetching and launching latest installer..."
|
|
40
|
-
npx -y @brezel/installer@latest "$@"
|
|
41
|
-
fi
|
package/src/index.ts
DELETED
|
@@ -1,538 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import prompts from 'prompts';
|
|
5
|
-
import { execa } from 'execa';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import ora from 'ora';
|
|
9
|
-
|
|
10
|
-
const program = new Command();
|
|
11
|
-
|
|
12
|
-
async function runTask(title: string, cmd: string, args: string[], options: any) {
|
|
13
|
-
const spinner = ora(title).start();
|
|
14
|
-
const subprocess = execa(cmd, args, { ...options, all: true });
|
|
15
|
-
|
|
16
|
-
subprocess.stdout?.on('data', (data) => {
|
|
17
|
-
const line = data.toString().trim().split('\n').pop();
|
|
18
|
-
if (line) {
|
|
19
|
-
spinner.text = `${title} ${chalk.dim(`(${line.substring(0, 60).trim()}...)`)}`;
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
subprocess.stderr?.on('data', (data) => {
|
|
24
|
-
const line = data.toString().trim().split('\n').pop();
|
|
25
|
-
if (line) {
|
|
26
|
-
spinner.text = `${title} ${chalk.dim(`(${line.substring(0, 60).trim()}...)`)}`;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
await subprocess;
|
|
32
|
-
spinner.succeed(title);
|
|
33
|
-
} catch (e: any) {
|
|
34
|
-
spinner.fail(`${title} failed.`);
|
|
35
|
-
if (e.all) console.error(chalk.red(e.all));
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
program
|
|
41
|
-
.name('brezel-installer')
|
|
42
|
-
.description('Installer for Brezel (SPA + API)')
|
|
43
|
-
.version('1.0.0')
|
|
44
|
-
.option('-d, --dir <directory>', 'Installation directory', './brezel')
|
|
45
|
-
.option('-m, --mode <mode>', 'Installation mode (native, docker)', 'native')
|
|
46
|
-
.option('-s, --system <name>', 'System name', 'example')
|
|
47
|
-
.option('-u, --url <url>', 'API URL', 'http://brezel-api.test')
|
|
48
|
-
.option('--spa-url <url>', 'SPA URL', 'http://localhost:5173')
|
|
49
|
-
.option('--db-host <host>', 'Database host', '127.0.0.1')
|
|
50
|
-
.option('--db-port <port>', 'Database port', '3306')
|
|
51
|
-
.option('--db-name <name>', 'Database name', 'brezel_meta')
|
|
52
|
-
.option('--db-user <user>', 'Database user', 'root')
|
|
53
|
-
.option('--db-password <password>', 'Database password', '')
|
|
54
|
-
.option('--php-path <path>', 'Path to PHP executable', 'php')
|
|
55
|
-
.option('--gitlab-token <token>', 'GitLab Personal Access Token')
|
|
56
|
-
.option('--no-interactive', 'Run in non-interactive mode')
|
|
57
|
-
.option('--source-mode <mode>', 'Source control mode (clone, fork)', 'clone')
|
|
58
|
-
.option('--components <list>', 'Optional components to install (mariadb, nginx, ssl, cron)', '');
|
|
59
|
-
|
|
60
|
-
const REPO_SKELETON = 'https://github.com/brezelio/brezel.git';
|
|
61
|
-
|
|
62
|
-
program.action(async (options) => {
|
|
63
|
-
console.log(chalk.bold.blue('\nš„Ø Welcome to the Brezel Installer!\n'));
|
|
64
|
-
|
|
65
|
-
const checkPhp = async (phpPath: string) => {
|
|
66
|
-
try {
|
|
67
|
-
const { stdout } = await execa(phpPath, ['-r', 'echo PHP_VERSION;']);
|
|
68
|
-
// Use regex to find the version string (e.g. 8.3.1 or 8.4.14) in case of warnings
|
|
69
|
-
const match = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
70
|
-
if (!match) return null;
|
|
71
|
-
|
|
72
|
-
const major = parseInt(match[1], 10);
|
|
73
|
-
const minor = parseInt(match[2], 10);
|
|
74
|
-
const version = match[0];
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
version,
|
|
78
|
-
valid: (major > 8 || (major === 8 && minor >= 3))
|
|
79
|
-
};
|
|
80
|
-
} catch (e) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// 1. Prerequisites Check
|
|
86
|
-
const spinner = ora('Checking prerequisites...').start();
|
|
87
|
-
const checks: any = {};
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Critical Deps
|
|
91
|
-
for (const dep of ['git', 'node', 'npm']) {
|
|
92
|
-
await execa(dep, ['--version']);
|
|
93
|
-
checks[dep] = true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// PHP version check
|
|
97
|
-
const phpResult = await checkPhp(options.phpPath || 'php');
|
|
98
|
-
if (phpResult) {
|
|
99
|
-
checks.php = phpResult.version;
|
|
100
|
-
checks.phpValid = phpResult.valid;
|
|
101
|
-
} else {
|
|
102
|
-
checks.php = false;
|
|
103
|
-
checks.phpValid = false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Composer check
|
|
107
|
-
try {
|
|
108
|
-
await execa('composer', ['--version']);
|
|
109
|
-
checks.composer = true;
|
|
110
|
-
} catch (e) {
|
|
111
|
-
checks.composer = false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Docker check
|
|
115
|
-
try {
|
|
116
|
-
await execa('docker', ['--version']);
|
|
117
|
-
checks.docker = true;
|
|
118
|
-
} catch (e) {
|
|
119
|
-
checks.docker = false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Valet check (macOS only)
|
|
123
|
-
if (process.platform === 'darwin') {
|
|
124
|
-
try {
|
|
125
|
-
await execa('valet', ['--version']);
|
|
126
|
-
checks.valet = true;
|
|
127
|
-
} catch (e) {
|
|
128
|
-
checks.valet = false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
spinner.succeed('System check complete.');
|
|
133
|
-
} catch (error) {
|
|
134
|
-
spinner.fail('Missing critical prerequisites (git, node, or npm).');
|
|
135
|
-
process.exit(1);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
let responses = options;
|
|
139
|
-
|
|
140
|
-
if (options.interactive !== false) {
|
|
141
|
-
const interactiveResponses = await prompts([
|
|
142
|
-
{
|
|
143
|
-
type: 'select',
|
|
144
|
-
name: 'mode',
|
|
145
|
-
message: 'How do you want to install Brezel?',
|
|
146
|
-
choices: [
|
|
147
|
-
{
|
|
148
|
-
title: `Native (Bare metal) ${!checks.phpValid && checks.php ? chalk.yellow('(PHP 8.3+ required, current: ' + checks.php + ')') : (!checks.php ? chalk.red('(PHP not found)') : '')}`,
|
|
149
|
-
value: 'native',
|
|
150
|
-
disabled: false
|
|
151
|
-
},
|
|
152
|
-
...(checks.valet ? [{
|
|
153
|
-
title: `Valet (macOS Magic) ${chalk.dim('- handles domain mapping automatically')}`,
|
|
154
|
-
value: 'valet'
|
|
155
|
-
}] : []),
|
|
156
|
-
{
|
|
157
|
-
title: `Docker (Containerized) ${!checks.docker ? chalk.red('(Docker required)') : ''}`,
|
|
158
|
-
value: 'docker',
|
|
159
|
-
disabled: !checks.docker
|
|
160
|
-
}
|
|
161
|
-
],
|
|
162
|
-
initial: (options.mode === 'valet' && checks.valet) ? 1 : ((options.mode === 'docker' && checks.docker) ? (checks.valet ? 2 : 1) : 0)
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
type: (prev, values) => (values.mode === 'native' || values.mode === 'valet') ? 'text' : null,
|
|
166
|
-
name: 'phpPath',
|
|
167
|
-
message: 'Path to PHP 8.3+ executable:',
|
|
168
|
-
initial: options.phpPath || 'php',
|
|
169
|
-
validate: async (val) => {
|
|
170
|
-
const res = await checkPhp(val);
|
|
171
|
-
if (!res) return 'PHP not found at this path.';
|
|
172
|
-
if (!res.valid) return `PHP version ${res.version} is too old. 8.3+ required.`;
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
type: 'select',
|
|
178
|
-
name: 'sourceMode',
|
|
179
|
-
message: 'Source control mode:',
|
|
180
|
-
choices: [
|
|
181
|
-
{ title: 'Clone without history', value: 'clone' },
|
|
182
|
-
{ title: 'Fork and clone', value: 'fork' }
|
|
183
|
-
],
|
|
184
|
-
initial: options.sourceMode === 'fork' ? 1 : 0
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
type: (prev) => prev === 'fork' ? 'text' : null,
|
|
188
|
-
name: 'forkUrl',
|
|
189
|
-
message: 'Enter your fork URL (git@...):',
|
|
190
|
-
validate: (v) => v.length > 0 ? true : 'Fork URL is required'
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
type: 'text',
|
|
194
|
-
name: 'gitlabToken',
|
|
195
|
-
message: 'GitLab Personal Access Token (for @kibro packages, scope: read_api, read_registry)',
|
|
196
|
-
initial: options.gitlabToken,
|
|
197
|
-
validate: (v) => (v && v.length > 0) ? true : 'Token is required'
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
type: 'text',
|
|
201
|
-
name: 'dir',
|
|
202
|
-
message: 'Installation directory:',
|
|
203
|
-
initial: options.dir
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
type: 'text',
|
|
207
|
-
name: 'system',
|
|
208
|
-
message: 'System name:',
|
|
209
|
-
initial: options.system
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
type: 'text',
|
|
213
|
-
name: 'url',
|
|
214
|
-
message: 'API URL:',
|
|
215
|
-
initial: (prev: any, values: any) => options.url !== 'http://brezel-api.test' ? options.url : `http://${values.system}.test`
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
type: 'text',
|
|
219
|
-
name: 'spaUrl',
|
|
220
|
-
message: 'SPA URL:',
|
|
221
|
-
initial: (prev: any, values: any) => {
|
|
222
|
-
if (options.spaUrl !== 'http://localhost:5173') return options.spaUrl;
|
|
223
|
-
if (values.mode === 'valet') return `http://${values.system}.test:5173`;
|
|
224
|
-
return `http://localhost:5173`;
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
type: 'multiselect',
|
|
229
|
-
name: 'components',
|
|
230
|
-
message: 'Select optional components to install:',
|
|
231
|
-
choices: [
|
|
232
|
-
{ title: 'MariaDB', value: 'mariadb' },
|
|
233
|
-
{ title: 'Nginx', value: 'nginx' },
|
|
234
|
-
{ title: 'SSL (Certbot)', value: 'ssl' },
|
|
235
|
-
{ title: 'Cron jobs', value: 'cron' }
|
|
236
|
-
],
|
|
237
|
-
initial: (options.components && typeof options.components === 'string')
|
|
238
|
-
? options.components.split(',').map(c => ['mariadb', 'nginx', 'ssl', 'cron'].indexOf(c))
|
|
239
|
-
: undefined
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
type: 'text',
|
|
243
|
-
name: 'dbHost',
|
|
244
|
-
message: 'Database Host',
|
|
245
|
-
initial: options.dbHost
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
type: 'text',
|
|
249
|
-
name: 'dbPort',
|
|
250
|
-
message: 'Database Port',
|
|
251
|
-
initial: options.dbPort
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
type: 'text',
|
|
255
|
-
name: 'dbName',
|
|
256
|
-
message: 'Database Name',
|
|
257
|
-
initial: options.dbName
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
type: 'text',
|
|
261
|
-
name: 'dbUser',
|
|
262
|
-
message: 'Database User',
|
|
263
|
-
initial: options.dbUser
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
type: 'password',
|
|
267
|
-
name: 'dbPassword',
|
|
268
|
-
message: 'Database Password',
|
|
269
|
-
initial: options.dbPassword
|
|
270
|
-
}
|
|
271
|
-
]);
|
|
272
|
-
|
|
273
|
-
responses = { ...options, ...interactiveResponses };
|
|
274
|
-
} else {
|
|
275
|
-
// In non-interactive mode, parse components string into array
|
|
276
|
-
if (typeof responses.components === 'string') {
|
|
277
|
-
responses.components = responses.components.split(',').filter(Boolean);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (!responses.dir) process.exit(1);
|
|
282
|
-
|
|
283
|
-
// Validation after response
|
|
284
|
-
const isNative = responses.mode === 'native' || responses.mode === 'valet';
|
|
285
|
-
|
|
286
|
-
if (isNative) {
|
|
287
|
-
const phpRes = await checkPhp(responses.phpPath);
|
|
288
|
-
if (!phpRes || !phpRes.valid) {
|
|
289
|
-
ora(`Native/Valet mode requires PHP 8.3+, but version ${phpRes?.version || 'none'} was found at ${responses.phpPath}.`).fail();
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
292
|
-
if (!checks.composer) {
|
|
293
|
-
ora('Native/Valet mode requires Composer, but it was not found.').fail();
|
|
294
|
-
process.exit(1);
|
|
295
|
-
}
|
|
296
|
-
} else if (responses.mode === 'docker' && !checks.docker) {
|
|
297
|
-
ora('Docker mode requires Docker, but it was not found.').fail();
|
|
298
|
-
process.exit(1);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const rootDir = path.resolve(responses.dir);
|
|
302
|
-
|
|
303
|
-
if (!fs.existsSync(rootDir)) {
|
|
304
|
-
const s = ora(`Cloning Brezel skeleton (${responses.sourceMode === 'clone' ? 'no history' : 'fork'})...`).start();
|
|
305
|
-
try {
|
|
306
|
-
const cloneUrl = responses.sourceMode === 'fork' ? responses.forkUrl : REPO_SKELETON;
|
|
307
|
-
const cloneArgs = ['clone'];
|
|
308
|
-
if (responses.sourceMode === 'clone') {
|
|
309
|
-
cloneArgs.push('--depth', '1');
|
|
310
|
-
}
|
|
311
|
-
cloneArgs.push(cloneUrl, rootDir);
|
|
312
|
-
|
|
313
|
-
await execa('git', cloneArgs);
|
|
314
|
-
|
|
315
|
-
if (responses.sourceMode === 'clone') {
|
|
316
|
-
fs.rmSync(path.join(rootDir, '.git'), { recursive: true, force: true });
|
|
317
|
-
await execa('git', ['init'], { cwd: rootDir });
|
|
318
|
-
await execa('git', ['add', '.'], { cwd: rootDir });
|
|
319
|
-
await execa('git', ['commit', '-m', 'Initial commit from Brezel Installer'], { cwd: rootDir });
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
s.succeed('Brezel skeleton cloned.');
|
|
323
|
-
} catch (e) {
|
|
324
|
-
s.fail('Failed to clone repository.');
|
|
325
|
-
console.error(e);
|
|
326
|
-
process.exit(1);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// 2. Optional Components (Bare metal only)
|
|
331
|
-
if (responses.mode === 'native' && responses.components && responses.components.length > 0) {
|
|
332
|
-
console.log(chalk.bold.cyan('\nš Installing optional components...'));
|
|
333
|
-
for (const component of responses.components) {
|
|
334
|
-
const compSpinner = ora(`Installing ${component}...`).start();
|
|
335
|
-
try {
|
|
336
|
-
if (process.platform === 'linux') {
|
|
337
|
-
if (component === 'mariadb') {
|
|
338
|
-
await execa('sudo', ['apt-get', 'update']);
|
|
339
|
-
await execa('sudo', ['apt-get', 'install', '-y', 'mariadb-server']);
|
|
340
|
-
compSpinner.succeed('MariaDB installed.');
|
|
341
|
-
} else if (component === 'nginx') {
|
|
342
|
-
await execa('sudo', ['apt-get', 'install', '-y', 'nginx']);
|
|
343
|
-
compSpinner.succeed('Nginx installed.');
|
|
344
|
-
} else if (component === 'ssl') {
|
|
345
|
-
await execa('sudo', ['apt-get', 'install', '-y', 'certbot', 'python3-certbot-nginx']);
|
|
346
|
-
compSpinner.succeed('Certbot installed.');
|
|
347
|
-
} else if (component === 'cron') {
|
|
348
|
-
const cronJob = `* * * * * cd ${rootDir} && ${responses.phpPath} bakery schedule:run >> /dev/null 2>&1`;
|
|
349
|
-
compSpinner.info('Cron job suggested: ' + cronJob);
|
|
350
|
-
}
|
|
351
|
-
} else if (process.platform === 'darwin') {
|
|
352
|
-
if (component === 'mariadb') {
|
|
353
|
-
await execa('brew', ['install', 'mariadb']);
|
|
354
|
-
compSpinner.succeed('MariaDB installed via Homebrew.');
|
|
355
|
-
} else if (component === 'nginx') {
|
|
356
|
-
await execa('brew', ['install', 'nginx']);
|
|
357
|
-
compSpinner.succeed('Nginx installed via Homebrew.');
|
|
358
|
-
} else {
|
|
359
|
-
compSpinner.warn(`${component} installation not fully automated for macOS.`);
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
compSpinner.warn(`${component} installation not supported on this OS.`);
|
|
363
|
-
}
|
|
364
|
-
} catch (e) {
|
|
365
|
-
compSpinner.fail(`Failed to install ${component}.`);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (responses.mode === 'docker') {
|
|
371
|
-
console.log(chalk.bold.cyan('\nš³ Setting up Docker environment...'));
|
|
372
|
-
|
|
373
|
-
const envExamplePath = path.join(rootDir, '.env.example');
|
|
374
|
-
const envPath = path.join(rootDir, '.env');
|
|
375
|
-
|
|
376
|
-
if (fs.existsSync(envExamplePath)) {
|
|
377
|
-
let envContent = fs.readFileSync(envExamplePath, 'utf-8');
|
|
378
|
-
|
|
379
|
-
envContent = envContent.replace(/APP_URL=.*/, `APP_URL=${responses.url}`);
|
|
380
|
-
envContent = envContent.replace(/VITE_APP_API_URL=.*/, `VITE_APP_API_URL=${responses.url}`);
|
|
381
|
-
envContent = envContent.replace(/VITE_APP_SYSTEM=.*/, `VITE_APP_SYSTEM=${responses.system}`);
|
|
382
|
-
envContent = envContent.replace(/TENANCY_HOST=.*/, `TENANCY_HOST=db`);
|
|
383
|
-
envContent = envContent.replace(/TENANCY_PASSWORD=.*/, `TENANCY_PASSWORD=password`);
|
|
384
|
-
|
|
385
|
-
if (!envContent.includes('APP_SYSTEM=')) {
|
|
386
|
-
envContent += `\nAPP_SYSTEM=${responses.system}\n`;
|
|
387
|
-
} else {
|
|
388
|
-
envContent = envContent.replace(/APP_SYSTEM=.*/, `APP_SYSTEM=${responses.system}`);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
fs.writeFileSync(envPath, envContent);
|
|
392
|
-
ora('Configured .env for Docker').succeed();
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
await runTask('Building and starting Docker containers', 'docker', ['compose', 'up', '-d', '--build'], {
|
|
396
|
-
cwd: rootDir,
|
|
397
|
-
env: {
|
|
398
|
-
...process.env,
|
|
399
|
-
COMPOSER_TOKEN: responses.gitlabToken,
|
|
400
|
-
NPM_TOKEN: responses.gitlabToken,
|
|
401
|
-
APP_SYSTEM: responses.system,
|
|
402
|
-
APP_URL: responses.url
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
console.log(chalk.bold.cyan('\nš„ Initializing Brezel in Docker...'));
|
|
407
|
-
const initSpinner = ora('Waiting for database...').start();
|
|
408
|
-
|
|
409
|
-
await new Promise(r => setTimeout(r, 10000));
|
|
410
|
-
|
|
411
|
-
const runDockerCmd = async (cmd: string[]) => {
|
|
412
|
-
await execa('docker', ['compose', 'exec', 'api', ...cmd], { cwd: rootDir, stdio: 'inherit' });
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
try {
|
|
416
|
-
initSpinner.text = 'Running bakery init...';
|
|
417
|
-
await runDockerCmd(['php', 'bakery', 'init']);
|
|
418
|
-
|
|
419
|
-
console.log(chalk.dim(`Creating system "${responses.system}"...`));
|
|
420
|
-
await runDockerCmd(['php', 'bakery', 'system', 'create', responses.system]);
|
|
421
|
-
|
|
422
|
-
console.log(chalk.dim('Applying system config...'));
|
|
423
|
-
await runDockerCmd(['php', 'bakery', 'apply']);
|
|
424
|
-
|
|
425
|
-
initSpinner.succeed('Brezel initialized in Docker.');
|
|
426
|
-
|
|
427
|
-
} catch (e) {
|
|
428
|
-
initSpinner.fail('Initialization in Docker failed.');
|
|
429
|
-
console.error(e);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
console.log(chalk.bold.green('\nā
Docker Installation complete!'));
|
|
433
|
-
console.log(chalk.white(`
|
|
434
|
-
Services are running:
|
|
435
|
-
API: ${responses.url} (mapped to localhost:8081)
|
|
436
|
-
SPA: ${responses.spaUrl} (mapped to localhost:3000)
|
|
437
|
-
|
|
438
|
-
To stop:
|
|
439
|
-
cd ${rootDir}
|
|
440
|
-
docker compose down
|
|
441
|
-
`));
|
|
442
|
-
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// 3. Install Dependencies (Native/Valet Mode)
|
|
447
|
-
if (isNative) {
|
|
448
|
-
console.log(chalk.bold.cyan('\nš¦ Installing Dependencies...'));
|
|
449
|
-
|
|
450
|
-
await execa('composer', ['config', 'gitlab-token.gitlab.kiwis-and-brownies.de', responses.gitlabToken], { cwd: rootDir });
|
|
451
|
-
|
|
452
|
-
await runTask('Installing PHP dependencies (Composer)', 'composer', ['install', '--no-interaction'], { cwd: rootDir });
|
|
453
|
-
|
|
454
|
-
const npmrcPath = path.join(rootDir, '.npmrc');
|
|
455
|
-
const npmrcContent = `
|
|
456
|
-
@kibro:registry=https://gitlab.kiwis-and-brownies.de/api/v4/packages/npm/
|
|
457
|
-
//gitlab.kiwis-and-brownies.de/api/v4/packages/npm/:_authToken=${responses.gitlabToken}
|
|
458
|
-
`;
|
|
459
|
-
fs.writeFileSync(npmrcPath, npmrcContent);
|
|
460
|
-
|
|
461
|
-
await runTask('Installing Node dependencies (npm)', 'npm', ['install'], { cwd: rootDir });
|
|
462
|
-
|
|
463
|
-
const envExamplePath = path.join(rootDir, '.env.example');
|
|
464
|
-
const envPath = path.join(rootDir, '.env');
|
|
465
|
-
if (fs.existsSync(envExamplePath)) {
|
|
466
|
-
let envContent = fs.readFileSync(envExamplePath, 'utf-8');
|
|
467
|
-
|
|
468
|
-
envContent = envContent.replace(/APP_URL=.*/, `APP_URL=${responses.url}`);
|
|
469
|
-
envContent = envContent.replace(/VITE_APP_API_URL=.*/, `VITE_APP_API_URL=${responses.url}`);
|
|
470
|
-
envContent = envContent.replace(/VITE_APP_SYSTEM=.*/, `VITE_APP_SYSTEM=${responses.system}`);
|
|
471
|
-
|
|
472
|
-
envContent = envContent.replace(/TENANCY_HOST=.*/, `TENANCY_HOST=${responses.dbHost}`);
|
|
473
|
-
envContent = envContent.replace(/TENANCY_PORT=.*/, `TENANCY_PORT=${responses.dbPort}`);
|
|
474
|
-
envContent = envContent.replace(/TENANCY_DATABASE=.*/, `TENANCY_DATABASE=${responses.dbName}`);
|
|
475
|
-
envContent = envContent.replace(/TENANCY_USERNAME=.*/, `TENANCY_USERNAME=${responses.dbUser}`);
|
|
476
|
-
envContent = envContent.replace(/TENANCY_PASSWORD=.*/, `TENANCY_PASSWORD=${responses.dbPassword}`);
|
|
477
|
-
|
|
478
|
-
fs.writeFileSync(envPath, envContent);
|
|
479
|
-
ora('Configured .env').succeed();
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
console.log(chalk.bold.cyan('\nš„ Initializing Brezel...'));
|
|
483
|
-
|
|
484
|
-
const runBakery = async (args: string[]) => {
|
|
485
|
-
await execa(responses.phpPath, ['bakery', ...args], { cwd: rootDir, stdio: 'inherit' });
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
try {
|
|
489
|
-
console.log(chalk.dim('Running initialization...'));
|
|
490
|
-
await runBakery(['init']);
|
|
491
|
-
console.log(chalk.dim(`Creating system "${responses.system}"...`));
|
|
492
|
-
await runBakery(['system', 'create', responses.system]);
|
|
493
|
-
console.log(chalk.dim('Applying system config...'));
|
|
494
|
-
await runBakery(['apply']);
|
|
495
|
-
} catch (e) {
|
|
496
|
-
console.error(chalk.red('Initialization failed.'));
|
|
497
|
-
console.error(e);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
await runTask('Building SPA', 'npm', ['run', 'build'], { cwd: rootDir });
|
|
501
|
-
|
|
502
|
-
// Valet specific setup
|
|
503
|
-
if (responses.mode === 'valet') {
|
|
504
|
-
console.log(chalk.bold.cyan('\nš© Valet Setup...'));
|
|
505
|
-
try {
|
|
506
|
-
await execa('valet', ['link', responses.system], { cwd: rootDir });
|
|
507
|
-
ora(`Linked ${responses.system}.test to Valet.`).succeed();
|
|
508
|
-
|
|
509
|
-
// Try to secure it
|
|
510
|
-
if (responses.url.startsWith('https://')) {
|
|
511
|
-
await execa('valet', ['secure', responses.system], { cwd: rootDir });
|
|
512
|
-
ora(`Secured ${responses.system}.test with SSL.`).succeed();
|
|
513
|
-
}
|
|
514
|
-
} catch (e) {
|
|
515
|
-
console.warn(chalk.yellow('Valet link/secure failed. You might need to run it manually.'));
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
console.log(chalk.bold.cyan('\nš¦ Installing Export Services...'));
|
|
521
|
-
try {
|
|
522
|
-
await execa('npx', ['@kibro/export-installer@latest'], { stdio: 'inherit' });
|
|
523
|
-
} catch (e) {
|
|
524
|
-
console.warn(chalk.yellow('Export services installer finished with warning or error.'));
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
console.log(chalk.bold.green('\nā
Installation complete!'));
|
|
528
|
-
console.log(chalk.white(`
|
|
529
|
-
To start the server (API + SPA dev):
|
|
530
|
-
cd ${responses.dir}
|
|
531
|
-
npm run dev
|
|
532
|
-
|
|
533
|
-
For Windows users:
|
|
534
|
-
bin\\serve_on_windows.ps1
|
|
535
|
-
`));
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
program.parse();
|
package/tsconfig.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "CommonJS",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"forceConsistentCasingInFileNames": true,
|
|
8
|
-
"strict": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"outDir": "./dist"
|
|
11
|
-
},
|
|
12
|
-
"include": ["src/**/*"]
|
|
13
|
-
}
|