hologit 0.43.2 → 0.45.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/README.md +69 -286
- package/index.d.ts +113 -76
- package/lib/Lens.js +218 -31
- package/lib/Projection.js +2 -2
- package/lib/Studio.js +150 -126
- package/package.json +3 -4
package/index.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
declare module 'git-client' {
|
|
2
|
+
export class Git {
|
|
3
|
+
constructor(options: { gitDir: string; workTree?: string });
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module '@iarna/toml' {
|
|
8
|
+
export function parse(content: string): any;
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
declare module 'hologit' {
|
|
2
12
|
import { Git as GitClient } from 'git-client';
|
|
3
|
-
import { Docker } from 'dockerode';
|
|
4
13
|
|
|
5
14
|
export interface GitOptions {
|
|
6
15
|
gitDir: string;
|
|
@@ -59,16 +68,40 @@ declare module 'hologit' {
|
|
|
59
68
|
cacheTo?: string | null;
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
export interface DockerExecOptions {
|
|
72
|
+
$relayStderr?: boolean;
|
|
73
|
+
$relayStdout?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface StudioContainer {
|
|
77
|
+
id?: string;
|
|
78
|
+
type?: 'studio';
|
|
79
|
+
env?: { [key: string]: string };
|
|
80
|
+
defaultUser?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface HoloSpec {
|
|
84
|
+
holospec: {
|
|
85
|
+
lens: {
|
|
86
|
+
input: string;
|
|
87
|
+
container?: string;
|
|
88
|
+
package?: string;
|
|
89
|
+
command?: string;
|
|
90
|
+
[key: string]: any;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
62
95
|
export class Git {
|
|
63
|
-
static
|
|
96
|
+
static get(): Promise<typeof GitClient>;
|
|
64
97
|
constructor(options: GitOptions);
|
|
65
98
|
gitDir: string;
|
|
66
99
|
workTree?: string;
|
|
67
100
|
}
|
|
68
101
|
|
|
69
102
|
export class BlobObject {
|
|
70
|
-
static
|
|
71
|
-
static
|
|
103
|
+
static write(repo: Repo, content: string): Promise<BlobObject>;
|
|
104
|
+
static writeFromFile(repo: Repo, filePath: string): Promise<BlobObject>;
|
|
72
105
|
|
|
73
106
|
constructor(repo: Repo, options: GitObjectOptions);
|
|
74
107
|
|
|
@@ -78,12 +111,12 @@ declare module 'hologit' {
|
|
|
78
111
|
isBlob: boolean;
|
|
79
112
|
type: 'blob';
|
|
80
113
|
|
|
81
|
-
|
|
114
|
+
read(): Promise<string>;
|
|
82
115
|
}
|
|
83
116
|
|
|
84
117
|
export class TreeObject {
|
|
85
118
|
static getEmptyTreeHash(): string;
|
|
86
|
-
static
|
|
119
|
+
static createFromRef(repo: Repo, ref: string): Promise<TreeObject>;
|
|
87
120
|
|
|
88
121
|
constructor(repo: Repo, options?: { hash?: string; parent?: TreeObject | null });
|
|
89
122
|
|
|
@@ -95,19 +128,19 @@ declare module 'hologit' {
|
|
|
95
128
|
type: 'tree';
|
|
96
129
|
mode: '040000';
|
|
97
130
|
|
|
98
|
-
|
|
131
|
+
getHash(): Promise<string>;
|
|
99
132
|
getWrittenHash(): string | null;
|
|
100
133
|
markDirty(): void;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
getChild(childPath: string): Promise<TreeObject | BlobObject | CommitObject | null>;
|
|
135
|
+
writeChild(childPath: string, content: string | BlobObject): Promise<BlobObject>;
|
|
136
|
+
getChildren(): Promise<{ [key: string]: TreeObject | BlobObject | CommitObject }>;
|
|
137
|
+
getBlobMap(): Promise<{ [key: string]: BlobObject }>;
|
|
138
|
+
deleteChild(childPath: string): Promise<void>;
|
|
139
|
+
getSubtree(subtreePath: string, create?: boolean): Promise<TreeObject | null>;
|
|
140
|
+
getSubtreeStack(subtreePath: string, create?: boolean): Promise<TreeObject[] | null>;
|
|
141
|
+
write(): Promise<string>;
|
|
142
|
+
merge(input: TreeObject, options?: MergeOptions, basePath?: string, preloadChildren?: boolean): Promise<void>;
|
|
143
|
+
clone(): Promise<TreeObject>;
|
|
111
144
|
}
|
|
112
145
|
|
|
113
146
|
export class CommitObject {
|
|
@@ -128,10 +161,10 @@ declare module 'hologit' {
|
|
|
128
161
|
|
|
129
162
|
getWorkspace(): Workspace;
|
|
130
163
|
getRepo(): Repo;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
164
|
+
readConfig(): Promise<any>;
|
|
165
|
+
writeConfig(config?: any): Promise<void>;
|
|
166
|
+
getConfig(): Promise<any>;
|
|
167
|
+
getCachedConfig(): Promise<any>;
|
|
135
168
|
}
|
|
136
169
|
|
|
137
170
|
export class Branch extends Configurable {
|
|
@@ -141,17 +174,17 @@ declare module 'hologit' {
|
|
|
141
174
|
|
|
142
175
|
getKind(): 'holobranch';
|
|
143
176
|
getConfigPath(): string;
|
|
144
|
-
|
|
177
|
+
isDefined(): Promise<boolean>;
|
|
145
178
|
getMapping(key: string): Mapping;
|
|
146
|
-
|
|
147
|
-
|
|
179
|
+
getMappings(): Promise<Map<string, Mapping>>;
|
|
180
|
+
composite(options: {
|
|
148
181
|
outputTree?: TreeObject;
|
|
149
182
|
fetch?: boolean | string[];
|
|
150
183
|
cacheFrom?: string | null;
|
|
151
184
|
cacheTo?: string | null;
|
|
152
185
|
}): Promise<TreeObject>;
|
|
153
186
|
getLens(name: string): Lens;
|
|
154
|
-
|
|
187
|
+
getLenses(): Promise<Map<string, Lens>>;
|
|
155
188
|
}
|
|
156
189
|
|
|
157
190
|
export class Source extends Configurable {
|
|
@@ -163,21 +196,21 @@ declare module 'hologit' {
|
|
|
163
196
|
|
|
164
197
|
getKind(): 'holosource';
|
|
165
198
|
getConfigPath(): string;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
199
|
+
getSpec(): Promise<{ hash: string; ref: string; data: any }>;
|
|
200
|
+
getCachedSpec(): Promise<{ hash: string; ref: string; data: any }>;
|
|
201
|
+
queryRef(): Promise<{ hash: string; ref: string } | null>;
|
|
202
|
+
hashWorkTree(): Promise<string | null>;
|
|
203
|
+
getOutputTree(options?: {
|
|
171
204
|
working?: boolean | null;
|
|
172
205
|
fetch?: boolean | string[];
|
|
173
206
|
cacheFrom?: string | null;
|
|
174
207
|
cacheTo?: string | null;
|
|
175
208
|
}): Promise<string>;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
209
|
+
getHead(options?: { required?: boolean; working?: boolean | null }): Promise<string | null>;
|
|
210
|
+
getCachedHead(): Promise<string | null>;
|
|
211
|
+
getBranch(): Promise<string | null>;
|
|
212
|
+
fetch(options?: { depth?: number; unshallow?: boolean | null }, ...refs: string[]): Promise<{ refs: string[] }>;
|
|
213
|
+
checkout(options?: { submodule?: boolean }): Promise<{
|
|
181
214
|
path: string;
|
|
182
215
|
head: string;
|
|
183
216
|
branch: string | null;
|
|
@@ -195,13 +228,13 @@ declare module 'hologit' {
|
|
|
195
228
|
|
|
196
229
|
getKind(): 'hololens';
|
|
197
230
|
getConfigPath(): string;
|
|
198
|
-
|
|
199
|
-
|
|
231
|
+
buildInputTree(inputRoot?: TreeObject): Promise<TreeObject>;
|
|
232
|
+
buildSpec(inputTree: TreeObject): Promise<{
|
|
200
233
|
hash: string;
|
|
201
234
|
ref: string;
|
|
202
235
|
data: any;
|
|
203
236
|
}>;
|
|
204
|
-
|
|
237
|
+
executeSpec(specHash: string, options: {
|
|
205
238
|
refresh?: boolean;
|
|
206
239
|
save?: boolean;
|
|
207
240
|
repo?: Repo | null;
|
|
@@ -209,7 +242,7 @@ declare module 'hologit' {
|
|
|
209
242
|
cacheTo?: string | null;
|
|
210
243
|
}): Promise<string>;
|
|
211
244
|
|
|
212
|
-
static
|
|
245
|
+
static executeSpec(specHash: string, options: {
|
|
213
246
|
refresh?: boolean;
|
|
214
247
|
save?: boolean;
|
|
215
248
|
repo?: Repo | null;
|
|
@@ -226,18 +259,18 @@ declare module 'hologit' {
|
|
|
226
259
|
getWorkspace(): Workspace;
|
|
227
260
|
getKind(): 'holospace';
|
|
228
261
|
getConfigPath(): string;
|
|
229
|
-
|
|
262
|
+
writeWorkingChanges(): Promise<void>;
|
|
230
263
|
getBranch(name: string): Branch;
|
|
231
|
-
|
|
264
|
+
getBranches(): Promise<Map<string, Branch>>;
|
|
232
265
|
getSource(name: string): Source;
|
|
233
|
-
|
|
234
|
-
|
|
266
|
+
getSources(): Promise<Map<string, Source>>;
|
|
267
|
+
getLayers(): Promise<Map<string, Map<string, Mapping>>>;
|
|
235
268
|
getLens(name: string): Lens;
|
|
236
|
-
|
|
269
|
+
getLenses(): Promise<Map<string, Lens>>;
|
|
237
270
|
}
|
|
238
271
|
|
|
239
272
|
export class Repo {
|
|
240
|
-
static
|
|
273
|
+
static getFromEnvironment(options?: { ref?: string; working?: boolean }): Promise<Repo>;
|
|
241
274
|
|
|
242
275
|
constructor(options: RepoOptions);
|
|
243
276
|
|
|
@@ -245,27 +278,27 @@ declare module 'hologit' {
|
|
|
245
278
|
ref: string;
|
|
246
279
|
workTree: string | null;
|
|
247
280
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
281
|
+
getWorkspace(): Promise<Workspace>;
|
|
282
|
+
createWorkspaceFromRef(ref: string): Promise<Workspace>;
|
|
283
|
+
createWorkspaceFromTreeHash(hash: string): Promise<Workspace>;
|
|
284
|
+
getGit(): Promise<GitClient>;
|
|
285
|
+
resolveRef(ref?: string | null): Promise<string | null>;
|
|
253
286
|
createBlob(options: GitObjectOptions): BlobObject;
|
|
254
|
-
|
|
255
|
-
|
|
287
|
+
writeBlob(content: string): Promise<BlobObject>;
|
|
288
|
+
writeBlobFromFile(filePath: string): Promise<BlobObject>;
|
|
256
289
|
createTree(options?: { hash?: string; parent?: TreeObject | null }): TreeObject;
|
|
257
|
-
|
|
290
|
+
createTreeFromRef(ref: string): Promise<TreeObject>;
|
|
258
291
|
createCommit(options: GitObjectOptions): CommitObject;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
292
|
+
hasCommit(commit: string): Promise<boolean>;
|
|
293
|
+
hashWorkTree(): Promise<string>;
|
|
294
|
+
watch(options: { callback: (treeHash: string, commitHash?: string) => void }): Promise<{
|
|
262
295
|
watching: Promise<void>;
|
|
263
296
|
cancel: () => void;
|
|
264
297
|
}>;
|
|
265
298
|
}
|
|
266
299
|
|
|
267
300
|
export class Projection {
|
|
268
|
-
static
|
|
301
|
+
static projectBranch(branch: Branch, options?: ProjectionOptions): Promise<string>;
|
|
269
302
|
|
|
270
303
|
constructor(options: { branch: Branch });
|
|
271
304
|
|
|
@@ -273,39 +306,39 @@ declare module 'hologit' {
|
|
|
273
306
|
workspace: Workspace;
|
|
274
307
|
output: Workspace;
|
|
275
308
|
|
|
276
|
-
|
|
309
|
+
composite(options: {
|
|
277
310
|
fetch?: boolean | string[];
|
|
278
311
|
cacheFrom?: string | null;
|
|
279
312
|
cacheTo?: string | null;
|
|
280
313
|
}): Promise<void>;
|
|
281
|
-
|
|
314
|
+
lens(options: {
|
|
282
315
|
cacheFrom?: string | null;
|
|
283
316
|
cacheTo?: string | null;
|
|
284
317
|
}): Promise<void>;
|
|
285
|
-
|
|
318
|
+
commit(ref: string, options?: {
|
|
286
319
|
parentCommit?: string | null;
|
|
287
320
|
commitMessage?: string | null;
|
|
288
321
|
}): Promise<string>;
|
|
289
322
|
}
|
|
290
323
|
|
|
291
324
|
export class Studio {
|
|
292
|
-
static
|
|
293
|
-
static
|
|
294
|
-
static
|
|
295
|
-
static
|
|
296
|
-
static
|
|
325
|
+
static cleanup(): Promise<void>;
|
|
326
|
+
static getHab(): Promise<any>;
|
|
327
|
+
static isEnvironmentStudio(): Promise<boolean>;
|
|
328
|
+
static get(gitDir: string): Promise<Studio>;
|
|
329
|
+
static execDocker(args: string[], options?: DockerExecOptions): Promise<string>;
|
|
297
330
|
|
|
298
|
-
constructor(options: { gitDir: string; container:
|
|
331
|
+
constructor(options: { gitDir: string; container: StudioContainer });
|
|
299
332
|
|
|
300
|
-
container:
|
|
333
|
+
container: StudioContainer;
|
|
301
334
|
gitDir: string;
|
|
302
335
|
|
|
303
336
|
isLocal(): boolean;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
337
|
+
habExec(...command: any[]): Promise<string>;
|
|
338
|
+
habPkgExec(pkg: string, bin: string, ...args: any[]): Promise<string>;
|
|
339
|
+
holoExec(...command: any[]): Promise<string>;
|
|
340
|
+
holoLensExec(spec: string): Promise<string>;
|
|
341
|
+
getPackage(query: string, options?: { install?: boolean }): Promise<string | null>;
|
|
309
342
|
}
|
|
310
343
|
|
|
311
344
|
export class Mapping extends Configurable {
|
|
@@ -319,13 +352,17 @@ declare module 'hologit' {
|
|
|
319
352
|
getConfigPath(): string;
|
|
320
353
|
}
|
|
321
354
|
|
|
322
|
-
export class SpecObject
|
|
323
|
-
|
|
355
|
+
export class SpecObject {
|
|
356
|
+
constructor(repo: Repo, options: GitObjectOptions);
|
|
357
|
+
|
|
358
|
+
repo: Repo;
|
|
359
|
+
hash: string;
|
|
360
|
+
isSpec: boolean;
|
|
361
|
+
|
|
362
|
+
static write(repo: Repo, kind: string, data: any): Promise<{
|
|
324
363
|
hash: string;
|
|
325
364
|
ref: string;
|
|
326
365
|
}>;
|
|
327
366
|
static buildRef(kind: string, hash: string): string;
|
|
328
|
-
|
|
329
|
-
isSpec: boolean;
|
|
330
367
|
}
|
|
331
368
|
}
|
package/lib/Lens.js
CHANGED
|
@@ -44,12 +44,10 @@ class Lens extends Configurable {
|
|
|
44
44
|
const config = await super.getConfig();
|
|
45
45
|
|
|
46
46
|
// process lens configuration
|
|
47
|
-
if (
|
|
48
|
-
|
|
47
|
+
if (config.package) {
|
|
48
|
+
config.command = config.command || 'lens-tree {{ input }}';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
config.command = config.command || 'lens-tree {{ input }}';
|
|
52
|
-
|
|
53
51
|
if (config.before) {
|
|
54
52
|
config.before =
|
|
55
53
|
typeof config.before == 'string'
|
|
@@ -103,7 +101,62 @@ class Lens extends Configurable {
|
|
|
103
101
|
async buildSpec (inputTree) {
|
|
104
102
|
const config = await this.getCachedConfig();
|
|
105
103
|
|
|
104
|
+
if (config.container) {
|
|
105
|
+
return this.buildSpecForContainer(inputTree, config);
|
|
106
|
+
} else if (config.package) {
|
|
107
|
+
return this.buildSpecForHabitatPackage(inputTree, config);
|
|
108
|
+
} else {
|
|
109
|
+
throw new Error(`hololens has no package or container defined: ${this.name}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async buildSpecForContainer (inputTree, config) {
|
|
114
|
+
const { container: containerQuery } = config;
|
|
115
|
+
|
|
116
|
+
// check if image exists locally first
|
|
117
|
+
let imageHash;
|
|
118
|
+
try {
|
|
119
|
+
const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
|
|
120
|
+
const imageInfo = JSON.parse(inspectOutput)[0];
|
|
121
|
+
imageHash = imageInfo.Id;
|
|
122
|
+
logger.info(`found local image: ${containerQuery}@${imageHash}`);
|
|
123
|
+
} catch (err) {
|
|
124
|
+
// image doesn't exist locally or can't be inspected, try pulling
|
|
125
|
+
logger.info(`pulling image: ${containerQuery}`);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await Studio.execDocker(['pull', containerQuery], { $relayStdout: true });
|
|
129
|
+
const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
|
|
130
|
+
const imageInfo = JSON.parse(inspectOutput)[0];
|
|
131
|
+
imageHash = imageInfo.Id;
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw new Error(`failed to pull container image ${containerQuery}: ${err.message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!imageHash) {
|
|
138
|
+
throw new Error(`failed to get hash for container image ${containerQuery}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// build spec
|
|
142
|
+
const data = {
|
|
143
|
+
...config,
|
|
144
|
+
container: `${containerQuery.replace(/:.*$/, '')}@${imageHash}`,
|
|
145
|
+
input: await inputTree.write(),
|
|
146
|
+
output: null,
|
|
147
|
+
before: null,
|
|
148
|
+
after: null
|
|
149
|
+
};
|
|
106
150
|
|
|
151
|
+
// write spec and return packet
|
|
152
|
+
return {
|
|
153
|
+
...await SpecObject.write(this.workspace.getRepo(), 'lens', data),
|
|
154
|
+
data,
|
|
155
|
+
type: 'container'
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async buildSpecForHabitatPackage (inputTree, config) {
|
|
107
160
|
// determine current package version
|
|
108
161
|
const { package: packageQuery } = config;
|
|
109
162
|
const [pkgOrigin, pkgName, pkgVersion, pkgBuild] = packageQuery.split('/');
|
|
@@ -163,14 +216,6 @@ class Lens extends Configurable {
|
|
|
163
216
|
}
|
|
164
217
|
|
|
165
218
|
|
|
166
|
-
// old studio method that might be useful as fallback/debug option
|
|
167
|
-
// const setupOutput = await studio.exec('hab', 'pkg', 'install', 'core/hab-plan-build');
|
|
168
|
-
// const originOutput = await studio.exec('hab', 'origin', 'key', 'generate', 'holo');
|
|
169
|
-
// const buildOutput = await studio.habPkgExec('core/hab-plan-build', 'hab-plan-build', '/src/lenses/compass');
|
|
170
|
-
// const studio = await Studio.get(this.workspace.getRepo().gitDir);
|
|
171
|
-
// let packageIdent = await studio.getPackage(packageQuery);
|
|
172
|
-
|
|
173
|
-
|
|
174
219
|
// build spec
|
|
175
220
|
const data = {
|
|
176
221
|
...config,
|
|
@@ -185,21 +230,21 @@ class Lens extends Configurable {
|
|
|
185
230
|
// write spec and return packet
|
|
186
231
|
return {
|
|
187
232
|
...await SpecObject.write(this.workspace.getRepo(), 'lens', data),
|
|
188
|
-
data
|
|
233
|
+
data,
|
|
234
|
+
type: 'habitat'
|
|
189
235
|
};
|
|
190
236
|
}
|
|
191
237
|
|
|
192
|
-
async executeSpec (specHash, options) {
|
|
193
|
-
return Lens.executeSpec(specHash, {...options, repo: this.workspace.getRepo()});
|
|
238
|
+
async executeSpec (specType, specHash, options) {
|
|
239
|
+
return Lens.executeSpec(specType, specHash, {...options, repo: this.workspace.getRepo()});
|
|
194
240
|
}
|
|
195
241
|
|
|
196
|
-
static async executeSpec (
|
|
242
|
+
static async executeSpec (specType, specHash, options) {
|
|
243
|
+
const { refresh=false, cacheFrom=null, cacheTo=null, save=true } = options;
|
|
197
244
|
|
|
198
|
-
// load holorepo
|
|
199
|
-
if (!repo) {
|
|
200
|
-
repo = await Repo.getFromEnvironment();
|
|
201
|
-
}
|
|
202
245
|
|
|
246
|
+
// load holorepo
|
|
247
|
+
const repo = options.repo || await Repo.getFromEnvironment();
|
|
203
248
|
const git = await repo.getGit();
|
|
204
249
|
|
|
205
250
|
|
|
@@ -228,9 +273,161 @@ class Lens extends Configurable {
|
|
|
228
273
|
}
|
|
229
274
|
|
|
230
275
|
|
|
231
|
-
//
|
|
276
|
+
// execute lens in container or with habitat package:
|
|
277
|
+
let lensedTreeHash;
|
|
278
|
+
if (specType == 'container') {
|
|
279
|
+
lensedTreeHash = await Lens.executeSpecForContainer(repo, specHash);
|
|
280
|
+
} else if (specType == 'habitat') {
|
|
281
|
+
lensedTreeHash = await Lens.executeSpecForHabitatPackage(repo, specHash);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// save ref to accelerate next projection
|
|
285
|
+
if (save) {
|
|
286
|
+
await git.updateRef(specRef, lensedTreeHash);
|
|
287
|
+
|
|
288
|
+
if (cacheTo) {
|
|
289
|
+
await _cacheResultTo(repo, specRef, cacheTo);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return lensedTreeHash;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static async executeSpecForContainer (repo, specHash) {
|
|
297
|
+
const git = await repo.getGit();
|
|
298
|
+
|
|
299
|
+
// read and parse spec file
|
|
300
|
+
const specToml = await git.catFile({ p: true }, specHash);
|
|
301
|
+
const {
|
|
302
|
+
holospec: {
|
|
303
|
+
lens: spec
|
|
304
|
+
}
|
|
305
|
+
} = TOML.parse(specToml);
|
|
306
|
+
|
|
307
|
+
// write commit with input tree and spec content
|
|
308
|
+
const commitHash = await git.commitTree(spec.input, {
|
|
309
|
+
p: [],
|
|
310
|
+
m: specToml
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// extract repository and hash from container string
|
|
314
|
+
const containerMatch = spec.container.match(/^.+@sha256:([a-f0-9]{64})$/);
|
|
315
|
+
if (!containerMatch) {
|
|
316
|
+
throw new Error(`Invalid container format: ${spec.container}`);
|
|
317
|
+
}
|
|
318
|
+
const [, sha256Hash] = containerMatch;
|
|
319
|
+
|
|
320
|
+
// create and start container
|
|
321
|
+
const persistentDebugContainer = process.env.HOLO_DEBUG_PERSIST_CONTAINER;
|
|
322
|
+
let containerId;
|
|
323
|
+
try {
|
|
324
|
+
if (persistentDebugContainer) {
|
|
325
|
+
try {
|
|
326
|
+
const containerInfo = await Studio.execDocker(['inspect', persistentDebugContainer]);
|
|
327
|
+
const containerState = JSON.parse(containerInfo)[0].State;
|
|
328
|
+
|
|
329
|
+
if (containerState.Running) {
|
|
330
|
+
logger.info(`Found running debug container: ${persistentDebugContainer}`);
|
|
331
|
+
containerId = persistentDebugContainer;
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
containerId = null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// create container
|
|
339
|
+
if (!containerId) {
|
|
340
|
+
containerId = await Studio.execDocker([
|
|
341
|
+
'create',
|
|
342
|
+
'-p', '9000:9000',
|
|
343
|
+
...(persistentDebugContainer ? ['--name', persistentDebugContainer] : []),
|
|
344
|
+
...(process.env.DEBUG ? ['-e', 'DEBUG=1'] : []),
|
|
345
|
+
sha256Hash
|
|
346
|
+
]);
|
|
347
|
+
containerId = containerId.trim();
|
|
348
|
+
|
|
349
|
+
logger.info('starting container');
|
|
350
|
+
await Studio.execDocker(['start', containerId]);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// wait for port 9000 to be available
|
|
354
|
+
let attempts = 0;
|
|
355
|
+
const maxAttempts = 30;
|
|
356
|
+
const waitTime = 1000; // 1 second
|
|
357
|
+
|
|
358
|
+
while (attempts < maxAttempts) {
|
|
359
|
+
try {
|
|
360
|
+
const containerInfo = await Studio.execDocker(['inspect', containerId]);
|
|
361
|
+
const containerState = JSON.parse(containerInfo)[0].State;
|
|
362
|
+
|
|
363
|
+
if (containerState.Running) {
|
|
364
|
+
// check if port 9000 is listening
|
|
365
|
+
try {
|
|
366
|
+
await Studio.execDocker([
|
|
367
|
+
'exec',
|
|
368
|
+
containerId,
|
|
369
|
+
'nc',
|
|
370
|
+
'-z',
|
|
371
|
+
'localhost',
|
|
372
|
+
'9000'
|
|
373
|
+
]);
|
|
374
|
+
break;
|
|
375
|
+
} catch (err) {
|
|
376
|
+
// ignore error and continue waiting
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
// ignore error and continue waiting
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
384
|
+
attempts++;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (attempts >= maxAttempts) {
|
|
388
|
+
throw new Error('Timeout waiting for git server to be ready');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// push commit to git server
|
|
392
|
+
logger.info(`pushing and executing job: ${commitHash}`);
|
|
393
|
+
await git.push(`http://localhost:9000/`, `${commitHash}:refs/heads/lens-input`, {
|
|
394
|
+
force: true,
|
|
395
|
+
$wait: true,
|
|
396
|
+
$onStderr: (line) => process.stderr.write(`\x1b[90m${line}\x1b[0m\n`)
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// fetch and verify output commit
|
|
400
|
+
const outputRef = `refs/lens-jobs/${specHash}`;
|
|
401
|
+
logger.info('fetching result');
|
|
402
|
+
await git.fetch('http://localhost:9000/', `+refs/heads/lens-input:${outputRef}`);
|
|
403
|
+
|
|
404
|
+
// verify the output commit's parent matches our input commit
|
|
405
|
+
const outputParent = await git.revParse(`${outputRef}^`);
|
|
406
|
+
if (outputParent !== commitHash) {
|
|
407
|
+
throw new Error(`Output commit parent ${outputParent} does not match input commit ${commitHash}`);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return await git.getTreeHash(outputRef);
|
|
411
|
+
|
|
412
|
+
} finally {
|
|
413
|
+
// cleanup
|
|
414
|
+
if (containerId && !persistentDebugContainer) {
|
|
415
|
+
try {
|
|
416
|
+
await Studio.execDocker(['stop', containerId]);
|
|
417
|
+
await Studio.execDocker(['rm', containerId]);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
logger.warn(`Failed to cleanup container: ${err.message}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static async executeSpecForHabitatPackage (repo, specHash) {
|
|
426
|
+
const git = await repo.getGit();
|
|
427
|
+
|
|
232
428
|
let lensedTreeHash;
|
|
233
429
|
|
|
430
|
+
// ensure the rest runs inside a studio environment
|
|
234
431
|
if (!await Studio.isEnvironmentStudio()) {
|
|
235
432
|
const studio = await Studio.get(repo.gitDir);
|
|
236
433
|
lensedTreeHash = await studio.holoLensExec(specHash);
|
|
@@ -307,16 +504,6 @@ class Lens extends Configurable {
|
|
|
307
504
|
}
|
|
308
505
|
|
|
309
506
|
|
|
310
|
-
// save ref to accelerate next projection
|
|
311
|
-
if (save) {
|
|
312
|
-
await git.updateRef(specRef, lensedTreeHash);
|
|
313
|
-
|
|
314
|
-
if (cacheTo) {
|
|
315
|
-
await _cacheResultTo(repo, specRef, cacheTo);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
507
|
// return tree hash
|
|
321
508
|
return lensedTreeHash;
|
|
322
509
|
}
|
package/lib/Projection.js
CHANGED
|
@@ -162,11 +162,11 @@ class Projection {
|
|
|
162
162
|
|
|
163
163
|
// build tree of matching files to input to lens
|
|
164
164
|
logger.info(`building input tree for lens ${lens.name} from ${inputRoot == '.' ? '' : (path.join(inputRoot, '.')+'/')}{${inputFiles}}`);
|
|
165
|
-
const { hash: specHash } = await lens.buildSpec(await lens.buildInputTree(this.output.root));
|
|
165
|
+
const { hash: specHash, type: specType } = await lens.buildSpec(await lens.buildInputTree(this.output.root));
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
// check for existing output tree
|
|
169
|
-
const outputTreeHash = await lens.executeSpec(specHash, { cacheFrom, cacheTo });
|
|
169
|
+
const outputTreeHash = await lens.executeSpec(specType, specHash, { cacheFrom, cacheTo });
|
|
170
170
|
|
|
171
171
|
|
|
172
172
|
// verify output
|