Skip to content

Commit 4693774

Browse files
committed
feat(docker): pin build toolchain image by digest
The build image was referenced only by a mutable tag (bugra9/cpp.js:0.3.2), so a hijacked or re-pushed tag on the registry could swap the compiler out from under every build. Pin it by digest for the pull/run/create paths; keep the tag solely for the container name (a digest's '@sha256:' is not a valid container-name character). The existence check moves from 'docker images -q' (which does not resolve a digest ref, so it would re-pull every build) to 'docker image inspect', which resolves both. - scripts/pin-docker-image.js: re-derives the registry digest from the tag and rewrites the IMAGE constant, keeping tag and digest in sync when the image is republished.
1 parent 4974993 commit 4693774

2 files changed

Lines changed: 68 additions & 5 deletions

File tree

cppjs-core/cpp.js/src/utils/pullDockerImage.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
11
import { execFileSync } from 'node:child_process';
22
import { getContentHash } from './hash.js';
33

4+
// The build toolchain image is pinned by digest, not just by tag, so a hijacked or re-pushed
5+
// tag on the registry can't silently swap the compiler out from under a build. IMAGE_TAG is the
6+
// same image kept only for a readable, stable container name — a digest's '@sha256:...' is not
7+
// valid in a Docker container name. Bump BOTH together when publishing a new image
8+
// (scripts/pin-docker-image.js reads the digest back from the registry).
9+
const IMAGE_TAG = 'bugra9/cpp.js:0.3.2';
10+
const IMAGE = 'bugra9/cpp.js@sha256:b99af109d096d21a09d44d8b119c671f0af3fe8725e5f88c6d47d7e48b612d2a';
11+
412
let isDockerImageAvailable = false;
513

614
export function getDockerImage() {
7-
return 'bugra9/cpp.js:0.3.2';
15+
return IMAGE;
816
}
917

1018
export function getDockerContainerName(base) {
11-
return `${getDockerImage()}-${getContentHash(base)}`.replaceAll('/', '-').replaceAll(':', '-');
19+
return `${IMAGE_TAG}-${getContentHash(base)}`.replaceAll('/', '-').replaceAll(':', '-');
20+
}
21+
22+
// `docker images -q` does not resolve a digest reference (returns empty), so the existence check
23+
// uses `docker image inspect`, which works for both tag and digest refs.
24+
function isImagePresent(ref) {
25+
try {
26+
execFileSync('docker', ['image', 'inspect', ref], { stdio: 'ignore' });
27+
return true;
28+
} catch {
29+
return false;
30+
}
1231
}
1332

1433
export default function pullDockerImage() {
1534
if (isDockerImageAvailable) return;
1635

17-
const isImageExist = execFileSync('docker', ['images', '-q', getDockerImage()], { encoding: 'utf-8' }).trim() !== '';
18-
19-
if (!isImageExist) {
36+
if (!isImagePresent(getDockerImage())) {
2037
console.log('');
2138
console.log('===========================================================');
2239
console.log('============= Downloading the docker image... =============');

scripts/pin-docker-image.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env node
2+
// Re-derives the build image's registry digest from its tag and writes it into pullDockerImage.js,
3+
// so the pinned digest (IMAGE) stays in sync with the tag (IMAGE_TAG) whenever the image is
4+
// republished. Run after `docker push`-ing a new image. Requires the tagged image pulled locally
5+
// (docker pull bugra9/cpp.js:<tag>) so its RepoDigest is known.
6+
//
7+
// node scripts/pin-docker-image.js
8+
9+
import fs from 'node:fs';
10+
import path from 'node:path';
11+
import { execFileSync } from 'node:child_process';
12+
import { fileURLToPath } from 'node:url';
13+
14+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
15+
const FILE = path.join(ROOT, 'cppjs-core', 'cpp.js', 'src', 'utils', 'pullDockerImage.js');
16+
17+
const text = fs.readFileSync(FILE, 'utf8');
18+
const tag = text.match(/const IMAGE_TAG = '([^']+)'/)?.[1];
19+
if (!tag) {
20+
console.error('pin-docker-image: could not find IMAGE_TAG in pullDockerImage.js');
21+
process.exit(1);
22+
}
23+
if (!/const IMAGE = '[^']*';/.test(text)) {
24+
console.error('pin-docker-image: could not find the IMAGE constant to update in pullDockerImage.js');
25+
process.exit(1);
26+
}
27+
28+
let repoDigest;
29+
try {
30+
repoDigest = execFileSync('docker', ['inspect', '--format', '{{index .RepoDigests 0}}', tag], { encoding: 'utf8' }).trim();
31+
} catch {
32+
console.error(`pin-docker-image: '${tag}' is not available locally. Run: docker pull ${tag}`);
33+
process.exit(1);
34+
}
35+
if (!/^[^@]+@sha256:[0-9a-f]{64}$/.test(repoDigest)) {
36+
console.error(`pin-docker-image: no registry digest for ${tag} (built locally but never pushed?). Got: ${repoDigest || '(empty)'}`);
37+
process.exit(1);
38+
}
39+
40+
const next = text.replace(/const IMAGE = '[^']*';/, `const IMAGE = '${repoDigest}';`);
41+
if (next === text) {
42+
console.log(`pin-docker-image: already pinned to ${repoDigest}`);
43+
process.exit(0);
44+
}
45+
fs.writeFileSync(FILE, next);
46+
console.log(`pin-docker-image: pinned ${tag} -> ${repoDigest}`);

0 commit comments

Comments
 (0)