Public Access
fix: address review findings in repo_wizard.php
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m2s
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m2s
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Fix #1: replace nonexistent menu() with choose() using select() - Fix #2: constructor — pass name only, not description as version - Fix #3: respect --non-interactive flag (skip prompts, use defaults) - Fix #4: use json_encode for composer/package.json (prevent injection) - Fix #5: remove pointless count() wrapper - Fix #6: validate --path exists or can be created before proceeding - Fix TOML description escaping
This commit is contained in:
+64
-46
@@ -47,6 +47,9 @@ class RepoWizard extends CliFramework
|
||||
/** Collected wizard answers. */
|
||||
private array $answers = [];
|
||||
|
||||
/** When true, skip all interactive prompts and use defaults. */
|
||||
private bool $nonInteractive = false;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription('Interactive configuration wizard for new repositories');
|
||||
@@ -57,16 +60,25 @@ class RepoWizard extends CliFramework
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$targetPath = realpath($this->getArgument('--path', '.')) ?: $this->getArgument('--path', '.');
|
||||
$rawPath = $this->getArgument('--path', '.');
|
||||
$targetPath = realpath($rawPath) ?: $rawPath;
|
||||
$this->nonInteractive = (bool) $this->getArgument('--non-interactive', false);
|
||||
|
||||
// Validate target path
|
||||
if (!is_dir($targetPath) && !@mkdir($targetPath, 0755, true)) {
|
||||
$this->log('ERROR', "Target path does not exist and cannot be created: {$targetPath}");
|
||||
return self::EXIT_USAGE;
|
||||
}
|
||||
$targetPath = realpath($targetPath) ?: $targetPath;
|
||||
|
||||
$this->section('MokoCli Repository Wizard');
|
||||
|
||||
// ── Gather info ──────────────────────────────────────────────
|
||||
$this->answers['name'] = $this->input('Repository name', basename($targetPath));
|
||||
$this->answers['platform'] = $this->menu('Platform type', self::PLATFORMS, 'generic');
|
||||
$this->answers['org'] = $this->input('Organization', 'MokoConsulting');
|
||||
$this->answers['description'] = $this->input('Description', '');
|
||||
$this->answers['license'] = $this->menu('License', self::LICENSES, 'GPL-3.0-or-later');
|
||||
$this->answers['name'] = $this->ask('Repository name', basename($targetPath));
|
||||
$this->answers['platform'] = $this->choose('Platform type', self::PLATFORMS, 'generic');
|
||||
$this->answers['org'] = $this->ask('Organization', 'MokoConsulting');
|
||||
$this->answers['description'] = $this->ask('Description', '');
|
||||
$this->answers['license'] = $this->choose('License', self::LICENSES, 'GPL-3.0-or-later');
|
||||
|
||||
// ── Confirm ──────────────────────────────────────────────────
|
||||
$this->section('Configuration Summary');
|
||||
@@ -94,7 +106,7 @@ class RepoWizard extends CliFramework
|
||||
}
|
||||
|
||||
$this->log('INFO', '');
|
||||
$this->log('INFO', "Generated {$this->count($generated)} files in {$targetPath}");
|
||||
$this->log('INFO', 'Generated ' . count($generated) . " files in {$targetPath}");
|
||||
$this->log('INFO', 'Next: git init && git add -A && git commit -m "chore: initial scaffold"');
|
||||
|
||||
return 0;
|
||||
@@ -209,7 +221,31 @@ class RepoWizard extends CliFramework
|
||||
}
|
||||
}
|
||||
|
||||
private function count(array $arr): int { return \count($arr); }
|
||||
// ── Interactive helpers (respect --non-interactive) ─────────────
|
||||
|
||||
private function ask(string $prompt, string $default): string
|
||||
{
|
||||
if ($this->nonInteractive) {
|
||||
return $default;
|
||||
}
|
||||
return $this->input($prompt, $default);
|
||||
}
|
||||
|
||||
private function choose(string $prompt, array $options, string $default): string
|
||||
{
|
||||
if ($this->nonInteractive) {
|
||||
return $default;
|
||||
}
|
||||
$keys = array_keys($options);
|
||||
$labels = [];
|
||||
foreach ($options as $key => $desc) {
|
||||
$labels[] = "{$key} — {$desc}";
|
||||
}
|
||||
$chosen = $this->select($prompt, $labels);
|
||||
// Extract the key from "key — description"
|
||||
$chosenKey = explode(' — ', $chosen, 2)[0] ?? $default;
|
||||
return in_array($chosenKey, $keys, true) ? $chosenKey : $default;
|
||||
}
|
||||
|
||||
// ── Template content ─────────────────────────────────────────────
|
||||
|
||||
@@ -266,24 +302,15 @@ MD;
|
||||
|
||||
private function composerJson(): string
|
||||
{
|
||||
$name = strtolower($this->answers['name']);
|
||||
$desc = $this->answers['description'] ?: $this->answers['name'];
|
||||
$license = $this->answers['license'];
|
||||
|
||||
return <<<JSON
|
||||
{
|
||||
"name": "mokoconsulting/{$name}",
|
||||
"description": "{$desc}",
|
||||
"type": "library",
|
||||
"license": "{$license}",
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {}
|
||||
}
|
||||
}
|
||||
JSON;
|
||||
$data = [
|
||||
'name' => 'mokoconsulting/' . strtolower($this->answers['name']),
|
||||
'description' => $this->answers['description'] ?: $this->answers['name'],
|
||||
'type' => 'library',
|
||||
'license' => $this->answers['license'],
|
||||
'require' => ['php' => '>=8.1'],
|
||||
'autoload' => ['psr-4' => new \stdClass()],
|
||||
];
|
||||
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
|
||||
}
|
||||
|
||||
private function phpcsXml(): string
|
||||
@@ -310,24 +337,15 @@ NEON;
|
||||
|
||||
private function packageJson(): string
|
||||
{
|
||||
$name = strtolower($this->answers['name']);
|
||||
$desc = $this->answers['description'] ?: $this->answers['name'];
|
||||
|
||||
return <<<JSON
|
||||
{
|
||||
"name": "@mokoconsulting/{$name}",
|
||||
"version": "0.1.0",
|
||||
"description": "{$desc}",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.0"
|
||||
}
|
||||
}
|
||||
JSON;
|
||||
$data = [
|
||||
'name' => '@mokoconsulting/' . strtolower($this->answers['name']),
|
||||
'version' => '0.1.0',
|
||||
'description' => $this->answers['description'] ?: $this->answers['name'],
|
||||
'type' => 'module',
|
||||
'scripts' => ['build' => 'tsc', 'start' => 'node dist/index.js'],
|
||||
'devDependencies' => ['typescript' => '^5.0'],
|
||||
];
|
||||
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
|
||||
}
|
||||
|
||||
private function tsconfigJson(): string
|
||||
@@ -363,7 +381,7 @@ JSON;
|
||||
private function pyprojectToml(): string
|
||||
{
|
||||
$name = strtolower($this->answers['name']);
|
||||
$desc = $this->answers['description'] ?: $this->answers['name'];
|
||||
$desc = str_replace(['\\', '"'], ['\\\\', '\\"'], $this->answers['description'] ?: $this->answers['name']);
|
||||
|
||||
return <<<TOML
|
||||
[project]
|
||||
@@ -407,5 +425,5 @@ GI;
|
||||
}
|
||||
}
|
||||
|
||||
$app = new RepoWizard('repo_wizard', 'Interactive configuration wizard for new repositories');
|
||||
$app = new RepoWizard('repo_wizard');
|
||||
exit($app->execute());
|
||||
|
||||
Reference in New Issue
Block a user