@git.zone/cli 2.14.2 → 2.15.0
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/.smartconfig.json +36 -11
- package/assets/templates/service/npmextra.json +8 -2
- package/assets/templates/smartconfig/_smartconfig.json +5 -1
- package/assets/templates/website/npmextra.json +8 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/gitzone.cli.js +8 -1
- package/dist_ts/helpers.changelog.d.ts +16 -0
- package/dist_ts/helpers.changelog.js +114 -0
- package/dist_ts/helpers.smartconfigmigrations.d.ts +7 -0
- package/dist_ts/helpers.smartconfigmigrations.js +161 -0
- package/dist_ts/helpers.workflow.d.ts +97 -0
- package/dist_ts/helpers.workflow.js +258 -0
- package/dist_ts/mod_commit/index.js +190 -303
- package/dist_ts/mod_commit/mod.helpers.d.ts +23 -0
- package/dist_ts/mod_commit/mod.helpers.js +22 -15
- package/dist_ts/mod_commit/mod.ui.d.ts +1 -1
- package/dist_ts/mod_commit/mod.ui.js +7 -2
- package/dist_ts/mod_config/classes.commitconfig.d.ts +6 -0
- package/dist_ts/mod_config/classes.commitconfig.js +28 -4
- package/dist_ts/mod_config/classes.releaseconfig.js +13 -7
- package/dist_ts/mod_config/index.js +77 -29
- package/dist_ts/mod_format/formatters/smartconfig.formatter.js +3 -57
- package/dist_ts/mod_release/index.d.ts +3 -0
- package/dist_ts/mod_release/index.js +299 -0
- package/dist_ts/mod_release/mod.plugins.d.ts +3 -0
- package/dist_ts/mod_release/mod.plugins.js +4 -0
- package/dist_ts/mod_standard/index.js +16 -3
- package/license +2 -2
- package/package.json +20 -30
- package/readme.hints.md +15 -17
- package/readme.md +239 -421
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/gitzone.cli.ts +8 -0
- package/ts/helpers.changelog.ts +165 -0
- package/ts/helpers.smartconfigmigrations.ts +192 -0
- package/ts/helpers.workflow.ts +387 -0
- package/ts/mod_commit/index.ts +233 -435
- package/ts/mod_commit/mod.helpers.ts +28 -16
- package/ts/mod_commit/mod.ui.ts +7 -2
- package/ts/mod_config/classes.commitconfig.ts +33 -3
- package/ts/mod_config/classes.releaseconfig.ts +14 -7
- package/ts/mod_config/index.ts +89 -28
- package/ts/mod_format/formatters/smartconfig.formatter.ts +2 -62
- package/ts/mod_release/index.ts +393 -0
- package/ts/mod_release/mod.plugins.ts +5 -0
- package/ts/mod_standard/index.ts +15 -2
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@git.zone/cli',
|
|
6
|
-
version: '2.
|
|
6
|
+
version: '2.15.0',
|
|
7
7
|
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
|
|
8
8
|
}
|
package/ts/gitzone.cli.ts
CHANGED
|
@@ -51,6 +51,14 @@ export let run = async () => {
|
|
|
51
51
|
await modCommit.run(argvArg);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* create a release from pending changelog entries
|
|
56
|
+
*/
|
|
57
|
+
gitzoneSmartcli.addCommand("release").subscribe(async (argvArg) => {
|
|
58
|
+
const modRelease = await import("./mod_release/index.js");
|
|
59
|
+
await modRelease.run(argvArg);
|
|
60
|
+
});
|
|
61
|
+
|
|
54
62
|
/**
|
|
55
63
|
* deprecate a package on npm
|
|
56
64
|
*/
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import * as plugins from "./plugins.js";
|
|
2
|
+
|
|
3
|
+
export type TChangelogBucket =
|
|
4
|
+
| "Breaking Changes"
|
|
5
|
+
| "Features"
|
|
6
|
+
| "Fixes"
|
|
7
|
+
| "Documentation"
|
|
8
|
+
| "Maintenance";
|
|
9
|
+
|
|
10
|
+
export interface IChangelogEntry {
|
|
11
|
+
type: string;
|
|
12
|
+
scope: string;
|
|
13
|
+
message: string;
|
|
14
|
+
details?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IPendingChangelog {
|
|
18
|
+
block: string;
|
|
19
|
+
isEmpty: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const bucketForCommitType = (commitType: string): TChangelogBucket => {
|
|
23
|
+
switch (commitType) {
|
|
24
|
+
case "BREAKING CHANGE":
|
|
25
|
+
return "Breaking Changes";
|
|
26
|
+
case "feat":
|
|
27
|
+
return "Features";
|
|
28
|
+
case "fix":
|
|
29
|
+
return "Fixes";
|
|
30
|
+
case "docs":
|
|
31
|
+
return "Documentation";
|
|
32
|
+
default:
|
|
33
|
+
return "Maintenance";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const readChangelog = async (filePath: string): Promise<string> => {
|
|
38
|
+
if (!(await plugins.smartfs.file(filePath).exists())) {
|
|
39
|
+
return "# Changelog\n\n";
|
|
40
|
+
}
|
|
41
|
+
return (await plugins.smartfs.file(filePath).encoding("utf8").read()) as string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const writeChangelog = async (filePath: string, content: string): Promise<void> => {
|
|
45
|
+
await plugins.smartfs.file(filePath).encoding("utf8").write(content.endsWith("\n") ? content : `${content}\n`);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const findPendingSection = (
|
|
49
|
+
content: string,
|
|
50
|
+
sectionName: string,
|
|
51
|
+
): { start: number; bodyStart: number; end: number } | null => {
|
|
52
|
+
const headingRegex = new RegExp(`^##\\s+${sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
53
|
+
const match = headingRegex.exec(content);
|
|
54
|
+
if (!match || match.index === undefined) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const bodyStart = match.index + match[0].length;
|
|
59
|
+
const rest = content.slice(bodyStart);
|
|
60
|
+
const nextHeadingMatch = /^##\s+/m.exec(rest);
|
|
61
|
+
const end = nextHeadingMatch ? bodyStart + nextHeadingMatch.index : content.length;
|
|
62
|
+
return { start: match.index, bodyStart, end };
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const ensurePendingSection = async (
|
|
66
|
+
filePath: string,
|
|
67
|
+
sectionName = "Pending",
|
|
68
|
+
): Promise<string> => {
|
|
69
|
+
let content = await readChangelog(filePath);
|
|
70
|
+
if (findPendingSection(content, sectionName)) {
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const pendingSection = `## ${sectionName}\n\n`;
|
|
75
|
+
const titleMatch = /^#\s+.+$/m.exec(content);
|
|
76
|
+
if (titleMatch && titleMatch.index !== undefined) {
|
|
77
|
+
const insertAt = titleMatch.index + titleMatch[0].length;
|
|
78
|
+
content = `${content.slice(0, insertAt)}\n\n${pendingSection}${content.slice(insertAt).replace(/^\n+/, "")}`;
|
|
79
|
+
} else {
|
|
80
|
+
content = `# Changelog\n\n${pendingSection}${content}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await writeChangelog(filePath, content);
|
|
84
|
+
return content;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const appendPendingChangelogEntry = async (
|
|
88
|
+
filePath: string,
|
|
89
|
+
sectionName: string,
|
|
90
|
+
entry: IChangelogEntry,
|
|
91
|
+
): Promise<void> => {
|
|
92
|
+
let content = await ensurePendingSection(filePath, sectionName);
|
|
93
|
+
const pendingSection = findPendingSection(content, sectionName)!;
|
|
94
|
+
let pendingBody = content.slice(pendingSection.bodyStart, pendingSection.end);
|
|
95
|
+
const bucket = bucketForCommitType(entry.type);
|
|
96
|
+
const bucketHeading = `### ${bucket}`;
|
|
97
|
+
|
|
98
|
+
const entryLines = [`- ${entry.message}${entry.scope ? ` (${entry.scope})` : ""}`];
|
|
99
|
+
for (const detail of entry.details || []) {
|
|
100
|
+
entryLines.push(` - ${detail}`);
|
|
101
|
+
}
|
|
102
|
+
const renderedEntry = entryLines.join("\n");
|
|
103
|
+
|
|
104
|
+
const bucketRegex = new RegExp(`^###\\s+${bucket.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
105
|
+
const bucketMatch = bucketRegex.exec(pendingBody);
|
|
106
|
+
if (!bucketMatch || bucketMatch.index === undefined) {
|
|
107
|
+
pendingBody = `${pendingBody.trimEnd()}\n\n${bucketHeading}\n\n${renderedEntry}\n`;
|
|
108
|
+
} else {
|
|
109
|
+
const bucketBodyStart = bucketMatch.index + bucketMatch[0].length;
|
|
110
|
+
const afterBucket = pendingBody.slice(bucketBodyStart);
|
|
111
|
+
const nextBucketMatch = /^###\s+/m.exec(afterBucket);
|
|
112
|
+
const insertAt = nextBucketMatch ? bucketBodyStart + nextBucketMatch.index : pendingBody.length;
|
|
113
|
+
const beforeInsert = pendingBody.slice(0, insertAt).trimEnd();
|
|
114
|
+
const afterInsert = pendingBody.slice(insertAt).replace(/^\n+/, "");
|
|
115
|
+
pendingBody = `${beforeInsert}\n${renderedEntry}\n\n${afterInsert}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
content = `${content.slice(0, pendingSection.bodyStart)}\n${pendingBody.trim()}\n\n${content.slice(pendingSection.end).replace(/^\n+/, "")}`;
|
|
119
|
+
await writeChangelog(filePath, content);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const readPendingChangelog = async (
|
|
123
|
+
filePath: string,
|
|
124
|
+
sectionName = "Pending",
|
|
125
|
+
): Promise<IPendingChangelog> => {
|
|
126
|
+
const content = await ensurePendingSection(filePath, sectionName);
|
|
127
|
+
const pendingSection = findPendingSection(content, sectionName)!;
|
|
128
|
+
const block = content.slice(pendingSection.bodyStart, pendingSection.end).trim();
|
|
129
|
+
return {
|
|
130
|
+
block,
|
|
131
|
+
isEmpty: block.length === 0,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const inferVersionTypeFromPending = (pendingBlock: string): "patch" | "minor" | "major" => {
|
|
136
|
+
if (/^###\s+Breaking Changes\s*$/m.test(pendingBlock)) {
|
|
137
|
+
return "major";
|
|
138
|
+
}
|
|
139
|
+
if (/^###\s+Features\s*$/m.test(pendingBlock)) {
|
|
140
|
+
return "minor";
|
|
141
|
+
}
|
|
142
|
+
return "patch";
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const movePendingToVersion = async (
|
|
146
|
+
filePath: string,
|
|
147
|
+
sectionName: string,
|
|
148
|
+
versionHeading: string,
|
|
149
|
+
version: string,
|
|
150
|
+
dateString: string,
|
|
151
|
+
): Promise<void> => {
|
|
152
|
+
let content = await ensurePendingSection(filePath, sectionName);
|
|
153
|
+
const pendingSection = findPendingSection(content, sectionName)!;
|
|
154
|
+
const pendingBlock = content.slice(pendingSection.bodyStart, pendingSection.end).trim();
|
|
155
|
+
if (!pendingBlock) {
|
|
156
|
+
throw new Error("No pending changelog entries. Nothing to release.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const renderedHeading = versionHeading
|
|
160
|
+
.replaceAll("{{version}}", version)
|
|
161
|
+
.replaceAll("{{date}}", dateString);
|
|
162
|
+
const nextContent = content.slice(pendingSection.end).replace(/^\n+/, "");
|
|
163
|
+
content = `${content.slice(0, pendingSection.bodyStart)}\n\n${renderedHeading}\n\n${pendingBlock}\n\n${nextContent}`;
|
|
164
|
+
await writeChangelog(filePath, content);
|
|
165
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export const CURRENT_GITZONE_CLI_SCHEMA_VERSION = 2;
|
|
2
|
+
|
|
3
|
+
export interface ISmartconfigMigrationResult {
|
|
4
|
+
migrated: boolean;
|
|
5
|
+
fromVersion: number;
|
|
6
|
+
toVersion: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const CLI_NAMESPACE = "@git.zone/cli";
|
|
10
|
+
|
|
11
|
+
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const ensureObject = (parent: Record<string, any>, key: string): Record<string, any> => {
|
|
16
|
+
if (!isPlainObject(parent[key])) {
|
|
17
|
+
parent[key] = {};
|
|
18
|
+
}
|
|
19
|
+
return parent[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const migrateNamespaceKeys = (smartconfigJson: Record<string, any>): boolean => {
|
|
23
|
+
let migrated = false;
|
|
24
|
+
const migrations = [
|
|
25
|
+
{ oldKey: "gitzone", newKey: CLI_NAMESPACE },
|
|
26
|
+
{ oldKey: "tsdoc", newKey: "@git.zone/tsdoc" },
|
|
27
|
+
{ oldKey: "npmdocker", newKey: "@git.zone/tsdocker" },
|
|
28
|
+
{ oldKey: "npmci", newKey: "@ship.zone/szci" },
|
|
29
|
+
{ oldKey: "szci", newKey: "@ship.zone/szci" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const { oldKey, newKey } of migrations) {
|
|
33
|
+
if (!isPlainObject(smartconfigJson[oldKey])) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!isPlainObject(smartconfigJson[newKey])) {
|
|
37
|
+
smartconfigJson[newKey] = smartconfigJson[oldKey];
|
|
38
|
+
} else {
|
|
39
|
+
smartconfigJson[newKey] = {
|
|
40
|
+
...smartconfigJson[oldKey],
|
|
41
|
+
...smartconfigJson[newKey],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
delete smartconfigJson[oldKey];
|
|
45
|
+
migrated = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return migrated;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const migrateToV2 = (smartconfigJson: Record<string, any>): boolean => {
|
|
52
|
+
const cliConfig = ensureObject(smartconfigJson, CLI_NAMESPACE);
|
|
53
|
+
const releaseConfig = ensureObject(cliConfig, "release");
|
|
54
|
+
|
|
55
|
+
let migrated = false;
|
|
56
|
+
const targets = ensureObject(releaseConfig, "targets");
|
|
57
|
+
const shipzoneConfig = smartconfigJson["@ship.zone/szci"];
|
|
58
|
+
|
|
59
|
+
if (isPlainObject(releaseConfig.git) && !isPlainObject(targets.git)) {
|
|
60
|
+
targets.git = releaseConfig.git;
|
|
61
|
+
delete releaseConfig.git;
|
|
62
|
+
migrated = true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (isPlainObject(releaseConfig.npm) && !isPlainObject(targets.npm)) {
|
|
66
|
+
targets.npm = releaseConfig.npm;
|
|
67
|
+
delete releaseConfig.npm;
|
|
68
|
+
migrated = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (isPlainObject(releaseConfig.docker) && !isPlainObject(targets.docker)) {
|
|
72
|
+
targets.docker = releaseConfig.docker;
|
|
73
|
+
delete releaseConfig.docker;
|
|
74
|
+
migrated = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (Array.isArray(releaseConfig.registries)) {
|
|
78
|
+
const npmTarget = ensureObject(targets, "npm");
|
|
79
|
+
if (!Array.isArray(npmTarget.registries)) {
|
|
80
|
+
npmTarget.registries = releaseConfig.registries;
|
|
81
|
+
}
|
|
82
|
+
delete releaseConfig.registries;
|
|
83
|
+
migrated = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (releaseConfig.accessLevel) {
|
|
87
|
+
const npmTarget = ensureObject(targets, "npm");
|
|
88
|
+
if (!npmTarget.accessLevel) {
|
|
89
|
+
npmTarget.accessLevel = releaseConfig.accessLevel;
|
|
90
|
+
}
|
|
91
|
+
delete releaseConfig.accessLevel;
|
|
92
|
+
migrated = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isPlainObject(shipzoneConfig)) {
|
|
96
|
+
if (shipzoneConfig.npmAccessLevel) {
|
|
97
|
+
const npmTarget = ensureObject(targets, "npm");
|
|
98
|
+
if (!npmTarget.accessLevel) {
|
|
99
|
+
npmTarget.accessLevel = shipzoneConfig.npmAccessLevel;
|
|
100
|
+
}
|
|
101
|
+
delete shipzoneConfig.npmAccessLevel;
|
|
102
|
+
migrated = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (shipzoneConfig.npmRegistryUrl) {
|
|
106
|
+
const npmTarget = ensureObject(targets, "npm");
|
|
107
|
+
const registry = normalizeRegistryUrl(shipzoneConfig.npmRegistryUrl);
|
|
108
|
+
const registries = Array.isArray(npmTarget.registries) ? npmTarget.registries : [];
|
|
109
|
+
if (!registries.includes(registry)) {
|
|
110
|
+
registries.push(registry);
|
|
111
|
+
}
|
|
112
|
+
npmTarget.registries = registries;
|
|
113
|
+
delete shipzoneConfig.npmRegistryUrl;
|
|
114
|
+
migrated = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (Array.isArray(releaseConfig.steps)) {
|
|
119
|
+
const steps = releaseConfig.steps as string[];
|
|
120
|
+
const preflight = ensureObject(releaseConfig, "preflight");
|
|
121
|
+
if (steps.includes("test") && preflight.test === undefined) {
|
|
122
|
+
preflight.test = true;
|
|
123
|
+
}
|
|
124
|
+
if (steps.includes("build") && preflight.build === undefined) {
|
|
125
|
+
preflight.build = true;
|
|
126
|
+
}
|
|
127
|
+
if (steps.includes("push")) {
|
|
128
|
+
const gitTarget = ensureObject(targets, "git");
|
|
129
|
+
if (gitTarget.enabled === undefined) {
|
|
130
|
+
gitTarget.enabled = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (steps.includes("publishNpm")) {
|
|
134
|
+
const npmTarget = ensureObject(targets, "npm");
|
|
135
|
+
if (npmTarget.enabled === undefined) {
|
|
136
|
+
npmTarget.enabled = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (steps.includes("publishDocker")) {
|
|
140
|
+
const dockerTarget = ensureObject(targets, "docker");
|
|
141
|
+
if (dockerTarget.enabled === undefined) {
|
|
142
|
+
dockerTarget.enabled = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
delete releaseConfig.steps;
|
|
146
|
+
migrated = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (releaseConfig.changelog) {
|
|
150
|
+
delete releaseConfig.changelog;
|
|
151
|
+
migrated = true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
cliConfig.schemaVersion = 2;
|
|
155
|
+
return migrated || true;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const normalizeRegistryUrl = (url: string): string => {
|
|
159
|
+
let normalizedUrl = url.trim();
|
|
160
|
+
if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) {
|
|
161
|
+
normalizedUrl = `https://${normalizedUrl}`;
|
|
162
|
+
}
|
|
163
|
+
return normalizedUrl.endsWith("/") ? normalizedUrl.slice(0, -1) : normalizedUrl;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const migrateSmartconfigData = (
|
|
167
|
+
smartconfigJson: Record<string, any>,
|
|
168
|
+
targetVersion = CURRENT_GITZONE_CLI_SCHEMA_VERSION,
|
|
169
|
+
): ISmartconfigMigrationResult => {
|
|
170
|
+
let migrated = false;
|
|
171
|
+
migrated = migrateNamespaceKeys(smartconfigJson) || migrated;
|
|
172
|
+
|
|
173
|
+
const cliConfig = ensureObject(smartconfigJson, CLI_NAMESPACE);
|
|
174
|
+
const fromVersion = typeof cliConfig.schemaVersion === "number" ? cliConfig.schemaVersion : 1;
|
|
175
|
+
let currentVersion = fromVersion;
|
|
176
|
+
|
|
177
|
+
if (currentVersion < 2 && targetVersion >= 2) {
|
|
178
|
+
migrated = migrateToV2(smartconfigJson) || migrated;
|
|
179
|
+
currentVersion = 2;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (targetVersion === CURRENT_GITZONE_CLI_SCHEMA_VERSION && cliConfig.schemaVersion !== targetVersion) {
|
|
183
|
+
cliConfig.schemaVersion = targetVersion;
|
|
184
|
+
migrated = true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
migrated,
|
|
189
|
+
fromVersion,
|
|
190
|
+
toVersion: Math.min(targetVersion, CURRENT_GITZONE_CLI_SCHEMA_VERSION),
|
|
191
|
+
};
|
|
192
|
+
};
|