mirror of
https://github.com/danbulant/docker-compose
synced 2026-06-17 21:51:08 +00:00
Compare commits
158 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb173b0dd | ||
|
|
18a9079ee6 | ||
|
|
748ba3e368 | ||
|
|
192979ffcc | ||
|
|
f60e4d5a18 | ||
|
|
dd4fe76eab | ||
|
|
6b9ab6150c | ||
|
|
a28e801590 | ||
|
|
b0ab29bbb3 | ||
|
|
d71e043be8 | ||
|
|
764f33fe1b | ||
|
|
9f84b89a45 | ||
|
|
d7fe723db3 | ||
|
|
74b4d6ea22 | ||
|
|
5921b51977 | ||
|
|
91abbfe63a | ||
|
|
639f9ceffd | ||
|
|
a3fb7c09bd | ||
|
|
26a3f0e9d8 | ||
|
|
866ac8d595 | ||
|
|
f7166c50c4 | ||
|
|
2fea41e6c1 | ||
|
|
1a7fec4601 | ||
|
|
c4b60872ae | ||
|
|
7eaf706be9 | ||
|
|
3b8aaec914 | ||
|
|
d9ed460405 | ||
|
|
0ebbc6f357 | ||
|
|
f5fb0b3907 | ||
|
|
5e969f588e | ||
|
|
e9bb9824a6 | ||
|
|
53fb97196f | ||
|
|
6dec0c6bb6 | ||
|
|
dbdb9008f2 | ||
|
|
8aa846553c | ||
|
|
21b63cbc53 | ||
|
|
48c9f0841a | ||
|
|
e7013dfab1 | ||
|
|
963b51573f | ||
|
|
361131a76b | ||
|
|
432413f087 | ||
|
|
44f0e9eeb3 | ||
|
|
58f09f4c74 | ||
|
|
876a032001 | ||
|
|
17aaf53a12 | ||
|
|
a3d0a52e36 | ||
|
|
e58387d82e | ||
|
|
524fe42a07 | ||
|
|
2de793b145 | ||
|
|
9c43efa2b2 | ||
|
|
c7c6f01658 | ||
|
|
2b089ff660 | ||
|
|
b487be048b | ||
|
|
57f00b36fe | ||
|
|
9443c7ebdc | ||
|
|
3be99e8360 | ||
|
|
880d2522b5 | ||
|
|
ed91eba770 | ||
|
|
51b20b43d6 | ||
|
|
6f105ca160 | ||
|
|
a2f5a4ec0e | ||
|
|
a7da0380a8 | ||
|
|
90421b7eb2 | ||
|
|
aa26e83884 | ||
|
|
70a98f473b | ||
|
|
b6eec0047d | ||
|
|
ecf4ba5e2b | ||
|
|
bfd45f47fa | ||
|
|
1bc50ed7a0 | ||
|
|
f83686e30e | ||
|
|
8d3c162015 | ||
|
|
bf8210ae9b | ||
|
|
46cb725156 | ||
|
|
bdbfc17de1 | ||
|
|
f85e6885dd | ||
|
|
ad6e008e1a | ||
|
|
a59476f65d | ||
|
|
d0bd17c8ec | ||
|
|
3495f93468 | ||
|
|
397000087c | ||
|
|
00f90c9620 | ||
|
|
c9abc11ffc | ||
|
|
afb2b1116b | ||
|
|
d2df827d0f | ||
|
|
af6d77b308 | ||
|
|
29cbab6298 | ||
|
|
ceb7ed6510 | ||
|
|
483e6827ad | ||
|
|
98c271de01 | ||
|
|
60e278d566 | ||
|
|
a8e9c2852f | ||
|
|
c74fc36909 | ||
|
|
f1c2c7e8c1 | ||
|
|
e8c14d318c | ||
|
|
4983b2b8dd | ||
|
|
a18b135a1d | ||
|
|
664b8d4eb6 | ||
|
|
dfc16820c8 | ||
|
|
6bdcfdff12 | ||
|
|
c808fa2b90 | ||
|
|
7a1f05e07b | ||
|
|
3a68db75e3 | ||
|
|
dc5bf430ff | ||
|
|
1b284097eb | ||
|
|
26902d2411 | ||
|
|
c4ed7ed2de | ||
|
|
e85af8c092 | ||
|
|
2f573eef17 | ||
|
|
558b458b3b | ||
|
|
269922cf7e | ||
|
|
c6b2ef848f | ||
|
|
5c74430011 | ||
|
|
0ce845e747 | ||
|
|
f30da27354 | ||
|
|
85fd435e5f | ||
|
|
5687076f1e | ||
|
|
33d04d0a2f | ||
|
|
a214979ac4 | ||
|
|
5b72a35f56 | ||
|
|
db73df9b0f | ||
|
|
1585dbb62a | ||
|
|
b30ae3b7a0 | ||
|
|
6ca763c3e2 | ||
|
|
fbc6147c74 | ||
|
|
662707e9f5 | ||
|
|
cf543345a8 | ||
|
|
b8287ea1f8 | ||
|
|
5f2a037ffc | ||
|
|
0a7368edf7 | ||
|
|
6651885df5 | ||
|
|
575a5b25ea | ||
|
|
6461c49813 | ||
|
|
fe0b9c3d2c | ||
|
|
bdd539ce8f | ||
|
|
98dc30de0f | ||
|
|
a568ed756a | ||
|
|
d1d3435161 | ||
|
|
30752fdb7a | ||
|
|
9b00f3021e | ||
|
|
3cea146b6e | ||
|
|
61d46416ef | ||
|
|
3c3c076cb1 | ||
|
|
01afd0a9b5 | ||
|
|
3d416f9eec | ||
|
|
21809b6292 | ||
|
|
9c3013be4f | ||
|
|
e8b5116230 | ||
|
|
4094b19e1b | ||
|
|
2a273a86ef | ||
|
|
ee41dc2b92 | ||
|
|
ebdd55bdfe | ||
|
|
ea089eed57 | ||
|
|
f7da6c26b6 | ||
|
|
cb511f11c1 | ||
|
|
4201f8469c | ||
|
|
2108801146 | ||
|
|
38aade6f02 | ||
|
|
afc95c16f1 |
33 changed files with 11110 additions and 7471 deletions
|
|
@ -1,39 +0,0 @@
|
||||||
# Javascript Node CircleCI 2.0 configuration file
|
|
||||||
#
|
|
||||||
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
|
|
||||||
#
|
|
||||||
version: 2
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
# specify the version you desire here
|
|
||||||
- image: circleci/node:8
|
|
||||||
|
|
||||||
# Specify service dependencies here if necessary
|
|
||||||
# CircleCI maintains a library of pre-built images
|
|
||||||
# documented at https://circleci.com/docs/2.0/circleci-images/
|
|
||||||
# - image: circleci/mongo:3.4.4
|
|
||||||
|
|
||||||
working_directory: ~/repo
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- setup_remote_docker
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
# Download and cache dependencies
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- v1-dependencies-{{ checksum "package.json" }}
|
|
||||||
# fallback to using the latest cache if no exact match is found
|
|
||||||
- v1-dependencies-
|
|
||||||
|
|
||||||
- run: npm install
|
|
||||||
|
|
||||||
- save_cache:
|
|
||||||
paths:
|
|
||||||
- node_modules
|
|
||||||
key: v1-dependencies-{{ checksum "package.json" }}
|
|
||||||
|
|
||||||
# run tests!
|
|
||||||
- run: npm test
|
|
||||||
|
|
||||||
|
|
@ -3,11 +3,19 @@ root = true
|
||||||
[**/**.yml]
|
[**/**.yml]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
[**/**.js]
|
[**/**.js]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[**/**.ts]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
[**/**.json]
|
[**/**.json]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
|
|
||||||
17
.eslintrc.js
Normal file
17
.eslintrc.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:jest/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
mocha: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint', 'prettier'],
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"extends": [
|
|
||||||
"es/2015/server",
|
|
||||||
"plugin:jest/recommended"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"no-console" :"off"
|
|
||||||
},
|
|
||||||
"plugins": ["jest"],
|
|
||||||
"env": {
|
|
||||||
"jest/globals": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
name: Lint
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12'
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn lint
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
name: Build + Test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12'
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn build
|
||||||
|
- run: yarn test
|
||||||
56
.github/workflows/docs.yml
vendored
Normal file
56
.github/workflows/docs.yml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
name: docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
# trigger deployment on every push to main branch
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
# trigger deployment manually
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
# fetch all commits to get last updated time or other git log info
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
# choose node.js version to use
|
||||||
|
node-version: '14'
|
||||||
|
|
||||||
|
# cache node_modules
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
**/node_modules
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
# install dependencies if the cache did not hit
|
||||||
|
- name: Install dependencies
|
||||||
|
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
||||||
|
run: yarn --frozen-lockfile
|
||||||
|
|
||||||
|
# run build script
|
||||||
|
- name: Build VuePress site
|
||||||
|
run: yarn docs:build
|
||||||
|
|
||||||
|
# please check out the docs of the workflow for more details
|
||||||
|
# @see https://github.com/crazy-max/ghaction-github-pages
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
uses: crazy-max/ghaction-github-pages@v2
|
||||||
|
with:
|
||||||
|
# deploy to gh-pages branch
|
||||||
|
target_branch: gh-pages
|
||||||
|
# deploy the default output dir of VuePress
|
||||||
|
build_dir: docs/.vuepress/dist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -73,3 +73,9 @@ typings/
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
# End of https://www.gitignore.io/api/node,jetbrains,visualstudiocode
|
# End of https://www.gitignore.io/api/node,jetbrains,visualstudiocode
|
||||||
|
|
||||||
|
/dist
|
||||||
|
|
||||||
|
### Vuepress ###
|
||||||
|
.cache
|
||||||
|
.temp
|
||||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
_
|
||||||
22
.npmignore
22
.npmignore
|
|
@ -1,9 +1,17 @@
|
||||||
# ignore all files in the test/ directory
|
# ignore docs
|
||||||
test/
|
docs/
|
||||||
|
community-samples/readme.md
|
||||||
|
readme.md
|
||||||
|
|
||||||
# ignore ESLint and EditorConfig
|
# ignore all files in the test/ directory and test settings
|
||||||
|
test/
|
||||||
|
jest.config.js
|
||||||
|
|
||||||
|
# ignore EditorConfig, ESLint, Prettier config
|
||||||
.editorconfig
|
.editorconfig
|
||||||
.eslintrc.json
|
.eslintrc.json
|
||||||
|
.eslintrc.js
|
||||||
|
.prettierrc.js
|
||||||
|
|
||||||
# JetBrains IDE settings
|
# JetBrains IDE settings
|
||||||
.idea
|
.idea
|
||||||
|
|
@ -11,5 +19,9 @@ test/
|
||||||
# VS Code settings
|
# VS Code settings
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# CircleCI
|
# GitHub actions
|
||||||
.circleci
|
.github
|
||||||
|
|
||||||
|
# VuePress
|
||||||
|
.cache
|
||||||
|
.temp
|
||||||
|
|
|
||||||
12
.prettierrc.js
Normal file
12
.prettierrc.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 80,
|
||||||
|
tabWidth: 2,
|
||||||
|
singleQuote: true,
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
trailingComma: 'none',
|
||||||
|
bracketSpacing: true,
|
||||||
|
semi: false,
|
||||||
|
useTabs: false,
|
||||||
|
jsxBracketSameLine: false,
|
||||||
|
proseWrap: 'never'
|
||||||
|
}
|
||||||
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
}
|
||||||
184
CHANGELOG.md
184
CHANGELOG.md
|
|
@ -1,8 +1,190 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.23.14](https://github.com/PDMLab/docker-compose/compare/v0.23.13...v0.23.14) (2021-11-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* passing callback to report progress ([#191](https://github.com/PDMLab/docker-compose/issues/191)) ([f60e4d5](https://github.com/PDMLab/docker-compose/commit/f60e4d5a186ea3ca0b99e8443e1c4006d75be5a7))
|
||||||
|
|
||||||
|
### [0.23.13](https://github.com/PDMLab/docker-compose/compare/v0.23.12...v0.23.13) (2021-07-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* single container pause and unpause added ([5921b51](https://github.com/PDMLab/docker-compose/commit/5921b51977cbd51ffcb1def458738ccfccbe1dd9))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* now lints correctly ([74b4d6e](https://github.com/PDMLab/docker-compose/commit/74b4d6ea22d7e1b9ebbbbe9874e5314b7adfa31a))
|
||||||
|
|
||||||
|
### [0.23.12](https://github.com/PDMLab/docker-compose/compare/v0.23.11...v0.23.12) (2021-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **dx:** add default export ([c4b6087](https://github.com/PDMLab/docker-compose/commit/c4b60872aecfdc6610e9b2141a45ea7785820af7))
|
||||||
|
|
||||||
|
### [0.23.11](https://github.com/PDMLab/docker-compose/compare/v0.23.10...v0.23.11) (2021-06-04)
|
||||||
|
|
||||||
|
### [0.23.10](https://github.com/PDMLab/docker-compose/compare/v0.23.9...v0.23.10) (2021-05-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix mapping ipv6-based port mappings ([53fb971](https://github.com/PDMLab/docker-compose/commit/53fb97196f3cf0ebb229d127e9b630c863eaf8fc))
|
||||||
|
* fix mapping ipv6-based port mappings ([6dec0c6](https://github.com/PDMLab/docker-compose/commit/6dec0c6bb6220b06eff188a26220b405d632bed3))
|
||||||
|
* fix mapping ipv6-based port mappings ([dbdb900](https://github.com/PDMLab/docker-compose/commit/dbdb9008f25d237254312d79382e342d03f4c5bb))
|
||||||
|
* fix mapping ipv6-based port mappings ([8aa8465](https://github.com/PDMLab/docker-compose/commit/8aa846553c2d045f610392bf38569fb3579b9aa6))
|
||||||
|
* fix mapping ipv6-based port mappings ([21b63cb](https://github.com/PDMLab/docker-compose/commit/21b63cbc53635ee8532c388e57f4552f1b3ae6e3))
|
||||||
|
* fix mapping ipv6-based port mappings ([48c9f08](https://github.com/PDMLab/docker-compose/commit/48c9f0841af2bbcb8ec666a38eee3ab8236304d0))
|
||||||
|
* fix mapping ipv6-based port mappings ([e7013df](https://github.com/PDMLab/docker-compose/commit/e7013dfab10e4e369ee076b465c40a5bcb50802b))
|
||||||
|
|
||||||
|
### [0.23.9](https://github.com/PDMLab/docker-compose/compare/v0.23.8...v0.23.9) (2021-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add execPath option to set path to docker-compose executable ([58f09f4](https://github.com/PDMLab/docker-compose/commit/58f09f4c7414df5a6ee7af7f77d2af297d84d6c1))
|
||||||
|
|
||||||
|
### [0.23.8](https://github.com/PDMLab/docker-compose/compare/v0.23.7...v0.23.8) (2021-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ignore vuepress cache + temp files on release ([#148](https://github.com/PDMLab/docker-compose/issues/148)) ([e58387d](https://github.com/PDMLab/docker-compose/commit/e58387d82e4586f7a8156446f12c999c9a6f9f42))
|
||||||
|
|
||||||
|
### [0.23.7](https://github.com/PDMLab/docker-compose/compare/v0.23.5...v0.23.7) (2021-04-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow to pass docker compose configuration as string ([e8c14d3](https://github.com/PDMLab/docker-compose/commit/e8c14d318ce440ab02c858b1ca1dbeeb2985dc00))
|
||||||
|
* make result for `config --services` command type safe ([6f105ca](https://github.com/PDMLab/docker-compose/commit/6f105ca16098ba8de11f85ec0d092130b65293e4))
|
||||||
|
* make result for `config --volumes` command type safe ([51b20b4](https://github.com/PDMLab/docker-compose/commit/51b20b43d6eccac45653553437a546e4ccee51a6))
|
||||||
|
* make result for `config` command type safe ([a2f5a4e](https://github.com/PDMLab/docker-compose/commit/a2f5a4ec0eee8fcbd60fa12f969b8eb83a4730fb))
|
||||||
|
* make result for `port` command type safe ([70a98f4](https://github.com/PDMLab/docker-compose/commit/70a98f473b7bd46f0aa03c1a4334fa2abf6c3455))
|
||||||
|
* make result for `ps` command type safe ([880d252](https://github.com/PDMLab/docker-compose/commit/880d2522b5777db30c48569a41dd09fb7e46a4b3))
|
||||||
|
* make result for `version` command type safe ([a7da038](https://github.com/PDMLab/docker-compose/commit/a7da0380a8464bbd2efc2c16b70b67d4fdf77c24))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* filter empty entries from `config --services` command ([ed91eba](https://github.com/PDMLab/docker-compose/commit/ed91eba770d3a32d82dda4e927d31ed4b6d01db4))
|
||||||
|
* **test:** fix broken tests ([afb2b11](https://github.com/PDMLab/docker-compose/commit/afb2b1116b12c4800563e85adb15ebd39e030516))
|
||||||
|
|
||||||
|
### [0.23.6](https://github.com/PDMLab/docker-compose/compare/v0.23.5...v0.23.6) (2021-01-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow to pass docker compose configuration as string ([e8c14d3](https://github.com/PDMLab/docker-compose/commit/e8c14d318ce440ab02c858b1ca1dbeeb2985dc00))
|
||||||
|
|
||||||
|
### [0.23.5](https://github.com/PDMLab/docker-compose/compare/v0.23.4...v0.23.5) (2020-07-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update flags incompatible with detached mode ([c808fa2](https://github.com/PDMLab/docker-compose/commit/c808fa2b90b2fd5db239685d101ccaa554ad98cf))
|
||||||
|
|
||||||
|
### [0.23.4](https://github.com/PDMLab/docker-compose/compare/v0.23.3...v0.23.4) (2020-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow passing service names to `rm` function ([3a68db7](https://github.com/PDMLab/docker-compose/commit/3a68db75e3b879f12fe3c6cedbc9ad09a52914a5))
|
||||||
|
|
||||||
|
### [0.23.3](https://github.com/PDMLab/docker-compose/compare/v0.23.2...v0.23.3) (2020-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability to pass --abort-on-container-exit flag to up methods ([e85af8c](https://github.com/PDMLab/docker-compose/commit/e85af8c092a54a3fb48519bb39956324bbef4fc2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* reset jest set timeout to original value ([c4ed7ed](https://github.com/PDMLab/docker-compose/commit/c4ed7ed2de9c4c161518ab1d1fc50877df4c7fa2))
|
||||||
|
|
||||||
|
### [0.23.2](https://github.com/PDMLab/docker-compose/compare/v0.23.1...v0.23.2) (2019-12-31)
|
||||||
|
|
||||||
|
### [0.23.1](https://github.com/PDMLab/docker-compose/compare/v0.23.0...v0.23.1) (2019-10-26)
|
||||||
|
|
||||||
|
# 0.23.0 / 2019-10-25
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#97](https://github.com/PDMLab/docker-compose/pull/97):
|
||||||
|
* Add a `version` function
|
||||||
|
|
||||||
|
# 0.22.2 / 2019-08-31
|
||||||
|
|
||||||
|
## :bug: Fixes
|
||||||
|
|
||||||
|
* [#94](https://github.com/PDMLab/docker-compose/pull/94):
|
||||||
|
* Fix a copy-paste typo for pull commands
|
||||||
|
|
||||||
|
# 0.22.1 / 2019-08-31
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#93](https://github.com/PDMLab/docker-compose/pull/93):
|
||||||
|
* Add pull command methods to readme
|
||||||
|
|
||||||
|
## :lock: Security Fixes
|
||||||
|
* [#92](https://github.com/PDMLab/docker-compose/pull/92):
|
||||||
|
* Fix `eslint-utils` security issue
|
||||||
|
|
||||||
|
# 0.22.0 / 2019-08-30
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#90](https://github.com/PDMLab/docker-compose/pull/90):
|
||||||
|
* Add `pullOne`, `pullMany` and `pullAll` methods
|
||||||
|
|
||||||
|
# 0.21.0 / 2019-08-15
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#89](https://github.com/PDMLab/docker-compose/pull/89):
|
||||||
|
* Add `stopOne()` command
|
||||||
|
|
||||||
|
# 0.20.2 / 2019-07-30
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#87](https://github.com/PDMLab/docker-compose/pull/87):
|
||||||
|
* Use proper `es2015` library (for Promises, etc.) in `tsconfig`
|
||||||
|
|
||||||
|
## :bug: Fixes
|
||||||
|
|
||||||
|
* [#87](https://github.com/PDMLab/docker-compose/pull/87):
|
||||||
|
* Fix linting after build was called
|
||||||
|
* [#86](https://github.com/PDMLab/docker-compose/pull/86):
|
||||||
|
* Fix interfaces not being exposed in typings file
|
||||||
|
|
||||||
|
# 0.20.1 / 2019-07-26
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#84](https://github.com/PDMLab/docker-compose/pull/84):
|
||||||
|
* Set Node 6.0 as minimum version
|
||||||
|
|
||||||
|
# 0.20.0 / 2019-07-23
|
||||||
|
|
||||||
|
## :tada: Enhancements
|
||||||
|
|
||||||
|
* [#83](https://github.com/PDMLab/docker-compose/pull/83):
|
||||||
|
* Migrate to Typescript
|
||||||
|
|
||||||
# 0.19.0 / 2019-07-13
|
# 0.19.0 / 2019-07-13
|
||||||
|
|
||||||
## :tada: Enhancements
|
## :tada: Enhancements
|
||||||
|
|
||||||
* [#77](https://github.com/PDMLab/docker-compose/pull/77):
|
* [#77](https://github.com/PDMLab/docker-compose/pull/77):
|
||||||
* handle error based on exit code
|
* handle error based on exit code
|
||||||
* replace `tape` with `jest`
|
* replace `tape` with `jest`
|
||||||
|
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 - 2019 PDMLab
|
Copyright (c) 2017 - 2021 PDMLab
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
12
docs/.vuepress/config.js
Normal file
12
docs/.vuepress/config.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
module.exports = {
|
||||||
|
base: '/docker-compose/',
|
||||||
|
themeConfig: {
|
||||||
|
navbar: [
|
||||||
|
{ text: 'GitHub', link: 'https://github.com/PDMLab/docker-compose' },
|
||||||
|
{ text: 'npm', link: 'https://npmjs.com/docker-compose' }
|
||||||
|
],
|
||||||
|
displayAllHeaders: true,
|
||||||
|
lastUpdated: true,
|
||||||
|
sidebar: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,11 @@
|
||||||
---
|
---
|
||||||
layout: default
|
title: Manage Docker-Compose via Node.js
|
||||||
title: README
|
lang: en-US
|
||||||
nav_order: 99
|
home: false
|
||||||
---
|
---
|
||||||
|
|
||||||
<img src="https://img.shields.io/circleci/project/github/PDMLab/docker-compose.svg" /> <a href="https://join.slack.com/t/pdmlab-oss/shared_invite/enQtNjEyMjQ0MDY3NTczLTg1ZDc0YjQxMGE3MTcyYTdkODU1YjFmMTBiODE2ZTZiNDFkNjc1MWE4OTE4NWY0Y2YyMWYzYmNhZGY0NDAyYWY"><img src="https://img.shields.io/badge/Slack-join-green.svg?logo=slack" /></a>
|
|
||||||
|
|
||||||
# Manage Docker-Compose via Node.js
|
# Manage Docker-Compose via Node.js
|
||||||
|
|
||||||
`docker-compose` is a small library that allows you to run [docker-compose](https://docs.docker.com/compose/)(which is still required) via Node.js. This is useful to bootstrap test environments. You might also generate your `docker-compose.yml` files using [composefile](https://www.npmjs.com/package/composefile).
|
`docker-compose` is a small library that allows you to run [docker-compose](https://docs.docker.com/compose/) (which is still required) via Node.js. This is useful to bootstrap test environments.
|
||||||
|
|
||||||
## Existing user? We need your help
|
|
||||||
First of all: thanks for using the `docker-compose` module.
|
|
||||||
As described in [#44][i44], we're planning to provide more guidance for CI/CD when using the `docker-compose` module and it would be great if you could support us here. More details in [#44][i44].
|
|
||||||
|
|
||||||
[i44]: https://github.com/PDMLab/docker-compose/issues/44
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -26,26 +17,34 @@ npm install --save-dev docker-compose
|
||||||
|
|
||||||
`docker-compose` current supports these commands:
|
`docker-compose` current supports these commands:
|
||||||
|
|
||||||
* `upAll(options)` - Builds, (re)creates, starts, and attaches to containers for all services - always uses the `-d` flag due to non interactive mode
|
|
||||||
* `upMany(services, options)` - Builds, (re)creates, starts, and attaches to containers for the services specified in `services` - always uses the `-d` flag due to non interactive mode
|
|
||||||
* `upOne(service, options)` - Builds, (re)creates, starts, and attaches to containers for a service specified in `service` - always uses the `-d` flag due to non interactive mode
|
|
||||||
* `down(options)` - Stops containers and removes containers, networks, volumes, and images created by `up`
|
|
||||||
* `kill(options)` - Force stop service containers
|
|
||||||
* `stop(options)` - Stop running containers without removing them
|
|
||||||
* `rm(options)` - Remove stopped service containers - always uses the `-f` flag due to non interactive mode
|
|
||||||
* `exec(container, command, options)` - Exec `command` inside `container`, uses `-T` to properly handle stdin & stdout
|
|
||||||
* `logs(container, command, options)` - Show logs of service. Use `options.follow` `true|false` to turn on `--follow` flag.
|
|
||||||
* `run(service, command, options)` - Run a one-off `command` on a service, uses `-T` to properly handle stdin & stdout
|
|
||||||
* `buildAll(options)` - Build or rebuild services
|
* `buildAll(options)` - Build or rebuild services
|
||||||
* `buildMany(services, options)` - Build or rebuild services
|
* `buildMany(services, options)` - Build or rebuild services
|
||||||
* `buildOne(service, options)` - Build or rebuild service
|
* `buildOne(service, options)` - Build or rebuild service
|
||||||
* `restartAll(options)` - Restart all services
|
|
||||||
* `restartMany(services, options)` - Restart services
|
|
||||||
* `restartOne(service, options)` - Restart service
|
|
||||||
* `ps(options)` - Lists containers information
|
|
||||||
* `config(options)` - Validates configuration files and returns configuration yaml
|
* `config(options)` - Validates configuration files and returns configuration yaml
|
||||||
* `configServices(options)` - Returns list of services defined in configuration files
|
* `configServices(options)` - Returns list of services defined in configuration files
|
||||||
* `configVolumes(options)` - Returns list of volumes defined in configuration files
|
* `configVolumes(options)` - Returns list of volumes defined in configuration files
|
||||||
|
* `down(options)` - Stops containers and removes containers, networks, volumes, and images created by `up`
|
||||||
|
* `exec(container, command, options)` - Exec `command` inside `container` - uses `-T` to properly handle stdin & stdout
|
||||||
|
* `kill(options)` - Force stop service containers
|
||||||
|
* `logs(services, options)` - Show logs of service(s) - use `options.follow` `true|false` to turn on `--follow` flag
|
||||||
|
* `pauseOne(service, options)` - Pause the specified service
|
||||||
|
* `port(service, containerPort, options)` - Returns the public port of the given service and internal port.
|
||||||
|
* `ps(options)` - Lists containers information
|
||||||
|
* `pullAll(options)` - Pull all service images
|
||||||
|
* `pullMany(services, options)` - Pull service images specified
|
||||||
|
* `pullOne(service, options)` - Pull a service image
|
||||||
|
* `restartAll(options)` - Restart all services
|
||||||
|
* `restartMany(services, options)` - Restart services
|
||||||
|
* `restartOne(service, options)` - Restart service
|
||||||
|
* `rm(options, services)` - Remove stopped service containers - always uses the `-f` flag due to non interactive mode - `services` can optionally be used to select the containers to remove
|
||||||
|
* `run(service, command, options)` - Run a one-off `command` on a service - uses `-T` to properly handle stdin & stdout
|
||||||
|
* `stop(options)` - Stop running containers without removing them
|
||||||
|
* `stopOne(service, options)` - Stops one container without removing it
|
||||||
|
* `unpauseOne(service, options)` - Resume the specified service
|
||||||
|
* `upAll(options)` - Builds, (re)creates, starts, and attaches to containers for all services - always uses the `-d` flag due to non interactive mode
|
||||||
|
* `upMany(services, options)` - Builds, (re)creates, starts, and attaches to containers for the services specified in `services` - always uses the `-d` flag due to non interactive mode
|
||||||
|
* `upOne(service, options)` - Builds, (re)creates, starts, and attaches to containers for a service specified in `service` - always uses the `-d` flag due to non interactive mode
|
||||||
|
* `version(options)` - Show `docker-compose` version strings
|
||||||
|
|
||||||
All commands return a `Promise({object})` with stdout and stderr strings and an exit code:
|
All commands return a `Promise({object})` with stdout and stderr strings and an exit code:
|
||||||
```javascript
|
```javascript
|
||||||
|
|
@ -55,6 +54,7 @@ All commands return a `Promise({object})` with stdout and stderr strings and an
|
||||||
exitCode: 0, // !== 0 in case of an error
|
exitCode: 0, // !== 0 in case of an error
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Although the return type is a `Promise`, it is still possible to get the process progres before the `Promise` resolves, by passing a callback function to the optional `callback` parameter.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
|
@ -67,6 +67,19 @@ compose.upAll({ cwd: path.join(__dirname), log: true })
|
||||||
err => { console.log('something went wrong:', err.message)}
|
err => { console.log('something went wrong:', err.message)}
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
To get process progres
|
||||||
|
```typescript
|
||||||
|
compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
callback: (chunk: Buffer) => {
|
||||||
|
console.log('job in progres: ', chunk.ToString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
() => { console.log('job done')},
|
||||||
|
err => { console.log('something went wrong:', err.message)}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
To execute command inside a running container
|
To execute command inside a running container
|
||||||
```javascript
|
```javascript
|
||||||
|
|
@ -78,9 +91,12 @@ compose.exec('node', 'npm install', { cwd: path.join(__dirname) })
|
||||||
`docker-compose` accepts these params:
|
`docker-compose` accepts these params:
|
||||||
|
|
||||||
* `cwd {string}`: mandatory folder path to the `docker-compose.yml`
|
* `cwd {string}`: mandatory folder path to the `docker-compose.yml`
|
||||||
|
* `executablePath {string}`: optional path to docker-compose executable in case it's not located in $PATH `/path/to/docker-compose`
|
||||||
* `config {(string|string[])}`: custom and/or multiple yml files can be specified (relative to `cwd`)
|
* `config {(string|string[])}`: custom and/or multiple yml files can be specified (relative to `cwd`)
|
||||||
|
* `configAsString {string}`: configuration can be provided as is, instead of relying on a file. In case `configAsString` is provided `config` will be ignored.
|
||||||
* `[log] {boolean}`: optional setting to enable console logging (output of `docker-compose` `stdout`/`stderr` output)
|
* `[log] {boolean}`: optional setting to enable console logging (output of `docker-compose` `stdout`/`stderr` output)
|
||||||
* `[composeOptions] string[]|Array<string|string[]`: pass optional compose options like `"--verbose"` or `[["--verbose"], ["--log-level", "DEBUG"]]` or `["--verbose", ["--loglevel", "DEBUG"]]` for *all* commands.
|
* `[composeOptions] string[]|Array<string|string[]`: pass optional compose options like `"--verbose"` or `[["--verbose"], ["--log-level", "DEBUG"]]` or `["--verbose", ["--loglevel", "DEBUG"]]` for *all* commands.
|
||||||
|
* `[callback] (chunk: Buffer, sourceStream?: 'stdout' | 'stderr') => void`: optional callback function, that provides infromation about the process while it is still runing.
|
||||||
* `[commandOptions] string[]|Array<string|string[]`: pass optional command options like `"--build"` or `[["--build"], ["--timeout", "5"]]` or `["--build", ["--timeout", "5"]]` for the `up` command. Viable `commandOptions` depend on the command (`up`, `down` etc.) itself
|
* `[commandOptions] string[]|Array<string|string[]`: pass optional command options like `"--build"` or `[["--build"], ["--timeout", "5"]]` or `["--build", ["--timeout", "5"]]` for the `up` command. Viable `commandOptions` depend on the command (`up`, `down` etc.) itself
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
|
|
@ -88,7 +104,7 @@ compose.exec('node', 'npm install', { cwd: path.join(__dirname) })
|
||||||
While `docker-compose` runs on Node.js 6+, running the tests requires you to use Node.js 8 as they make use of `async/await`.
|
While `docker-compose` runs on Node.js 6+, running the tests requires you to use Node.js 8 as they make use of `async/await`.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm test
|
yarn test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Want to help?
|
## Want to help?
|
||||||
|
|
@ -101,17 +117,19 @@ If you see a bug, please be so kind as to show how it's failing, and we'll do ou
|
||||||
|
|
||||||
Before sending a PR, please [create an issue](https://github.com/PDMLab/docker-compose/issues/new) to introduce your idea and have a reference for your PR.
|
Before sending a PR, please [create an issue](https://github.com/PDMLab/docker-compose/issues/new) to introduce your idea and have a reference for your PR.
|
||||||
|
|
||||||
Also please add tests and make sure to run `npm run eslint`.
|
We're using [conventional commits](https://www.conventionalcommits.org), so please use it for your commits as well.
|
||||||
|
|
||||||
### Slack
|
Also please add tests and make sure to run `yarn lint`.
|
||||||
|
|
||||||
If you want to discuss an `docker-compose` issue or PR in more detail, feel free to [join our Slack workspace](https://join.slack.com/t/pdmlab-oss/shared_invite/enQtNjEyMjQ0MDY3NTczLTg1ZDc0YjQxMGE3MTcyYTdkODU1YjFmMTBiODE2ZTZiNDFkNjc1MWE4OTE4NWY0Y2YyMWYzYmNhZGY0NDAyYWY).
|
### Discussions
|
||||||
|
|
||||||
|
If you want to discuss an `docker-compose` issue or PR in more detail, feel free to [start a discussion](https://github.com/PDMLab/docker-compose/discussions).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 - 2019 PDMLab
|
Copyright (c) 2017 - 2021 PDMLab
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
title: docker-compose
|
|
||||||
description: A Jekyll theme for documentation
|
|
||||||
baseurl: "/docker-compose/" # the subpath of your site, e.g. /blog
|
|
||||||
url: "https://pdmlab.github.io" # the base hostname & protocol for your site, e.g. http://example.com
|
|
||||||
|
|
||||||
permalink: pretty
|
|
||||||
exclude: ["node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "lib/", "bin/", "README.md", "Rakefile"]
|
|
||||||
|
|
||||||
# Enable or disable the site search
|
|
||||||
search_enabled: true
|
|
||||||
|
|
||||||
# Aux links for the upper right navigation
|
|
||||||
aux_links:
|
|
||||||
"docker-compose on GitHub":
|
|
||||||
- "//github.com/pdmlab/docker-compose"
|
|
||||||
|
|
||||||
# Color scheme currently only supports "dark" or nil (default)
|
|
||||||
color_scheme: nil
|
|
||||||
|
|
||||||
remote_theme: pmarsceill/just-the-docs
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- jekyll-seo-tag
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
// //
|
|
||||||
// // Typography
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $body-font-family: -apple-system, BlinkMacSystemFont, "helvetica neue", helvetica, roboto, noto, "segoe ui", arial, sans-serif;
|
|
||||||
// $mono-font-family: "SFMono-Regular", Menlo, Consolas, Monospace;
|
|
||||||
// $root-font-size: 16px; // Base font-size for rems
|
|
||||||
// $body-line-height: 1.4;
|
|
||||||
// $body-heading-line-height: 1.15;
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // Colors
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $white: #fff;
|
|
||||||
//
|
|
||||||
// $grey-dk-000: #959396;
|
|
||||||
// $grey-dk-100: #5c5962;
|
|
||||||
// $grey-dk-200: #44434d;
|
|
||||||
// $grey-dk-250: #302d36 !default;
|
|
||||||
// $grey-dk-300: #27262b;
|
|
||||||
//
|
|
||||||
// $grey-lt-000: #f5f6fa;
|
|
||||||
// $grey-lt-100: #eeebee;
|
|
||||||
// $grey-lt-200: #ecebed;
|
|
||||||
// $grey-lt-300: #e6e1e8;
|
|
||||||
//
|
|
||||||
// $purple-000: #7253ed;
|
|
||||||
// $purple-100: #5e41d0;
|
|
||||||
// $purple-200: #4e26af;
|
|
||||||
// $purple-300: #381885;
|
|
||||||
//
|
|
||||||
// $blue-000: #2c84fa;
|
|
||||||
// $blue-100: #2869e6;
|
|
||||||
// $blue-200: #264caf;
|
|
||||||
// $blue-300: #183385;
|
|
||||||
//
|
|
||||||
// $green-000: #41d693;
|
|
||||||
// $green-100: #11b584;
|
|
||||||
// $green-200: #009c7b;
|
|
||||||
// $green-300: #026e57;
|
|
||||||
//
|
|
||||||
// $body-background-color: $white !default;
|
|
||||||
// $sidebar-color: $grey-lt-000 !default;
|
|
||||||
// $code-background-color: $grey-lt-000 !default;
|
|
||||||
|
|
||||||
// $body-text-color: $grey-dk-100 !default;
|
|
||||||
// $body-heading-color: $grey-dk-300 !default;
|
|
||||||
// $nav-child-link-color: $grey-dk-100 !default
|
|
||||||
$link-color: $blue-000;
|
|
||||||
// $btn-primary-color: $purple-100 !default;
|
|
||||||
// $base-button-color: #f7f7f7 !default;
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // Media queries in pixels
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $media-queries: (
|
|
||||||
// xs: 320px,
|
|
||||||
// sm: 500px,
|
|
||||||
// md: 740px,
|
|
||||||
// lg: 1120px,
|
|
||||||
// xl: 1400px
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // Spacing
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $spacing-unit: 1rem; // 1rem == 16px
|
|
||||||
//
|
|
||||||
// $spacers: (
|
|
||||||
// sp-0: 0,
|
|
||||||
// sp-1: $spacing-unit * 0.25,
|
|
||||||
// sp-2: $spacing-unit * 0.5,
|
|
||||||
// sp-3: $spacing-unit * 0.75,
|
|
||||||
// sp-4: $spacing-unit,
|
|
||||||
// sp-5: $spacing-unit * 1.5,
|
|
||||||
// sp-6: $spacing-unit * 2,
|
|
||||||
// sp-7: $spacing-unit * 2.5,
|
|
||||||
// sp-8: $spacing-unit * 3,
|
|
||||||
// sp-9: $spacing-unit * 3.5,
|
|
||||||
// sp-10: $spacing-unit * 4
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// $sp-1: map-get($spacers, sp-1); // 0.25 rem == 4px
|
|
||||||
// $sp-2: map-get($spacers, sp-2); // 0.5 rem == 8px
|
|
||||||
// $sp-3: map-get($spacers, sp-3); // 0.75 rem == 12px
|
|
||||||
// $sp-4: map-get($spacers, sp-4); // 1 rem == 16px
|
|
||||||
// $sp-5: map-get($spacers, sp-5); // 1.5 rem == 24px
|
|
||||||
// $sp-6: map-get($spacers, sp-6); // 2 rem == 32px
|
|
||||||
// $sp-7: map-get($spacers, sp-7); // 2.5 rem == 40px
|
|
||||||
// $sp-8: map-get($spacers, sp-8); // 3 rem == 48px
|
|
||||||
// $sp-9: map-get($spacers, sp-9); // 4 rem == 48px
|
|
||||||
// $sp-10: map-get($spacers, sp-10); // 4.5 rem == 48px
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // Borders
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $border: 1px solid;
|
|
||||||
// $border-radius: 4px;
|
|
||||||
// $border-color: $grey-lt-100;
|
|
||||||
//
|
|
||||||
// //
|
|
||||||
// // Grid system
|
|
||||||
// //
|
|
||||||
//
|
|
||||||
// $gutter-spacing: $sp-6;
|
|
||||||
// $gutter-spacing-sm: $sp-4;
|
|
||||||
// $nav-width: 232px;
|
|
||||||
// $content-width: 800px;
|
|
||||||
//
|
|
||||||
// $media-queries: (
|
|
||||||
// xs: 320px,
|
|
||||||
// sm: 500px,
|
|
||||||
// md: 740px,
|
|
||||||
// lg: 800px,
|
|
||||||
// xl: 1316px
|
|
||||||
// );
|
|
||||||
69
index.d.ts
vendored
69
index.d.ts
vendored
|
|
@ -1,69 +0,0 @@
|
||||||
export declare function upAll(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function upMany(services: String[], options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function upOne(service: String, options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function kill(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function down(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function stop(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function restartAll(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function restartMany(services: String[], options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function restartOne(service: String, options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function rm(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function exec(container: String, command: String | String[], options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function logs(services: String[], options?: IDockerComposeLogOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function run(service: String, command: String | String[], options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function buildAll(options?: IDockerComposeBuildOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function buildMany(services: String[], options?: IDockerComposeBuildOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function buildOne(service: String, options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function config(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function configServices(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function configVolumes(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function ps(options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function push(options?: IDockerComposePushOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
export declare function port(service: String, containerPort: String | Number, options?: IDockerComposeOptions): Promise<IDockerComposeResult>;
|
|
||||||
|
|
||||||
interface IDockerComposeOptions {
|
|
||||||
cwd?: string;
|
|
||||||
config?: string | string[];
|
|
||||||
log?: boolean;
|
|
||||||
composeOptions?: string[] | Array<string | string[]>;
|
|
||||||
commandOptions?: string[] | Array<string | string[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDockerComposeLogOptions extends IDockerComposeOptions {
|
|
||||||
follow: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDockerComposeBuildOptions extends IDockerComposeOptions {
|
|
||||||
parallel?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDockerComposePushOptions extends IDockerComposeOptions {
|
|
||||||
ignorePushFailures?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDockerComposeResult {
|
|
||||||
exitCode?: number;
|
|
||||||
out: string;
|
|
||||||
err: string;
|
|
||||||
}
|
|
||||||
454
index.js
454
index.js
|
|
@ -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<string|string[]>} 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<string|string[]>)} [options.commandOptions]
|
|
||||||
* @param {?(string[]|Array<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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<string|string[]>)} [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
|
|
||||||
};
|
|
||||||
187
jest.config.js
Normal file
187
jest.config.js
Normal file
|
|
@ -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: [
|
||||||
|
// '<rootDir>/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,
|
||||||
|
};
|
||||||
6180
package-lock.json
generated
6180
package-lock.json
generated
File diff suppressed because it is too large
Load diff
90
package.json
90
package.json
|
|
@ -1,11 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "docker-compose",
|
"name": "@iceprod/docker-compose",
|
||||||
"version": "0.19.0",
|
"version": "0.24.0",
|
||||||
"main": "index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest test --verbose",
|
"test": "jest test --verbose",
|
||||||
"eslint": "./node_modules/.bin/eslint ."
|
"lint": "eslint src/**/*.ts test/**/*.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"prepublishOnly": "tsc",
|
||||||
|
"release": "yarn build && standard-version",
|
||||||
|
"docs:dev": "vuepress dev docs",
|
||||||
|
"docs:build": "vuepress build docs"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "git@github.com:PDMLab/docker-compose.git"
|
"url": "git@github.com:PDMLab/docker-compose.git"
|
||||||
|
|
@ -18,7 +24,7 @@
|
||||||
"test",
|
"test",
|
||||||
"test-tools"
|
"test-tools"
|
||||||
],
|
],
|
||||||
"author": "Alexander Zeitler <alexander.zeitler@pdmlab.com>",
|
"author": "Alexander Zeitler <alexander.zeitler@pdmlab.com>, Daniel Bulan <danbulant@danbulant.eu>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
"name": "Ignatiev Mikhail"
|
"name": "Ignatiev Mikhail"
|
||||||
|
|
@ -32,6 +38,9 @@
|
||||||
{
|
{
|
||||||
"name": "lacabra"
|
"name": "lacabra"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Lance Rutkin"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "MartinJLee"
|
"name": "MartinJLee"
|
||||||
},
|
},
|
||||||
|
|
@ -39,7 +48,8 @@
|
||||||
"name": "Sergey Falinsky"
|
"name": "Sergey Falinsky"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Lars Kumbier"
|
"name": "Lars Kumbier",
|
||||||
|
"url": "https://kumbier.it"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Paweł Niedzielski"
|
"name": "Paweł Niedzielski"
|
||||||
|
|
@ -49,19 +59,67 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Eduardo Weiland"
|
"name": "Eduardo Weiland"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nacho González Bullón"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Will O'Beirne"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sebastián Balay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gabriel Fürstenheim",
|
||||||
|
"url": "https://github.com/furstenheim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gautaz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Daniel Bulant",
|
||||||
|
"url": "https://github.com/danbulant"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "Manage docker-compose from Node.js",
|
"description": "Manage docker-compose from Node.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"composefile": "^0.3.0",
|
"@commitlint/cli": "^13.1.0",
|
||||||
"dockerode": "^2.5.7",
|
"@commitlint/config-conventional": "13.1.0",
|
||||||
"eslint": "^5.6.0",
|
"@types/dockerode": "^2.5.27",
|
||||||
"eslint-config-es": "^0.8.11",
|
"@types/jest": "^26.0.22",
|
||||||
"eslint-plugin-extended": "^0.2.0",
|
"@types/node": "^12.12.6",
|
||||||
"eslint-plugin-jest": "^22.6.4",
|
"@typescript-eslint/eslint-plugin": "^4.21.0",
|
||||||
"jest": "^24.8.0",
|
"@typescript-eslint/parser": "^4.21.0",
|
||||||
"tape": "^4.6.3",
|
"dockerode": "^3.2.1",
|
||||||
"tape-promise": "^2.0.1"
|
"eslint": "^7.24.0",
|
||||||
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
"eslint-plugin-import": "^2.22.1",
|
||||||
|
"eslint-plugin-jest": "^24.3.4",
|
||||||
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-watch": "^7.0.0",
|
||||||
|
"husky": "^6.0.0",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
|
"standard-version": "9.3.1",
|
||||||
|
"ts-jest": "^26.5.4",
|
||||||
|
"typescript": "^4.2.4",
|
||||||
|
"vuepress": "^2.0.0-beta.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commitlint": {
|
||||||
|
"extends": [
|
||||||
|
"@commitlint/config-conventional"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"yaml": "^1.10.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
109
readme.md
109
readme.md
|
|
@ -1,88 +1,46 @@
|
||||||
<img src="https://img.shields.io/circleci/project/github/PDMLab/docker-compose.svg" /> <a href="https://join.slack.com/t/pdmlab-oss/shared_invite/enQtNjEyMjQ0MDY3NTczLTg1ZDc0YjQxMGE3MTcyYTdkODU1YjFmMTBiODE2ZTZiNDFkNjc1MWE4OTE4NWY0Y2YyMWYzYmNhZGY0NDAyYWY"><img src="https://img.shields.io/npm/dm/docker-compose.svg" /> <img src="https://img.shields.io/badge/Slack-join-green.svg?logo=slack" /></a>
|
[](https://conventionalcommits.org) [](https://gitter.im/pdmlab/docker-compose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <img src="https://img.shields.io/github/workflow/status/pdmlab/docker-compose/Node.js%20CI/master" /> <img src="https://img.shields.io/npm/dm/docker-compose.svg" />
|
||||||
|
|
||||||
# Manage Docker-Compose via Node.js
|
# Manage Docker-Compose via Node.js
|
||||||
|
|
||||||
`docker-compose` is a small library that allows you to run [docker-compose](https://docs.docker.com/compose/)(which is still required) via Node.js. This is useful to bootstrap test environments. You might also generate your `docker-compose.yml` files using [composefile](https://www.npmjs.com/package/composefile).
|
`docker-compose` is a small library that allows you to run [docker-compose](https://docs.docker.com/compose/)(which is still required) via Node.js. This is useful to bootstrap test environments.
|
||||||
|
|
||||||
## Existing user? We need your help
|
|
||||||
First of all: thanks for using the `docker-compose` module.
|
|
||||||
As described in [#44][i44], we're planning to provide more guidance for CI/CD when using the `docker-compose` module and it would be great if you could support us here. More details in [#44][i44].
|
|
||||||
|
|
||||||
[i44]: https://github.com/PDMLab/docker-compose/issues/44
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npm install --save-dev docker-compose
|
yarn add --dev docker-compose
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Documentation
|
||||||
|
|
||||||
`docker-compose` current supports these commands:
|
The documentation can be found [here](https://pdmlab.github.io/docker-compose/).
|
||||||
|
|
||||||
* `upAll(options)` - Builds, (re)creates, starts, and attaches to containers for all services - always uses the `-d` flag due to non interactive mode
|
## Example
|
||||||
* `upMany(services, options)` - Builds, (re)creates, starts, and attaches to containers for the services specified in `services` - always uses the `-d` flag due to non interactive mode
|
|
||||||
* `upOne(service, options)` - Builds, (re)creates, starts, and attaches to containers for a service specified in `service` - always uses the `-d` flag due to non interactive mode
|
|
||||||
* `down(options)` - Stops containers and removes containers, networks, volumes, and images created by `up`
|
|
||||||
* `kill(options)` - Force stop service containers
|
|
||||||
* `stop(options)` - Stop running containers without removing them
|
|
||||||
* `rm(options)` - Remove stopped service containers - always uses the `-f` flag due to non interactive mode
|
|
||||||
* `exec(container, command, options)` - Exec `command` inside `container`, uses `-T` to properly handle stdin & stdout
|
|
||||||
* `logs(services, options)` - Show logs of service(s). Use `options.follow` `true|false` to turn on `--follow` flag.
|
|
||||||
* `run(service, command, options)` - Run a one-off `command` on a service, uses `-T` to properly handle stdin & stdout
|
|
||||||
* `buildAll(options)` - Build or rebuild services
|
|
||||||
* `buildMany(services, options)` - Build or rebuild services
|
|
||||||
* `buildOne(service, options)` - Build or rebuild service
|
|
||||||
* `restartAll(options)` - Restart all services
|
|
||||||
* `restartMany(services, options)` - Restart services
|
|
||||||
* `restartOne(service, options)` - Restart service
|
|
||||||
* `ps(options)` - Lists containers information
|
|
||||||
* `config(options)` - Validates configuration files and returns configuration yaml
|
|
||||||
* `configServices(options)` - Returns list of services defined in configuration files
|
|
||||||
* `configVolumes(options)` - Returns list of volumes defined in configuration files
|
|
||||||
|
|
||||||
All commands return a `Promise({object})` with stdout and stderr strings and an exit code:
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
out: 'stdout contents',
|
|
||||||
err: 'stderr contents',
|
|
||||||
exitCode: 0, // !== 0 in case of an error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
To start service containers based on the `docker-compose.yml` file in your current directory, just call `compose.up` like this:
|
To start service containers based on the `docker-compose.yml` file in your current directory, just call `compose.up` like this:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
compose.upAll({ cwd: path.join(__dirname), log: true })
|
compose.upAll({ cwd: path.join(__dirname), log: true }).then(
|
||||||
.then(
|
() => {
|
||||||
() => { console.log('done')},
|
console.log('done')
|
||||||
err => { console.log('something went wrong:', err.message)}
|
},
|
||||||
);
|
(err) => {
|
||||||
|
console.log('something went wrong:', err.message)
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
To execute command inside a running container
|
To execute command inside a running container
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
compose.exec('node', 'npm install', { cwd: path.join(__dirname) })
|
compose.exec('node', 'npm install', { cwd: path.join(__dirname) })
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
`docker-compose` accepts these params:
|
|
||||||
|
|
||||||
* `cwd {string}`: mandatory folder path to the `docker-compose.yml`
|
|
||||||
* `config {(string|string[])}`: custom and/or multiple yml files can be specified (relative to `cwd`)
|
|
||||||
* `[log] {boolean}`: optional setting to enable console logging (output of `docker-compose` `stdout`/`stderr` output)
|
|
||||||
* `[composeOptions] string[]|Array<string|string[]`: pass optional compose options like `"--verbose"` or `[["--verbose"], ["--log-level", "DEBUG"]]` or `["--verbose", ["--loglevel", "DEBUG"]]` for *all* commands.
|
|
||||||
* `[commandOptions] string[]|Array<string|string[]`: pass optional command options like `"--build"` or `[["--build"], ["--timeout", "5"]]` or `["--build", ["--timeout", "5"]]` for the `up` command. Viable `commandOptions` depend on the command (`up`, `down` etc.) itself
|
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
|
|
||||||
While `docker-compose` runs on Node.js 6+, running the tests requires you to use Node.js 8 as they make use of `async/await`.
|
While `docker-compose` runs on Node.js 6+, running the tests requires you to use Node.js 8+ as they make use of `async/await`.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npm test
|
yarn test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Want to help?
|
## Want to help?
|
||||||
|
|
@ -95,33 +53,22 @@ If you see a bug, please be so kind as to show how it's failing, and we'll do ou
|
||||||
|
|
||||||
Before sending a PR, please [create an issue](https://github.com/PDMLab/docker-compose/issues/new) to introduce your idea and have a reference for your PR.
|
Before sending a PR, please [create an issue](https://github.com/PDMLab/docker-compose/issues/new) to introduce your idea and have a reference for your PR.
|
||||||
|
|
||||||
Also please add tests and make sure to run `npm run eslint`.
|
We're using [conventional commits](https://www.conventionalcommits.org), so please use it for your commits as well.
|
||||||
|
|
||||||
### Slack
|
Also please add tests and make sure to run `yarn lint`.
|
||||||
|
|
||||||
If you want to discuss an `docker-compose` issue or PR in more detail, feel free to [join our Slack workspace](https://join.slack.com/t/pdmlab-oss/shared_invite/enQtNjEyMjQ0MDY3NTczLTg1ZDc0YjQxMGE3MTcyYTdkODU1YjFmMTBiODE2ZTZiNDFkNjc1MWE4OTE4NWY0Y2YyMWYzYmNhZGY0NDAyYWY).
|
### Discussions
|
||||||
|
|
||||||
|
If you want to discuss an `docker-compose` issue or PR in more detail, feel free to [start a discussion](https://github.com/PDMLab/docker-compose/discussions).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 - 2019 PDMLab
|
Copyright (c) 2017 - 2021 PDMLab
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
|
||||||
542
src/index.ts
Normal file
542
src/index.ts
Normal file
|
|
@ -0,0 +1,542 @@
|
||||||
|
import childProcess from 'child_process'
|
||||||
|
import yaml from 'yaml'
|
||||||
|
import mapPorts from './map-ports'
|
||||||
|
|
||||||
|
export interface IDockerComposeOptions {
|
||||||
|
cwd?: string
|
||||||
|
executablePath?: string
|
||||||
|
config?: string | string[]
|
||||||
|
configAsString?: string
|
||||||
|
log?: boolean
|
||||||
|
composeOptions?: string[] | (string | string[])[]
|
||||||
|
commandOptions?: string[] | (string | string[])[]
|
||||||
|
env?: NodeJS.ProcessEnv
|
||||||
|
callback?: (chunk: Buffer, streamSource?: 'stdout' | 'stderr') => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DockerComposePortResult = {
|
||||||
|
address: string
|
||||||
|
port: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DockerComposeVersionResult = {
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DockerComposeConfigResult = {
|
||||||
|
config: {
|
||||||
|
version: Record<string, string>
|
||||||
|
services: Record<string, string | Record<string, string>>
|
||||||
|
volumes: Record<string, string>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DockerComposeConfigServicesResult = {
|
||||||
|
services: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DockerComposeConfigVolumesResult = {
|
||||||
|
volumes: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDockerComposeLogOptions extends IDockerComposeOptions {
|
||||||
|
follow?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDockerComposeBuildOptions extends IDockerComposeOptions {
|
||||||
|
parallel?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDockerComposePushOptions extends IDockerComposeOptions {
|
||||||
|
ignorePushFailures?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDockerComposeResult {
|
||||||
|
exitCode: number | null
|
||||||
|
out: string
|
||||||
|
err: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TypedDockerComposeResult<T> = {
|
||||||
|
exitCode: number | null
|
||||||
|
out: string
|
||||||
|
err: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonEmptyString = (v: string) => v !== ''
|
||||||
|
|
||||||
|
export type DockerComposePsResult = {
|
||||||
|
services: Array<{
|
||||||
|
name: string
|
||||||
|
command: string
|
||||||
|
state: string
|
||||||
|
ports: Array<{
|
||||||
|
mapped?: { address: string; port: number }
|
||||||
|
exposed: { port: number; protocol: string }
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mapPsOutput = (output: string): DockerComposePsResult => {
|
||||||
|
const services = output
|
||||||
|
.split(`\n`)
|
||||||
|
.filter(nonEmptyString)
|
||||||
|
.filter((_, index) => index > 0)
|
||||||
|
.map((line) => {
|
||||||
|
const [
|
||||||
|
nameFragment,
|
||||||
|
commandFragment,
|
||||||
|
serviceFragment,
|
||||||
|
stateFragment,
|
||||||
|
untypedPortsFragment
|
||||||
|
] = line.split(/\s{3,}/)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: nameFragment.trim(),
|
||||||
|
command: commandFragment.trim(),
|
||||||
|
service: serviceFragment.trim(),
|
||||||
|
state: stateFragment.trim(),
|
||||||
|
ports: mapPorts(untypedPortsFragment.trim())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { services }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
export const execCompose = (
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
options: IDockerComposeOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> =>
|
||||||
|
new Promise((resolve, reject): void => {
|
||||||
|
const composeOptions = options.composeOptions || []
|
||||||
|
const commandOptions = options.commandOptions || []
|
||||||
|
let composeArgs = composeOptionsToArgs(composeOptions)
|
||||||
|
const isConfigProvidedAsString = !!options.configAsString
|
||||||
|
|
||||||
|
const configArgs = isConfigProvidedAsString
|
||||||
|
? ['-f', '-']
|
||||||
|
: configToArgs(options.config)
|
||||||
|
|
||||||
|
composeArgs = composeArgs.concat(
|
||||||
|
configArgs.concat(
|
||||||
|
[command].concat(composeOptionsToArgs(commandOptions), args)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const cwd = options.cwd
|
||||||
|
const env = options.env || undefined
|
||||||
|
const executablePath = options.executablePath || 'docker-compose'
|
||||||
|
|
||||||
|
const childProc = childProcess.spawn(executablePath, 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()
|
||||||
|
options.callback?.(chunk, 'stdout')
|
||||||
|
})
|
||||||
|
|
||||||
|
childProc.stderr.on('data', (chunk): void => {
|
||||||
|
result.err += chunk.toString()
|
||||||
|
options.callback?.(chunk, 'stderr')
|
||||||
|
})
|
||||||
|
|
||||||
|
childProc.on('exit', (exitCode): void => {
|
||||||
|
result.exitCode = exitCode
|
||||||
|
if (exitCode === 0) {
|
||||||
|
resolve(result)
|
||||||
|
} else {
|
||||||
|
reject(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isConfigProvidedAsString) {
|
||||||
|
childProc.stdin.write(options.configAsString)
|
||||||
|
childProc.stdin.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.log) {
|
||||||
|
childProc.stdout.pipe(process.stdout)
|
||||||
|
childProc.stderr.pipe(process.stderr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not to use the default non-interactive flag -d for up commands
|
||||||
|
*/
|
||||||
|
const shouldUseDefaultNonInteractiveFlag = function (
|
||||||
|
options: IDockerComposeOptions = {}
|
||||||
|
): boolean {
|
||||||
|
const commandOptions = options.commandOptions || []
|
||||||
|
const containsOtherNonInteractiveFlag = commandOptions.reduce(
|
||||||
|
(memo: boolean, item: string | string[]) => {
|
||||||
|
return (
|
||||||
|
memo &&
|
||||||
|
!item.includes('--abort-on-container-exit') &&
|
||||||
|
!item.includes('--no-start')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
return containsOtherNonInteractiveFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
export const upAll = function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
const args = shouldUseDefaultNonInteractiveFlag(options) ? ['-d'] : []
|
||||||
|
return execCompose('up', args, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const upMany = function (
|
||||||
|
services: string[],
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
const args = shouldUseDefaultNonInteractiveFlag(options)
|
||||||
|
? ['-d'].concat(services)
|
||||||
|
: services
|
||||||
|
return execCompose('up', args, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const upOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
const args = shouldUseDefaultNonInteractiveFlag(options)
|
||||||
|
? ['-d', service]
|
||||||
|
: [service]
|
||||||
|
return execCompose('up', args, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const down = function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('down', [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stop = function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('stop', [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stopOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('stop', [service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pauseOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('pause', [service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unpauseOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('unpause', [service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const kill = function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('kill', [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rm = function (
|
||||||
|
options?: IDockerComposeOptions,
|
||||||
|
...services: string[]
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('rm', ['-f', ...services], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exec = function (
|
||||||
|
container: string,
|
||||||
|
command: string | string[],
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
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<IDockerComposeResult> {
|
||||||
|
const args = Array.isArray(command) ? command : command.split(/\s+/)
|
||||||
|
|
||||||
|
return execCompose('run', ['-T', container].concat(args), options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildAll = function (
|
||||||
|
options: IDockerComposeBuildOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('build', options.parallel ? ['--parallel'] : [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildMany = function (
|
||||||
|
services: string[],
|
||||||
|
options: IDockerComposeBuildOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose(
|
||||||
|
'build',
|
||||||
|
options.parallel ? ['--parallel'].concat(services) : services,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeBuildOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('build', [service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pullAll = function (
|
||||||
|
options: IDockerComposeOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('pull', [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pullMany = function (
|
||||||
|
services: string[],
|
||||||
|
options: IDockerComposeOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('pull', services, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pullOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('pull', [service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = async function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposeConfigResult>> {
|
||||||
|
try {
|
||||||
|
const result = await execCompose('config', [], options)
|
||||||
|
const config = yaml.parse(result.out)
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: { config }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const configServices = async function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposeConfigServicesResult>> {
|
||||||
|
try {
|
||||||
|
const result = await execCompose('config', ['--services'], options)
|
||||||
|
const services = result.out.split('\n').filter(nonEmptyString)
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: { services }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const configVolumes = async function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposeConfigVolumesResult>> {
|
||||||
|
try {
|
||||||
|
const result = await execCompose('config', ['--volumes'], options)
|
||||||
|
const volumes = result.out.split('\n').filter(nonEmptyString)
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: { volumes }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ps = async function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposePsResult>> {
|
||||||
|
try {
|
||||||
|
const result = await execCompose('ps', [], options)
|
||||||
|
const data = mapPsOutput(result.out)
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const push = function (
|
||||||
|
options: IDockerComposePushOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose(
|
||||||
|
'push',
|
||||||
|
options.ignorePushFailures ? ['--ignore-push-failures'] : [],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const restartAll = function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('restart', [], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const restartMany = function (
|
||||||
|
services: string[],
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return execCompose('restart', services, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const restartOne = function (
|
||||||
|
service: string,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
return restartMany([service], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logs = function (
|
||||||
|
services: string | string[],
|
||||||
|
options: IDockerComposeLogOptions = {}
|
||||||
|
): Promise<IDockerComposeResult> {
|
||||||
|
let args = Array.isArray(services) ? services : [services]
|
||||||
|
|
||||||
|
if (options.follow) {
|
||||||
|
args = ['--follow', ...args]
|
||||||
|
}
|
||||||
|
|
||||||
|
return execCompose('logs', args, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const port = async function (
|
||||||
|
service: string,
|
||||||
|
containerPort: string | number,
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposePortResult>> {
|
||||||
|
const args = [service, containerPort]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await execCompose('port', args, options)
|
||||||
|
const [address, port] = result.out.split(':')
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: {
|
||||||
|
address,
|
||||||
|
port: Number(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const version = async function (
|
||||||
|
options?: IDockerComposeOptions
|
||||||
|
): Promise<TypedDockerComposeResult<DockerComposeVersionResult>> {
|
||||||
|
try {
|
||||||
|
const result = await execCompose('version', ['--short'], options)
|
||||||
|
const version = result.out.replace('\n', '')
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: { version }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
upAll,
|
||||||
|
upMany,
|
||||||
|
upOne,
|
||||||
|
down,
|
||||||
|
stop,
|
||||||
|
stopOne,
|
||||||
|
pauseOne,
|
||||||
|
unpauseOne,
|
||||||
|
kill,
|
||||||
|
rm,
|
||||||
|
exec,
|
||||||
|
run,
|
||||||
|
buildAll,
|
||||||
|
buildMany,
|
||||||
|
buildOne,
|
||||||
|
pullAll,
|
||||||
|
pullMany,
|
||||||
|
pullOne,
|
||||||
|
config,
|
||||||
|
configServices,
|
||||||
|
configVolumes,
|
||||||
|
ps,
|
||||||
|
push,
|
||||||
|
restartAll,
|
||||||
|
restartMany,
|
||||||
|
restartOne,
|
||||||
|
logs,
|
||||||
|
port,
|
||||||
|
version
|
||||||
|
}
|
||||||
38
src/map-ports.ts
Normal file
38
src/map-ports.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
const mapPorts = (
|
||||||
|
ports: string
|
||||||
|
): Array<{
|
||||||
|
mapped?: { address: string; port: number }
|
||||||
|
exposed: { port: number; protocol: string }
|
||||||
|
}> => {
|
||||||
|
if (!ports) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return ports.split(',').map((untypedPort) => {
|
||||||
|
const exposedFragments = untypedPort.trim().split('->')
|
||||||
|
|
||||||
|
const [port, protocol] =
|
||||||
|
exposedFragments.length === 1
|
||||||
|
? exposedFragments[0].split('/')
|
||||||
|
: exposedFragments[1].split('/')
|
||||||
|
|
||||||
|
const mapped = exposedFragments[0]
|
||||||
|
const lastDoubleColon = mapped.lastIndexOf(':')
|
||||||
|
|
||||||
|
if (lastDoubleColon === -1) {
|
||||||
|
return {
|
||||||
|
exposed: { port: Number(port), protocol }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = mapped.substr(0, lastDoubleColon)
|
||||||
|
const mappedPort = mapped.substr(lastDoubleColon + 1)
|
||||||
|
|
||||||
|
return {
|
||||||
|
exposed: { port: Number(port), protocol },
|
||||||
|
mapped: { port: Number(mappedPort), address }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default mapPorts
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
version: '2'
|
version: '2'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
web:
|
||||||
image: nginx:1.16.0
|
image: nginx:1.16.0
|
||||||
container_name: compose_test_nginx_2
|
container_name: compose_test_web_2
|
||||||
command: 'nginx -g "daemon off;"'
|
command: 'nginx -g "daemon off;"'
|
||||||
environment:
|
environment:
|
||||||
NGINX_PORT: 5432
|
NGINX_PORT: 8888
|
||||||
ports:
|
ports:
|
||||||
- "5432"
|
- 8888:8888
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
some-service:
|
some-service:
|
||||||
image: alpine:3.7.3
|
image: nginx:1.19.9-alpine
|
||||||
|
command: 'nginx -g "daemon off;"'
|
||||||
volumes:
|
volumes:
|
||||||
- ./volume:/mountedvolume
|
# - ./volume:/mountedvolume
|
||||||
- db-data:/mountedsecondvolume
|
- db-data:/mountedsecondvolume
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
version: '2'
|
version: '2'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
web:
|
||||||
image: nginx:1.16.0
|
image: nginx:1.16.0
|
||||||
container_name: compose_test_nginx
|
container_name: compose_test_web
|
||||||
command: 'nginx -g "daemon off;"'
|
command: 'nginx -g "daemon off;"'
|
||||||
alpine:
|
ports:
|
||||||
image: alpine:3.7.3
|
- '0.0.0.0:80:80'
|
||||||
container_name: compose_test_alpine
|
- '0.0.0.0:443:443'
|
||||||
|
proxy:
|
||||||
|
image: nginx:1.19.9-alpine
|
||||||
|
container_name: compose_test_proxy
|
||||||
|
command: 'nginx -g "daemon off;"'
|
||||||
|
hello:
|
||||||
|
image: hello-world
|
||||||
|
container_name: compose_test_hello
|
||||||
|
|
|
||||||
|
|
@ -1,425 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
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.
|
|
||||||
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) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const running = containers.filter(container => container.Names.includes(name));
|
|
||||||
|
|
||||||
resolve(running.length > 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const repoTags = imageInfo => imageInfo.RepoTags || [];
|
|
||||||
|
|
||||||
const imageExists = async name => {
|
|
||||||
const images = await docker.listImages();
|
|
||||||
|
|
||||||
const foundImage = images.findIndex(imageInfo => repoTags(imageInfo).includes(name));
|
|
||||||
|
|
||||||
return foundImage > -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeImagesStartingWith = async searchString => {
|
|
||||||
const images = await docker.listImages();
|
|
||||||
|
|
||||||
for (const image of images) {
|
|
||||||
for (const repoTag of repoTags(image)) {
|
|
||||||
if (repoTag.startsWith(searchString)) {
|
|
||||||
const dockerImage = docker.getImage(repoTag);
|
|
||||||
|
|
||||||
if (logOutput) {
|
|
||||||
process.stdout.write(`removing image ${repoTag} ${dockerImage.id || ''}`);
|
|
||||||
}
|
|
||||||
await dockerImage.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test('ensure container gets started', async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure exit code is returned correctly', async () => {
|
|
||||||
let result = await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
await expect(result).toMatchObject({
|
|
||||||
exitCode: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
result = await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(result).toMatchObject({
|
|
||||||
exitCode: 0
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
await compose.logs('non_existent_service', { cwd: path.join(__dirname) });
|
|
||||||
expect(false).toBeTruthy();
|
|
||||||
} catch (rejectionResult) {
|
|
||||||
expect(rejectionResult.exitCode).toBe(1);
|
|
||||||
}
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('starts containers properly with --build and --timeout options', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-build.yml' });
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-build.yml' });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets started with --build option', async () => {
|
|
||||||
await compose.upAll({
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml',
|
|
||||||
commandOptions: [ '--build' ]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets started with --build and --timeout option', async () => {
|
|
||||||
await compose.upAll({
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml',
|
|
||||||
commandOptions: [[ '--build' ], [ '--timeout', '5' ]]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets started with --build and --timeout option with different command style', async () => {
|
|
||||||
await compose.upAll({
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml',
|
|
||||||
commandOptions: [ '--build', [ '--timeout', '5' ]]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container command executed with --workdir command option', async () => {
|
|
||||||
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),
|
|
||||||
log: true,
|
|
||||||
config: 'docker-compose-42.yml',
|
|
||||||
composeOptions: [ '--verbose' ],
|
|
||||||
|
|
||||||
// Alpine has "/" as default
|
|
||||||
commandOptions: [ '--workdir', '/home/root' ]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.out).toBe('/home/root\n');
|
|
||||||
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure only single container gets started', async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.upOne('alpine', { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_alpine')).toBeTruthy();
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure only multiple containers get started', async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.upMany([ 'alpine' ], { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_alpine')).toBeTruthy();
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets down', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets stopped', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
|
|
||||||
await compose.stop({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure container gets killed', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
|
|
||||||
await compose.kill({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeFalsy();
|
|
||||||
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure custom ymls are working', async () => {
|
|
||||||
const config = './docker-compose-2.yml';
|
|
||||||
const cwd = path.join(__dirname);
|
|
||||||
|
|
||||||
await compose.upAll({ cwd, log: logOutput, config });
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx_2')).toBeTruthy();
|
|
||||||
|
|
||||||
// config & [config] are the same thing, ensures that multiple configs are handled properly
|
|
||||||
await compose.kill({ cwd, log: logOutput, config: [ config ]});
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx_2')).toBeFalsy();
|
|
||||||
|
|
||||||
await compose.down({ cwd, log: logOutput, config });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ensure run and exec are working', async () => {
|
|
||||||
const checkOSID = (out, id) => {
|
|
||||||
// parse /etc/os-release contents
|
|
||||||
const re = /([\w,_]+)=(.*)/g;
|
|
||||||
let match;
|
|
||||||
const os = {};
|
|
||||||
|
|
||||||
while ((match = re.exec(out)) !== null) { // eslint-disable-line no-cond-assign
|
|
||||||
os[match[1]] = match[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(os.ID).toBe(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const opts = { cwd: path.join(__dirname), log: logOutput };
|
|
||||||
|
|
||||||
await compose.upAll(opts);
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
|
|
||||||
let std = await compose.exec('db', 'cat /etc/os-release', opts);
|
|
||||||
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
checkOSID(std.out, 'debian');
|
|
||||||
|
|
||||||
std = await compose.run('alpine', 'cat /etc/os-release', opts);
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
checkOSID(std.out, 'alpine');
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
// parse /etc/os-release contents
|
|
||||||
const re = /([\w,_]+)=(.*)/g;
|
|
||||||
let match;
|
|
||||||
const os = {};
|
|
||||||
|
|
||||||
while ((match = re.exec(out)) !== null) { // eslint-disable-line no-cond-assign
|
|
||||||
os[match[1]] = match[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(os.ID).toBe(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const opts = { cwd: path.join(__dirname), log: false };
|
|
||||||
|
|
||||||
await compose.upAll(opts);
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBe(true);
|
|
||||||
|
|
||||||
let std = await compose.exec('db', [ '/bin/sh', '-c', 'cat /etc/os-release' ], opts);
|
|
||||||
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
checkOSID(std.out, 'debian');
|
|
||||||
|
|
||||||
std = await compose.run('alpine', [ '/bin/sh', '-c', 'cat /etc/os-release' ], opts);
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
checkOSID(std.out, 'alpine');
|
|
||||||
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('build single service', async () => {
|
|
||||||
const opts = {
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml'
|
|
||||||
};
|
|
||||||
|
|
||||||
await removeImagesStartingWith('compose-test-build-image');
|
|
||||||
|
|
||||||
await compose.buildOne('build_test_1', opts);
|
|
||||||
|
|
||||||
expect(await imageExists('compose-test-build-image-1:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-2:test')).toBeFalsy();
|
|
||||||
expect(await imageExists('compose-test-build-image-3:test')).toBeFalsy();
|
|
||||||
expect(await imageExists('compose-test-build-image-4:test')).toBeFalsy();
|
|
||||||
|
|
||||||
await removeImagesStartingWith('compose-test-build-image');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('build multiple services', async () => {
|
|
||||||
const opts = {
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml'
|
|
||||||
};
|
|
||||||
|
|
||||||
await compose.buildMany([ 'build_test_2', 'build_test_3' ], opts);
|
|
||||||
|
|
||||||
expect(await imageExists('compose-test-build-image-1:test')).toBeFalsy();
|
|
||||||
expect(await imageExists('compose-test-build-image-2:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-3:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-4:test')).toBeFalsy();
|
|
||||||
|
|
||||||
await removeImagesStartingWith('compose-test-build-image');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('build all services', async () => {
|
|
||||||
const opts = {
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
log: logOutput,
|
|
||||||
config: 'docker-compose-build.yml'
|
|
||||||
};
|
|
||||||
|
|
||||||
await compose.buildAll(opts);
|
|
||||||
|
|
||||||
expect(await imageExists('compose-test-build-image-1:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-2:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-3:test')).toBeTruthy();
|
|
||||||
expect(await imageExists('compose-test-build-image-4:test')).toBeTruthy();
|
|
||||||
|
|
||||||
await removeImagesStartingWith('compose-test-build-image');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('teardown', async () => {
|
|
||||||
docker.listContainers((err, containers) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
containers.forEach(container => {
|
|
||||||
container.Names.forEach(name => {
|
|
||||||
if (name.startsWith('/compose_test_')) {
|
|
||||||
console.log(`stopping ${container.Id} ${container.Names}`);
|
|
||||||
docker.getContainer(container.Id).stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await removeImagesStartingWith('compose-test-build-image');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('config show data for docker-compose files', async () => {
|
|
||||||
const std = await compose.config({ cwd: path.join(__dirname), log: logOutput, config: 'docker-compose-42.yml' });
|
|
||||||
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
expect(std.out.includes('some-service')).toBeTruthy();
|
|
||||||
expect(std.out.includes('test/volume:/mountedvolume:rw')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('config show data for docker-compose files (services)', async () => {
|
|
||||||
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 () => {
|
|
||||||
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 () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
const std = await compose.ps({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
expect(std.out.includes('compose_test_alpine')).toBeTruthy();
|
|
||||||
expect(std.out.includes('compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ps does not show status data for stopped containers', async () => {
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.upOne('alpine', { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
const std = await compose.ps({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(std.err).toBeFalsy();
|
|
||||||
expect(std.out.includes('compose_test_alpine')).toBeTruthy();
|
|
||||||
expect(std.out.includes('compose_test_nginx')).toBeFalsy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('restartAll does restart all containers', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.restartAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('restartMany does restart selected containers', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.restartMany([ 'db', 'alpine' ], { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('restartOne does restart container', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.restartOne('db', { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('logs does follow service logs', async () => {
|
|
||||||
await compose.upAll({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
await compose.logs('db', { cwd: path.join(__dirname), log: logOutput });
|
|
||||||
|
|
||||||
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy();
|
|
||||||
await compose.down({ cwd: path.join(__dirname), log: logOutput });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns the port for a started service', async () => {
|
|
||||||
const config = {
|
|
||||||
cwd: path.join(__dirname),
|
|
||||||
config: './docker-compose-2.yml',
|
|
||||||
log: logOutput
|
|
||||||
};
|
|
||||||
|
|
||||||
await compose.upAll(config);
|
|
||||||
const port = await compose.port('db', 5432, config);
|
|
||||||
|
|
||||||
expect(port.out).toMatch(/.*:[0-9]{1,5}/);
|
|
||||||
await compose.down(config);
|
|
||||||
});
|
|
||||||
736
test/index.test.ts
Normal file
736
test/index.test.ts
Normal file
|
|
@ -0,0 +1,736 @@
|
||||||
|
import Docker from 'dockerode'
|
||||||
|
import * as compose from '../src/index'
|
||||||
|
import * as path from 'path'
|
||||||
|
import { readFile } from 'fs'
|
||||||
|
import { mapPsOutput } from '../src/index'
|
||||||
|
const docker = new Docker()
|
||||||
|
|
||||||
|
// Docker commands, especially builds, can take some time. This makes sure that they can take the time they need.
|
||||||
|
jest.setTimeout(25000)
|
||||||
|
|
||||||
|
// Set to true if you need to diagnose using output
|
||||||
|
const logOutput = false
|
||||||
|
|
||||||
|
const isContainerRunning = async (name: string): Promise<boolean> =>
|
||||||
|
new Promise((resolve, reject): void => {
|
||||||
|
docker.listContainers((err, containers): void => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const running = (containers || []).filter((container): boolean =>
|
||||||
|
container.Names.includes(name)
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve(running.length > 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const repoTags = (imageInfo): string[] => imageInfo.RepoTags || []
|
||||||
|
|
||||||
|
const imageExists = async (name: string): Promise<boolean> => {
|
||||||
|
const images = await docker.listImages()
|
||||||
|
|
||||||
|
const foundImage = images.findIndex((imageInfo): boolean =>
|
||||||
|
repoTags(imageInfo).includes(name)
|
||||||
|
)
|
||||||
|
|
||||||
|
return foundImage > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeImagesStartingWith = async (
|
||||||
|
searchString: string
|
||||||
|
): Promise<void> => {
|
||||||
|
const images = await docker.listImages()
|
||||||
|
|
||||||
|
for (const image of images) {
|
||||||
|
for (const repoTag of repoTags(image)) {
|
||||||
|
if (repoTag.startsWith(searchString)) {
|
||||||
|
const dockerImage = docker.getImage(repoTag)
|
||||||
|
|
||||||
|
if (logOutput) {
|
||||||
|
process.stdout.write(
|
||||||
|
`removing image ${repoTag} ${dockerImage.id || ''}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await dockerImage.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('ensure container gets started', async (): Promise<void> => {
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure exit code is returned correctly', async (): Promise<void> => {
|
||||||
|
let result = await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
exitCode: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
result = await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
exitCode: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
let failedResult = 0
|
||||||
|
try {
|
||||||
|
await compose.logs('non_existent_service', {
|
||||||
|
cwd: path.join(__dirname)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
failedResult = error.exitCode
|
||||||
|
}
|
||||||
|
expect(failedResult).toBe(1)
|
||||||
|
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('starts containers properly with --build and --timeout options', (): void => {
|
||||||
|
beforeEach(
|
||||||
|
async (): Promise<void> => {
|
||||||
|
await compose.down({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
afterEach(
|
||||||
|
async (): Promise<void> => {
|
||||||
|
await compose.down({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test('ensure container gets started with --build option', async (): Promise<void> => {
|
||||||
|
await compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml',
|
||||||
|
commandOptions: ['--build']
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets started with --build and --timeout option', async (): Promise<void> => {
|
||||||
|
await compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml',
|
||||||
|
commandOptions: [['--build'], ['--timeout', '5']]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets started with --build and --timeout option with different command style', async (): Promise<void> => {
|
||||||
|
await compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml',
|
||||||
|
commandOptions: ['--build', ['--timeout', '5']]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_nginx')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container command executed with --workdir command option', async (): Promise<void> => {
|
||||||
|
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),
|
||||||
|
log: true,
|
||||||
|
config: 'docker-compose-42.yml',
|
||||||
|
composeOptions: ['--verbose'],
|
||||||
|
|
||||||
|
// Alpine has "/" as default
|
||||||
|
commandOptions: ['--workdir', '/home/root']
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.out).toBe('/home/root\n')
|
||||||
|
|
||||||
|
await compose.down({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-42.yml'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure only single container gets started', async (): Promise<void> => {
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.upOne('web', { cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure only multiple containers get started', async (): Promise<void> => {
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.upMany(['web'], { cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets down', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets stopped', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.stop({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure only single container gets stopped', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.stopOne('proxy', { cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure only single container gets paused then resumed', async (): Promise<void> => {
|
||||||
|
const opts = { cwd: path.join(__dirname), log: logOutput }
|
||||||
|
await compose.upAll(opts)
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.pauseOne('proxy', opts)
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
let errMsg
|
||||||
|
try {
|
||||||
|
await compose.exec('proxy', 'cat /etc/os-release', opts)
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.err
|
||||||
|
}
|
||||||
|
expect(errMsg).toContain('is paused')
|
||||||
|
await compose.unpauseOne('proxy', opts)
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
const std = await compose.exec('proxy', 'cat /etc/os-release', opts)
|
||||||
|
expect(std.out).toContain('Alpine Linux')
|
||||||
|
await compose.down(opts)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets started with --abort-on-container-exit option', async (): Promise<void> => {
|
||||||
|
const result = await compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
commandOptions: ['--abort-on-container-exit']
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
exitCode: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets started with --abort-on-container-exit option correctly aborts all services when a container exits', async (): Promise<void> => {
|
||||||
|
const result = await compose.upAll({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
commandOptions: ['--abort-on-container-exit']
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.out).toMatch(/Aborting on container exit/)
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure container gets killed', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.kill({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure custom ymls are working', async (): Promise<void> => {
|
||||||
|
const config = './docker-compose-2.yml'
|
||||||
|
const cwd = path.join(__dirname)
|
||||||
|
|
||||||
|
await compose.upAll({ cwd, log: logOutput, config })
|
||||||
|
expect(await isContainerRunning('/compose_test_web_2')).toBeTruthy()
|
||||||
|
|
||||||
|
// config & [config] are the same thing, ensures that multiple configs are handled properly
|
||||||
|
await compose.kill({ cwd, log: logOutput, config: [config] })
|
||||||
|
expect(await isContainerRunning('/compose_test_web_2')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.down({ cwd, log: logOutput, config })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure run and exec are working', async (): Promise<void> => {
|
||||||
|
const checkOSID = (out, id): void => {
|
||||||
|
// parse /etc/os-release contents
|
||||||
|
const re = /([\w,_]+)=(.*)/g
|
||||||
|
let match
|
||||||
|
const os: { ID?: string } = {}
|
||||||
|
|
||||||
|
while ((match = re.exec(out)) !== null) {
|
||||||
|
// eslint-disable-line no-cond-assign
|
||||||
|
os[match[1]] = match[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(os.ID).toBe(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts = { cwd: path.join(__dirname), log: logOutput }
|
||||||
|
|
||||||
|
await compose.upAll(opts)
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
|
||||||
|
let std = await compose.exec('web', 'cat /etc/os-release', opts)
|
||||||
|
|
||||||
|
checkOSID(std.out, 'debian')
|
||||||
|
|
||||||
|
std = await compose.run('proxy', 'cat /etc/os-release', opts)
|
||||||
|
|
||||||
|
checkOSID(std.out, 'alpine')
|
||||||
|
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure run and exec with command defined as array are working', async (): Promise<void> => {
|
||||||
|
const checkOSID = (out, id): void => {
|
||||||
|
// parse /etc/os-release contents
|
||||||
|
const re = /([\w,_]+)=(.*)/g
|
||||||
|
let match
|
||||||
|
const os: { ID?: string } = {}
|
||||||
|
|
||||||
|
while ((match = re.exec(out)) !== null) {
|
||||||
|
// eslint-disable-line no-cond-assign
|
||||||
|
os[match[1]] = match[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(os.ID).toBe(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts = { cwd: path.join(__dirname), log: false }
|
||||||
|
|
||||||
|
await compose.upAll(opts)
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBe(true)
|
||||||
|
|
||||||
|
let std = await compose.exec(
|
||||||
|
'web',
|
||||||
|
['/bin/sh', '-c', 'cat /etc/os-release'],
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
checkOSID(std.out, 'debian')
|
||||||
|
|
||||||
|
std = await compose.run(
|
||||||
|
'proxy',
|
||||||
|
['/bin/sh', '-c', 'cat /etc/os-release'],
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
checkOSID(std.out, 'alpine')
|
||||||
|
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('build accepts config as string', async (): Promise<void> => {
|
||||||
|
const configuration = await new Promise<string>(function (
|
||||||
|
resolve,
|
||||||
|
reject
|
||||||
|
): void {
|
||||||
|
readFile(
|
||||||
|
path.join(__dirname, 'docker-compose-2.yml'),
|
||||||
|
function (err, content) {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(content.toString())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const config = {
|
||||||
|
configAsString: configuration,
|
||||||
|
log: logOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
await compose.upAll(config)
|
||||||
|
const result = await compose.port('web', 8888, config)
|
||||||
|
|
||||||
|
expect(result.data.address).toBe('0.0.0.0')
|
||||||
|
expect(result.data.port).toBe(8888)
|
||||||
|
await compose.down(config)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('build single service', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeImagesStartingWith('compose-test-build-image')
|
||||||
|
|
||||||
|
await compose.buildOne('build_test_1', opts)
|
||||||
|
|
||||||
|
expect(await imageExists('compose-test-build-image-1:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-2:test')).toBeFalsy()
|
||||||
|
expect(await imageExists('compose-test-build-image-3:test')).toBeFalsy()
|
||||||
|
expect(await imageExists('compose-test-build-image-4:test')).toBeFalsy()
|
||||||
|
|
||||||
|
await removeImagesStartingWith('compose-test-build-image')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('build multiple services', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await compose.buildMany(['build_test_2', 'build_test_3'], opts)
|
||||||
|
|
||||||
|
expect(await imageExists('compose-test-build-image-1:test')).toBeFalsy()
|
||||||
|
expect(await imageExists('compose-test-build-image-2:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-3:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-4:test')).toBeFalsy()
|
||||||
|
|
||||||
|
await removeImagesStartingWith('compose-test-build-image')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('build all services', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await compose.buildAll(opts)
|
||||||
|
|
||||||
|
expect(await imageExists('compose-test-build-image-1:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-2:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-3:test')).toBeTruthy()
|
||||||
|
expect(await imageExists('compose-test-build-image-4:test')).toBeTruthy()
|
||||||
|
|
||||||
|
await removeImagesStartingWith('compose-test-build-image')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('pull single service', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeImagesStartingWith('nginx:1.19.9-alpine')
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.pullOne('proxy', opts)
|
||||||
|
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('pull multiple services', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeImagesStartingWith('nginx:1.16.0')
|
||||||
|
await removeImagesStartingWith('nginx:1.19.9-alpine')
|
||||||
|
|
||||||
|
expect(await imageExists('nginx:1.16.0')).toBeFalsy()
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.pullMany(['web', 'proxy'], opts)
|
||||||
|
|
||||||
|
expect(await imageExists('nginx:1.16.0')).toBeTruthy()
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('pull all services', async (): Promise<void> => {
|
||||||
|
const opts = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose.yml'
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeImagesStartingWith('nginx:1.16.0')
|
||||||
|
await removeImagesStartingWith('nginx:1.19.9-alpine')
|
||||||
|
|
||||||
|
expect(await imageExists('nginx:1.16.0')).toBeFalsy()
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.pullAll(opts)
|
||||||
|
|
||||||
|
expect(await imageExists('nginx:1.16.0')).toBeTruthy()
|
||||||
|
expect(await imageExists('nginx:1.19.9-alpine')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('teardown', async (): Promise<void> => {
|
||||||
|
interface Container {
|
||||||
|
Names: string[]
|
||||||
|
Id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
docker.listContainers((err, containers: Container[]): void => {
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await removeImagesStartingWith('compose-test-build-image')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('config show data for docker-compose files', async (): Promise<void> => {
|
||||||
|
const std = await compose.config({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-42.yml'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(std.data.config.version).toBe('3')
|
||||||
|
expect(std.data.config.services['some-service']['image']).toBe(
|
||||||
|
'nginx:1.19.9-alpine'
|
||||||
|
)
|
||||||
|
expect(std.data.config.volumes['db-data']).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('config show data for docker-compose files (services)', async (): Promise<void> => {
|
||||||
|
const std = await compose.configServices({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-build.yml'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(std.data.services.length).toBe(5)
|
||||||
|
expect(std.data.services[0]).toBe('build_test_1')
|
||||||
|
expect(std.err).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('config show data for docker-compose files (volumes)', async (): Promise<void> => {
|
||||||
|
const std = await compose.configVolumes({
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput,
|
||||||
|
config: 'docker-compose-42.yml'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(std.data.volumes.length).toBe(1)
|
||||||
|
expect(std.data.volumes[0]).toBe('db-data')
|
||||||
|
expect(std.err).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ps shows status data for started containers', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
const std = await compose.ps({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(std.err).toBeFalsy()
|
||||||
|
expect(std.data.services.length).toBe(3)
|
||||||
|
const web = std.data.services.find(
|
||||||
|
(service) => service.name === 'compose_test_web'
|
||||||
|
)
|
||||||
|
expect(std.data.services.length).toBe(3)
|
||||||
|
expect(web?.ports.length).toBe(2)
|
||||||
|
expect(web?.ports[0].exposed.port).toBe(443)
|
||||||
|
expect(web?.ports[0].exposed.protocol).toBe('tcp')
|
||||||
|
expect(web?.ports[0].mapped?.port).toBe(443)
|
||||||
|
expect(web?.ports[0].mapped?.address).toBe('0.0.0.0')
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ps does not show status data for stopped containers', async (): Promise<void> => {
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.upOne('web', { cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
const std = await compose.ps({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(std.err).toBeFalsy()
|
||||||
|
const web = std.data.services.find(
|
||||||
|
(service) => service.name === 'compose_test_web'
|
||||||
|
)
|
||||||
|
const proxy = std.data.services.find(
|
||||||
|
(service) => service.name === 'compose_test_proxy'
|
||||||
|
)
|
||||||
|
expect(web?.name).toBe('compose_test_web')
|
||||||
|
expect(proxy).toBeFalsy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('restartAll does restart all containers', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.restartAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('restartMany does restart selected containers', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.restartMany(['web', 'proxy'], {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('restartOne does restart container', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
await compose.restartOne('proxy', {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('logs does follow service logs', async (): Promise<void> => {
|
||||||
|
await compose.upAll({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
const std = await compose.logs('proxy', {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
log: logOutput
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(std.out.includes('compose_test_proxy')).toBeTruthy()
|
||||||
|
await compose.down({ cwd: path.join(__dirname), log: logOutput })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns the port for a started service', async (): Promise<void> => {
|
||||||
|
const config = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
config: './docker-compose-2.yml',
|
||||||
|
log: logOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
await compose.upAll(config)
|
||||||
|
const port = await compose.port('web', 8888, config)
|
||||||
|
|
||||||
|
expect(port.out).toMatch(/.*:[0-9]{1,5}/)
|
||||||
|
await compose.down(config)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('removes container', async (): Promise<void> => {
|
||||||
|
const config = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
config: './docker-compose.yml',
|
||||||
|
log: logOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
await compose.upAll(config)
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeTruthy()
|
||||||
|
|
||||||
|
await compose.rm({ ...config, commandOptions: ['-s'] }, 'proxy')
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeTruthy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
|
||||||
|
await compose.rm({ ...config, commandOptions: ['-s'] }, 'proxy', 'web')
|
||||||
|
expect(await isContainerRunning('/compose_test_web')).toBeFalsy()
|
||||||
|
expect(await isContainerRunning('/compose_test_proxy')).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns version information', async (): Promise<void> => {
|
||||||
|
const version = await (await compose.version()).data.version
|
||||||
|
|
||||||
|
expect(version).toMatch(/^(\d+\.)?(\d+\.)?(\*|\d+)$/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parse ps output', () => {
|
||||||
|
const output = ` Name Command State Ports \n-------------------------------------------------------------------------------------------------------\ncompose_test_hello /hello Exit 0 \ncompose_test_proxy /docker-entrypoint.sh ngin ... Up 80/tcp \ncompose_test_web nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp\n`
|
||||||
|
|
||||||
|
const psOut = mapPsOutput(output)
|
||||||
|
expect(psOut.services[0]).toEqual({
|
||||||
|
command: '/hello',
|
||||||
|
name: 'compose_test_hello',
|
||||||
|
state: 'Exit 0',
|
||||||
|
ports: []
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(psOut.services[1]).toEqual({
|
||||||
|
command: '/docker-entrypoint.sh ngin ...',
|
||||||
|
name: 'compose_test_proxy',
|
||||||
|
state: 'Up',
|
||||||
|
ports: [{ exposed: { port: 80, protocol: 'tcp' } }]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(psOut.services[2]).toEqual({
|
||||||
|
command: 'nginx -g daemon off;',
|
||||||
|
name: 'compose_test_web',
|
||||||
|
state: 'Up',
|
||||||
|
ports: [
|
||||||
|
{
|
||||||
|
exposed: { port: 443, protocol: 'tcp' },
|
||||||
|
mapped: { port: 443, address: '0.0.0.0' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exposed: { port: 80, protocol: 'tcp' },
|
||||||
|
mapped: { port: 80, address: '0.0.0.0' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ensure progress callback is called', async (): Promise<void> => {
|
||||||
|
const config = {
|
||||||
|
cwd: path.join(__dirname),
|
||||||
|
config: './docker-compose.yml',
|
||||||
|
callback: jest.fn()
|
||||||
|
}
|
||||||
|
await compose.upAll(config)
|
||||||
|
expect(config.callback).toBeCalled()
|
||||||
|
await compose.down(config)
|
||||||
|
})
|
||||||
58
test/map-ports.test.ts
Normal file
58
test/map-ports.test.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import mapPorts from '../src/map-ports'
|
||||||
|
|
||||||
|
test('map ports for empty string', () => {
|
||||||
|
expect(mapPorts('')).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('map ports for exposed tcp', () => {
|
||||||
|
expect(mapPorts('80/tcp')).toEqual([
|
||||||
|
{ exposed: { port: 80, protocol: 'tcp' } }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('map ports for exposed tcp on ivp4 interface', () => {
|
||||||
|
expect(mapPorts('0.0.0.0:443->443/tcp')).toEqual([
|
||||||
|
{
|
||||||
|
exposed: { port: 443, protocol: 'tcp' },
|
||||||
|
mapped: { address: '0.0.0.0', port: 443 }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('map multiple tcp ports exposed on ivp4 interfaces', () => {
|
||||||
|
expect(mapPorts('0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp')).toEqual([
|
||||||
|
{
|
||||||
|
exposed: { port: 443, protocol: 'tcp' },
|
||||||
|
mapped: { address: '0.0.0.0', port: 443 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exposed: { port: 80, protocol: 'tcp' },
|
||||||
|
mapped: { address: '0.0.0.0', port: 80 }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('map multiple tcp ports exposed on ipv4 and ipv6 interfaces', () => {
|
||||||
|
expect(
|
||||||
|
mapPorts(
|
||||||
|
'0.0.0.0:443->443/tcp,:::443->443/tcp, 0.0.0.0:80->80/tcp,:::80->80/tcp'
|
||||||
|
)
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
exposed: { port: 443, protocol: 'tcp' },
|
||||||
|
mapped: { address: '0.0.0.0', port: 443 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exposed: { port: 443, protocol: 'tcp' },
|
||||||
|
mapped: { address: '::', port: 443 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exposed: { port: 80, protocol: 'tcp' },
|
||||||
|
mapped: { address: '0.0.0.0', port: 80 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exposed: { port: 80, protocol: 'tcp' },
|
||||||
|
mapped: { address: '::', port: 80 }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
68
tsconfig.json
Normal file
68
tsconfig.json
Normal file
|
|
@ -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": ["es2015"], /* 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue