@saws/docker-service 2.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/DockerService.d.ts +160 -0
- package/dist/DockerService.js +656 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/typescript/lib/lib.es2023.d.ts","../../../node_modules/typescript/lib/lib.es2024.d.ts","../../../node_modules/typescript/lib/lib.es2025.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../../node_modules/typescript/lib/lib.es2025.collection.d.ts","../../../node_modules/typescript/lib/lib.es2025.float16.d.ts","../../../node_modules/typescript/lib/lib.es2025.intl.d.ts","../../../node_modules/typescript/lib/lib.es2025.iterator.d.ts","../../../node_modules/typescript/lib/lib.es2025.promise.d.ts","../../../node_modules/typescript/lib/lib.es2025.regexp.d.ts","../../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../../node_modules/typescript/lib/lib.esnext.temporal.d.ts","../../../node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../core/dist/utils/stage-outputs.d.ts","../../core/dist/secrets-manager.d.ts","../../core/dist/servicedefinition.d.ts","../../core/dist/find-service-definition.d.ts","../../core/dist/get-saws-config.d.ts","../../core/dist/host-readiness.d.ts","../../core/dist/host.d.ts","../../core/dist/global-hosts.d.ts","../../core/dist/index.d.ts","../../core/dist/utils/run-local.d.ts","../../core/dist/utils/shell-quote.d.ts","../src/dockerservice.ts","../src/index.ts","../../../node_modules/@types/node/globals.typedarray.d.ts","../../../node_modules/@types/node/buffer.buffer.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../../node_modules/@types/node/web-globals/blob.d.ts","../../../node_modules/@types/node/web-globals/console.d.ts","../../../node_modules/@types/node/web-globals/crypto.d.ts","../../../node_modules/@types/node/web-globals/domexception.d.ts","../../../node_modules/@types/node/web-globals/encoding.d.ts","../../../node_modules/@types/node/web-globals/events.d.ts","../../../node_modules/undici-types/utility.d.ts","../../../node_modules/undici-types/header.d.ts","../../../node_modules/undici-types/readable.d.ts","../../../node_modules/undici-types/fetch.d.ts","../../../node_modules/undici-types/formdata.d.ts","../../../node_modules/undici-types/connector.d.ts","../../../node_modules/undici-types/client-stats.d.ts","../../../node_modules/undici-types/client.d.ts","../../../node_modules/undici-types/errors.d.ts","../../../node_modules/undici-types/dispatcher.d.ts","../../../node_modules/undici-types/global-dispatcher.d.ts","../../../node_modules/undici-types/global-origin.d.ts","../../../node_modules/undici-types/pool-stats.d.ts","../../../node_modules/undici-types/pool.d.ts","../../../node_modules/undici-types/handlers.d.ts","../../../node_modules/undici-types/balanced-pool.d.ts","../../../node_modules/undici-types/round-robin-pool.d.ts","../../../node_modules/undici-types/h2c-client.d.ts","../../../node_modules/undici-types/agent.d.ts","../../../node_modules/undici-types/dispatcher1-wrapper.d.ts","../../../node_modules/undici-types/mock-interceptor.d.ts","../../../node_modules/undici-types/mock-call-history.d.ts","../../../node_modules/undici-types/mock-agent.d.ts","../../../node_modules/undici-types/mock-client.d.ts","../../../node_modules/undici-types/mock-pool.d.ts","../../../node_modules/undici-types/snapshot-agent.d.ts","../../../node_modules/undici-types/mock-errors.d.ts","../../../node_modules/undici-types/proxy-agent.d.ts","../../../node_modules/undici-types/socks5-proxy-agent.d.ts","../../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../../node_modules/undici-types/retry-handler.d.ts","../../../node_modules/undici-types/retry-agent.d.ts","../../../node_modules/undici-types/api.d.ts","../../../node_modules/undici-types/cache-interceptor.d.ts","../../../node_modules/undici-types/interceptors.d.ts","../../../node_modules/undici-types/util.d.ts","../../../node_modules/undici-types/cookies.d.ts","../../../node_modules/undici-types/patch.d.ts","../../../node_modules/undici-types/websocket.d.ts","../../../node_modules/undici-types/eventsource.d.ts","../../../node_modules/undici-types/diagnostics-channel.d.ts","../../../node_modules/undici-types/content-type.d.ts","../../../node_modules/undici-types/cache.d.ts","../../../node_modules/undici-types/index.d.ts","../../../node_modules/@types/node/web-globals/fetch.d.ts","../../../node_modules/@types/node/web-globals/importmeta.d.ts","../../../node_modules/@types/node/web-globals/messaging.d.ts","../../../node_modules/@types/node/web-globals/navigator.d.ts","../../../node_modules/@types/node/web-globals/performance.d.ts","../../../node_modules/@types/node/web-globals/storage.d.ts","../../../node_modules/@types/node/web-globals/streams.d.ts","../../../node_modules/@types/node/web-globals/timers.d.ts","../../../node_modules/@types/node/web-globals/url.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/ffi.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/inspector.generated.d.ts","../../../node_modules/@types/node/inspector/promises.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/path/posix.d.ts","../../../node_modules/@types/node/path/win32.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/quic.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/readline/promises.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/sea.d.ts","../../../node_modules/@types/node/sqlite.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/iter.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/test/reporters.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/util/types.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/zlib/iter.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@types/find-package-json/package-json.d.ts","../../../node_modules/@types/find-package-json/index.d.ts"],"fileIdsList":[[94,159,167,172,175,177,178,179,191,218,219],[94,159,167,172,175,177,178,179,191],[94,156,157,159,167,172,175,177,178,179,191],[94,158,159,167,172,175,177,178,179,191],[159,167,172,175,177,178,179,191],[94,159,167,172,175,177,178,179,191,200],[94,159,160,165,167,170,172,175,177,178,179,181,191,196,209],[94,159,160,161,167,170,172,175,177,178,179,191],[94,159,162,167,172,175,177,178,179,191,210],[94,159,163,164,167,172,175,177,178,179,182,191],[94,159,164,167,172,175,177,178,179,191,196,206],[94,159,165,167,170,172,175,177,178,179,181,191],[94,158,159,166,167,172,175,177,178,179,191],[94,159,167,168,172,175,177,178,179,191],[94,159,167,169,170,172,175,177,178,179,191],[94,158,159,167,170,172,175,177,178,179,191],[94,159,167,170,172,173,175,177,178,179,191,196,209],[94,159,167,170,172,173,175,177,178,179,191,196,198,200],[94,146,159,167,170,172,174,175,177,178,179,181,191,196,209],[94,159,167,170,172,174,175,177,178,179,181,191,196,206,209],[94,159,167,172,174,175,176,177,178,179,191,196,206,209],[93,94,95,96,97,98,99,100,101,102,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217],[94,159,167,170,172,175,177,178,179,191],[94,159,167,172,175,177,179,191],[94,159,167,172,175,177,178,179,180,191,209],[94,159,167,170,172,175,177,178,179,181,191,196],[94,159,167,172,175,177,178,179,182,191],[94,159,167,172,175,177,178,179,183,191],[94,159,167,170,172,175,177,178,179,186,191],[94,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216],[94,159,167,172,175,177,178,179,188,191],[94,159,167,172,175,177,178,179,189,191],[94,159,164,167,172,175,177,178,179,181,191,200],[94,159,167,170,172,175,177,178,179,191,192],[94,159,167,172,175,177,178,179,191,193,210,213],[94,159,167,170,172,175,177,178,179,191,196,198,199,200],[94,159,167,172,175,177,178,179,191,197,200],[94,159,167,170,172,175,177,178,179,191,196,198],[94,159,167,170,172,175,177,178,179,191,196,199,200],[94,159,167,172,175,177,178,179,191,200,210],[94,159,167,172,175,177,178,179,191,201],[94,156,159,167,172,175,177,178,179,191,196,203,209],[94,159,167,172,175,177,178,179,191,196,202],[94,159,167,170,172,175,177,178,179,191,204,205],[94,159,167,172,175,177,178,179,191,204,205],[94,159,164,167,172,175,177,178,179,181,191,196,206],[94,159,167,172,175,177,178,179,191,207],[94,159,167,172,175,177,178,179,181,191,208],[94,159,167,172,174,175,177,178,179,189,191,209],[94,159,167,172,175,177,178,179,191,210,211],[94,159,164,167,172,175,177,178,179,191,211],[94,159,167,172,175,177,178,179,191,196,212],[94,159,167,172,175,177,178,179,180,191,213],[94,159,167,172,175,177,178,179,191,214],[94,159,162,167,172,175,177,178,179,191],[94,159,164,167,172,175,177,178,179,191],[94,159,167,172,175,177,178,179,191,210],[94,146,159,167,172,175,177,178,179,191],[94,159,167,172,175,177,178,179,191,209],[94,159,167,172,175,177,178,179,191,215],[94,159,167,172,175,177,178,179,186,191],[94,159,167,172,175,177,178,179,191,205],[94,146,159,167,170,172,173,175,177,178,179,186,191,196,200,209,212,213,215],[94,159,167,172,175,177,178,179,191,196,216],[94,159,167,172,175,177,178,179,191,198,217],[94,109,112,115,116,159,167,172,175,177,178,179,191,209],[94,112,159,167,172,175,177,178,179,191,196,209],[94,112,116,159,167,172,175,177,178,179,191,209],[94,159,167,172,175,177,178,179,191,196],[94,106,159,167,172,175,177,178,179,191],[94,110,159,167,172,175,177,178,179,191],[94,108,109,112,159,167,172,175,177,178,179,191,209],[94,159,167,172,175,177,178,179,181,191,206],[94,159,167,172,175,177,178,179,191,218],[94,106,159,167,172,175,177,178,179,191,218],[94,108,112,159,167,172,175,177,178,179,181,191,209],[94,103,104,105,107,111,159,167,170,172,175,177,178,179,191,196,209],[94,112,159,167,172,175,177,178,179,191],[94,112,121,130,159,167,172,175,177,178,179,191],[94,104,110,159,167,172,175,177,178,179,191],[94,112,140,141,159,167,172,175,177,178,179,191],[94,104,107,112,159,167,172,175,177,178,179,191,200,209,218],[94,108,112,159,167,172,175,177,178,179,191,209],[94,103,159,167,172,175,177,178,179,191],[94,106,107,108,110,111,112,113,114,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,141,142,143,144,145,159,167,172,175,177,178,179,191],[94,112,133,136,159,167,172,175,177,178,179,191],[94,112,121,123,124,159,167,172,175,177,178,179,191],[94,110,112,123,125,159,167,172,175,177,178,179,191],[94,111,159,167,172,175,177,178,179,191],[94,104,106,112,159,167,172,175,177,178,179,191],[94,112,116,123,125,159,167,172,175,177,178,179,191],[94,116,159,167,172,175,177,178,179,191],[94,110,112,115,159,167,172,175,177,178,179,191,209],[94,104,108,112,121,159,167,172,175,177,178,179,191],[94,112,133,159,167,172,175,177,178,179,191],[94,125,159,167,172,175,177,178,179,191],[94,104,108,112,116,159,167,172,175,177,178,179,191],[94,106,112,140,159,167,172,175,177,178,179,191,200,215,218],[82,94,159,167,172,175,177,178,179,191],[81,86,94,159,167,172,175,177,178,179,191],[81,85,94,159,167,172,175,177,178,179,191],[81,82,83,84,85,86,87,94,159,167,172,175,177,178,179,191],[80,81,94,159,167,172,175,177,178,179,191,196],[88,89,90,94,159,160,164,167,172,173,175,177,178,179,183,191],[91,94,159,167,172,175,177,178,179,191]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"1e9332c23e9a907175e0ffc6a49e236f97b48838cc8aec9ce7e4cec21e544b65","impliedFormat":1},{"version":"3753fbc1113dc511214802a2342280a8b284ab9094f6420e7aa171e868679f91","impliedFormat":1},{"version":"999ca32883495a866aa5737fe1babc764a469e4cde6ee6b136a4b9ae68853e4b","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bd714129fca875f7d4c477a1a392200b0bcd13fb2e80928cd334b63830ea047","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2c9037ae6cd2c52d80ceef0b3c5ffdb488627d71529cf4f63776daf11161c9a","affectsGlobalScope":true,"impliedFormat":1},{"version":"135d5cf4d345f59f1a9caadfafcd858d3d9cc68290db616cc85797224448cccc","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc238c3f81c2984751932b6aab223cd5b830e0ac6cad76389e5e9d2ffc03287d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4a07f9b76d361f572620927e5735b77d6d2101c23cdd94383eb5b706e7b36357","affectsGlobalScope":true,"impliedFormat":1},{"version":"7c4e8dc6ab834cc6baa0227e030606d29e3e8449a9f67cdf5605ea5493c4db29","affectsGlobalScope":true,"impliedFormat":1},{"version":"de7ba0fd02e06cd9a5bd4ab441ed0e122735786e67dde1e849cced1cd8b46b78","affectsGlobalScope":true,"impliedFormat":1},{"version":"6148e4e88d720a06855071c3db02069434142a8332cf9c182cda551adedf3156","affectsGlobalScope":true,"impliedFormat":1},{"version":"d63dba625b108316a40c95a4425f8d4294e0deeccfd6c7e59d819efa19e23409","affectsGlobalScope":true,"impliedFormat":1},{"version":"0568d6befee03dd435bed4fc25c4e46865b24bdcb8c563fdc21f580a2c301904","affectsGlobalScope":true,"impliedFormat":1},{"version":"30d62269b05b584741f19a5369852d5d34895aa2ac4fd948956f886d15f9cc0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ffbe6d7b295306b2ba88030f65b74c107d8d99bdcf596ea99c62a02f606108b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"996fb27b15277369c68a4ba46ed138b4e9e839a02fb4ec756f7997629242fd9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"79b712591b270d4778c89706ca2cfc56ddb8c3f895840e477388f1710dc5eda9","affectsGlobalScope":true,"impliedFormat":1},{"version":"20884846cef428b992b9bd032e70a4ef88e349263f63aeddf04dda837a7dba26","affectsGlobalScope":true,"impliedFormat":1},{"version":"db59a81f070c1880ad645b2c0275022baa6a0c4f0acdc58d29d349c6efcf0903","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ffedae1d1c2d53fdbca1c96d3c7dda544281f7d262f99b6880634f8fd8d9820","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"b507173a7718609bb52011f33e6fc61b0f3de62ec38c935b38dcfbb68223dea9","impliedFormat":99},{"version":"ed03f7587c6405bca4aa5cd0e13c159ba6a38d29438bf368174cbc12b7952990","impliedFormat":99},{"version":"c2c45e23c9ade84835e6589a92f4b1e0d959f4aad517ca2b9f306d505c8c186b","impliedFormat":99},{"version":"d5f72b73663996558032aae95128fd1c003dbc2a3c7c9616a4b9582885554547","impliedFormat":99},{"version":"db04ab7cf477c959b90d137c9150114e93ab293f7a82068813e62596e514c8b9","impliedFormat":99},{"version":"d096b43d56e50a815348abdc7f505bff20b7326edd25d5ceab70be8ae550b216","impliedFormat":99},{"version":"f4b14039ec5893b0c7691e5695493a13207b5a6e7ea07e9d58fe3f112e9e87fd","impliedFormat":99},{"version":"7e7edbefeefa7d89d7d0e3b51e4361ff4cbafd2f0b8691af8f00d4ec7a2d0dbb","impliedFormat":99},{"version":"b5a012b8d2b5aedaa0284247fe44638a60a00afb8a4727aa86567f401ae06b1d","impliedFormat":99},{"version":"d5af5a55477c977d5c969e0b57015847db4ef7444431770cabb0dc067c35b629","impliedFormat":99},{"version":"3fdb492c7902b3162a9fa765e5ce843a831b87bf6a47c1aa4b73056e39a2eb17","impliedFormat":99},{"version":"76a357cb60e5a7b836d50630a11192ce43c873e13aeda7401798a0585e21381a","signature":"1ef4d20b196cea0b21d8f6a8a62108355283f839b9e4796b5ac9a6c09d559e83","impliedFormat":99},{"version":"41c2effc93c4a7c1143353cdd450d9f8b91c091630764c5654727bcb5af3cfce","impliedFormat":99},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"f53a7652392cf26ebbe4e29fd0672aa87c93bd6d0241289c13fab87b9ac35c8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"b00a630557d1622ad312633bdbfbdb9c6b7220d948dca9f899db30679f160074","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"88809d6c1b9c78d04a133646a6feb926def05a8774c308c7c93bc32ee163d271","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"3ac40516c33b87f751f7507346933081a26cdb8a3e11a6b3aa07d23f803c85db","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"14e9acf826baba0ef4b5665704084896e7bcc06f65a9ab13af7e93d27d6b7069","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"829bc57ee8f287b490ab5bbc5a962fca57e432c1e38ec680ecd3ecaf12800613","impliedFormat":1},{"version":"eec76bf6b9346f3f95fa402621b889489e96930e72295b0369022f332e9b4a6a","impliedFormat":1},{"version":"3a3ff14da53d5013f3e6d8c4ad55225e3649c08786c4421ce639c00d8d589b7d","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"38004e6801340cb890afb8cb5a9fc8972297e7f88ab94026e4b0b3c61fb32f8a","impliedFormat":1},{"version":"d243db6b25788f439e7e2f03c05688e92f46764351673bb0e7b2f3631232e186","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"149f9a9e7f04e67afa0e2a49fc0ff421035c01d6b793cfcae7d2e9f6819431e2","impliedFormat":1},{"version":"e8a9dfa4c75ef6d25df8b40eaa9c31e0a69452aaf2ced4a3f4215dbdbaa876f4","impliedFormat":1},{"version":"a70af845a2eb9dd6e2723e319e14ea7fb28b129ec1361c21509b49305448c323","impliedFormat":1},{"version":"b53dc572d4f187904207ae1166652de47aab8eeb00c254d009cb226863076b56","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"f501234c5aeeeb5d7659412335227466aaacf30b952372d60afeb21c02c96348","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"9ac977503f15bf13ca7c82ad9a32a782f42d43e474824e8b3bffe228fd5f2639","impliedFormat":1},{"version":"8b91ff5bb912be3ea213cbcf0075aace1f5d4ff249a0d227ed673868cb7bfabc","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"7cb4f431a4591b0e23002bed805dc871a874dfed6b9a3994dce68a6df73e6739","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"436d7b4543b340b0f3eef4310d524242e41369b9652aa9c70428767c4dcac455","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"e99963ae1e3a48ca7a7958c02f3e88bb963eb7978c28b68ae6b8c9f03309d83c","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"594ae90cacd813fa392ff80d2e0a8eff4e41f4a136329a940e321399dd8895b4","impliedFormat":1},{"version":"d1f333ade8ff35a409b4984e1f11956fe11c61855b0c7e9b59f0313e48b40c4a","impliedFormat":1},{"version":"0224aa4b3464895d69c413a640a19ac2166778a74eb5eb3b36b021c72d42b274","impliedFormat":1},{"version":"2e6fca0024dd8a076a886d9ec031a936ea59ad878a25b944820495d92f8381dd","affectsGlobalScope":true,"impliedFormat":1},{"version":"177459cba484e2f1e08872a3d2fdbca3162d9d43ca5ec9dc0c946835b55f74be","impliedFormat":1},{"version":"2fbf504c4791f9d32cd766cfe6b605bcda63289b925401953a7900db9af85348","impliedFormat":1},{"version":"ed0fb633cae35948d9e144004299a4bdf1ab912667c787b7fbffcd6d8c7b92a2","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"0e312260895afc9de021ce9de3c8a857392f0ccc7f19d3a030e4d6136a4ac91e","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"98fc7231d6c846a8ed19bdef026ddffe5e2182cc818be74899287421ad370a87","impliedFormat":1},{"version":"3a40b5d34e014017539acb92f04cf2bc0aa71e49efb33c66039df93be97842a1","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"8c7e618c2a91ea7f6b5cca272a295864e92c16413be8fc56a943e8c7d5320011","affectsGlobalScope":true,"impliedFormat":1},{"version":"79a85c64bf083c5f4dd7074e804d1000c0c47301b84d48417a224decd681be30","impliedFormat":1},{"version":"9f1e3a76d5f509b5579e567de522b402cb79e201c4f47dcd014451c0580b290d","impliedFormat":1},{"version":"2ddab5b6932f9a6327f71baeb82a1d6b0824e0d23a4c7a5de5d355857113c22f","impliedFormat":1},{"version":"70f3f8b2dcaf7388f26731a59c79437813e3620b594903525a303cb2a7901016","impliedFormat":1},{"version":"62e8f884c3bc6dff6189b6e662ebe8b238a645938f2df7a97b8a82fca56773a4","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"57a0d1b3d59063d4af2d3f8aac27cfe3c20a0f5d1faf0f8598ccf0b779637939","impliedFormat":1},{"version":"5ff4433a2deae4f85ab1377e90a7554ce6b47ae51c69a84ca30a6e22fae85834","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"97234c5303866576f913d0ccae7d58d6322d9e803c7bf1228a3fda46ab8087b2","affectsGlobalScope":true,"impliedFormat":1},{"version":"93ecf87143cac7b9d05cffc1d6bdc075b7e4fdd48ff05f1fad85043f6ae678d3","impliedFormat":1},{"version":"6f200afcb82b3e9a54bed6db23f2edf2508cd266801257312c0f124b47f2bdb9","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"39338f84e8c4d0e3de7b3c4a7024fb3925f42100eed5cbe73be58902799dff4d","impliedFormat":1},{"version":"fbf83eb33bf5e329527add92acc65da59323876c702ddccf5f4bf1e848c92369","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"515318bc6e656f75e9e34db625316bfafbafd800dfb1642574af93735a24ad38","impliedFormat":1},{"version":"441b9bb09013654aa3d050e68b06464d8959b473e85868249d9d18f692acd35b","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"55246f15e33ff1e2e9e679b25fa9790a48db55dc63d567fe25fac8b6a0efe911","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"03b7804d69cecd66850abada858ed6d4a59001f8c228a9fe212306ee860a4ff6","impliedFormat":1},{"version":"5b26c4dd672d88336bb929787c9bfb55119e80ba89ec6dc923da1c063892843e","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"987fd1f85dc36f4b2c927aa3db814ac6b452e970f4f55d24ed4ecbb7df09be00","impliedFormat":1},{"version":"f12624a4a8d042b68914eac1b0a16571fc1c523173fcdf2517c65d191bd5a86c","impliedFormat":1},{"version":"b4769767f13a1692a66186e01c3aa186ff808d5ff72ed36eda8c37738fb2ac92","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"683edfc4f2b411ebe04729cb6dc09ddd26fbac2c57771a6d7c55964af776e176","impliedFormat":1},{"version":"1ec3f3a5f04cc42df33274fbe5c0937d9a9e06f249a7a26288d7d54f0763ffd4","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"e47f532d6e1617833f13a5b0710c0089d402c89c2f2b54f324e5a20e418d287a","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"8e2577e7262051fd3c5bd6ca2b2056d358ff8853565720f92455860824c25188","impliedFormat":1},{"version":"7ec8d7d483f7394f9da611b10d3a0e402f76ff5c991261e6ce43a15f81ca4258","impliedFormat":1},{"version":"bb9dbb4b2ad81e3e71ec5ba4314973718555b9d04ba2a17dfbf875efecb8e2c0","impliedFormat":1},{"version":"82c3cc48c692438e41b94d936b8acd75ca97b18c50fa1af8a0715eb9d07d18cd","impliedFormat":1},{"version":"045a210189ec63c5488410b33f9fca53d77d051599d0d6506d8048551399c5f3","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"632c45ccb4cdc2c3a80c7a36a63dd7763016e59cac5e07bfb3e37e3548957028","impliedFormat":1},{"version":"c9a2daf6cd1eb854cd5b9e424247c5e306692055738c2effd35f7871d942b76e","impliedFormat":1},{"version":"afa1c49f8e559e413d57343339db857d2a8159435cf9cf7d4deb41718fff1b88","impliedFormat":1},{"version":"e50a7130339a951e6da9e968ed5521b0997a5b0479e148914087213839b5474c","impliedFormat":1},{"version":"990db7c9743d636fd3b2820ec8c40b512879da4c82e00488e7a352e2d005fc39","impliedFormat":1},{"version":"f091ed534c92e6408336f94323ecfe1890c0b5e0166154120712ed97ac9754a1","impliedFormat":1}],"root":[91,92],"options":{"composite":true,"esModuleInterop":true,"module":199,"outDir":"./","rootDir":"../src","skipLibCheck":true,"target":12,"tsBuildInfoFile":"./.tsbuildinfo"},"referencedMap":[[220,1],[219,2],[156,3],[157,3],[158,4],[94,5],[159,6],[160,7],[161,8],[162,9],[163,10],[164,11],[165,12],[166,13],[167,14],[168,14],[169,15],[170,16],[171,2],[172,17],[173,18],[95,2],[93,2],[174,19],[175,20],[176,21],[218,22],[177,23],[178,24],[179,23],[180,25],[181,26],[182,27],[183,28],[184,28],[185,28],[186,29],[187,30],[188,31],[189,32],[190,33],[191,34],[192,34],[193,35],[194,2],[195,2],[196,36],[197,37],[198,38],[199,39],[200,40],[201,41],[202,42],[203,43],[204,44],[205,45],[206,46],[207,47],[208,48],[209,49],[210,50],[211,51],[212,52],[213,53],[214,54],[96,23],[97,2],[98,55],[99,56],[100,2],[101,57],[102,2],[147,58],[148,59],[149,60],[150,60],[151,61],[152,2],[153,6],[154,62],[155,59],[215,63],[216,64],[217,65],[78,2],[79,2],[14,2],[13,2],[2,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[3,2],[23,2],[24,2],[4,2],[25,2],[29,2],[26,2],[27,2],[28,2],[30,2],[31,2],[32,2],[5,2],[33,2],[34,2],[35,2],[36,2],[6,2],[40,2],[37,2],[38,2],[39,2],[41,2],[7,2],[42,2],[47,2],[48,2],[43,2],[44,2],[45,2],[46,2],[8,2],[52,2],[49,2],[50,2],[51,2],[53,2],[9,2],[54,2],[55,2],[56,2],[58,2],[57,2],[59,2],[60,2],[10,2],[61,2],[62,2],[63,2],[11,2],[64,2],[65,2],[66,2],[67,2],[68,2],[69,2],[12,2],[70,2],[71,2],[72,2],[73,2],[74,2],[1,2],[75,2],[76,2],[77,2],[121,66],[135,67],[118,68],[136,69],[145,70],[109,71],[110,72],[108,73],[144,74],[139,75],[143,76],[112,77],[122,78],[132,79],[111,80],[142,81],[106,82],[107,75],[113,78],[114,2],[120,83],[117,78],[104,84],[146,85],[137,86],[125,87],[124,78],[126,88],[129,89],[123,90],[127,91],[140,74],[115,92],[116,93],[130,94],[105,69],[134,95],[133,78],[119,93],[128,96],[131,97],[138,2],[103,2],[141,98],[83,99],[84,99],[87,100],[85,2],[86,101],[88,102],[81,2],[82,103],[89,99],[90,2],[80,2],[91,104],[92,105]],"latestChangedDtsFile":"./DockerService.d.ts","version":"6.0.3"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { ServiceDefinition, type Host, type SecretReference, type ServiceDefinitionConfig } from "@saws/core";
|
|
3
|
+
export type DockerHealthCheckConfig = {
|
|
4
|
+
/** Command executed by Docker inside the container using CMD-SHELL. */
|
|
5
|
+
command: string;
|
|
6
|
+
/** Time between checks, expressed as a Docker duration such as "10s". */
|
|
7
|
+
interval?: string;
|
|
8
|
+
/** Maximum time one check may run, expressed as a Docker duration. */
|
|
9
|
+
timeout?: string;
|
|
10
|
+
/** Consecutive failures required before the container is unhealthy. */
|
|
11
|
+
retries?: number;
|
|
12
|
+
/** Startup grace period during which failures do not count. */
|
|
13
|
+
startPeriod?: string;
|
|
14
|
+
};
|
|
15
|
+
export type RestartConfig = "always" | "unless-stopped" | "no";
|
|
16
|
+
type ImageConfig = {
|
|
17
|
+
image: string;
|
|
18
|
+
};
|
|
19
|
+
type DockerFileConfig = {
|
|
20
|
+
dockerfile: string;
|
|
21
|
+
buildContext?: string;
|
|
22
|
+
};
|
|
23
|
+
type DefaultDockerFileConfig = {
|
|
24
|
+
image?: never;
|
|
25
|
+
dockerfile?: never;
|
|
26
|
+
buildContext?: never;
|
|
27
|
+
};
|
|
28
|
+
export type DockerRegistryAuthConfig = {
|
|
29
|
+
username: string;
|
|
30
|
+
password: string | SecretReference;
|
|
31
|
+
};
|
|
32
|
+
export type DockerRunConfig = {
|
|
33
|
+
name: string;
|
|
34
|
+
image: string;
|
|
35
|
+
network: string;
|
|
36
|
+
env?: Record<string, string>;
|
|
37
|
+
envFiles?: string[];
|
|
38
|
+
volumes?: string[];
|
|
39
|
+
ports?: string[];
|
|
40
|
+
entrypoint?: string;
|
|
41
|
+
command?: string[];
|
|
42
|
+
labels?: Record<string, string>;
|
|
43
|
+
restart?: RestartConfig;
|
|
44
|
+
healthCheck?: DockerHealthCheckConfig | false;
|
|
45
|
+
pull?: boolean;
|
|
46
|
+
/** Content digests for runtime files. Values are safe to persist in the config hash. */
|
|
47
|
+
runtimeFileDigests?: Record<string, string>;
|
|
48
|
+
configHash?: string;
|
|
49
|
+
};
|
|
50
|
+
export type RuntimeFile = {
|
|
51
|
+
localPath: string;
|
|
52
|
+
remotePath: string;
|
|
53
|
+
};
|
|
54
|
+
/** A file whose contents affect the behavior of a container. */
|
|
55
|
+
export type ContainerRuntimeFile = {
|
|
56
|
+
/** Stable identifier for the file, preferably its path inside the container. */
|
|
57
|
+
path: string;
|
|
58
|
+
/** Exact bytes that will be made available to the container. */
|
|
59
|
+
contents: string | Uint8Array;
|
|
60
|
+
};
|
|
61
|
+
export type DockerServiceConfig = (ImageConfig | DockerFileConfig | DefaultDockerFileConfig) & {
|
|
62
|
+
host: Host;
|
|
63
|
+
appDirectory?: string;
|
|
64
|
+
network?: string;
|
|
65
|
+
registry?: string;
|
|
66
|
+
auth?: DockerRegistryAuthConfig;
|
|
67
|
+
volumes?: string[];
|
|
68
|
+
ports?: string[];
|
|
69
|
+
entrypoint?: string;
|
|
70
|
+
command?: string[];
|
|
71
|
+
labels?: Record<string, string>;
|
|
72
|
+
restart?: RestartConfig;
|
|
73
|
+
healthCheck?: DockerHealthCheckConfig | false;
|
|
74
|
+
} & ServiceDefinitionConfig;
|
|
75
|
+
export declare class DockerService extends ServiceDefinition {
|
|
76
|
+
readonly host: Host;
|
|
77
|
+
readonly appDirectory: string;
|
|
78
|
+
readonly network: string;
|
|
79
|
+
readonly registry?: string;
|
|
80
|
+
readonly auth?: DockerRegistryAuthConfig;
|
|
81
|
+
readonly image?: string;
|
|
82
|
+
readonly dockerfile?: string;
|
|
83
|
+
readonly buildContext?: string;
|
|
84
|
+
readonly volumes: string[];
|
|
85
|
+
readonly ports: string[];
|
|
86
|
+
readonly entrypoint?: string;
|
|
87
|
+
readonly command: string[];
|
|
88
|
+
readonly labels: Record<string, string>;
|
|
89
|
+
readonly restart?: RestartConfig;
|
|
90
|
+
readonly healthCheck?: DockerHealthCheckConfig | false;
|
|
91
|
+
protected readonly serviceType: string;
|
|
92
|
+
protected devProcess?: ChildProcess;
|
|
93
|
+
private devEnvironmentFile?;
|
|
94
|
+
private readonly localRunAbortController;
|
|
95
|
+
private readonly activeEphemeralContainers;
|
|
96
|
+
private localRegistryAuthenticated;
|
|
97
|
+
private remoteRegistryAuthenticated;
|
|
98
|
+
constructor(config: DockerServiceConfig);
|
|
99
|
+
dev(): Promise<void>;
|
|
100
|
+
deploy(stage: string): Promise<void>;
|
|
101
|
+
logs(stage: string): Promise<void>;
|
|
102
|
+
exit(): void;
|
|
103
|
+
protected getContainerName(stage: string): string;
|
|
104
|
+
protected getContainerEnvironment(stage: string): Promise<Record<string, string>>;
|
|
105
|
+
protected getDockerRunConfig(stage: string, deploy: boolean): Promise<DockerRunConfig>;
|
|
106
|
+
protected onContainerStarted(_stage: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Registers generated or copied files whose contents affect the running container.
|
|
109
|
+
* Only SHA-256 digests of these contents participate in deployment change detection.
|
|
110
|
+
* Subclasses must generate or copy the files before calling `super.deploy(stage)`.
|
|
111
|
+
*/
|
|
112
|
+
protected getContainerRuntimeFiles(_stage: string, _deploy: boolean): Promise<readonly ContainerRuntimeFile[]>;
|
|
113
|
+
protected getImage(stage: string, deploy: boolean): string;
|
|
114
|
+
protected buildDockerfileImage(stage: string, deploy: boolean): Promise<void>;
|
|
115
|
+
protected pushDockerfileImage(stage: string): Promise<void>;
|
|
116
|
+
protected getNetwork(stage: string): string;
|
|
117
|
+
protected getAppDirectory(stage: string): string;
|
|
118
|
+
protected getBuiltImageName(stage: string, deploy: boolean): string;
|
|
119
|
+
private buildImage;
|
|
120
|
+
protected pushImage(stage: string, image: string): Promise<void>;
|
|
121
|
+
private authenticateLocalRegistry;
|
|
122
|
+
private authenticateRemoteRegistry;
|
|
123
|
+
private resolveRegistryPassword;
|
|
124
|
+
private getRegistryServer;
|
|
125
|
+
private prepareLocalNetwork;
|
|
126
|
+
protected prepareRemote(stage: string, network: string, dryRun?: boolean): Promise<void>;
|
|
127
|
+
protected assertRemoteHostReady(dryRun?: boolean): Promise<void>;
|
|
128
|
+
protected runLocalDetachedContainer(stage: string, config: DockerRunConfig): Promise<void>;
|
|
129
|
+
protected runEphemeralContainer(stage: string, config: DockerRunConfig, options?: {
|
|
130
|
+
dryRun?: boolean;
|
|
131
|
+
logServiceName?: string;
|
|
132
|
+
}): Promise<void>;
|
|
133
|
+
private startLocalContainer;
|
|
134
|
+
private getLocalRunOptions;
|
|
135
|
+
protected runRemoteContainer(stage: string, config: DockerRunConfig): Promise<void>;
|
|
136
|
+
protected getDockerRunCommand(config: DockerRunConfig, detached: boolean, options?: {
|
|
137
|
+
remove?: boolean;
|
|
138
|
+
includeRestart?: boolean;
|
|
139
|
+
}): string;
|
|
140
|
+
private getDockerHealthCheckArgs;
|
|
141
|
+
protected getContainerConfigHash(config: DockerRunConfig): string;
|
|
142
|
+
private getContainerRuntimeFileDigests;
|
|
143
|
+
protected writeRemoteEnvironmentFile(stage: string, config: DockerRunConfig, dryRun?: boolean): Promise<RuntimeFile | undefined>;
|
|
144
|
+
protected writeLocalEnvironmentFile(stage: string, config: DockerRunConfig): Promise<string | undefined>;
|
|
145
|
+
private withLocalEnvironmentFile;
|
|
146
|
+
protected writeRemoteRuntimeFile(stage: string, relativePath: string, contents: string, options?: boolean | {
|
|
147
|
+
dryRun?: boolean;
|
|
148
|
+
mode?: number;
|
|
149
|
+
}): Promise<RuntimeFile>;
|
|
150
|
+
protected removeRemoteRuntimeFile(runtimeFile: RuntimeFile, dryRun?: boolean): Promise<void>;
|
|
151
|
+
private writeLocalRuntimeFile;
|
|
152
|
+
private observeDevProcess;
|
|
153
|
+
getStdOut(): null;
|
|
154
|
+
getStdErr(): null;
|
|
155
|
+
private removeDevEnvironmentFile;
|
|
156
|
+
private getDockerLogsCommand;
|
|
157
|
+
private runLocalLogsCommand;
|
|
158
|
+
private removeActiveEphemeralContainers;
|
|
159
|
+
}
|
|
160
|
+
export {};
|
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { ServiceDefinition, } from "@saws/core";
|
|
6
|
+
import { runLocal } from "@saws/core/utils/run-local";
|
|
7
|
+
import { shellQuote } from "@saws/core/utils/shell-quote";
|
|
8
|
+
export class DockerService extends ServiceDefinition {
|
|
9
|
+
host;
|
|
10
|
+
appDirectory;
|
|
11
|
+
network;
|
|
12
|
+
registry;
|
|
13
|
+
auth;
|
|
14
|
+
image;
|
|
15
|
+
dockerfile;
|
|
16
|
+
buildContext;
|
|
17
|
+
volumes;
|
|
18
|
+
ports;
|
|
19
|
+
entrypoint;
|
|
20
|
+
command;
|
|
21
|
+
labels;
|
|
22
|
+
restart;
|
|
23
|
+
healthCheck;
|
|
24
|
+
serviceType = "docker";
|
|
25
|
+
devProcess;
|
|
26
|
+
devEnvironmentFile;
|
|
27
|
+
localRunAbortController = new AbortController();
|
|
28
|
+
activeEphemeralContainers = new Set();
|
|
29
|
+
localRegistryAuthenticated = false;
|
|
30
|
+
remoteRegistryAuthenticated = false;
|
|
31
|
+
constructor(config) {
|
|
32
|
+
super(config);
|
|
33
|
+
const hasImage = "image" in config && config.image != null;
|
|
34
|
+
const hasDockerfile = "dockerfile" in config && config.dockerfile != null;
|
|
35
|
+
if (hasImage && hasDockerfile) {
|
|
36
|
+
throw new Error(`Docker service "${config.name}" cannot configure both image and dockerfile`);
|
|
37
|
+
}
|
|
38
|
+
const buildsDockerfile = !hasImage;
|
|
39
|
+
if (buildsDockerfile &&
|
|
40
|
+
config.registry != null &&
|
|
41
|
+
config.registry.replace(/\/+$/, "").length === 0) {
|
|
42
|
+
throw new Error(`Docker service "${config.name}" registry cannot be empty`);
|
|
43
|
+
}
|
|
44
|
+
if (buildsDockerfile && config.auth != null && config.auth.username.trim().length === 0) {
|
|
45
|
+
throw new Error(`Docker service "${config.name}" registry auth username cannot be empty`);
|
|
46
|
+
}
|
|
47
|
+
this.host = config.host;
|
|
48
|
+
this.appDirectory = config.appDirectory ?? "/opt/saws";
|
|
49
|
+
this.network = config.network ?? "saws";
|
|
50
|
+
this.registry = config.registry?.replace(/\/+$/, "");
|
|
51
|
+
this.auth = config.auth;
|
|
52
|
+
if (hasImage) {
|
|
53
|
+
this.image = config.image;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.dockerfile = hasDockerfile ? config.dockerfile : path.join(config.name, "Dockerfile");
|
|
57
|
+
this.buildContext = hasDockerfile ? config.buildContext : config.name;
|
|
58
|
+
}
|
|
59
|
+
this.volumes = config.volumes ?? [];
|
|
60
|
+
this.ports = config.ports ?? [];
|
|
61
|
+
this.entrypoint = config.entrypoint;
|
|
62
|
+
this.command = config.command ?? [];
|
|
63
|
+
this.labels = config.labels ?? {};
|
|
64
|
+
this.restart = config.restart;
|
|
65
|
+
this.healthCheck = config.healthCheck;
|
|
66
|
+
}
|
|
67
|
+
async dev() {
|
|
68
|
+
await super.dev();
|
|
69
|
+
const stage = "local";
|
|
70
|
+
await this.buildDockerfileImage(stage, false);
|
|
71
|
+
const config = await this.getDockerRunConfig(stage, false);
|
|
72
|
+
try {
|
|
73
|
+
this.devEnvironmentFile = await this.writeLocalEnvironmentFile(stage, config);
|
|
74
|
+
this.devProcess = await this.startLocalContainer(config);
|
|
75
|
+
this.observeDevProcess(this.devProcess);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
await this.removeDevEnvironmentFile();
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
await this.onContainerStarted(stage);
|
|
82
|
+
}
|
|
83
|
+
async deploy(stage) {
|
|
84
|
+
await super.deploy(stage);
|
|
85
|
+
await this.buildDockerfileImage(stage, true);
|
|
86
|
+
await this.pushDockerfileImage(stage);
|
|
87
|
+
const config = await this.getDockerRunConfig(stage, true);
|
|
88
|
+
config.configHash = this.getContainerConfigHash(config);
|
|
89
|
+
if (this.host == null) {
|
|
90
|
+
await this.runLocalDetachedContainer(stage, config);
|
|
91
|
+
await this.onContainerStarted(stage);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
await this.assertRemoteHostReady();
|
|
95
|
+
let environmentFile;
|
|
96
|
+
try {
|
|
97
|
+
environmentFile = await this.writeRemoteEnvironmentFile(stage, config);
|
|
98
|
+
await this.runRemoteContainer(stage, config);
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
if (environmentFile != null) {
|
|
102
|
+
await this.removeRemoteRuntimeFile(environmentFile);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
await this.onContainerStarted(stage);
|
|
106
|
+
}
|
|
107
|
+
async logs(stage) {
|
|
108
|
+
if (stage === "local")
|
|
109
|
+
return;
|
|
110
|
+
const command = this.getDockerLogsCommand(stage);
|
|
111
|
+
if (this.host == null) {
|
|
112
|
+
await this.runLocalLogsCommand(command);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await this.host.exec(command);
|
|
116
|
+
}
|
|
117
|
+
exit() {
|
|
118
|
+
super.exit();
|
|
119
|
+
this.localRunAbortController.abort();
|
|
120
|
+
this.devProcess?.kill();
|
|
121
|
+
this.devProcess = undefined;
|
|
122
|
+
this.removeActiveEphemeralContainers();
|
|
123
|
+
void this.removeDevEnvironmentFile();
|
|
124
|
+
}
|
|
125
|
+
getContainerName(stage) {
|
|
126
|
+
return `${stage}-${this.name}`.replaceAll("_", "-").toLowerCase();
|
|
127
|
+
}
|
|
128
|
+
async getContainerEnvironment(stage) {
|
|
129
|
+
return {
|
|
130
|
+
...(await this.getDependenciesEnvironmentVariables(stage)),
|
|
131
|
+
...(await this.getStageEnvironmentVariables(stage)),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async getDockerRunConfig(stage, deploy) {
|
|
135
|
+
const runtimeFileDigests = this.getContainerRuntimeFileDigests(await this.getContainerRuntimeFiles(stage, deploy));
|
|
136
|
+
return {
|
|
137
|
+
name: this.getContainerName(stage),
|
|
138
|
+
image: this.getImage(stage, deploy),
|
|
139
|
+
pull: this.dockerfile == null || deploy,
|
|
140
|
+
network: this.getNetwork(stage),
|
|
141
|
+
env: await this.getContainerEnvironment(stage),
|
|
142
|
+
volumes: this.volumes,
|
|
143
|
+
ports: this.ports,
|
|
144
|
+
entrypoint: this.entrypoint,
|
|
145
|
+
command: this.command,
|
|
146
|
+
restart: this.restart,
|
|
147
|
+
healthCheck: this.healthCheck,
|
|
148
|
+
runtimeFileDigests,
|
|
149
|
+
labels: {
|
|
150
|
+
...this.labels,
|
|
151
|
+
"saws.service": this.name,
|
|
152
|
+
"saws.serviceType": this.serviceType,
|
|
153
|
+
"saws.stage": stage,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async onContainerStarted(_stage) { }
|
|
158
|
+
/**
|
|
159
|
+
* Registers generated or copied files whose contents affect the running container.
|
|
160
|
+
* Only SHA-256 digests of these contents participate in deployment change detection.
|
|
161
|
+
* Subclasses must generate or copy the files before calling `super.deploy(stage)`.
|
|
162
|
+
*/
|
|
163
|
+
async getContainerRuntimeFiles(_stage, _deploy) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
getImage(stage, deploy) {
|
|
167
|
+
if (this.image != null)
|
|
168
|
+
return this.image;
|
|
169
|
+
return this.getBuiltImageName(stage, deploy);
|
|
170
|
+
}
|
|
171
|
+
async buildDockerfileImage(stage, deploy) {
|
|
172
|
+
if (this.dockerfile == null)
|
|
173
|
+
return;
|
|
174
|
+
await this.buildImage(this.getImage(stage, deploy), deploy && this.host != null ? this.host.platform : undefined);
|
|
175
|
+
}
|
|
176
|
+
async pushDockerfileImage(stage) {
|
|
177
|
+
if (this.dockerfile == null || this.host == null)
|
|
178
|
+
return;
|
|
179
|
+
await this.pushImage(stage, this.getImage(stage, true));
|
|
180
|
+
}
|
|
181
|
+
getNetwork(stage) {
|
|
182
|
+
return `${this.network}-${stage}`;
|
|
183
|
+
}
|
|
184
|
+
getAppDirectory(stage) {
|
|
185
|
+
return path.posix.join(this.appDirectory, stage);
|
|
186
|
+
}
|
|
187
|
+
getBuiltImageName(stage, deploy) {
|
|
188
|
+
const repository = `${stage}-${this.name}`
|
|
189
|
+
.toLowerCase()
|
|
190
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
191
|
+
.replace(/^[._-]+|[._-]+$/g, "");
|
|
192
|
+
if (repository.length === 0) {
|
|
193
|
+
throw new Error(`Cannot derive a Docker image name for service "${this.name}"`);
|
|
194
|
+
}
|
|
195
|
+
if (deploy && this.host != null) {
|
|
196
|
+
if (this.registry == null) {
|
|
197
|
+
throw new Error(`Docker service "${this.name}" uses a Dockerfile and remote deploy, but no registry is configured`);
|
|
198
|
+
}
|
|
199
|
+
return `${this.registry}/${repository}:latest`;
|
|
200
|
+
}
|
|
201
|
+
return `saws-${repository}:latest`;
|
|
202
|
+
}
|
|
203
|
+
async buildImage(image, platform) {
|
|
204
|
+
const dockerfile = path.resolve(this.dockerfile);
|
|
205
|
+
const buildContext = path.resolve(this.buildContext ?? path.dirname(this.dockerfile));
|
|
206
|
+
await runLocal([
|
|
207
|
+
"docker build",
|
|
208
|
+
...(platform == null ? [] : [`--platform ${shellQuote(platform)}`]),
|
|
209
|
+
`-f ${shellQuote(dockerfile)}`,
|
|
210
|
+
`-t ${shellQuote(image)}`,
|
|
211
|
+
shellQuote(buildContext),
|
|
212
|
+
].join(" "), this.getLocalRunOptions());
|
|
213
|
+
}
|
|
214
|
+
async pushImage(stage, image) {
|
|
215
|
+
await this.authenticateLocalRegistry(stage);
|
|
216
|
+
await runLocal(`docker push ${shellQuote(image)}`, this.getLocalRunOptions());
|
|
217
|
+
}
|
|
218
|
+
async authenticateLocalRegistry(stage) {
|
|
219
|
+
if (this.registry == null || this.auth == null || this.localRegistryAuthenticated)
|
|
220
|
+
return;
|
|
221
|
+
await runLocal([
|
|
222
|
+
"docker login",
|
|
223
|
+
shellQuote(this.getRegistryServer()),
|
|
224
|
+
`--username ${shellQuote(this.auth.username)}`,
|
|
225
|
+
"--password-stdin",
|
|
226
|
+
].join(" "), this.getLocalRunOptions({ input: `${await this.resolveRegistryPassword(stage)}\n` }));
|
|
227
|
+
this.localRegistryAuthenticated = true;
|
|
228
|
+
}
|
|
229
|
+
async authenticateRemoteRegistry(stage) {
|
|
230
|
+
if (this.registry == null || this.auth == null || this.remoteRegistryAuthenticated)
|
|
231
|
+
return;
|
|
232
|
+
await this.host.exec([
|
|
233
|
+
"docker login",
|
|
234
|
+
shellQuote(this.getRegistryServer()),
|
|
235
|
+
`--username ${shellQuote(this.auth.username)}`,
|
|
236
|
+
"--password-stdin",
|
|
237
|
+
].join(" "), { input: `${await this.resolveRegistryPassword(stage)}\n` });
|
|
238
|
+
this.remoteRegistryAuthenticated = true;
|
|
239
|
+
}
|
|
240
|
+
async resolveRegistryPassword(stage) {
|
|
241
|
+
return typeof this.auth.password === "string"
|
|
242
|
+
? this.auth.password
|
|
243
|
+
: this.auth.password.resolve({ stage });
|
|
244
|
+
}
|
|
245
|
+
getRegistryServer() {
|
|
246
|
+
return this.registry.split("/", 1)[0];
|
|
247
|
+
}
|
|
248
|
+
async prepareLocalNetwork(network, options = {}) {
|
|
249
|
+
await runLocal(`docker network inspect ${shellQuote(network)} >/dev/null 2>&1 || docker network create ${shellQuote(network)}`, this.getLocalRunOptions(options));
|
|
250
|
+
}
|
|
251
|
+
async prepareRemote(stage, network, dryRun) {
|
|
252
|
+
if (!dryRun) {
|
|
253
|
+
await this.authenticateRemoteRegistry(stage);
|
|
254
|
+
}
|
|
255
|
+
await this.host.exec(`mkdir -p ${shellQuote(this.getAppDirectory(stage))}`, { dryRun });
|
|
256
|
+
await this.host.exec(`docker network inspect ${shellQuote(network)} >/dev/null 2>&1 || docker network create ${shellQuote(network)}`, { dryRun });
|
|
257
|
+
}
|
|
258
|
+
async assertRemoteHostReady(dryRun) {
|
|
259
|
+
await this.host.assertReady({ dryRun });
|
|
260
|
+
}
|
|
261
|
+
async runLocalDetachedContainer(stage, config) {
|
|
262
|
+
await this.prepareLocalNetwork(config.network);
|
|
263
|
+
if (config.pull !== false) {
|
|
264
|
+
await runLocal(`docker pull ${shellQuote(config.image)}`, this.getLocalRunOptions());
|
|
265
|
+
}
|
|
266
|
+
await this.withLocalEnvironmentFile(stage, config, async () => {
|
|
267
|
+
await runLocal(`docker rm -f ${shellQuote(config.name)} >/dev/null 2>&1 || true`, this.getLocalRunOptions());
|
|
268
|
+
await runLocal(this.getDockerRunCommand({
|
|
269
|
+
...config,
|
|
270
|
+
labels: {
|
|
271
|
+
...config.labels,
|
|
272
|
+
"saws.configHash": config.configHash ?? this.getContainerConfigHash(config),
|
|
273
|
+
},
|
|
274
|
+
}, true), this.getLocalRunOptions());
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async runEphemeralContainer(stage, config, options = {}) {
|
|
278
|
+
if (stage === "local" || this.host == null) {
|
|
279
|
+
await this.prepareLocalNetwork(config.network, {
|
|
280
|
+
dryRun: options.dryRun,
|
|
281
|
+
serviceName: options.logServiceName,
|
|
282
|
+
});
|
|
283
|
+
if (config.pull !== false) {
|
|
284
|
+
await runLocal(`docker pull ${shellQuote(config.image)}`, this.getLocalRunOptions({
|
|
285
|
+
dryRun: options.dryRun,
|
|
286
|
+
serviceName: options.logServiceName,
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
289
|
+
await this.withLocalEnvironmentFile(stage, config, async () => {
|
|
290
|
+
await runLocal(`docker rm -f ${shellQuote(config.name)} >/dev/null 2>&1 || true`, this.getLocalRunOptions({
|
|
291
|
+
dryRun: options.dryRun,
|
|
292
|
+
serviceName: options.logServiceName,
|
|
293
|
+
}));
|
|
294
|
+
if (!options.dryRun)
|
|
295
|
+
this.activeEphemeralContainers.add(config.name);
|
|
296
|
+
try {
|
|
297
|
+
await runLocal(this.getDockerRunCommand(config, false, { remove: true, includeRestart: false }), this.getLocalRunOptions({
|
|
298
|
+
dryRun: options.dryRun,
|
|
299
|
+
serviceName: options.logServiceName,
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
this.activeEphemeralContainers.delete(config.name);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
await this.assertRemoteHostReady(options.dryRun);
|
|
309
|
+
await this.prepareRemote(stage, config.network, options.dryRun);
|
|
310
|
+
if (config.pull !== false) {
|
|
311
|
+
await this.host.exec(`docker pull ${shellQuote(config.image)}`, { dryRun: options.dryRun });
|
|
312
|
+
}
|
|
313
|
+
let environmentFile;
|
|
314
|
+
try {
|
|
315
|
+
environmentFile = await this.writeRemoteEnvironmentFile(stage, config, options.dryRun);
|
|
316
|
+
await this.host.exec(`docker rm -f ${shellQuote(config.name)} >/dev/null 2>&1 || true`, {
|
|
317
|
+
dryRun: options.dryRun,
|
|
318
|
+
});
|
|
319
|
+
await this.host.exec(this.getDockerRunCommand(config, false, { remove: true, includeRestart: false }), { dryRun: options.dryRun });
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
if (environmentFile != null) {
|
|
323
|
+
await this.removeRemoteRuntimeFile(environmentFile, options.dryRun);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async startLocalContainer(config) {
|
|
328
|
+
await this.prepareLocalNetwork(config.network);
|
|
329
|
+
if (config.pull !== false) {
|
|
330
|
+
await runLocal(`docker pull ${shellQuote(config.image)}`, this.getLocalRunOptions());
|
|
331
|
+
}
|
|
332
|
+
await runLocal(`docker rm -f ${shellQuote(config.name)} >/dev/null 2>&1 || true`, this.getLocalRunOptions());
|
|
333
|
+
const args = [
|
|
334
|
+
"run",
|
|
335
|
+
"--name",
|
|
336
|
+
config.name,
|
|
337
|
+
"--network",
|
|
338
|
+
config.network,
|
|
339
|
+
...(config.env == null
|
|
340
|
+
? []
|
|
341
|
+
: Object.entries(config.env).flatMap(([key, value]) => ["-e", `${key}=${value}`])),
|
|
342
|
+
...(config.envFiles ?? []).flatMap((envFile) => ["--env-file", envFile]),
|
|
343
|
+
...(config.volumes ?? []).flatMap((volume) => ["-v", volume]),
|
|
344
|
+
...(config.ports ?? []).flatMap((port) => ["-p", port]),
|
|
345
|
+
...(config.entrypoint == null ? [] : ["--entrypoint", config.entrypoint]),
|
|
346
|
+
...Object.entries(config.labels ?? {}).flatMap(([key, value]) => [
|
|
347
|
+
"--label",
|
|
348
|
+
`${key}=${value}`,
|
|
349
|
+
]),
|
|
350
|
+
...this.getDockerHealthCheckArgs(config.healthCheck).flatMap((argument) => argument.flagOnly ? [argument.flag] : [argument.flag, argument.value]),
|
|
351
|
+
config.image,
|
|
352
|
+
...(config.command ?? []),
|
|
353
|
+
];
|
|
354
|
+
return spawn("docker", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
355
|
+
}
|
|
356
|
+
getLocalRunOptions(options = {}) {
|
|
357
|
+
const { serviceName, ...runOptions } = options;
|
|
358
|
+
return {
|
|
359
|
+
...runOptions,
|
|
360
|
+
logSink: this.getRuntimeLogSink(),
|
|
361
|
+
serviceName: serviceName ?? this.name,
|
|
362
|
+
signal: this.localRunAbortController.signal,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
async runRemoteContainer(stage, config) {
|
|
366
|
+
await this.prepareRemote(stage, config.network);
|
|
367
|
+
if (config.pull !== false) {
|
|
368
|
+
await this.host.exec(`docker pull ${shellQuote(config.image)}`);
|
|
369
|
+
}
|
|
370
|
+
const configHash = config.configHash ?? this.getContainerConfigHash(config);
|
|
371
|
+
const containerName = shellQuote(config.name);
|
|
372
|
+
const image = shellQuote(config.image);
|
|
373
|
+
const currentHash = `$(docker inspect --format ${shellQuote('{{index .Config.Labels "saws.configHash"}}')} ${containerName} 2>/dev/null || true)`;
|
|
374
|
+
const currentImage = `$(docker inspect --format ${shellQuote("{{.Image}}")} ${containerName} 2>/dev/null || true)`;
|
|
375
|
+
const desiredImage = `$(docker image inspect --format ${shellQuote("{{.Id}}")} ${image})`;
|
|
376
|
+
const isRunning = `$(docker inspect --format ${shellQuote("{{.State.Running}}")} ${containerName} 2>/dev/null || true)`;
|
|
377
|
+
await this.host.exec([
|
|
378
|
+
`if [ "${currentHash}" = ${shellQuote(configHash)} ] && [ "${currentImage}" = "${desiredImage}" ]; then`,
|
|
379
|
+
`if [ "${isRunning}" = "true" ]; then`,
|
|
380
|
+
`echo ${shellQuote(`Container ${config.name} is unchanged`)}`,
|
|
381
|
+
"else",
|
|
382
|
+
`docker start ${containerName}`,
|
|
383
|
+
"fi",
|
|
384
|
+
"else",
|
|
385
|
+
`docker rm -f ${containerName} >/dev/null 2>&1 || true`,
|
|
386
|
+
this.getDockerRunCommand({
|
|
387
|
+
...config,
|
|
388
|
+
labels: {
|
|
389
|
+
...config.labels,
|
|
390
|
+
"saws.configHash": configHash,
|
|
391
|
+
},
|
|
392
|
+
}, true),
|
|
393
|
+
"fi",
|
|
394
|
+
].join("\n"));
|
|
395
|
+
}
|
|
396
|
+
getDockerRunCommand(config, detached, options = {}) {
|
|
397
|
+
const envArgs = Object.entries(config.env ?? {})
|
|
398
|
+
.map(([key, value]) => `-e ${shellQuote(`${key}=${value}`)}`)
|
|
399
|
+
.join(" ");
|
|
400
|
+
const envFileArgs = (config.envFiles ?? [])
|
|
401
|
+
.map((envFile) => `--env-file ${shellQuote(envFile)}`)
|
|
402
|
+
.join(" ");
|
|
403
|
+
const volumeArgs = (config.volumes ?? []).map((volume) => `-v ${shellQuote(volume)}`).join(" ");
|
|
404
|
+
const portArgs = (config.ports ?? []).map((port) => `-p ${shellQuote(port)}`).join(" ");
|
|
405
|
+
const entrypointArg = config.entrypoint == null ? "" : `--entrypoint ${shellQuote(config.entrypoint)}`;
|
|
406
|
+
const labelArgs = Object.entries(config.labels ?? {})
|
|
407
|
+
.map(([key, value]) => `--label ${shellQuote(`${key}=${value}`)}`)
|
|
408
|
+
.join(" ");
|
|
409
|
+
const healthCheckArgs = this.getDockerHealthCheckArgs(config.healthCheck)
|
|
410
|
+
.map((argument) => argument.flagOnly ? argument.flag : `${argument.flag} ${shellQuote(argument.value)}`)
|
|
411
|
+
.join(" ");
|
|
412
|
+
const command = (config.command ?? []).map((part) => shellQuote(part)).join(" ");
|
|
413
|
+
return [
|
|
414
|
+
"docker run",
|
|
415
|
+
options.remove ? "--rm" : "",
|
|
416
|
+
detached ? "-d" : "",
|
|
417
|
+
`--name ${shellQuote(config.name)}`,
|
|
418
|
+
`--network ${shellQuote(config.network)}`,
|
|
419
|
+
options.includeRestart === false
|
|
420
|
+
? ""
|
|
421
|
+
: `--restart ${shellQuote(config.restart ?? "unless-stopped")}`,
|
|
422
|
+
envArgs,
|
|
423
|
+
envFileArgs,
|
|
424
|
+
volumeArgs,
|
|
425
|
+
portArgs,
|
|
426
|
+
entrypointArg,
|
|
427
|
+
labelArgs,
|
|
428
|
+
healthCheckArgs,
|
|
429
|
+
shellQuote(config.image),
|
|
430
|
+
command,
|
|
431
|
+
]
|
|
432
|
+
.filter(Boolean)
|
|
433
|
+
.join(" ");
|
|
434
|
+
}
|
|
435
|
+
getDockerHealthCheckArgs(healthCheck) {
|
|
436
|
+
if (healthCheck == null)
|
|
437
|
+
return [];
|
|
438
|
+
if (healthCheck === false)
|
|
439
|
+
return [{ flag: "--no-healthcheck", flagOnly: true }];
|
|
440
|
+
if (healthCheck.command.length === 0) {
|
|
441
|
+
throw new Error("Docker health check command cannot be empty");
|
|
442
|
+
}
|
|
443
|
+
if (healthCheck.retries != null &&
|
|
444
|
+
(!Number.isInteger(healthCheck.retries) || healthCheck.retries < 1)) {
|
|
445
|
+
throw new Error("Docker health check retries must be a positive integer");
|
|
446
|
+
}
|
|
447
|
+
const args = [{ flag: "--health-cmd", flagOnly: false, value: healthCheck.command }];
|
|
448
|
+
for (const [flag, value] of [
|
|
449
|
+
["--health-interval", healthCheck.interval],
|
|
450
|
+
["--health-timeout", healthCheck.timeout],
|
|
451
|
+
["--health-start-period", healthCheck.startPeriod],
|
|
452
|
+
]) {
|
|
453
|
+
if (value == null)
|
|
454
|
+
continue;
|
|
455
|
+
if (!isDockerDuration(value)) {
|
|
456
|
+
throw new Error(`${flag.slice(2)} must be a positive Docker duration`);
|
|
457
|
+
}
|
|
458
|
+
args.push({ flag, flagOnly: false, value });
|
|
459
|
+
}
|
|
460
|
+
if (healthCheck.retries != null) {
|
|
461
|
+
args.push({ flag: "--health-retries", flagOnly: false, value: String(healthCheck.retries) });
|
|
462
|
+
}
|
|
463
|
+
return args;
|
|
464
|
+
}
|
|
465
|
+
getContainerConfigHash(config) {
|
|
466
|
+
const labels = { ...config.labels };
|
|
467
|
+
delete labels["saws.configHash"];
|
|
468
|
+
return createHash("sha256")
|
|
469
|
+
.update(JSON.stringify({
|
|
470
|
+
name: config.name,
|
|
471
|
+
image: config.image,
|
|
472
|
+
network: config.network,
|
|
473
|
+
environment: sortRecord(config.env),
|
|
474
|
+
envFiles: [...(config.envFiles ?? [])].sort(),
|
|
475
|
+
volumes: [...(config.volumes ?? [])].sort(),
|
|
476
|
+
ports: [...(config.ports ?? [])].sort(),
|
|
477
|
+
entrypoint: config.entrypoint,
|
|
478
|
+
command: config.command ?? [],
|
|
479
|
+
labels: sortRecord(labels),
|
|
480
|
+
runtimeFileDigests: config.runtimeFileDigests == null ? undefined : sortRecord(config.runtimeFileDigests),
|
|
481
|
+
restart: config.restart ?? "unless-stopped",
|
|
482
|
+
healthCheck: config.healthCheck === false
|
|
483
|
+
? false
|
|
484
|
+
: config.healthCheck == null
|
|
485
|
+
? null
|
|
486
|
+
: {
|
|
487
|
+
command: config.healthCheck.command,
|
|
488
|
+
interval: config.healthCheck.interval,
|
|
489
|
+
timeout: config.healthCheck.timeout,
|
|
490
|
+
retries: config.healthCheck.retries,
|
|
491
|
+
startPeriod: config.healthCheck.startPeriod,
|
|
492
|
+
},
|
|
493
|
+
}))
|
|
494
|
+
.digest("hex");
|
|
495
|
+
}
|
|
496
|
+
getContainerRuntimeFileDigests(files) {
|
|
497
|
+
if (files.length === 0)
|
|
498
|
+
return undefined;
|
|
499
|
+
const digests = {};
|
|
500
|
+
for (const file of files) {
|
|
501
|
+
if (file.path.length === 0) {
|
|
502
|
+
throw new Error("Container runtime file path cannot be empty");
|
|
503
|
+
}
|
|
504
|
+
if (digests[file.path] != null) {
|
|
505
|
+
throw new Error(`Container runtime file path "${file.path}" is registered more than once`);
|
|
506
|
+
}
|
|
507
|
+
digests[file.path] = createHash("sha256").update(file.contents).digest("hex");
|
|
508
|
+
}
|
|
509
|
+
return sortRecord(digests);
|
|
510
|
+
}
|
|
511
|
+
async writeRemoteEnvironmentFile(stage, config, dryRun) {
|
|
512
|
+
const contents = serializeEnvironment(config.env);
|
|
513
|
+
if (contents == null)
|
|
514
|
+
return undefined;
|
|
515
|
+
const runtimeFile = await this.writeRemoteRuntimeFile(stage, `${this.name}/container.env`, contents, dryRun);
|
|
516
|
+
config.env = undefined;
|
|
517
|
+
config.envFiles = [...(config.envFiles ?? []), runtimeFile.remotePath];
|
|
518
|
+
return runtimeFile;
|
|
519
|
+
}
|
|
520
|
+
async writeLocalEnvironmentFile(stage, config) {
|
|
521
|
+
const contents = serializeEnvironment(config.env);
|
|
522
|
+
if (contents == null)
|
|
523
|
+
return undefined;
|
|
524
|
+
const localPath = await this.writeLocalRuntimeFile(stage, `${this.name}/container.env`, contents);
|
|
525
|
+
config.env = undefined;
|
|
526
|
+
config.envFiles = [...(config.envFiles ?? []), localPath];
|
|
527
|
+
return localPath;
|
|
528
|
+
}
|
|
529
|
+
async withLocalEnvironmentFile(stage, config, callback) {
|
|
530
|
+
const environmentFile = await this.writeLocalEnvironmentFile(stage, config);
|
|
531
|
+
try {
|
|
532
|
+
return await callback();
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
if (environmentFile != null) {
|
|
536
|
+
await rm(environmentFile, { force: true });
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async writeRemoteRuntimeFile(stage, relativePath, contents, options = {}) {
|
|
541
|
+
const dryRun = typeof options === "boolean" ? options : options.dryRun;
|
|
542
|
+
const mode = typeof options === "boolean" ? 0o600 : (options.mode ?? 0o600);
|
|
543
|
+
const localDir = path.resolve(".saws", "hosts", this.host.name, stage);
|
|
544
|
+
await mkdir(localDir, { recursive: true });
|
|
545
|
+
const localPath = path.join(localDir, relativePath);
|
|
546
|
+
await mkdir(path.dirname(localPath), { recursive: true });
|
|
547
|
+
await writeFile(localPath, contents, { mode });
|
|
548
|
+
const remotePath = path.posix.join(this.getAppDirectory(stage), relativePath);
|
|
549
|
+
await this.host.exec(`mkdir -p ${shellQuote(path.posix.dirname(remotePath))}`, { dryRun });
|
|
550
|
+
await this.host.copyFile(localPath, remotePath, { dryRun });
|
|
551
|
+
await this.host.exec(`chmod ${mode.toString(8)} ${shellQuote(remotePath)}`, { dryRun });
|
|
552
|
+
return { localPath, remotePath };
|
|
553
|
+
}
|
|
554
|
+
async removeRemoteRuntimeFile(runtimeFile, dryRun) {
|
|
555
|
+
await rm(runtimeFile.localPath, { force: true });
|
|
556
|
+
await this.host.exec(`rm -f ${shellQuote(runtimeFile.remotePath)}`, { dryRun });
|
|
557
|
+
}
|
|
558
|
+
async writeLocalRuntimeFile(stage, relativePath, contents) {
|
|
559
|
+
const localPath = path.resolve(".saws", "local", stage, relativePath);
|
|
560
|
+
await mkdir(path.dirname(localPath), { recursive: true });
|
|
561
|
+
await writeFile(localPath, contents, { mode: 0o600 });
|
|
562
|
+
return localPath;
|
|
563
|
+
}
|
|
564
|
+
observeDevProcess(process) {
|
|
565
|
+
process.stdout?.on("data", (chunk) => {
|
|
566
|
+
this.writeRuntimeLog(chunk.toString("utf8"), "stdout");
|
|
567
|
+
});
|
|
568
|
+
process.stderr?.on("data", (chunk) => {
|
|
569
|
+
this.writeRuntimeLog(chunk.toString("utf8"), "stderr");
|
|
570
|
+
});
|
|
571
|
+
process.once("error", (error) => {
|
|
572
|
+
this.writeRuntimeLog(`${error.stack ?? error.message}\n`, "stderr");
|
|
573
|
+
});
|
|
574
|
+
process.once("exit", (code, signal) => {
|
|
575
|
+
if (this.devProcess === process)
|
|
576
|
+
this.devProcess = undefined;
|
|
577
|
+
if (code !== 0 && signal !== "SIGTERM" && signal !== "SIGINT") {
|
|
578
|
+
this.writeRuntimeLog(`Docker container exited with code ${code ?? "unknown"}${signal == null ? "" : ` (${signal})`}\n`, "stderr");
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
getStdOut() {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
getStdErr() {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
async removeDevEnvironmentFile() {
|
|
589
|
+
if (this.devEnvironmentFile == null)
|
|
590
|
+
return;
|
|
591
|
+
const localPath = this.devEnvironmentFile;
|
|
592
|
+
this.devEnvironmentFile = undefined;
|
|
593
|
+
await rm(localPath, { force: true });
|
|
594
|
+
}
|
|
595
|
+
getDockerLogsCommand(stage) {
|
|
596
|
+
return [
|
|
597
|
+
`CONTAINERS=$(docker ps --filter label=saws.service=${shellQuote(this.name)} --filter label=saws.stage=${shellQuote(stage)} --format '{{.Names}}')`,
|
|
598
|
+
'if [ -z "$CONTAINERS" ]; then',
|
|
599
|
+
`echo ${shellQuote(`No running containers found for ${this.name} (${stage})`)} >&2`,
|
|
600
|
+
"exit 1",
|
|
601
|
+
"fi",
|
|
602
|
+
"PIDS=",
|
|
603
|
+
'cleanup() { for pid in $PIDS; do kill "$pid" >/dev/null 2>&1 || true; done; }',
|
|
604
|
+
"trap cleanup INT TERM EXIT",
|
|
605
|
+
"for container in $CONTAINERS; do",
|
|
606
|
+
'docker logs --tail 100 -f "$container" &',
|
|
607
|
+
'PIDS="$PIDS $!"',
|
|
608
|
+
"done",
|
|
609
|
+
"wait",
|
|
610
|
+
].join("\n");
|
|
611
|
+
}
|
|
612
|
+
async runLocalLogsCommand(command) {
|
|
613
|
+
await new Promise((resolve, reject) => {
|
|
614
|
+
const child = spawn(command, {
|
|
615
|
+
shell: true,
|
|
616
|
+
stdio: "inherit",
|
|
617
|
+
});
|
|
618
|
+
child.on("error", reject);
|
|
619
|
+
child.on("exit", (code) => {
|
|
620
|
+
if (code === 0) {
|
|
621
|
+
resolve();
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
reject(new Error(`docker logs exited with code ${code}`));
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
removeActiveEphemeralContainers() {
|
|
629
|
+
for (const container of this.activeEphemeralContainers) {
|
|
630
|
+
spawnSync("docker", ["rm", "-f", container], { stdio: "ignore" });
|
|
631
|
+
}
|
|
632
|
+
this.activeEphemeralContainers.clear();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function serializeEnvironment(environment) {
|
|
636
|
+
const entries = Object.entries(environment ?? {});
|
|
637
|
+
if (entries.length === 0)
|
|
638
|
+
return undefined;
|
|
639
|
+
for (const [key, value] of entries) {
|
|
640
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
641
|
+
throw new Error(`Invalid Docker environment variable name: ${key}`);
|
|
642
|
+
}
|
|
643
|
+
if (value.includes("\n") || value.includes("\r")) {
|
|
644
|
+
throw new Error(`Docker environment variable ${key} contains a newline`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return `${entries.map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
|
|
648
|
+
}
|
|
649
|
+
function sortRecord(record) {
|
|
650
|
+
return Object.fromEntries(Object.entries(record ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
651
|
+
}
|
|
652
|
+
function isDockerDuration(value) {
|
|
653
|
+
if (!/^(?:\d+(?:\.\d+)?(?:ns|us|µs|ms|s|m|h))+$/.test(value))
|
|
654
|
+
return false;
|
|
655
|
+
return [...value.matchAll(/(\d+(?:\.\d+)?)(?:ns|us|µs|ms|s|m|h)/g)].some((match) => Number(match[1]) > 0);
|
|
656
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./DockerService.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./DockerService.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saws/docker-service",
|
|
3
|
+
"version": "2.0.0-beta.11",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/shichongrui/saws.git"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"./dist"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "tsc -b && node --test tests/*.test.mjs"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@saws/core": "2.0.0-beta.11"
|
|
27
|
+
}
|
|
28
|
+
}
|