@git.zone/cli 3.2.1 → 5.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.
- package/.smartconfig.json +0 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.gitzoneconfig.js +1 -1
- package/dist_ts/classes.project.js +1 -1
- package/dist_ts/gitzone.cli.js +1 -1
- package/dist_ts/gitzone.logging.js +1 -1
- package/dist_ts/helpers.changelog.js +1 -1
- package/dist_ts/helpers.climode.js +1 -1
- package/dist_ts/helpers.smartconfig.js +61 -11
- package/dist_ts/helpers.smartconfigmigrations.js +1 -1
- package/dist_ts/helpers.tsdocker.js +1 -1
- package/dist_ts/helpers.workflow.d.ts +0 -2
- package/dist_ts/helpers.workflow.js +23 -12
- package/dist_ts/index.js +1 -1
- package/dist_ts/mod_audit/index.js +1 -1
- package/dist_ts/mod_commit/index.js +1 -1
- package/dist_ts/mod_commit/mod.helpers.d.ts +3 -3
- package/dist_ts/mod_commit/mod.helpers.js +10 -10
- package/dist_ts/mod_commit/mod.plugins.js +1 -1
- package/dist_ts/mod_commit/mod.ui.js +1 -1
- package/dist_ts/mod_config/classes.commitconfig.js +1 -1
- package/dist_ts/mod_config/classes.releaseconfig.js +1 -1
- package/dist_ts/mod_config/index.js +23 -24
- package/dist_ts/mod_config/mod.plugins.js +1 -1
- package/dist_ts/mod_deprecate/index.js +1 -1
- package/dist_ts/mod_deprecate/mod.plugins.js +1 -1
- package/dist_ts/mod_docker/classes.dockerpruner.js +7 -1
- package/dist_ts/mod_docker/index.js +1 -1
- package/dist_ts/mod_docker/mod.plugins.js +1 -1
- package/dist_ts/mod_format/classes.baseformatter.js +1 -1
- package/dist_ts/mod_format/classes.diffreporter.js +1 -1
- package/dist_ts/mod_format/classes.formatcontext.js +1 -1
- package/dist_ts/mod_format/classes.formatplanner.js +1 -1
- package/dist_ts/mod_format/classes.formatstats.js +1 -1
- package/dist_ts/mod_format/formatters/assets.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/cleanup.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/copy.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/gitignore.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/license.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/packagejson.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/prettier.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/readme.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/smartconfig.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/templates.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/tsconfig.formatter.js +1 -1
- package/dist_ts/mod_format/index.js +1 -1
- package/dist_ts/mod_format/interfaces.format.js +1 -1
- package/dist_ts/mod_format/mod.plugins.js +1 -1
- package/dist_ts/mod_helpers/index.js +1 -1
- package/dist_ts/mod_helpers/mod.plugins.js +1 -1
- package/dist_ts/mod_meta/index.js +1 -1
- package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
- package/dist_ts/mod_meta/meta.interfaces.js +1 -1
- package/dist_ts/mod_meta/meta.plugins.js +1 -1
- package/dist_ts/mod_open/index.js +1 -1
- package/dist_ts/mod_open/mod.plugins.js +1 -1
- package/dist_ts/mod_release/helpers.releasebranch.d.ts +46 -0
- package/dist_ts/mod_release/helpers.releasebranch.js +579 -0
- package/dist_ts/mod_release/index.d.ts +10 -0
- package/dist_ts/mod_release/index.js +300 -92
- package/dist_ts/mod_release/mod.plugins.js +1 -1
- package/dist_ts/mod_services/classes.dockercontainer.d.ts +50 -3
- package/dist_ts/mod_services/classes.dockercontainer.js +646 -84
- package/dist_ts/mod_services/classes.globalregistry.d.ts +10 -24
- package/dist_ts/mod_services/classes.globalregistry.js +210 -62
- package/dist_ts/mod_services/classes.globalregistrystore.d.ts +48 -0
- package/dist_ts/mod_services/classes.globalregistrystore.js +156 -0
- package/dist_ts/mod_services/classes.interprocesslock.d.ts +21 -0
- package/dist_ts/mod_services/classes.interprocesslock.js +141 -0
- package/dist_ts/mod_services/classes.serviceconfiguration.d.ts +15 -12
- package/dist_ts/mod_services/classes.serviceconfiguration.js +188 -91
- package/dist_ts/mod_services/classes.servicedatamarker.d.ts +32 -7
- package/dist_ts/mod_services/classes.servicedatamarker.js +145 -49
- package/dist_ts/mod_services/classes.servicemanager.d.ts +70 -28
- package/dist_ts/mod_services/classes.servicemanager.js +995 -421
- package/dist_ts/mod_services/classes.servicenames.d.ts +8 -0
- package/dist_ts/mod_services/classes.servicenames.js +54 -0
- package/dist_ts/mod_services/classes.serviceoptions.js +1 -1
- package/dist_ts/mod_services/classes.servicepruner.d.ts +3 -0
- package/dist_ts/mod_services/classes.servicepruner.js +208 -46
- package/dist_ts/mod_services/helpers.js +1 -1
- package/dist_ts/mod_services/index.d.ts +2 -0
- package/dist_ts/mod_services/index.js +81 -58
- package/dist_ts/mod_services/mod.plugins.js +1 -1
- package/dist_ts/mod_standard/index.js +2 -2
- package/dist_ts/mod_standard/mod.plugins.js +1 -1
- package/dist_ts/mod_start/index.js +1 -1
- package/dist_ts/mod_start/mod.plugins.js +1 -1
- package/dist_ts/mod_template/index.js +1 -1
- package/dist_ts/mod_template/mod.plugins.js +1 -1
- package/dist_ts/mod_tools/classes.packagemanager.js +1 -1
- package/dist_ts/mod_tools/index.js +1 -1
- package/dist_ts/mod_tools/mod.plugins.js +1 -1
- package/dist_ts/paths.js +1 -1
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +4 -2
- package/dist_ts_migration/001.service-selection.d.ts +9 -0
- package/dist_ts_migration/001.service-selection.js +51 -0
- package/dist_ts_migration/002.runtime-config.d.ts +19 -0
- package/dist_ts_migration/002.runtime-config.js +170 -0
- package/dist_ts_migration/003.service-data-marker.d.ts +9 -0
- package/dist_ts_migration/003.service-data-marker.js +156 -0
- package/dist_ts_migration/004.global-registry.d.ts +13 -0
- package/dist_ts_migration/004.global-registry.js +165 -0
- package/dist_ts_migration/index.d.ts +11 -0
- package/dist_ts_migration/index.js +59 -0
- package/package.json +6 -3
- package/readme.hints.md +102 -25
- package/readme.md +144 -41
- package/readme.plan.md +29 -16
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/helpers.smartconfig.ts +63 -10
- package/ts/helpers.workflow.ts +41 -12
- package/ts/mod_commit/mod.helpers.ts +12 -8
- package/ts/mod_config/index.ts +22 -23
- package/ts/mod_docker/classes.dockerpruner.ts +6 -0
- package/ts/mod_release/helpers.releasebranch.ts +1282 -0
- package/ts/mod_release/index.ts +608 -129
- package/ts/mod_services/classes.dockercontainer.ts +912 -96
- package/ts/mod_services/classes.globalregistry.ts +290 -89
- package/ts/mod_services/classes.globalregistrystore.ts +279 -0
- package/ts/mod_services/classes.interprocesslock.ts +189 -0
- package/ts/mod_services/classes.serviceconfiguration.ts +215 -108
- package/ts/mod_services/classes.servicedatamarker.ts +210 -52
- package/ts/mod_services/classes.servicemanager.ts +1332 -512
- package/ts/mod_services/classes.servicenames.ts +76 -0
- package/ts/mod_services/classes.servicepruner.ts +310 -52
- package/ts/mod_services/index.ts +89 -62
- package/ts/mod_standard/index.ts +1 -1
- package/ts/plugins.ts +4 -0
- package/ts_migration/001.service-selection.ts +77 -0
- package/ts_migration/002.runtime-config.ts +218 -0
- package/ts_migration/003.service-data-marker.ts +216 -0
- package/ts_migration/004.global-registry.ts +272 -0
- package/ts_migration/index.ts +83 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as plugins from './mod.plugins.js';
|
|
2
|
+
import { withInterProcessLock } from './classes.interprocesslock.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Canonical service identifiers used across mod_services.
|
|
@@ -8,9 +9,13 @@ import * as plugins from './mod.plugins.js';
|
|
|
8
9
|
* `@git.zone/tsdeploy` (`deriveRequiredCapabilities`), so the strings and the
|
|
9
10
|
* array shape must never change here.
|
|
10
11
|
*/
|
|
11
|
-
export type TServiceName = 'mongodb' | '
|
|
12
|
+
export type TServiceName = 'mongodb' | 'objectstorage' | 'elasticsearch';
|
|
13
|
+
export type TLegacyServiceName = 'minio';
|
|
12
14
|
|
|
13
|
-
export const serviceNames: TServiceName[] = ['mongodb', '
|
|
15
|
+
export const serviceNames: TServiceName[] = ['mongodb', 'objectstorage', 'elasticsearch'];
|
|
16
|
+
|
|
17
|
+
export const objectStorageImage =
|
|
18
|
+
'code.foss.global/lossless.zone/objectstorage@sha256:43b60897eed17b54feb4e363808ce540ed1d9d27201871c5c8a869300d41e1b8';
|
|
14
19
|
|
|
15
20
|
/** Owner recorded in markers and container labels. */
|
|
16
21
|
export const serviceDataOwner = '@git.zone/cli';
|
|
@@ -20,6 +25,10 @@ export const serviceToolLabel = 'gitzone-services';
|
|
|
20
25
|
|
|
21
26
|
/** Marker file name, stored in `<projectPath>/.nogit/`. */
|
|
22
27
|
export const serviceDataMarkerFile = '.gitzone-services.json';
|
|
28
|
+
export const serviceDataMarkerSchemaVersion = 2;
|
|
29
|
+
export const legacyMinioDataDirectoryName = 'miniodata';
|
|
30
|
+
export const serviceOperationLabel = 'git.zone.operation';
|
|
31
|
+
export const serviceBindOperation = 'service-bind-operation';
|
|
23
32
|
|
|
24
33
|
/**
|
|
25
34
|
* Directory basename used below `.nogit/` for each service's bind-mounted data.
|
|
@@ -28,18 +37,18 @@ export const serviceDataMarkerFile = '.gitzone-services.json';
|
|
|
28
37
|
*/
|
|
29
38
|
export const serviceDataDirectoryNames: { [key in TServiceName]: string } = {
|
|
30
39
|
mongodb: 'mongodata',
|
|
31
|
-
|
|
40
|
+
objectstorage: 'objectstoragedata',
|
|
32
41
|
elasticsearch: 'esdata',
|
|
33
42
|
};
|
|
34
43
|
|
|
35
44
|
/**
|
|
36
45
|
* Image used to run each service, and therefore the image available to perform
|
|
37
|
-
* a
|
|
46
|
+
* a root-helper cleanup of that service's data directory: if the data exists,
|
|
38
47
|
* the service ran, so its image is present locally.
|
|
39
48
|
*/
|
|
40
49
|
export const serviceImages: { [key in TServiceName]: string } = {
|
|
41
50
|
mongodb: 'mongo:7.0',
|
|
42
|
-
|
|
51
|
+
objectstorage: objectStorageImage,
|
|
43
52
|
elasticsearch: 'elasticsearch:8.11.0',
|
|
44
53
|
};
|
|
45
54
|
|
|
@@ -51,22 +60,36 @@ export interface IServiceDataMarkerEntry {
|
|
|
51
60
|
export interface IServiceDataMarker {
|
|
52
61
|
owner: string;
|
|
53
62
|
kind: 'service-data';
|
|
54
|
-
schemaVersion:
|
|
63
|
+
schemaVersion: 2;
|
|
55
64
|
projectPath: string;
|
|
56
65
|
safeToPrune: true;
|
|
57
66
|
createdAt: string;
|
|
58
67
|
dataDirectories: IServiceDataMarkerEntry[];
|
|
68
|
+
legacyDataDirectories?: ILegacyServiceDataMarkerEntry[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ILegacyServiceDataMarkerEntry {
|
|
72
|
+
service: TLegacyServiceName;
|
|
73
|
+
path: string;
|
|
74
|
+
safeToPrune: false;
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
|
62
78
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
63
79
|
};
|
|
64
80
|
|
|
81
|
+
const hasOnlyKeys = (valueArg: Record<string, any>, allowedKeysArg: readonly string[]): boolean =>
|
|
82
|
+
Object.keys(valueArg).every((keyArg) => allowedKeysArg.includes(keyArg));
|
|
83
|
+
|
|
65
84
|
/** Absolute path of the marker file for a project. */
|
|
66
85
|
export const getServiceDataMarkerPath = (projectPathArg: string): string => {
|
|
67
86
|
return plugins.path.join(plugins.path.resolve(projectPathArg), '.nogit', serviceDataMarkerFile);
|
|
68
87
|
};
|
|
69
88
|
|
|
89
|
+
export const getServiceDataMarkerLockPath = (projectPathArg: string): string => {
|
|
90
|
+
return `${getServiceDataMarkerPath(projectPathArg)}.lock`;
|
|
91
|
+
};
|
|
92
|
+
|
|
70
93
|
/**
|
|
71
94
|
* Expected absolute data directory path for a service in a project.
|
|
72
95
|
* `mod_services` must always derive data paths through this helper so that
|
|
@@ -83,8 +106,17 @@ export const getServiceDataDirectory = (
|
|
|
83
106
|
);
|
|
84
107
|
};
|
|
85
108
|
|
|
109
|
+
/** Exact legacy path retained only for migration detection. */
|
|
110
|
+
export const getLegacyMinioDataDirectory = (projectPathArg: string): string => {
|
|
111
|
+
return plugins.path.join(
|
|
112
|
+
plugins.path.resolve(projectPathArg),
|
|
113
|
+
'.nogit',
|
|
114
|
+
legacyMinioDataDirectoryName,
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
86
118
|
/**
|
|
87
|
-
* True only when `candidatePathArg` is exactly one of the
|
|
119
|
+
* True only when `candidatePathArg` is exactly one of the canonical data
|
|
88
120
|
* directories `gitzone services` creates for `projectPathArg`.
|
|
89
121
|
*
|
|
90
122
|
* This is an allowlist, not a denylist: any path that is not byte-for-byte one
|
|
@@ -104,31 +136,44 @@ export const isSafeServiceDataPath = (
|
|
|
104
136
|
return false;
|
|
105
137
|
};
|
|
106
138
|
|
|
107
|
-
|
|
139
|
+
const hasValidMarkerEnvelope = (markerArg: Record<string, any>): boolean => {
|
|
140
|
+
return (
|
|
141
|
+
markerArg.owner === serviceDataOwner &&
|
|
142
|
+
markerArg.kind === 'service-data' &&
|
|
143
|
+
markerArg.safeToPrune === true &&
|
|
144
|
+
typeof markerArg.projectPath === 'string' &&
|
|
145
|
+
markerArg.projectPath.length > 0 &&
|
|
146
|
+
typeof markerArg.createdAt === 'string' &&
|
|
147
|
+
markerArg.createdAt.length > 0 &&
|
|
148
|
+
Array.isArray(markerArg.dataDirectories)
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** Validate an unknown value as a canonical v2 marker. */
|
|
108
153
|
export const isValidServiceDataMarker = (markerArg: unknown): markerArg is IServiceDataMarker => {
|
|
109
154
|
if (!isPlainObject(markerArg)) {
|
|
110
155
|
return false;
|
|
111
156
|
}
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
if (!Array.isArray(markerArg.dataDirectories)) {
|
|
157
|
+
if (
|
|
158
|
+
!hasOnlyKeys(markerArg, [
|
|
159
|
+
'owner',
|
|
160
|
+
'kind',
|
|
161
|
+
'schemaVersion',
|
|
162
|
+
'projectPath',
|
|
163
|
+
'safeToPrune',
|
|
164
|
+
'createdAt',
|
|
165
|
+
'dataDirectories',
|
|
166
|
+
'legacyDataDirectories',
|
|
167
|
+
]) ||
|
|
168
|
+
!hasValidMarkerEnvelope(markerArg) ||
|
|
169
|
+
markerArg.schemaVersion !== serviceDataMarkerSchemaVersion
|
|
170
|
+
) {
|
|
128
171
|
return false;
|
|
129
172
|
}
|
|
173
|
+
const projectPath = plugins.path.resolve(markerArg.projectPath);
|
|
174
|
+
const seenServices = new Set<TServiceName>();
|
|
130
175
|
for (const entry of markerArg.dataDirectories) {
|
|
131
|
-
if (!isPlainObject(entry)) {
|
|
176
|
+
if (!isPlainObject(entry) || !hasOnlyKeys(entry, ['service', 'path'])) {
|
|
132
177
|
return false;
|
|
133
178
|
}
|
|
134
179
|
if (!serviceNames.includes(entry.service)) {
|
|
@@ -137,10 +182,60 @@ export const isValidServiceDataMarker = (markerArg: unknown): markerArg is IServ
|
|
|
137
182
|
if (typeof entry.path !== 'string' || !entry.path) {
|
|
138
183
|
return false;
|
|
139
184
|
}
|
|
185
|
+
if (
|
|
186
|
+
seenServices.has(entry.service) ||
|
|
187
|
+
plugins.path.resolve(entry.path) !== getServiceDataDirectory(projectPath, entry.service)
|
|
188
|
+
) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
seenServices.add(entry.service);
|
|
192
|
+
}
|
|
193
|
+
if (markerArg.legacyDataDirectories !== undefined) {
|
|
194
|
+
if (!Array.isArray(markerArg.legacyDataDirectories)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
for (const entry of markerArg.legacyDataDirectories) {
|
|
198
|
+
if (
|
|
199
|
+
!isPlainObject(entry) ||
|
|
200
|
+
!hasOnlyKeys(entry, ['service', 'path', 'safeToPrune']) ||
|
|
201
|
+
entry.service !== 'minio' ||
|
|
202
|
+
entry.safeToPrune !== false ||
|
|
203
|
+
typeof entry.path !== 'string' ||
|
|
204
|
+
plugins.path.resolve(entry.path) !== getLegacyMinioDataDirectory(projectPath)
|
|
205
|
+
) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (markerArg.legacyDataDirectories.length > 1) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
140
212
|
}
|
|
141
213
|
return true;
|
|
142
214
|
};
|
|
143
215
|
|
|
216
|
+
const writeServiceDataMarker = async (
|
|
217
|
+
projectPathArg: string,
|
|
218
|
+
markerArg: IServiceDataMarker,
|
|
219
|
+
): Promise<void> => {
|
|
220
|
+
const markerPath = getServiceDataMarkerPath(projectPathArg);
|
|
221
|
+
await plugins.smartfs.directory(plugins.path.dirname(markerPath)).recursive().create();
|
|
222
|
+
const tempPath = `${markerPath}.tmp-${process.pid}-${plugins.crypto
|
|
223
|
+
.randomBytes(12)
|
|
224
|
+
.toString('hex')}`;
|
|
225
|
+
try {
|
|
226
|
+
await plugins.fs.writeFile(tempPath, `${JSON.stringify(markerArg, null, 2)}\n`, {
|
|
227
|
+
encoding: 'utf8',
|
|
228
|
+
flag: 'wx',
|
|
229
|
+
});
|
|
230
|
+
await plugins.fs.rename(tempPath, markerPath);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
await plugins.fs.rm(tempPath, { force: true }).catch(() => {});
|
|
233
|
+
throw new Error(`Could not persist service data marker atomically: ${markerPath}`, {
|
|
234
|
+
cause: error,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
144
239
|
/** Read and validate the marker for a project, or undefined when absent/invalid. */
|
|
145
240
|
export const readServiceDataMarker = async (
|
|
146
241
|
projectPathArg: string,
|
|
@@ -152,18 +247,32 @@ export const readServiceDataMarker = async (
|
|
|
152
247
|
}
|
|
153
248
|
const content = (await plugins.smartfs.file(markerPath).encoding('utf8').read()) as string;
|
|
154
249
|
const parsed = JSON.parse(content);
|
|
155
|
-
return isValidServiceDataMarker(parsed)
|
|
250
|
+
return isValidServiceDataMarker(parsed) &&
|
|
251
|
+
plugins.path.resolve(parsed.projectPath) === plugins.path.resolve(projectPathArg)
|
|
252
|
+
? parsed
|
|
253
|
+
: undefined;
|
|
156
254
|
} catch {
|
|
157
255
|
return undefined;
|
|
158
256
|
}
|
|
159
257
|
};
|
|
160
258
|
|
|
259
|
+
export const markerProvesServiceDataDirectory = (
|
|
260
|
+
markerArg: IServiceDataMarker | undefined,
|
|
261
|
+
projectPathArg: string,
|
|
262
|
+
serviceArg: TServiceName,
|
|
263
|
+
): boolean => {
|
|
264
|
+
const expectedPath = getServiceDataDirectory(projectPathArg, serviceArg);
|
|
265
|
+
return !!markerArg?.dataDirectories.some(
|
|
266
|
+
(entry) => entry.service === serviceArg && plugins.path.resolve(entry.path) === expectedPath,
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
|
|
161
270
|
/**
|
|
162
271
|
* Record that `gitzone services` owns a data directory for this project.
|
|
163
272
|
*
|
|
164
273
|
* The marker lives in `.nogit/` rather than inside the bind-mounted data
|
|
165
|
-
* directory itself, so it can never confuse
|
|
166
|
-
*
|
|
274
|
+
* directory itself, so it can never confuse a service runtime. Entries are
|
|
275
|
+
* merged so enabling a service later does not drop the
|
|
167
276
|
* claim on previously created directories.
|
|
168
277
|
*/
|
|
169
278
|
export const recordServiceDataDirectory = async (
|
|
@@ -171,31 +280,44 @@ export const recordServiceDataDirectory = async (
|
|
|
171
280
|
serviceArg: TServiceName,
|
|
172
281
|
): Promise<void> => {
|
|
173
282
|
const projectPath = plugins.path.resolve(projectPathArg);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
createdAt: existingMarker?.createdAt || new Date().toISOString(),
|
|
190
|
-
dataDirectories: entries,
|
|
191
|
-
};
|
|
283
|
+
await withInterProcessLock(
|
|
284
|
+
{
|
|
285
|
+
lockPath: getServiceDataMarkerLockPath(projectPath),
|
|
286
|
+
description: 'Service data marker',
|
|
287
|
+
},
|
|
288
|
+
async () => {
|
|
289
|
+
const dataPath = getServiceDataDirectory(projectPath, serviceArg);
|
|
290
|
+
const markerPath = getServiceDataMarkerPath(projectPath);
|
|
291
|
+
const markerExists = await plugins.smartfs.file(markerPath).exists();
|
|
292
|
+
const existingMarker = await readServiceDataMarker(projectPath);
|
|
293
|
+
if (markerExists && !existingMarker) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`Refusing to overwrite invalid or foreign service data marker: ${markerPath}`,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
192
298
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
299
|
+
const entries: IServiceDataMarkerEntry[] = existingMarker
|
|
300
|
+
? existingMarker.dataDirectories.filter((entry) => entry.service !== serviceArg)
|
|
301
|
+
: [];
|
|
302
|
+
entries.push({ service: serviceArg, path: dataPath });
|
|
303
|
+
entries.sort((entryA, entryB) => entryA.service.localeCompare(entryB.service));
|
|
304
|
+
|
|
305
|
+
const marker: IServiceDataMarker = {
|
|
306
|
+
owner: serviceDataOwner,
|
|
307
|
+
kind: 'service-data',
|
|
308
|
+
schemaVersion: serviceDataMarkerSchemaVersion,
|
|
309
|
+
projectPath,
|
|
310
|
+
safeToPrune: true,
|
|
311
|
+
createdAt: existingMarker?.createdAt || new Date().toISOString(),
|
|
312
|
+
dataDirectories: entries,
|
|
313
|
+
...(existingMarker?.legacyDataDirectories
|
|
314
|
+
? { legacyDataDirectories: existingMarker.legacyDataDirectories }
|
|
315
|
+
: {}),
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
await writeServiceDataMarker(projectPath, marker);
|
|
319
|
+
},
|
|
320
|
+
);
|
|
199
321
|
};
|
|
200
322
|
|
|
201
323
|
/**
|
|
@@ -219,6 +341,22 @@ export const getServiceContainerLabels = (optionsArg: {
|
|
|
219
341
|
};
|
|
220
342
|
};
|
|
221
343
|
|
|
344
|
+
export const hasExactServiceContainerLabels = (
|
|
345
|
+
labelsArg: Record<string, string>,
|
|
346
|
+
expectedArg: Record<string, string>,
|
|
347
|
+
): boolean => {
|
|
348
|
+
const actualGitZoneLabels = Object.entries(labelsArg).filter(([key]) =>
|
|
349
|
+
key.startsWith('git.zone.'),
|
|
350
|
+
);
|
|
351
|
+
const expectedGitZoneLabels = Object.entries(expectedArg).filter(([key]) =>
|
|
352
|
+
key.startsWith('git.zone.'),
|
|
353
|
+
);
|
|
354
|
+
return (
|
|
355
|
+
actualGitZoneLabels.length === expectedGitZoneLabels.length &&
|
|
356
|
+
expectedGitZoneLabels.every(([key, value]) => labelsArg[key] === value)
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
|
|
222
360
|
/** True when a container's labels prove `gitzone services` created it. */
|
|
223
361
|
export const isServiceOwnedByLabels = (labelsArg: { [key: string]: string }): boolean => {
|
|
224
362
|
return (
|
|
@@ -226,3 +364,23 @@ export const isServiceOwnedByLabels = (labelsArg: { [key: string]: string }): bo
|
|
|
226
364
|
labelsArg['git.zone.safe-to-prune'] === 'true'
|
|
227
365
|
);
|
|
228
366
|
};
|
|
367
|
+
|
|
368
|
+
/** Legacy provider evidence is retained for migration, never active lifecycle ownership. */
|
|
369
|
+
export const isLegacyMinioContainerLabels = (labelsArg: {
|
|
370
|
+
[key: string]: string;
|
|
371
|
+
}): boolean => {
|
|
372
|
+
return (
|
|
373
|
+
labelsArg['git.zone.tool'] === serviceToolLabel &&
|
|
374
|
+
labelsArg['git.zone.service'] === 'minio'
|
|
375
|
+
);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/** Transient bind-operation containers are owned by their invocation, not by prune. */
|
|
379
|
+
export const isServiceOperationHelperLabels = (labelsArg: {
|
|
380
|
+
[key: string]: string;
|
|
381
|
+
}): boolean => {
|
|
382
|
+
return (
|
|
383
|
+
labelsArg['git.zone.tool'] === serviceToolLabel &&
|
|
384
|
+
labelsArg[serviceOperationLabel] === serviceBindOperation
|
|
385
|
+
);
|
|
386
|
+
};
|