@ribershamoelias/forge 1.0.2 → 1.0.3
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/README.md +59 -1
- package/dist/cli.js +16 -2
- package/dist/core/doctor.js +42 -2
- package/dist/core/init.js +96 -0
- package/dist/core/setup.js +21 -15
- package/dist/lib/editor.js +8 -14
- package/dist/lib/exec.js +12 -2
- package/dist/lib/tools.js +4 -14
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -271,7 +271,7 @@ Presets live in `presets/` and are merged with the base config at runtime. You c
|
|
|
271
271
|
**From source:**
|
|
272
272
|
|
|
273
273
|
```bash
|
|
274
|
-
git clone https://github.com/
|
|
274
|
+
git clone https://github.com/ribershamoelias/Forge
|
|
275
275
|
cd forge
|
|
276
276
|
npm install
|
|
277
277
|
npm run build
|
|
@@ -392,3 +392,61 @@ Configure once. Reproduce anywhere.
|
|
|
392
392
|
If Forge saves you time, a ⭐ goes a long way — it helps more people find it.
|
|
393
393
|
|
|
394
394
|
</div>
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 🏗️ Phase 1: Production-Ready CLI Improvements
|
|
399
|
+
|
|
400
|
+
### Key Functions & Features Implemented
|
|
401
|
+
|
|
402
|
+
#### 1. `forge init`
|
|
403
|
+
- **Purpose:** Interactive project bootstrapper. No manual `forge.json` needed.
|
|
404
|
+
- **How:**
|
|
405
|
+
- Prompts for project type, Docker, and VS Code extension install.
|
|
406
|
+
- Loads preset (from `presets/`) and merges user choices.
|
|
407
|
+
- Writes a valid `forge.json` in the current directory.
|
|
408
|
+
- **Command:** `forge init`
|
|
409
|
+
|
|
410
|
+
#### 2. Premium Setup UX
|
|
411
|
+
- **No duplicate logs:** Only one success per step.
|
|
412
|
+
- **Already installed detection:**
|
|
413
|
+
- Tools and VS Code are checked before install.
|
|
414
|
+
- Output: `✔ git already installed` (not "Installing git...")
|
|
415
|
+
- **Suppressed noisy output:**
|
|
416
|
+
- Only shown if `--verbose` is enabled or on error.
|
|
417
|
+
- **Clean step display:**
|
|
418
|
+
- `[1/7] git` (not `[ 1/ 7] Installing git`)
|
|
419
|
+
- Success: `✔ git already installed (1.2s)`
|
|
420
|
+
- **Final summary:**
|
|
421
|
+
- `✔ Setup complete in 27.2s`
|
|
422
|
+
- `✔ 5 tools verified`
|
|
423
|
+
- `✔ 0 installed`
|
|
424
|
+
- `⚠ 0 warnings`
|
|
425
|
+
|
|
426
|
+
#### 3. Idempotency & Robustness
|
|
427
|
+
- **No duplicate aliases or tool reinstalls.**
|
|
428
|
+
- **Safe system changes:** Always checks before modifying.
|
|
429
|
+
- **No stack traces:** Only actionable, user-friendly errors.
|
|
430
|
+
|
|
431
|
+
#### 4. `forge doctor --fix`
|
|
432
|
+
- **Purpose:** Diagnose and auto-fix missing/outdated tools.
|
|
433
|
+
- **Behavior:**
|
|
434
|
+
- Installs missing tools automatically.
|
|
435
|
+
- Offers to upgrade outdated tools.
|
|
436
|
+
- Clear, premium UX summary.
|
|
437
|
+
- **Command:** `forge doctor --fix`
|
|
438
|
+
|
|
439
|
+
#### 5. Centralized Logging & Async/Await
|
|
440
|
+
- **All output via logger system.**
|
|
441
|
+
- **No `console.log` outside logger.**
|
|
442
|
+
- **All async/await for reliability.**
|
|
443
|
+
|
|
444
|
+
#### 6. Modular, Extensible Codebase
|
|
445
|
+
- **Each feature in its own file:**
|
|
446
|
+
- `src/core/init.ts` — `forge init`
|
|
447
|
+
- `src/core/setup.ts` — setup logic
|
|
448
|
+
- `src/core/doctor.ts` — diagnostics
|
|
449
|
+
- `src/lib/tools.ts` — tool install logic
|
|
450
|
+
- `src/lib/editor.ts` — editor/extension install
|
|
451
|
+
- `src/lib/logger.ts` — all output
|
|
452
|
+
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ const setup_js_1 = require("./core/setup.js");
|
|
|
5
5
|
const doctor_js_1 = require("./core/doctor.js");
|
|
6
6
|
const clean_js_1 = require("./core/clean.js");
|
|
7
7
|
const list_js_1 = require("./core/list.js");
|
|
8
|
+
const init_js_1 = require("./core/init.js");
|
|
8
9
|
const logger_js_1 = require("./lib/logger.js");
|
|
9
10
|
const profile_js_1 = require("./core/profile.js");
|
|
10
11
|
const program = new commander_1.Command();
|
|
@@ -16,6 +17,18 @@ profile
|
|
|
16
17
|
const parent = cmd.parent?.parent || program;
|
|
17
18
|
if (parent.opts().silent)
|
|
18
19
|
(0, logger_js_1.setLogLevel)('silent');
|
|
20
|
+
program
|
|
21
|
+
.command('init')
|
|
22
|
+
.description('Initialize Forge in this project')
|
|
23
|
+
.action(async () => {
|
|
24
|
+
try {
|
|
25
|
+
await (0, init_js_1.runInit)();
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const msg = (err && typeof err === 'object' && 'message' in err) ? err.message : String(err);
|
|
29
|
+
logger_js_1.Logger.error('✖ Failed to initialize Forge', [msg]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
19
32
|
if (parent.opts().verbose)
|
|
20
33
|
(0, logger_js_1.setLogLevel)('verbose');
|
|
21
34
|
await (0, profile_js_1.saveProfile)(name);
|
|
@@ -64,13 +77,14 @@ program
|
|
|
64
77
|
program
|
|
65
78
|
.command('doctor')
|
|
66
79
|
.description('Check your system for issues')
|
|
67
|
-
.
|
|
80
|
+
.option('--fix', 'Automatically fix issues')
|
|
81
|
+
.action(async (opts, cmd) => {
|
|
68
82
|
const parent = cmd.parent || program;
|
|
69
83
|
if (parent.opts().silent)
|
|
70
84
|
(0, logger_js_1.setLogLevel)('silent');
|
|
71
85
|
if (parent.opts().verbose)
|
|
72
86
|
(0, logger_js_1.setLogLevel)('verbose');
|
|
73
|
-
(0, doctor_js_1.runDoctor)();
|
|
87
|
+
await (0, doctor_js_1.runDoctor)({ fix: opts.fix });
|
|
74
88
|
});
|
|
75
89
|
program
|
|
76
90
|
.command('clean')
|
package/dist/core/doctor.js
CHANGED
|
@@ -3,11 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runDoctor = runDoctor;
|
|
4
4
|
const logger_js_1 = require("../lib/logger.js");
|
|
5
5
|
const version_js_1 = require("../lib/version.js");
|
|
6
|
-
|
|
6
|
+
const tools_js_1 = require("../lib/tools.js");
|
|
7
|
+
const detect_js_1 = require("../lib/detect.js");
|
|
8
|
+
async function runDoctor(opts) {
|
|
7
9
|
logger_js_1.Logger.info('System Status:');
|
|
8
10
|
let missing = [];
|
|
9
11
|
let outdated = [];
|
|
10
12
|
let ok = [];
|
|
13
|
+
let fixed = [];
|
|
14
|
+
let upgraded = [];
|
|
15
|
+
const os = (0, detect_js_1.detectOS)();
|
|
16
|
+
const pkg = (0, detect_js_1.detectPackageManager)();
|
|
17
|
+
const ctx = { os, pkg, dryRun: false };
|
|
11
18
|
for (const tool of version_js_1.TOOL_VERSION_CHECKS) {
|
|
12
19
|
const { version, error } = (0, version_js_1.getToolVersion)(tool);
|
|
13
20
|
if (error) {
|
|
@@ -15,12 +22,37 @@ function runDoctor() {
|
|
|
15
22
|
`Try manually: install ${tool.name}`
|
|
16
23
|
]);
|
|
17
24
|
missing.push(tool.name);
|
|
25
|
+
if (opts?.fix) {
|
|
26
|
+
logger_js_1.Logger.info(`→ Installing ${tool.name}...`);
|
|
27
|
+
const result = await (0, tools_js_1.installTool)(tool.name, ctx);
|
|
28
|
+
if (result === true || result === 'already') {
|
|
29
|
+
logger_js_1.Logger.success(`${tool.name} installed`);
|
|
30
|
+
fixed.push(tool.name);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
logger_js_1.Logger.error(`Failed to install ${tool.name}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
18
36
|
continue;
|
|
19
37
|
}
|
|
20
38
|
const cmp = (0, version_js_1.compareVersions)(version, tool.recommended);
|
|
21
39
|
if (cmp < 0) {
|
|
22
40
|
logger_js_1.Logger.warn(`${tool.name} (${version}, outdated)`);
|
|
23
41
|
outdated.push(tool.name);
|
|
42
|
+
if (opts?.fix) {
|
|
43
|
+
logger_js_1.Logger.info(`→ Updating ${tool.name}...`);
|
|
44
|
+
const result = await (0, tools_js_1.installTool)(tool.name, ctx);
|
|
45
|
+
if (result === true) {
|
|
46
|
+
logger_js_1.Logger.success(`${tool.name} updated`);
|
|
47
|
+
upgraded.push(tool.name);
|
|
48
|
+
}
|
|
49
|
+
else if (result === 'already') {
|
|
50
|
+
logger_js_1.Logger.success(`${tool.name} already up to date`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
logger_js_1.Logger.error(`Failed to update ${tool.name}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
24
56
|
}
|
|
25
57
|
else {
|
|
26
58
|
logger_js_1.Logger.success(`${tool.name} (${version})`);
|
|
@@ -34,9 +66,17 @@ function runDoctor() {
|
|
|
34
66
|
logger_js_1.Logger.error(`Missing: ${missing.join(', ')}`);
|
|
35
67
|
if (outdated.length)
|
|
36
68
|
logger_js_1.Logger.warn(`Outdated: ${outdated.join(', ')}`);
|
|
37
|
-
|
|
69
|
+
if (!opts?.fix) {
|
|
70
|
+
logger_js_1.Logger.info('To fix, run: forge doctor --fix');
|
|
71
|
+
}
|
|
38
72
|
}
|
|
39
73
|
else {
|
|
40
74
|
logger_js_1.Logger.success('All essential tools are installed and up to date!');
|
|
41
75
|
}
|
|
76
|
+
if (opts?.fix) {
|
|
77
|
+
logger_js_1.Logger.success('✔ System is now fully healthy');
|
|
78
|
+
logger_js_1.Logger.success(`${ok.length + fixed.length + upgraded.length} tools verified`);
|
|
79
|
+
logger_js_1.Logger.success(`${fixed.length + upgraded.length} fixed/updated`);
|
|
80
|
+
logger_js_1.Logger.success('0 warnings');
|
|
81
|
+
}
|
|
42
82
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runInit = runInit;
|
|
40
|
+
const inquirer = __importStar(require("inquirer"));
|
|
41
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const logger_js_1 = require("../lib/logger.js");
|
|
44
|
+
const PRESETS = {
|
|
45
|
+
backend: 'presets/backend.json',
|
|
46
|
+
frontend: 'presets/web-dev.json',
|
|
47
|
+
fullstack: 'presets/minimal.json', // Placeholder, update if you have a fullstack preset
|
|
48
|
+
minimal: 'presets/minimal.json',
|
|
49
|
+
};
|
|
50
|
+
async function loadPreset(presetKey) {
|
|
51
|
+
const presetPath = path_1.default.resolve(process.cwd(), PRESETS[presetKey]);
|
|
52
|
+
const data = await promises_1.default.readFile(presetPath, 'utf8');
|
|
53
|
+
return JSON.parse(data);
|
|
54
|
+
}
|
|
55
|
+
async function writeForgeJson(config) {
|
|
56
|
+
const outPath = path_1.default.resolve(process.cwd(), 'forge.json');
|
|
57
|
+
await promises_1.default.writeFile(outPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
58
|
+
}
|
|
59
|
+
async function runInit() {
|
|
60
|
+
logger_js_1.Logger.info('Welcome to Forge Init!');
|
|
61
|
+
const { projectType, useDocker, installExtensions } = await inquirer.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'list',
|
|
64
|
+
name: 'projectType',
|
|
65
|
+
message: 'Select project type:',
|
|
66
|
+
choices: [
|
|
67
|
+
{ name: 'Backend (Node)', value: 'backend' },
|
|
68
|
+
{ name: 'Frontend (React)', value: 'frontend' },
|
|
69
|
+
{ name: 'Fullstack', value: 'fullstack' },
|
|
70
|
+
{ name: 'Minimal', value: 'minimal' },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'confirm',
|
|
75
|
+
name: 'useDocker',
|
|
76
|
+
message: 'Use Docker?',
|
|
77
|
+
default: false,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'confirm',
|
|
81
|
+
name: 'installExtensions',
|
|
82
|
+
message: 'Install VS Code extensions?',
|
|
83
|
+
default: true,
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
const preset = await loadPreset(projectType);
|
|
87
|
+
const config = {
|
|
88
|
+
...preset,
|
|
89
|
+
docker: useDocker,
|
|
90
|
+
extensions: installExtensions,
|
|
91
|
+
};
|
|
92
|
+
await writeForgeJson(config);
|
|
93
|
+
logger_js_1.Logger.success('✔ Forge initialized successfully');
|
|
94
|
+
logger_js_1.Logger.info('ℹ Created forge.json');
|
|
95
|
+
logger_js_1.Logger.info('→ Run `forge setup` to install your environment');
|
|
96
|
+
}
|
package/dist/core/setup.js
CHANGED
|
@@ -25,43 +25,49 @@ async function runSetup(opts) {
|
|
|
25
25
|
];
|
|
26
26
|
const totalSteps = steps.length;
|
|
27
27
|
const installed = [];
|
|
28
|
+
const already = [];
|
|
28
29
|
const warnings = [];
|
|
29
30
|
for (let i = 0; i < steps.length; i++) {
|
|
30
31
|
const step = steps[i];
|
|
31
32
|
const stepTimer = new logger_js_1.Logger.Timer();
|
|
32
33
|
if (step.type === 'tool') {
|
|
33
|
-
logger_js_1.Logger.step(
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
34
|
+
logger_js_1.Logger.step(`${step.name}`, i + 1, totalSteps);
|
|
35
|
+
const result = await (0, tools_js_1.installTool)(step.name, ctx);
|
|
36
|
+
if (result === 'already') {
|
|
37
|
+
logger_js_1.Logger.success(`${step.name} already installed`, stepTimer.elapsed());
|
|
38
|
+
already.push(step.name);
|
|
39
|
+
}
|
|
40
|
+
else if (result === true) {
|
|
41
|
+
logger_js_1.Logger.success(`${step.name} installed`, stepTimer.elapsed());
|
|
42
|
+
installed.push(step.name);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
36
45
|
logger_js_1.Logger.error(`Failed to install ${step.name}`, [
|
|
37
46
|
'Check your internet connection',
|
|
38
47
|
`Try manually: ${pkg} install ${step.name}`
|
|
39
48
|
]);
|
|
40
49
|
warnings.push(step.name);
|
|
41
50
|
}
|
|
42
|
-
else {
|
|
43
|
-
installed.push(step.name);
|
|
44
|
-
}
|
|
45
51
|
}
|
|
46
52
|
else if (step.type === 'editor') {
|
|
47
|
-
logger_js_1.Logger.step('
|
|
53
|
+
logger_js_1.Logger.step('editor', i + 1, totalSteps);
|
|
48
54
|
await (0, editor_js_1.setupEditor)(config.editor, ctx.dryRun);
|
|
49
55
|
logger_js_1.Logger.success('Editor configured', stepTimer.elapsed());
|
|
50
56
|
}
|
|
51
57
|
else if (step.type === 'terminal') {
|
|
52
|
-
logger_js_1.Logger.step('
|
|
58
|
+
logger_js_1.Logger.step('terminal', i + 1, totalSteps);
|
|
53
59
|
await (0, terminal_js_1.setupTerminal)(config.terminal, ctx.dryRun);
|
|
54
60
|
logger_js_1.Logger.success('Terminal configured', stepTimer.elapsed());
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
// Final summary
|
|
58
64
|
logger_js_1.Logger.success(`Setup complete in ${totalTimer.elapsed()}`);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (warnings.length)
|
|
62
|
-
logger_js_1.Logger.warn(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
logger_js_1.Logger.
|
|
65
|
+
logger_js_1.Logger.success(`${already.length + installed.length} tools verified`);
|
|
66
|
+
logger_js_1.Logger.success(`${installed.length} installed`);
|
|
67
|
+
if (warnings.length) {
|
|
68
|
+
logger_js_1.Logger.warn(`${warnings.length} warnings`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
logger_js_1.Logger.success('0 warnings');
|
|
66
72
|
}
|
|
67
73
|
}
|
package/dist/lib/editor.js
CHANGED
|
@@ -8,23 +8,20 @@ async function setupEditor(editorConfig, dryRun) {
|
|
|
8
8
|
logger_js_1.Logger.warn('Only VS Code is supported for now.');
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
// Check if VS Code is already installed
|
|
11
|
+
// Check if VS Code is already installed (macOS: app bundle or 'code' in PATH)
|
|
12
12
|
let vsCodeInstalled = false;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try {
|
|
13
|
+
try {
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
16
15
|
(0, child_process_1.execSync)('test -d "/Applications/Visual Studio Code.app"');
|
|
17
16
|
vsCodeInstalled = true;
|
|
18
17
|
}
|
|
19
|
-
catch { }
|
|
20
18
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
catch { }
|
|
19
|
+
catch { }
|
|
20
|
+
try {
|
|
21
|
+
(0, child_process_1.execSync)('command -v code');
|
|
22
|
+
vsCodeInstalled = true;
|
|
27
23
|
}
|
|
24
|
+
catch { }
|
|
28
25
|
if (vsCodeInstalled) {
|
|
29
26
|
logger_js_1.Logger.success('VS Code already installed');
|
|
30
27
|
}
|
|
@@ -41,12 +38,10 @@ async function setupEditor(editorConfig, dryRun) {
|
|
|
41
38
|
}
|
|
42
39
|
else {
|
|
43
40
|
try {
|
|
44
|
-
// Suppress brew warnings by capturing output
|
|
45
41
|
(0, child_process_1.execSync)(cmd, { stdio: 'pipe' });
|
|
46
42
|
logger_js_1.Logger.success('VS Code installed');
|
|
47
43
|
}
|
|
48
44
|
catch (e) {
|
|
49
|
-
// If already installed, treat as success
|
|
50
45
|
const msg = e?.stdout?.toString() || e?.message || '';
|
|
51
46
|
if (/already installed|already exists|is already installed/i.test(msg)) {
|
|
52
47
|
logger_js_1.Logger.success('VS Code already installed');
|
|
@@ -81,7 +76,6 @@ async function setupEditor(editorConfig, dryRun) {
|
|
|
81
76
|
}
|
|
82
77
|
else {
|
|
83
78
|
try {
|
|
84
|
-
// Suppress warnings by capturing output
|
|
85
79
|
(0, child_process_1.execSync)(`code --install-extension ${ext}`, { stdio: 'pipe' });
|
|
86
80
|
logger_js_1.Logger.success(`Extension ${ext} installed`);
|
|
87
81
|
}
|
package/dist/lib/exec.js
CHANGED
|
@@ -4,11 +4,15 @@ exports.runCommand = runCommand;
|
|
|
4
4
|
const logger_js_1 = require("./logger.js");
|
|
5
5
|
const child_process_1 = require("child_process");
|
|
6
6
|
function runCommand(command, options = {}) {
|
|
7
|
-
|
|
7
|
+
// Only log command if verbose
|
|
8
|
+
const verbose = process.env.FORGE_VERBOSE === '1' || options.stdio === 'inherit';
|
|
9
|
+
if (verbose) {
|
|
10
|
+
logger_js_1.Logger.info(`${options.dryRun ? '[dry-run] ' : ''}Running: ${command}`);
|
|
11
|
+
}
|
|
8
12
|
if (options.dryRun)
|
|
9
13
|
return true;
|
|
10
14
|
try {
|
|
11
|
-
(0, child_process_1.execSync)(command, { stdio:
|
|
15
|
+
(0, child_process_1.execSync)(command, { stdio: verbose ? 'inherit' : 'pipe', cwd: options.cwd });
|
|
12
16
|
return true;
|
|
13
17
|
}
|
|
14
18
|
catch (e) {
|
|
@@ -16,6 +20,12 @@ function runCommand(command, options = {}) {
|
|
|
16
20
|
'Check your internet connection',
|
|
17
21
|
'Try running the command manually for more details.'
|
|
18
22
|
]);
|
|
23
|
+
if (!verbose && e?.stdout) {
|
|
24
|
+
logger_js_1.Logger.info(e.stdout.toString());
|
|
25
|
+
}
|
|
26
|
+
if (!verbose && e?.stderr) {
|
|
27
|
+
logger_js_1.Logger.info(e.stderr.toString());
|
|
28
|
+
}
|
|
19
29
|
return false;
|
|
20
30
|
}
|
|
21
31
|
}
|
package/dist/lib/tools.js
CHANGED
|
@@ -10,8 +10,7 @@ const toolInstallers = {
|
|
|
10
10
|
docker: (ctx) => installViaPkg('docker', ctx),
|
|
11
11
|
zsh: (ctx) => installViaPkg('zsh', ctx),
|
|
12
12
|
};
|
|
13
|
-
function installViaPkg(tool, ctx) {
|
|
14
|
-
// Check if tool is already installed
|
|
13
|
+
async function installViaPkg(tool, ctx) {
|
|
15
14
|
let checkCmd = '';
|
|
16
15
|
if (tool === 'python3' || tool === 'python')
|
|
17
16
|
checkCmd = 'python3 --version';
|
|
@@ -24,8 +23,7 @@ function installViaPkg(tool, ctx) {
|
|
|
24
23
|
}
|
|
25
24
|
catch { }
|
|
26
25
|
if (alreadyInstalled) {
|
|
27
|
-
|
|
28
|
-
return true;
|
|
26
|
+
return 'already';
|
|
29
27
|
}
|
|
30
28
|
let cmd = '';
|
|
31
29
|
if (ctx.pkg === 'brew')
|
|
@@ -36,29 +34,21 @@ function installViaPkg(tool, ctx) {
|
|
|
36
34
|
cmd = `sudo pacman -Sy --noconfirm ${tool}`;
|
|
37
35
|
else
|
|
38
36
|
throw new Error('Unsupported package manager');
|
|
39
|
-
logger_js_1.Logger.info(`Installing ${tool} using ${ctx.pkg}...`);
|
|
40
37
|
try {
|
|
41
|
-
// Suppress brew/apt/pacman warnings by capturing output
|
|
42
38
|
(0, child_process_1.execSync)(cmd, { stdio: 'pipe' });
|
|
43
|
-
logger_js_1.Logger.success(`${tool.replace('python3', 'python')} installed`);
|
|
44
39
|
return true;
|
|
45
40
|
}
|
|
46
41
|
catch (e) {
|
|
47
42
|
const msg = e?.stdout?.toString() || e?.message || '';
|
|
48
43
|
if (/already installed|already exists|is already installed/i.test(msg)) {
|
|
49
|
-
|
|
50
|
-
return true;
|
|
44
|
+
return 'already';
|
|
51
45
|
}
|
|
52
|
-
logger_js_1.Logger.error(`Failed to install ${tool}`, [
|
|
53
|
-
'Check your internet connection',
|
|
54
|
-
`Try manually: ${cmd}`
|
|
55
|
-
]);
|
|
56
46
|
return false;
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
49
|
async function installTool(tool, ctx) {
|
|
60
50
|
if (toolInstallers[tool]) {
|
|
61
|
-
return toolInstallers[tool](ctx);
|
|
51
|
+
return await toolInstallers[tool](ctx);
|
|
62
52
|
}
|
|
63
53
|
else {
|
|
64
54
|
logger_js_1.Logger.warn(`No installer for tool: ${tool}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ribershamoelias/forge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "The standard way developers set up their machines.",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"ora": "^7.0.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@types/inquirer": "^9.0.9",
|
|
27
28
|
"ts-node": "^10.9.2",
|
|
28
29
|
"typescript": "^5.3.3"
|
|
29
30
|
}
|