@git.zone/cli 6.9.0 → 6.10.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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/cli',
6
- version: '6.9.0',
6
+ version: '6.10.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
  }
@@ -5,7 +5,7 @@ export type TConfirmationMode = "prompt" | "auto" | "plan";
5
5
  export type TCommitStep =
6
6
  "format" | "analyze" | "test" | "build" | "changelog" | "commit" | "push";
7
7
 
8
- export type TReleaseTarget = "git" | "npm" | "docker";
8
+ export type TReleaseTarget = "git" | "giteaAssets" | "npm" | "docker";
9
9
 
10
10
  export interface ICommitWorkflowConfig {
11
11
  confirmation?: TConfirmationMode;
@@ -45,6 +45,96 @@ export interface IReleaseNpmTargetConfig {
45
45
  alreadyPublished?: "success" | "error";
46
46
  }
47
47
 
48
+ export interface IReleaseGiteaAssetsTargetConfig {
49
+ enabled?: boolean;
50
+ apiOrigin?: string;
51
+ owner?: string;
52
+ repository?: string;
53
+ tokenEnv?: string;
54
+ manifestPath?: string;
55
+ }
56
+
57
+ export interface IResolvedReleaseGiteaAssetsConfiguration {
58
+ apiOrigin: string;
59
+ owner: string;
60
+ repository: string;
61
+ tokenEnv: string;
62
+ manifestPath: string;
63
+ }
64
+
65
+ export const resolveGiteaAssetsConfiguration = (
66
+ configArg: IReleaseGiteaAssetsTargetConfig,
67
+ ): IResolvedReleaseGiteaAssetsConfiguration => {
68
+ const { apiOrigin, owner, repository, tokenEnv, manifestPath } = configArg;
69
+ let url: URL;
70
+ try {
71
+ url = new URL(apiOrigin!);
72
+ } catch {
73
+ throw new Error("Gitea assets require a canonical HTTPS API origin.");
74
+ }
75
+ if (url.protocol !== "https:" || url.origin !== apiOrigin) {
76
+ throw new Error(
77
+ "Gitea assets require a credential-free canonical HTTPS API origin.",
78
+ );
79
+ }
80
+ if (
81
+ typeof owner !== "string" ||
82
+ !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(owner) ||
83
+ typeof repository !== "string" ||
84
+ !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(repository)
85
+ )
86
+ throw new Error(
87
+ "Gitea assets require canonical owner and repository names.",
88
+ );
89
+ if (typeof tokenEnv !== "string" || !/^[A-Z_][A-Z0-9_]*$/.test(tokenEnv)) {
90
+ throw new Error("Gitea assets require a token environment-variable name.");
91
+ }
92
+ if (
93
+ typeof manifestPath !== "string" ||
94
+ !/^[A-Za-z0-9_-][A-Za-z0-9._/-]*\.json$/.test(manifestPath) ||
95
+ manifestPath
96
+ .split("/")
97
+ .some(
98
+ (part) =>
99
+ !part || part === "." || part === ".." || part.startsWith("."),
100
+ )
101
+ )
102
+ throw new Error(
103
+ "Gitea asset manifestPath must be a canonical repository-relative JSON path.",
104
+ );
105
+ return { apiOrigin, owner, repository, tokenEnv, manifestPath };
106
+ };
107
+
108
+ /** Match the resolved destination, without guessing SSH aliases or URL rewrites. */
109
+ export const assertGiteaAssetsGitDestination = (
110
+ configArg: IResolvedReleaseGiteaAssetsConfiguration,
111
+ destinationArg: string,
112
+ ): void => {
113
+ let destination: URL;
114
+ try {
115
+ const scp = destinationArg.match(/^(?:[^@/:]+@)?([^/:]+):([^/].*)$/);
116
+ destination = new URL(scp ? `ssh://${scp[1]}/${scp[2]}` : destinationArg);
117
+ } catch {
118
+ throw new Error(
119
+ "Gitea assets require a provable SSH or HTTPS Git destination.",
120
+ );
121
+ }
122
+ if (
123
+ !["ssh:", "https:"].includes(destination.protocol) ||
124
+ destination.password ||
125
+ destination.search ||
126
+ destination.hash ||
127
+ destination.hostname !== new URL(configArg.apiOrigin).hostname ||
128
+ (destination.protocol === "https:" &&
129
+ destination.origin !== configArg.apiOrigin) ||
130
+ destination.pathname.replace(/\.git$/, "") !==
131
+ `/${configArg.owner}/${configArg.repository}`
132
+ )
133
+ throw new Error(
134
+ "Gitea assets and Git must select the same host and exact repository.",
135
+ );
136
+ };
137
+
48
138
  export interface IReleaseDockerTargetConfig {
49
139
  enabled?: boolean;
50
140
  engine?: "tsdocker";
@@ -73,6 +163,7 @@ export interface IReleaseWorkflowConfig {
73
163
  targets?: {
74
164
  git?: IReleaseGitTargetConfig;
75
165
  npm?: IReleaseNpmTargetConfig;
166
+ giteaAssets?: IReleaseGiteaAssetsTargetConfig;
76
167
  docker?: IReleaseDockerTargetConfig;
77
168
  };
78
169
  }
@@ -111,6 +202,7 @@ export interface IResolvedReleaseWorkflow {
111
202
  npmPackageSource: "root" | "tspublish";
112
203
  npmAccessLevel: "public" | "private";
113
204
  npmAlreadyPublished: "success" | "error";
205
+ giteaAssets?: IResolvedReleaseGiteaAssetsConfiguration;
114
206
  dockerEnabled: boolean;
115
207
  dockerEngine: "tsdocker";
116
208
  dockerRegistry?: string;
@@ -129,6 +221,7 @@ export interface IResolvedReleaseResumeConfiguration {
129
221
  npmRegistries: string[];
130
222
  npmAccessLevel: "public" | "private";
131
223
  npmAlreadyPublished: "success" | "error";
224
+ giteaAssets?: IResolvedReleaseGiteaAssetsConfiguration;
132
225
  }
133
226
 
134
227
  export type TResolvedReleaseDockerConfiguration = Pick<
@@ -460,7 +553,12 @@ const normalizeCommitSteps = (rawSteps: TCommitStep[]): TCommitStep[] => {
460
553
  };
461
554
 
462
555
  const getTargetOverride = (argvArg: any): TReleaseTarget[] | undefined => {
463
- const validTargets: TReleaseTarget[] = ["git", "npm", "docker"];
556
+ const validTargets: TReleaseTarget[] = [
557
+ "git",
558
+ "giteaAssets",
559
+ "npm",
560
+ "docker",
561
+ ];
464
562
  const targetKey = ["target", "targets"].find((keyArg) =>
465
563
  Object.prototype.hasOwnProperty.call(argvArg, keyArg),
466
564
  );
@@ -495,7 +593,12 @@ const getTargetOverride = (argvArg: any): TReleaseTarget[] | undefined => {
495
593
  return targets.length > 0 ? targets : undefined;
496
594
  };
497
595
 
498
- const releaseTargetOrder: TReleaseTarget[] = ["git", "npm", "docker"];
596
+ const releaseTargetOrder: TReleaseTarget[] = [
597
+ "git",
598
+ "giteaAssets",
599
+ "npm",
600
+ "docker",
601
+ ];
499
602
 
500
603
  const buildReleasePlan = (options: {
501
604
  mergeRequested: boolean;
@@ -511,6 +614,8 @@ const buildReleasePlan = (options: {
511
614
  plan.push("core.version", "core.changelog", "core.commit", "core.tag");
512
615
  if (options.runBuild) plan.push("core.build");
513
616
  if (options.targets.includes("npm")) plan.push("core.packNpmArtifact");
617
+ if (options.targets.includes("giteaAssets"))
618
+ plan.push("core.freezeGiteaAssets");
514
619
  plan.push("core.writeReleaseJournal");
515
620
  if (options.targets.includes("docker")) {
516
621
  plan.push("target.docker.qualify");
@@ -608,6 +713,14 @@ export const resolveReleaseWorkflow = async (
608
713
  const npmEnabled = npmConfig.enabled ?? npmRegistryConfigurationPresent;
609
714
  const gitEnabled = gitConfig.enabled ?? true;
610
715
  const dockerEnabled = dockerConfig.enabled ?? false;
716
+ if (
717
+ targetConfig.giteaAssets?.enabled !== undefined &&
718
+ typeof targetConfig.giteaAssets.enabled !== "boolean"
719
+ ) {
720
+ throw new Error(
721
+ "release.targets.giteaAssets.enabled must be true or false.",
722
+ );
723
+ }
611
724
  const pushBranch = resolveGitPushSetting(
612
725
  gitConfig.pushBranch,
613
726
  "pushBranch",
@@ -638,6 +751,8 @@ export const resolveReleaseWorkflow = async (
638
751
 
639
752
  const configuredTargets: TReleaseTarget[] = [];
640
753
  if (gitEnabled) configuredTargets.push("git");
754
+ if (targetConfig.giteaAssets?.enabled === true)
755
+ configuredTargets.push("giteaAssets");
641
756
  if (npmEnabled) configuredTargets.push("npm");
642
757
  if (dockerEnabled) configuredTargets.push("docker");
643
758
  let targets = getTargetOverride(argvArg) || configuredTargets;
@@ -651,6 +766,28 @@ export const resolveReleaseWorkflow = async (
651
766
  targets = releaseTargetOrder.filter((target) =>
652
767
  selectedTargets.includes(target),
653
768
  );
769
+ if (
770
+ targetConfig.giteaAssets?.enabled === true &&
771
+ targets.includes("npm") &&
772
+ !targets.includes("giteaAssets")
773
+ ) {
774
+ throw new Error(
775
+ "npm publication cannot bypass the configured Gitea asset target.",
776
+ );
777
+ }
778
+ const giteaAssets = targets.includes("giteaAssets")
779
+ ? resolveGiteaAssetsConfiguration(targetConfig.giteaAssets || {})
780
+ : undefined;
781
+ if (giteaAssets && (!targets.includes("git") || !pushBranch || !pushTags)) {
782
+ throw new Error(
783
+ "Gitea assets require the active Git branch and tag target.",
784
+ );
785
+ }
786
+ if (giteaAssets && (targets.includes("docker") || argvArg.merge === true)) {
787
+ throw new Error(
788
+ "Gitea asset releases cannot combine Docker or --merge; integrate main before release.",
789
+ );
790
+ }
654
791
  let npmRegistries: string[] = [];
655
792
  if (targets.includes("npm")) {
656
793
  if (
@@ -706,6 +843,7 @@ export const resolveReleaseWorkflow = async (
706
843
  : "root",
707
844
  npmAccessLevel: npmConfig.accessLevel || "public",
708
845
  npmAlreadyPublished: npmConfig.alreadyPublished || "success",
846
+ ...(giteaAssets ? { giteaAssets } : {}),
709
847
  dockerEnabled,
710
848
  dockerEngine: "tsdocker",
711
849
  ...dockerConfiguration,
@@ -715,12 +853,18 @@ export const resolveReleaseWorkflow = async (
715
853
  export const resolveReleaseResumeConfiguration = async (optionsArg: {
716
854
  git: boolean;
717
855
  npm: boolean;
856
+ giteaAssets?: boolean;
718
857
  }): Promise<IResolvedReleaseResumeConfiguration> => {
719
858
  const cliConfig = await readCliWorkflowConfig();
720
859
  const releaseConfig = cliConfig.release || {};
721
860
  const targetConfig = releaseConfig.targets || {};
722
861
  const gitConfig = targetConfig.git || {};
723
862
  const npmConfig = targetConfig.npm || {};
863
+ if (optionsArg.giteaAssets && targetConfig.giteaAssets?.enabled !== true) {
864
+ throw new Error(
865
+ "The journaled Gitea asset target must remain enabled for resume.",
866
+ );
867
+ }
724
868
 
725
869
  const gitRemote = optionsArg.git ? gitConfig.remote || "origin" : "origin";
726
870
  if (typeof gitRemote !== "string" || !gitRemote.trim()) {
@@ -778,6 +922,13 @@ export const resolveReleaseResumeConfiguration = async (optionsArg: {
778
922
  : "root",
779
923
  npmAccessLevel,
780
924
  npmAlreadyPublished,
925
+ ...(optionsArg.giteaAssets
926
+ ? {
927
+ giteaAssets: resolveGiteaAssetsConfiguration(
928
+ targetConfig.giteaAssets || {},
929
+ ),
930
+ }
931
+ : {}),
781
932
  };
782
933
  };
783
934
 
@@ -19,7 +19,10 @@ import {
19
19
  CURRENT_GITZONE_CLI_SCHEMA_VERSION,
20
20
  migrateSmartconfigData,
21
21
  } from "../helpers.smartconfigmigrations.js";
22
- import { getDeclaredTsdockerRange } from "../helpers.workflow.js";
22
+ import {
23
+ getDeclaredTsdockerRange,
24
+ resolveGiteaAssetsConfiguration,
25
+ } from "../helpers.workflow.js";
23
26
  import { hasInstalledProjectTsdockerBinary } from "../helpers.tsdocker.js";
24
27
  import { TsdockerReleaseClient } from "../mod_release/helpers.tsdockerprotocol.js";
25
28
  import { getSealedReleaseConfigError } from "../helpers.sealedrelease.js";
@@ -304,6 +307,10 @@ async function handleShow(mode: ICliMode): Promise<void> {
304
307
 
305
308
  printConfigSection("Release Targets", [
306
309
  ["git", formatTarget(targets.git?.enabled, targets.git)],
310
+ [
311
+ "giteaAssets",
312
+ formatTarget(targets.giteaAssets?.enabled, targets.giteaAssets),
313
+ ],
307
314
  ["npm", formatTarget(targets.npm?.enabled, targets.npm)],
308
315
  ["docker", formatTarget(targets.docker?.enabled, targets.docker)],
309
316
  ]);
@@ -1800,6 +1807,37 @@ async function validateReleaseConfig(
1800
1807
 
1801
1808
  const targets = releaseConfig.targets || {};
1802
1809
  await validateGitTarget(targets.git || {}, findings);
1810
+ if (
1811
+ targets.giteaAssets?.enabled !== undefined &&
1812
+ typeof targets.giteaAssets.enabled !== "boolean"
1813
+ ) {
1814
+ findings.push({
1815
+ level: "error",
1816
+ message: "Gitea assets enabled must be true or false",
1817
+ });
1818
+ }
1819
+ if (targets.giteaAssets?.enabled === true) {
1820
+ try {
1821
+ resolveGiteaAssetsConfiguration(targets.giteaAssets);
1822
+ if (targets.git?.enabled === false || targets.docker?.enabled === true)
1823
+ throw new Error(
1824
+ "Gitea assets require Git publication and cannot combine Docker.",
1825
+ );
1826
+ findings.push({
1827
+ level: "ok",
1828
+ message:
1829
+ "Gitea assets configured; release verifies Git destination and publisher capability before publication",
1830
+ });
1831
+ } catch (error) {
1832
+ findings.push({
1833
+ level: "error",
1834
+ message:
1835
+ error instanceof Error
1836
+ ? error.message
1837
+ : "Invalid Gitea asset configuration",
1838
+ });
1839
+ }
1840
+ }
1803
1841
  await validateNpmTarget(targets.npm || {}, findings);
1804
1842
  await validateDockerTarget(targets.docker || {}, smartconfigData, findings);
1805
1843
  }
@@ -1830,9 +1868,19 @@ async function validateAssetsConfig(
1830
1868
  }
1831
1869
  if (rawAssetsConfig.kind === "tspackRelease") {
1832
1870
  const error = getSealedReleaseConfigError(rawAssetsConfig.sealedRelease);
1833
- findings.push(error ? {
1834
- level: "error", message: error, fix: "Set @git.zone/cli.assets.sealedRelease using the documented TsPack release configuration.",
1835
- } : { level: "ok", message: "Managed tspackRelease assets have valid component commands and retention output" });
1871
+ findings.push(
1872
+ error
1873
+ ? {
1874
+ level: "error",
1875
+ message: error,
1876
+ fix: "Set @git.zone/cli.assets.sealedRelease using the documented TsPack release configuration.",
1877
+ }
1878
+ : {
1879
+ level: "ok",
1880
+ message:
1881
+ "Managed tspackRelease assets have valid component commands and retention output",
1882
+ },
1883
+ );
1836
1884
  return;
1837
1885
  }
1838
1886
  if (rawAssetsConfig.kind !== "denoBinaryCli") {
@@ -2001,8 +2049,15 @@ async function validateNpmTarget(
2001
2049
  fix: "Use success or error.",
2002
2050
  });
2003
2051
  }
2004
- if (npmTarget.packageSource !== undefined && !["root", "tspublish"].includes(npmTarget.packageSource)) {
2005
- findings.push({ level: "error", message: `Invalid npm package source: ${npmTarget.packageSource}`, fix: "Use root or tspublish." });
2052
+ if (
2053
+ npmTarget.packageSource !== undefined &&
2054
+ !["root", "tspublish"].includes(npmTarget.packageSource)
2055
+ ) {
2056
+ findings.push({
2057
+ level: "error",
2058
+ message: `Invalid npm package source: ${npmTarget.packageSource}`,
2059
+ fix: "Use root or tspublish.",
2060
+ });
2006
2061
  }
2007
2062
 
2008
2063
  const smartNetwork = new plugins.smartnetwork.SmartNetwork();