UNPKG

3.14 kBPlain TextView Raw
1import fs from 'fs'
2import path from 'path'
3import { spawnSync } from 'child_process'
4import Docker from 'dockerode'
5import yaml from 'js-yaml'
6import NixGenerator from './NixGenerator'
7import { SoftwareEnvironment } from '@stencila/schema'
8import CachingUrlFetcher from './CachingUrlFetcher'
9
10const generator = new NixGenerator(new CachingUrlFetcher(), undefined)
11const docker = new Docker()
12
13/**
14 * Generates a default.nix and a nixDockerfile for a `SoftwareEnvironment`
15 */
16function compile (environ: void | SoftwareEnvironment | null, folder: any) {
17 // Generate .default.nix file
18 generator.generate(environ, folder)
19
20 // Figure out if a custom default.nix file is present
21 let defaultNix = path.join(folder, 'default.nix')
22 let dockterNix = path.join(folder, '.default.nix')
23 let nixfile = fs.existsSync(defaultNix) ? defaultNix : dockterNix
24
25 // Generate .nixDockerfile
26 let dockerfile = path.join(folder, '.nixDockerfile')
27
28 fs.writeFileSync(dockerfile, `FROM nixos/nix
29
30# Copy over the Nix derivation
31COPY ${path.basename(nixfile)} default.nix
32# Run nix-shell
33CMD nix-shell --pure\n`
34 )
35}
36
37/**
38 * Builds a Docker image from a nixDockerfile
39 */
40async function build (folder: any) {
41 let name = path.basename(folder).toLocaleLowerCase().replace(' ', '-')
42
43 // Figure out if a custom default.nix file is present
44 let defaultNix = path.join(folder, 'default.nix')
45 let dockterNix = path.join(folder, '.default.nix')
46 let nixfile = fs.existsSync(defaultNix) ? defaultNix : dockterNix
47
48 // Start building the image
49 let build = await docker.buildImage({
50 context: folder,
51 src: ['.nixDockerfile', path.basename(nixfile)]
52 }, { t: name, dockerfile: '.nixDockerfile' })
53
54 // Wait for image to finish building
55 docker.modem.followProgress(build, (err: any, res: any) => {
56 if (err) throw err
57 output(res)
58 })
59}
60
61/**
62 * Executes nix-shell inside a Docker image from nixDockerfile with a Docker data volume for /nix store
63 */
64async function execute (folder: any, command: string = '') {
65 // Create shared /nix/store Docker volume if needed
66 let volumes: any = await docker.listVolumes()
67 let nixStoreVolume = volumes.Volumes.find((vol: any) => (vol.Name === 'nix-store'))
68 if (!nixStoreVolume) { await docker.createVolume({ name: 'nix-store' }) }
69
70 let name = path.basename(folder).toLocaleLowerCase().replace(' ', '-')
71 let args = `run -it --rm -v /tmp:/tmp -v nix-store:/nix ${name}`
72 // If there is a user specified command then run that within the Nix shell, otherwise
73 // will run the CMD from the Dockerfile
74 if (command) args += ` /root/.nix-profile/bin/nix-shell --pure --run "${command.replace('"', '\\"')}"`
75 spawnSync(
76 'docker', args.split(' '),
77 {
78 shell: true,
79 cwd: process.cwd(),
80 stdio: 'inherit'
81 }
82 )
83}
84
85export default { compile, build, execute }
86
87/**
88 * Print output to stdout
89 *
90 * @param object The object to print
91 * @param format The format use: `json` or `yaml`
92 */
93function output (object: any, format: string = 'json') {
94 if (object) console.log(format === 'yaml' ? yaml.safeDump(object, { lineWidth: 120 }) : JSON.stringify(object, null, ' '))
95}