187 lines
5.4 KiB
TypeScript
187 lines
5.4 KiB
TypeScript
import { BaseFormatter } from "../classes.baseformatter.js";
|
|
import type { IPlannedChange } from "../interfaces.format.js";
|
|
import * as plugins from "../mod.plugins.js";
|
|
import { logger, logVerbose } from "../../gitzone.logging.js";
|
|
|
|
/**
|
|
* Migrates .smartconfig.json from old namespace keys to new package-scoped keys
|
|
*/
|
|
const migrateNamespaceKeys = (smartconfigJson: any): boolean => {
|
|
let migrated = false;
|
|
const migrations = [
|
|
{ oldKey: "gitzone", newKey: "@git.zone/cli" },
|
|
{ oldKey: "tsdoc", newKey: "@git.zone/tsdoc" },
|
|
{ oldKey: "npmdocker", newKey: "@git.zone/tsdocker" },
|
|
{ oldKey: "npmci", newKey: "@ship.zone/szci" },
|
|
{ oldKey: "szci", newKey: "@ship.zone/szci" },
|
|
];
|
|
for (const { oldKey, newKey } of migrations) {
|
|
if (smartconfigJson[oldKey]) {
|
|
if (!smartconfigJson[newKey]) {
|
|
smartconfigJson[newKey] = smartconfigJson[oldKey];
|
|
} else {
|
|
smartconfigJson[newKey] = {
|
|
...smartconfigJson[oldKey],
|
|
...smartconfigJson[newKey],
|
|
};
|
|
}
|
|
delete smartconfigJson[oldKey];
|
|
migrated = true;
|
|
}
|
|
}
|
|
return migrated;
|
|
};
|
|
|
|
/**
|
|
* Migrates npmAccessLevel from @ship.zone/szci to @git.zone/cli.release.accessLevel
|
|
*/
|
|
const migrateAccessLevel = (smartconfigJson: any): boolean => {
|
|
const szciConfig = smartconfigJson["@ship.zone/szci"];
|
|
|
|
if (!szciConfig?.npmAccessLevel) {
|
|
return false;
|
|
}
|
|
|
|
const gitzoneConfig = smartconfigJson["@git.zone/cli"] || {};
|
|
if (gitzoneConfig?.release?.accessLevel) {
|
|
delete szciConfig.npmAccessLevel;
|
|
return true;
|
|
}
|
|
|
|
if (!smartconfigJson["@git.zone/cli"]) {
|
|
smartconfigJson["@git.zone/cli"] = {};
|
|
}
|
|
if (!smartconfigJson["@git.zone/cli"].release) {
|
|
smartconfigJson["@git.zone/cli"].release = {};
|
|
}
|
|
|
|
smartconfigJson["@git.zone/cli"].release.accessLevel =
|
|
szciConfig.npmAccessLevel;
|
|
delete szciConfig.npmAccessLevel;
|
|
|
|
return true;
|
|
};
|
|
|
|
const CONFIG_FILE = ".smartconfig.json";
|
|
|
|
export class SmartconfigFormatter extends BaseFormatter {
|
|
get name(): string {
|
|
return "smartconfig";
|
|
}
|
|
|
|
async analyze(): Promise<IPlannedChange[]> {
|
|
const changes: IPlannedChange[] = [];
|
|
|
|
// File rename (npmextra.json/smartconfig.json → .smartconfig.json)
|
|
// is handled by the orchestrator before analysis.
|
|
// This formatter only operates on .smartconfig.json.
|
|
const exists = await plugins.smartfs.file(CONFIG_FILE).exists();
|
|
if (!exists) {
|
|
logVerbose(".smartconfig.json does not exist, skipping");
|
|
return changes;
|
|
}
|
|
|
|
const currentContent = (await plugins.smartfs
|
|
.file(CONFIG_FILE)
|
|
.encoding("utf8")
|
|
.read()) as string;
|
|
|
|
const smartconfigJson = JSON.parse(currentContent);
|
|
|
|
// Apply key migrations
|
|
migrateNamespaceKeys(smartconfigJson);
|
|
migrateAccessLevel(smartconfigJson);
|
|
|
|
// Ensure namespaces exist
|
|
if (!smartconfigJson["@git.zone/cli"]) {
|
|
smartconfigJson["@git.zone/cli"] = {};
|
|
}
|
|
if (!smartconfigJson["@ship.zone/szci"]) {
|
|
smartconfigJson["@ship.zone/szci"] = {};
|
|
}
|
|
|
|
const newContent = JSON.stringify(smartconfigJson, null, 2);
|
|
|
|
if (newContent !== currentContent) {
|
|
changes.push({
|
|
type: "modify",
|
|
path: CONFIG_FILE,
|
|
module: this.name,
|
|
description: "Migrate and format .smartconfig.json",
|
|
content: newContent,
|
|
});
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
async applyChange(change: IPlannedChange): Promise<void> {
|
|
if (change.type !== "modify" || !change.content) return;
|
|
|
|
const smartconfigJson = JSON.parse(change.content);
|
|
|
|
// Check for missing required module information
|
|
const expectedRepoInformation: string[] = [
|
|
"projectType",
|
|
"module.githost",
|
|
"module.gitscope",
|
|
"module.gitrepo",
|
|
"module.description",
|
|
"module.npmPackagename",
|
|
"module.license",
|
|
];
|
|
|
|
const interactInstance = new plugins.smartinteract.SmartInteract();
|
|
const missingRepoInformation = expectedRepoInformation.filter(
|
|
(expectedRepoInformationItem) => {
|
|
return !plugins.smartobject.smartGet(
|
|
smartconfigJson["@git.zone/cli"],
|
|
expectedRepoInformationItem,
|
|
);
|
|
},
|
|
);
|
|
|
|
if (missingRepoInformation.length > 0 && !this.context.isInteractive()) {
|
|
throw new Error(
|
|
`Missing required .smartconfig.json fields: ${missingRepoInformation.join(", ")}`,
|
|
);
|
|
}
|
|
|
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
if (
|
|
!plugins.smartobject.smartGet(
|
|
smartconfigJson["@git.zone/cli"],
|
|
expectedRepoInformationItem,
|
|
)
|
|
) {
|
|
interactInstance.addQuestions([
|
|
{
|
|
message: `What is the value of ${expectedRepoInformationItem}`,
|
|
name: expectedRepoInformationItem,
|
|
type: "input",
|
|
default: "undefined variable",
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const answerbucket = await interactInstance.runQueue();
|
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
const cliProvidedValue = answerbucket.getAnswerFor(
|
|
expectedRepoInformationItem,
|
|
);
|
|
if (cliProvidedValue) {
|
|
plugins.smartobject.smartAdd(
|
|
smartconfigJson["@git.zone/cli"],
|
|
expectedRepoInformationItem,
|
|
cliProvidedValue,
|
|
);
|
|
}
|
|
}
|
|
|
|
const finalContent = JSON.stringify(smartconfigJson, null, 2);
|
|
await this.modifyFile(change.path, finalContent);
|
|
logger.log("info", "Updated .smartconfig.json");
|
|
}
|
|
}
|