hammerkit 1.21.0 → 1.21.2
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.
|
@@ -108,20 +108,18 @@ class RegistryClient {
|
|
|
108
108
|
this.authorization = `Bearer ${token}`;
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
|
-
// Send a request, performing the auth handshake once on a 401.
|
|
112
|
-
//
|
|
111
|
+
// Send a request, performing the auth handshake once on a 401. A string,
|
|
112
|
+
// buffer or blob body is replayable and fetch derives its content-length;
|
|
113
|
+
// never set that header by hand, the undici that serves `fetch` rejects it
|
|
114
|
+
// on some Node versions.
|
|
113
115
|
async request(method, urlOrPath, init = {}) {
|
|
114
116
|
const url = urlOrPath.startsWith('http') ? urlOrPath : this.url(urlOrPath);
|
|
115
|
-
const send = () => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
redirect: init.redirect ?? 'follow',
|
|
122
|
-
...(body instanceof stream_1.Readable ? { duplex: 'half' } : {}),
|
|
123
|
-
});
|
|
124
|
-
};
|
|
117
|
+
const send = () => fetch(url, {
|
|
118
|
+
method,
|
|
119
|
+
headers: { ...(init.headers ?? {}), ...(this.authorization ? { authorization: this.authorization } : {}) },
|
|
120
|
+
body: init.body?.(),
|
|
121
|
+
redirect: init.redirect ?? 'follow',
|
|
122
|
+
});
|
|
125
123
|
let response = await send();
|
|
126
124
|
if (response.status === 401 && (await this.authenticate(response.headers.get('www-authenticate')))) {
|
|
127
125
|
await response.body?.cancel();
|
|
@@ -223,8 +221,10 @@ class RegistryClient {
|
|
|
223
221
|
return true;
|
|
224
222
|
}
|
|
225
223
|
// Upload a blob from a buffer or a file (monolithic upload: POST to open the
|
|
226
|
-
// session, PUT the content with its digest to commit it).
|
|
227
|
-
|
|
224
|
+
// session, PUT the content with its digest to commit it). A file is sent as a
|
|
225
|
+
// blob backed by the file, so it is read lazily and fetch sends its size as
|
|
226
|
+
// the content-length.
|
|
227
|
+
async uploadBlob(digest, content) {
|
|
228
228
|
if (await this.blobExists(digest)) {
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
@@ -238,9 +238,10 @@ class RegistryClient {
|
|
|
238
238
|
}
|
|
239
239
|
const uploadUrl = new URL(location, this.url(''));
|
|
240
240
|
uploadUrl.searchParams.set('digest', digest);
|
|
241
|
+
const body = 'buffer' in content ? content.buffer : await (0, fs_1.openAsBlob)(content.file);
|
|
241
242
|
const response = await this.request('PUT', uploadUrl.toString(), {
|
|
242
|
-
headers: { 'content-type': 'application/octet-stream'
|
|
243
|
-
body: () =>
|
|
243
|
+
headers: { 'content-type': 'application/octet-stream' },
|
|
244
|
+
body: () => body,
|
|
244
245
|
});
|
|
245
246
|
if (response.status !== 201) {
|
|
246
247
|
return this.fail(response, `uploading blob ${digest}`);
|
|
@@ -85,8 +85,8 @@ function createRegistryCacheBackend(spec) {
|
|
|
85
85
|
config: { Labels: { 'dev.hammerkit.task-id': taskId, 'dev.hammerkit.state-key': stateKey } },
|
|
86
86
|
}));
|
|
87
87
|
const configDigest = (0, registry_client_1.digestBuffer)(config);
|
|
88
|
-
await registry.uploadBlob(layer.digest,
|
|
89
|
-
await registry.uploadBlob(configDigest.digest,
|
|
88
|
+
await registry.uploadBlob(layer.digest, { file: layerFile });
|
|
89
|
+
await registry.uploadBlob(configDigest.digest, { buffer: config });
|
|
90
90
|
const manifest = {
|
|
91
91
|
schemaVersion: 2,
|
|
92
92
|
mediaType: registry_client_1.OCI_MANIFEST,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.awaitRunningNeeds = exports.awaitRunningNeed = exports.awaitCompleted = exports.awaitCompletedDependencies = exports.awaitNoRequirements = exports.awaitDependentNeed = exports.awaitRequirement = void 0;
|
|
3
|
+
exports.awaitRunningNeeds = exports.awaitRunningNeed = exports.awaitCompleted = exports.awaitCompletedDependencies = exports.transitiveDeps = exports.awaitNoRequirements = exports.awaitDependentNeed = exports.awaitRequirement = void 0;
|
|
4
4
|
const work_item_1 = require("../planner/work-item");
|
|
5
5
|
const state_resolver_1 = require("./state-resolver");
|
|
6
6
|
// Resolve true once any requirer is `ready` (about to run). With
|
|
@@ -42,8 +42,26 @@ async function awaitNoRequirements(svc, abort) {
|
|
|
42
42
|
}));
|
|
43
43
|
}
|
|
44
44
|
exports.awaitNoRequirements = awaitNoRequirements;
|
|
45
|
+
// The dependencies and, recursively, theirs, each once.
|
|
46
|
+
function transitiveDeps(deps) {
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
const visit = (items) => {
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
if (!seen.has(item)) {
|
|
51
|
+
seen.add(item);
|
|
52
|
+
visit(item.deps);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
visit(deps);
|
|
57
|
+
return [...seen];
|
|
58
|
+
}
|
|
59
|
+
exports.transitiveDeps = transitiveDeps;
|
|
60
|
+
// Waits for the whole dependency closure, not only the direct dependencies: a
|
|
61
|
+
// cached direct dependency completes at once, while the dependencies it
|
|
62
|
+
// skipped past are still being restored for the item about to run.
|
|
45
63
|
async function awaitCompletedDependencies(work, deps, abort) {
|
|
46
|
-
for (const dep of deps) {
|
|
64
|
+
for (const dep of transitiveDeps(deps)) {
|
|
47
65
|
work.status.write('debug', `await completion of ${dep.name} with state ${dep.state.current.type}`);
|
|
48
66
|
await awaitCompleted(work, dep, abort);
|
|
49
67
|
}
|
|
@@ -5,6 +5,7 @@ const plan_work_tasks_1 = require("../planner/utils/plan-work-tasks");
|
|
|
5
5
|
const execute_work_service_1 = require("./execute-work-service");
|
|
6
6
|
const execute_work_task_1 = require("./execute-work-task");
|
|
7
7
|
const execute_init_task_1 = require("./execute-init-task");
|
|
8
|
+
const await_completed_dependencies_1 = require("./await-completed-dependencies");
|
|
8
9
|
async function executeWorkTree(work, environment, options) {
|
|
9
10
|
const itemPromises = [];
|
|
10
11
|
const dependents = skippableDependents(work, options);
|
|
@@ -67,9 +68,13 @@ function skippableDependents(work, options) {
|
|
|
67
68
|
result.set(task.name, []);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
71
|
+
// A dependent also needs everything its dependencies need: a cached `build`
|
|
72
|
+
// does not restore the `install` a re-running `test` uses at run time. So a
|
|
73
|
+
// task is a dependent of its whole dependency closure, requested tasks
|
|
74
|
+
// included in the walk (they are cached or run on their own).
|
|
70
75
|
const items = [...(0, plan_work_tasks_1.iterateWorkTasks)(work), ...(0, plan_work_tasks_1.iterateWorkServices)(work)];
|
|
71
76
|
for (const item of items) {
|
|
72
|
-
for (const dep of item.deps) {
|
|
77
|
+
for (const dep of (0, await_completed_dependencies_1.transitiveDeps)(item.deps)) {
|
|
73
78
|
if (work.requested.includes(dep.name)) {
|
|
74
79
|
continue;
|
|
75
80
|
}
|
package/package.json
CHANGED