diff --git a/garden-service/package-lock.json b/garden-service/package-lock.json index 52ced2b18f..23fdb8e7ac 100644 --- a/garden-service/package-lock.json +++ b/garden-service/package-lock.json @@ -402,7 +402,7 @@ }, "@types/async-lock": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/@types/async-lock/-/async-lock-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.1.0.tgz", "integrity": "sha512-Eo8EXiqmChtkt0ETf6AQ8aiDHT3Tht6OuMSa3/9nfuyqFimp7ZwPMiufsA56A7ZUGBuwFzH860jO0d8n0lETtg==", "dev": true }, @@ -429,7 +429,7 @@ }, "@types/chokidar": { "version": "1.7.5", - "resolved": "http://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", + "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", "integrity": "sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ==", "dev": true, "requires": { @@ -498,12 +498,12 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" }, "@types/execa": { "version": "0.9.0", - "resolved": "http://registry.npmjs.org/@types/execa/-/execa-0.9.0.tgz", + "resolved": "https://registry.npmjs.org/@types/execa/-/execa-0.9.0.tgz", "integrity": "sha512-mgfd93RhzjYBUHHV532turHC2j4l/qxsF/PbfDmprHDEUHmNZGlDn1CEsulGK3AfsPdhkWzZQT/S/k0UGhLGsA==", "dev": true, "requires": { @@ -749,13 +749,13 @@ }, "@types/path-is-inside": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/@types/path-is-inside/-/path-is-inside-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/@types/path-is-inside/-/path-is-inside-1.0.0.tgz", "integrity": "sha512-hfnXRGugz+McgX2jxyy5qz9sB21LRzlGn24zlwN2KEgoPtEvjzNRrLtUkOOebPDPZl3Rq7ywKxYvylVcEZDnEw==", "dev": true }, "@types/prettyjson": { "version": "0.0.28", - "resolved": "http://registry.npmjs.org/@types/prettyjson/-/prettyjson-0.0.28.tgz", + "resolved": "https://registry.npmjs.org/@types/prettyjson/-/prettyjson-0.0.28.tgz", "integrity": "sha1-ExqJDe1kIbG1RfRRCkrqvG1GUnU=", "dev": true }, @@ -931,7 +931,7 @@ }, "@types/undertaker": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.0.tgz", "integrity": "sha512-bx/5nZCGkasXs6qaA3B6SVDjBZqdyk04UO12e0uEPSzjt5H8jEJw0DKe7O7IM0hM2bVHRh70pmOH7PEHqXwzOw==", "dev": true, "requires": { @@ -992,7 +992,7 @@ }, "@types/wrap-ansi": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", "dev": true }, @@ -1075,7 +1075,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -1412,7 +1412,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "requires": { "follow-redirects": "^1.3.0", @@ -1444,7 +1444,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1466,7 +1466,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -1991,7 +1991,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", @@ -2010,7 +2010,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -2862,7 +2862,7 @@ }, "enabled": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { "env-variable": "0.0.x" @@ -3051,6 +3051,11 @@ "es5-ext": "~0.10.14" } }, + "eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" + }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -3464,24 +3469,24 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "optional": true, "requires": { @@ -3491,12 +3496,12 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", @@ -3505,34 +3510,34 @@ }, "chownr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "optional": true, "requires": { @@ -3541,25 +3546,25 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "resolved": false, "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "optional": true, "requires": { @@ -3568,13 +3573,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { @@ -3590,7 +3595,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "optional": true, "requires": { @@ -3604,13 +3609,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.21", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "resolved": false, "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "optional": true, "requires": { @@ -3619,7 +3624,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "optional": true, "requires": { @@ -3628,7 +3633,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { @@ -3638,18 +3643,18 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "^1.0.0" @@ -3657,13 +3662,13 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" @@ -3671,12 +3676,12 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "requires": { "safe-buffer": "^5.1.1", @@ -3685,7 +3690,7 @@ }, "minizlib": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "resolved": false, "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "optional": true, "requires": { @@ -3694,7 +3699,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -3702,13 +3707,13 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "optional": true }, "needle": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "resolved": false, "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "optional": true, "requires": { @@ -3719,7 +3724,7 @@ }, "node-pre-gyp": { "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "resolved": false, "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "optional": true, "requires": { @@ -3737,7 +3742,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "optional": true, "requires": { @@ -3747,13 +3752,13 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "resolved": false, "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "resolved": false, "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "optional": true, "requires": { @@ -3763,7 +3768,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { @@ -3775,18 +3780,18 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" @@ -3794,19 +3799,19 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { @@ -3816,19 +3821,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "optional": true }, "rc": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "resolved": false, "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "optional": true, "requires": { @@ -3840,7 +3845,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "optional": true } @@ -3848,7 +3853,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "optional": true, "requires": { @@ -3863,7 +3868,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "optional": true, "requires": { @@ -3872,42 +3877,42 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -3917,7 +3922,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { @@ -3926,7 +3931,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -3934,13 +3939,13 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "resolved": false, "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "optional": true, "requires": { @@ -3955,13 +3960,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "optional": true, "requires": { @@ -3970,12 +3975,12 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } @@ -4349,7 +4354,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -4362,7 +4367,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -4400,7 +4405,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -4438,7 +4443,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -4462,7 +4467,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -4567,7 +4572,7 @@ "dependencies": { "chalk": { "version": "2.3.1", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { @@ -4640,7 +4645,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -4701,7 +4706,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -4719,7 +4724,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -5135,7 +5140,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6324,7 +6329,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { @@ -6400,7 +6405,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -6713,7 +6718,7 @@ }, "lodash": { "version": "2.4.1", - "resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", "integrity": "sha1-W3cjA03aTSYuWkb7LFjXzCL3FCA=", "dev": true } @@ -8535,7 +8540,7 @@ }, "pegjs": { "version": "0.10.0", - "resolved": "http://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", "dev": true }, @@ -8567,7 +8572,7 @@ }, "pkijs": { "version": "1.3.33", - "resolved": "http://registry.npmjs.org/pkijs/-/pkijs-1.3.33.tgz", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-1.3.33.tgz", "integrity": "sha1-ponvYhE7fDSOH/wJll0iOeW7TJI=" }, "plugin-error": { @@ -8594,7 +8599,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -8786,7 +8791,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -9072,7 +9077,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" @@ -9209,7 +9214,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10073,7 +10078,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-indent": { @@ -10255,7 +10260,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -10290,6 +10295,12 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, + "timekeeper": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.1.2.tgz", + "integrity": "sha512-fc1DDqbiyz5vxRO4xkiATwfWUw1FV7W20+FJYal1SnoIYgNuB4WNxYLtbG3zjUBwOSk3P4u1TgBAZYG/aqBWMw==", + "dev": true + }, "timers-ext": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", @@ -10467,7 +10478,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -11097,7 +11108,7 @@ }, "yargs": { "version": "11.1.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "requires": { "cliui": "^4.0.0", diff --git a/garden-service/package.json b/garden-service/package.json index 4722115848..a1beec5006 100644 --- a/garden-service/package.json +++ b/garden-service/package.json @@ -45,6 +45,7 @@ "dockerode": "^2.5.7", "elegant-spinner": "^1.0.1", "escape-string-regexp": "^1.0.5", + "eventemitter2": "^5.0.1", "execa": "^1.0.0", "fs-extra": "^7.0.0", "get-port": "^4.0.0", @@ -138,6 +139,7 @@ "snyk": "^1.105.0", "testdouble": "^3.8.2", "testdouble-chai": "^0.5.0", + "timekeeper": "^2.1.2", "tmp-promise": "^1.0.5", "ts-node": "^7.0.1", "tslint": "^5.11.0", diff --git a/garden-service/src/events.ts b/garden-service/src/events.ts new file mode 100644 index 0000000000..51027d7a18 --- /dev/null +++ b/garden-service/src/events.ts @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { EventEmitter2 } from "eventemitter2" +import { TaskResult } from "./task-graph" +import { ModuleVersion } from "./vcs/base" + +/** + * This simple class serves as the central event bus for a Garden instance. Its function + * is mainly to consolidate all events for the instance, to ensure type-safety. + * + * See below for the event interfaces. + */ +export class EventBus extends EventEmitter2 { + constructor() { + super({ + wildcard: false, + newListener: false, + maxListeners: 100, // we may need to adjust this + }) + } + + emit(name: T, payload: Events[T]) { + return super.emit(name, payload) + } + + on(name: T, listener: (payload: Events[T]) => void) { + return super.on(name, listener) + } + + onAny(listener: (name: T, payload: Events[T]) => void) { + return super.onAny(listener) + } + + once(name: T, listener: (payload: Events[T]) => void) { + return super.once(name, listener) + } + + // TODO: wrap more methods to make them type-safe +} + +/** + * The supported events and their interfaces. + */ +export interface Events { + _test: string, + taskPending: { + addedAt: Date, + key: string, + version: ModuleVersion, + }, + taskComplete: TaskResult, + taskError: TaskResult, +} + +export type EventName = keyof Events diff --git a/garden-service/src/garden.ts b/garden-service/src/garden.ts index 4ed440a24c..01812d228d 100644 --- a/garden-service/src/garden.ts +++ b/garden-service/src/garden.ts @@ -107,6 +107,7 @@ import { ModuleAndRuntimeActions, Plugins, RegisterPluginParam } from "./types/p import { SUPPORTED_PLATFORMS, SupportedPlatform } from "./constants" import { platform, arch } from "os" import { LogEntry } from "./logger/log-entry" +import { EventBus } from "./events" export interface ActionHandlerMap { [actionName: string]: PluginActions[T] @@ -159,6 +160,7 @@ export class Garden { public readonly vcs: VcsHandler public readonly cache: TreeCache public readonly actions: ActionHelper + public readonly events: EventBus constructor( public readonly projectRoot: string, @@ -203,12 +205,15 @@ export class Garden { this.actionHandlers = fromPairs(pluginActionNames.map(n => [n, {}])) this.moduleActionHandlers = fromPairs(moduleActionNames.map(n => [n, {}])) - this.taskGraph = new TaskGraph(this.log) + this.taskGraph = new TaskGraph(this, this.log) this.actions = new ActionHelper(this) this.hotReloadScheduler = new HotReloadScheduler() + this.events = new EventBus() } - static async factory(currentDirectory: string, { env, config, log, plugins = {} }: ContextOpts = {}) { + static async factory( + this: T, currentDirectory: string, { env, config, log, plugins = {} }: ContextOpts = {}, + ): Promise> { let parsedConfig: GardenConfig if (config) { @@ -288,7 +293,7 @@ export class Garden { const buildDir = await BuildDir.factory(projectRoot) - const garden = new Garden( + const garden = new this( projectRoot, projectName, environmentName, @@ -296,17 +301,18 @@ export class Garden { projectSources, buildDir, log, - ) + ) as InstanceType // Register plugins for (const [name, pluginFactory] of Object.entries({ ...builtinPlugins, ...plugins })) { - garden.registerPlugin(name, pluginFactory) + // This cast is required for the linter to accept the instance type hackery. + (garden).registerPlugin(name, pluginFactory) } // Load configured plugins // Validate configuration for (const provider of Object.values(mergedProviderConfigs)) { - await garden.loadPlugin(provider.name, provider) + await (garden).loadPlugin(provider.name, provider) } return garden diff --git a/garden-service/src/task-graph.ts b/garden-service/src/task-graph.ts index 271c9b1e36..cf3804eada 100644 --- a/garden-service/src/task-graph.ts +++ b/garden-service/src/task-graph.ts @@ -14,6 +14,7 @@ import { BaseTask, TaskDefinitionError } from "./tasks/base" import { LogEntry } from "./logger/log-entry" import { toGardenError } from "./exceptions" +import { Garden } from "./garden" class TaskGraphError extends Error { } @@ -45,7 +46,7 @@ export class TaskGraph { private resultCache: ResultCache private opQueue: PQueue - constructor(private log: LogEntry, private concurrency: number = DEFAULT_CONCURRENCY) { + constructor(private garden: Garden, private log: LogEntry, private concurrency: number = DEFAULT_CONCURRENCY) { this.roots = new TaskNodeMap() this.index = new TaskNodeMap() this.inProgress = new TaskNodeMap() @@ -87,6 +88,13 @@ export class TaskGraph { } this.index.addNode(node) + + this.garden.events.emit("taskPending", { + addedAt: new Date(), + key: node.getKey(), + version: task.version, + }) + await this.addDependencies(node) if (node.getDependencies().length === 0) { @@ -142,8 +150,10 @@ export class TaskGraph { try { result = await node.process(dependencyResults) + this.garden.events.emit("taskComplete", result) } catch (error) { result = { type, description, error } + this.garden.events.emit("taskError", result) this.logTaskError(node, error) this.cancelDependants(node) } finally { diff --git a/garden-service/src/util/util.ts b/garden-service/src/util/util.ts index 7944fb2529..9301b79417 100644 --- a/garden-service/src/util/util.ts +++ b/garden-service/src/util/util.ts @@ -35,6 +35,7 @@ export type HookCallback = (callback?: () => void) => void const exitHookNames: string[] = [] // For debugging/testing/inspection purposes +export type ValueOf = T[keyof T] export type Omit = Pick> export type Diff = T extends U ? never : T export type Nullable = { [P in keyof T]: T[P] | null } diff --git a/garden-service/test/helpers.ts b/garden-service/test/helpers.ts index b28748bc42..cd2a3b77c5 100644 --- a/garden-service/test/helpers.ts +++ b/garden-service/test/helpers.ts @@ -12,9 +12,7 @@ import { remove, readdirSync, existsSync } from "fs-extra" import { containerModuleSpecSchema } from "../src/plugins/container" import { testGenericModule, buildGenericModule } from "../src/plugins/generic" import { TaskResults } from "../src/task-graph" -import { - validate, -} from "../src/config/common" +import { validate, PrimitiveMap } from "../src/config/common" import { GardenPlugin, PluginActions, @@ -34,13 +32,14 @@ import { RunTaskParams, SetSecretParams, } from "../src/types/plugin/params" -import { - helpers, -} from "../src/vcs/git" -import { - ModuleVersion, -} from "../src/vcs/base" +import { helpers } from "../src/vcs/git" +import { ModuleVersion } from "../src/vcs/base" import { GARDEN_DIR_NAME } from "../src/constants" +import { EventBus, Events } from "../src/events" +import { ValueOf } from "../src/util/util" +import { SourceConfig } from "../src/config/project" +import { BuildDir } from "../src/build-dir" +import timekeeper = require("timekeeper") export const dataDir = resolve(__dirname, "data") export const examplesDir = resolve(__dirname, "..", "..", "examples") @@ -230,7 +229,46 @@ export const makeTestModule = (params: Partial = {}) => { return { ...defaultModuleConfig, ...params } } -export const makeTestGarden = async (projectRoot: string, extraPlugins: Plugins = {}) => { +interface EventLogEntry { + name: string + payload: ValueOf +} + +/** + * Used for test Garden instances, to log emitted events. + */ +class TestEventBus extends EventBus { + log: EventLogEntry[] + + constructor() { + super() + this.log = [] + } + + emit(name: T, payload: Events[T]) { + this.log.push({ name, payload }) + return super.emit(name, payload) + } +} + +export class TestGarden extends Garden { + events: TestEventBus + + constructor( + public readonly projectRoot: string, + public readonly projectName: string, + environmentName: string, + variables: PrimitiveMap, + public readonly projectSources: SourceConfig[] = [], + public readonly buildDir: BuildDir, + log?, + ) { + super(projectRoot, projectName, environmentName, variables, projectSources, buildDir, log) + this.events = new TestEventBus() + } +} + +export const makeTestGarden = async (projectRoot: string, extraPlugins: Plugins = {}): Promise => { const testPlugins = { "test-plugin": testPlugin, "test-plugin-b": testPluginB, @@ -238,7 +276,7 @@ export const makeTestGarden = async (projectRoot: string, extraPlugins: Plugins } const plugins = { ...testPlugins, ...extraPlugins } - return Garden.factory(projectRoot, { plugins }) + return TestGarden.factory(projectRoot, { plugins }) } export const makeTestGardenA = async (extraPlugins: Plugins = {}) => { @@ -319,3 +357,11 @@ export function getExampleProjects() { const names = readdirSync(examplesDir).filter(n => existsSync(join(examplesDir, n, "garden.yml"))) return fromPairs(names.map(n => [n, join(examplesDir, n)])) } + +export function freezeTime(date?: Date) { + if (!date) { + date = new Date() + } + timekeeper.freeze(date) + return date +} diff --git a/garden-service/test/setup.ts b/garden-service/test/setup.ts index ec0b224641..fd7113904d 100644 --- a/garden-service/test/setup.ts +++ b/garden-service/test/setup.ts @@ -1,4 +1,5 @@ import * as td from "testdouble" +import * as timekeeper from "timekeeper" import { Logger } from "../src/logger/logger" import { LogLevel } from "../src/logger/log-node" import { makeTestGardenA } from "./helpers" @@ -18,4 +19,7 @@ before(async function(this: any) { }) beforeEach(() => { }) -afterEach(() => td.reset()) +afterEach(() => { + td.reset() + timekeeper.reset() +}) diff --git a/garden-service/test/src/events.ts b/garden-service/test/src/events.ts new file mode 100644 index 0000000000..3782af7688 --- /dev/null +++ b/garden-service/test/src/events.ts @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { EventBus } from "../../src/events" +import { expect } from "chai" + +describe("EventBus", () => { + let events: EventBus + + beforeEach(() => { + events = new EventBus() + }) + + it("should send+receive events", (done) => { + events.on("_test", (payload) => { + expect(payload).to.equal("foo") + done() + }) + events.emit("_test", "foo") + }) + + describe("onAny", () => { + it("should add listener for any supported event", (done) => { + events.onAny((name, payload) => { + expect(name).to.equal("_test") + expect(payload).to.equal("foo") + done() + }) + events.emit("_test", "foo") + }) + }) + + describe("once", () => { + it("should add a listener that only gets called once", (done) => { + events.once("_test", (payload) => { + expect(payload).to.equal("foo") + done() + }) + events.emit("_test", "foo") + events.emit("_test", "bar") + }) + }) +}) diff --git a/garden-service/test/src/task-graph.ts b/garden-service/test/src/task-graph.ts index 78bece8c56..f5ecb93201 100644 --- a/garden-service/test/src/task-graph.ts +++ b/garden-service/test/src/task-graph.ts @@ -6,7 +6,7 @@ import { TaskResult, TaskResults, } from "../../src/task-graph" -import { makeTestGarden } from "../helpers" +import { makeTestGarden, freezeTime } from "../helpers" import { Garden } from "../../src/garden" import { DependencyGraphNodeType } from "../../src/dependency-graph" @@ -16,6 +16,7 @@ type TestTaskCallback = (name: string, result: any) => Promise interface TestTaskOptions { callback?: TestTaskCallback + dependencies?: BaseTask[], id?: string throwError?: boolean } @@ -31,7 +32,6 @@ class TestTask extends BaseTask { constructor( garden: Garden, name: string, - dependencies?: BaseTask[], options?: TestTaskOptions, ) { super({ @@ -43,14 +43,16 @@ class TestTask extends BaseTask { dependencyVersions: {}, }, }) - this.name = name - this.callback = (options && options.callback) || null - this.id = (options && options.id) || "" - this.throwError = !!(options && options.throwError) - if (dependencies) { - this.dependencies = dependencies + if (!options) { + options = {} } + + this.name = name + this.callback = options.callback || null + this.id = options.id || "" + this.throwError = !!options.throwError + this.dependencies = options.dependencies || [] } getName() { @@ -93,7 +95,7 @@ describe("task-graph", () => { it("should successfully process a single task without dependencies", async () => { const garden = await getGarden() - const graph = new TaskGraph(garden.log) + const graph = new TaskGraph(garden, garden.log) const task = new TestTask(garden, "a") await graph.addTask(task) @@ -114,9 +116,55 @@ describe("task-graph", () => { expect(results).to.eql(expected) }) + it("should emit a taskPending event when adding a task", async () => { + const now = freezeTime() + + const garden = await getGarden() + const graph = new TaskGraph(garden, garden.log) + const task = new TestTask(garden, "a") + + await graph.addTask(task) + + expect(garden.events.log).to.eql([ + { name: "taskPending", payload: { addedAt: now, key: task.getKey(), version: task.version } }, + ]) + }) + + it("should emit a taskComplete event when completing a task", async () => { + const now = freezeTime() + + const garden = await getGarden() + const graph = new TaskGraph(garden, garden.log) + const task = new TestTask(garden, "a") + + await graph.addTask(task) + const result = await graph.processTasks() + + expect(garden.events.log).to.eql([ + { name: "taskPending", payload: { addedAt: now, key: task.getKey(), version: task.version } }, + { name: "taskComplete", payload: result["a"] }, + ]) + }) + + it("should emit a taskError event when failing a task", async () => { + const now = freezeTime() + + const garden = await getGarden() + const graph = new TaskGraph(garden, garden.log) + const task = new TestTask(garden, "a", { throwError: true }) + + await graph.addTask(task) + const result = await graph.processTasks() + + expect(garden.events.log).to.eql([ + { name: "taskPending", payload: { addedAt: now, key: task.getKey(), version: task.version } }, + { name: "taskError", payload: result["a"] }, + ]) + }) + it("should process multiple tasks in dependency order", async () => { const garden = await getGarden() - const graph = new TaskGraph(garden.log) + const graph = new TaskGraph(garden, garden.log) const callbackResults = {} const resultOrder: string[] = [] @@ -128,10 +176,10 @@ describe("task-graph", () => { const opts = { callback } - const taskA = new TestTask(garden, "a", [], opts) - const taskB = new TestTask(garden, "b", [taskA], opts) - const taskC = new TestTask(garden, "c", [taskB], opts) - const taskD = new TestTask(garden, "d", [taskB, taskC], opts) + const taskA = new TestTask(garden, "a", { ...opts }) + const taskB = new TestTask(garden, "b", { ...opts, dependencies: [taskA] }) + const taskC = new TestTask(garden, "c", { ...opts, dependencies: [taskB] }) + const taskD = new TestTask(garden, "d", { ...opts, dependencies: [taskB, taskC] }) // we should be able to add tasks multiple times and in any order await graph.addTask(taskC) @@ -208,7 +256,7 @@ describe("task-graph", () => { it("should recursively cancel a task's dependants when it throws an error", async () => { const garden = await getGarden() - const graph = new TaskGraph(garden.log) + const graph = new TaskGraph(garden, garden.log) const resultOrder: string[] = [] @@ -218,10 +266,10 @@ describe("task-graph", () => { const opts = { callback } - const taskA = new TestTask(garden, "a", [], opts) - const taskB = new TestTask(garden, "b", [taskA], { callback, throwError: true }) - const taskC = new TestTask(garden, "c", [taskB], opts) - const taskD = new TestTask(garden, "d", [taskB, taskC], opts) + const taskA = new TestTask(garden, "a", { ...opts }) + const taskB = new TestTask(garden, "b", { callback, throwError: true, dependencies: [taskA] }) + const taskC = new TestTask(garden, "c", { ...opts, dependencies: [taskB] }) + const taskD = new TestTask(garden, "d", { ...opts, dependencies: [taskB, taskC] }) await graph.addTask(taskA) await graph.addTask(taskB) @@ -249,7 +297,7 @@ describe("task-graph", () => { "should process a task as an inheritor of an existing, in-progress task when they have the same base key", async () => { const garden = await getGarden() - const graph = new TaskGraph(garden.log) + const graph = new TaskGraph(garden, garden.log) let callbackResults = {} let resultOrder: string[] = [] @@ -287,19 +335,20 @@ describe("task-graph", () => { callbackResults[key] = result } - const dependencyA = new TestTask(garden, "dependencyA", [], { callback: defaultCallback }) - const dependencyB = new TestTask(garden, "dependencyB", [], { callback: defaultCallback }) + const dependencyA = new TestTask(garden, "dependencyA", { callback: defaultCallback }) + const dependencyB = new TestTask(garden, "dependencyB", { callback: defaultCallback }) const parentTask = new TestTask( garden, "sharedName", - [dependencyA, dependencyB], - { callback: parentCallback, id: "1" }, + { callback: parentCallback, id: "1", dependencies: [dependencyA, dependencyB] }, ) - const dependantA = new TestTask(garden, "dependantA", [parentTask], { callback: defaultCallback }) - const dependantB = new TestTask(garden, "dependantB", [parentTask], { callback: defaultCallback }) + const dependantA = new TestTask(garden, "dependantA", { callback: defaultCallback, dependencies: [parentTask] }) + const dependantB = new TestTask(garden, "dependantB", { callback: defaultCallback, dependencies: [parentTask] }) - const inheritorTask = new TestTask(garden, - "sharedName", [dependencyA, dependencyB], { callback: defaultCallback, id: "2" }, + const inheritorTask = new TestTask( + garden, + "sharedName", + { callback: defaultCallback, id: "2", dependencies: [dependencyA, dependencyB] }, ) await graph.addTask(dependencyA)