@gpc-cli/cli 0.9.23 → 0.9.24
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 +32 -27
- package/dist/bin.js +1 -1
- package/dist/{bundle-U7RF6HDE.js → bundle-7IF5FIB4.js} +63 -1
- package/dist/bundle-7IF5FIB4.js.map +1 -0
- package/dist/{chunk-CE2HXEJX.js → chunk-SZLO6HOO.js} +7 -7
- package/dist/config-2L7QUYWP.js +97 -0
- package/dist/config-2L7QUYWP.js.map +1 -0
- package/dist/{doctor-TEIKODLP.js → doctor-JMEHYOGJ.js} +76 -63
- package/dist/doctor-JMEHYOGJ.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/migrate-XQV7P4R7.js +135 -0
- package/dist/migrate-XQV7P4R7.js.map +1 -0
- package/dist/{publish-PZRWX3TH.js → publish-26ZPS7XX.js} +86 -16
- package/dist/publish-26ZPS7XX.js.map +1 -0
- package/dist/status-C222ZKHH.js +96 -0
- package/dist/status-C222ZKHH.js.map +1 -0
- package/package.json +5 -5
- package/dist/bundle-U7RF6HDE.js.map +0 -1
- package/dist/config-R5U7GV56.js +0 -51
- package/dist/config-R5U7GV56.js.map +0 -1
- package/dist/doctor-TEIKODLP.js.map +0 -1
- package/dist/migrate-V6G5YUVH.js +0 -80
- package/dist/migrate-V6G5YUVH.js.map +0 -1
- package/dist/publish-PZRWX3TH.js.map +0 -1
- package/dist/status-S3FAEXNH.js +0 -37
- package/dist/status-S3FAEXNH.js.map +0 -1
- /package/dist/{chunk-CE2HXEJX.js.map → chunk-SZLO6HOO.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getOutputFormat
|
|
4
|
+
} from "./chunk-ELXAK7GI.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/migrate.ts
|
|
7
|
+
import { access } from "fs/promises";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { loadConfig } from "@gpc-cli/config";
|
|
10
|
+
import {
|
|
11
|
+
detectFastlane,
|
|
12
|
+
generateMigrationPlan,
|
|
13
|
+
writeMigrationOutput,
|
|
14
|
+
formatOutput
|
|
15
|
+
} from "@gpc-cli/core";
|
|
16
|
+
var WARN = "\u26A0";
|
|
17
|
+
async function fileExists(path) {
|
|
18
|
+
try {
|
|
19
|
+
await access(path);
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function registerMigrateCommands(program) {
|
|
26
|
+
const migrate = program.command("migrate").description("Migrate from other tools to GPC");
|
|
27
|
+
migrate.command("fastlane").description("Migrate from Fastlane to GPC").option("--dir <path>", "Directory containing Fastlane files", ".").option("--output <path>", "Output directory for migration files", ".").option("--dry-run", "Preview migration plan without writing any files").action(async (options) => {
|
|
28
|
+
const config = await loadConfig();
|
|
29
|
+
const format = getOutputFormat(program, config);
|
|
30
|
+
const dryRun = options.dryRun ?? false;
|
|
31
|
+
try {
|
|
32
|
+
const detection = await detectFastlane(options.dir);
|
|
33
|
+
if (format === "json") {
|
|
34
|
+
if (!detection.hasFastfile && !detection.hasAppfile && !detection.hasMetadata) {
|
|
35
|
+
console.log(formatOutput({ detection, plan: null, files: [] }, format));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const plan2 = generateMigrationPlan(detection);
|
|
39
|
+
if (!dryRun) {
|
|
40
|
+
const files2 = await writeMigrationOutput(plan2, options.output);
|
|
41
|
+
console.log(formatOutput({ detection, plan: plan2, files: files2 }, format));
|
|
42
|
+
} else {
|
|
43
|
+
console.log(formatOutput({ detection, plan: plan2, files: [] }, format));
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log("Fastlane Detection Results:\n");
|
|
48
|
+
console.log(` Fastfile: ${detection.hasFastfile ? "found" : "not found"}`);
|
|
49
|
+
console.log(` Appfile: ${detection.hasAppfile ? "found" : "not found"}`);
|
|
50
|
+
console.log(` Metadata: ${detection.hasMetadata ? "found" : "not found"}`);
|
|
51
|
+
console.log(` Gemfile: ${detection.hasGemfile ? "found" : "not found"}`);
|
|
52
|
+
if (detection.packageName) {
|
|
53
|
+
console.log(` Package: ${detection.packageName}`);
|
|
54
|
+
}
|
|
55
|
+
if (detection.lanes.length > 0) {
|
|
56
|
+
console.log(`
|
|
57
|
+
Lanes (${detection.lanes.length}):`);
|
|
58
|
+
for (const lane of detection.lanes) {
|
|
59
|
+
const equiv = lane.gpcEquivalent ? ` \u2192 ${lane.gpcEquivalent}` : " (no equivalent)";
|
|
60
|
+
console.log(` ${lane.name}${equiv}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (detection.metadataLanguages.length > 0) {
|
|
64
|
+
console.log(`
|
|
65
|
+
Metadata languages: ${detection.metadataLanguages.join(", ")}`);
|
|
66
|
+
}
|
|
67
|
+
if (detection.parseWarnings.length > 0) {
|
|
68
|
+
console.log("");
|
|
69
|
+
for (const w of detection.parseWarnings) {
|
|
70
|
+
console.log(` ${WARN} ${w}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!detection.hasFastfile && !detection.hasAppfile && !detection.hasMetadata) {
|
|
74
|
+
console.log("\nNo Fastlane files detected in this directory. Nothing to migrate.");
|
|
75
|
+
console.log(" Try: gpc migrate fastlane --dir <path-to-your-android-project>");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const plan = generateMigrationPlan(detection);
|
|
79
|
+
if (dryRun) {
|
|
80
|
+
console.log("\nMigration Plan (dry run \u2014 nothing written):\n");
|
|
81
|
+
if (plan.warnings.length > 0) {
|
|
82
|
+
console.log("Warnings:");
|
|
83
|
+
for (const w of plan.warnings) {
|
|
84
|
+
console.log(` ${WARN} ${w}`);
|
|
85
|
+
}
|
|
86
|
+
console.log("");
|
|
87
|
+
}
|
|
88
|
+
console.log("Checklist:");
|
|
89
|
+
for (const item of plan.checklist) {
|
|
90
|
+
console.log(` [ ] ${item}`);
|
|
91
|
+
}
|
|
92
|
+
if (Object.keys(plan.config).length > 0) {
|
|
93
|
+
console.log("\n.gpcrc.json (would be written):");
|
|
94
|
+
console.log(JSON.stringify(plan.config, null, 2).replace(/^/gm, " "));
|
|
95
|
+
}
|
|
96
|
+
console.log(
|
|
97
|
+
"\nRun without --dry-run to write MIGRATION.md" + (Object.keys(plan.config).length > 0 ? " and .gpcrc.json" : "") + "."
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (Object.keys(plan.config).length > 0 && await fileExists(join(options.output, ".gpcrc.json"))) {
|
|
102
|
+
console.log(
|
|
103
|
+
`
|
|
104
|
+
${WARN} .gpcrc.json already exists in ${options.output} \u2014 it will be overwritten.`
|
|
105
|
+
);
|
|
106
|
+
console.log(" Use --dry-run to preview the new config first.");
|
|
107
|
+
}
|
|
108
|
+
const files = await writeMigrationOutput(plan, options.output);
|
|
109
|
+
console.log("\nMigration files written:");
|
|
110
|
+
for (const file of files) {
|
|
111
|
+
console.log(` ${file}`);
|
|
112
|
+
}
|
|
113
|
+
if (plan.warnings.length > 0) {
|
|
114
|
+
console.log("\nWarnings:");
|
|
115
|
+
for (const w of plan.warnings) {
|
|
116
|
+
console.log(` ${WARN} ${w}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
console.log("\nMigration Checklist:");
|
|
120
|
+
for (const item of plan.checklist) {
|
|
121
|
+
console.log(` [ ] ${item}`);
|
|
122
|
+
}
|
|
123
|
+
console.log(
|
|
124
|
+
"\nNext step: open MIGRATION.md and work through the checklist. Run `gpc doctor` when done."
|
|
125
|
+
);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
registerMigrateCommands
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=migrate-XQV7P4R7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/migrate.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport {\n detectFastlane,\n generateMigrationPlan,\n writeMigrationOutput,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nconst WARN = \"\\u26A0\";\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function registerMigrateCommands(program: Command): void {\n const migrate = program.command(\"migrate\").description(\"Migrate from other tools to GPC\");\n\n migrate\n .command(\"fastlane\")\n .description(\"Migrate from Fastlane to GPC\")\n .option(\"--dir <path>\", \"Directory containing Fastlane files\", \".\")\n .option(\"--output <path>\", \"Output directory for migration files\", \".\")\n .option(\"--dry-run\", \"Preview migration plan without writing any files\")\n .action(async (options: { dir: string; output: string; dryRun?: boolean }) => {\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n const dryRun = options.dryRun ?? false;\n\n try {\n const detection = await detectFastlane(options.dir);\n\n if (format === \"json\") {\n if (\n !detection.hasFastfile &&\n !detection.hasAppfile &&\n !detection.hasMetadata\n ) {\n console.log(formatOutput({ detection, plan: null, files: [] }, format));\n return;\n }\n const plan = generateMigrationPlan(detection);\n if (!dryRun) {\n const files = await writeMigrationOutput(plan, options.output);\n console.log(formatOutput({ detection, plan, files }, format));\n } else {\n console.log(formatOutput({ detection, plan, files: [] }, format));\n }\n return;\n }\n\n // Human-readable output\n console.log(\"Fastlane Detection Results:\\n\");\n console.log(` Fastfile: ${detection.hasFastfile ? \"found\" : \"not found\"}`);\n console.log(` Appfile: ${detection.hasAppfile ? \"found\" : \"not found\"}`);\n console.log(` Metadata: ${detection.hasMetadata ? \"found\" : \"not found\"}`);\n console.log(` Gemfile: ${detection.hasGemfile ? \"found\" : \"not found\"}`);\n\n if (detection.packageName) {\n console.log(` Package: ${detection.packageName}`);\n }\n\n if (detection.lanes.length > 0) {\n console.log(`\\n Lanes (${detection.lanes.length}):`);\n for (const lane of detection.lanes) {\n const equiv = lane.gpcEquivalent ? ` → ${lane.gpcEquivalent}` : \" (no equivalent)\";\n console.log(` ${lane.name}${equiv}`);\n }\n }\n\n if (detection.metadataLanguages.length > 0) {\n console.log(`\\n Metadata languages: ${detection.metadataLanguages.join(\", \")}`);\n }\n\n if (detection.parseWarnings.length > 0) {\n console.log(\"\");\n for (const w of detection.parseWarnings) {\n console.log(` ${WARN} ${w}`);\n }\n }\n\n if (\n !detection.hasFastfile &&\n !detection.hasAppfile &&\n !detection.hasMetadata\n ) {\n console.log(\"\\nNo Fastlane files detected in this directory. Nothing to migrate.\");\n console.log(\" Try: gpc migrate fastlane --dir <path-to-your-android-project>\");\n return;\n }\n\n const plan = generateMigrationPlan(detection);\n\n if (dryRun) {\n console.log(\"\\nMigration Plan (dry run — nothing written):\\n\");\n\n if (plan.warnings.length > 0) {\n console.log(\"Warnings:\");\n for (const w of plan.warnings) {\n console.log(` ${WARN} ${w}`);\n }\n console.log(\"\");\n }\n\n console.log(\"Checklist:\");\n for (const item of plan.checklist) {\n console.log(` [ ] ${item}`);\n }\n\n if (Object.keys(plan.config).length > 0) {\n console.log(\"\\n.gpcrc.json (would be written):\");\n console.log(JSON.stringify(plan.config, null, 2).replace(/^/gm, \" \"));\n }\n\n console.log(\n \"\\nRun without --dry-run to write MIGRATION.md\" +\n (Object.keys(plan.config).length > 0 ? \" and .gpcrc.json\" : \"\") +\n \".\",\n );\n return;\n }\n\n // Conflict check — warn before clobbering existing .gpcrc.json\n if (\n Object.keys(plan.config).length > 0 &&\n (await fileExists(join(options.output, \".gpcrc.json\")))\n ) {\n console.log(\n `\\n${WARN} .gpcrc.json already exists in ${options.output} — it will be overwritten.`,\n );\n console.log(\" Use --dry-run to preview the new config first.\");\n }\n\n const files = await writeMigrationOutput(plan, options.output);\n\n console.log(\"\\nMigration files written:\");\n for (const file of files) {\n console.log(` ${file}`);\n }\n\n if (plan.warnings.length > 0) {\n console.log(\"\\nWarnings:\");\n for (const w of plan.warnings) {\n console.log(` ${WARN} ${w}`);\n }\n }\n\n console.log(\"\\nMigration Checklist:\");\n for (const item of plan.checklist) {\n console.log(` [ ] ${item}`);\n }\n\n console.log(\n \"\\nNext step: open MIGRATION.md and work through the checklist. Run `gpc doctor` when done.\",\n );\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":["plan","files"]}
|
|
@@ -16,8 +16,16 @@ import { appendFile } from "fs/promises";
|
|
|
16
16
|
import { loadConfig, getCacheDir } from "@gpc-cli/config";
|
|
17
17
|
import { resolveAuth } from "@gpc-cli/auth";
|
|
18
18
|
import { createApiClient } from "@gpc-cli/api";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
publish,
|
|
21
|
+
generateNotesFromGit,
|
|
22
|
+
writeAuditLog,
|
|
23
|
+
createAuditEntry,
|
|
24
|
+
formatOutput
|
|
25
|
+
} from "@gpc-cli/core";
|
|
26
|
+
var PASS = "\u2713";
|
|
27
|
+
var FAIL = "\u2717";
|
|
28
|
+
var WARN = "\u26A0";
|
|
21
29
|
function resolvePackageName(packageArg, config) {
|
|
22
30
|
const name = packageArg || config.app;
|
|
23
31
|
if (!name) {
|
|
@@ -26,11 +34,67 @@ function resolvePackageName(packageArg, config) {
|
|
|
26
34
|
}
|
|
27
35
|
return name;
|
|
28
36
|
}
|
|
37
|
+
function formatChecks(checks) {
|
|
38
|
+
return checks.map((c) => ` ${c.passed ? PASS : FAIL} ${c.message}`);
|
|
39
|
+
}
|
|
40
|
+
function formatValidationOutput(result, format) {
|
|
41
|
+
if (format !== "table") {
|
|
42
|
+
return formatOutput({ success: false, validation: result.validation }, format);
|
|
43
|
+
}
|
|
44
|
+
const lines = ["Validation failed:\n", ...formatChecks(result.validation.checks)];
|
|
45
|
+
for (const w of result.validation.warnings) {
|
|
46
|
+
lines.push(` ${WARN} ${w}`);
|
|
47
|
+
}
|
|
48
|
+
return lines.join("\n");
|
|
49
|
+
}
|
|
50
|
+
function formatPublishOutput(result, format) {
|
|
51
|
+
if (format !== "table") return formatOutput(result, format);
|
|
52
|
+
const upload = result.upload;
|
|
53
|
+
const rollout = upload.status === "inProgress" && "userFraction" in upload ? ` (${Math.round(Number(upload["userFraction"]) * 100)}% rollout)` : "";
|
|
54
|
+
const lines = [
|
|
55
|
+
"Published successfully\n",
|
|
56
|
+
...formatChecks(result.validation.checks)
|
|
57
|
+
];
|
|
58
|
+
for (const w of result.validation.warnings) {
|
|
59
|
+
lines.push(` ${WARN} ${w}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push(` versionCode ${upload.versionCode}`);
|
|
63
|
+
lines.push(` track ${upload.track}`);
|
|
64
|
+
lines.push(` status ${upload.status}${rollout}`);
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
function formatDryRunOutput(result, format) {
|
|
68
|
+
if (format !== "table") return formatOutput(result, format);
|
|
69
|
+
const lines = [
|
|
70
|
+
"Dry run \u2014 no changes made\n",
|
|
71
|
+
...formatChecks(result.validation.checks)
|
|
72
|
+
];
|
|
73
|
+
for (const w of result.validation.warnings) {
|
|
74
|
+
lines.push(` ${WARN} ${w}`);
|
|
75
|
+
}
|
|
76
|
+
const u = result.upload;
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push(` Track ${u.track}`);
|
|
79
|
+
if (u.currentReleases.length === 0) {
|
|
80
|
+
lines.push(` Current (no releases on this track)`);
|
|
81
|
+
} else {
|
|
82
|
+
for (const r of u.currentReleases) {
|
|
83
|
+
const fraction = r.userFraction !== void 0 ? ` (${Math.round(r.userFraction * 100)}%)` : "";
|
|
84
|
+
lines.push(` Current ${r.versionCodes.join(", ")} \xB7 ${r.status}${fraction}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const plannedFraction = u.plannedRelease.userFraction !== void 0 ? ` (${Math.round(u.plannedRelease.userFraction * 100)}%)` : "";
|
|
88
|
+
lines.push(` Would be (new bundle) \xB7 ${u.plannedRelease.status}${plannedFraction}`);
|
|
89
|
+
return lines.join("\n");
|
|
90
|
+
}
|
|
29
91
|
function registerPublishCommand(program) {
|
|
30
92
|
program.command("publish <file>").description("Validate, upload, and release in one step").option("--track <track>", "Target track", "internal").option("--rollout <percent>", "Staged rollout percentage (1-100)").option("--notes <text>", "Release notes (en-US)").option("--notes-dir <dir>", "Read release notes from directory (<dir>/<lang>.txt)").option("--notes-from-git", "Generate release notes from git commit history").option("--since <ref>", "Git ref to start from (tag, SHA) \u2014 used with --notes-from-git").option("--name <name>", "Release name").option("--mapping <file>", "ProGuard/R8 mapping file for deobfuscation").option("--retry-log <path>", "Write retry log entries to file (JSONL)").action(async (file, options) => {
|
|
31
93
|
const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);
|
|
32
94
|
if (noteSources.length > 1) {
|
|
33
|
-
console.error(
|
|
95
|
+
console.error(
|
|
96
|
+
"Error: Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one."
|
|
97
|
+
);
|
|
34
98
|
process.exit(2);
|
|
35
99
|
}
|
|
36
100
|
const config = await loadConfig();
|
|
@@ -55,9 +119,23 @@ function registerPublishCommand(program) {
|
|
|
55
119
|
if (notes) options.notes = notes;
|
|
56
120
|
}
|
|
57
121
|
}
|
|
122
|
+
if (options.rollout !== void 0) {
|
|
123
|
+
const rollout = Number(options.rollout);
|
|
124
|
+
if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {
|
|
125
|
+
console.error(
|
|
126
|
+
`Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`
|
|
127
|
+
);
|
|
128
|
+
process.exit(2);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
58
131
|
if (options.notesFromGit) {
|
|
59
132
|
const gitNotes = await generateNotesFromGit({ since: options.since });
|
|
60
133
|
options.notes = gitNotes.text;
|
|
134
|
+
if (gitNotes.truncated) {
|
|
135
|
+
console.error(
|
|
136
|
+
`${WARN} Release notes truncated to 500 characters (${gitNotes.commitCount} commits from ${gitNotes.since}).`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
61
139
|
}
|
|
62
140
|
let onRetry;
|
|
63
141
|
if (options.retryLog) {
|
|
@@ -82,7 +160,7 @@ function registerPublishCommand(program) {
|
|
|
82
160
|
mappingFile: options.mapping,
|
|
83
161
|
dryRun: true
|
|
84
162
|
});
|
|
85
|
-
console.log(
|
|
163
|
+
console.log(formatDryRunOutput(result, format));
|
|
86
164
|
} catch (error) {
|
|
87
165
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
88
166
|
process.exit(4);
|
|
@@ -91,11 +169,7 @@ function registerPublishCommand(program) {
|
|
|
91
169
|
}
|
|
92
170
|
const auditEntry = createAuditEntry(
|
|
93
171
|
"publish",
|
|
94
|
-
{
|
|
95
|
-
file,
|
|
96
|
-
track: options.track,
|
|
97
|
-
rollout: options.rollout
|
|
98
|
-
},
|
|
172
|
+
{ file, track: options.track, rollout: options.rollout },
|
|
99
173
|
packageName
|
|
100
174
|
);
|
|
101
175
|
try {
|
|
@@ -108,16 +182,12 @@ function registerPublishCommand(program) {
|
|
|
108
182
|
mappingFile: options.mapping
|
|
109
183
|
});
|
|
110
184
|
if (!result.upload) {
|
|
111
|
-
console.
|
|
112
|
-
for (const check of result.validation.checks) {
|
|
113
|
-
const icon = check.passed ? "\u2713" : "\u2717";
|
|
114
|
-
console.error(` ${icon} ${check.name}: ${check.message}`);
|
|
115
|
-
}
|
|
185
|
+
console.log(formatValidationOutput(result, format));
|
|
116
186
|
auditEntry.success = false;
|
|
117
187
|
auditEntry.error = "Validation failed";
|
|
118
188
|
process.exit(1);
|
|
119
189
|
}
|
|
120
|
-
console.log(
|
|
190
|
+
console.log(formatPublishOutput(result, format));
|
|
121
191
|
auditEntry.success = true;
|
|
122
192
|
} catch (error) {
|
|
123
193
|
auditEntry.success = false;
|
|
@@ -134,4 +204,4 @@ function registerPublishCommand(program) {
|
|
|
134
204
|
export {
|
|
135
205
|
registerPublishCommand
|
|
136
206
|
};
|
|
137
|
-
//# sourceMappingURL=publish-
|
|
207
|
+
//# sourceMappingURL=publish-26ZPS7XX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { appendFile } from \"node:fs/promises\";\nimport type { GpcConfig, OutputFormat } from \"@gpc-cli/config\";\nimport type { Command } from \"commander\";\nimport { loadConfig, getCacheDir } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { RetryLogEntry } from \"@gpc-cli/api\";\nimport {\n publish,\n generateNotesFromGit,\n writeAuditLog,\n createAuditEntry,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport type { PublishResult, DryRunPublishResult } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun } from \"../dry-run.js\";\nimport { isInteractive, promptSelect, promptInput } from \"../prompt.js\";\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\n// ---------------------------------------------------------------------------\n// Output formatters\n// ---------------------------------------------------------------------------\n\nfunction formatChecks(checks: { name: string; passed: boolean; message: string }[]): string[] {\n return checks.map((c) => ` ${c.passed ? PASS : FAIL} ${c.message}`);\n}\n\nfunction formatValidationOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") {\n return formatOutput({ success: false, validation: result.validation }, format);\n }\n const lines = [\"Validation failed:\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatPublishOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const upload = result.upload!;\n const rollout =\n upload.status === \"inProgress\" && \"userFraction\" in upload\n ? ` (${Math.round(Number((upload as Record<string, unknown>)[\"userFraction\"]) * 100)}% rollout)`\n : \"\";\n\n const lines = [\n \"Published successfully\\n\",\n ...formatChecks(result.validation.checks),\n ];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n lines.push(\"\");\n lines.push(` versionCode ${upload.versionCode}`);\n lines.push(` track ${upload.track}`);\n lines.push(` status ${upload.status}${rollout}`);\n return lines.join(\"\\n\");\n}\n\nfunction formatDryRunOutput(result: DryRunPublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const lines = [\n \"Dry run — no changes made\\n\",\n ...formatChecks(result.validation.checks),\n ];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n\n const u = result.upload;\n lines.push(\"\");\n lines.push(` Track ${u.track}`);\n\n if (u.currentReleases.length === 0) {\n lines.push(` Current (no releases on this track)`);\n } else {\n for (const r of u.currentReleases) {\n const fraction = r.userFraction !== undefined ? ` (${Math.round(r.userFraction * 100)}%)` : \"\";\n lines.push(` Current ${r.versionCodes.join(\", \")} · ${r.status}${fraction}`);\n }\n }\n const plannedFraction =\n u.plannedRelease.userFraction !== undefined\n ? ` (${Math.round(u.plannedRelease.userFraction * 100)}%)`\n : \"\";\n lines.push(` Would be (new bundle) · ${u.plannedRelease.status}${plannedFraction}`);\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function registerPublishCommand(program: Command): void {\n program\n .command(\"publish <file>\")\n .description(\"Validate, upload, and release in one step\")\n .option(\"--track <track>\", \"Target track\", \"internal\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage (1-100)\")\n .option(\"--notes <text>\", \"Release notes (en-US)\")\n .option(\"--notes-dir <dir>\", \"Read release notes from directory (<dir>/<lang>.txt)\")\n .option(\"--notes-from-git\", \"Generate release notes from git commit history\")\n .option(\"--since <ref>\", \"Git ref to start from (tag, SHA) — used with --notes-from-git\")\n .option(\"--name <name>\", \"Release name\")\n .option(\"--mapping <file>\", \"ProGuard/R8 mapping file for deobfuscation\")\n .option(\"--retry-log <path>\", \"Write retry log entries to file (JSONL)\")\n .action(async (file: string, options) => {\n const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);\n if (noteSources.length > 1) {\n console.error(\n \"Error: Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.\",\n );\n process.exit(2);\n }\n\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n // Interactive mode: prompt for missing options\n if (isInteractive(program)) {\n if (!options.track || options.track === \"internal\") {\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n options.track = await promptSelect(\"Select track:\", tracks, \"internal\");\n }\n\n if (!options.rollout && options.track === \"production\") {\n const rolloutStr = await promptInput(\n \"Staged rollout percentage (1-100, blank for full)\",\n \"100\",\n );\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir && !options.notesFromGit) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n // Rollout range guard\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n console.error(\n `Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n );\n process.exit(2);\n }\n }\n\n // Resolve git-based release notes before calling publish\n if (options.notesFromGit) {\n const gitNotes = await generateNotesFromGit({ since: options.since });\n options.notes = gitNotes.text;\n if (gitNotes.truncated) {\n console.error(\n `${WARN} Release notes truncated to 500 characters (${gitNotes.commitCount} commits from ${gitNotes.since}).`,\n );\n }\n }\n\n let onRetry: ((entry: RetryLogEntry) => void) | undefined;\n if (options.retryLog) {\n onRetry = (entry: RetryLogEntry) => {\n appendFile(options.retryLog, JSON.stringify(entry) + \"\\n\").catch(() => {});\n };\n }\n\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const client = createApiClient({ auth, onRetry });\n\n if (isDryRun(program)) {\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n dryRun: true,\n });\n console.log(formatDryRunOutput(result as DryRunPublishResult, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n return;\n }\n\n const auditEntry = createAuditEntry(\n \"publish\",\n { file, track: options.track, rollout: options.rollout },\n packageName,\n );\n\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n });\n\n if (!result.upload) {\n console.log(formatValidationOutput(result as PublishResult, format));\n auditEntry.success = false;\n auditEntry.error = \"Validation failed\";\n process.exit(1);\n }\n\n console.log(formatPublishOutput(result as PublishResult, format));\n auditEntry.success = true;\n } catch (error) {\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getOutputFormat
|
|
4
|
+
} from "./chunk-ELXAK7GI.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/status.ts
|
|
7
|
+
import { loadConfig } from "@gpc-cli/config";
|
|
8
|
+
import { resolveAuth } from "@gpc-cli/auth";
|
|
9
|
+
import { createApiClient, createReportingClient } from "@gpc-cli/api";
|
|
10
|
+
import {
|
|
11
|
+
getAppStatus,
|
|
12
|
+
formatStatusTable,
|
|
13
|
+
loadStatusCache,
|
|
14
|
+
saveStatusCache,
|
|
15
|
+
statusHasBreach,
|
|
16
|
+
formatOutput
|
|
17
|
+
} from "@gpc-cli/core";
|
|
18
|
+
function registerStatusCommand(program) {
|
|
19
|
+
program.command("status").description("Unified app health snapshot: releases, vitals, and reviews").option("--days <n>", "Vitals window in days", parseInt, 7).option("--cached", "Use last fetched data, skip API calls").option("--refresh", "Force live fetch, ignore cache TTL").option("--ttl <seconds>", "Cache TTL in seconds", parseInt, 3600).action(async (opts) => {
|
|
20
|
+
const config = await loadConfig();
|
|
21
|
+
const packageName = program.opts()["app"] || config.app;
|
|
22
|
+
if (!packageName) {
|
|
23
|
+
console.error(
|
|
24
|
+
"Error: No package name. Use --app <package> or gpc config set app <package>"
|
|
25
|
+
);
|
|
26
|
+
process.exit(2);
|
|
27
|
+
}
|
|
28
|
+
const format = getOutputFormat(program, config);
|
|
29
|
+
try {
|
|
30
|
+
if (opts.cached) {
|
|
31
|
+
const cached = await loadStatusCache(packageName, opts.ttl);
|
|
32
|
+
if (!cached) {
|
|
33
|
+
console.error(
|
|
34
|
+
"Error: No cached status found. Run without --cached to fetch live data."
|
|
35
|
+
);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
if (format === "json") {
|
|
39
|
+
console.log(formatOutput(cached, format));
|
|
40
|
+
} else {
|
|
41
|
+
console.log(formatStatusTable(cached));
|
|
42
|
+
}
|
|
43
|
+
if (statusHasBreach(cached)) process.exit(6);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!opts.refresh) {
|
|
47
|
+
const cached = await loadStatusCache(packageName, opts.ttl);
|
|
48
|
+
if (cached) {
|
|
49
|
+
if (format === "json") {
|
|
50
|
+
console.log(formatOutput(cached, format));
|
|
51
|
+
} else {
|
|
52
|
+
console.log(formatStatusTable(cached));
|
|
53
|
+
}
|
|
54
|
+
if (statusHasBreach(cached)) process.exit(6);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
|
|
59
|
+
const client = createApiClient({ auth });
|
|
60
|
+
const reporting = createReportingClient({ auth });
|
|
61
|
+
const vitalThresholds = config["vitals"] ? (() => {
|
|
62
|
+
const vitals = config["vitals"];
|
|
63
|
+
const t = vitals["thresholds"];
|
|
64
|
+
const toThreshold = (v) => {
|
|
65
|
+
if (v === void 0 || v === null) return void 0;
|
|
66
|
+
const n = Number(v);
|
|
67
|
+
return isNaN(n) ? void 0 : n;
|
|
68
|
+
};
|
|
69
|
+
return t ? {
|
|
70
|
+
crashRate: toThreshold(t["crashRate"]),
|
|
71
|
+
anrRate: toThreshold(t["anrRate"]),
|
|
72
|
+
slowStartRate: toThreshold(t["slowStartRate"]),
|
|
73
|
+
slowRenderingRate: toThreshold(t["slowRenderingRate"])
|
|
74
|
+
} : void 0;
|
|
75
|
+
})() : void 0;
|
|
76
|
+
const status = await getAppStatus(client, reporting, packageName, {
|
|
77
|
+
days: opts.days,
|
|
78
|
+
vitalThresholds
|
|
79
|
+
});
|
|
80
|
+
await saveStatusCache(packageName, status, opts.ttl);
|
|
81
|
+
if (format === "json") {
|
|
82
|
+
console.log(formatOutput(status, format));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(formatStatusTable(status));
|
|
85
|
+
}
|
|
86
|
+
if (statusHasBreach(status)) process.exit(6);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
process.exit(4);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
registerStatusCommand
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=status-C222ZKHH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient, createReportingClient } from \"@gpc-cli/api\";\nimport {\n getAppStatus,\n formatStatusTable,\n loadStatusCache,\n saveStatusCache,\n statusHasBreach,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Unified app health snapshot: releases, vitals, and reviews\")\n .option(\"--days <n>\", \"Vitals window in days\", parseInt, 7)\n .option(\"--cached\", \"Use last fetched data, skip API calls\")\n .option(\"--refresh\", \"Force live fetch, ignore cache TTL\")\n .option(\"--ttl <seconds>\", \"Cache TTL in seconds\", parseInt, 3600)\n .action(async (opts: { days: number; cached?: boolean; refresh?: boolean; ttl: number }) => {\n const config = await loadConfig();\n const packageName = program.opts()[\"app\"] || config.app;\n if (!packageName) {\n console.error(\n \"Error: No package name. Use --app <package> or gpc config set app <package>\",\n );\n process.exit(2);\n }\n\n const format = getOutputFormat(program, config);\n\n try {\n // --cached: read from cache, skip API\n if (opts.cached) {\n const cached = await loadStatusCache(packageName, opts.ttl);\n if (!cached) {\n console.error(\n \"Error: No cached status found. Run without --cached to fetch live data.\",\n );\n process.exit(1);\n }\n if (format === \"json\") {\n console.log(formatOutput(cached, format));\n } else {\n console.log(formatStatusTable(cached));\n }\n if (statusHasBreach(cached)) process.exit(6);\n return;\n }\n\n // Check cache unless --refresh\n if (!opts.refresh) {\n const cached = await loadStatusCache(packageName, opts.ttl);\n if (cached) {\n if (format === \"json\") {\n console.log(formatOutput(cached, format));\n } else {\n console.log(formatStatusTable(cached));\n }\n if (statusHasBreach(cached)) process.exit(6);\n return;\n }\n }\n\n // Live fetch\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n const client = createApiClient({ auth });\n const reporting = createReportingClient({ auth });\n\n const vitalThresholds = (config as Record<string, unknown>)[\"vitals\"]\n ? (() => {\n const vitals = (config as Record<string, unknown>)[\"vitals\"] as Record<\n string,\n unknown\n >;\n const t = vitals[\"thresholds\"] as Record<string, unknown> | undefined;\n const toThreshold = (v: unknown): number | undefined => {\n if (v === undefined || v === null) return undefined;\n const n = Number(v);\n return isNaN(n) ? undefined : n;\n };\n return t\n ? {\n crashRate: toThreshold(t[\"crashRate\"]),\n anrRate: toThreshold(t[\"anrRate\"]),\n slowStartRate: toThreshold(t[\"slowStartRate\"]),\n slowRenderingRate: toThreshold(t[\"slowRenderingRate\"]),\n }\n : undefined;\n })()\n : undefined;\n\n const status = await getAppStatus(client, reporting, packageName, {\n days: opts.days,\n vitalThresholds,\n });\n\n await saveStatusCache(packageName, status, opts.ttl);\n\n if (format === \"json\") {\n console.log(formatOutput(status, format));\n } else {\n console.log(formatStatusTable(status));\n }\n\n if (statusHasBreach(status)) process.exit(6);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gpc-cli/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.24",
|
|
4
4
|
"description": "The complete Google Play CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"commander": "^14.0.3",
|
|
22
|
-
"@gpc-cli/api": "1.0.16",
|
|
23
|
-
"@gpc-cli/auth": "^0.9.9",
|
|
24
22
|
"@gpc-cli/config": "0.9.8",
|
|
25
|
-
"@gpc-cli/
|
|
26
|
-
"@gpc-cli/plugin-sdk": "0.9.6"
|
|
23
|
+
"@gpc-cli/auth": "^0.9.9",
|
|
24
|
+
"@gpc-cli/plugin-sdk": "0.9.6",
|
|
25
|
+
"@gpc-cli/api": "1.0.16",
|
|
26
|
+
"@gpc-cli/core": "0.9.22"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"google-play",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/bundle.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport {\n analyzeBundle,\n compareBundles,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nfunction formatSize(bytes: number): string {\n const abs = Math.abs(bytes);\n const sign = bytes < 0 ? \"-\" : \"\";\n if (abs < 1024) return `${sign}${abs} B`;\n if (abs < 1024 * 1024) return `${sign}${(abs / 1024).toFixed(1)} KB`;\n return `${sign}${(abs / (1024 * 1024)).toFixed(2)} MB`;\n}\n\nfunction formatDelta(delta: number): string {\n const prefix = delta > 0 ? \"+\" : \"\";\n return `${prefix}${formatSize(delta)}`;\n}\n\nexport function registerBundleCommands(program: Command): void {\n const bundle = program.command(\"bundle\").description(\"Analyze app bundles and APKs\");\n\n bundle\n .command(\"analyze <file>\")\n .description(\"Analyze size breakdown of an AAB or APK\")\n .option(\"--threshold <mb>\", \"Fail if compressed size exceeds threshold (MB)\", parseFloat)\n .action(async (file: string, opts: { threshold?: number }) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const analysis = await analyzeBundle(file);\n\n if (format === \"json\") {\n console.log(formatOutput(analysis, format));\n } else {\n console.log(`\\nFile: ${analysis.filePath}`);\n console.log(`Type: ${analysis.fileType.toUpperCase()}`);\n console.log(`Total compressed: ${formatSize(analysis.totalCompressed)}`);\n console.log(`Total uncompressed: ${formatSize(analysis.totalUncompressed)}`);\n console.log(`Entries: ${analysis.entryCount}\\n`);\n\n // Modules table\n const moduleRows = analysis.modules.map((m) => ({\n module: m.name,\n compressed: formatSize(m.compressedSize),\n uncompressed: formatSize(m.uncompressedSize),\n entries: m.entries,\n }));\n console.log(\"Modules:\");\n console.log(formatOutput(moduleRows, \"table\"));\n\n // Categories table\n const categoryRows = analysis.categories.map((c) => ({\n category: c.name,\n compressed: formatSize(c.compressedSize),\n uncompressed: formatSize(c.uncompressedSize),\n entries: c.entries,\n }));\n console.log(\"\\nCategories:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n\n // Threshold check\n if (opts.threshold !== undefined) {\n const thresholdBytes = opts.threshold * 1024 * 1024;\n if (analysis.totalCompressed > thresholdBytes) {\n console.error(\n `\\nThreshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold} MB`,\n );\n process.exit(6);\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n bundle\n .command(\"compare <file1> <file2>\")\n .description(\"Compare size differences between two bundles or APKs\")\n .action(async (file1: string, file2: string) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const [before, after] = await Promise.all([\n analyzeBundle(file1),\n analyzeBundle(file2),\n ]);\n const comparison = compareBundles(before, after);\n\n if (format === \"json\") {\n console.log(formatOutput(comparison, format));\n } else {\n const sign = comparison.sizeDelta >= 0 ? \"+\" : \"\";\n console.log(`\\nBefore: ${comparison.before.path} (${formatSize(comparison.before.totalCompressed)})`);\n console.log(`After: ${comparison.after.path} (${formatSize(comparison.after.totalCompressed)})`);\n console.log(`Delta: ${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)\\n`);\n\n // Module deltas\n const moduleRows = comparison.moduleDeltas\n .filter((m) => m.delta !== 0)\n .map((m) => ({\n module: m.module,\n before: formatSize(m.before),\n after: formatSize(m.after),\n delta: formatDelta(m.delta),\n }));\n if (moduleRows.length > 0) {\n console.log(\"Module changes:\");\n console.log(formatOutput(moduleRows, \"table\"));\n }\n\n // Category deltas\n const categoryRows = comparison.categoryDeltas\n .filter((c) => c.delta !== 0)\n .map((c) => ({\n category: c.category,\n before: formatSize(c.before),\n after: formatSize(c.after),\n delta: formatDelta(c.delta),\n }));\n if (categoryRows.length > 0) {\n console.log(\"\\nCategory changes:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n\nasync function getConfig() {\n const { loadConfig } = await import(\"@gpc-cli/config\");\n return loadConfig();\n}\n"],"mappings":"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","names":[]}
|
package/dist/config-R5U7GV56.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getOutputFormat
|
|
4
|
-
} from "./chunk-ELXAK7GI.js";
|
|
5
|
-
import {
|
|
6
|
-
isInteractive,
|
|
7
|
-
promptInput,
|
|
8
|
-
promptSelect
|
|
9
|
-
} from "./chunk-NV75I5VP.js";
|
|
10
|
-
|
|
11
|
-
// src/commands/config.ts
|
|
12
|
-
import { loadConfig, setConfigValue, getUserConfigPath, initConfig } from "@gpc-cli/config";
|
|
13
|
-
import { formatOutput, writeAuditLog, createAuditEntry } from "@gpc-cli/core";
|
|
14
|
-
function registerConfigCommands(program) {
|
|
15
|
-
const config = program.command("config").description("Manage configuration");
|
|
16
|
-
config.command("init").description("Create a configuration file").option("--global", "Create in user config directory (~/.config/gpc/)").action(async (_options) => {
|
|
17
|
-
const initialConfig = {};
|
|
18
|
-
if (isInteractive(program)) {
|
|
19
|
-
const app = await promptInput("Default package name (e.g. com.example.app, blank to skip)");
|
|
20
|
-
if (app) initialConfig["app"] = app;
|
|
21
|
-
const output = await promptSelect(
|
|
22
|
-
"Default output format:",
|
|
23
|
-
["table", "json", "yaml", "markdown"],
|
|
24
|
-
"table"
|
|
25
|
-
);
|
|
26
|
-
if (output !== "table") initialConfig["output"] = output;
|
|
27
|
-
const sa = await promptInput("Service account JSON path (blank to skip)");
|
|
28
|
-
if (sa) initialConfig["auth"] = { serviceAccount: sa };
|
|
29
|
-
}
|
|
30
|
-
const path = await initConfig(initialConfig);
|
|
31
|
-
console.log(`Configuration file created at: ${path}`);
|
|
32
|
-
writeAuditLog(createAuditEntry("config init", { path })).catch(() => {
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
config.command("show").description("Display resolved configuration").action(async () => {
|
|
36
|
-
const resolved = await loadConfig();
|
|
37
|
-
const format = getOutputFormat(program, resolved);
|
|
38
|
-
console.log(formatOutput(resolved, format));
|
|
39
|
-
});
|
|
40
|
-
config.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
|
|
41
|
-
await setConfigValue(key, value);
|
|
42
|
-
console.log(`Set ${key} = ${value}`);
|
|
43
|
-
});
|
|
44
|
-
config.command("path").description("Show configuration file path").action(() => {
|
|
45
|
-
console.log(getUserConfigPath());
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
export {
|
|
49
|
-
registerConfigCommands
|
|
50
|
-
};
|
|
51
|
-
//# sourceMappingURL=config-R5U7GV56.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig, setConfigValue, getUserConfigPath, initConfig } from \"@gpc-cli/config\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { formatOutput, writeAuditLog, createAuditEntry } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isInteractive, promptInput, promptSelect } from \"../prompt.js\";\n\nexport function registerConfigCommands(program: Command): void {\n const config = program.command(\"config\").description(\"Manage configuration\");\n\n config\n .command(\"init\")\n .description(\"Create a configuration file\")\n .option(\"--global\", \"Create in user config directory (~/.config/gpc/)\")\n .action(async (_options: { global?: boolean }) => {\n const initialConfig: Record<string, unknown> = {};\n\n if (isInteractive(program)) {\n const app = await promptInput(\"Default package name (e.g. com.example.app, blank to skip)\");\n if (app) initialConfig[\"app\"] = app;\n\n const output = await promptSelect(\n \"Default output format:\",\n [\"table\", \"json\", \"yaml\", \"markdown\"],\n \"table\",\n );\n if (output !== \"table\") initialConfig[\"output\"] = output;\n\n const sa = await promptInput(\"Service account JSON path (blank to skip)\");\n if (sa) initialConfig[\"auth\"] = { serviceAccount: sa };\n }\n\n const path = await initConfig(initialConfig as GpcConfig);\n console.log(`Configuration file created at: ${path}`);\n writeAuditLog(createAuditEntry(\"config init\", { path })).catch(() => {});\n });\n\n config\n .command(\"show\")\n .description(\"Display resolved configuration\")\n .action(async () => {\n const resolved = await loadConfig();\n const format = getOutputFormat(program, resolved);\n console.log(formatOutput(resolved, format));\n });\n\n config\n .command(\"set <key> <value>\")\n .description(\"Set a configuration value\")\n .action(async (key: string, value: string) => {\n await setConfigValue(key, value);\n console.log(`Set ${key} = ${value}`);\n });\n\n config\n .command(\"path\")\n .description(\"Show configuration file path\")\n .action(() => {\n console.log(getUserConfigPath());\n });\n}\n"],"mappings":";;;;;;;;;;;AACA,SAAS,YAAY,gBAAgB,mBAAmB,kBAAkB;AAE1E,SAAS,cAAc,eAAe,wBAAwB;AAIvD,SAAS,uBAAuB,SAAwB;AAC7D,QAAM,SAAS,QAAQ,QAAQ,QAAQ,EAAE,YAAY,sBAAsB;AAE3E,SACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,YAAY,kDAAkD,EACrE,OAAO,OAAO,aAAmC;AAChD,UAAM,gBAAyC,CAAC;AAEhD,QAAI,cAAc,OAAO,GAAG;AAC1B,YAAM,MAAM,MAAM,YAAY,4DAA4D;AAC1F,UAAI,IAAK,eAAc,KAAK,IAAI;AAEhC,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,CAAC,SAAS,QAAQ,QAAQ,UAAU;AAAA,QACpC;AAAA,MACF;AACA,UAAI,WAAW,QAAS,eAAc,QAAQ,IAAI;AAElD,YAAM,KAAK,MAAM,YAAY,2CAA2C;AACxE,UAAI,GAAI,eAAc,MAAM,IAAI,EAAE,gBAAgB,GAAG;AAAA,IACvD;AAEA,UAAM,OAAO,MAAM,WAAW,aAA0B;AACxD,YAAQ,IAAI,kCAAkC,IAAI,EAAE;AACpD,kBAAc,iBAAiB,eAAe,EAAE,KAAK,CAAC,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzE,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,UAAM,WAAW,MAAM,WAAW;AAClC,UAAM,SAAS,gBAAgB,SAAS,QAAQ;AAChD,YAAQ,IAAI,aAAa,UAAU,MAAM,CAAC;AAAA,EAC5C,CAAC;AAEH,SACG,QAAQ,mBAAmB,EAC3B,YAAY,2BAA2B,EACvC,OAAO,OAAO,KAAa,UAAkB;AAC5C,UAAM,eAAe,KAAK,KAAK;AAC/B,YAAQ,IAAI,OAAO,GAAG,MAAM,KAAK,EAAE;AAAA,EACrC,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,8BAA8B,EAC1C,OAAO,MAAM;AACZ,YAAQ,IAAI,kBAAkB,CAAC;AAAA,EACjC,CAAC;AACL;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/doctor.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig, getCacheDir, getConfigDir } from \"@gpc-cli/config\";\nimport { resolveAuth, AuthError } from \"@gpc-cli/auth\";\nimport { existsSync, accessSync, statSync, constants } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { lookup } from \"node:dns/promises\";\n\ninterface CheckResult {\n name: string;\n status: \"pass\" | \"fail\" | \"warn\" | \"info\";\n message: string;\n suggestion?: string;\n}\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\nconst INFO = \"-\";\n\nfunction icon(status: CheckResult[\"status\"]): string {\n switch (status) {\n case \"pass\":\n return PASS;\n case \"fail\":\n return FAIL;\n case \"warn\":\n return WARN;\n case \"info\":\n return INFO;\n }\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Verify setup and connectivity\")\n .option(\"--json\", \"Output results as JSON\")\n .action(async (opts: { json?: boolean }) => {\n const results: CheckResult[] = [];\n const jsonMode = opts.json ?? false;\n\n // 1. Node.js version\n const nodeVersion = process.versions.node;\n const major = parseInt(nodeVersion.split(\".\")[0] ?? \"0\", 10);\n results.push(\n major >= 20\n ? { name: \"node\", status: \"pass\", message: `Node.js ${nodeVersion}` }\n : {\n name: \"node\",\n status: \"fail\",\n message: `Node.js ${nodeVersion} (requires >=20)`,\n suggestion: \"Upgrade Node.js to v20 or later: https://nodejs.org\",\n },\n );\n\n // 2. Config file\n let config;\n try {\n config = await loadConfig();\n results.push({ name: \"config\", status: \"pass\", message: \"Configuration loaded\" });\n if (config.app) {\n results.push({\n name: \"default-app\",\n status: \"pass\",\n message: `Default app: ${config.app}`,\n });\n } else {\n results.push({\n name: \"default-app\",\n status: \"info\",\n message: \"No default app configured\",\n suggestion: \"Use --app flag or run: gpc config set app <package>\",\n });\n }\n } catch {\n results.push({\n name: \"config\",\n status: \"fail\",\n message: \"Configuration error\",\n suggestion: \"Check your .gpcrc.json or config file for syntax errors\",\n });\n }\n\n // 3. Config directory permissions\n const configDir = getConfigDir();\n try {\n if (existsSync(configDir)) {\n accessSync(configDir, constants.R_OK | constants.W_OK);\n results.push({\n name: \"config-dir\",\n status: \"pass\",\n message: `Config directory: ${configDir}`,\n });\n } else {\n results.push({\n name: \"config-dir\",\n status: \"info\",\n message: `Config directory does not exist yet: ${configDir}`,\n });\n }\n } catch {\n results.push({\n name: \"config-dir\",\n status: \"warn\",\n message: `Config directory not writable: ${configDir}`,\n suggestion: `Fix permissions: chmod 755 ${configDir}`,\n });\n }\n\n // 4. Cache directory permissions\n const cacheDir = getCacheDir();\n try {\n if (existsSync(cacheDir)) {\n accessSync(cacheDir, constants.R_OK | constants.W_OK);\n results.push({\n name: \"cache-dir\",\n status: \"pass\",\n message: `Cache directory: ${cacheDir}`,\n });\n } else {\n results.push({\n name: \"cache-dir\",\n status: \"info\",\n message: `Cache directory does not exist yet: ${cacheDir}`,\n });\n }\n } catch {\n results.push({\n name: \"cache-dir\",\n status: \"warn\",\n message: `Cache directory not writable: ${cacheDir}`,\n suggestion: `Fix permissions: chmod 700 ${cacheDir}`,\n });\n }\n\n // 5. Service account file existence\n if (config?.auth?.serviceAccount) {\n const saValue = config.auth.serviceAccount;\n const looksLikePath = !saValue.trim().startsWith(\"{\");\n if (looksLikePath) {\n const saPath = resolve(saValue);\n if (existsSync(saPath)) {\n try {\n accessSync(saPath, constants.R_OK);\n results.push({\n name: \"service-account-file\",\n status: \"pass\",\n message: `Service account file: ${saPath}`,\n });\n } catch {\n results.push({\n name: \"service-account-file\",\n status: \"fail\",\n message: `Service account file not readable: ${saPath}`,\n suggestion: `Fix permissions: chmod 600 ${saPath}`,\n });\n }\n\n // 5b. SA key file permissions (Unix only)\n if (process.platform !== \"win32\") {\n try {\n const mode = statSync(saPath).mode;\n const groupRead = (mode & 0o040) !== 0;\n const worldRead = (mode & 0o004) !== 0;\n if (groupRead || worldRead) {\n results.push({\n name: \"service-account-permissions\",\n status: \"warn\",\n message: `Service account file is group/world-readable (mode: ${(mode & 0o777).toString(8)})`,\n suggestion: `Restrict permissions: chmod 600 ${saPath}`,\n });\n } else {\n results.push({\n name: \"service-account-permissions\",\n status: \"pass\",\n message: `Service account file permissions OK (mode: ${(mode & 0o777).toString(8)})`,\n });\n }\n } catch {\n // stat failed — skip permission check\n }\n }\n } else {\n results.push({\n name: \"service-account-file\",\n status: \"fail\",\n message: `Service account file not found: ${saPath}`,\n suggestion: \"Check the path in your config or GPC_SERVICE_ACCOUNT env var\",\n });\n }\n }\n }\n\n // 5c. Profile validation\n const gpcProfile = process.env[\"GPC_PROFILE\"];\n if (gpcProfile && config) {\n if (config.profiles && gpcProfile in config.profiles) {\n results.push({\n name: \"profile\",\n status: \"pass\",\n message: `Profile \"${gpcProfile}\" found`,\n });\n } else {\n const available = config.profiles ? Object.keys(config.profiles).join(\", \") : \"\";\n results.push({\n name: \"profile\",\n status: \"fail\",\n message: `Profile \"${gpcProfile}\" not found`,\n suggestion: available\n ? `Available profiles: ${available}. Create with: gpc auth login --profile ${gpcProfile}`\n : `No profiles defined. Create one with: gpc auth login --profile ${gpcProfile}`,\n });\n }\n }\n\n // 6. Proxy configuration\n const proxyUrl =\n process.env[\"HTTPS_PROXY\"] ||\n process.env[\"https_proxy\"] ||\n process.env[\"HTTP_PROXY\"] ||\n process.env[\"http_proxy\"];\n if (proxyUrl) {\n try {\n new URL(proxyUrl);\n results.push({\n name: \"proxy\",\n status: \"pass\",\n message: `Proxy configured: ${proxyUrl}`,\n });\n } catch {\n results.push({\n name: \"proxy\",\n status: \"warn\",\n message: `Invalid proxy URL: ${proxyUrl}`,\n suggestion: \"Set HTTPS_PROXY to a valid URL (e.g., http://proxy.example.com:8080)\",\n });\n }\n }\n\n // 7. CA certificate\n const caCert = process.env[\"GPC_CA_CERT\"] || process.env[\"NODE_EXTRA_CA_CERTS\"];\n if (caCert) {\n if (existsSync(caCert)) {\n results.push({\n name: \"ca-cert\",\n status: \"pass\",\n message: `CA certificate: ${caCert}`,\n });\n } else {\n results.push({\n name: \"ca-cert\",\n status: \"warn\",\n message: `CA certificate file not found: ${caCert}`,\n suggestion: \"Check that GPC_CA_CERT points to an existing PEM file\",\n });\n }\n }\n\n // 8. DNS resolution\n try {\n await lookup(\"androidpublisher.googleapis.com\");\n results.push({\n name: \"dns\",\n status: \"pass\",\n message: \"DNS resolution: androidpublisher.googleapis.com\",\n });\n } catch {\n results.push({\n name: \"dns\",\n status: \"fail\",\n message: \"Cannot resolve androidpublisher.googleapis.com\",\n suggestion: \"Check your DNS settings and network connection\",\n });\n }\n\n // 9. Authentication + API connectivity\n try {\n const authConfig = config ?? (await loadConfig());\n const client = await resolveAuth({\n serviceAccountPath: authConfig.auth?.serviceAccount,\n });\n results.push({\n name: \"auth\",\n status: \"pass\",\n message: `Authenticated as ${client.getClientEmail()}`,\n });\n\n await client.getAccessToken();\n results.push({\n name: \"api-connectivity\",\n status: \"pass\",\n message: \"API connectivity verified\",\n });\n } catch (error) {\n if (error instanceof AuthError) {\n results.push({\n name: \"auth\",\n status: \"fail\",\n message: `Authentication: ${error.message}`,\n suggestion: error.suggestion,\n });\n } else {\n results.push({\n name: \"api-connectivity\",\n status: \"fail\",\n message: \"API connectivity failed\",\n suggestion: \"Check your network connection and credentials\",\n });\n }\n }\n\n // Output\n if (jsonMode) {\n const errors = results.filter((r) => r.status === \"fail\").length;\n const warnings = results.filter((r) => r.status === \"warn\").length;\n console.log(\n JSON.stringify(\n {\n success: errors === 0,\n errors,\n warnings,\n checks: results,\n },\n null,\n 2,\n ),\n );\n if (errors > 0) process.exit(1);\n return;\n }\n\n console.log(\"GPC Doctor\\n\");\n for (const r of results) {\n console.log(` ${icon(r.status)} ${r.message}`);\n if (r.suggestion && r.status !== \"pass\") {\n console.log(` ${r.suggestion}`);\n }\n }\n\n const errors = results.filter((r) => r.status === \"fail\").length;\n const warnings = results.filter((r) => r.status === \"warn\").length;\n\n console.log(\"\");\n if (errors > 0) {\n console.log(\"Some checks failed. Fix the issues above and run again.\");\n process.exit(1);\n } else if (warnings > 0) {\n console.log(\"All checks passed with warnings.\");\n } else {\n console.log(\"All checks passed!\");\n }\n });\n}\n"],"mappings":"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","names":["errors","warnings"]}
|