diff --git a/.eslintrc.json b/.eslintrc.json index 04fea0e..7bee487 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,12 +1,30 @@ { "extends": [ "es/2015/server", - "plugin:jest/recommended" + "plugin:jest/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ], - "rules": { - "no-console" :"off" + "parserOptions": { + "project": "./tsconfig.json" }, - "plugins": ["jest"], + "rules": { + "no-console" :"off", + "@typescript-eslint/indent": "off", + "@typescript-eslint/interface-name-prefix": "off", + "indent": [ + "warn", + 2, + { + "SwitchCase": 1 + } + ] + }, + "parser": "@typescript-eslint/parser", + "plugins": [ + "jest", + "@typescript-eslint" + ], "env": { "jest/globals": true } diff --git a/.gitignore b/.gitignore index b6851ff..ccbc68d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ typings/ !.vscode/extensions.json # End of https://www.gitignore.io/api/node,jetbrains,visualstudiocode + +/dist diff --git a/index.d.ts b/index.d.ts index c46b1a9..585806a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,69 +1,70 @@ -export declare function upAll(options?: IDockerComposeOptions): Promise; +export declare const upAll: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function upMany(services: String[], options?: IDockerComposeOptions): Promise; +export declare const upMany: (services: string[], options?: IDockerComposeOptions | undefined) => Promise; -export declare function upOne(service: String, options?: IDockerComposeOptions): Promise; +export declare const upOne: (service: string, options?: IDockerComposeOptions | undefined) => Promise; -export declare function kill(options?: IDockerComposeOptions): Promise; +export declare const kill: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function down(options?: IDockerComposeOptions): Promise; +export declare const down: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function stop(options?: IDockerComposeOptions): Promise; +export declare const stop: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function restartAll(options?: IDockerComposeOptions): Promise; +export declare const restartAll: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function restartMany(services: String[], options?: IDockerComposeOptions): Promise; +export declare const restartMany: (services: string[], options?: IDockerComposeOptions | undefined) => Promise; -export declare function restartOne(service: String, options?: IDockerComposeOptions): Promise; +export declare const restartOne: (service: string, options?: IDockerComposeOptions | undefined) => Promise; -export declare function rm(options?: IDockerComposeOptions): Promise; +export declare const rm: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function exec(container: String, command: String | String[], options?: IDockerComposeOptions): Promise; +export declare const exec: (container: string, command: string | string[], options?: IDockerComposeOptions | undefined) => Promise; -export declare function logs(services: String[], options?: IDockerComposeLogOptions): Promise; +export declare const logs: (services: string | string[], options?: IDockerComposeLogOptions) => Promise; -export declare function run(service: String, command: String | String[], options?: IDockerComposeOptions): Promise; +export declare const run: (container: string, command: string | string[], options?: IDockerComposeOptions | undefined) => Promise; -export declare function buildAll(options?: IDockerComposeBuildOptions): Promise; +export declare const buildAll: (options?: IDockerComposeBuildOptions) => Promise; -export declare function buildMany(services: String[], options?: IDockerComposeBuildOptions): Promise; +export declare const buildMany: (services: string[], options?: IDockerComposeBuildOptions) => Promise; -export declare function buildOne(service: String, options?: IDockerComposeOptions): Promise; +export declare const buildOne: (service: string, options?: IDockerComposeBuildOptions | undefined) => Promise; -export declare function config(options?: IDockerComposeOptions): Promise; +export declare const config: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function configServices(options?: IDockerComposeOptions): Promise; +export declare const configServices: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function configVolumes(options?: IDockerComposeOptions): Promise; +export declare const configVolumes: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function ps(options?: IDockerComposeOptions): Promise; +export declare const ps: (options?: IDockerComposeOptions | undefined) => Promise; -export declare function push(options?: IDockerComposePushOptions): Promise; +export declare const push: (options?: IDockerComposePushOptions) => Promise; -export declare function port(service: String, containerPort: String | Number, options?: IDockerComposeOptions): Promise; +export declare const port: (service: string, containerPort: string | number, options?: IDockerComposeOptions | undefined) => Promise; interface IDockerComposeOptions { - cwd?: string; - config?: string | string[]; - log?: boolean; - composeOptions?: string[] | Array; - commandOptions?: string[] | Array; + cwd?: string; + config?: string | string[]; + log?: boolean; + composeOptions?: string[] | (string | string[])[]; + commandOptions?: string[] | (string | string[])[]; + env?: string; } interface IDockerComposeLogOptions extends IDockerComposeOptions { - follow: boolean; + follow?: boolean; } interface IDockerComposeBuildOptions extends IDockerComposeOptions { - parallel?: boolean; + parallel?: boolean; } interface IDockerComposePushOptions extends IDockerComposeOptions { - ignorePushFailures?: boolean; + ignorePushFailures?: boolean; } interface IDockerComposeResult { - exitCode?: number; - out: string; - err: string; + exitCode: number | null; + out: string; + err: string; } diff --git a/index.js b/index.js deleted file mode 100644 index 54083f7..0000000 --- a/index.js +++ /dev/null @@ -1,454 +0,0 @@ -'use strict'; - -const childProcess = require('child_process'); - -/** - * Converts supplied yml files to cli arguments - * https://docs.docker.com/compose/reference/overview/#use--f-to-specify-name-and-path-of-one-or-more-compose-files - * @param {?(string|string[])} config - */ -const configToArgs = config => { - if (typeof config === 'undefined') { - return []; - } else if (typeof config === 'string') { - return [ '-f', config ]; - } else if (config instanceof Array) { - return config.reduce((args, item) => args.concat([ '-f', item ]), []); - } - throw new Error(`Invalid argument supplied: ${config}`); -}; - -/** - * Converts docker-compose commandline options to cli arguments - * @param {string[]|Array} composeOptions - * @return {Array} - */ -const composeOptionsToArgs = function (composeOptions) { - let composeArgs = []; - - composeOptions.forEach(option => { - if (option instanceof Array) { - composeArgs = composeArgs.concat(option); - } - if (typeof option === 'string') { - composeArgs = composeArgs.concat([ option ]); - } - }); - - return composeArgs; -}; - -/** - * Executes docker-compose command with common options - * @param {string} command - * @param {string[]} args - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.commandOptions] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const execCompose = (command, args, options) => new Promise((resolve, reject) => { - if (typeof options === 'undefined') { - options = {}; - } - const composeOptions = options.composeOptions || []; - const commandOptions = options.commandOptions || []; - let composeArgs = composeOptionsToArgs(composeOptions); - - composeArgs = composeArgs.concat(configToArgs(options.config).concat([ command ].concat(composeOptionsToArgs(commandOptions), args))); - - const cwd = options.cwd; - const env = options.env || null; - - const childProc = childProcess.spawn('docker-compose', composeArgs, { cwd, env }); - - childProc.on('error', err => { - reject(err); - }); - - const result = { - exitCode: null, - err: '', - out: '' - }; - - childProc.stdout.on('data', chunk => { - result.out += chunk.toString(); - }); - - childProc.stderr.on('data', chunk => { - result.err += chunk.toString(); - }); - - childProc.on('exit', exitCode => { - result.exitCode = exitCode; - if (exitCode === 0) { - resolve(result); - } else { - reject(result); - } - }); - - if (options.log) { - childProc.stdout.pipe(process.stdout); - childProc.stderr.pipe(process.stderr); - } -}); - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const upAll = function (options) { - return execCompose('up', [ '-d' ], options); -}; - -/** - * @param {string[]} services - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const upMany = function (services, options) { - return execCompose('up', [ '-d' ].concat(services), options); -}; - -/** - * @param {string} service - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const upOne = function (service, options) { - return execCompose('up', [ '-d', service ], options); -}; - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const down = function (options) { - return execCompose('down', [], options); -}; - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const stop = function (options) { - return execCompose('stop', [], options); -}; - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const kill = function (options) { - return execCompose('kill', [], options); -}; - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const rm = function (options) { - return execCompose('rm', [ '-f' ], options); -}; - -/** - * Execute command in a running container - * @param {string} container container name - * @param {string|string[]} command command to execute - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const exec = function (container, command, options) { - const args = Array.isArray(command) ? command : command.split(/\s+/); - - return execCompose('exec', [ '-T', container ].concat(args), options); -}; - -/** - * Run command - * @param {string} container container name - * @param {string|string[]} command command to execute - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const run = function (container, command, options) { - const args = Array.isArray(command) ? command : command.split(/\s+/); - - return execCompose('run', [ '-T', container ].concat(args), options); -}; - -/** - * Build command - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?boolean} [options.parallel] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const buildAll = function (options) { - return execCompose( - 'build', - options.parallel ? [ '--parallel' ] : [], - options - ); -}; - -/** - * Build command - * @param {string[]} services list of service names - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?boolean} [options.parallel] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * - * @return {object} std.out / std.err - */ -const buildMany = function (services, options) { - return execCompose( - 'build', - options.parallel ? [ '--parallel' ].concat(services) : services, - options - ); -}; - -/** - * Build command - * @param {string} service service name - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const buildOne = function (service, options) { - return execCompose('build', [ service ], options); -}; - -/** - * Config command - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const config = function (options) { - return execCompose('config', [], options); -}; - -/** - * Config command with --services option - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const configServices = function (options) { - return execCompose('config', [ '--services' ], options); -}; - -/** - * Config command with --volumes option - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - * - * @return {object} std.out / std.err - */ -const configVolumes = function (options) { - return execCompose('config', [ '--volumes' ], options); -}; - -/** - * Ps command - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const ps = function (options) { - return execCompose('ps', [], options); -}; - -/** - * Push command - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?boolean} options.ignorePushFailures - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const push = function (options) { - return execCompose( - 'push', - options.ignorePushFailures ? [ '--ignore-push-failures' ] : [], - options - ); -}; - -/** - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const restartAll = function (options) { - return execCompose('restart', [], options); -}; - -/** - * @param {string[]} services - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const restartMany = function (services, options) { - return execCompose('restart', services, options); -}; - -/** - * @param {string} service - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const restartOne = function (service, options) { - return restartMany([ service ], options); -}; - -/** - * @param {?(string|string[])} services - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {boolean} [options.follow] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const logs = function (services, options) { - let args = Array.isArray(services) ? services : [ services ]; - - if (options.follow) { - args = [ '--follow', ...args ]; - } - - return execCompose('logs', args, options); -}; - -/** - * @param {string} service - * @param {string|number} containerPort - * @param {object} options - * @param {string} options.cwd - * @param {boolean} [options.log] - * @param {?(string|string[])} [options.config] - * @param {?object} [options.env] - * @param {?(string[]|Array)} [options.composeOptions] - */ -const port = function (service, containerPort, options) { - const args = [ service, containerPort ]; - - return execCompose('port', args, options); -}; - -module.exports = { - upAll, - upMany, - upOne, - kill, - down, - stop, - rm, - exec, - logs, - restartAll, - restartMany, - restartOne, - run, - buildAll, - buildMany, - buildOne, - ps, - config, - configServices, - configVolumes, - push, - port -}; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..517d75d --- /dev/null +++ b/jest.config.js @@ -0,0 +1,187 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // Respect "browser" field in package.json when resolving modules + // browser: false, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + // clearMocks: false, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: null, + + // The directory where Jest should output its coverage files + coverageDirectory: 'coverage', + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: null, + + // A path to a custom dependency extractor + // dependencyExtractor: null, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: null, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: null, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: null, + + // Run tests from one or more projects + // projects: null, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: null, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: null, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // '/src' + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: 'node', + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: null, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + transform: { + '^.+\\.tsx?$': 'ts-jest' + } + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: null, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/package-lock.json b/package-lock.json index 9702603..6740a66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -465,6 +465,12 @@ "@babel/types": "^7.3.0" } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -490,6 +496,27 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "24.0.15", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.15.tgz", + "integrity": "sha512-MU1HIvWUme74stAoc3mgAi+aMlgKOudgEvQDIm1v4RkrDudBh1T+NFp5sftpBAdXdx1J0PbdpJ+M2EsSOi1djA==", + "dev": true, + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", + "dev": true + }, + "@types/node": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.2.tgz", + "integrity": "sha512-gojym4tX0FWeV2gsW4Xmzo5wxGjXGm550oVUII7f7G5o4BV6c7DBdiG1RRQd+y1bvqRyYtPfMK85UM95vsapqQ==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -502,6 +529,67 @@ "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.12.0.tgz", + "integrity": "sha512-J/ZTZF+pLNqjXBGNfq5fahsoJ4vJOkYbitWPavA05IrZ7BXUaf4XWlhUB/ic1lpOGTRpLWF+PLAePjiHp6dz8g==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.12.0", + "eslint-utils": "^1.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^2.0.1", + "tsutils": "^3.7.0" + }, + "dependencies": { + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.12.0.tgz", + "integrity": "sha512-s0soOTMJloytr9GbPteMLNiO2HvJ+qgQkRNplABXiVw6vq7uQRvidkby64Gqt/nA7pys74HksHwRULaB/QRVyw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.12.0", + "eslint-scope": "^4.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.12.0.tgz", + "integrity": "sha512-0uzbaa9ZLCA5yMWJywnJJ7YVENKGWVUhJDV5UrMoldC5HoI54W5kkdPhTfmtFKpPFp93MIwmJj0/61ztvmz5Dw==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.12.0", + "@typescript-eslint/typescript-estree": "1.12.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.12.0.tgz", + "integrity": "sha512-nwN6yy//XcVhFs0ZyU+teJHB8tbCm7AIA8mu6E2r5hu6MajwYBY3Uwop7+rPZWUN/IUOHpL8C+iUPMDVYUU3og==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, "JSONStream": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", @@ -939,6 +1027,15 @@ } } }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, "bser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", @@ -1303,12 +1400,6 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1366,12 +1457,6 @@ } } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -1999,15 +2084,6 @@ "write": "^0.2.1" } }, - "for-each": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", - "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", - "dev": true, - "requires": { - "is-function": "~1.0.0" - } - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3008,12 +3084,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", - "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", - "dev": true - }, "is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -3952,6 +4022,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3985,6 +4061,12 @@ } } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", @@ -4341,12 +4423,6 @@ } } }, - "object-inspect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.2.2.tgz", - "integrity": "sha1-yCEV5PzIiK6hTWTCLk8X9qcNXlo=", - "dev": true - }, "object-keys": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", @@ -4965,15 +5041,6 @@ "signal-exit": "^3.0.2" } }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true, - "requires": { - "through": "~2.3.4" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -5455,17 +5522,6 @@ "strip-ansi": "^4.0.0" } }, - "string.prototype.trim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", - "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.0", - "function-bind": "^1.0.2" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5531,62 +5587,6 @@ "string-width": "^2.1.1" } }, - "tape": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-4.6.3.tgz", - "integrity": "sha1-Y353WB6ass4XV36b1M5PV1gG2LY=", - "dev": true, - "requires": { - "deep-equal": "~1.0.1", - "defined": "~1.0.0", - "for-each": "~0.3.2", - "function-bind": "~1.1.0", - "glob": "~7.1.1", - "has": "~1.0.1", - "inherits": "~2.0.3", - "minimist": "~1.2.0", - "object-inspect": "~1.2.1", - "resolve": "~1.1.7", - "resumer": "~0.0.0", - "string.prototype.trim": "~1.1.2", - "through": "~2.3.8" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "tape-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tape-promise/-/tape-promise-2.0.1.tgz", - "integrity": "sha1-dryf91CLPgV9CW9ErJqTw8rk7TQ=", - "dev": true, - "requires": { - "is-promise": "^2.1.0", - "onetime": "^2.0.0" - }, - "dependencies": { - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - } - } - }, "tar-fs": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", @@ -5775,12 +5775,55 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "ts-jest": { + "version": "24.0.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", + "integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, + "tsutils": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz", + "integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -5811,6 +5854,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "dev": true + }, "uglify-js": { "version": "3.5.15", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.15.tgz", diff --git a/package.json b/package.json index 0402859..9624e80 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,14 @@ { "name": "docker-compose", "version": "0.19.0", - "main": "index.js", - "typings": "index.d.ts", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "types": "dist/index.d.ts", "scripts": { "test": "jest test --verbose", - "eslint": "./node_modules/.bin/eslint ." + "eslint": "./node_modules/.bin/eslint .", + "build": "tsc", + "prepublish": "tsc" }, "repository": { "url": "git@github.com:PDMLab/docker-compose.git" @@ -54,6 +57,10 @@ "license": "MIT", "description": "Manage docker-compose from Node.js", "devDependencies": { + "@types/jest": "^24.0.15", + "@types/node": "^12.6.2", + "@typescript-eslint/eslint-plugin": "^1.12.0", + "@typescript-eslint/parser": "^1.12.0", "composefile": "^0.3.0", "dockerode": "^2.5.7", "eslint": "^5.6.0", @@ -61,7 +68,7 @@ "eslint-plugin-extended": "^0.2.0", "eslint-plugin-jest": "^22.6.4", "jest": "^24.8.0", - "tape": "^4.6.3", - "tape-promise": "^2.0.1" + "ts-jest": "^24.0.2", + "typescript": "^3.5.3" } } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..76b65a8 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,221 @@ +import childProcess from 'child_process'; + +interface IDockerComposeOptions { + cwd?: string; + config?: string | string[]; + log?: boolean; + composeOptions?: string[] | (string | string[])[]; + commandOptions?: string[] | (string | string[])[]; + env?: NodeJS.ProcessEnv; +} + +interface IDockerComposeLogOptions extends IDockerComposeOptions { + follow?: boolean; +} + +interface IDockerComposeBuildOptions extends IDockerComposeOptions { + parallel?: boolean; +} + +interface IDockerComposePushOptions extends IDockerComposeOptions { + ignorePushFailures?: boolean; +} + +interface IDockerComposeResult { + exitCode: number | null; + out: string; + err: string; +} + +/** + * Converts supplied yml files to cli arguments + * https://docs.docker.com/compose/reference/overview/#use--f-to-specify-name-and-path-of-one-or-more-compose-files + */ +const configToArgs = (config): string[] => { + if (typeof config === 'undefined') { + return []; + } else if (typeof config === 'string') { + return [ '-f', config ]; + } else if (config instanceof Array) { + return config.reduce((args, item): string[] => args.concat([ '-f', item ]), []); + } + throw new Error(`Invalid argument supplied: ${config}`); +}; + +/** + * Converts docker-compose commandline options to cli arguments + */ +const composeOptionsToArgs = (composeOptions): string[] => { + let composeArgs: string[] = []; + + composeOptions.forEach((option: string[] | string): void => { + if (option instanceof Array) { + composeArgs = composeArgs.concat(option); + } + if (typeof option === 'string') { + composeArgs = composeArgs.concat([ option ]); + } + }); + + return composeArgs; +}; + +/** + * Executes docker-compose command with common options + */ +const execCompose = (command, args, options: IDockerComposeOptions = {}): Promise => new Promise((resolve, reject): void => { + const composeOptions = options.composeOptions || []; + const commandOptions = options.commandOptions || []; + let composeArgs = composeOptionsToArgs(composeOptions); + + composeArgs = composeArgs.concat(configToArgs(options.config).concat([ command ].concat(composeOptionsToArgs(commandOptions), args))); + + const cwd = options.cwd; + const env = options.env || undefined; + + const childProc = childProcess.spawn('docker-compose', composeArgs, { cwd, env }); + + childProc.on('error', (err): void => { + reject(err); + }); + + const result: IDockerComposeResult = { + exitCode: null, + err: '', + out: '' + }; + + childProc.stdout.on('data', (chunk): void => { + result.out += chunk.toString(); + }); + + childProc.stderr.on('data', (chunk): void => { + result.err += chunk.toString(); + }); + + childProc.on('exit', (exitCode): void => { + result.exitCode = exitCode; + if (exitCode === 0) { + resolve(result); + } else { + reject(result); + } + }); + + if (options.log) { + childProc.stdout.pipe(process.stdout); + childProc.stderr.pipe(process.stderr); + } +}); + +export const upAll = function (options?: IDockerComposeOptions): Promise { + return execCompose('up', [ '-d' ], options); +}; + +export const upMany = function (services, options?: IDockerComposeOptions): Promise { + return execCompose('up', [ '-d' ].concat(services), options); +}; + +export const upOne = function (service, options?: IDockerComposeOptions): Promise { + return execCompose('up', [ '-d', service ], options); +}; + +export const down = function (options?: IDockerComposeOptions): Promise { + return execCompose('down', [], options); +}; + +export const stop = function (options?: IDockerComposeOptions): Promise { + return execCompose('stop', [], options); +}; + +export const kill = function (options?: IDockerComposeOptions): Promise { + return execCompose('kill', [], options); +}; + +export const rm = function (options?: IDockerComposeOptions): Promise { + return execCompose('rm', [ '-f' ], options); +}; + +export const exec = function (container: string, command: string | string[], options?: IDockerComposeOptions): Promise { + const args = Array.isArray(command) ? command : command.split(/\s+/); + + return execCompose('exec', [ '-T', container ].concat(args), options); +}; + +export const run = function (container: string, command: string | string[], options?: IDockerComposeOptions): Promise { + const args = Array.isArray(command) ? command : command.split(/\s+/); + + return execCompose('run', [ '-T', container ].concat(args), options); +}; + +export const buildAll = function (options: IDockerComposeBuildOptions = {}): Promise { + return execCompose( + 'build', + options.parallel ? [ '--parallel' ] : [], + options + ); +}; + +export const buildMany = function (services: string[], options: IDockerComposeBuildOptions = {}): Promise { + return execCompose( + 'build', + options.parallel ? [ '--parallel' ].concat(services) : services, + options + ); +}; + +export const buildOne = function (service: string, options?: IDockerComposeBuildOptions): Promise { + return execCompose('build', [ service ], options); +}; + +export const config = function (options?: IDockerComposeOptions): Promise { + return execCompose('config', [], options); +}; + +export const configServices = function (options?: IDockerComposeOptions): Promise { + return execCompose('config', [ '--services' ], options); +}; + +export const configVolumes = function (options?: IDockerComposeOptions): Promise { + return execCompose('config', [ '--volumes' ], options); +}; + +export const ps = function (options?: IDockerComposeOptions): Promise { + return execCompose('ps', [], options); +}; + +export const push = function (options: IDockerComposePushOptions = {}): Promise { + return execCompose( + 'push', + options.ignorePushFailures ? [ '--ignore-push-failures' ] : [], + options + ); +}; + +export const restartAll = function (options?: IDockerComposeOptions): Promise { + return execCompose('restart', [], options); +}; + +export const restartMany = function (services: string[], options?: IDockerComposeOptions): Promise { + return execCompose('restart', services, options); +}; + +export const restartOne = function (service: string, options?: IDockerComposeOptions): Promise { + return restartMany([ service ], options); +}; + +export const logs = function (services: string | string[], options: IDockerComposeLogOptions = {}): Promise { + let args = Array.isArray(services) ? services : [ services ]; + + if (options.follow) { + args = [ '--follow', ...args ]; + } + + return execCompose('logs', args, options); +}; + +export const port = function (service: string, containerPort: string | number, options?: IDockerComposeOptions): Promise { + const args = [ service, containerPort ]; + + return execCompose('port', args, options); +}; diff --git a/test/index.test.js b/test/index.test.ts similarity index 81% rename from test/index.test.js rename to test/index.test.ts index 9340a65..11add9d 100644 --- a/test/index.test.js +++ b/test/index.test.ts @@ -1,8 +1,7 @@ -'use strict'; +import Docker from 'dockerode'; +import * as compose from '../src/index'; +import * as path from 'path'; -const compose = require('../index'); -const path = require('path'); -const Docker = require('dockerode'); const docker = new Docker(); // Docker commands, especially builds, can take some time. This makes sure that they can take the time they need. @@ -11,29 +10,29 @@ jest.setTimeout(25000); // Set to true if you need to diagnose using output const logOutput = false; -const isContainerRunning = async name => new Promise((resolve, reject) => { - docker.listContainers((err, containers) => { +const isContainerRunning = async (name: string): Promise => new Promise((resolve, reject): void => { + docker.listContainers((err, containers): void => { if (err) { reject(err); } - const running = containers.filter(container => container.Names.includes(name)); + const running = containers.filter((container): boolean => container.Names.includes(name)); resolve(running.length > 0); }); }); -const repoTags = imageInfo => imageInfo.RepoTags || []; +const repoTags = (imageInfo): string[] => imageInfo.RepoTags || []; -const imageExists = async name => { +const imageExists = async (name: string): Promise => { const images = await docker.listImages(); - const foundImage = images.findIndex(imageInfo => repoTags(imageInfo).includes(name)); + const foundImage = images.findIndex((imageInfo): boolean => repoTags(imageInfo).includes(name)); return foundImage > -1; }; -const removeImagesStartingWith = async searchString => { +const removeImagesStartingWith = async (searchString: string): Promise => { const images = await docker.listImages(); for (const image of images) { @@ -50,7 +49,7 @@ const removeImagesStartingWith = async searchString => { } }; -test('ensure container gets started', async () => { +test('ensure container gets started', async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); @@ -58,7 +57,7 @@ test('ensure container gets started', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure exit code is returned correctly', async () => { +test('ensure exit code is returned correctly', async (): Promise => { let result = await compose.down({ cwd: path.join(__dirname), log: logOutput }); await expect(result).toMatchObject({ @@ -78,16 +77,16 @@ test('ensure exit code is returned correctly', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -describe('starts containers properly with --build and --timeout options', () => { - beforeEach(async () => { +describe('starts containers properly with --build and --timeout options', (): void => { + beforeEach(async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-build.yml' }); }); - afterEach(async () => { + afterEach(async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-build.yml' }); }); - test('ensure container gets started with --build option', async () => { + test('ensure container gets started with --build option', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput, @@ -98,7 +97,7 @@ describe('starts containers properly with --build and --timeout options', () => expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy(); }); - test('ensure container gets started with --build and --timeout option', async () => { + test('ensure container gets started with --build and --timeout option', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput, @@ -109,7 +108,7 @@ describe('starts containers properly with --build and --timeout options', () => expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy(); }); - test('ensure container gets started with --build and --timeout option with different command style', async () => { + test('ensure container gets started with --build and --timeout option with different command style', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput, @@ -121,7 +120,7 @@ describe('starts containers properly with --build and --timeout options', () => }); }); -test('ensure container command executed with --workdir command option', async () => { +test('ensure container command executed with --workdir command option', async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' }); const result = await compose.run('some-service', 'pwd', { cwd: path.join(__dirname), @@ -138,7 +137,7 @@ test('ensure container command executed with --workdir command option', async () await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' }); }); -test('ensure only single container gets started', async () => { +test('ensure only single container gets started', async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); await compose.upOne('alpine', { cwd: path.join(__dirname), log: logOutput }); @@ -147,7 +146,7 @@ test('ensure only single container gets started', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure only multiple containers get started', async () => { +test('ensure only multiple containers get started', async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); await compose.upMany([ 'alpine' ], { cwd: path.join(__dirname), log: logOutput }); @@ -156,7 +155,7 @@ test('ensure only multiple containers get started', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure container gets down', async () => { +test('ensure container gets down', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy(); @@ -164,7 +163,7 @@ test('ensure container gets down', async () => { expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy(); }); -test('ensure container gets stopped', async () => { +test('ensure container gets stopped', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy(); @@ -173,7 +172,7 @@ test('ensure container gets stopped', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure container gets killed', async () => { +test('ensure container gets killed', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy(); @@ -183,7 +182,7 @@ test('ensure container gets killed', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure custom ymls are working', async () => { +test('ensure custom ymls are working', async (): Promise => { const config = './docker-compose-2.yml'; const cwd = path.join(__dirname); @@ -197,12 +196,12 @@ test('ensure custom ymls are working', async () => { await compose.down({ cwd, log: logOutput, config }); }); -test('ensure run and exec are working', async () => { - const checkOSID = (out, id) => { +test('ensure run and exec are working', async (): Promise => { + const checkOSID = (out, id): void => { // parse /etc/os-release contents const re = /([\w,_]+)=(.*)/g; let match; - const os = {}; + const os: {ID?: string} = {}; while ((match = re.exec(out)) !== null) { // eslint-disable-line no-cond-assign os[match[1]] = match[2]; @@ -229,12 +228,12 @@ test('ensure run and exec are working', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ensure run and exec with command defined as array are working', async () => { - const checkOSID = (out, id) => { +test('ensure run and exec with command defined as array are working', async (): Promise => { + const checkOSID = (out, id): void => { // parse /etc/os-release contents const re = /([\w,_]+)=(.*)/g; let match; - const os = {}; + const os: {ID?: string} = {}; while ((match = re.exec(out)) !== null) { // eslint-disable-line no-cond-assign os[match[1]] = match[2]; @@ -261,7 +260,7 @@ test('ensure run and exec with command defined as array are working', async () = await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('build single service', async () => { +test('build single service', async (): Promise => { const opts = { cwd: path.join(__dirname), log: logOutput, @@ -280,7 +279,7 @@ test('build single service', async () => { await removeImagesStartingWith('compose-test-build-image'); }); -test('build multiple services', async () => { +test('build multiple services', async (): Promise => { const opts = { cwd: path.join(__dirname), log: logOutput, @@ -297,7 +296,7 @@ test('build multiple services', async () => { await removeImagesStartingWith('compose-test-build-image'); }); -test('build all services', async () => { +test('build all services', async (): Promise => { const opts = { cwd: path.join(__dirname), log: logOutput, @@ -314,14 +313,19 @@ test('build all services', async () => { await removeImagesStartingWith('compose-test-build-image'); }); -test('teardown', async () => { - docker.listContainers((err, containers) => { +test('teardown', async (): Promise => { + interface Container { + Names: string[]; + Id: string; + } + + docker.listContainers((err, containers: Container[]): void => { if (err) { throw err; } - containers.forEach(container => { - container.Names.forEach(name => { + containers.forEach((container): void => { + container.Names.forEach((name: string): void => { if (name.startsWith('/compose_test_')) { console.log(`stopping ${container.Id} ${container.Names}`); docker.getContainer(container.Id).stop(); @@ -333,7 +337,7 @@ test('teardown', async () => { await removeImagesStartingWith('compose-test-build-image'); }); -test('config show data for docker-compose files', async () => { +test('config show data for docker-compose files', async (): Promise => { const std = await compose.config({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' }); expect(std.err).toBeFalsy(); @@ -341,21 +345,21 @@ test('config show data for docker-compose files', async () => { expect(std.out.includes('test/volume:/mountedvolume:rw')).toBeTruthy(); }); -test('config show data for docker-compose files (services)', async () => { +test('config show data for docker-compose files (services)', async (): Promise => { const std = await compose.configServices({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' }); expect(std.err).toBeFalsy(); expect(std.out.includes('some-service')).toBeTruthy(); }); -test('config show data for docker-compose files (volumes)', async () => { +test('config show data for docker-compose files (volumes)', async (): Promise => { const std = await compose.configVolumes({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' }); expect(std.err).toBeFalsy(); expect(std.out.includes('db-data')).toBeTruthy(); }); -test('ps shows status data for started containers', async () => { +test('ps shows status data for started containers', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); const std = await compose.ps({ cwd: path.join(__dirname), log: logOutput }); @@ -366,7 +370,7 @@ test('ps shows status data for started containers', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('ps does not show status data for stopped containers', async () => { +test('ps does not show status data for stopped containers', async (): Promise => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); await compose.upOne('alpine', { cwd: path.join(__dirname), log: logOutput }); @@ -378,7 +382,7 @@ test('ps does not show status data for stopped containers', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('restartAll does restart all containers', async () => { +test('restartAll does restart all containers', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); await compose.restartAll({ cwd: path.join(__dirname), log: logOutput }); @@ -386,7 +390,7 @@ test('restartAll does restart all containers', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('restartMany does restart selected containers', async () => { +test('restartMany does restart selected containers', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); await compose.restartMany([ 'db', 'alpine' ], { cwd: path.join(__dirname), log: logOutput }); @@ -394,7 +398,7 @@ test('restartMany does restart selected containers', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('restartOne does restart container', async () => { +test('restartOne does restart container', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); await compose.restartOne('db', { cwd: path.join(__dirname), log: logOutput }); @@ -402,7 +406,7 @@ test('restartOne does restart container', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('logs does follow service logs', async () => { +test('logs does follow service logs', async (): Promise => { await compose.upAll({ cwd: path.join(__dirname), log: logOutput }); await compose.logs('db', { cwd: path.join(__dirname), log: logOutput }); @@ -410,7 +414,7 @@ test('logs does follow service logs', async () => { await compose.down({ cwd: path.join(__dirname), log: logOutput }); }); -test('returns the port for a started service', async () => { +test('returns the port for a started service', async (): Promise => { const config = { cwd: path.join(__dirname), config: './docker-compose-2.yml', diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..769d5dd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "exclude": [ + "node_modules", + "dist", + "test" + ] +}