Skip to content

Commit

Permalink
Merge pull request #59 from stuartleeks/sl/mounts
Browse files Browse the repository at this point in the history
Add mount support
  • Loading branch information
stuartleeks authored Jun 22, 2021
2 parents a4f3cbe + bba01ea commit 50291aa
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 13 deletions.
62 changes: 62 additions & 0 deletions common/__tests__/docker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {parseMount} from '../src/docker'

describe('parseMount', () => {

test('handles type,src,dst', () => {
const input = 'type=bind,src=/my/source,dst=/my/dest'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})
test('handles type,source,destination', () => {
const input = 'type=bind,source=/my/source,destination=/my/dest'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})
test('handles type,source,target', () => {
const input = 'type=bind,source=/my/source,target=/my/dest'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})

test('throws on unexpected option', () => {
const input = 'type=bind,source=/my/source,target=/my/dest,made-up'
const action = () => parseMount(input)
expect(action).toThrow("Unhandled mount option 'made-up'")
})

test('ignores readonly', () => {
const input = 'type=bind,source=/my/source,target=/my/dest,readonly'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})
test('ignores ro', () => {
const input = 'type=bind,source=/my/source,target=/my/dest,ro'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})
test('ignores readonly with value', () => {
const input = 'type=bind,source=/my/source,target=/my/dest,readonly=false'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})
test('ignores ro with value', () => {
const input = 'type=bind,source=/my/source,target=/my/dest,ro=0'
const result = parseMount(input)
expect(result.type).toBe('bind')
expect(result.source).toBe('/my/source')
expect(result.target).toBe('/my/dest')
})

})
72 changes: 69 additions & 3 deletions common/src/docker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from 'path'
import * as fs from 'fs'
import * as config from './config'
import {ExecFunction} from './exec'
import {getAbsolutePath} from './file'
Expand Down Expand Up @@ -70,7 +71,8 @@ export async function runContainer(
checkoutPath: string,
subFolder: string,
command: string,
envs?: string[]
envs?: string[],
mounts?: string[]
): Promise<void> {
const checkoutPathAbsolute = getAbsolutePath(checkoutPath, process.cwd())
const folder = path.join(checkoutPathAbsolute, subFolder)
Expand All @@ -89,13 +91,28 @@ export async function runContainer(
'--mount',
`type=bind,src=${checkoutPathAbsolute},dst=${workspaceFolder}`
)
if (devcontainerConfig.mounts) {
devcontainerConfig.mounts
.map(m => substituteValues(m))
.forEach(m => {
const mount = parseMount(m)
if (mount.type === "bind") {
// check path exists
if (!fs.existsSync(mount.source)) {
console.log(`Skipping mount as source does not exist: '${m}'`)
return;
}
}
args.push('--mount', m)
});
}
args.push('--workdir', workspaceFolder)
args.push('--user', remoteUser)
if (devcontainerConfig.runArgs) {
const subtitutedRunArgs = devcontainerConfig.runArgs.map(a =>
const substitutedRunArgs = devcontainerConfig.runArgs.map(a =>
substituteValues(a)
)
args.push(...subtitutedRunArgs)
args.push(...substitutedRunArgs)
}
if (envs) {
for (const env of envs) {
Expand Down Expand Up @@ -132,3 +149,52 @@ export async function pushImage(exec: ExecFunction, imageName: string): Promise<
// core.endGroup()
}
}

export interface DockerMount {
type: string,
source: string,
target: string,
// ignoring readonly as not relevant
}

export function parseMount(mountString: string): DockerMount {
// https://docs.docker.com/engine/reference/commandline/service_create/#add-bind-mounts-volumes-or-memory-filesystems
// examples:
// type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock
// src=home-cache,target=/home/vscode/.cache

let type = ''
let source = ''
let target = ''

const options= mountString.split(',')

for (const option of options) {
const parts = option.split('=');

switch (parts[0]) {
case 'type':
type = parts[1]
break;
case 'src':
case 'source':
source = parts[1]
break;
case 'dst':
case 'destination':
case 'target':
target = parts[1]
break;

case 'readonly':
case 'ro':
// ignore
break;

default:
throw new Error(`Unhandled mount option '${parts[0]}'`);
}
}

return {type, source, target}
}
8 changes: 7 additions & 1 deletion common_lib/docker.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ExecFunction } from './exec';
export declare function isDockerBuildXInstalled(exec: ExecFunction): Promise<boolean>;
export declare function buildImage(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string): Promise<void>;
export declare function runContainer(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string, command: string, envs?: string[]): Promise<void>;
export declare function runContainer(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string, command: string, envs?: string[], mounts?: string[]): Promise<void>;
export declare function pushImage(exec: ExecFunction, imageName: string): Promise<void>;
export interface DockerMount {
type: string;
source: string;
target: string;
}
export declare function parseMount(mountString: string): DockerMount;
59 changes: 55 additions & 4 deletions common_lib/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pushImage = exports.runContainer = exports.buildImage = exports.isDockerBuildXInstalled = void 0;
exports.parseMount = exports.pushImage = exports.runContainer = exports.buildImage = exports.isDockerBuildXInstalled = void 0;
const path_1 = __importDefault(require("path"));
const fs = __importStar(require("fs"));
const config = __importStar(require("./config"));
const file_1 = require("./file");
const envvars_1 = require("./envvars");
Expand Down Expand Up @@ -85,7 +86,7 @@ function buildImage(exec, imageName, checkoutPath, subFolder) {
});
}
exports.buildImage = buildImage;
function runContainer(exec, imageName, checkoutPath, subFolder, command, envs) {
function runContainer(exec, imageName, checkoutPath, subFolder, command, envs, mounts) {
return __awaiter(this, void 0, void 0, function* () {
const checkoutPathAbsolute = file_1.getAbsolutePath(checkoutPath, process.cwd());
const folder = path_1.default.join(checkoutPathAbsolute, subFolder);
Expand All @@ -95,11 +96,26 @@ function runContainer(exec, imageName, checkoutPath, subFolder, command, envs) {
const remoteUser = config.getRemoteUser(devcontainerConfig);
const args = ['run'];
args.push('--mount', `type=bind,src=${checkoutPathAbsolute},dst=${workspaceFolder}`);
if (devcontainerConfig.mounts) {
devcontainerConfig.mounts
.map(m => envvars_1.substituteValues(m))
.forEach(m => {
const mount = parseMount(m);
if (mount.type === "bind") {
// check path exists
if (!fs.existsSync(mount.source)) {
console.log(`Skipping mount as source does not exist: '${m}'`);
return;
}
}
args.push('--mount', m);
});
}
args.push('--workdir', workspaceFolder);
args.push('--user', remoteUser);
if (devcontainerConfig.runArgs) {
const subtitutedRunArgs = devcontainerConfig.runArgs.map(a => envvars_1.substituteValues(a));
args.push(...subtitutedRunArgs);
const substitutedRunArgs = devcontainerConfig.runArgs.map(a => envvars_1.substituteValues(a));
args.push(...substitutedRunArgs);
}
if (envs) {
for (const env of envs) {
Expand Down Expand Up @@ -138,3 +154,38 @@ function pushImage(exec, imageName) {
});
}
exports.pushImage = pushImage;
function parseMount(mountString) {
// https://docs.docker.com/engine/reference/commandline/service_create/#add-bind-mounts-volumes-or-memory-filesystems
// examples:
// type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock
// src=home-cache,target=/home/vscode/.cache
let type = '';
let source = '';
let target = '';
const options = mountString.split(',');
for (const option of options) {
const parts = option.split('=');
switch (parts[0]) {
case 'type':
type = parts[1];
break;
case 'src':
case 'source':
source = parts[1];
break;
case 'dst':
case 'destination':
case 'target':
target = parts[1];
break;
case 'readonly':
case 'ro':
// ignore
break;
default:
throw new Error(`Unhandled mount option '${parts[0]}'`);
}
}
return { type, source, target };
}
exports.parseMount = parseMount;
8 changes: 7 additions & 1 deletion github-action/dist/docker.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ExecFunction } from './exec';
export declare function isDockerBuildXInstalled(exec: ExecFunction): Promise<boolean>;
export declare function buildImage(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string): Promise<void>;
export declare function runContainer(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string, command: string, envs?: string[]): Promise<void>;
export declare function runContainer(exec: ExecFunction, imageName: string, checkoutPath: string, subFolder: string, command: string, envs?: string[], mounts?: string[]): Promise<void>;
export declare function pushImage(exec: ExecFunction, imageName: string): Promise<void>;
export interface DockerMount {
type: string;
source: string;
target: string;
}
export declare function parseMount(mountString: string): DockerMount;
57 changes: 54 additions & 3 deletions github-action/dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion github-action/dist/index.js.map

Large diffs are not rendered by default.

0 comments on commit 50291aa

Please sign in to comment.