Skip to content

Commit 328292f

Browse files
authored
Add observable-builder user and use /project (#10)
* Add observable-builder user and use /project Adds an observable-builder user with a home directory of /project. This uses 8000 as the uid since that's not likely to collide with any users on the container runtime host machines. * Add useradd to base and fix tests to use workdir * Add GID so the container user can read the files * Add `z` to fix GHA SELinux issues * Copy the files into the container * Remove host tempdir and file copy * Code review followups * Use a traditional /home directory * Combine poetry into python layer * Rename mounts * Removed unused imports * Put tar-fs into devDependencies
1 parent a977bbb commit 328292f

File tree

5 files changed

+182
-45
lines changed

5 files changed

+182
-45
lines changed

Dockerfile

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# == base ======================
22
FROM buildpack-deps:bookworm AS base
3-
ENV CACHEBUST=2024-09-06
4-
RUN apt update
3+
ENV CACHEBUST=2024-09-17
4+
RUN useradd -m -u 8000 observable-builder && mkdir /project && \
5+
chown 8000:8000 /project && apt update
56

67
ENV RUSTUP_HOME=/usr/local/rustup \
78
CARGO_HOME=/usr/local/cargo \
89
RUST_VERSION=1.81.0 \
9-
VIRTUAL_ENV=/var/local/python-venv
10-
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:/root/.local/bin:$PATH
10+
VIRTUAL_ENV=/home/observable-builder/.local/python-venv
11+
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:/home/observable-builder/.local/bin:$PATH
1112

1213
# == node ======================
1314
FROM base AS node
@@ -32,8 +33,9 @@ RUN --mount=type=cache,target=/var/cache/apt,id=framework-runtime-python \
3233
python3-wheel \
3334
python3-dev \
3435
python3-venv \
35-
pipx \
36-
&& pipx install poetry \
36+
pipx
37+
USER 8000
38+
RUN pipx install poetry \
3739
&& python3 -m venv $VIRTUAL_ENV
3840

3941
# == R ===========================
@@ -125,3 +127,5 @@ COPY --from=python . .
125127
COPY --from=r . .
126128
COPY --from=duckdb . .
127129
COPY --from=rust . .
130+
USER 8000:8000
131+
WORKDIR /project

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
"devDependencies": {
1313
"@types/dockerode": "^3.3.28",
1414
"@types/node": "^20",
15+
"@types/tar-fs": "^2.0.4",
1516
"dockerode": "^4.0.2",
1617
"glob": "^10.3.12",
1718
"semver": "^7.6.0",
19+
"tar-fs": "^3.0.6",
1820
"tsx": "^4.7.1",
1921
"typescript": "^5.4.3"
2022
}

tests/dataloader-languages.test.ts

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -71,34 +71,21 @@ describe("Dataloader languages", () => {
7171
});
7272

7373
test(`Poetry can install dependencies in the virtualenv`, async () => {
74-
let testDir = await mkdtemp(join(os.tmpdir(), "poetry-test-"));
75-
try {
76-
// This will install dependencies using Poetry, and then try to run `ls`
77-
// in the installed dependency's package. If the package is not
78-
// installed here, the `ls` command will exit non-zero and
79-
// `runCommandInContainer` will throw.
80-
await cp(
81-
"./tests/fixtures/poetry-test/pyproject.toml",
82-
`${testDir}/pyproject.toml`,
83-
);
84-
let res = await runCommandInContainer(
85-
[
86-
"sh",
87-
"-c",
88-
"poetry install; ls $(poetry env info --path)/lib/python3.11/site-packages/pip_install_test/__init__.py",
89-
],
90-
{
91-
workingDir: "/poetry-test",
92-
mounts: [{ host: testDir, container: "/poetry-test" }],
93-
},
94-
);
95-
} finally {
96-
try {
97-
await rm(testDir, { recursive: true });
98-
} catch {
99-
/* ok */
100-
}
101-
}
74+
// This will install dependencies using Poetry, and then try to run `ls`
75+
// in the installed dependency's package. If the package is not
76+
// installed here, the `ls` command will exit non-zero and
77+
// `runCommandInContainer` will throw.
78+
let res = await runCommandInContainer(
79+
[
80+
"sh",
81+
"-c",
82+
"poetry install; ls $(poetry env info --path)/lib/python3.11/site-packages/pip_install_test/__init__.py",
83+
],
84+
{
85+
workingDir: "/project/poetry-test",
86+
hostContainerDirs: [{ host: "./tests/fixtures/poetry-test", container: "/project/poetry-test" }],
87+
},
88+
);
10289
});
10390
});
10491

tests/index.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { test, before } from "node:test";
2-
import { resolve } from "node:path";
32
import assert from "node:assert";
43
import Dockerode from "dockerode";
54
import { Stream } from "node:stream";
65
import semverSatisfies from "semver/functions/satisfies";
7-
import { basename } from "node:path";
6+
import tar from "tar-fs";
87

98
export const IMAGE_TAG = "observablehq/framework-runtime:test";
109

@@ -87,13 +86,23 @@ function ensureDocker() {
8786

8887
before(ensureDocker);
8988

89+
function copyFilesToContainer(
90+
dockerContainer: Dockerode.Container,
91+
directories: { host: string; container: string }[],
92+
) {
93+
for (const { host, container } of directories) {
94+
const tarStream = tar.pack(host);
95+
dockerContainer.putArchive(tarStream, { path: container });
96+
}
97+
}
98+
9099
export async function runCommandInContainer(
91100
command: string[],
92101
{
93-
mounts = [],
94-
workingDir = "/",
102+
hostContainerDirs = [],
103+
workingDir = "/project",
95104
}: {
96-
mounts?: { host: string; container: string}[];
105+
hostContainerDirs?: { host: string; container: string}[];
97106
workingDir?: string;
98107
} = {},
99108
): Promise<{ stdout: string; stderr: string }> {
@@ -102,13 +111,10 @@ export async function runCommandInContainer(
102111
WorkingDir: workingDir,
103112
Image: IMAGE_TAG,
104113
Cmd: command,
105-
HostConfig: {
106-
Binds: mounts.map(
107-
({ host, container }) =>
108-
`${resolve(host)}:${container}`,
109-
),
110-
},
111114
});
115+
116+
copyFilesToContainer(container, hostContainerDirs);
117+
112118
const stdout = new StringStream();
113119
const stderr = new StringStream();
114120
const attach = await container.attach({

yarn.lock

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,11 @@ __metadata:
215215
dependencies:
216216
"@types/dockerode": "npm:^3.3.28"
217217
"@types/node": "npm:^20"
218+
"@types/tar-fs": "npm:^2.0.4"
218219
dockerode: "npm:^4.0.2"
219220
glob: "npm:^10.3.12"
220221
semver: "npm:^7.6.0"
222+
tar-fs: "npm:^3.0.6"
221223
tsx: "npm:^4.7.1"
222224
typescript: "npm:^5.4.3"
223225
languageName: unknown
@@ -287,6 +289,25 @@ __metadata:
287289
languageName: node
288290
linkType: hard
289291

292+
"@types/tar-fs@npm:^2.0.4":
293+
version: 2.0.4
294+
resolution: "@types/tar-fs@npm:2.0.4"
295+
dependencies:
296+
"@types/node": "npm:*"
297+
"@types/tar-stream": "npm:*"
298+
checksum: 10c0/d1dd6944d0905debaabe5787af7f3aeb98f13a928d688d00fb7de0411040f8556c297d388abdd046f6b0646a374b53c198ade0484060b63ef36ad5ac585df138
299+
languageName: node
300+
linkType: hard
301+
302+
"@types/tar-stream@npm:*":
303+
version: 3.1.3
304+
resolution: "@types/tar-stream@npm:3.1.3"
305+
dependencies:
306+
"@types/node": "npm:*"
307+
checksum: 10c0/64f87d209bd2edf1a7d029a922a246ef0dcfb19e623b95714e2c074195a61ed4fe4d67d0c3c6dc33239ef7d18902fcb70df7f7e85cfbd92a6bf25d087ce531fd
308+
languageName: node
309+
linkType: hard
310+
290311
"abbrev@npm:^2.0.0":
291312
version: 2.0.0
292313
resolution: "abbrev@npm:2.0.0"
@@ -352,13 +373,64 @@ __metadata:
352373
languageName: node
353374
linkType: hard
354375

376+
"b4a@npm:^1.6.4, b4a@npm:^1.6.6":
377+
version: 1.6.6
378+
resolution: "b4a@npm:1.6.6"
379+
checksum: 10c0/56f30277666cb511a15829e38d369b114df7dc8cec4cedc09cc5d685bc0f27cb63c7bcfb58e09a19a1b3c4f2541069ab078b5328542e85d74a39620327709a38
380+
languageName: node
381+
linkType: hard
382+
355383
"balanced-match@npm:^1.0.0":
356384
version: 1.0.2
357385
resolution: "balanced-match@npm:1.0.2"
358386
checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee
359387
languageName: node
360388
linkType: hard
361389

390+
"bare-events@npm:^2.0.0, bare-events@npm:^2.2.0":
391+
version: 2.4.2
392+
resolution: "bare-events@npm:2.4.2"
393+
checksum: 10c0/09fa923061f31f815e83504e2ed4a8ba87732a01db40a7fae703dbb7eef7f05d99264b5e186074cbe9698213990d1af564c62cca07a5ff88baea8099ad9a6303
394+
languageName: node
395+
linkType: hard
396+
397+
"bare-fs@npm:^2.1.1":
398+
version: 2.3.5
399+
resolution: "bare-fs@npm:2.3.5"
400+
dependencies:
401+
bare-events: "npm:^2.0.0"
402+
bare-path: "npm:^2.0.0"
403+
bare-stream: "npm:^2.0.0"
404+
checksum: 10c0/ff18cc9be7c557c38e0342681ba3672ae4b01e5696b567d4035e5995255dc6bc7d4df88ed210fa4d3eb940eb29512e924ebb42814c87fc59a2bee8cf83b7c2f9
405+
languageName: node
406+
linkType: hard
407+
408+
"bare-os@npm:^2.1.0":
409+
version: 2.4.4
410+
resolution: "bare-os@npm:2.4.4"
411+
checksum: 10c0/e7d1a7b2100c05da8d25b60d0d48cf850c6f57064577a3f2f51cf18d417fbcfd6967ed2d8314320914ed69e0f2ebcf54eb1b36092dd172d8e8f969cf8cccf041
412+
languageName: node
413+
linkType: hard
414+
415+
"bare-path@npm:^2.0.0, bare-path@npm:^2.1.0":
416+
version: 2.1.3
417+
resolution: "bare-path@npm:2.1.3"
418+
dependencies:
419+
bare-os: "npm:^2.1.0"
420+
checksum: 10c0/35587e177fc8fa5b13fb90bac8779b5ce49c99016d221ddaefe2232d02bd4295d79b941e14ae19fda75ec42a6fe5fb66c07d83ae7ec11462178e66b7be65ca74
421+
languageName: node
422+
linkType: hard
423+
424+
"bare-stream@npm:^2.0.0":
425+
version: 2.3.0
426+
resolution: "bare-stream@npm:2.3.0"
427+
dependencies:
428+
b4a: "npm:^1.6.6"
429+
streamx: "npm:^2.20.0"
430+
checksum: 10c0/374a517542e6a0c3c07f3a1d567db612685e66708f79781112aa0e81c1f117ec561cc1ff3926144f15a2200316a77030c95dcc13a1b96d5303f0748798b764cf
431+
languageName: node
432+
linkType: hard
433+
362434
"base64-js@npm:^1.3.1":
363435
version: 1.5.1
364436
resolution: "base64-js@npm:1.5.1"
@@ -666,6 +738,13 @@ __metadata:
666738
languageName: node
667739
linkType: hard
668740

741+
"fast-fifo@npm:^1.2.0, fast-fifo@npm:^1.3.2":
742+
version: 1.3.2
743+
resolution: "fast-fifo@npm:1.3.2"
744+
checksum: 10c0/d53f6f786875e8b0529f784b59b4b05d4b5c31c651710496440006a398389a579c8dbcd2081311478b5bf77f4b0b21de69109c5a4eabea9d8e8783d1eb864e4c
745+
languageName: node
746+
linkType: hard
747+
669748
"foreground-child@npm:^3.1.0":
670749
version: 3.1.1
671750
resolution: "foreground-child@npm:3.1.1"
@@ -1133,6 +1212,13 @@ __metadata:
11331212
languageName: node
11341213
linkType: hard
11351214

1215+
"queue-tick@npm:^1.0.1":
1216+
version: 1.0.1
1217+
resolution: "queue-tick@npm:1.0.1"
1218+
checksum: 10c0/0db998e2c9b15215317dbcf801e9b23e6bcde4044e115155dae34f8e7454b9a783f737c9a725528d677b7a66c775eb7a955cf144fe0b87f62b575ce5bfd515a9
1219+
languageName: node
1220+
linkType: hard
1221+
11361222
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0":
11371223
version: 3.6.2
11381224
resolution: "readable-stream@npm:3.6.2"
@@ -1274,6 +1360,21 @@ __metadata:
12741360
languageName: node
12751361
linkType: hard
12761362

1363+
"streamx@npm:^2.15.0, streamx@npm:^2.20.0":
1364+
version: 2.20.1
1365+
resolution: "streamx@npm:2.20.1"
1366+
dependencies:
1367+
bare-events: "npm:^2.2.0"
1368+
fast-fifo: "npm:^1.3.2"
1369+
queue-tick: "npm:^1.0.1"
1370+
text-decoder: "npm:^1.1.0"
1371+
dependenciesMeta:
1372+
bare-events:
1373+
optional: true
1374+
checksum: 10c0/34ffa2ee9465d70e18c7e2ba70189720c166d150ab83eb7700304620fa23ff42a69cb37d712ea4b5fc6234d8e74346a88bb4baceb873c6b05e52ac420f8abb4d
1375+
languageName: node
1376+
linkType: hard
1377+
12771378
"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
12781379
version: 4.2.3
12791380
resolution: "string-width@npm:4.2.3"
@@ -1323,6 +1424,23 @@ __metadata:
13231424
languageName: node
13241425
linkType: hard
13251426

1427+
"tar-fs@npm:^3.0.6":
1428+
version: 3.0.6
1429+
resolution: "tar-fs@npm:3.0.6"
1430+
dependencies:
1431+
bare-fs: "npm:^2.1.1"
1432+
bare-path: "npm:^2.1.0"
1433+
pump: "npm:^3.0.0"
1434+
tar-stream: "npm:^3.1.5"
1435+
dependenciesMeta:
1436+
bare-fs:
1437+
optional: true
1438+
bare-path:
1439+
optional: true
1440+
checksum: 10c0/207b7c0f193495668bd9dbad09a0108ce4ffcfec5bce2133f90988cdda5c81fad83c99f963d01e47b565196594f7a17dbd063ae55b97b36268fcc843975278ee
1441+
languageName: node
1442+
linkType: hard
1443+
13261444
"tar-fs@npm:~2.0.1":
13271445
version: 2.0.1
13281446
resolution: "tar-fs@npm:2.0.1"
@@ -1348,6 +1466,17 @@ __metadata:
13481466
languageName: node
13491467
linkType: hard
13501468

1469+
"tar-stream@npm:^3.1.5":
1470+
version: 3.1.7
1471+
resolution: "tar-stream@npm:3.1.7"
1472+
dependencies:
1473+
b4a: "npm:^1.6.4"
1474+
fast-fifo: "npm:^1.2.0"
1475+
streamx: "npm:^2.15.0"
1476+
checksum: 10c0/a09199d21f8714bd729993ac49b6c8efcb808b544b89f23378ad6ffff6d1cb540878614ba9d4cfec11a64ef39e1a6f009a5398371491eb1fda606ffc7f70f718
1477+
languageName: node
1478+
linkType: hard
1479+
13511480
"tar@npm:^6.1.11, tar@npm:^6.1.2":
13521481
version: 6.2.1
13531482
resolution: "tar@npm:6.2.1"
@@ -1362,6 +1491,15 @@ __metadata:
13621491
languageName: node
13631492
linkType: hard
13641493

1494+
"text-decoder@npm:^1.1.0":
1495+
version: 1.2.0
1496+
resolution: "text-decoder@npm:1.2.0"
1497+
dependencies:
1498+
b4a: "npm:^1.6.4"
1499+
checksum: 10c0/398171bef376e06864cd6ba24e0787cc626bebc84a1bbda758d06a6e9b729cc8613f7923dd0d294abd88e8bb5cd7261aad5fda7911fb87253fe71b2b5ac6e507
1500+
languageName: node
1501+
linkType: hard
1502+
13651503
"tsx@npm:^4.7.1":
13661504
version: 4.7.1
13671505
resolution: "tsx@npm:4.7.1"

0 commit comments

Comments
 (0)