1 | ;
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | const os_1 = __importDefault(require("os"));
|
7 | const path_1 = __importDefault(require("path"));
|
8 | const stream_1 = __importDefault(require("stream"));
|
9 | const dockerode_1 = __importDefault(require("dockerode"));
|
10 | /**
|
11 | * Executes a Docker environment.
|
12 | *
|
13 | * This class has a single method, `execute`, which starts a container from an
|
14 | * image and runs the command specified in the Dockerfile `CMD` instruction.
|
15 | *
|
16 | * It mounts the project's directory into the container a `/work` and uses it
|
17 | * as the working directory.
|
18 | *
|
19 | * It also sets the current user and group as the
|
20 | * user and group in the container. This means that within the container the
|
21 | * command that runs has the same permissions as the current user does in the
|
22 | * `/work` directory.
|
23 | *
|
24 | * Finally, it removes the container (but not the image).
|
25 | *
|
26 | * This then is the equivalent of running the container with Docker from within
|
27 | * the project directory using,
|
28 | *
|
29 | * docker run --rm --volume $(pwd):/work --workdir=/work --user=$(id -u):$(id -g) <image>
|
30 | */
|
31 | class DockerExecutor {
|
32 | /**
|
33 | * Run a Docker container
|
34 | *
|
35 | * @param name Name of the Docker image to use
|
36 | * @param folder Path of the project folder which will be mounted into the image
|
37 | */
|
38 | async execute(name, folder, command = '') {
|
39 | // Capture stdout so we can attempt to parse it
|
40 | // to JSON
|
41 | let out = '';
|
42 | let stdout = new stream_1.default.Writable({
|
43 | write(chunk, encoding, callback) {
|
44 | out += chunk.toString();
|
45 | callback();
|
46 | }
|
47 | });
|
48 | // Just write errors through to local console error
|
49 | let stderr = new stream_1.default.Writable({
|
50 | write(chunk, encoding, callback) {
|
51 | console.error(chunk.toString());
|
52 | callback();
|
53 | }
|
54 | });
|
55 | // Get and set user:group
|
56 | const userInfo = os_1.default.userInfo();
|
57 | const user = `${userInfo.uid}:${userInfo.gid}`;
|
58 | // Run the container!
|
59 | // Options from https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate
|
60 | const docker = new dockerode_1.default();
|
61 | // If the user has specified a command thaen use that, otherwise fallback to the
|
62 | // CMD in the Dockerfile
|
63 | let cmd;
|
64 | if (command)
|
65 | cmd = command.split(' ');
|
66 | const container = await docker.run(name, [], [stdout, stderr], {
|
67 | Cmd: cmd,
|
68 | HostConfig: {
|
69 | Binds: [
|
70 | `${path_1.default.resolve(folder)}:/work`
|
71 | ]
|
72 | },
|
73 | Tty: false,
|
74 | User: user,
|
75 | WorkingDir: '/work'
|
76 | });
|
77 | container.remove();
|
78 | // Attempt to parse output as JSON
|
79 | try {
|
80 | return JSON.parse(out);
|
81 | }
|
82 | catch (_a) {
|
83 | return out.trim();
|
84 | }
|
85 | }
|
86 | }
|
87 | exports.default = DockerExecutor;
|