@faable/faable 1.15.3 → 1.16.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.
|
@@ -35,7 +35,15 @@ const node_buildpack = {
|
|
|
35
35
|
};
|
|
36
36
|
},
|
|
37
37
|
async build(ctx, plan) {
|
|
38
|
-
const env =
|
|
38
|
+
const env = {
|
|
39
|
+
...R.fromPairs(ctx.env_vars.map((e) => [e.name, e.value])),
|
|
40
|
+
// Platform-injected build-time identity, mirroring the runtime env the
|
|
41
|
+
// controller sets on the pod. FAABLE_DEPLOY_ID (build id ≡ runtime id)
|
|
42
|
+
// is the deterministic Next.js buildId source — user secrets can't
|
|
43
|
+
// override either. See arch/deploy/version-skew-coexistence.md.
|
|
44
|
+
FAABLE_APP_ID: ctx.app.id,
|
|
45
|
+
FAABLE_DEPLOY_ID: ctx.deployment.id,
|
|
46
|
+
};
|
|
39
47
|
log.info(`Building with env variables ${Object.keys(env).join(",")}`);
|
|
40
48
|
// The workflow no longer runs `npm ci` — install here when needed, before
|
|
41
49
|
// the build and before `COPY . .` packages node_modules into the image.
|
|
@@ -104,12 +104,16 @@ const deploy = {
|
|
|
104
104
|
.catch((error) => log.warn(`Could not mark deployment BUILDING: ${error.message}`));
|
|
105
105
|
const stop_log_sync = start_log_sync(api, deployment.id);
|
|
106
106
|
try {
|
|
107
|
-
await buildpack.build({ workdir, config, app, env_vars }, plan);
|
|
108
|
-
// Upload to Faable registry
|
|
109
|
-
const {
|
|
107
|
+
await buildpack.build({ workdir, config, app, env_vars, deployment }, plan);
|
|
108
|
+
// Upload to Faable registry, tagged by version and pinned to digest
|
|
109
|
+
const { image_ref } = await upload_tag({
|
|
110
|
+
app,
|
|
111
|
+
api,
|
|
112
|
+
deployment_id: deployment.id
|
|
113
|
+
});
|
|
110
114
|
// Complete the deployment with the built image — this is the handoff:
|
|
111
115
|
// the controller claims it and materializes the workload.
|
|
112
|
-
await api.completeDeployment(deployment.id,
|
|
116
|
+
await api.completeDeployment(deployment.id, image_ref);
|
|
113
117
|
}
|
|
114
118
|
finally {
|
|
115
119
|
stop_log_sync();
|
|
@@ -1,22 +1,73 @@
|
|
|
1
1
|
import { log } from '../../log.js';
|
|
2
2
|
import { cmd } from '../../lib/cmd.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* `<hostname>/<image>:<deployment_id>` — one tag per build, never
|
|
6
|
+
* overwritten. Without a tag docker resolves `:latest`, which every deploy
|
|
7
|
+
* rewrites: any rescheduled pod (node drain, eviction) would silently pull
|
|
8
|
+
* whatever build was pushed last, breaking the deployment ↔ code identity.
|
|
9
|
+
*/
|
|
10
|
+
const build_upload_tagname = (hostname, image, deployment_id) => `${hostname}/${image}:${deployment_id}`;
|
|
11
|
+
/**
|
|
12
|
+
* Strips the tag from an image ref, keeping registry ports intact
|
|
13
|
+
* (`host:5000/img:tag` → `host:5000/img`). A colon only denotes a tag when
|
|
14
|
+
* it appears after the last path segment separator.
|
|
15
|
+
*/
|
|
16
|
+
const strip_image_tag = (ref) => {
|
|
17
|
+
const last_slash = ref.lastIndexOf("/");
|
|
18
|
+
const last_colon = ref.lastIndexOf(":");
|
|
19
|
+
return last_colon > last_slash ? ref.slice(0, last_colon) : ref;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Pins a pushed tag to its content digest: `repo:tag@sha256:…`. Kubernetes
|
|
23
|
+
* pulls by digest (immutable even if the tag were re-pushed); the tag stays
|
|
24
|
+
* as the human-readable label. `RepoDigests` entries come as `repo@sha256:…`
|
|
25
|
+
* (no tag) and may reference other registries — only the entry for this
|
|
26
|
+
* repo counts. Returns null when none matches (caller falls back to the tag).
|
|
27
|
+
*/
|
|
28
|
+
const pin_tag_to_digest = (tagname, repo_digests) => {
|
|
29
|
+
const repo = strip_image_tag(tagname);
|
|
30
|
+
const match = repo_digests.find((d) => d.startsWith(`${repo}@sha256:`));
|
|
31
|
+
if (!match)
|
|
32
|
+
return null;
|
|
33
|
+
return `${tagname}@${match.slice(repo.length + 1)}`;
|
|
34
|
+
};
|
|
4
35
|
const upload_tag = async (args) => {
|
|
5
|
-
const { api, app } = args;
|
|
36
|
+
const { api, app, deployment_id } = args;
|
|
6
37
|
log.info(`🔁 Uploading...`);
|
|
7
38
|
const registry = await api.getRegistry(app.id);
|
|
8
39
|
// Registry login
|
|
9
40
|
const { user, password, hostname, image } = registry;
|
|
10
41
|
await cmd(`echo "${password}" | docker login --username "${user}" --password-stdin ${hostname}`);
|
|
11
|
-
// Tag
|
|
12
|
-
const upload_tagname =
|
|
42
|
+
// Tag the local build (tagged `app.id` by the buildpack) for this version
|
|
43
|
+
const upload_tagname = build_upload_tagname(hostname, image, deployment_id);
|
|
13
44
|
await cmd(`docker tag ${app.id} ${upload_tagname}`);
|
|
14
45
|
// Upload the image to faable registry
|
|
15
46
|
await cmd(`docker push ${upload_tagname}`);
|
|
47
|
+
// Pin to the digest the registry just assigned. Best-effort: the tagged
|
|
48
|
+
// ref alone is already unique per build, the digest just makes it
|
|
49
|
+
// tamper-proof.
|
|
50
|
+
let image_ref = upload_tagname;
|
|
51
|
+
try {
|
|
52
|
+
const inspect = await cmd(`docker inspect --format '{{json .RepoDigests}}' ${upload_tagname}`);
|
|
53
|
+
const repo_digests = JSON.parse(String(inspect.stdout).trim());
|
|
54
|
+
const pinned = pin_tag_to_digest(upload_tagname, repo_digests);
|
|
55
|
+
if (pinned) {
|
|
56
|
+
image_ref = pinned;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
log.warn(`Could not resolve the pushed digest; deploying by tag only.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
log.warn(`Could not inspect the pushed image (${error.message}); deploying by tag only.`);
|
|
64
|
+
}
|
|
16
65
|
log.info(`✅ Upload completed.`);
|
|
17
66
|
return {
|
|
18
67
|
upload_tagname,
|
|
68
|
+
/** Immutable ref recorded on the deployment: `repo:tag@sha256:…` */
|
|
69
|
+
image_ref,
|
|
19
70
|
};
|
|
20
71
|
};
|
|
21
72
|
|
|
22
|
-
export { upload_tag };
|
|
73
|
+
export { build_upload_tagname, pin_tag_to_digest, strip_image_tag, upload_tag };
|