@git.zone/cli 3.2.1 → 4.0.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.
Files changed (125) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/classes.gitzoneconfig.js +1 -1
  3. package/dist_ts/classes.project.js +1 -1
  4. package/dist_ts/gitzone.cli.js +1 -1
  5. package/dist_ts/gitzone.logging.js +1 -1
  6. package/dist_ts/helpers.changelog.js +1 -1
  7. package/dist_ts/helpers.climode.js +1 -1
  8. package/dist_ts/helpers.smartconfig.js +61 -11
  9. package/dist_ts/helpers.smartconfigmigrations.js +1 -1
  10. package/dist_ts/helpers.tsdocker.js +1 -1
  11. package/dist_ts/helpers.workflow.js +1 -1
  12. package/dist_ts/index.js +1 -1
  13. package/dist_ts/mod_audit/index.js +1 -1
  14. package/dist_ts/mod_commit/index.js +1 -1
  15. package/dist_ts/mod_commit/mod.helpers.js +1 -1
  16. package/dist_ts/mod_commit/mod.plugins.js +1 -1
  17. package/dist_ts/mod_commit/mod.ui.js +1 -1
  18. package/dist_ts/mod_config/classes.commitconfig.js +1 -1
  19. package/dist_ts/mod_config/classes.releaseconfig.js +1 -1
  20. package/dist_ts/mod_config/index.js +2 -2
  21. package/dist_ts/mod_config/mod.plugins.js +1 -1
  22. package/dist_ts/mod_deprecate/index.js +1 -1
  23. package/dist_ts/mod_deprecate/mod.plugins.js +1 -1
  24. package/dist_ts/mod_docker/classes.dockerpruner.js +7 -1
  25. package/dist_ts/mod_docker/index.js +1 -1
  26. package/dist_ts/mod_docker/mod.plugins.js +1 -1
  27. package/dist_ts/mod_format/classes.baseformatter.js +1 -1
  28. package/dist_ts/mod_format/classes.diffreporter.js +1 -1
  29. package/dist_ts/mod_format/classes.formatcontext.js +1 -1
  30. package/dist_ts/mod_format/classes.formatplanner.js +1 -1
  31. package/dist_ts/mod_format/classes.formatstats.js +1 -1
  32. package/dist_ts/mod_format/formatters/assets.formatter.js +1 -1
  33. package/dist_ts/mod_format/formatters/cleanup.formatter.js +1 -1
  34. package/dist_ts/mod_format/formatters/copy.formatter.js +1 -1
  35. package/dist_ts/mod_format/formatters/gitignore.formatter.js +1 -1
  36. package/dist_ts/mod_format/formatters/license.formatter.js +1 -1
  37. package/dist_ts/mod_format/formatters/packagejson.formatter.js +1 -1
  38. package/dist_ts/mod_format/formatters/prettier.formatter.js +1 -1
  39. package/dist_ts/mod_format/formatters/readme.formatter.js +1 -1
  40. package/dist_ts/mod_format/formatters/smartconfig.formatter.js +1 -1
  41. package/dist_ts/mod_format/formatters/templates.formatter.js +1 -1
  42. package/dist_ts/mod_format/formatters/tsconfig.formatter.js +1 -1
  43. package/dist_ts/mod_format/index.js +1 -1
  44. package/dist_ts/mod_format/interfaces.format.js +1 -1
  45. package/dist_ts/mod_format/mod.plugins.js +1 -1
  46. package/dist_ts/mod_helpers/index.js +1 -1
  47. package/dist_ts/mod_helpers/mod.plugins.js +1 -1
  48. package/dist_ts/mod_meta/index.js +1 -1
  49. package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
  50. package/dist_ts/mod_meta/meta.interfaces.js +1 -1
  51. package/dist_ts/mod_meta/meta.plugins.js +1 -1
  52. package/dist_ts/mod_open/index.js +1 -1
  53. package/dist_ts/mod_open/mod.plugins.js +1 -1
  54. package/dist_ts/mod_release/index.js +1 -1
  55. package/dist_ts/mod_release/mod.plugins.js +1 -1
  56. package/dist_ts/mod_services/classes.dockercontainer.d.ts +50 -3
  57. package/dist_ts/mod_services/classes.dockercontainer.js +646 -84
  58. package/dist_ts/mod_services/classes.globalregistry.d.ts +10 -24
  59. package/dist_ts/mod_services/classes.globalregistry.js +210 -62
  60. package/dist_ts/mod_services/classes.globalregistrystore.d.ts +48 -0
  61. package/dist_ts/mod_services/classes.globalregistrystore.js +156 -0
  62. package/dist_ts/mod_services/classes.interprocesslock.d.ts +21 -0
  63. package/dist_ts/mod_services/classes.interprocesslock.js +141 -0
  64. package/dist_ts/mod_services/classes.serviceconfiguration.d.ts +15 -12
  65. package/dist_ts/mod_services/classes.serviceconfiguration.js +188 -91
  66. package/dist_ts/mod_services/classes.servicedatamarker.d.ts +32 -7
  67. package/dist_ts/mod_services/classes.servicedatamarker.js +145 -49
  68. package/dist_ts/mod_services/classes.servicemanager.d.ts +70 -28
  69. package/dist_ts/mod_services/classes.servicemanager.js +995 -421
  70. package/dist_ts/mod_services/classes.servicenames.d.ts +8 -0
  71. package/dist_ts/mod_services/classes.servicenames.js +54 -0
  72. package/dist_ts/mod_services/classes.serviceoptions.js +1 -1
  73. package/dist_ts/mod_services/classes.servicepruner.d.ts +3 -0
  74. package/dist_ts/mod_services/classes.servicepruner.js +208 -46
  75. package/dist_ts/mod_services/helpers.js +1 -1
  76. package/dist_ts/mod_services/index.d.ts +2 -0
  77. package/dist_ts/mod_services/index.js +81 -58
  78. package/dist_ts/mod_services/mod.plugins.js +1 -1
  79. package/dist_ts/mod_standard/index.js +2 -2
  80. package/dist_ts/mod_standard/mod.plugins.js +1 -1
  81. package/dist_ts/mod_start/index.js +1 -1
  82. package/dist_ts/mod_start/mod.plugins.js +1 -1
  83. package/dist_ts/mod_template/index.js +1 -1
  84. package/dist_ts/mod_template/mod.plugins.js +1 -1
  85. package/dist_ts/mod_tools/classes.packagemanager.js +1 -1
  86. package/dist_ts/mod_tools/index.js +1 -1
  87. package/dist_ts/mod_tools/mod.plugins.js +1 -1
  88. package/dist_ts/paths.js +1 -1
  89. package/dist_ts/plugins.d.ts +3 -1
  90. package/dist_ts/plugins.js +4 -2
  91. package/dist_ts_migration/001.service-selection.d.ts +9 -0
  92. package/dist_ts_migration/001.service-selection.js +51 -0
  93. package/dist_ts_migration/002.runtime-config.d.ts +19 -0
  94. package/dist_ts_migration/002.runtime-config.js +170 -0
  95. package/dist_ts_migration/003.service-data-marker.d.ts +9 -0
  96. package/dist_ts_migration/003.service-data-marker.js +156 -0
  97. package/dist_ts_migration/004.global-registry.d.ts +13 -0
  98. package/dist_ts_migration/004.global-registry.js +165 -0
  99. package/dist_ts_migration/index.d.ts +11 -0
  100. package/dist_ts_migration/index.js +59 -0
  101. package/package.json +6 -3
  102. package/readme.hints.md +102 -25
  103. package/readme.md +123 -28
  104. package/readme.plan.md +29 -16
  105. package/ts/00_commitinfo_data.ts +1 -1
  106. package/ts/helpers.smartconfig.ts +63 -10
  107. package/ts/mod_config/index.ts +1 -1
  108. package/ts/mod_docker/classes.dockerpruner.ts +6 -0
  109. package/ts/mod_services/classes.dockercontainer.ts +912 -96
  110. package/ts/mod_services/classes.globalregistry.ts +290 -89
  111. package/ts/mod_services/classes.globalregistrystore.ts +279 -0
  112. package/ts/mod_services/classes.interprocesslock.ts +189 -0
  113. package/ts/mod_services/classes.serviceconfiguration.ts +215 -108
  114. package/ts/mod_services/classes.servicedatamarker.ts +210 -52
  115. package/ts/mod_services/classes.servicemanager.ts +1332 -512
  116. package/ts/mod_services/classes.servicenames.ts +76 -0
  117. package/ts/mod_services/classes.servicepruner.ts +310 -52
  118. package/ts/mod_services/index.ts +89 -62
  119. package/ts/mod_standard/index.ts +1 -1
  120. package/ts/plugins.ts +4 -0
  121. package/ts_migration/001.service-selection.ts +77 -0
  122. package/ts_migration/002.runtime-config.ts +218 -0
  123. package/ts_migration/003.service-data-marker.ts +216 -0
  124. package/ts_migration/004.global-registry.ts +272 -0
  125. package/ts_migration/index.ts +83 -0
@@ -6,14 +6,20 @@
6
6
  * `@git.zone/tsdeploy` (`deriveRequiredCapabilities`), so the strings and the
7
7
  * array shape must never change here.
8
8
  */
9
- export type TServiceName = 'mongodb' | 'minio' | 'elasticsearch';
9
+ export type TServiceName = 'mongodb' | 'objectstorage' | 'elasticsearch';
10
+ export type TLegacyServiceName = 'minio';
10
11
  export declare const serviceNames: TServiceName[];
12
+ export declare const objectStorageImage = "code.foss.global/lossless.zone/objectstorage@sha256:43b60897eed17b54feb4e363808ce540ed1d9d27201871c5c8a869300d41e1b8";
11
13
  /** Owner recorded in markers and container labels. */
12
14
  export declare const serviceDataOwner = "@git.zone/cli";
13
15
  /** Value of the `git.zone.tool` label on containers created by `gitzone services`. */
14
16
  export declare const serviceToolLabel = "gitzone-services";
15
17
  /** Marker file name, stored in `<projectPath>/.nogit/`. */
16
18
  export declare const serviceDataMarkerFile = ".gitzone-services.json";
19
+ export declare const serviceDataMarkerSchemaVersion = 2;
20
+ export declare const legacyMinioDataDirectoryName = "miniodata";
21
+ export declare const serviceOperationLabel = "git.zone.operation";
22
+ export declare const serviceBindOperation = "service-bind-operation";
17
23
  /**
18
24
  * Directory basename used below `.nogit/` for each service's bind-mounted data.
19
25
  * This mapping is the only shape `gitzone services` ever creates, and prune
@@ -24,7 +30,7 @@ export declare const serviceDataDirectoryNames: {
24
30
  };
25
31
  /**
26
32
  * Image used to run each service, and therefore the image available to perform
27
- * a privileged cleanup of that service's data directory: if the data exists,
33
+ * a root-helper cleanup of that service's data directory: if the data exists,
28
34
  * the service ran, so its image is present locally.
29
35
  */
30
36
  export declare const serviceImages: {
@@ -37,22 +43,31 @@ export interface IServiceDataMarkerEntry {
37
43
  export interface IServiceDataMarker {
38
44
  owner: string;
39
45
  kind: 'service-data';
40
- schemaVersion: 1;
46
+ schemaVersion: 2;
41
47
  projectPath: string;
42
48
  safeToPrune: true;
43
49
  createdAt: string;
44
50
  dataDirectories: IServiceDataMarkerEntry[];
51
+ legacyDataDirectories?: ILegacyServiceDataMarkerEntry[];
52
+ }
53
+ export interface ILegacyServiceDataMarkerEntry {
54
+ service: TLegacyServiceName;
55
+ path: string;
56
+ safeToPrune: false;
45
57
  }
46
58
  /** Absolute path of the marker file for a project. */
47
59
  export declare const getServiceDataMarkerPath: (projectPathArg: string) => string;
60
+ export declare const getServiceDataMarkerLockPath: (projectPathArg: string) => string;
48
61
  /**
49
62
  * Expected absolute data directory path for a service in a project.
50
63
  * `mod_services` must always derive data paths through this helper so that
51
64
  * creation and reclamation agree on a single shape.
52
65
  */
53
66
  export declare const getServiceDataDirectory: (projectPathArg: string, serviceArg: TServiceName) => string;
67
+ /** Exact legacy path retained only for migration detection. */
68
+ export declare const getLegacyMinioDataDirectory: (projectPathArg: string) => string;
54
69
  /**
55
- * True only when `candidatePathArg` is exactly one of the three data
70
+ * True only when `candidatePathArg` is exactly one of the canonical data
56
71
  * directories `gitzone services` creates for `projectPathArg`.
57
72
  *
58
73
  * This is an allowlist, not a denylist: any path that is not byte-for-byte one
@@ -60,16 +75,17 @@ export declare const getServiceDataDirectory: (projectPathArg: string, serviceAr
60
75
  * before a recursive delete.
61
76
  */
62
77
  export declare const isSafeServiceDataPath: (projectPathArg: string, candidatePathArg: string) => boolean;
63
- /** Validate an unknown value as a usable marker. */
78
+ /** Validate an unknown value as a canonical v2 marker. */
64
79
  export declare const isValidServiceDataMarker: (markerArg: unknown) => markerArg is IServiceDataMarker;
65
80
  /** Read and validate the marker for a project, or undefined when absent/invalid. */
66
81
  export declare const readServiceDataMarker: (projectPathArg: string) => Promise<IServiceDataMarker | undefined>;
82
+ export declare const markerProvesServiceDataDirectory: (markerArg: IServiceDataMarker | undefined, projectPathArg: string, serviceArg: TServiceName) => boolean;
67
83
  /**
68
84
  * Record that `gitzone services` owns a data directory for this project.
69
85
  *
70
86
  * The marker lives in `.nogit/` rather than inside the bind-mounted data
71
- * directory itself, so it can never confuse mongod, MinIO or Elasticsearch at
72
- * runtime. Entries are merged so enabling a service later does not drop the
87
+ * directory itself, so it can never confuse a service runtime. Entries are
88
+ * merged so enabling a service later does not drop the
73
89
  * claim on previously created directories.
74
90
  */
75
91
  export declare const recordServiceDataDirectory: (projectPathArg: string, serviceArg: TServiceName) => Promise<void>;
@@ -87,7 +103,16 @@ export declare const getServiceContainerLabels: (optionsArg: {
87
103
  }) => {
88
104
  [key: string]: string;
89
105
  };
106
+ export declare const hasExactServiceContainerLabels: (labelsArg: Record<string, string>, expectedArg: Record<string, string>) => boolean;
90
107
  /** True when a container's labels prove `gitzone services` created it. */
91
108
  export declare const isServiceOwnedByLabels: (labelsArg: {
92
109
  [key: string]: string;
93
110
  }) => boolean;
111
+ /** Legacy provider evidence is retained for migration, never active lifecycle ownership. */
112
+ export declare const isLegacyMinioContainerLabels: (labelsArg: {
113
+ [key: string]: string;
114
+ }) => boolean;
115
+ /** Transient bind-operation containers are owned by their invocation, not by prune. */
116
+ export declare const isServiceOperationHelperLabels: (labelsArg: {
117
+ [key: string]: string;
118
+ }) => boolean;
@@ -1,11 +1,17 @@
1
1
  import * as plugins from './mod.plugins.js';
2
- export const serviceNames = ['mongodb', 'minio', 'elasticsearch'];
2
+ import { withInterProcessLock } from './classes.interprocesslock.js';
3
+ export const serviceNames = ['mongodb', 'objectstorage', 'elasticsearch'];
4
+ export const objectStorageImage = 'code.foss.global/lossless.zone/objectstorage@sha256:43b60897eed17b54feb4e363808ce540ed1d9d27201871c5c8a869300d41e1b8';
3
5
  /** Owner recorded in markers and container labels. */
4
6
  export const serviceDataOwner = '@git.zone/cli';
5
7
  /** Value of the `git.zone.tool` label on containers created by `gitzone services`. */
6
8
  export const serviceToolLabel = 'gitzone-services';
7
9
  /** Marker file name, stored in `<projectPath>/.nogit/`. */
8
10
  export const serviceDataMarkerFile = '.gitzone-services.json';
11
+ export const serviceDataMarkerSchemaVersion = 2;
12
+ export const legacyMinioDataDirectoryName = 'miniodata';
13
+ export const serviceOperationLabel = 'git.zone.operation';
14
+ export const serviceBindOperation = 'service-bind-operation';
9
15
  /**
10
16
  * Directory basename used below `.nogit/` for each service's bind-mounted data.
11
17
  * This mapping is the only shape `gitzone services` ever creates, and prune
@@ -13,26 +19,30 @@ export const serviceDataMarkerFile = '.gitzone-services.json';
13
19
  */
14
20
  export const serviceDataDirectoryNames = {
15
21
  mongodb: 'mongodata',
16
- minio: 'miniodata',
22
+ objectstorage: 'objectstoragedata',
17
23
  elasticsearch: 'esdata',
18
24
  };
19
25
  /**
20
26
  * Image used to run each service, and therefore the image available to perform
21
- * a privileged cleanup of that service's data directory: if the data exists,
27
+ * a root-helper cleanup of that service's data directory: if the data exists,
22
28
  * the service ran, so its image is present locally.
23
29
  */
24
30
  export const serviceImages = {
25
31
  mongodb: 'mongo:7.0',
26
- minio: 'minio/minio',
32
+ objectstorage: objectStorageImage,
27
33
  elasticsearch: 'elasticsearch:8.11.0',
28
34
  };
29
35
  const isPlainObject = (value) => {
30
36
  return typeof value === 'object' && value !== null && !Array.isArray(value);
31
37
  };
38
+ const hasOnlyKeys = (valueArg, allowedKeysArg) => Object.keys(valueArg).every((keyArg) => allowedKeysArg.includes(keyArg));
32
39
  /** Absolute path of the marker file for a project. */
33
40
  export const getServiceDataMarkerPath = (projectPathArg) => {
34
41
  return plugins.path.join(plugins.path.resolve(projectPathArg), '.nogit', serviceDataMarkerFile);
35
42
  };
43
+ export const getServiceDataMarkerLockPath = (projectPathArg) => {
44
+ return `${getServiceDataMarkerPath(projectPathArg)}.lock`;
45
+ };
36
46
  /**
37
47
  * Expected absolute data directory path for a service in a project.
38
48
  * `mod_services` must always derive data paths through this helper so that
@@ -41,8 +51,12 @@ export const getServiceDataMarkerPath = (projectPathArg) => {
41
51
  export const getServiceDataDirectory = (projectPathArg, serviceArg) => {
42
52
  return plugins.path.join(plugins.path.resolve(projectPathArg), '.nogit', serviceDataDirectoryNames[serviceArg]);
43
53
  };
54
+ /** Exact legacy path retained only for migration detection. */
55
+ export const getLegacyMinioDataDirectory = (projectPathArg) => {
56
+ return plugins.path.join(plugins.path.resolve(projectPathArg), '.nogit', legacyMinioDataDirectoryName);
57
+ };
44
58
  /**
45
- * True only when `candidatePathArg` is exactly one of the three data
59
+ * True only when `candidatePathArg` is exactly one of the canonical data
46
60
  * directories `gitzone services` creates for `projectPathArg`.
47
61
  *
48
62
  * This is an allowlist, not a denylist: any path that is not byte-for-byte one
@@ -58,31 +72,39 @@ export const isSafeServiceDataPath = (projectPathArg, candidatePathArg) => {
58
72
  }
59
73
  return false;
60
74
  };
61
- /** Validate an unknown value as a usable marker. */
75
+ const hasValidMarkerEnvelope = (markerArg) => {
76
+ return (markerArg.owner === serviceDataOwner &&
77
+ markerArg.kind === 'service-data' &&
78
+ markerArg.safeToPrune === true &&
79
+ typeof markerArg.projectPath === 'string' &&
80
+ markerArg.projectPath.length > 0 &&
81
+ typeof markerArg.createdAt === 'string' &&
82
+ markerArg.createdAt.length > 0 &&
83
+ Array.isArray(markerArg.dataDirectories));
84
+ };
85
+ /** Validate an unknown value as a canonical v2 marker. */
62
86
  export const isValidServiceDataMarker = (markerArg) => {
63
87
  if (!isPlainObject(markerArg)) {
64
88
  return false;
65
89
  }
66
- if (markerArg.owner !== serviceDataOwner) {
67
- return false;
68
- }
69
- if (markerArg.kind !== 'service-data') {
70
- return false;
71
- }
72
- if (markerArg.schemaVersion !== 1) {
73
- return false;
74
- }
75
- if (markerArg.safeToPrune !== true) {
76
- return false;
77
- }
78
- if (typeof markerArg.projectPath !== 'string' || !markerArg.projectPath) {
79
- return false;
80
- }
81
- if (!Array.isArray(markerArg.dataDirectories)) {
90
+ if (!hasOnlyKeys(markerArg, [
91
+ 'owner',
92
+ 'kind',
93
+ 'schemaVersion',
94
+ 'projectPath',
95
+ 'safeToPrune',
96
+ 'createdAt',
97
+ 'dataDirectories',
98
+ 'legacyDataDirectories',
99
+ ]) ||
100
+ !hasValidMarkerEnvelope(markerArg) ||
101
+ markerArg.schemaVersion !== serviceDataMarkerSchemaVersion) {
82
102
  return false;
83
103
  }
104
+ const projectPath = plugins.path.resolve(markerArg.projectPath);
105
+ const seenServices = new Set();
84
106
  for (const entry of markerArg.dataDirectories) {
85
- if (!isPlainObject(entry)) {
107
+ if (!isPlainObject(entry) || !hasOnlyKeys(entry, ['service', 'path'])) {
86
108
  return false;
87
109
  }
88
110
  if (!serviceNames.includes(entry.service)) {
@@ -91,9 +113,52 @@ export const isValidServiceDataMarker = (markerArg) => {
91
113
  if (typeof entry.path !== 'string' || !entry.path) {
92
114
  return false;
93
115
  }
116
+ if (seenServices.has(entry.service) ||
117
+ plugins.path.resolve(entry.path) !== getServiceDataDirectory(projectPath, entry.service)) {
118
+ return false;
119
+ }
120
+ seenServices.add(entry.service);
121
+ }
122
+ if (markerArg.legacyDataDirectories !== undefined) {
123
+ if (!Array.isArray(markerArg.legacyDataDirectories)) {
124
+ return false;
125
+ }
126
+ for (const entry of markerArg.legacyDataDirectories) {
127
+ if (!isPlainObject(entry) ||
128
+ !hasOnlyKeys(entry, ['service', 'path', 'safeToPrune']) ||
129
+ entry.service !== 'minio' ||
130
+ entry.safeToPrune !== false ||
131
+ typeof entry.path !== 'string' ||
132
+ plugins.path.resolve(entry.path) !== getLegacyMinioDataDirectory(projectPath)) {
133
+ return false;
134
+ }
135
+ }
136
+ if (markerArg.legacyDataDirectories.length > 1) {
137
+ return false;
138
+ }
94
139
  }
95
140
  return true;
96
141
  };
142
+ const writeServiceDataMarker = async (projectPathArg, markerArg) => {
143
+ const markerPath = getServiceDataMarkerPath(projectPathArg);
144
+ await plugins.smartfs.directory(plugins.path.dirname(markerPath)).recursive().create();
145
+ const tempPath = `${markerPath}.tmp-${process.pid}-${plugins.crypto
146
+ .randomBytes(12)
147
+ .toString('hex')}`;
148
+ try {
149
+ await plugins.fs.writeFile(tempPath, `${JSON.stringify(markerArg, null, 2)}\n`, {
150
+ encoding: 'utf8',
151
+ flag: 'wx',
152
+ });
153
+ await plugins.fs.rename(tempPath, markerPath);
154
+ }
155
+ catch (error) {
156
+ await plugins.fs.rm(tempPath, { force: true }).catch(() => { });
157
+ throw new Error(`Could not persist service data marker atomically: ${markerPath}`, {
158
+ cause: error,
159
+ });
160
+ }
161
+ };
97
162
  /** Read and validate the marker for a project, or undefined when absent/invalid. */
98
163
  export const readServiceDataMarker = async (projectPathArg) => {
99
164
  const markerPath = getServiceDataMarkerPath(projectPathArg);
@@ -103,44 +168,59 @@ export const readServiceDataMarker = async (projectPathArg) => {
103
168
  }
104
169
  const content = (await plugins.smartfs.file(markerPath).encoding('utf8').read());
105
170
  const parsed = JSON.parse(content);
106
- return isValidServiceDataMarker(parsed) ? parsed : undefined;
171
+ return isValidServiceDataMarker(parsed) &&
172
+ plugins.path.resolve(parsed.projectPath) === plugins.path.resolve(projectPathArg)
173
+ ? parsed
174
+ : undefined;
107
175
  }
108
176
  catch {
109
177
  return undefined;
110
178
  }
111
179
  };
180
+ export const markerProvesServiceDataDirectory = (markerArg, projectPathArg, serviceArg) => {
181
+ const expectedPath = getServiceDataDirectory(projectPathArg, serviceArg);
182
+ return !!markerArg?.dataDirectories.some((entry) => entry.service === serviceArg && plugins.path.resolve(entry.path) === expectedPath);
183
+ };
112
184
  /**
113
185
  * Record that `gitzone services` owns a data directory for this project.
114
186
  *
115
187
  * The marker lives in `.nogit/` rather than inside the bind-mounted data
116
- * directory itself, so it can never confuse mongod, MinIO or Elasticsearch at
117
- * runtime. Entries are merged so enabling a service later does not drop the
188
+ * directory itself, so it can never confuse a service runtime. Entries are
189
+ * merged so enabling a service later does not drop the
118
190
  * claim on previously created directories.
119
191
  */
120
192
  export const recordServiceDataDirectory = async (projectPathArg, serviceArg) => {
121
193
  const projectPath = plugins.path.resolve(projectPathArg);
122
- const dataPath = getServiceDataDirectory(projectPath, serviceArg);
123
- const existingMarker = await readServiceDataMarker(projectPath);
124
- const entries = existingMarker
125
- ? existingMarker.dataDirectories.filter((entry) => entry.service !== serviceArg)
126
- : [];
127
- entries.push({ service: serviceArg, path: dataPath });
128
- entries.sort((entryA, entryB) => entryA.service.localeCompare(entryB.service));
129
- const marker = {
130
- owner: serviceDataOwner,
131
- kind: 'service-data',
132
- schemaVersion: 1,
133
- projectPath,
134
- safeToPrune: true,
135
- createdAt: existingMarker?.createdAt || new Date().toISOString(),
136
- dataDirectories: entries,
137
- };
138
- const markerPath = getServiceDataMarkerPath(projectPath);
139
- await plugins.smartfs.directory(plugins.path.dirname(markerPath)).recursive().create();
140
- await plugins.smartfs
141
- .file(markerPath)
142
- .encoding('utf8')
143
- .write(`${JSON.stringify(marker, null, 2)}\n`);
194
+ await withInterProcessLock({
195
+ lockPath: getServiceDataMarkerLockPath(projectPath),
196
+ description: 'Service data marker',
197
+ }, async () => {
198
+ const dataPath = getServiceDataDirectory(projectPath, serviceArg);
199
+ const markerPath = getServiceDataMarkerPath(projectPath);
200
+ const markerExists = await plugins.smartfs.file(markerPath).exists();
201
+ const existingMarker = await readServiceDataMarker(projectPath);
202
+ if (markerExists && !existingMarker) {
203
+ throw new Error(`Refusing to overwrite invalid or foreign service data marker: ${markerPath}`);
204
+ }
205
+ const entries = existingMarker
206
+ ? existingMarker.dataDirectories.filter((entry) => entry.service !== serviceArg)
207
+ : [];
208
+ entries.push({ service: serviceArg, path: dataPath });
209
+ entries.sort((entryA, entryB) => entryA.service.localeCompare(entryB.service));
210
+ const marker = {
211
+ owner: serviceDataOwner,
212
+ kind: 'service-data',
213
+ schemaVersion: serviceDataMarkerSchemaVersion,
214
+ projectPath,
215
+ safeToPrune: true,
216
+ createdAt: existingMarker?.createdAt || new Date().toISOString(),
217
+ dataDirectories: entries,
218
+ ...(existingMarker?.legacyDataDirectories
219
+ ? { legacyDataDirectories: existingMarker.legacyDataDirectories }
220
+ : {}),
221
+ };
222
+ await writeServiceDataMarker(projectPath, marker);
223
+ });
144
224
  };
145
225
  /**
146
226
  * Labels applied to every container `gitzone services` creates.
@@ -158,9 +238,25 @@ export const getServiceContainerLabels = (optionsArg) => {
158
238
  'git.zone.safe-to-prune': 'true',
159
239
  };
160
240
  };
241
+ export const hasExactServiceContainerLabels = (labelsArg, expectedArg) => {
242
+ const actualGitZoneLabels = Object.entries(labelsArg).filter(([key]) => key.startsWith('git.zone.'));
243
+ const expectedGitZoneLabels = Object.entries(expectedArg).filter(([key]) => key.startsWith('git.zone.'));
244
+ return (actualGitZoneLabels.length === expectedGitZoneLabels.length &&
245
+ expectedGitZoneLabels.every(([key, value]) => labelsArg[key] === value));
246
+ };
161
247
  /** True when a container's labels prove `gitzone services` created it. */
162
248
  export const isServiceOwnedByLabels = (labelsArg) => {
163
249
  return (labelsArg['git.zone.tool'] === serviceToolLabel &&
164
250
  labelsArg['git.zone.safe-to-prune'] === 'true');
165
251
  };
166
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zZXJ2aWNlZGF0YW1hcmtlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9zZXJ2aWNlcy9jbGFzc2VzLnNlcnZpY2VkYXRhbWFya2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFZNUMsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFtQixDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUM7QUFFbEYsc0RBQXNEO0FBQ3RELE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLGVBQWUsQ0FBQztBQUVoRCxzRkFBc0Y7QUFDdEYsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsa0JBQWtCLENBQUM7QUFFbkQsMkRBQTJEO0FBQzNELE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLHdCQUF3QixDQUFDO0FBRTlEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBc0M7SUFDMUUsT0FBTyxFQUFFLFdBQVc7SUFDcEIsS0FBSyxFQUFFLFdBQVc7SUFDbEIsYUFBYSxFQUFFLFFBQVE7Q0FDeEIsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQXNDO0lBQzlELE9BQU8sRUFBRSxXQUFXO0lBQ3BCLEtBQUssRUFBRSxhQUFhO0lBQ3BCLGFBQWEsRUFBRSxzQkFBc0I7Q0FDdEMsQ0FBQztBQWlCRixNQUFNLGFBQWEsR0FBRyxDQUFDLEtBQWMsRUFBZ0MsRUFBRTtJQUNyRSxPQUFPLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUM5RSxDQUFDLENBQUM7QUFFRixzREFBc0Q7QUFDdEQsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsQ0FBQyxjQUFzQixFQUFVLEVBQUU7SUFD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
252
+ /** Legacy provider evidence is retained for migration, never active lifecycle ownership. */
253
+ export const isLegacyMinioContainerLabels = (labelsArg) => {
254
+ return (labelsArg['git.zone.tool'] === serviceToolLabel &&
255
+ labelsArg['git.zone.service'] === 'minio');
256
+ };
257
+ /** Transient bind-operation containers are owned by their invocation, not by prune. */
258
+ export const isServiceOperationHelperLabels = (labelsArg) => {
259
+ return (labelsArg['git.zone.tool'] === serviceToolLabel &&
260
+ labelsArg[serviceOperationLabel] === serviceBindOperation);
261
+ };
262
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zZXJ2aWNlZGF0YW1hcmtlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21vZF9zZXJ2aWNlcy9jbGFzc2VzLnNlcnZpY2VkYXRhbWFya2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFhckUsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFtQixDQUFDLFNBQVMsRUFBRSxlQUFlLEVBQUUsZUFBZSxDQUFDLENBQUM7QUFFMUYsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQzdCLHNIQUFzSCxDQUFDO0FBRXpILHNEQUFzRDtBQUN0RCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxlQUFlLENBQUM7QUFFaEQsc0ZBQXNGO0FBQ3RGLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLGtCQUFrQixDQUFDO0FBRW5ELDJEQUEyRDtBQUMzRCxNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBRyx3QkFBd0IsQ0FBQztBQUM5RCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxDQUFDLENBQUM7QUFDaEQsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQUcsV0FBVyxDQUFDO0FBQ3hELE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLG9CQUFvQixDQUFDO0FBQzFELE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLHdCQUF3QixDQUFDO0FBRTdEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBc0M7SUFDMUUsT0FBTyxFQUFFLFdBQVc7SUFDcEIsYUFBYSxFQUFFLG1CQUFtQjtJQUNsQyxhQUFhLEVBQUUsUUFBUTtDQUN4QixDQUFDO0FBRUY7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBc0M7SUFDOUQsT0FBTyxFQUFFLFdBQVc7SUFDcEIs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
@@ -1,6 +1,7 @@
1
+ import * as plugins from './mod.plugins.js';
1
2
  import { ServiceConfiguration } from './classes.serviceconfiguration.js';
2
- import { DockerContainer } from './classes.dockercontainer.js';
3
3
  import { type TServiceName } from './classes.servicedatamarker.js';
4
+ import { type TServiceMigrationScope } from '../../dist_ts_migration/index.js';
4
5
  import type { ContainerStatus } from './classes.dockercontainer.js';
5
6
  import type { TServiceOptionSource } from './classes.serviceoptions.js';
6
7
  export interface IServiceStatus {
@@ -18,7 +19,12 @@ export interface IServiceStatus {
18
19
  authEnabled?: boolean;
19
20
  /** MongoDB only: whether the auth mode is declared in committed config. */
20
21
  authSource?: TServiceOptionSource;
22
+ /** ObjectStorage only: an unowned same-name container is a conflict, not managed state. */
23
+ ownership?: 'owned' | 'conflict' | 'absent';
24
+ managementUi?: string;
21
25
  }
26
+ export type TServiceManagerOperation = 'auth' | 'clean' | 'compass' | 'config' | 'logs' | 'remove' | 'reconfigure' | 'restart' | 'start' | 'status' | 'stop';
27
+ export declare const getServiceManagerMigrationScopes: (operationArg: TServiceManagerOperation) => TServiceMigrationScope[];
22
28
  export interface IServicesStatus {
23
29
  project: {
24
30
  name: string;
@@ -27,9 +33,14 @@ export interface IServicesStatus {
27
33
  };
28
34
  services: {
29
35
  mongodb: IServiceStatus;
30
- minio: IServiceStatus;
36
+ objectstorage: IServiceStatus;
31
37
  elasticsearch: IServiceStatus;
32
38
  };
39
+ legacy: {
40
+ minioContainer?: string;
41
+ minioDataDirectory: string;
42
+ minioDataExists: boolean;
43
+ };
33
44
  totalDataBytes: number;
34
45
  }
35
46
  export declare const mongoServiceUlimits: {
@@ -38,33 +49,47 @@ export declare const mongoServiceUlimits: {
38
49
  readonly hard: 65536;
39
50
  };
40
51
  };
41
- export declare const minioReconciliationTimeoutMs = 30000;
42
- export interface IMinioReconciliationClock {
52
+ export declare const objectStorageReconciliationTimeoutMs = 30000;
53
+ export interface IObjectStorageReconciliationClock {
43
54
  now(): number;
44
55
  delay(millisecondsArg: number): Promise<void>;
45
56
  }
46
- export interface IMinioReconciliationOptions {
47
- docker: Pick<DockerContainer, 'execArgv'>;
48
- /** Full immutable Docker container id; mutable names are never accepted here. */
49
- containerId: string;
57
+ export interface IObjectStorageBucketReconciliationOptions {
58
+ smartBucket: Pick<plugins.smartbucket.SmartBucket, 'checkReadiness' | 'close'> & {
59
+ storageClient: {
60
+ send(commandArg: plugins.s3.HeadBucketCommand | plugins.s3.CreateBucketCommand, optionsArg?: {
61
+ abortSignal?: AbortSignal;
62
+ }): Promise<unknown>;
63
+ };
64
+ };
50
65
  bucket: string;
51
- /** Shared absolute deadline. Defaults to 30 seconds from this call. */
52
66
  deadlineAt?: number;
53
- clock?: IMinioReconciliationClock;
67
+ clock?: IObjectStorageReconciliationClock;
54
68
  }
55
- /** Converge a running MinIO container to an authenticated, verified bucket. */
56
- export declare const reconcileMinioBucket: (optionsArg: IMinioReconciliationOptions) => Promise<void>;
69
+ export declare const objectStorageExpectedEntrypoint: string[];
70
+ export declare const objectStorageExpectedCommand: string[];
71
+ export declare const objectStorageExpectedHealthcheck: {
72
+ test: string[];
73
+ interval: number;
74
+ timeout: number;
75
+ startPeriod: number;
76
+ retries: number;
77
+ };
78
+ /** Create the configured bucket only after an authenticated 404, then verify by HEAD. */
79
+ export declare const reconcileObjectStorageBucket: (optionsArg: IObjectStorageBucketReconciliationOptions) => Promise<void>;
57
80
  export declare class ServiceManager {
58
81
  private config;
59
82
  private docker;
60
83
  private enabledServices;
61
84
  private globalRegistry;
62
- private minioClock;
85
+ private objectStorageClock;
86
+ private fetchImplementation;
87
+ private smartBucketFactory;
63
88
  constructor();
64
89
  /**
65
90
  * Initialize the service manager
66
91
  */
67
- init(): Promise<void>;
92
+ init(operationArg: TServiceManagerOperation): Promise<void>;
68
93
  /**
69
94
  * Human phrasing for where the auth mode came from. A declared auth-off is
70
95
  * committed to the repository and therefore affects everyone who clones it,
@@ -86,7 +111,7 @@ export declare class ServiceManager {
86
111
  /**
87
112
  * Check if a service is enabled
88
113
  */
89
- private isServiceEnabled;
114
+ isServiceEnabled(service: TServiceName): boolean;
90
115
  /**
91
116
  * Prepare a service's data directory: create it and record tool ownership.
92
117
  *
@@ -94,6 +119,7 @@ export declare class ServiceManager {
94
119
  * prove the directory is reclaimable instead of guessing from its path.
95
120
  */
96
121
  private prepareDataDirectory;
122
+ private createDataDirectory;
97
123
  /**
98
124
  * Register this project with the global registry.
99
125
  *
@@ -124,6 +150,8 @@ export declare class ServiceManager {
124
150
  * (localhost:MONGODB_PORT) is reachable from host and container alike.
125
151
  */
126
152
  startMongoDB(): Promise<void>;
153
+ private assertNoPendingDataDeletion;
154
+ private startMongoDBLocked;
127
155
  /**
128
156
  * Create the MongoDB container as a single-node replica set
129
157
  */
@@ -150,31 +178,42 @@ export declare class ServiceManager {
150
178
  * Initiate the single-node replica set once mongod is reachable
151
179
  */
152
180
  private ensureMongoReplicaSetInitiated;
153
- private inspectMinioContainer;
154
- private assertMinioOwnership;
155
- private revalidateMinioContainer;
156
- /**
157
- * Start MinIO service
158
- */
159
- startMinIO(): Promise<void>;
160
- /** Create stopped MinIO, inspect its id, and prove exact current labels. */
161
- private createMinioContainer;
181
+ private inspectObjectStorageContainer;
182
+ private inspectObjectStorageIdentity;
183
+ private discoverContainerIdByExactName;
184
+ private assertObjectStorageOwnership;
185
+ private revalidateObjectStorageContainer;
186
+ private objectStorageConfigurationMatches;
187
+ private assertNoSymlinkedObjectStoragePath;
188
+ private prepareObjectStorageDataDirectory;
189
+ private repairObjectStorageMarkerFromOwnedContainer;
190
+ private assertNoLegacyMinioResources;
191
+ /** Read-only preflight used before restart or reconfigure may stop anything. */
192
+ assertObjectStorageStartupAllowed(): Promise<void>;
193
+ private assertObjectStorageConfigurationSafe;
194
+ private waitForObjectStorageReadiness;
195
+ private createObjectStorageContainer;
196
+ /** Start or reconcile the canonical ObjectStorage service. */
197
+ startObjectStorage(): Promise<void>;
198
+ private startObjectStorageLocked;
162
199
  /**
163
200
  * Start Elasticsearch service
164
201
  */
165
202
  startElasticsearch(): Promise<void>;
203
+ private startElasticsearchLocked;
166
204
  /**
167
205
  * Stop MongoDB service
168
206
  */
169
207
  stopMongoDB(): Promise<void>;
170
- /**
171
- * Stop MinIO service
172
- */
173
- stopMinIO(): Promise<void>;
208
+ private stopMongoDBLocked;
209
+ /** Stop only an exactly owned canonical ObjectStorage container. */
210
+ stopObjectStorage(): Promise<void>;
211
+ private stopObjectStorageLocked;
174
212
  /**
175
213
  * Stop Elasticsearch service
176
214
  */
177
215
  stopElasticsearch(): Promise<void>;
216
+ private stopElasticsearchLocked;
178
217
  /**
179
218
  * Collect service status as structured data.
180
219
  *
@@ -210,6 +249,8 @@ export declare class ServiceManager {
210
249
  * Remove containers
211
250
  */
212
251
  removeContainers(): Promise<void>;
252
+ private removeContainersLocked;
253
+ private withProjectServiceLocks;
213
254
  /**
214
255
  * Clean data directories
215
256
  */
@@ -222,4 +263,5 @@ export declare class ServiceManager {
222
263
  * Reconfigure services with new ports
223
264
  */
224
265
  reconfigure(): Promise<void>;
266
+ private reconfigureLocked;
225
267
  }