@git.zone/cli 6.4.0 → 6.6.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.
@@ -4,6 +4,7 @@ import * as plugins from '../mod.plugins.js';
4
4
  import * as paths from '../../paths.js';
5
5
  import { logger, logVerbose } from '../../gitzone.logging.js';
6
6
  import { getCliConfigValue, readSmartconfigFile } from '../../helpers.smartconfig.js';
7
+ import { getSealedReleaseConfigError } from '../../helpers.sealedrelease.js';
7
8
 
8
9
  interface IAssetFileMapping {
9
10
  from: string;
@@ -131,6 +132,16 @@ const normalizeBinaryTargets = (
131
132
  };
132
133
 
133
134
  const buildDefaultEntries = (assetsConfig: Record<string, any>, cliName: string): IAssetTemplateEntry[] => {
135
+ if (assetsConfig.kind === 'tspackRelease') {
136
+ return [{
137
+ template: 'asset_tspack_release_gitea',
138
+ files: [
139
+ { from: '.gitea/workflows/release.yml', to: '.gitea/workflows/release.yml' },
140
+ { from: 'scripts/gitzone-sealed-release.mjs', to: 'scripts/gitzone-sealed-release.mjs' },
141
+ { from: 'scripts/gitzone-release-api.mjs', to: 'scripts/gitzone-release-api.mjs' },
142
+ ],
143
+ }];
144
+ }
134
145
  if (assetsConfig.kind !== 'denoBinaryCli') {
135
146
  return [];
136
147
  }
@@ -241,6 +252,10 @@ export class AssetsFormatter extends BaseFormatter {
241
252
  if (!isPlainObject(assetsConfig) || assetsConfig.enabled === false) {
242
253
  return [];
243
254
  }
255
+ if (assetsConfig.kind === 'tspackRelease') {
256
+ const error = getSealedReleaseConfigError(assetsConfig.sealedRelease);
257
+ return error ? [{ level: 'error', module: this.name, message: error }] : [];
258
+ }
244
259
  if (assetsConfig.kind && assetsConfig.kind !== 'denoBinaryCli') {
245
260
  return [{ level: 'error', module: this.name, message: `Unsupported managed asset kind: ${assetsConfig.kind}` }];
246
261
  }
@@ -438,28 +438,64 @@ const resolvePushUrl = async (
438
438
  return resolvedPushUrl;
439
439
  };
440
440
 
441
+ /** Git's exact absent-ref lease identity; null remains the disabled-target state. */
442
+ export const isAbsentGitOid = (valueArg: string): boolean =>
443
+ /^(?:0{40}|0{64})$/.test(valueArg);
444
+
441
445
  const resolveRemoteMainOid = async (
442
446
  smartshellArg: TSmartshell,
443
447
  cwdArg: string,
444
448
  pushUrlArg: string,
445
449
  readOnlyArg = false,
446
450
  ): Promise<string> => {
447
- const result = requireGitSuccess(
448
- await runGit(
449
- smartshellArg,
450
- cwdArg,
451
- ["ls-remote", "--exit-code", pushUrlArg, mainBranchRef],
452
- readOnlyArg,
453
- ),
454
- "The configured Git push destination does not expose refs/heads/main.",
451
+ const result = await runGit(
452
+ smartshellArg,
453
+ cwdArg,
454
+ ["ls-remote", "--exit-code", pushUrlArg, mainBranchRef],
455
+ readOnlyArg,
456
+ );
457
+ if (result.exitCode === 2 && !result.stdout.trim()) {
458
+ const allRefs = requireGitSuccess(
459
+ await runGit(
460
+ smartshellArg,
461
+ cwdArg,
462
+ ["ls-remote", pushUrlArg],
463
+ readOnlyArg,
464
+ ),
465
+ "Unable to verify the empty Git push destination.",
466
+ );
467
+ if (allRefs.stdout.trim()) {
468
+ throw new Error(
469
+ "First release requires an empty Git remote; main is missing from a nonempty destination.",
470
+ );
471
+ }
472
+ const format = requireGitSuccess(
473
+ await runGit(
474
+ smartshellArg,
475
+ cwdArg,
476
+ ["rev-parse", "--show-object-format"],
477
+ readOnlyArg,
478
+ ),
479
+ "Unable to identify the repository object format.",
480
+ ).stdout.trim();
481
+ if (format !== "sha1" && format !== "sha256") {
482
+ throw new Error("Unsupported Git object format for first release.");
483
+ }
484
+ return "0".repeat(format === "sha256" ? 64 : 40);
485
+ }
486
+ requireGitSuccess(
487
+ result,
488
+ "Unable to inspect main at the configured Git push destination.",
455
489
  );
456
- const oid = result.stdout.trim().split(/\s+/)[0];
457
- if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(oid)) {
490
+ const match = result.stdout
491
+ .trim()
492
+ .match(/^([0-9a-f]{40}|[0-9a-f]{64})\s+refs\/heads\/main$/i);
493
+ if (!match || isAbsentGitOid(match[1])) {
458
494
  throw new Error(
459
495
  "The configured Git push destination returned an invalid main commit ID.",
460
496
  );
461
497
  }
462
- return oid;
498
+ return match[1];
463
499
  };
464
500
 
465
501
  const hasCommitObject = async (
@@ -788,6 +824,15 @@ export async function inspectReleaseBranch(
788
824
  context.pushUrl,
789
825
  true,
790
826
  );
827
+ if (isAbsentGitOid(context.remoteMainOid)) {
828
+ if (symbolic.branch !== mainBranch) {
829
+ throw new Error(
830
+ "First release into an empty Git remote must run directly from main.",
831
+ );
832
+ }
833
+ context.planAncestryVerified = true;
834
+ return context;
835
+ }
791
836
  if (
792
837
  await hasCommitObject(
793
838
  optionsArg.smartshell,
@@ -850,6 +895,24 @@ export async function prepareReleaseIntegration(
850
895
  "Remote main changed during release preparation. Fetch, rebase cleanly, and retry.",
851
896
  );
852
897
  }
898
+ if (isAbsentGitOid(remoteMainOid)) {
899
+ if (
900
+ contextArg.sourceBranch !== mainBranch ||
901
+ contextArg.remoteMainOid !== remoteMainOid
902
+ ) {
903
+ throw new Error(
904
+ "First release requires unchanged empty remote state and local main.",
905
+ );
906
+ }
907
+ await verifySourceState(smartshellArg, contextArg);
908
+ await verifyMainWorktree(
909
+ smartshellArg,
910
+ contextArg.mainWorktreePath,
911
+ contextArg.localMainOid,
912
+ contextArg.mainWorktreeGitDir,
913
+ );
914
+ return { ...contextArg, planAncestryVerified: true };
915
+ }
853
916
  requireGitSuccess(
854
917
  await runGit(smartshellArg, contextArg.sourceCwd, [
855
918
  "fetch",
@@ -19,7 +19,7 @@ import {
19
19
  type INpmArtifactProbeOptions,
20
20
  type INpmArtifactProbeResult,
21
21
  } from "./helpers.npmartifact.js";
22
- import { releaseGitEnv } from "./helpers.releasebranch.js";
22
+ import { isAbsentGitOid, releaseGitEnv } from "./helpers.releasebranch.js";
23
23
  import {
24
24
  TsdockerReleaseCommandError,
25
25
  TsdockerReleaseRequestCleanupError,
@@ -292,7 +292,10 @@ const inspectRemoteRelease = async (
292
292
  return { status: "exact" };
293
293
  }
294
294
  if (
295
- mainOid === journalArg.git.expectedRemoteMainOid &&
295
+ (mainOid === journalArg.git.expectedRemoteMainOid ||
296
+ (mainOid === undefined &&
297
+ journalArg.git.expectedRemoteMainOid !== null &&
298
+ isAbsentGitOid(journalArg.git.expectedRemoteMainOid))) &&
296
299
  tagOid === undefined &&
297
300
  tagCommitOid === undefined
298
301
  ) {