From d8e6a2ea34df6114e580b8b7f14413489551f59c Mon Sep 17 00:00:00 2001 From: Ivan Duplenskikh <115665590+ivanduplenskikh@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:46:11 +0200 Subject: [PATCH] Add DockerComposeV1 task and refactoring DockerComposeV0 (#20085) * Update task.loc messages * Add DockerComposeV1 * Add DockerComposeV1 to the CODEOWNERS * Set the docker compose v2 util as default for the DockerComposeV1 task * Add condition for COMPATIBILITY flag * Remove useDockerComposeV2 FF * Add DockerComposemV1 task to make-options * Remove buildConfig from DockerComposeV1 and add Node20 handler as default * Update DockerComposeV1 task version * Bump DockerComposeV0 task version * Changed if empty condition for docker which result * Build DockerComposeV0 * Update resjson manually * Update DockerComposeV1 * Update unit tests * Check in unit tests if docker cli exists * Update unit tests and deprecationMessage * Add unit tests * Update unit tests DockerComposeV1 * Remove compatibility answers * Update DockerComposeV1 resources.resjson * Update L0Linux * Show stdout & stderr in tests * Add console.log with composeCommand * Generated task * Enable system debug * Update * Change Node20 to Node20_1 --------- Co-authored-by: Dmitrii Bobreshev (Akvelon INC) --- .github/CODEOWNERS | 2 + .../resources.resjson/en-US/resources.resjson | 2 +- Tasks/DockerComposeV0/Tests/L0.ts | 650 ++++---- Tasks/DockerComposeV0/Tests/L0Linux.ts | 44 +- Tasks/DockerComposeV0/Tests/L0Windows.ts | 20 +- .../dockercomposeconnection.ts | 13 +- Tasks/DockerComposeV0/task.json | 6 +- Tasks/DockerComposeV0/task.loc.json | 4 +- .../resources.resjson/de-DE/resources.resjson | 84 + .../resources.resjson/en-US/resources.resjson | 87 ++ .../resources.resjson/es-ES/resources.resjson | 84 + .../resources.resjson/fr-FR/resources.resjson | 84 + .../resources.resjson/it-IT/resources.resjson | 84 + .../resources.resjson/ja-JP/resources.resjson | 84 + .../resources.resjson/ko-KR/resources.resjson | 84 + .../resources.resjson/ru-RU/resources.resjson | 84 + .../resources.resjson/zh-CN/resources.resjson | 84 + .../resources.resjson/zh-TW/resources.resjson | 84 + Tasks/DockerComposeV1/Tests/L0.ts | 323 ++++ Tasks/DockerComposeV1/Tests/L0Linux.ts | 131 ++ Tasks/DockerComposeV1/Tests/L0Windows.ts | 131 ++ Tasks/DockerComposeV1/ThirdPartyNotices.txt | 1068 +++++++++++++ Tasks/DockerComposeV1/dockercompose.ts | 80 + Tasks/DockerComposeV1/dockercomposebuild.ts | 86 + Tasks/DockerComposeV1/dockercomposecommand.ts | 18 + Tasks/DockerComposeV1/dockercomposeconfig.ts | 35 + .../dockercomposeconnection.ts | 218 +++ Tasks/DockerComposeV1/dockercomposedigests.ts | 71 + Tasks/DockerComposeV1/dockercomposelock.ts | 22 + Tasks/DockerComposeV1/dockercomposepush.ts | 80 + Tasks/DockerComposeV1/dockercomposerun.ts | 67 + Tasks/DockerComposeV1/dockercomposeup.ts | 33 + Tasks/DockerComposeV1/dockercomposeutils.ts | 22 + Tasks/DockerComposeV1/icon.png | Bin 0 -> 789 bytes Tasks/DockerComposeV1/icon.svg | 4 + Tasks/DockerComposeV1/make.json | 12 + Tasks/DockerComposeV1/package-lock.json | 1392 +++++++++++++++++ Tasks/DockerComposeV1/package.json | 16 + Tasks/DockerComposeV1/task.json | 359 +++++ Tasks/DockerComposeV1/task.loc.json | 359 +++++ Tasks/DockerComposeV1/tsconfig.json | 9 + Tasks/DockerComposeV1/utils.ts | 36 + _generated/DockerComposeV0.versionmap.txt | 4 +- .../resources.resjson/en-US/resources.resjson | 2 +- _generated/DockerComposeV0/Tests/L0.ts | 650 ++++---- _generated/DockerComposeV0/Tests/L0Linux.ts | 44 +- _generated/DockerComposeV0/Tests/L0Windows.ts | 20 +- .../dockercomposeconnection.ts | 13 +- _generated/DockerComposeV0/task.json | 10 +- _generated/DockerComposeV0/task.loc.json | 8 +- .../resources.resjson/en-US/resources.resjson | 2 +- _generated/DockerComposeV0_Node20/Tests/L0.ts | 650 ++++---- .../DockerComposeV0_Node20/Tests/L0Linux.ts | 44 +- .../DockerComposeV0_Node20/Tests/L0Windows.ts | 20 +- .../dockercomposeconnection.ts | 13 +- _generated/DockerComposeV0_Node20/task.json | 10 +- .../DockerComposeV0_Node20/task.loc.json | 8 +- azure-pipelines.yml | 1 + make-options.json | 1 + 59 files changed, 6500 insertions(+), 1156 deletions(-) create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/de-DE/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/en-US/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/es-ES/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/fr-FR/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/it-IT/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/ja-JP/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/ko-KR/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/ru-RU/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/zh-CN/resources.resjson create mode 100644 Tasks/DockerComposeV1/Strings/resources.resjson/zh-TW/resources.resjson create mode 100644 Tasks/DockerComposeV1/Tests/L0.ts create mode 100644 Tasks/DockerComposeV1/Tests/L0Linux.ts create mode 100644 Tasks/DockerComposeV1/Tests/L0Windows.ts create mode 100644 Tasks/DockerComposeV1/ThirdPartyNotices.txt create mode 100644 Tasks/DockerComposeV1/dockercompose.ts create mode 100644 Tasks/DockerComposeV1/dockercomposebuild.ts create mode 100644 Tasks/DockerComposeV1/dockercomposecommand.ts create mode 100644 Tasks/DockerComposeV1/dockercomposeconfig.ts create mode 100644 Tasks/DockerComposeV1/dockercomposeconnection.ts create mode 100644 Tasks/DockerComposeV1/dockercomposedigests.ts create mode 100644 Tasks/DockerComposeV1/dockercomposelock.ts create mode 100644 Tasks/DockerComposeV1/dockercomposepush.ts create mode 100644 Tasks/DockerComposeV1/dockercomposerun.ts create mode 100644 Tasks/DockerComposeV1/dockercomposeup.ts create mode 100644 Tasks/DockerComposeV1/dockercomposeutils.ts create mode 100644 Tasks/DockerComposeV1/icon.png create mode 100644 Tasks/DockerComposeV1/icon.svg create mode 100644 Tasks/DockerComposeV1/make.json create mode 100644 Tasks/DockerComposeV1/package-lock.json create mode 100644 Tasks/DockerComposeV1/package.json create mode 100644 Tasks/DockerComposeV1/task.json create mode 100644 Tasks/DockerComposeV1/task.loc.json create mode 100644 Tasks/DockerComposeV1/tsconfig.json create mode 100644 Tasks/DockerComposeV1/utils.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2da86717aff1..7cef5c307297 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -193,6 +193,8 @@ Tasks/DockerV2/ @microsoft/release-management-task-team @manolerazvan Tasks/DockerComposeV0/ @microsoft/release-management-task-team @manolerazvan +Tasks/DockerComposeV1/ @microsoft/release-management-task-team @manolerazvan + Tasks/DockerInstallerV0/ @microsoft/release-management-task-team @manolerazvan Tasks/DotNetCoreCLIV2/ @microsoft/akvelon-build-task-team @DergachevE diff --git a/Tasks/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson b/Tasks/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson index da4e38ca91f4..6983106d2444 100644 --- a/Tasks/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson @@ -82,5 +82,5 @@ "loc.messages.ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "loc.messages.WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "loc.messages.InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "loc.messages.MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "loc.messages.MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" } \ No newline at end of file diff --git a/Tasks/DockerComposeV0/Tests/L0.ts b/Tasks/DockerComposeV0/Tests/L0.ts index bed9005ab92a..9b9d50a8fbcc 100644 --- a/Tasks/DockerComposeV0/Tests/L0.ts +++ b/Tasks/DockerComposeV0/Tests/L0.ts @@ -3,348 +3,318 @@ import * as assert from 'assert'; import * as ttm from 'azure-pipelines-task-lib/mock-test'; import tl = require('azure-pipelines-task-lib'); -[{ - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: true -}, { - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: false -}].forEach((featureFlag) => { - process.env.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE = String(featureFlag.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE); - - describe('Docker Compose Suite', function() { - this.timeout(30000); - let composeCommand: string; - - before(async() => { - composeCommand = tl.getBoolFeatureFlag('USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE') ? "docker compose --compatibility" : "docker-compose"; +describe('Docker Compose Suite', function() { + this.timeout(30000); + let composeCommand: string; + + before(() => { + composeCommand = "docker compose"; + }); + + beforeEach(() => { + delete process.env["__command__"]; + delete process.env["__container_type__"]; + delete process.env["__qualifyImageNames__"]; + delete process.env["__additionalDockerComposeFiles__"]; + delete process.env["__composeFilePath__"]; + delete process.env["__dockerComposeCommand__"]; + delete process.env["__arguments__"]; + delete process.env["__dockerComposePath__"]; + }); + + if (tl.getPlatform() === tl.Platform.Windows) { + it('Runs successfully for windows docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); }); - beforeEach(() => { - delete process.env["__command__"]; - delete process.env["__container_type__"]; - delete process.env["__qualifyImageNames__"]; - delete process.env["__additionalDockerComposeFiles__"]; - delete process.env["__composeFilePath__"]; - delete process.env["__dockerComposeCommand__"]; - delete process.env["__arguments__"]; - delete process.env["__dockerComposePath__"]; + it('Runs successfully for windows docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); }); - if (tl.osType().match(/^Win/)) { - it('Runs successfully for windows docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - }); - - it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for windows docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose run service', async() => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - console.log(tr.stdout); - console.log(tr.stderr); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); - - }); - - it('Runs successfully for windows docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - } else { - it('Runs successfully for linux docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose run service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - - it('Runs successfully for linux docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - } - }); + it('Runs successfully for windows docker compose run service', async() => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); + }); + + it('Runs successfully for windows docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + } else { + it('Runs successfully for linux docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose run service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + + it('Runs successfully for linux docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + } }); \ No newline at end of file diff --git a/Tasks/DockerComposeV0/Tests/L0Linux.ts b/Tasks/DockerComposeV0/Tests/L0Linux.ts index b4a6cc18a864..4e0addc60f08 100644 --- a/Tasks/DockerComposeV0/Tests/L0Linux.ts +++ b/Tasks/DockerComposeV0/Tests/L0Linux.ts @@ -33,8 +33,8 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; -process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; -process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; process.env['AGENT_HOMEDIRECTORY'] = '/tmp/tempdir/100/'; // provide answers for task mock @@ -48,27 +48,27 @@ let a: any = { "docker-compose": true }, "exec": { - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, @@ -80,59 +80,59 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up": { + "docker compose -f /tmp/tempdir/100/docker-compose.yml up": { "code": 0, "stdout": "sucessfully ran services" }, - "docker push ajgtestacr1.azurecr.io/100_web":{ + "docker push ajgtestacr1.azurecr.io/100_web": { "code": 0, "stdout": "successfully pushed with qualified image" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" } diff --git a/Tasks/DockerComposeV0/Tests/L0Windows.ts b/Tasks/DockerComposeV0/Tests/L0Windows.ts index 91fda927a028..34352fc1e454 100644 --- a/Tasks/DockerComposeV0/Tests/L0Windows.ts +++ b/Tasks/DockerComposeV0/Tests/L0Windows.ts @@ -52,7 +52,7 @@ let a: any = { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build" : { + "docker compose -f F:\\dir2\\docker-compose.yml build" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -64,7 +64,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml config" : { + "docker compose -f F:\\dir2\\docker-compose.yml config" : { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, @@ -80,11 +80,15 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, + "docker compose -f F:\\dir2\\docker-compose.yml up": { + "code": 0, + "stdout": "sucessfully ran services" + }, "docker-compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { + "docker compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -92,7 +96,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -104,7 +108,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -112,7 +116,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -120,7 +124,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -128,7 +132,7 @@ let a: any = { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml pull service1 service2" : { + "docker compose -f F:\\dir2\\docker-compose.yml pull service1 service2" : { "code": 0, "stdout": "successfully pulled the passed service images" } diff --git a/Tasks/DockerComposeV0/dockercomposeconnection.ts b/Tasks/DockerComposeV0/dockercomposeconnection.ts index 893fb8812a09..5bd24c20e5f9 100644 --- a/Tasks/DockerComposeV0/dockercomposeconnection.ts +++ b/Tasks/DockerComposeV0/dockercomposeconnection.ts @@ -19,11 +19,9 @@ export default class DockerComposeConnection extends ContainerConnection { private requireAdditionalDockerComposeFiles: boolean; private projectName: string; private finalComposeFile: string; - private useDockerComposeV2: boolean; constructor() { super(); - this.useDockerComposeV2 = tl.getBoolFeatureFlag("USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE"); this.setDockerComposePath(); this.dockerComposeFile = DockerComposeUtils.findDockerFile(tl.getInput("dockerComposeFile", true), tl.getInput("cwd")); if (!this.dockerComposeFile) { @@ -95,7 +93,7 @@ export default class DockerComposeConnection extends ContainerConnection { public createComposeCommand(): tr.ToolRunner { var command = tl.tool(this.dockerComposePath); - if (this.useDockerComposeV2 && !tl.getInput('dockerComposePath')) { + if (!tl.getInput('dockerComposePath')) { command.arg("compose"); process.env["COMPOSE_COMPATIBILITY"] = "true"; } @@ -186,10 +184,9 @@ export default class DockerComposeConnection extends ContainerConnection { //Priority to docker-compose path provided by user this.dockerComposePath = tl.getInput('dockerComposePath'); if (!this.dockerComposePath) { - // If not use the docker-compose avilable on agent - if (this.useDockerComposeV2) { - this.dockerComposePath = tl.which("docker"); - } else { + this.dockerComposePath = tl.which("docker"); + + if (!this.dockerComposePath) { this.dockerComposePath = tl.which("docker-compose"); } @@ -204,7 +201,7 @@ export default class DockerComposeConnection extends ContainerConnection { private validateProjectNameDockerComposeV2() { tl.debug(`Start validating project name ${this.projectName}`); - if (this.dockerComposePath.includes("docker-compose") || !this.useDockerComposeV2) { + if (this.dockerComposePath.includes("docker-compose")) { tl.warning(tl.loc("MigrateToDockerComposeV2")); return; } diff --git a/Tasks/DockerComposeV0/task.json b/Tasks/DockerComposeV0/task.json index ade5d35de9f2..b4e4e676af1e 100644 --- a/Tasks/DockerComposeV0/task.json +++ b/Tasks/DockerComposeV0/task.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 2 + "Patch": 4 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", @@ -357,6 +359,6 @@ "ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" } } \ No newline at end of file diff --git a/Tasks/DockerComposeV0/task.loc.json b/Tasks/DockerComposeV0/task.loc.json index 9af9fb93eab8..5117272a37cf 100644 --- a/Tasks/DockerComposeV0/task.loc.json +++ b/Tasks/DockerComposeV0/task.loc.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 2 + "Patch": 4 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/de-DE/resources.resjson new file mode 100644 index 000000000000..e0ee1ceaabfa --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/de-DE/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[Weitere Informationen zu dieser Aufgabe](https://go.microsoft.com/fwlink/?linkid=848006) oder [Docker Compose-Dokumentation anzeigen](https://docs.docker.com/)", + "loc.description": "Erstellen Sie Docker-Anwendungen mit mehreren Containern, übertragen Sie sie mithilfe von Push, oder führen Sie sie aus. Die Aufgabe kann mit Docker oder Azure Container Registry verwendet werden.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Erweiterte Optionen", + "loc.input.label.containerregistrytype": "Containerregistrierungstyp", + "loc.input.help.containerregistrytype": "Wählen Sie einen Containerregistrierungstyp aus.", + "loc.input.label.dockerRegistryEndpoint": "Dienstverbindung für Docker-Registrierung", + "loc.input.help.dockerRegistryEndpoint": "Wählen Sie eine Dienstverbindung für die Docker-Registrierung aus. Erforderlich für Befehle, die eine Authentifizierung bei einer Registrierung erfordern.", + "loc.input.label.azureSubscriptionEndpoint": "Azure-Abonnement", + "loc.input.help.azureSubscriptionEndpoint": "Wählen Sie ein Azure-Abonnement aus.", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Wählen Sie eine Azure Container Registry aus.", + "loc.input.label.dockerComposeFile": "Docker Compose-Datei", + "loc.input.help.dockerComposeFile": "Pfad zur zu verwendenden primären Docker Compose-Datei.", + "loc.input.label.additionalDockerComposeFiles": "Zusätzliche Docker Compose-Dateien", + "loc.input.help.additionalDockerComposeFiles": "Zusätzliche Docker Compose-Dateien, die mit der primären Docker Compose-Datei kombiniert werden soll. Relative Pfade werden in Bezug auf das Verzeichnis, dass die primäre Docker Compose-Datei enthält, aufgelöst. Wenn eine angegebene Datei nicht gefunden wird, wird sie ignoriert. Geben Sie jeden Dateipfad in einer neuen Zeile an.", + "loc.input.label.dockerComposeFileArgs": "Umgebungsvariablen", + "loc.input.help.dockerComposeFileArgs": "Umgebungsvariablen, die während des Befehls festgelegt werden. Geben Sie jedes Name/Wert-Paar in einer neuen Zeile an.", + "loc.input.label.projectName": "Projektname", + "loc.input.help.projectName": "Projektname, der für Standardnamen von Images und Containern verwendet wird.", + "loc.input.label.qualifyImageNames": "Imagenamen qualifizieren", + "loc.input.help.qualifyImageNames": "Qualifizieren Sie die Imagenamen für erstellte Dienste mit dem Hostnamen der Docker-Registrierungsdienstverbindung, sofern nicht anders angegeben.", + "loc.input.label.action": "Aktion", + "loc.input.help.action": "Wählen Sie eine Docker Compose-Aktion aus.", + "loc.input.label.additionalImageTags": "Zusätzliche Imagetags", + "loc.input.help.additionalImageTags": "Zusätzliche Tags für die Docker-Images, die erstellt oder mithilfe von Push übertragen werden sollen.", + "loc.input.label.includeSourceTags": "Quelltags einschließen", + "loc.input.help.includeSourceTags": "Fügen Sie Git-Tags ein, wenn Sie Docker-Images erstellen oder mittels Push übertragen.", + "loc.input.label.includeLatestTag": "Aktuelles Tag einschließen", + "loc.input.help.includeLatestTag": "Fügen Sie das Tag \"latest\" (aktuell) ein, wenn Sie die Docker-Images erstellen oder mittels Push übertragen.", + "loc.input.label.buildImages": "Images erstellen", + "loc.input.help.buildImages": "Erstellen Sie Images, bevor Sie Dienstcontainer starten.", + "loc.input.label.serviceName": "Dienstname", + "loc.input.help.serviceName": "Name des auszuführenden spezifischen Diensts.", + "loc.input.label.containerName": "Containername", + "loc.input.help.containerName": "Name des auszuführenden spezifischen Dienstcontainers.", + "loc.input.label.ports": "Ports", + "loc.input.help.ports": "Ports im spezifischen Dienstcontainer zum Veröffentlichen an den Host. Geben Sie jede Hostport:Containerport-Bindung in einer neuen Zeile an.", + "loc.input.label.workDir": "Arbeitsverzeichnis", + "loc.input.help.workDir": "Das Arbeitsverzeichnis für den spezifischen Dienstcontainer.", + "loc.input.label.entrypoint": "Überschreiben des Einstiegspunkts", + "loc.input.help.entrypoint": "Überschreiben Sie den Standardeinstiegspunkt für den spezifischen Dienstcontainer.", + "loc.input.label.containerCommand": "Befehl", + "loc.input.help.containerCommand": "Im spezifischen Dienstcontainer auszuführender Befehl. Wenn das Image beispielsweise eine einfache Python Flask-Webanwendung enthält, können Sie zum Starten der Webanwendung \"python app.py\" angeben.", + "loc.input.label.detached": "Im Hintergrund ausführen", + "loc.input.help.detached": "Führen Sie die Dienstcontainer im Hintergrund aus.", + "loc.input.label.abortOnContainerExit": "Beim Beenden des Containers abbrechen", + "loc.input.help.abortOnContainerExit": "Alle Container anhalten, wenn ein Container beendet wird.", + "loc.input.label.imageDigestComposeFile": "Imagehashwert-Compose-Datei", + "loc.input.help.imageDigestComposeFile": "Pfad zu einer Docker Compose-Datei, die erstellt und mit den vollständigen Imagerepository-Hashwerten des Docker-Images jedes Diensts befüllt wird.", + "loc.input.label.removeBuildOptions": "Buildoptionen entfernen", + "loc.input.help.removeBuildOptions": "Entfernen Sie die Buildoptionen aus der Docker Compose-Ausgabedatei.", + "loc.input.label.baseResolveDirectory": "Basisauflösungsverzeichnis", + "loc.input.help.baseResolveDirectory": "Das Basisverzeichnis, in dem die entsprechenden Pfade in der Docker Compose-Ausgabedatei aufgelöst werden sollen.", + "loc.input.label.outputDockerComposeFile": "Docker Compose-Datei ausgeben", + "loc.input.help.outputDockerComposeFile": "Pfad zu einer Docker Compose-Ausgabedatei.", + "loc.input.label.dockerComposeCommand": "Befehl", + "loc.input.help.dockerComposeCommand": "Docker Compose-Befehl, der mit Argumenten ausgeführt werden soll. Beispielsweise \"rm --all\" entfernt alle angehaltenen Dienstcontainer.", + "loc.input.label.arguments": "Argumente", + "loc.input.help.arguments": "Docker Compose-Befehlsoptionen. Beispiel:
Für Buildbefehl:
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker-Hostdienstverbindung", + "loc.input.help.dockerHostEndpoint": "Wählen Sie eine Docker-Hostdienstverbindung. Entspricht standardmäßig dem Agent-Host.", + "loc.input.label.nopIfNoDockerComposeFile": "Kein Vorgang, wenn keine Docker Compose-Datei vorhanden ist", + "loc.input.help.nopIfNoDockerComposeFile": "Wenn die Docker Compose-Datei nicht vorhanden ist, überspringen Sie diese Aufgabe. Dies ist hilfreich, wenn die Aufgabe ein optionales Verhalten ermöglicht, das darauf basiert, dass im Repository eine Docker Compose-Datei enthalten ist.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Zusätzliche Docker Compose-Dateien erfordern", + "loc.input.help.requireAdditionalDockerComposeFiles": "Erzeugt einen Fehler, wenn die zusätzlichen Docker Compose-Dateien nicht vorhanden sind. Dies überschreibt das Standardverhalten, das darin besteht, eine nicht vorhandene Datei zu ignorieren.", + "loc.input.label.cwd": "Arbeitsverzeichnis", + "loc.input.help.cwd": "Arbeitsverzeichnis für den Docker Compose-Befehl.", + "loc.input.label.dockerComposePath": "Pfad zur ausführbaren Docker Compose-Datei", + "loc.input.help.dockerComposePath": "Diese ausführbare docker-compose-Datei wird verwendet, wenn der Pfad angegeben wird.", + "loc.messages.ConnectingToDockerHost": "Die DOCKER_HOST-Variable ist festgelegt. Docker versucht, eine Verbindung mit dem Docker-Host herzustellen: %s", + "loc.messages.ContainerPatternFound": "Muster in Docker Compose-Parameter \"filepath\" gefunden.", + "loc.messages.ContainerPatternNotFound": "Kein Muster in Docker Compose-Parameter \"filepath\" gefunden.", + "loc.messages.ContainerDockerFileNotFound": "Es wurde keine Docker-Datei gefunden, die %s entspricht.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "Die zusätzliche Docker Compose-Datei \"%s\" ist nicht vorhanden.", + "loc.messages.CantWriteDataToFile": "In die Datei \"%s\" können keine Daten geschrieben werden. Fehler: %s", + "loc.messages.DockerHostVariableWarning": "Die DOCKER_HOST-Variable ist festgelegt. Stellen Sie sicher, dass der Docker-Daemon auf \"%s\" ausgeführt wird.", + "loc.messages.NoDataWrittenOnFile": "In die Datei \"%s\" wurden keine Daten geschrieben. ", + "loc.messages.FileContentSynced": "Der Inhalt der Datei wurde auf dem Datenträger synchronisiert. Der Inhalt ist \"%s\".", + "loc.messages.ImageNameWithoutTag": "Imagename nicht mit Tag angegeben, Übertragung aller Imagetags mithilfe von Push angegeben.", + "loc.messages.WritingDockerConfigToTempFile": "Die Docker-Konfiguration wird in eine temporäre Datei geschrieben. Dateipfad: %s, Docker-Konfiguration: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/en-US/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/en-US/resources.resjson new file mode 100644 index 000000000000..4143a00c1c29 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/en-US/resources.resjson @@ -0,0 +1,87 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?linkid=848006) or [see the Docker Compose documentation](https://docs.docker.com/)", + "loc.description": "Build, push or run multi-container Docker applications. Task can be used with Docker or Azure Container registry.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Advanced Options", + "loc.input.label.containerregistrytype": "Container Registry Type", + "loc.input.help.containerregistrytype": "Select a Container Registry Type.", + "loc.input.label.dockerRegistryEndpoint": "Docker Registry Service Connection", + "loc.input.help.dockerRegistryEndpoint": "Select a Docker registry service connection. Required for commands that need to authenticate with a registry.", + "loc.input.label.azureSubscriptionEndpoint": "Azure subscription", + "loc.input.help.azureSubscriptionEndpoint": "Select an Azure subscription", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Select an Azure Container Registry", + "loc.input.label.dockerComposeFile": "Docker Compose File", + "loc.input.help.dockerComposeFile": "Path to the primary Docker Compose file to use.", + "loc.input.label.additionalDockerComposeFiles": "Additional Docker Compose Files", + "loc.input.help.additionalDockerComposeFiles": "Additional Docker Compose files to be combined with the primary Docker Compose file. Relative paths are resolved relative to the directory containing the primary Docker Compose file. If a specified file is not found, it is ignored. Specify each file path on a new line.", + "loc.input.label.dockerComposeFileArgs": "Environment Variables", + "loc.input.help.dockerComposeFileArgs": "Environment variables to be set during the command. Specify each name=value pair on a new line.", + "loc.input.label.projectName": "Project Name", + "loc.input.help.projectName": "Project name used for default naming of images and containers.", + "loc.input.label.qualifyImageNames": "Qualify Image Names", + "loc.input.help.qualifyImageNames": "Qualify image names for built services with the Docker registry service connection's hostname if not otherwise specified.", + "loc.input.label.action": "Action", + "loc.input.help.action": "Select a Docker Compose action.", + "loc.input.label.additionalImageTags": "Additional Image Tags", + "loc.input.help.additionalImageTags": "Additional tags for the Docker images being built or pushed.", + "loc.input.label.includeSourceTags": "Include Source Tags", + "loc.input.help.includeSourceTags": "Include Git tags when building or pushing Docker images.", + "loc.input.label.includeLatestTag": "Include Latest Tag", + "loc.input.help.includeLatestTag": "Include the 'latest' tag when building or pushing Docker images.", + "loc.input.label.buildImages": "Build Images", + "loc.input.help.buildImages": "Build images before starting service containers.", + "loc.input.label.serviceName": "Service Name", + "loc.input.help.serviceName": "Name of the specific service to run.", + "loc.input.label.containerName": "Container Name", + "loc.input.help.containerName": "Name of the specific service container to run.", + "loc.input.label.ports": "Ports", + "loc.input.help.ports": "Ports in the specific service container to publish to the host. Specify each host-port:container-port binding on a new line.", + "loc.input.label.workDir": "Working Directory", + "loc.input.help.workDir": "The working directory for the specific service container.", + "loc.input.label.entrypoint": "Entry Point Override", + "loc.input.help.entrypoint": "Override the default entry point for the specific service container.", + "loc.input.label.containerCommand": "Command", + "loc.input.help.containerCommand": "Command to run in the specific service container. For example, if the image contains a simple Python Flask web application you can specify 'python app.py' to launch the web application.", + "loc.input.label.detached": "Run in Background", + "loc.input.help.detached": "Run the service containers in the background.", + "loc.input.label.abortOnContainerExit": "Abort on Container Exit", + "loc.input.help.abortOnContainerExit": "Stop all containers when any container exits.", + "loc.input.label.imageDigestComposeFile": "Image Digest Compose File", + "loc.input.help.imageDigestComposeFile": "Path to a Docker Compose file that is created and populated with the full image repository digests of each service's Docker image.", + "loc.input.label.removeBuildOptions": "Remove Build Options", + "loc.input.help.removeBuildOptions": "Remove the build options from the output Docker Compose file.", + "loc.input.label.baseResolveDirectory": "Base Resolve Directory", + "loc.input.help.baseResolveDirectory": "The base directory from which relative paths in the output Docker Compose file should be resolved.", + "loc.input.label.outputDockerComposeFile": "Output Docker Compose File", + "loc.input.help.outputDockerComposeFile": "Path to an output Docker Compose file.", + "loc.input.label.dockerComposeCommand": "Command", + "loc.input.help.dockerComposeCommand": "Docker Compose command to execute with arguments. For example, 'rm --all' to remove all stopped service containers.", + "loc.input.label.arguments": "Arguments", + "loc.input.help.arguments": "Docker Compose command options. Ex:
For build command,
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker Host Service Connection", + "loc.input.help.dockerHostEndpoint": "Select a Docker host service connection. Defaults to the agent's host.", + "loc.input.label.nopIfNoDockerComposeFile": "No-op if no Docker Compose File", + "loc.input.help.nopIfNoDockerComposeFile": "If the Docker Compose file does not exist, skip this task. This is useful when the task offers optional behavior based on the existence of a Docker Compose file in the repository.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Require Additional Docker Compose Files", + "loc.input.help.requireAdditionalDockerComposeFiles": "Produces an error if the additional Docker Compose files do not exist. This overrides the default behavior which is to ignore a file if it does not exist.", + "loc.input.label.cwd": "Working Directory", + "loc.input.help.cwd": "Working directory for the Docker Compose command.", + "loc.input.label.dockerComposePath": "Docker Compose executable Path", + "loc.input.help.dockerComposePath": "This docker-compose executable will be used if the path is provided", + "loc.messages.ConnectingToDockerHost": "DOCKER_HOST variable is set. Docker will try to connect to the Docker host: %s", + "loc.messages.ContainerPatternFound": "Pattern found in docker compose filepath parameter", + "loc.messages.ContainerPatternNotFound": "No pattern found in docker compose filepath parameter", + "loc.messages.ContainerDockerFileNotFound": "No Docker file matching %s was found.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "Additional Docker Compose File %s does not exists.", + "loc.messages.CantWriteDataToFile": "Can not write data to the file %s. Error: %s", + "loc.messages.DockerHostVariableWarning": "DOCKER_HOST variable is set. Please ensure that the Docker daemon is running on: %s", + "loc.messages.NoDataWrittenOnFile": "No data was written into the file %s", + "loc.messages.FileContentSynced": "Synced the file content to the disk. The content is %s.", + "loc.messages.ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", + "loc.messages.WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", + "loc.messages.DeprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", + "loc.messages.InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", + "loc.messages.MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/es-ES/resources.resjson new file mode 100644 index 000000000000..14fdb54cc852 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/es-ES/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[Obtener más información acerca de esta tarea](https://go.microsoft.com/fwlink/?linkid=848006) o [consultar la documentación de Docker Compose](https://docs.docker.com/)", + "loc.description": "Compile, inserte o ejecute aplicaciones de Docker con varios contenedores. La tarea se puede usar con Docker o Azure Container Registry.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Opciones avanzadas", + "loc.input.label.containerregistrytype": "Tipo de Container Registry", + "loc.input.help.containerregistrytype": "Seleccione un tipo de Container Registry.", + "loc.input.label.dockerRegistryEndpoint": "Conexión de servicio del Registro de Docker", + "loc.input.help.dockerRegistryEndpoint": "Seleccione una conexión de servicio de registro de Docker. Obligatorio para los comandos que deben autenticarse en un registro.", + "loc.input.label.azureSubscriptionEndpoint": "Suscripción a Azure", + "loc.input.help.azureSubscriptionEndpoint": "Seleccione una suscripción de Azure", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Seleccione una instancia de Azure Container Registry", + "loc.input.label.dockerComposeFile": "Archivo de Docker Compose", + "loc.input.help.dockerComposeFile": "Ruta de acceso al archivo principal de Docker Compose que debe usarse.", + "loc.input.label.additionalDockerComposeFiles": "Archivos adicionales de Docker Compose", + "loc.input.help.additionalDockerComposeFiles": "Archivos adicionales de Docker Compose que deben combinarse con el archivo principal de Docker Compose. Las rutas de acceso relativas se resuelven respecto al directorio que contiene el archivo principal de Docker Compose. Si no se encuentra un archivo especificado, se omite. Especifique cada ruta de acceso de archivo en una línea nueva.", + "loc.input.label.dockerComposeFileArgs": "Variables de entorno", + "loc.input.help.dockerComposeFileArgs": "Variables de entorno que deben establecerse durante la ejecución del comando. Especifique cada par nombre=valor en una línea nueva.", + "loc.input.label.projectName": "Nombre de proyecto", + "loc.input.help.projectName": "Nombre de proyecto que se usa para nombrar de forma predeterminada las imágenes y los contenedores.", + "loc.input.label.qualifyImageNames": "Completar los nombres de imagen", + "loc.input.help.qualifyImageNames": "Completa los nombres de imagen para los servicios compilados con el nombre de host de la conexión del servicio de registro de Docker si no se ha especificado otra cosa.", + "loc.input.label.action": "Acción", + "loc.input.help.action": "Seleccione una acción de Docker Compose.", + "loc.input.label.additionalImageTags": "Etiquetas de imagen adicionales", + "loc.input.help.additionalImageTags": "Etiquetas adicionales para las imágenes de Docker que se van a compilar o insertar.", + "loc.input.label.includeSourceTags": "Incluir etiquetas de origen", + "loc.input.help.includeSourceTags": "Incluye las etiquetas de GIT cuando se compilan o insertan imágenes de Docker.", + "loc.input.label.includeLatestTag": "Incluir etiqueta \"latest\"", + "loc.input.help.includeLatestTag": "Incluye la etiqueta \"latest\" cuando se compilan o insertan imágenes de Docker.", + "loc.input.label.buildImages": "Compilar imágenes", + "loc.input.help.buildImages": "Compila las imágenes antes de iniciar los contenedores de servicio.", + "loc.input.label.serviceName": "Nombre del servicio", + "loc.input.help.serviceName": "Nombre del servicio específico que debe ejecutarse.", + "loc.input.label.containerName": "Nombre del contenedor", + "loc.input.help.containerName": "Nombre del contenedor de servicio específico que debe ejecutarse.", + "loc.input.label.ports": "Puertos", + "loc.input.help.ports": "Puertos en el contenedor de servicio específico para publicar en el host. Especifique cada enlace puerto-del-host:puerto-del-contenedor en una línea nueva.", + "loc.input.label.workDir": "Directorio de trabajo", + "loc.input.help.workDir": "Directorio de trabajo para el contenedor de servicio específico.", + "loc.input.label.entrypoint": "Invalidación de punto de entrada", + "loc.input.help.entrypoint": "Reemplace el punto de entrada predeterminado para el contenedor de servicio específico.", + "loc.input.label.containerCommand": "Comando", + "loc.input.help.containerCommand": "Comando que debe ejecutarse en el contenedor de servicio especificado. Por ejemplo, si la imagen contiene una aplicación web de Python Flask sencilla, puede especificar \"python app.py\" para iniciar la aplicación web.", + "loc.input.label.detached": "Ejecutar en segundo plano", + "loc.input.help.detached": "Ejecuta los contenedores de servicio en segundo plano.", + "loc.input.label.abortOnContainerExit": "Anular al salir del contenedor", + "loc.input.help.abortOnContainerExit": "Detiene todos los contenedores cuando existe algún contenedor.", + "loc.input.label.imageDigestComposeFile": "Archivo de resumen de imagen de Compose", + "loc.input.help.imageDigestComposeFile": "Ruta de acceso a un archivo de Docker Compose que se crea y rellena con los resúmenes del repositorio de imágenes completo de cada imagen de Docker del servicio.", + "loc.input.label.removeBuildOptions": "Quitar opciones de compilación", + "loc.input.help.removeBuildOptions": "Quita las opciones de compilación del archivo de salida de Docker Compose.", + "loc.input.label.baseResolveDirectory": "Directorio de resolución base", + "loc.input.help.baseResolveDirectory": "Directorio base a partir del cual deben resolverse las rutas de acceso relativas en el archivo de salida de Docker Compose.", + "loc.input.label.outputDockerComposeFile": "Archivo de salida de Docker Compose", + "loc.input.help.outputDockerComposeFile": "Ruta de acceso a un archivo de salida de Docker Compose.", + "loc.input.label.dockerComposeCommand": "Comando", + "loc.input.help.dockerComposeCommand": "Comando de Docker Compose que debe ejecutarse con argumentos. Por ejemplo, \"rm --all\" para quitar todos los contenedores de servicio detenidos.", + "loc.input.label.arguments": "Argumentos", + "loc.input.help.arguments": "Opciones de comandos de Docker Compose. Ejemplo:
Para el comando de compilación,
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Conexión de servicio de host de Docker", + "loc.input.help.dockerHostEndpoint": "Seleccione una conexión de servicio de host de Docker. El valor predeterminado es el host del agente.", + "loc.input.label.nopIfNoDockerComposeFile": "No-op si no existe un archivo de Docker Compose", + "loc.input.help.nopIfNoDockerComposeFile": "Si el archivo de Docker Compose no existe, se omite esta tarea. Esto es útil cuando la tarea ofrece un comportamiento opcional en función de que exista o no un archivo de Docker Compose en el repositorio.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Requerir archivos adicionales de Docker Compose", + "loc.input.help.requireAdditionalDockerComposeFiles": "Produce un error si no existen los archivos adicionales de Docker Compose. Esta acción reemplaza el comportamiento predeterminado, que es omitir un archivo si no existe.", + "loc.input.label.cwd": "Directorio de trabajo", + "loc.input.help.cwd": "Directorio de trabajo para el comando de Docker Compose.", + "loc.input.label.dockerComposePath": "Ruta de acceso del ejecutable de Docker Compose", + "loc.input.help.dockerComposePath": "Este ejecutable de Docker Compose se usará si se proporciona la ruta de acceso.", + "loc.messages.ConnectingToDockerHost": "La variable DOCKER_HOST está establecida. Docker intentará conectarse a su host: %s", + "loc.messages.ContainerPatternFound": "Se encontró un patrón en el parámetro filepath de Docker Compose.", + "loc.messages.ContainerPatternNotFound": "No se encontró ningún patrón en el parámetro filepath de Docker Compose.", + "loc.messages.ContainerDockerFileNotFound": "No se encontró ningún archivo de Docker que coincidiera con %s.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "El archivo adicional de Docker Compose %s no existe.", + "loc.messages.CantWriteDataToFile": "No se pueden escribir datos en el archivo %s. Error: %s", + "loc.messages.DockerHostVariableWarning": "La variable DOCKER_HOST está establecida. Asegúrese de que el demonio de Docker se está ejecutando en %s.", + "loc.messages.NoDataWrittenOnFile": "No se escribieron datos en el archivo %s", + "loc.messages.FileContentSynced": "Se sincronizó el contenido del archivo con el disco. El contenido es %s.", + "loc.messages.ImageNameWithoutTag": "El nombre de la imagen no se especifica con la etiqueta, insertando todas las etiquetas de la imagen especificada.", + "loc.messages.WritingDockerConfigToTempFile": "Escribiendo la configuración de Docker en el archivo temporal. Ruta de acceso del archivo: %s. Configuración de Docker: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/fr-FR/resources.resjson new file mode 100644 index 000000000000..c3947074ae6a --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/fr-FR/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[En savoir plus sur cette tâche](https://go.microsoft.com/fwlink/?linkid=848006) ou [consulter la documentation de Docker Compose](https://docs.docker.com/)", + "loc.description": "Effectuez la build, le Push ou l'exécution d'applications Docker à plusieurs conteneurs. La tâche peut être utilisée avec le registre de conteneurs Docker ou Azure.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Options avancées", + "loc.input.label.containerregistrytype": "Type de registre de conteneurs", + "loc.input.help.containerregistrytype": "Sélectionnez un type de registre de conteneurs.", + "loc.input.label.dockerRegistryEndpoint": "Connexion de service de registre Docker", + "loc.input.help.dockerRegistryEndpoint": "Sélectionnez une connexion de service de registre Docker. Obligatoire pour les commandes qui doivent s'authentifier après d'un registre.", + "loc.input.label.azureSubscriptionEndpoint": "Abonnement Azure", + "loc.input.help.azureSubscriptionEndpoint": "Sélectionnez un abonnement Azure", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Sélectionnez un registre Azure Container Registry", + "loc.input.label.dockerComposeFile": "Fichier Docker Compose", + "loc.input.help.dockerComposeFile": "Chemin du fichier Docker Compose principal à utiliser.", + "loc.input.label.additionalDockerComposeFiles": "Fichiers Docker Compose supplémentaires", + "loc.input.help.additionalDockerComposeFiles": "Fichiers Docker Compose supplémentaires à combiner au fichier Docker Compose principal. Les chemins relatifs sont résolus par rapport au répertoire contenant le fichier Docker Compose principal. Si le fichier spécifié est introuvable, il est ignoré. Spécifiez chaque chemin de fichier sur une nouvelle ligne.", + "loc.input.label.dockerComposeFileArgs": "Variables d'environnement", + "loc.input.help.dockerComposeFileArgs": "Variables d'environnement à définir durant la commande. Spécifiez chaque paire nom=valeur sur une nouvelle ligne.", + "loc.input.label.projectName": "Nom du projet", + "loc.input.help.projectName": "Nom de projet utilisé pour la dénomination par défaut des images et des conteneurs.", + "loc.input.label.qualifyImageNames": "Qualifier les noms d'images", + "loc.input.help.qualifyImageNames": "Qualifiez les noms d'images des services générés à l'aide du nom d'hôte de la connexion de service de registre Docker, sauf indication contraire.", + "loc.input.label.action": "Action", + "loc.input.help.action": "Sélectionnez une action Docker Compose.", + "loc.input.label.additionalImageTags": "Étiquettes d'image supplémentaires", + "loc.input.help.additionalImageTags": "Étiquettes supplémentaires pour les images Docker faisant l'objet d'une génération ou d'un Push.", + "loc.input.label.includeSourceTags": "Inclure les étiquettes sources", + "loc.input.help.includeSourceTags": "Incluez les étiquettes Git durant la génération ou le Push d'images Docker.", + "loc.input.label.includeLatestTag": "Inclure l'étiquette Latest", + "loc.input.help.includeLatestTag": "Incluez l'étiquette 'latest' durant la génération ou le Push d'images Docker.", + "loc.input.label.buildImages": "Images de build", + "loc.input.help.buildImages": "Générez les images avant de démarrer les conteneurs de service.", + "loc.input.label.serviceName": "Nom du service", + "loc.input.help.serviceName": "Nom du service spécifique à exécuter.", + "loc.input.label.containerName": "Nom du conteneur", + "loc.input.help.containerName": "Nom du conteneur de service spécifique à exécuter.", + "loc.input.label.ports": "Ports", + "loc.input.help.ports": "Ports du conteneur de service spécifique à publier sur l'hôte. Spécifiez chaque liaison port d'hôte:port de conteneur sur une nouvelle ligne.", + "loc.input.label.workDir": "Répertoire de travail", + "loc.input.help.workDir": "Répertoire de travail du conteneur de service spécifique.", + "loc.input.label.entrypoint": "Remplacement du point d'entrée", + "loc.input.help.entrypoint": "Remplacez le point d'entrée par défaut du conteneur de service spécifique.", + "loc.input.label.containerCommand": "Commande", + "loc.input.help.containerCommand": "Commande à exécuter dans le conteneur de service spécifique. Par exemple, si l'image contient une simple application web Python Flask, vous pouvez spécifier 'python app.py' pour lancer l'application web.", + "loc.input.label.detached": "Exécuter en arrière-plan", + "loc.input.help.detached": "Exécutez les conteneurs de service en arrière-plan.", + "loc.input.label.abortOnContainerExit": "Abandonner en cas de sortie du conteneur", + "loc.input.help.abortOnContainerExit": "Arrêtez tous les conteneurs en cas de sortie d'un conteneur.", + "loc.input.label.imageDigestComposeFile": "Fichier Compose de condensat d'image", + "loc.input.help.imageDigestComposeFile": "Chemin d'un fichier Docker Compose créé et rempli à l'aide du condensat de dépôt d'image complet de l'image Docker de chaque service.", + "loc.input.label.removeBuildOptions": "Supprimer les options de build", + "loc.input.help.removeBuildOptions": "Supprimez les options de build du fichier Docker Compose de sortie.", + "loc.input.label.baseResolveDirectory": "Répertoire de résolution de base", + "loc.input.help.baseResolveDirectory": "Répertoire de base à partir duquel les chemins relatifs du fichier Docker Compose de sortie doivent être résolus.", + "loc.input.label.outputDockerComposeFile": "Générer la sortie du fichier Docker Compose", + "loc.input.help.outputDockerComposeFile": "Chemin d'un fichier Docker Compose de sortie.", + "loc.input.label.dockerComposeCommand": "Commande", + "loc.input.help.dockerComposeCommand": "Commande Docker Compose à exécuter avec les arguments. Par exemple, 'rm --all' permet de supprimer tous les conteneurs de service arrêtés.", + "loc.input.label.arguments": "Arguments", + "loc.input.help.arguments": "Options de commande Docker Compose. Exemple :
Pour la commande de build,
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Connexion de service d'hôte Docker", + "loc.input.help.dockerHostEndpoint": "Sélectionnez une connexion de service d'hôte Docker. Par défaut, il s'agit de l'hôte de l'agent.", + "loc.input.label.nopIfNoDockerComposeFile": "Ne rien faire s'il ne s'agit pas d'un fichier Docker Compose", + "loc.input.help.nopIfNoDockerComposeFile": "Si le fichier Docker Compose n'existe pas, ignorez cette tâche. Ceci est utile quand la tâche offre un comportement facultatif en fonction de l'existence d'un fichier Docker Compose dans le dépôt.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Exiger des fichiers Docker Compose supplémentaires", + "loc.input.help.requireAdditionalDockerComposeFiles": "Produit une erreur si les fichiers Docker Compose supplémentaires n'existent pas. Ceci remplace le comportement par défaut qui consiste à ignorer un fichier inexistant.", + "loc.input.label.cwd": "Répertoire de travail", + "loc.input.help.cwd": "Répertoire de travail de la commande Docker Compose.", + "loc.input.label.dockerComposePath": "Chemin de l'exécutable Docker Compose", + "loc.input.help.dockerComposePath": "Cet exécutable docker-compose est utilisé si le chemin est fourni", + "loc.messages.ConnectingToDockerHost": "La variable DOCKER_HOST est définie. Docker va tenter de se connecter à l'hôte Docker : %s", + "loc.messages.ContainerPatternFound": "Modèle trouvé dans le paramètre filepath Docker Compose", + "loc.messages.ContainerPatternNotFound": "Modèle introuvable dans le paramètre filepath Docker Compose", + "loc.messages.ContainerDockerFileNotFound": "Le fichier Docker correspondant à %s est introuvable.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "Le fichier Docker Compose supplémentaire %s n'existe pas.", + "loc.messages.CantWriteDataToFile": "Impossible d'écrire des données dans le fichier %s. Erreur : %s", + "loc.messages.DockerHostVariableWarning": "La variable DOCKER_HOST est définie. Vérifiez que le démon Docker s'exécute sur %s", + "loc.messages.NoDataWrittenOnFile": "Aucune donnée n'a été écrite dans le fichier %s", + "loc.messages.FileContentSynced": "Synchronisation effectuée du contenu du fichier sur le disque. Le contenu est %s.", + "loc.messages.ImageNameWithoutTag": "Nom d'image non spécifié avec l'étiquette. Envoi (push) de toutes les étiquettes de l'image spécifiée.", + "loc.messages.WritingDockerConfigToTempFile": "Écriture de la configuration Docker dans le fichier temporaire. Chemin de fichier : %s. Configuration Docker : %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/it-IT/resources.resjson new file mode 100644 index 000000000000..a90bc2024367 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/it-IT/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[Altre informazioni su questa attività](https://go.microsoft.com/fwlink/?linkid=848006). In alternativa [vedere la documentazione di Docker Compose](https://docs.docker.com/)", + "loc.description": "Consente di compilare, effettuare il push o eseguire applicazioni Docker con più contenitori. È possibile usare l'attività con il registro contenitori di Azure o Docker.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Opzioni avanzate", + "loc.input.label.containerregistrytype": "Tipo di registro contenitori", + "loc.input.help.containerregistrytype": "Consente di selezionare un tipo di registro contenitori.", + "loc.input.label.dockerRegistryEndpoint": "Connessione al servizio Registro Docker", + "loc.input.help.dockerRegistryEndpoint": "Consente di selezionare una connessione al servizio Registro Docker. È obbligatorio per i comandi che devono eseguire l'autenticazione con un registro.", + "loc.input.label.azureSubscriptionEndpoint": "Sottoscrizione di Azure", + "loc.input.help.azureSubscriptionEndpoint": "Consente di selezionare una sottoscrizione di Azure", + "loc.input.label.azureContainerRegistry": "Registro contenitori di Azure", + "loc.input.help.azureContainerRegistry": "Consente di selezionare un registro contenitori di Azure", + "loc.input.label.dockerComposeFile": "File Docker Compose", + "loc.input.help.dockerComposeFile": "Percorso del file di Docker Compose primario da usare.", + "loc.input.label.additionalDockerComposeFiles": "File Docker Compose aggiuntivi", + "loc.input.help.additionalDockerComposeFiles": "File di Docker Compose aggiuntivi da compilare con il file di Docker Compose primario. I percorsi relativi vengono risolti in base alla directory che contiene il file di Docker Compose primario. Se un file specificato non viene trovato, viene ignorato. Specificare ogni percorso di file su una nuova riga.", + "loc.input.label.dockerComposeFileArgs": "Variabili di ambiente", + "loc.input.help.dockerComposeFileArgs": "Variabili di ambiente da impostare durante il comando. Specificare ogni coppia nome=valore su una nuova riga.", + "loc.input.label.projectName": "Nome progetto", + "loc.input.help.projectName": "Nome del progetto usato per l'assegnazione di nomi predefiniti a immagini e contenitori.", + "loc.input.label.qualifyImageNames": "Qualifica nomi delle immagini", + "loc.input.help.qualifyImageNames": "Qualifica i nomi delle immagini per i servizi compilati con il nome host della connessione al servizio Registro Docker se non viene specificato diversamente.", + "loc.input.label.action": "Azione", + "loc.input.help.action": "Consente di selezionare un'azione di Docker Compose.", + "loc.input.label.additionalImageTags": "Tag immagine aggiuntivi", + "loc.input.help.additionalImageTags": "Tag aggiuntivi per le immagini Docker da compilare o di cui effettuare il push.", + "loc.input.label.includeSourceTags": "Includi tag di origine", + "loc.input.help.includeSourceTags": "Include i tag GIT quando si compila o si effettua il push delle immagini Docker.", + "loc.input.label.includeLatestTag": "Includi tag latest", + "loc.input.help.includeLatestTag": "Include il tag 'latest' quando si compila o si effettua il push delle immagini Docker.", + "loc.input.label.buildImages": "Compila immagini", + "loc.input.help.buildImages": "Consente di compilare le immagini prima di avviare i contenitori dei servizi.", + "loc.input.label.serviceName": "Nome del servizio", + "loc.input.help.serviceName": "Nome del servizio specifico da eseguire.", + "loc.input.label.containerName": "Nome contenitore", + "loc.input.help.containerName": "Nome del contenitore dei servizi specifico da eseguire.", + "loc.input.label.ports": "Porte", + "loc.input.help.ports": "Porte nel contenitore dei servizi specifico da pubblicare nell'host. Specificare ogni binding porta-host:porta-contenitore su una nuova riga.", + "loc.input.label.workDir": "Directory di lavoro", + "loc.input.help.workDir": "Directory di lavoro per il contenitore dei servizi specifico.", + "loc.input.label.entrypoint": "Override del punto di ingresso", + "loc.input.help.entrypoint": "Esegue l'override del punto di ingresso predefinito per il contenitore dei servizi specifico.", + "loc.input.label.containerCommand": "Comando", + "loc.input.help.containerCommand": "Comando da eseguire nel contenitore dei servizi specifico. Se ad esempio l'immagine contiene un'applicazione Web Python Flask semplice, è possibile specificare 'python app.py' per avviarla.", + "loc.input.label.detached": "Esegui in background", + "loc.input.help.detached": "Consente di eseguire i contenitori dei servizi in background.", + "loc.input.label.abortOnContainerExit": "Interrompi alla chiusura del contenitore", + "loc.input.help.abortOnContainerExit": "Arresta tutti i contenitori in caso di chiusura di un qualsiasi contenitore.", + "loc.input.label.imageDigestComposeFile": "File digest dell'immagine di Docker Compose", + "loc.input.help.imageDigestComposeFile": "Percorso di un file di Docker Compose che viene creato e popolato con i digest completi del repository delle immagini dell'immagine Docker di ogni servizio.", + "loc.input.label.removeBuildOptions": "Rimuovi opzioni di compilazione", + "loc.input.help.removeBuildOptions": "Rimuove le opzioni di compilazione dal file Docker Compose di output.", + "loc.input.label.baseResolveDirectory": "Directory di risoluzione di base", + "loc.input.help.baseResolveDirectory": "Directory di base da cui devono essere risolti i percorsi relativi nel file di Docker Compose di output.", + "loc.input.label.outputDockerComposeFile": "File Docker Compose di output", + "loc.input.help.outputDockerComposeFile": "Percorso di un file Docker Compose di output.", + "loc.input.label.dockerComposeCommand": "Comando", + "loc.input.help.dockerComposeCommand": "Comando di Docker Compose da eseguire, con relativi argomenti, ad esempio 'rm --all' per rimuovere tutti i contenitori dei servizi arrestati.", + "loc.input.label.arguments": "Argomenti", + "loc.input.help.arguments": "Opzioni dei comandi di Docker Compose. Esempio:
Per il comando build:
--pull--compress--parallel", + "loc.input.label.dockerHostEndpoint": "Connessione al servizio host Docker", + "loc.input.help.dockerHostEndpoint": "Consente di selezionare una connessione al servizio host Docker. Per impostazione predefinita, viene usato l'host dell'agente.", + "loc.input.label.nopIfNoDockerComposeFile": "Non eseguire operazioni se il file Docker Compose non esiste", + "loc.input.help.nopIfNoDockerComposeFile": "Se il file Docker Compose non esiste, ignora questa attività. Questa opzione è utile quando l'attività offre un comportamento facoltativo basato sull'esistenza di un file Docker Compose nel repository.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Richiedi file Docker Compose aggiuntivi", + "loc.input.help.requireAdditionalDockerComposeFiles": "Produce un errore se i file Docker Compose aggiuntivi non esistono. Sostituisce il comportamento predefinito, in cui un file inesistente viene ignorato.", + "loc.input.label.cwd": "Directory di lavoro", + "loc.input.help.cwd": "Directory di lavoro per il comando di Docker Compose.", + "loc.input.label.dockerComposePath": "Percorso dell'eseguibile Docker Compose", + "loc.input.help.dockerComposePath": "Eseguibile Docker Compose che verrà usato se si specifica il percorso", + "loc.messages.ConnectingToDockerHost": "La variabile DOCKER_HOST è impostata. Docker proverà a connettersi all'host Docker: %s", + "loc.messages.ContainerPatternFound": "Il criterio è stato trovato nel parametro filepath di Docker Compose", + "loc.messages.ContainerPatternNotFound": "Non è stato trovato alcun criterio nel parametro filepath di Docker Compose", + "loc.messages.ContainerDockerFileNotFound": "Non è stato trovato alcun file Docker corrispondente a %s.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "Il file Docker Compose aggiuntivo %s non esiste.", + "loc.messages.CantWriteDataToFile": "Non è possibile scrivere dati nel file %s. Errore: %s", + "loc.messages.DockerHostVariableWarning": "La variabile DOCKER_HOST è impostata. Assicurarsi che il daemon Docker sia in esecuzione in: %s", + "loc.messages.NoDataWrittenOnFile": "Non sono stati scritti dati nel file %s", + "loc.messages.FileContentSynced": "Il contenuto del file è stato sincronizzato con il disco. Il contenuto è %s.", + "loc.messages.ImageNameWithoutTag": "Il nome dell'immagine non è specificato con tag. Verrà eseguito il push di tutti i tag dell'immagine specificata.", + "loc.messages.WritingDockerConfigToTempFile": "Scrittura della configurazione di Docker nel file temporaneo. Percorso del file: %s. Configurazione di Docker: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/ja-JP/resources.resjson new file mode 100644 index 000000000000..bd9d1ac65397 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/ja-JP/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[このタスクの詳細を表示](https://go.microsoft.com/fwlink/?linkid=848006)、または [Docker Compose のドキュメントを参照](https://docs.docker.com/)", + "loc.description": "複数コンテナーの Docker アプリケーションをビルド、プッシュ、実行します。タスクは、Docker または Azure Container Registry で使用できます。", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "詳細設定のオプション", + "loc.input.label.containerregistrytype": "コンテナー レジストリの種類", + "loc.input.help.containerregistrytype": "コンテナー レジストリの種類を選択します。", + "loc.input.label.dockerRegistryEndpoint": "Docker レジストリ サービス接続", + "loc.input.help.dockerRegistryEndpoint": "Docker レジストリ サービス接続を選択します。レジストリを使って認証する必要のあるコマンドの場合に必要です。", + "loc.input.label.azureSubscriptionEndpoint": "Azure サブスクリプション", + "loc.input.help.azureSubscriptionEndpoint": "Azure サブスクリプションを選択します", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Azure Container Registry を選択します", + "loc.input.label.dockerComposeFile": "Docker Compose ファイル", + "loc.input.help.dockerComposeFile": "使用するプライマリ Docker Compose ファイルへのパスです。", + "loc.input.label.additionalDockerComposeFiles": "追加の Docker Compose ファイル", + "loc.input.help.additionalDockerComposeFiles": "プライマリ Docker Compose ファイルに結合する追加の Docker Compose ファイルです。相対パスは、プライマリ Docker Compose ファイルの入っているディレクトリを基準にして解決されます。指定されたファイルが見つからない場合、そのファイルは無視されます。それぞれのファイル パスを新しい行ごとに指定します。", + "loc.input.label.dockerComposeFileArgs": "環境変数", + "loc.input.help.dockerComposeFileArgs": "コマンドの実行中に設定する環境変数です。名前=値のペアを新しい行ごとに指定します。", + "loc.input.label.projectName": "プロジェクト名 ", + "loc.input.help.projectName": "イメージとコンテナーの既定の名前付けで使うプロジェクト名です。", + "loc.input.label.qualifyImageNames": "イメージ名を修飾", + "loc.input.help.qualifyImageNames": "特に指定されていない場合、ビルド サービスのイメージ名を Docker レジストリ サービス接続のホスト名で修飾します。", + "loc.input.label.action": "アクション", + "loc.input.help.action": "Docker Compose アクションを選択します。", + "loc.input.label.additionalImageTags": "追加のイメージ タグ", + "loc.input.help.additionalImageTags": "ビルドまたはプッシュされている Docker イメージの追加タグです。", + "loc.input.label.includeSourceTags": "ソース タグを含める", + "loc.input.help.includeSourceTags": "Docker イメージをビルドまたはプッシュするときに、Git タグを組み込みます。", + "loc.input.label.includeLatestTag": "最終のタグを含める", + "loc.input.help.includeLatestTag": "Docker イメージをビルドまたはプッシュするときに、'latest' タグを組み込みます。", + "loc.input.label.buildImages": "イメージのビルド", + "loc.input.help.buildImages": "サービス コンテナーを開始する前に、イメージをビルドします。", + "loc.input.label.serviceName": "サービス名", + "loc.input.help.serviceName": "実行する特定のサービスの名前です。", + "loc.input.label.containerName": "コンテナー名 ", + "loc.input.help.containerName": "実行する特定のサービス コンテナーの名前です。", + "loc.input.label.ports": "ポート", + "loc.input.help.ports": "ホストに発行する特定のサービス コンテナーのポートです。ホスト ポート:コンテナー ポートのバインドを新しい行ごとに指定します。", + "loc.input.label.workDir": "作業ディレクトリ", + "loc.input.help.workDir": "特定のサービス コンテナーの作業ディレクトリです。", + "loc.input.label.entrypoint": "エントリ ポイントのオーバーライド", + "loc.input.help.entrypoint": "特定のサービス コンテナーの既定のエントリ ポイントをオーバーライドします。", + "loc.input.label.containerCommand": "コマンド", + "loc.input.help.containerCommand": "特定のサービス コンテナー内で実行するコマンドです。たとえば、イメージに単純な Python Flask Web アプリケーションが含まれている場合は、'python app.py' を指定して Web アプリケーションを起動することができます。", + "loc.input.label.detached": "バックグラウンドで実行", + "loc.input.help.detached": "バックグラウンドでサービス コンテナーを実行します。", + "loc.input.label.abortOnContainerExit": "コンテナーの終了時に中止する", + "loc.input.help.abortOnContainerExit": "いずれかのコンテナーが終了したとき、すべてのコンテナーを停止します。", + "loc.input.label.imageDigestComposeFile": "イメージ ダイジェスト Compose ファイル", + "loc.input.help.imageDigestComposeFile": "各サービスの Docker イメージのフル イメージ リポジトリのダイジェストを設定して作成された Docker Compose ファイルへのパスです。", + "loc.input.label.removeBuildOptions": "ビルド オプションの削除", + "loc.input.help.removeBuildOptions": "出力 Docker Compose ファイルからビルド オプションを削除します。", + "loc.input.label.baseResolveDirectory": "解決用のベース ディレクトリ", + "loc.input.help.baseResolveDirectory": "出力 Docker Compose ファイル内の相対パスを解決するためのベース ディレクトリです。", + "loc.input.label.outputDockerComposeFile": "出力 Docker Compose ファイル", + "loc.input.help.outputDockerComposeFile": "出力 Docker Compose ファイルへのパスです。", + "loc.input.label.dockerComposeCommand": "コマンド", + "loc.input.help.dockerComposeCommand": "実行する Docker Compose コマンド (引数付き) です。たとえば、すべての停止しているサービス コンテナーを削除するには、'rm --all' と入力します。", + "loc.input.label.arguments": "引数", + "loc.input.help.arguments": "Docker Compose コマンドのオプション。例:
build コマンドの場合:
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker ホスト サービス接続", + "loc.input.help.dockerHostEndpoint": "Docker ホスト サービス接続を選択します。既定では、エージェントのホストになります。", + "loc.input.label.nopIfNoDockerComposeFile": "Docker Compose ファイルが存在しなければ操作なし", + "loc.input.help.nopIfNoDockerComposeFile": "Docker Compose ファイルが存在しない場合に、このタスクをスキップします。これは、リポジトリ内に Docker Compose ファイルがあるかどうかに基づいて省略可能な動作を提供するタスクの場合に役立ちます。", + "loc.input.label.requireAdditionalDockerComposeFiles": "追加の Docker Compose ファイルを必須にする", + "loc.input.help.requireAdditionalDockerComposeFiles": "追加の Docker Compose ファイルが存在しない場合に、エラーを生成します。これは、ファイルが存在しない場合にそのファイルを無視するという既定の動作をオーバーライドします。", + "loc.input.label.cwd": "作業ディレクトリ", + "loc.input.help.cwd": "Docker Compose コマンドの作業ディレクトリです。", + "loc.input.label.dockerComposePath": "Docker Compose 実行可能ファイルのパス", + "loc.input.help.dockerComposePath": "パスが指定されている場合、この Docker-compose 実行可能ファイルが使用されます", + "loc.messages.ConnectingToDockerHost": "DOCKER_HOST 変数が設定されています。Docker により、Docker ホスト %s への接続が試みられます", + "loc.messages.ContainerPatternFound": "docker compose filepath パラメーターにパターンが見つかりました", + "loc.messages.ContainerPatternNotFound": "docker compose filepath パラメーターにパターンが見つかりませんでした ", + "loc.messages.ContainerDockerFileNotFound": "%s に一致する Docker ファイルは見つかりませんでした。", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "追加の Docker Compose ファイル %s は存在しません。", + "loc.messages.CantWriteDataToFile": "ファイル %s にデータを書き込めません。エラー: %s", + "loc.messages.DockerHostVariableWarning": "DOCKER_HOST 変数が設定されています。Docker デーモンが %s で実行されていることをご確認ください", + "loc.messages.NoDataWrittenOnFile": "ファイル %s にデータが書き込まれませんでした", + "loc.messages.FileContentSynced": "ファイルの内容がディスクに同期されました。内容は %s です。", + "loc.messages.ImageNameWithoutTag": "イメージ名がタグに指定されていません。指定されたイメージのすべてのタグをプッシュしています。", + "loc.messages.WritingDockerConfigToTempFile": "Docker 構成を一時ファイルに書き込んでいます。ファイル パス: %s、Docker 構成: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/ko-KR/resources.resjson new file mode 100644 index 000000000000..97ab46a75920 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/ko-KR/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[이 작업에 대한 자세한 정보](https://go.microsoft.com/fwlink/?linkid=848006) 또는 [Docker Compose 설명서 참조](https://docs.docker.com/)", + "loc.description": "다중 컨테이너 Docker 응용 프로그램을 빌드, 푸시 또는 실행합니다. Docker 또는 Azure Container Registry에서 작업을 사용할 수 있습니다.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "고급 옵션", + "loc.input.label.containerregistrytype": "Container Registry 유형", + "loc.input.help.containerregistrytype": "Container Registry 유형을 선택하세요.", + "loc.input.label.dockerRegistryEndpoint": "Docker 레지스트리 서비스 연결", + "loc.input.help.dockerRegistryEndpoint": "Docker 레지스트리 서비스 연결을 선택합니다. 레지스트리를 사용하여 인증해야 하는 명령에 필요합니다.", + "loc.input.label.azureSubscriptionEndpoint": "Azure 구독", + "loc.input.help.azureSubscriptionEndpoint": "Azure 구독을 선택합니다.", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "Azure Container Registry를 선택합니다.", + "loc.input.label.dockerComposeFile": "Docker Compose 파일", + "loc.input.help.dockerComposeFile": "사용할 기본 Docker Compose 파일에 대한 경로입니다.", + "loc.input.label.additionalDockerComposeFiles": "추가 Docker Compose 파일", + "loc.input.help.additionalDockerComposeFiles": "기본 Docker Compose 파일과 결합되어야 하는 추가 Docker Compose 파일입니다. 상대 경로는 기본 Docker Compose 파일을 포함하는 디렉터리를 기준으로 확인됩니다. 지정된 파일이 없으면 무시됩니다. 새 줄에 각 파일 경로를 지정하세요.", + "loc.input.label.dockerComposeFileArgs": "환경 변수", + "loc.input.help.dockerComposeFileArgs": "명령하는 동안 설정해야 할 환경 변수입니다. 새 줄에서 각 이름=값 쌍을 지정하세요.", + "loc.input.label.projectName": "프로젝트 이름", + "loc.input.help.projectName": "이미지 및 컨테이너의 기본 이름 지정에 사용되는 프로젝트 이름입니다.", + "loc.input.label.qualifyImageNames": "이미지 이름 한정", + "loc.input.help.qualifyImageNames": "달리 지정되지 않은 경우 Docker 레지스트리 서비스 연결의 호스트 이름으로 빌드된 서비스의 이미지 이름을 한정합니다.", + "loc.input.label.action": "작업", + "loc.input.help.action": "Docker Compose 작업을 선택합니다.", + "loc.input.label.additionalImageTags": "추가 이미지 태그", + "loc.input.help.additionalImageTags": "빌드 또는 푸시되고 있는 Docker 이미지에 대한 추가 태그입니다.", + "loc.input.label.includeSourceTags": "소스 태그 포함", + "loc.input.help.includeSourceTags": "Docker 이미지를 빌드하거나 푸시할 때 Git 태그를 포함합니다.", + "loc.input.label.includeLatestTag": "최신 태그 포함", + "loc.input.help.includeLatestTag": "Docker 이미지를 빌드하거나 푸시할 때 '최신' 태그를 포함합니다.", + "loc.input.label.buildImages": "빌드 이미지", + "loc.input.help.buildImages": "서비스 컨테이너를 시작하기 전에 이미지를 빌드합니다.", + "loc.input.label.serviceName": "서비스 이름", + "loc.input.help.serviceName": "실행할 특정 서비스의 이름입니다.", + "loc.input.label.containerName": "컨테이너 이름", + "loc.input.help.containerName": "실행할 특정 서비스 컨테이너의 이름입니다.", + "loc.input.label.ports": "포트", + "loc.input.help.ports": "호스트에 게시할 특정 서비스 컨테이너의 포트입니다. 새 줄에 각 host-port:container-port 바인딩을 지정하세요.", + "loc.input.label.workDir": "작업 디렉터리", + "loc.input.help.workDir": "특정 서비스 컨테이너에 대한 작업 디렉터리입니다.", + "loc.input.label.entrypoint": "진입점 재정의", + "loc.input.help.entrypoint": "특정 서비스 컨테이너에 대한 기본 진입점을 재정의합니다.", + "loc.input.label.containerCommand": "명령", + "loc.input.help.containerCommand": "특정 서비스 컨테이너에서 실행할 명령입니다. 예를 들어 이미지에 단순한 Python Flask 웹 응용 프로그램이 포함되어 있는 경우에는 웹 응용 프로그램 실행을 위해 'python app.py'를 지정할 수 있습니다.", + "loc.input.label.detached": "백그라운드에서 실행", + "loc.input.help.detached": "서비스 컨테이너를 백그라운드에서 실행합니다.", + "loc.input.label.abortOnContainerExit": "컨테이너 종료 시 중단", + "loc.input.help.abortOnContainerExit": "컨테이너가 있는 경우 모든 컨테이너를 중지합니다.", + "loc.input.label.imageDigestComposeFile": "이미지 다이제스트 Compose 파일", + "loc.input.help.imageDigestComposeFile": "각 서비스의 Docker 이미지의 전체 이미지 리포지토리 다이제스트로 채워져 만들어진 Docker Compose 파일에 대한 경로입니다.", + "loc.input.label.removeBuildOptions": "빌드 옵션 제거", + "loc.input.help.removeBuildOptions": "출력 Docker Compose 파일에서 빌드 옵션을 제거합니다.", + "loc.input.label.baseResolveDirectory": "기본 확인 디렉터리", + "loc.input.help.baseResolveDirectory": "출력 Docker Compose 파일의 상대 경로를 확인해야 할 기본 디렉터리입니다.", + "loc.input.label.outputDockerComposeFile": "출력 Docker Compose 파일", + "loc.input.help.outputDockerComposeFile": "출력 Docker Compose 파일에 대한 경로입니다.", + "loc.input.label.dockerComposeCommand": "명령", + "loc.input.help.dockerComposeCommand": "인수와 함께 실행할 Docker Compose 명령입니다. 예를 들어, 모든 중지된 서비스 컨테이너를 제거하려는 경우 'rm --all'입니다.", + "loc.input.label.arguments": "인수", + "loc.input.help.arguments": "Docker Compose 명령 옵션. 예:
빌드 명령의 경우
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker 호스트 서비스 연결", + "loc.input.help.dockerHostEndpoint": "Docker 호스트 서비스 연결을 선택합니다. 기본값은 에이전트의 호스트로 지정됩니다.", + "loc.input.label.nopIfNoDockerComposeFile": "Docker Compose 파일이 없는 경우 no-op", + "loc.input.help.nopIfNoDockerComposeFile": "Docker Compose 파일이 없는 경우 이 작업을 건너뜁니다. 작업이 리포지토리에 Docker Compose 파일이 있는지 여부에 따라 선택적 동작을 제공하는 경우에 유용합니다.", + "loc.input.label.requireAdditionalDockerComposeFiles": "추가 Docker Compose 파일 필요", + "loc.input.help.requireAdditionalDockerComposeFiles": "추가 Docker Compose 파일이 없는 경우 오류가 발생합니다. 이로 인해, 파일이 없는 경우 해당 파일을 무시하는 기본 동작이 재정의됩니다.", + "loc.input.label.cwd": "작업 디렉터리", + "loc.input.help.cwd": "Docker Compose 명령에 대한 작업 디렉터리입니다.", + "loc.input.label.dockerComposePath": "Docker Compose 실행 파일 경로", + "loc.input.help.dockerComposePath": "경로가 제공되는 경우 이 Docker Compose 실행 파일이 사용됩니다.", + "loc.messages.ConnectingToDockerHost": "DOCKER_HOST 변수가 설정되었습니다. Docker는 Docker 호스트 %s에 연결하려고 합니다.", + "loc.messages.ContainerPatternFound": "Docker Compose filepath 매개 변수에 패턴 있음", + "loc.messages.ContainerPatternNotFound": "Docker Compose filepath 매개 변수에 패턴 없음", + "loc.messages.ContainerDockerFileNotFound": "%s과(와) 일치하는 Docker 파일이 없습니다.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "추가 Docker Compose 파일 %s이(가) 없습니다.", + "loc.messages.CantWriteDataToFile": "데이터를 %s 파일에 쓸 수 없습니다. 오류: %s", + "loc.messages.DockerHostVariableWarning": "DOCKER_HOST 변수가 설정되었습니다. Docker 디먼 %s이(가) 실행 중인지 확인하세요.", + "loc.messages.NoDataWrittenOnFile": "데이터를 %s 파일에 쓰지 않았습니다.", + "loc.messages.FileContentSynced": "파일 콘텐츠를 디스크에 동기화했습니다. 콘텐츠는 %s입니다.", + "loc.messages.ImageNameWithoutTag": "태그로 지정되지 않은 이미지 이름으로, 지정한 이미지의 모든 태그를 푸시합니다.", + "loc.messages.WritingDockerConfigToTempFile": "임시 파일에 Docker 구성을 쓰는 중입니다. 파일 경로: %s, Docker 구성: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/ru-RU/resources.resjson new file mode 100644 index 000000000000..0014b3bafb75 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/ru-RU/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[См. дополнительные сведения об этой задаче](https://go.microsoft.com/fwlink/?linkid=848006) или [документацию по Docker Compose](https://docs.docker.com/)", + "loc.description": "Создание, отправка или запуск многоконтейнерных приложений Docker. Задачу можно использовать в Docker или реестре контейнеров Azure.", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "Дополнительные параметры", + "loc.input.label.containerregistrytype": "Тип реестра контейнеров", + "loc.input.help.containerregistrytype": "Выберите тип реестра контейнеров.", + "loc.input.label.dockerRegistryEndpoint": "Подключение к службе реестра Docker", + "loc.input.help.dockerRegistryEndpoint": "Выберите подключение к службе реестра Docker. Это требуется для команд, для выполнения которых необходимо пройти проверку подлинности в реестре.", + "loc.input.label.azureSubscriptionEndpoint": "Подписка Azure", + "loc.input.help.azureSubscriptionEndpoint": "Выберите подписку Azure", + "loc.input.label.azureContainerRegistry": "Реестр контейнеров Azure", + "loc.input.help.azureContainerRegistry": "Выберите реестр контейнеров Azure", + "loc.input.label.dockerComposeFile": "Файл Docker Compose", + "loc.input.help.dockerComposeFile": "Путь к основному файлу Docker Compose для использования.", + "loc.input.label.additionalDockerComposeFiles": "Дополнительные файлы Docker Compose", + "loc.input.help.additionalDockerComposeFiles": "Дополнительные файлы Docker Compose для объединения с основным файлом Docker Compose. Относительные пути разрешаются относительно каталога, в котором содержится основной файл Docker Compose. Если указанный файл не найден, он пропускается. Каждый путь к файлу следует указывать в новой строке.", + "loc.input.label.dockerComposeFileArgs": "Переменные среды", + "loc.input.help.dockerComposeFileArgs": "Переменные среды для задания во время выполнения команды. Каждую пару \"имя=значение\" следует указывать в новой строке.", + "loc.input.label.projectName": "Имя проекта", + "loc.input.help.projectName": "Имя проекта, которое по умолчанию используется для именования образов и контейнеров.", + "loc.input.label.qualifyImageNames": "Определить имена образов", + "loc.input.help.qualifyImageNames": "Определение имен образов для созданных служб с помощью имени узла подключения к службе реестра Docker, если не указано иное.", + "loc.input.label.action": "Действие", + "loc.input.help.action": "Выберите действие Docker Compose.", + "loc.input.label.additionalImageTags": "Дополнительные теги образов", + "loc.input.help.additionalImageTags": "Выполняется создание или отправка дополнительных тегов для образов Docker.", + "loc.input.label.includeSourceTags": "Включить исходные теги", + "loc.input.help.includeSourceTags": "При создании или отправке образов Docker следует включить теги Git.", + "loc.input.label.includeLatestTag": "Включить последний тег", + "loc.input.help.includeLatestTag": "При создании или отправке образов Docker следует включить тег \"latest\".", + "loc.input.label.buildImages": "Образы сборки", + "loc.input.help.buildImages": "Создайте образы, прежде чем запускать контейнеры службы.", + "loc.input.label.serviceName": "Имя службы", + "loc.input.help.serviceName": "Имя определенной службы для выполнения.", + "loc.input.label.containerName": "Имя контейнера", + "loc.input.help.containerName": "Имя контейнера определенной службы для выполнения.", + "loc.input.label.ports": "Порты", + "loc.input.help.ports": "Порты в контейнере определенной службы для публикации на узле. Каждую привязку \"порт узла:порт контейнера\" следует указывать в новой строке.", + "loc.input.label.workDir": "Рабочий каталог", + "loc.input.help.workDir": "Рабочий каталог для контейнера определенной службы.", + "loc.input.label.entrypoint": "Переопределение точки входа", + "loc.input.help.entrypoint": "Переопределение точки входа по умолчанию для контейнера указанной службы.", + "loc.input.label.containerCommand": "Команда", + "loc.input.help.containerCommand": "Команда для выполнения в контейнере определенной службы. Например, если в образе имеется простое веб-приложение Python Flask, можно указать параметр \"python app.py\" для запуска веб-приложения.", + "loc.input.label.detached": "Запустить в фоновом режиме", + "loc.input.help.detached": "Запуск контейнеров службы в фоновом режиме.", + "loc.input.label.abortOnContainerExit": "Прервать на выходе контейнера", + "loc.input.help.abortOnContainerExit": "Остановить все контейнеры при выходе любого контейнера.", + "loc.input.label.imageDigestComposeFile": "Файл Docker Compose образа", + "loc.input.help.imageDigestComposeFile": "Путь к созданному файлу Docker Compose, в который внесены все дайджесты репозитория образа Docker каждой службы.", + "loc.input.label.removeBuildOptions": "Удалить параметры сборки", + "loc.input.help.removeBuildOptions": "Удаление параметров сборки из выходного файла Docker Compose.", + "loc.input.label.baseResolveDirectory": "Базовый каталог для разрешения", + "loc.input.help.baseResolveDirectory": "Базовый каталог, из которого следует выполнять разрешение относительных путей в выходном файле Docker Compose.", + "loc.input.label.outputDockerComposeFile": "Выходной файл Docker Compose", + "loc.input.help.outputDockerComposeFile": "Путь к выходному файлу Docker Compose.", + "loc.input.label.dockerComposeCommand": "Команда", + "loc.input.help.dockerComposeCommand": "Команда Docker Compose для выполнения (с аргументами). Например, \"rm --all\" для удаления всех остановленных контейнеров службы.", + "loc.input.label.arguments": "Аргументы", + "loc.input.help.arguments": "Параметры команды Docker Compose. Пример
Для команды сборки
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Подключение к службе узла Docker", + "loc.input.help.dockerHostEndpoint": "Выберите подключение к службе узла Docker. По умолчанию используется узел агента.", + "loc.input.label.nopIfNoDockerComposeFile": "Холостая команда, если файлы Docker Compose отсутствуют", + "loc.input.help.nopIfNoDockerComposeFile": "Если файл Docker Compose не существует, пропустите эту задачу. Это полезно в случаях, когда в этой задаче предлагается дополнительное поведение на основе сведений о существовании в репозитории файла Docker Compose.", + "loc.input.label.requireAdditionalDockerComposeFiles": "Требовать дополнительные файлы Docker Compose", + "loc.input.help.requireAdditionalDockerComposeFiles": "Выдает ошибку, если дополнительные файлы Docker Compose не существуют. Данный параметр переопределяет поведение по умолчанию, при котором отсутствующий файл пропускается.", + "loc.input.label.cwd": "Рабочий каталог", + "loc.input.help.cwd": "Рабочий каталог для команды Docker Compose.", + "loc.input.label.dockerComposePath": "Путь к исполняемому файлу Docker Compose", + "loc.input.help.dockerComposePath": "Этот исполняемый файл docker-compose будет использоваться, если указан путь.", + "loc.messages.ConnectingToDockerHost": "Задана переменная DOCKER_HOST. Docker попытается подключиться к узлу Docker: %s", + "loc.messages.ContainerPatternFound": "Обнаружен шаблон в параметре filepath в Docker Compose", + "loc.messages.ContainerPatternNotFound": "Не найден шаблон в параметре filepath в Docker Compose", + "loc.messages.ContainerDockerFileNotFound": "Файл Docker, соответствующий %s, не найден.", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "Дополнительный файл Docker Compose %s не существует.", + "loc.messages.CantWriteDataToFile": "Не удается записать данные в файл %s. Ошибка: %s", + "loc.messages.DockerHostVariableWarning": "Задана переменная DOCKER_HOST. Убедитесь, что управляющая программа Docker запущена на: %s", + "loc.messages.NoDataWrittenOnFile": "Данные не были записаны в файл %s", + "loc.messages.FileContentSynced": "Содержимое файла синхронизировано с диском. Содержимое: %s.", + "loc.messages.ImageNameWithoutTag": "Имя образа указано без тега; передаются все теги указанного образа.", + "loc.messages.WritingDockerConfigToTempFile": "Запись конфигурации Docker во временный файл. Путь к файлу: %s, конфигурация Docker: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/zh-CN/resources.resjson new file mode 100644 index 000000000000..c99ec75eb2fd --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/zh-CN/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[详细了解此任务](https://go.microsoft.com/fwlink/?linkid=848006)或[参阅 Docker Compose 文档](https://docs.docker.com/)", + "loc.description": "生成、推送或运行多容器 Docker 应用程序。任务可与 Docker 或 Azure 容器注册表一起使用。", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "高级选项", + "loc.input.label.containerregistrytype": "容器注册表类型", + "loc.input.help.containerregistrytype": "选择容器注册表类型。", + "loc.input.label.dockerRegistryEndpoint": "Docker 注册表服务连接", + "loc.input.help.dockerRegistryEndpoint": "选择 Docker 注册表服务连接。这对于需要执行注册表身份验证的命令是必需的。", + "loc.input.label.azureSubscriptionEndpoint": "Azure 订阅", + "loc.input.help.azureSubscriptionEndpoint": "选择 Azure 订阅", + "loc.input.label.azureContainerRegistry": "Azure 容器注册表", + "loc.input.help.azureContainerRegistry": "选择 Azure 容器注册表", + "loc.input.label.dockerComposeFile": "Docker Compose 文件", + "loc.input.help.dockerComposeFile": "要使用的主 Docker Compose 文件的路径。", + "loc.input.label.additionalDockerComposeFiles": "其他 Docker Compose 文件", + "loc.input.help.additionalDockerComposeFiles": "要与主 Docker Compose 文件合并的其他 Docker Compose 文件。相对包含主 Docker Compose 文件的目录解析相对路径。如果找不到指定的文件,则忽略。在新的一行指定每个文件路径。", + "loc.input.label.dockerComposeFileArgs": "环境变量", + "loc.input.help.dockerComposeFileArgs": "执行命令期间要设置的环境变量。在新的一行指定每个名称=值对。", + "loc.input.label.projectName": "项目名", + "loc.input.help.projectName": "默认用于命名映像和容器的项目名称。", + "loc.input.label.qualifyImageNames": "指定映像名", + "loc.input.help.qualifyImageNames": "如果没有另外指定,将生成服务的映像名限定为 Docker 注册表服务连接的主机名。", + "loc.input.label.action": "操作", + "loc.input.help.action": "选择 Docker Compose 操作。", + "loc.input.label.additionalImageTags": "其他映像标记", + "loc.input.help.additionalImageTags": "正在生成或推送的 Docker 映像的其他标记。", + "loc.input.label.includeSourceTags": "包括源标记", + "loc.input.help.includeSourceTags": "生成或推送 Docker 映像时包括 Git 标记。", + "loc.input.label.includeLatestTag": "包括最近标记", + "loc.input.help.includeLatestTag": "生成或推送 Docker 映像时包括 \"latest\" 标记。", + "loc.input.label.buildImages": "生成映像", + "loc.input.help.buildImages": "在启动服务容器之前生成映像。", + "loc.input.label.serviceName": "服务名称", + "loc.input.help.serviceName": "要运行的特定服务的名称。", + "loc.input.label.containerName": "容器名", + "loc.input.help.containerName": "要运行的特定服务容器的名称。", + "loc.input.label.ports": "端口", + "loc.input.help.ports": "特定服务容器中要发布到主机的端口。在新的一行指定每个 host-port:container-port 绑定。", + "loc.input.label.workDir": "工作目录", + "loc.input.help.workDir": "特定服务容器的工作目录。", + "loc.input.label.entrypoint": "入口点替代", + "loc.input.help.entrypoint": "替代特定服务容器的默认入口点。", + "loc.input.label.containerCommand": "命令", + "loc.input.help.containerCommand": "要在特定服务容器中运行的命令。例如,如果映像包含一个简单的 Python Flask Web 应用程序,则可以将“python app.py”指定为启动 Web 应用程序。", + "loc.input.label.detached": "后台运行", + "loc.input.help.detached": "后台运行服务容器。", + "loc.input.label.abortOnContainerExit": "容器退出时中止", + "loc.input.help.abortOnContainerExit": "存在任何容器时停止所有容器。", + "loc.input.label.imageDigestComposeFile": "映像摘要 Compose 文件", + "loc.input.help.imageDigestComposeFile": "使用每个服务的 Docker 映像的完全映像存储库摘要创建和填充的 Docker Compose 文件的路径。", + "loc.input.label.removeBuildOptions": "删除生成选项", + "loc.input.help.removeBuildOptions": "从输出 Docker Compose 文件中删除生成选项。", + "loc.input.label.baseResolveDirectory": "基础映像解析目录", + "loc.input.help.baseResolveDirectory": "解析输出 Docker Compose 文件的相对路径应该针对的基目录。", + "loc.input.label.outputDockerComposeFile": "输出 Docker Compose 文件", + "loc.input.help.outputDockerComposeFile": "输出 Docker Compose 文件的路径。", + "loc.input.label.dockerComposeCommand": "命令", + "loc.input.help.dockerComposeCommand": "要执行的带参数的 Docker Compose 命令。例如,可以使用 \"rm --all\" 删除所有已停止的服务容器。", + "loc.input.label.arguments": "参数", + "loc.input.help.arguments": "Docker Compose 命令选项。例如:
对于生成命令,选项有
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker 主机服务连接", + "loc.input.help.dockerHostEndpoint": "选择 Docker 主机服务连接。默认为代理的主机。", + "loc.input.label.nopIfNoDockerComposeFile": "如果没有 Docker Compose 文件,则无操作", + "loc.input.help.nopIfNoDockerComposeFile": "如果 Docker Compose 文件不存在,则跳过此任务。当任务根据存储库中是否存在 Docker Compose 文件来提供可选行为时,这会非常有用。", + "loc.input.label.requireAdditionalDockerComposeFiles": "需要其他 Docker Compose 文件", + "loc.input.help.requireAdditionalDockerComposeFiles": "不存在其他 Docker Compose 文件时发生错误。这会替代如果文件不存在则忽略该文件的默认行为。", + "loc.input.label.cwd": "工作目录", + "loc.input.help.cwd": "Docker Compose 命令的工作目录。", + "loc.input.label.dockerComposePath": "Docker Compose 可执行文件路径", + "loc.input.help.dockerComposePath": "如果提供了路径,则将使用此 docker-compose 可执行文件", + "loc.messages.ConnectingToDockerHost": "已设置 DOCKER_HOST 变量。Docker 将尝试连接到 Docker 主机: %s", + "loc.messages.ContainerPatternFound": "Docker Compose 文件路径参数中找到的模式", + "loc.messages.ContainerPatternNotFound": "Docker Compose 文件路径参数中未找到任何模式", + "loc.messages.ContainerDockerFileNotFound": "找不到匹配 %s 的任何 Docker 文件。", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "不存在其他 Docker Compose 文件 %s。", + "loc.messages.CantWriteDataToFile": "无法将数据写入文件 %s。错误: %s", + "loc.messages.DockerHostVariableWarning": "已设置 DOCKER_HOST 变量。请确保 Docker 守护程序正在 %s 上运行", + "loc.messages.NoDataWrittenOnFile": "没有任何数据写入到文件 %s", + "loc.messages.FileContentSynced": "文件内容已同步到磁盘。内容为 %s。", + "loc.messages.ImageNameWithoutTag": "未使用标记指定映像名,推送指定映像的所有标记。", + "loc.messages.WritingDockerConfigToTempFile": "正在将 Docker 配置写入临时文件。文件路径: %s,Docker 配置: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/DockerComposeV1/Strings/resources.resjson/zh-TW/resources.resjson new file mode 100644 index 000000000000..6c42b4ea9d33 --- /dev/null +++ b/Tasks/DockerComposeV1/Strings/resources.resjson/zh-TW/resources.resjson @@ -0,0 +1,84 @@ +{ + "loc.friendlyName": "Docker Compose", + "loc.helpMarkDown": "[深入了解此工作](https://go.microsoft.com/fwlink/?linkid=848006)或[參閱 Docker Compose 文件](https://docs.docker.com/)", + "loc.description": "組建、推送或執行多容器 Docker 應用程式。工作可用於 Docker 或 Azure Container 登錄。", + "loc.instanceNameFormat": "$(action)", + "loc.group.displayName.advanced": "進階選項", + "loc.input.label.containerregistrytype": "容器登錄類型", + "loc.input.help.containerregistrytype": "請選取容器登錄類型。", + "loc.input.label.dockerRegistryEndpoint": "Docker 登錄服務連線", + "loc.input.help.dockerRegistryEndpoint": "選取 Docker 登錄服務連線。必須為需要向登錄驗證的命令選取。", + "loc.input.label.azureSubscriptionEndpoint": "Azure 訂用帳戶", + "loc.input.help.azureSubscriptionEndpoint": "選取 Azure 訂閱", + "loc.input.label.azureContainerRegistry": "Azure Container Registry", + "loc.input.help.azureContainerRegistry": "選取 Azure Container Registry", + "loc.input.label.dockerComposeFile": "Docker Compose 檔案", + "loc.input.help.dockerComposeFile": "要使用之主要 Docker Compose 檔案的路徑。", + "loc.input.label.additionalDockerComposeFiles": "其他 Docker Compose 檔案", + "loc.input.help.additionalDockerComposeFiles": "其他要與主要 Docker Compose 檔案合併的 Docker Compose 檔案。相對路徑會解析成相對於包含主要 Docker Compose 檔案的目錄。若指定的檔案找不到,將會予以忽略。請在新行上指定每一個檔案路徑。", + "loc.input.label.dockerComposeFileArgs": "環境變數", + "loc.input.help.dockerComposeFileArgs": "要在執行命令時設定的環境變數。請在新行上指定每一個 name=value 組。", + "loc.input.label.projectName": "專案名稱", + "loc.input.help.projectName": "用於命名映像及容器之預設名稱的專案名稱。", + "loc.input.label.qualifyImageNames": "限定映像名稱", + "loc.input.help.qualifyImageNames": "若未另外指定,便以 Docker 登錄服務連線的主機名稱來為建置的服務限定映像名稱。", + "loc.input.label.action": "動作", + "loc.input.help.action": "請選取 Docker Compose 動作。", + "loc.input.label.additionalImageTags": "其他映像標記", + "loc.input.help.additionalImageTags": "要建置或推送之 Docker 映像的其他標記。", + "loc.input.label.includeSourceTags": "包含來源標記", + "loc.input.help.includeSourceTags": "於建置或推送 Docker 映像時加入 Git 標記。", + "loc.input.label.includeLatestTag": "包含最新標記", + "loc.input.help.includeLatestTag": "於建置或推送 Docker 映像時加入最新標記。", + "loc.input.label.buildImages": "組建映像", + "loc.input.help.buildImages": "啟動服務容器之前先建置映像。", + "loc.input.label.serviceName": "服務名稱", + "loc.input.help.serviceName": "要執行之特定服務的名稱。", + "loc.input.label.containerName": "容器名稱", + "loc.input.help.containerName": "要執行之特定服務容器的名稱。", + "loc.input.label.ports": "連接埠", + "loc.input.help.ports": "要推送到主機之特定服務容器中的連接埠。每一個 host-port:container-port 繫結都必須在新行上指定。", + "loc.input.label.workDir": "工作目錄", + "loc.input.help.workDir": "特定服務容器的工作目錄。", + "loc.input.label.entrypoint": "進入點覆寫", + "loc.input.help.entrypoint": "覆寫特定服務容器的預設進入點。", + "loc.input.label.containerCommand": "命令", + "loc.input.help.containerCommand": "要在特定服務容器中執行的命令。例如,若映像只包含 Python Flask Web 應用程式,您可以指定 'python app.py' 來啟動該 Web 應用程式。", + "loc.input.label.detached": "在背景執行", + "loc.input.help.detached": "在背景執行服務容器。", + "loc.input.label.abortOnContainerExit": "容器結束時中止", + "loc.input.help.abortOnContainerExit": "一有任何容器結束,即停止所有容器。", + "loc.input.label.imageDigestComposeFile": "映像摘要 Compose 檔案", + "loc.input.help.imageDigestComposeFile": "所建立之 Docker Compose 檔案的路徑; 該檔案填入了每項服務之 Docker 映像的完整映像保存庫摘要。", + "loc.input.label.removeBuildOptions": "移除建置選項", + "loc.input.help.removeBuildOptions": "從輸出 Docker Compose 檔案移除建置選項。", + "loc.input.label.baseResolveDirectory": "基礎解析目錄", + "loc.input.help.baseResolveDirectory": "輸出 Docker Compose 檔案中要解析之相對路徑的基礎目錄。", + "loc.input.label.outputDockerComposeFile": "輸出 Docker Compose 檔案", + "loc.input.help.outputDockerComposeFile": "輸出 Docker Compose 檔案的路徑。", + "loc.input.label.dockerComposeCommand": "命令", + "loc.input.help.dockerComposeCommand": "要加上引數一起執行的 Docker Compose 命令。例如 'rm --all' 表示要移除所有已經停止的服務容器。", + "loc.input.label.arguments": "引數", + "loc.input.help.arguments": "Docker Compose 命令選項。例如:
建置命令為
--pull --compress --parallel", + "loc.input.label.dockerHostEndpoint": "Docker 主機服務連線", + "loc.input.help.dockerHostEndpoint": "選取 Docker 主機服務連線。預設為代理程式的主機。", + "loc.input.label.nopIfNoDockerComposeFile": "若無 Docker Compose 檔案,即不執行任何作業", + "loc.input.help.nopIfNoDockerComposeFile": "若沒有 Docker Compose 檔案,請跳過此步驟。當工作依據保存庫中有無 Docker Compose 檔案來提供選擇性行為時,即可加以使用。", + "loc.input.label.requireAdditionalDockerComposeFiles": "需要其他 Docker Compose 檔案", + "loc.input.help.requireAdditionalDockerComposeFiles": "若無其他 Docker Compose 檔案,即產生錯誤。這會覆寫預設行為 (亦即當檔案不存在時予以忽略)。", + "loc.input.label.cwd": "工作目錄", + "loc.input.help.cwd": "Docker Compose 命令的工作目錄。", + "loc.input.label.dockerComposePath": "Docker Compose 可執行檔路徑", + "loc.input.help.dockerComposePath": "如果提供路徑,將會使用這個 docker-compose 可執行檔", + "loc.messages.ConnectingToDockerHost": "已設定 DOCKER_HOST 變數。Docker 會嘗試連線到 Docker 主機: %s", + "loc.messages.ContainerPatternFound": "在 docker compose filepath 參數中找到的模式", + "loc.messages.ContainerPatternNotFound": "在 docker compose filepath 參數中找不到任何模式", + "loc.messages.ContainerDockerFileNotFound": "找不到任何的 Docker 檔案符合 %s。", + "loc.messages.AdditionalDockerComposeFileDoesNotExists": "其他 Docker Compose 檔案 %s 不存在。", + "loc.messages.CantWriteDataToFile": "無法將資料寫入檔案 %s。錯誤: %s", + "loc.messages.DockerHostVariableWarning": "已設定 DOCKER_HOST 變數。請確定 Docker 精靈正在下列位置執行: %s", + "loc.messages.NoDataWrittenOnFile": "沒有任何資料寫入檔案 %s", + "loc.messages.FileContentSynced": "檔案內容已同步到磁碟。內容為 %s。", + "loc.messages.ImageNameWithoutTag": "未使用標籤指定映像名稱,因此會推送指定映像的所有標籤。", + "loc.messages.WritingDockerConfigToTempFile": "正在將 Docker 設定寫入暫存檔案。檔案路徑: %s,Docker 設定: %s" +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Tests/L0.ts b/Tasks/DockerComposeV1/Tests/L0.ts new file mode 100644 index 000000000000..16fe8e790de8 --- /dev/null +++ b/Tasks/DockerComposeV1/Tests/L0.ts @@ -0,0 +1,323 @@ +import * as path from 'path'; +import * as assert from 'assert'; +import * as ttm from 'azure-pipelines-task-lib/mock-test'; +import tl = require('azure-pipelines-task-lib'); + +describe('Docker Compose Suite', function() { + this.timeout(30000); + let composeCommand: string; + + before(async() => { + composeCommand = "docker compose"; + }); + + beforeEach(() => { + delete process.env["__command__"]; + delete process.env["__container_type__"]; + delete process.env["__qualifyImageNames__"]; + delete process.env["__additionalDockerComposeFiles__"]; + delete process.env["__composeFilePath__"]; + delete process.env["__dockerComposeCommand__"]; + delete process.env["__arguments__"]; + delete process.env["__dockerComposePath__"]; + }); + + if (tl.getPlatform() === tl.Platform.Windows) { + it('Runs successfully for windows docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for windows docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose run service', async() => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + console.log(tr.stdout); + console.log(tr.stderr); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); + }); + + it('Runs successfully for windows docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + } else { + it('Runs successfully for linux docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose run service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + + it('Runs successfully for linux docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + } +}); \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Tests/L0Linux.ts b/Tasks/DockerComposeV1/Tests/L0Linux.ts new file mode 100644 index 000000000000..00f0450ce78e --- /dev/null +++ b/Tasks/DockerComposeV1/Tests/L0Linux.ts @@ -0,0 +1,131 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +const DefaultWorkingDirectory: string = "/a/w"; +let taskPath = path.join(__dirname, '..', 'dockercompose.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput('containerregistrytype', process.env["__container_type__"] || 'Container Registry'); +tr.setInput('action', process.env["__command__"] || 'Build services'); +tr.setInput('dockerRegistryEndpoint', 'dockerhubendpoint'); +tr.setInput('dockerComposeFile', process.env["__composeFilePath__"] ||'/tmp/tempdir/100/docker-compose.yml'); +tr.setInput('customCommand', "pull test/test:2"); +tr.setInput('includeLatestTag', process.env["__includeLatestTag__"] || "false"); +tr.setInput('qualifyImageNames', process.env["__qualifyImageNames__"] || "false"); +tr.setInput('additionalDockerComposeFiles', process.env["__additionalDockerComposeFiles__"] || null); +tr.setInput('dockerComposeCommand', process.env["__dockerComposeCommand__"] || null); +tr.setInput('azureSubscriptionEndpoint', 'AzureRMSpn'); +tr.setInput('azureContainerRegistry', '{"loginServer":"ajgtestacr1.azurecr.io", "id" : "/subscriptions/c00d16c7-6c1f-4c03-9be1-6934a4c49682/resourcegroups/ajgtestacr1rg/providers/Microsoft.ContainerRegistry/registries/ajgtestacr1"}'); +tr.setInput('arguments', process.env["__arguments__"] || ''); +tr.setInput('dockerComposePath', process.env["__dockerComposePath__"] || ''); + +console.log("Inputs have been set"); + +process.env["RELEASE_RELEASENAME"] = "Release-1"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = DefaultWorkingDirectory; +process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/"; +process.env["SYSTEM_SERVERTYPE"] = "hosted"; +process.env['AGENT_TEMPDIRECTORY'] = '.'; +process.env["ENDPOINT_AUTH_dockerhubendpoint"] = "{\"parameters\":{\"username\":\"test\", \"password\":\"regpassword\", \"email\":\"test@microsoft.com\",\"registry\":\"https://index.docker.io/v1/\"},\"scheme\":\"UsernamePassword\"}"; +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env['AGENT_HOMEDIRECTORY'] = '/tmp/tempdir/100/'; + +// provide answers for task mock +let a: any = { + "which": { + "docker": "docker", + "docker-compose": "docker-compose" + }, + "checkPath": { + "docker": true, + "docker-compose": true + }, + "exec": { + "docker compose -f /tmp/tempdir/100/docker-compose.yml build": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" + }, + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" + }, + "docker push 100_web": { + "code": 0, + "stdout": "sucessfully pushed 100_web" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml up": { + "code": 0, + "stdout": "sucessfully ran services" + }, + "docker push ajgtestacr1.azurecr.io/100_web": { + "code": 0, + "stdout": "successfully pushed with qualified image" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { + "code": 0, + "stdout": "successfully ran up command" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml up -d": { + "code": 0, + "stdout": "successfully ran up command" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { + "code": 0, + "stdout": "successfully pulled the passed service images" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" + }, + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" + } + }, + "exist": { + "/tmp/tempdir/100/.docker-compose.12345.yml" : true, + "/tmp/tempdir/100/docker-compose.override.yml" : true + } +}; + +var ut = require('../utils'); +tr.registerMock('./utils', { + IsNullOrEmpty : ut.IsNullOrEmpty, + HasItems : ut.HasItems, + StringWritable: ut.StringWritable, + PackerVersion: ut.PackerVersion, + isGreaterVersion: ut.isGreaterVersion, + getFinalComposeFileName: function(){ + return ".docker-compose.12345" + ".yml"; + }, + writeFileSync: function(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }){ + console.log("content of yaml file : "+data); + }, + writeTaskOutput: function (commandName: string, output: string): string { + let outputFileName = commandName + "_" + Date.now() + ".txt"; + console.log(`Mocked test writing to: ${outputFileName}`); + return outputFileName; + } +}); + +tr.setAnswers(a); +tr.run(); \ No newline at end of file diff --git a/Tasks/DockerComposeV1/Tests/L0Windows.ts b/Tasks/DockerComposeV1/Tests/L0Windows.ts new file mode 100644 index 000000000000..7eff1b7a18ed --- /dev/null +++ b/Tasks/DockerComposeV1/Tests/L0Windows.ts @@ -0,0 +1,131 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +const DefaultWorkingDirectory: string = "C:\\a\\w\\"; +let taskPath = path.join(__dirname, '..\\dockercompose.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput('containerregistrytype', process.env["__container_type__"] || 'Container Registry'); +tr.setInput('action', process.env["__command__"] || 'Build services'); +tr.setInput('dockerRegistryEndpoint', 'dockerhubendpoint'); +tr.setInput('dockerComposeFile', process.env["__composeFilePath__"] ||'F:\\dir2\\docker-compose.yml'); +tr.setInput('customCommand', "pull test/test:2"); +tr.setInput('includeLatestTag', process.env["__includeLatestTag__"] || "false"); +tr.setInput('qualifyImageNames', process.env["__qualifyImageNames__"] || "false"); +tr.setInput('additionalDockerComposeFiles', process.env["__additionalDockerComposeFiles__"] || null); +tr.setInput('dockerComposeCommand', process.env["__dockerComposeCommand__"] || null); +tr.setInput('azureSubscriptionEndpoint', 'AzureRMSpn'); +tr.setInput('azureContainerRegistry', '{"loginServer":"ajgtestacr1.azurecr.io", "id" : "/subscriptions/c00d16c7-6c1f-4c03-9be1-6934a4c49682/resourcegroups/ajgtestacr1rg/providers/Microsoft.ContainerRegistry/registries/ajgtestacr1"}'); +tr.setInput('arguments', process.env["__arguments__"] || ''); +tr.setInput('dockerComposePath', process.env["__dockerComposePath__"] || ''); + +console.log("Inputs have been set"); + +process.env["RELEASE_RELEASENAME"] = "Release-1"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = DefaultWorkingDirectory; +process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/"; +process.env["SYSTEM_SERVERTYPE"] = "hosted"; +process.env['AGENT_TEMPDIRECTORY'] = '.'; +process.env["ENDPOINT_AUTH_dockerhubendpoint"] = "{\"parameters\": {\"username\":\"test\", \"password\":\"regpassword\", \"email\":\"test@microsoft.com\",\"registry\":\"https://index.docker.io/v1/\"},\"scheme\":\"UsernamePassword\"}"; +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env['AGENT_HOMEDIRECTORY'] = 'F:\\dir2\\'; + +// provide answers for task mock +let a: any = { + "which": { + "docker": "docker", + "docker-compose" : "docker-compose" + }, + "checkPath": { + "docker": true, + "docker-compose": true + }, + "exec": { + "docker compose -f F:\\dir2\\docker-compose.yml build": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker compose -f F:\\dir2\\docker-compose.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" + }, + "docker-compose-userdefined -f F:\\dir2\\docker-compose.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" + }, + "docker push dir2_web": { + "code": 0, + "stdout": "sucessfully pushed dir2_web" + }, + "docker compose -f F:\\dir2\\docker-compose.yml up": { + "code": 0, + "stdout": "sucessfully ran services" + }, + "docker compose -f F:\\dir2\\docker-compose.yml build --pull --parallel": { + "code": 0, + "stdout": "sucessfully built the service images" + }, + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" + }, + "docker push ajgtestacr1.azurecr.io/dir2_web": { + "code": 0, + "stdout": "successfully pushed with qualified image" + }, + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { + "code": 0, + "stdout": "successfully ran up command" + }, + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { + "code": 0, + "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" + }, + "docker compose -f F:\\dir2\\docker-compose.yml up -d": { + "code": 0, + "stdout": "successfully ran up command" + }, + "docker compose -f F:\\dir2\\docker-compose.yml pull service1 service2": { + "code": 0, + "stdout": "successfully pulled the passed service images" + } + }, + "exist": { + "F:\\dir2\\.docker-compose.12345.yml" : true, + "F:\\dir2\\docker-compose.override.yml": true + } +}; + +var ut = require('../utils'); +tr.registerMock('./utils', { + IsNullOrEmpty : ut.IsNullOrEmpty, + HasItems : ut.HasItems, + StringWritable: ut.StringWritable, + PackerVersion: ut.PackerVersion, + isGreaterVersion: ut.isGreaterVersion, + getFinalComposeFileName: function(){ + return ".docker-compose.12345" + ".yml"; + }, + writeFileSync: function(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }){ + console.log("content of yaml file : "+data); + }, + writeTaskOutput: function (commandName: string, output: string): string { + let outputFileName = commandName + "_" + Date.now() + ".txt"; + console.log(`Mocked test writing to: ${outputFileName}`); + return outputFileName; + } +}); + +tr.setAnswers(a); +tr.run(); \ No newline at end of file diff --git a/Tasks/DockerComposeV1/ThirdPartyNotices.txt b/Tasks/DockerComposeV1/ThirdPartyNotices.txt new file mode 100644 index 000000000000..5f5946872246 --- /dev/null +++ b/Tasks/DockerComposeV1/ThirdPartyNotices.txt @@ -0,0 +1,1068 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +The Docker Compose task for Azure Pipelines or Team Foundation Server incorporates components from the projects listed below. Microsoft licenses these components to you under the Microsoft software license terms. The original copyright notices and the licenses under which Microsoft received such components are set forth below for informational purposes. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. + + +1. argparse (https://github.com/nodeca/argparse) +2. array-union (https://github.com/sindresorhus/array-union) +3. array-uniq (https://github.com/sindresorhus/array-uniq) +4. arrify (https://github.com/sindresorhus/arrify) +5. balanced-match (https://github.com/juliangruber/balanced-match) +6. brace-expansion (https://github.com/juliangruber/brace-expansion) +7. concat-map (https://github.com/substack/node-concat-map) +8. del (https://github.com/sindresorhus/del) +9. esprima (https://github.com/ariya/esprima) +10. fs.realpath (https://github.com/isaacs/fs.realpath) +11. Glob (https://github.com/isaacs/node-glob) +12. globby (https://github.com/sindresorhus/globby) +13. inflight (https://github.com/npm/inflight) +14. inherits (https://github.com/isaacs/inherits) +15. is-path-cwd (https://github.com/sindresorhus/is-path-cwd) +16. is-path-in-cwd (https://github.com/sindresorhus/is-path-in-cwd) +17. is-path-inside (https://github.com/sindresorhus/is-path-inside) +18. js-yaml (https://github.com/nodeca/js-yaml) +19. minimatch (https://github.com/isaacs/minimatch) +20. Mockery (https://github.com/mfncooper/mockery) +21. node-uuid (https://github.com/broofa/node-uuid) +22. object-assign (https://github.com/sindresorhus/object-assign) +23. once (https://github.com/isaacs/once) +24. path-is-absolute (https://github.com/sindresorhus/path-is-absolute) +25. path-is-inside (https://github.com/domenic/path-is-inside) +26. pify (https://github.com/sindresorhus/pify) +27. pinkie (https://github.com/floatdrop/pinkie) +28. pinkie-promise (https://github.com/floatdrop/pinkie-promise) +29. q (https://github.com/kriskowal/q) +30. Rimraf (https://github.com/isaacs/rimraf) +31. semver (https://github.com/npm/node-semver/) +32. ShellJS (https://github.com/shelljs/shelljs) + Includes:wrench-js +33. sprintf-js (https://github.com/alexei/sprintf.js) +34. tunnel (https://github.com/koichik/node-tunnel) +35. underscore (https://github.com/jashkenas/underscore) +36. vso-node-api (https://github.com/Microsoft/vsts-node-api) +37. azure-pipelines-task-lib (https://github.com/Microsoft/azure-pipelines-task-lib) +38. wrappy (https://github.com/npm/wrappy) + + +%% argparse NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(The MIT License) + +Copyright (C) 2012 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF argparse NOTICES, INFORMATION, AND LICENSE + +%% array-union NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF array-union NOTICES, INFORMATION, AND LICENSE + +%% array-uniq NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF array-uniq NOTICES, INFORMATION, AND LICENSE + +%% arrify NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF arrify NOTICES, INFORMATION, AND LICENSE + +%% balanced-match NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(MIT) + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF balanced-match NOTICES, INFORMATION, AND LICENSE + +%% brace-expansion NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(MIT) + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF brace-expansion NOTICES, INFORMATION, AND LICENSE + +%% concat-map NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) James Halliday/substack + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF concat-map NOTICES, INFORMATION, AND LICENSE + +%% del NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF del NOTICES, INFORMATION, AND LICENSE + +%% esprima NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= + Copyright (C) 2012 Ariya Hidayat + Copyright (C) 2012 Mathias Bynens + Copyright (C) 2012 Joost-Wim Boekesteijn + Copyright (C) 2012 Kris Kowal + Copyright (C) 2012 Yusuke Suzuki + Copyright (C) 2012 Arpad Borsos + Copyright (C) 2011 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF esprima NOTICES, INFORMATION, AND LICENSE + +%% fs.realpath NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---- + +This library bundles a version of the `fs.realpath` and `fs.realpathSync` +methods from Node.js v0.10 under the terms of the Node.js MIT license, as follows: + + Copyright Joyent, Inc. and other Node contributors. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +========================================= +END OF fs.realpath NOTICES, INFORMATION, AND LICENSE + +%% Glob NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF Glob NOTICES, INFORMATION, AND LICENSE + +%% globby NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF globby NOTICES, INFORMATION, AND LICENSE + +%% inflight NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF inflight NOTICES, INFORMATION, AND LICENSE + +%% inherits NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF inherits NOTICES, INFORMATION, AND LICENSE + +%% is-path-cwd NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-path-cwd NOTICES, INFORMATION, AND LICENSE + +%% is-path-in-cwd NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-path-in-cwd NOTICES, INFORMATION, AND LICENSE + +%% is-path-inside NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF is-path-inside NOTICES, INFORMATION, AND LICENSE + +%% js-yaml NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(The MIT License) + +Copyright (C) 2011-2015 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF js-yaml NOTICES, INFORMATION, AND LICENSE + +%% minimatch NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF minimatch NOTICES, INFORMATION, AND LICENSE + +%% Mockery NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyrights for code authored by Yahoo! Inc. is licensed under the following + terms: + + MIT License + + Copyright (c) 2011 Yahoo! Inc. All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +========================================= +END OF Mockery NOTICES, INFORMATION, AND LICENSE + +%% node-uuid NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2010-2016 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF node-uuid NOTICES, INFORMATION, AND LICENSE + +%% object-assign NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF object-assign NOTICES, INFORMATION, AND LICENSE + +%% once NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF once NOTICES, INFORMATION, AND LICENSE + +%% path-is-absolute NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +path-is-absolute + +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +node.js: + +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +Software), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF path-is-absolute NOTICES, INFORMATION, AND LICENSE + +%% path-is-inside NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright � 2013�2014 Domenic Denicola +Licensed under the WTFPL Version 2 (http://www.wtfpl.net/about/) +========================================= +END OF path-is-inside NOTICES, INFORMATION, AND LICENSE + +%% pify NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF pify NOTICES, INFORMATION, AND LICENSE + +%% pinkie NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF pinkie NOTICES, INFORMATION, AND LICENSE + +%% pinkie-promise NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF pinkie-promise NOTICES, INFORMATION, AND LICENSE + +%% q NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2009�2014 Kristopher Michael Kowal. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +/*! + * + * Copyright 2009-2012 Kris Kowal under the terms of the MIT + * license found at http://github.com/kriskowal/q/raw/master/LICENSE + * + * With parts by Tyler Close + * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found + * at http://www.opensource.org/licenses/mit-license.html + * Forked at ref_send.js version: 2009-05-11 + * + * With parts by Mark Miller + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +========================================= +END OF q NOTICES, INFORMATION, AND LICENSE + +%% Rimraf NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF Rimraf NOTICES, INFORMATION, AND LICENSE + +%% semver NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF semver NOTICES, INFORMATION, AND LICENSE + +%% ShellJS NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012, Artur Adib +All rights reserved. + +You may use this project under the terms of the New BSD license as follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Artur Adib nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +Includes wrench-js +The MIT License + +Copyright (c) 2010 Ryan McGrath + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF ShellJS NOTICES, INFORMATION, AND LICENSE + +%% sprintf-js NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2007-2014, Alexandru Marasteanu +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of this software nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF sprintf-js NOTICES, INFORMATION, AND LICENSE + +%% tunnel NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012 Koichi Kobayashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF tunnel NOTICES, INFORMATION, AND LICENSE + +%% underscore NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative +Reporters & Editors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF underscore NOTICES, INFORMATION, AND LICENSE + +%% vso-node-api NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF vso-node-api NOTICES, INFORMATION, AND LICENSE + +%% azure-pipelines-task-lib NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +========================================= +END OF azure-pipelines-task-lib NOTICES, INFORMATION, AND LICENSE + +%% wrappy NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF wrappy NOTICES, INFORMATION, AND LICENSE + + diff --git a/Tasks/DockerComposeV1/dockercompose.ts b/Tasks/DockerComposeV1/dockercompose.ts new file mode 100644 index 000000000000..b73b7c74173f --- /dev/null +++ b/Tasks/DockerComposeV1/dockercompose.ts @@ -0,0 +1,80 @@ +"use strict"; + +import * as path from "path"; +import * as tl from "azure-pipelines-task-lib/task"; +import * as DockerComposeUtils from "./dockercomposeutils"; + +import ACRAuthenticationTokenProvider from "azure-pipelines-tasks-docker-common/registryauthenticationprovider/acrauthenticationtokenprovider" +import DockerComposeConnection from "./dockercomposeconnection"; +import { getDockerRegistryEndpointAuthenticationToken } from "azure-pipelines-tasks-docker-common/registryauthenticationprovider/registryauthenticationtoken"; + +import Q = require('q'); + +tl.setResourcePath(path.join(__dirname, 'task.json')); + +var cwd = tl.getInput("cwd"); + +// Change to any specified working directory +tl.cd(cwd); + +// get the registry server authentication provider +var registryType = tl.getInput("containerregistrytype", true); +var registryAuthenticationToken; + +if (registryType == "Azure Container Registry") { + const tokenProvider = new ACRAuthenticationTokenProvider(tl.getInput("azureSubscriptionEndpoint"), tl.getInput("azureContainerRegistry")); + registryAuthenticationToken = tokenProvider.getToken(); +} +else { + registryAuthenticationToken = getDockerRegistryEndpointAuthenticationToken(tl.getInput("dockerRegistryEndpoint")) +} + +var dockerComposeFile = tl.getInput("dockerComposeFile", true); +var nopIfNoDockerComposeFile = tl.getBoolInput("nopIfNoDockerComposeFile"); +var dockerFile = DockerComposeUtils.findDockerFile(dockerComposeFile, cwd); +if (nopIfNoDockerComposeFile && !tl.exist(dockerFile)) { + console.log("No Docker Compose file matching " + dockerComposeFile + " was found."); + tl.setResult(tl.TaskResult.Succeeded, ""); +} else { + registryAuthenticationToken + .then(function success(authToken) { + let resultPaths = ""; + + // Connect to any specified Docker host and/or registry + var connection = new DockerComposeConnection(); + connection.open(tl.getInput("dockerHostEndpoint"), authToken) + .then(function runAction() { + // Run the specified action + var action = tl.getInput("action", true).toLowerCase(); + var telemetry = { + registryType: registryType, + command: action !== "Run a Docker Compose command" ? action : tl.getInput("dockerComposeCommand", true) + }; + + /* tslint:disable:no-var-requires */ + return require({ + "build services": "./dockercomposebuild", + "push services": "./dockercomposepush", + "run services": "./dockercomposeup", + "run a specific service": "./dockercomposerun", + "lock services": "./dockercomposelock", + "write service image digests": "./dockercomposedigests", + "combine configuration": "./dockercomposeconfig", + "run a docker compose command": "./dockercomposecommand" + }[action]).run(connection, (pathToResult) => { + resultPaths += `${pathToResult}\n`; + }); + /* tslint:enable:no-var-requires */ + }) + .fin(function cleanup() { + connection.close(); + }) + .then(function success() { + tl.setVariable("DockerComposeOutput", resultPaths); + tl.setResult(tl.TaskResult.Succeeded, ""); + }, function failure(err) { + tl.setResult(tl.TaskResult.Failed, err.message); + }) + .done(); + }); +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/dockercomposebuild.ts b/Tasks/DockerComposeV1/dockercomposebuild.ts new file mode 100644 index 000000000000..c72856e6b396 --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposebuild.ts @@ -0,0 +1,86 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as sourceUtils from "azure-pipelines-tasks-docker-common/sourceutils"; +import * as imageUtils from "azure-pipelines-tasks-docker-common/containerimageutils"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; +import * as utils from "./utils"; + +function dockerTag(connection: DockerComposeConnection, source: string, target: string, outputUpdate: (output: any) => any) { + var command = connection.createCommand(); + command.arg("tag"); + command.arg(source); + command.arg(target); + return connection.execCommandWithLogging(command) + .then((output) => outputUpdate(utils.writeTaskOutput(`tag_${source}`, output))); +} + +function addTag(promise: any, connection: DockerComposeConnection, source: string, target: string, onCommandOut: (data: string) => any) { + if (!promise) { + return dockerTag(connection, source, target, onCommandOut); + } else { + return promise.then(() => dockerTag(connection, source, target, onCommandOut)); + } +} + +function addOtherTags(connection: DockerComposeConnection, imageName: string, outputUpdate: (data: string) => any): any { + var baseImageName = imageUtils.imageNameWithoutTag(imageName); + + let output = ""; + + function addAdditionalTags() { + var promise: any; + tl.getDelimitedInput("additionalImageTags", "\n").forEach(tag => { + promise = addTag(promise, connection, imageName, baseImageName + ":" + tag, outputUpdate); + }); + return promise; + } + + function addSourceTags() { + var promise: any; + var includeSourceTags = tl.getBoolInput("includeSourceTags"); + if (includeSourceTags) { + sourceUtils.getSourceTags().forEach(tag => { + promise = addTag(promise, connection, imageName, baseImageName + ":" + tag, outputUpdate); + }); + } + return promise; + } + + function addLatestTag() { + var includeLatestTag = tl.getBoolInput("includeLatestTag"); + if (baseImageName !== imageName && includeLatestTag) { + return dockerTag(connection, imageName, baseImageName, outputUpdate); + } + } + + var promise = addAdditionalTags(); + promise = !promise ? addSourceTags() : promise.then(addSourceTags); + promise = !promise ? addLatestTag() : promise.then(addLatestTag); + + return promise; +} + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + var command = connection.createComposeCommand(); + command.arg("build"); + var arg = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(arg || ""); + command.line(commandArgs || ""); + + return connection.execCommandWithLogging(command) + .then((output) => outputUpdate(utils.writeTaskOutput("build", output))) + .then(() => connection.getImages(true)) + .then(images => { + var promise: any; + Object.keys(images).map(serviceName => images[serviceName]).forEach(imageName => { + if (!promise) { + promise = addOtherTags(connection, imageName, outputUpdate); + } else { + promise = promise.then(() => addOtherTags(connection, imageName, outputUpdate)); + } + }); + return promise; + }); +} diff --git a/Tasks/DockerComposeV1/dockercomposecommand.ts b/Tasks/DockerComposeV1/dockercomposecommand.ts new file mode 100644 index 000000000000..e8bcc0b8fcea --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposecommand.ts @@ -0,0 +1,18 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as utils from "./utils"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + var command = connection.createComposeCommand(); + command.line(tl.getInput("dockerComposeCommand", true)); + + var args = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(args || ""); + command.line(commandArgs || ""); + + return connection.execCommandWithLogging(command) + .then((output) => outputUpdate(utils.writeTaskOutput("command", output))); +} diff --git a/Tasks/DockerComposeV1/dockercomposeconfig.ts b/Tasks/DockerComposeV1/dockercomposeconfig.ts new file mode 100644 index 000000000000..c206316e3d93 --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposeconfig.ts @@ -0,0 +1,35 @@ +"use strict"; + +import * as fs from "fs"; +import * as tl from "azure-pipelines-task-lib/task"; +import * as yaml from "js-yaml"; +import DockerComposeConnection from "./dockercomposeconnection"; + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any, imageDigestComposeFile?: string): any { + return connection.getCombinedConfig(imageDigestComposeFile).then(output => { + var removeBuildOptions = tl.getBoolInput("removeBuildOptions"); + if (removeBuildOptions) { + var doc = yaml.safeLoad(output); + for (var serviceName in doc.services || {}) { + delete doc.services[serviceName].build; + } + output = yaml.safeDump(doc, {lineWidth: -1} as any); + } + + var baseResolveDir = tl.getPathInput("baseResolveDirectory"); + if (baseResolveDir) { + // This just searches the output string and replaces all + // occurrences of the base resolve directory. This isn't + // precisely accurate but is a good enough solution. + var replaced = output; + do { + output = replaced; + replaced = output.replace(baseResolveDir, "."); + } while (replaced !== output); + } + + var outputDockerComposeFile = tl.getPathInput("outputDockerComposeFile", true); + + fs.writeFileSync(outputDockerComposeFile, output); + }); +} diff --git a/Tasks/DockerComposeV1/dockercomposeconnection.ts b/Tasks/DockerComposeV1/dockercomposeconnection.ts new file mode 100644 index 000000000000..ae7f88a04f9d --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposeconnection.ts @@ -0,0 +1,218 @@ +"use strict"; + +import * as del from "del"; +import * as path from "path"; +import * as tl from "azure-pipelines-task-lib/task"; +import * as tr from "azure-pipelines-task-lib/toolrunner"; +import * as yaml from "js-yaml"; +import * as DockerComposeUtils from "./dockercomposeutils"; + +import ContainerConnection from "azure-pipelines-tasks-docker-common/containerconnection" +import AuthenticationToken from "azure-pipelines-tasks-docker-common/registryauthenticationprovider/registryauthenticationtoken" +import * as Utils from "./utils"; + +export default class DockerComposeConnection extends ContainerConnection { + private dockerComposePath: string; + private dockerComposeFile: string; + private dockerComposeVersion: string; + private additionalDockerComposeFiles: string[]; + private requireAdditionalDockerComposeFiles: boolean; + private projectName: string; + private finalComposeFile: string; + + constructor() { + super(); + this.setDockerComposePath(); + this.dockerComposeFile = DockerComposeUtils.findDockerFile(tl.getInput("dockerComposeFile", true), tl.getInput("cwd")); + if (!this.dockerComposeFile) { + throw new Error("No Docker Compose file matching " + tl.getInput("dockerComposeFile") + " was found."); + } + this.dockerComposeVersion = "2"; + this.additionalDockerComposeFiles = tl.getDelimitedInput("additionalDockerComposeFiles", "\n"); + this.requireAdditionalDockerComposeFiles = tl.getBoolInput("requireAdditionalDockerComposeFiles"); + this.projectName = tl.getInput("projectName"); + this.validateProjectNameDockerComposeV2(); + } + + public open(hostEndpoint?: string, authenticationToken?: AuthenticationToken): any { + super.open(hostEndpoint, authenticationToken); + + if (this.hostUrl) { + process.env["DOCKER_HOST"] = this.hostUrl; + process.env["DOCKER_TLS_VERIFY"] = "1"; + process.env["DOCKER_CERT_PATH"] = this.certsDir; + } + + tl.getDelimitedInput("dockerComposeFileArgs", "\n").forEach(envVar => { + var tokens = envVar.split("="); + if (tokens.length < 2) { + throw new Error("Environment variable '" + envVar + "' is invalid."); + } + process.env[tokens[0].trim()] = tokens.slice(1).join("=").trim(); + }); + + return this.getImages(true).then(images => { + var qualifyImageNames = tl.getBoolInput("qualifyImageNames"); + if (!qualifyImageNames) { + return; + } + var agentDirectory = tl.getVariable("Agent.HomeDirectory"); + this.finalComposeFile = path.join(agentDirectory, Utils.getFinalComposeFileName()); + var services = {}; + if (qualifyImageNames) { + for (var serviceName in images) { + images[serviceName] = this.getQualifiedImageNameIfRequired(images[serviceName]); + } + } + for (var serviceName in images) { + services[serviceName] = { + image: images[serviceName] + }; + } + Utils.writeFileSync(this.finalComposeFile, yaml.safeDump({ + version: this.dockerComposeVersion, + services: services + }, { lineWidth: -1 } as any)); + }); + } + + public async execCommandWithLogging(command, options?: tr.IExecOptions): Promise { + // setup variable to store the command output + let output = ""; + command.on("stdout", data => { + output += data; + }); + + command.on("stderr", data => { + output += data; + }); + await this.execCommand(command, options); + return output || '\n'; + } + + public createComposeCommand(): tr.ToolRunner { + var command = tl.tool(this.dockerComposePath); + + if (!tl.getInput('dockerComposePath')) { + command.arg("compose"); + process.env["COMPOSE_COMPATIBILITY"] = "true"; + } + + command.arg(["-f", this.dockerComposeFile]); + var basePath = path.dirname(this.dockerComposeFile); + this.additionalDockerComposeFiles.forEach(file => { + file = this.resolveAdditionalDockerComposeFilePath(basePath, file); + if (this.requireAdditionalDockerComposeFiles || tl.exist(file)) { + command.arg(["-f", file]); + } + }); + if (this.finalComposeFile) { + command.arg(["-f", this.finalComposeFile]); + } + + if (this.projectName) { + command.arg(["-p", this.projectName]); + } + return command; + } + + public getCombinedConfig(imageDigestComposeFile?: string): any { + var command = this.createComposeCommand(); + if (imageDigestComposeFile) { + command.arg(["-f", imageDigestComposeFile]); + } + command.arg("config"); + var result = ""; + command.on("stdout", data => { + result += data; + }); + command.on("errline", line => { + tl.error(line); + }); + return command.exec({ silent: true } as any).then(() => result); + } + + public getImages(builtOnly?: boolean): any { + return this.getCombinedConfig().then(input => { + var doc = yaml.safeLoad(input); + if (doc.version) { + this.dockerComposeVersion = doc.version; + } + var projectName = this.projectName; + if (!projectName) { + projectName = path.basename(path.dirname(this.dockerComposeFile)); + } + var images: any = {}; + for (var serviceName in doc.services || {}) { + var service = doc.services[serviceName]; + var image = service.image; + if (!image) { + image = projectName.toLowerCase().replace(/[^0-9a-z]/g, "") + "_" + serviceName; + } + if (!builtOnly || service.build) { + images[serviceName] = image; + } + } + return images; + }); + } + + public getVersion(): string { + return this.dockerComposeVersion; + } + + public close(): void { + if (this.finalComposeFile && tl.exist(this.finalComposeFile)) { + del.sync(this.finalComposeFile, { force: true }); + } + super.close(); + } + + private resolveAdditionalDockerComposeFilePath(dockerComposeFolderPath: string, additionalComposeFilePath: string): string { + if (!path.isAbsolute(additionalComposeFilePath)) { + additionalComposeFilePath = path.join(dockerComposeFolderPath, additionalComposeFilePath); + } + + if (!tl.exist(additionalComposeFilePath)) { + tl.warning(tl.loc('AdditionalDockerComposeFileDoesNotExists', additionalComposeFilePath)); + } + + return additionalComposeFilePath; + } + + private setDockerComposePath(): void { + //Priority to docker-compose path provided by user + this.dockerComposePath = tl.getInput('dockerComposePath'); + if (!this.dockerComposePath) { + this.dockerComposePath = tl.which("docker"); + + if (!this.dockerComposePath) { + throw new Error("Docker Compose was not found. You can provide the path to docker-compose via 'dockerComposePath' "); + } + } else { + console.log("Using docker-compose from 'dockerComposePath' "); + } + } + + private validateProjectNameDockerComposeV2() { + tl.debug(`Start validating project name ${this.projectName}`); + + // The regular expression pattern is taken from compose-spec.json + // https://github.com/compose-spec/compose-spec/blob/864b24c24f7f24a26aa2c2b8a89a82478ce03a32/schema/compose-spec.json#L16 + const regexpPattern = new RegExp("^[a-z0-9][a-z0-9_-]*$"); + + if (!regexpPattern.test(this.projectName)) { + tl.warning(tl.loc("InvalidProjectName", this.projectName)); + + console.log(`##vso[telemetry.publish area=TaskHub;feature=DockerComposeV0]${JSON.stringify({ + "SYSTEM_JOBID": tl.getVariable("SYSTEM_JOBID"), + "SYSTEM_PLANID": tl.getVariable("SYSTEM_PLANID"), + "BUILD_BUILDID": tl.getVariable("BUILD_BUILDID"), + "IS_PROJECT_NAME_VALID": false, + })}`); + return; + } + + tl.debug("Project name is valid"); + } +} diff --git a/Tasks/DockerComposeV1/dockercomposedigests.ts b/Tasks/DockerComposeV1/dockercomposedigests.ts new file mode 100644 index 000000000000..9763bc95013a --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposedigests.ts @@ -0,0 +1,71 @@ +"use strict"; + +import * as fs from "fs"; +import * as tl from "azure-pipelines-task-lib/task"; +import * as yaml from "js-yaml"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as imageUtils from "azure-pipelines-tasks-docker-common/containerimageutils"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; +import * as utils from "./utils"; + +async function dockerPull(connection: DockerComposeConnection, imageName: string, imageDigests: any, serviceName: string) : Promise { + var command = connection.createCommand(); + command.arg("pull"); + var arg = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(arg || ""); + command.line(commandArgs || ""); + command.arg(imageName); + + const output = await connection.execCommandWithLogging(command); + + // Parse the output to find the repository digest + var imageDigest = output.match(/^Digest: (.*)$/m)[1]; + if (imageDigest) { + var baseImageName = imageUtils.imageNameWithoutTag(imageName); + imageDigests[serviceName] = baseImageName + "@" + imageDigest; + } + + return output; +} + +function writeImageDigestComposeFile(version: string, imageDigests: any, imageDigestComposeFile: string): void { + var services = {}; + Object.keys(imageDigests).forEach(serviceName => { + services[serviceName] = { + image: imageDigests[serviceName] + }; + }); + fs.writeFileSync(imageDigestComposeFile, yaml.safeDump({ + version: version, + services: services + }, { lineWidth: -1 } as any)); +} + +export function createImageDigestComposeFile(connection: DockerComposeConnection, imageDigestComposeFile: string, outputUpdate: (data: string) => any) { + return connection.getImages().then(images => { + var promise: any; + var version = connection.getVersion(); + var imageDigests = {}; + Object.keys(images).forEach(serviceName => { + (imageName => { + if (!promise) { + promise = dockerPull(connection, imageName, imageDigests, serviceName); + } else { + promise = promise.then(() => dockerPull(connection, imageName, imageDigests, serviceName)); + } + promise = promise.then((output) => outputUpdate(utils.writeTaskOutput(`pull_${imageName}`, output))); + })(images[serviceName]); + }); + if (!promise) { + writeImageDigestComposeFile(version, imageDigests, imageDigestComposeFile); + } else { + return promise.then(() => writeImageDigestComposeFile(version, imageDigests, imageDigestComposeFile)); + } + }); +} + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + var imageDigestComposeFile = tl.getPathInput("imageDigestComposeFile", true); + + return createImageDigestComposeFile(connection, imageDigestComposeFile, outputUpdate); +} diff --git a/Tasks/DockerComposeV1/dockercomposelock.ts b/Tasks/DockerComposeV1/dockercomposelock.ts new file mode 100644 index 000000000000..21877b8fe70f --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposelock.ts @@ -0,0 +1,22 @@ +"use strict"; + +import * as del from "del"; +import * as path from "path"; +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import { createImageDigestComposeFile } from "./dockercomposedigests"; +import { run as runDockerComposeConfig } from "./dockercomposeconfig"; +import * as utils from "./utils"; + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + var agentDirectory = tl.getVariable("Agent.HomeDirectory"), + imageDigestComposeFile = path.join(agentDirectory, ".docker-compose.images" + Date.now() + ".yml"); + + return createImageDigestComposeFile(connection, imageDigestComposeFile, outputUpdate) + .then(() => runDockerComposeConfig(connection, outputUpdate, imageDigestComposeFile)) + .fin(() => { + if (tl.exist(imageDigestComposeFile)) { + del.sync(imageDigestComposeFile, { force: true }); + } + }); +} diff --git a/Tasks/DockerComposeV1/dockercomposepush.ts b/Tasks/DockerComposeV1/dockercomposepush.ts new file mode 100644 index 000000000000..f6fa80bac767 --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposepush.ts @@ -0,0 +1,80 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as sourceUtils from "azure-pipelines-tasks-docker-common/sourceutils"; +import * as imageUtils from "azure-pipelines-tasks-docker-common/containerimageutils"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; +import * as utils from "./utils"; + +function dockerPush(connection: DockerComposeConnection, imageName: string, onCommandOut: (output: any) => any) { + var command = connection.createCommand(); + command.arg("push"); + var arg = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(arg || ""); + command.line(commandArgs || ""); + command.arg(imageName); + + return connection.execCommandWithLogging(command) + .then((output) => onCommandOut(output)); +} + +function pushTag(promise: any, connection: DockerComposeConnection, imageName: string, onCommandOut: (output: any) => any) { + if (!promise) { + return dockerPush(connection, imageName, onCommandOut); + } else { + return promise.then(() => dockerPush(connection, imageName, onCommandOut)); + } +} + +function pushTags(connection: DockerComposeConnection, imageName: string, onCommandOut: (output: any) => any): any { + var baseImageName = imageUtils.imageNameWithoutTag(imageName); + if (baseImageName == imageName) + { + tl.debug(tl.loc('ImageNameWithoutTag')); + } + + return dockerPush(connection, imageName, onCommandOut) + .then(function pushAdditionalTags() { + var promise: any; + tl.getDelimitedInput("additionalImageTags", "\n").forEach(tag => { + promise = pushTag(promise, connection, baseImageName + ":" + tag, onCommandOut); + }); + return promise; + }) + .then(function pushSourceTags() { + var promise: any; + var includeSourceTags = tl.getBoolInput("includeSourceTags"); + if (includeSourceTags) { + sourceUtils.getSourceTags().forEach(tag => { + promise = pushTag(promise, connection, baseImageName + ":" + tag, onCommandOut); + }); + } + return promise; + }) + .then(function pushLatestTag() { + var includeLatestTag = tl.getBoolInput("includeLatestTag"); + if (baseImageName !== imageName && includeLatestTag) { + return dockerPush(connection, baseImageName + ":latest", onCommandOut); + } + }); +} + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + return connection.getImages(true) + .then(images => { + var promise: any; + Object.keys(images).forEach(serviceName => { + (imageName => { + let output = ""; + if (!promise) { + promise = pushTags(connection, imageName, (data) => output += data); + } else { + promise = promise.then(() => pushTags(connection, imageName, (data) => output += data)); + } + promise = promise.then(() => outputUpdate(utils.writeTaskOutput(`push_${imageName}`, output))); + })(images[serviceName]); + }); + return promise; + }); +} diff --git a/Tasks/DockerComposeV1/dockercomposerun.ts b/Tasks/DockerComposeV1/dockercomposerun.ts new file mode 100644 index 000000000000..05e5b36bd71d --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposerun.ts @@ -0,0 +1,67 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; +import * as utils from "./utils"; + +export async function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): Promise { + var command = connection.createComposeCommand(); + command.arg("run"); + + var detached = tl.getBoolInput("detached"); + if (detached) { + command.arg("-d"); + } + + var entrypoint = tl.getInput("entrypoint"); + if (entrypoint) { + command.arg(["--entrypoint", entrypoint]); + } + + var containerName = tl.getInput("containerName"); + if (containerName) { + command.arg(["--name", containerName]); + } + + tl.getDelimitedInput("ports", "\n").forEach(port => { + command.arg(["-p", port]); + }); + + if (!detached) { + command.arg("--rm"); + } + + command.arg("-T"); + + var workDir = tl.getInput("workDir"); + if (workDir) { + command.arg(["-w", workDir]); + } + + var serviceName = tl.getInput("serviceName", true); + command.arg(serviceName); + + var arg = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(arg || ""); + command.line(commandArgs || ""); + + var containerCommand = tl.getInput("containerCommand"); + if (containerCommand) { + command.line(containerCommand); + } + + try { + await connection.execCommandWithLogging(command) + .then((output) => outputUpdate(utils.writeTaskOutput("run", output))); + } finally { + if (!detached) { + + var downCommand = connection.createComposeCommand(); + downCommand.arg("down"); + + await connection.execCommandWithLogging(downCommand) + .then((output) => outputUpdate(utils.writeTaskOutput("down", output))); + } + } +} diff --git a/Tasks/DockerComposeV1/dockercomposeup.ts b/Tasks/DockerComposeV1/dockercomposeup.ts new file mode 100644 index 000000000000..0c9016106ed2 --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposeup.ts @@ -0,0 +1,33 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; +import DockerComposeConnection from "./dockercomposeconnection"; +import * as dockerCommandUtils from "azure-pipelines-tasks-docker-common/dockercommandutils"; +import * as utils from "./utils"; + +export function run(connection: DockerComposeConnection, outputUpdate: (data: string) => any): any { + var command = connection.createComposeCommand(); + command.arg("up"); + + var detached = tl.getBoolInput("detached"); + if (detached) { + command.arg("-d"); + } + + var buildImages = tl.getBoolInput("buildImages"); + if (buildImages) { + command.arg("--build"); + } + + var abortOnContainerExit = tl.getBoolInput("abortOnContainerExit"); + if (!detached && abortOnContainerExit) { + command.arg("--abort-on-container-exit"); + } + + var arg = tl.getInput("arguments", false); + var commandArgs = dockerCommandUtils.getCommandArguments(arg || ""); + command.line(commandArgs || ""); + + return connection.execCommandWithLogging(command) + .then((output) => outputUpdate(utils.writeTaskOutput("up", output))) +} diff --git a/Tasks/DockerComposeV1/dockercomposeutils.ts b/Tasks/DockerComposeV1/dockercomposeutils.ts new file mode 100644 index 000000000000..5f9d7653d4ee --- /dev/null +++ b/Tasks/DockerComposeV1/dockercomposeutils.ts @@ -0,0 +1,22 @@ +"use strict"; + +import * as tl from "azure-pipelines-task-lib/task"; + +export function findDockerFile(dockerfilepath: string, currentWorkingDirectory: string = ""): string { + if (dockerfilepath.indexOf('*') >= 0 || dockerfilepath.indexOf('?') >= 0) { + tl.debug(tl.loc('ContainerPatternFound')); + var rootPath = currentWorkingDirectory.length > 0 ? currentWorkingDirectory : tl.getVariable('System.DefaultWorkingDirectory'); + var allFiles = tl.find(rootPath); + var matchingResultsFiles = tl.match(allFiles, dockerfilepath, rootPath, { matchBase: true }); + + if (!matchingResultsFiles || matchingResultsFiles.length == 0) { + throw new Error(tl.loc('ContainerDockerFileNotFound', dockerfilepath)); + } + + return matchingResultsFiles[0]; + } + else { + tl.debug(tl.loc('ContainerPatternNotFound')); + return dockerfilepath; + } +} diff --git a/Tasks/DockerComposeV1/icon.png b/Tasks/DockerComposeV1/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3561e582b08b762d39bb392bd4e6e159ea123c GIT binary patch literal 789 zcmV+w1M2*VP)gp=V zWD?u9ab5Sqpydn<3=j&10REnN25t-Vo0@*J*!I}k+9Dp0v$(j3<2W@1EoXFe)N6h_ zE`h+~E`!j&U0yxvqP3;yQA#P6mX?^GpJ#h}yY8SKhG8%?Gz5Uw`fEwR=Qn-y^_boO z8yg$M<8fA2RyaI7ymDZVXf%pp7y!T8+S=@rfW96R$8lI#SRfXQv9q(|TTl-XVSIcX z0Il^WC_9AH)6*mp2@Vbpd=Km~I54TZ*MQaAHy)dc>UKRz_KhRCno_Akq;tLs&lCX?d|Oq3zbrra<=p6=;&Z-YKrde zZh%-Y7@RF1_w_bB>i`FXK>~pQTI=6OM@KI#%gQ%QKouO|yX(5|J3BkGrw9K7E@k-R TL%tvA00000NkvXXu0mjfh<05? literal 0 HcmV?d00001 diff --git a/Tasks/DockerComposeV1/icon.svg b/Tasks/DockerComposeV1/icon.svg new file mode 100644 index 000000000000..7671582150c5 --- /dev/null +++ b/Tasks/DockerComposeV1/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Tasks/DockerComposeV1/make.json b/Tasks/DockerComposeV1/make.json new file mode 100644 index 000000000000..de959ee5f012 --- /dev/null +++ b/Tasks/DockerComposeV1/make.json @@ -0,0 +1,12 @@ +{ + "rm": [ + { + "items": [ + "node_modules/azure-pipelines-tasks-docker-common/node_modules/azure-pipelines-task-lib", + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/azure-pipelines-task-lib", + "node_modules/https-proxy-agent/node_modules/agent-base" + ], + "options": "-Rf" + } + ] +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/package-lock.json b/Tasks/DockerComposeV1/package-lock.json new file mode 100644 index 000000000000..30a4d09a1497 --- /dev/null +++ b/Tasks/DockerComposeV1/package-lock.json @@ -0,0 +1,1392 @@ +{ + "name": "DockerComposeV1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^16.11.39", + "@types/q": "^1.5.0", + "agent-base": "^6.0.2", + "azure-pipelines-task-lib": "^4.13.0", + "azure-pipelines-tasks-docker-common": "2.226.0", + "del": "2.2.0", + "esprima": "2.7.1", + "js-yaml": "3.13.1" + }, + "devDependencies": { + "typescript": "4.0.2" + } + }, + "node_modules/@azure/msal-common": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.1.1.tgz", + "integrity": "sha512-we9xR8lvu47fF0h+J8KyXoRy9+G/fPzm3QEa2TrdR3jaVS3LKAyE2qyMuUkNdbVkvzl8Zr9f7l+IUSP22HeqXw==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.14.5.tgz", + "integrity": "sha512-NcVdMfn8Z3ogN+9RjOSF7uwf2Gki5DEJl0BdDSL83KUAgVAobtkZi5W8EqxbJLrTO/ET0jv5DregrcR5qg2pEA==", + "deprecated": "A newer major version of this library is available. Please upgrade to the latest available version.", + "dependencies": { + "@azure/msal-common": "^9.0.1", + "jsonwebtoken": "^8.5.1", + "uuid": "^8.3.0" + }, + "engines": { + "node": "10 || 12 || 14 || 16 || 18" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" + }, + "node_modules/@types/node": { + "version": "16.18.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.39.tgz", + "integrity": "sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==" + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + }, + "node_modules/adm-zip": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz", + "integrity": "sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/async-mutex": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.1.tgz", + "integrity": "sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/azure-pipelines-task-lib": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-4.13.0.tgz", + "integrity": "sha512-KVguui31If98vgokNepHUxE3/D8UFB4FHV1U6XxjGOkgxxwKxbupC3knVnEiZA/hNl7X+vmj9KrYOx79iwmezQ==", + "dependencies": { + "adm-zip": "^0.5.10", + "minimatch": "3.0.5", + "nodejs-file-downloader": "^4.11.1", + "q": "^1.5.1", + "semver": "^5.1.0", + "shelljs": "^0.8.5", + "uuid": "^3.0.1" + } + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest": { + "version": "3.226.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-tasks-azure-arm-rest/-/azure-pipelines-tasks-azure-arm-rest-3.226.0.tgz", + "integrity": "sha512-yHibJejhY8GL+sN/zvrjLkcGzHE6QT9FX6LXNZkBd1dcn0MEBKdgGEM5I841Yaj7RxY+EhSfUJSpBCaJrlfkSQ==", + "dependencies": { + "@azure/msal-node": "1.14.5", + "@types/jsonwebtoken": "^8.5.8", + "@types/mocha": "^5.2.7", + "@types/node": "^10.17.0", + "@types/q": "1.5.4", + "async-mutex": "^0.4.0", + "azure-devops-node-api": "^12.0.0", + "azure-pipelines-task-lib": "^3.4.0", + "https-proxy-agent": "^4.0.0", + "jsonwebtoken": "^8.5.1", + "node-fetch": "^2.6.7", + "q": "1.5.1", + "typed-rest-client": "1.8.4", + "xml2js": "0.6.2" + } + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest/node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest/node_modules/azure-pipelines-task-lib": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-3.4.0.tgz", + "integrity": "sha512-3eC4OTFw+7xD7A2aUhxR/j+jRlTI+vVfS0CGxt1pCLs4c/KmY0tQWgbqjD3157kmiucWxELBvgZHaD2gCBe9fg==", + "dependencies": { + "minimatch": "3.0.5", + "mockery": "^2.1.0", + "q": "^1.5.1", + "semver": "^5.1.0", + "shelljs": "^0.8.5", + "sync-request": "6.1.0", + "uuid": "^3.0.1" + } + }, + "node_modules/azure-pipelines-tasks-docker-common": { + "version": "2.226.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-tasks-docker-common/-/azure-pipelines-tasks-docker-common-2.226.0.tgz", + "integrity": "sha512-gk8XJGYviqQrt9OUVX4YhD3JjuowsvLNYJZSalNdFPIVM9ZgEaSohuGL/DoXZFF7x+fgSS8UawsQ5dg+87Ktig==", + "dependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^10.17.0", + "@types/q": "1.5.4", + "@types/uuid": "^8.3.0", + "azure-pipelines-task-lib": "^3.1.0", + "azure-pipelines-tasks-azure-arm-rest": "3.226.0", + "del": "2.2.0", + "q": "1.4.1" + } + }, + "node_modules/azure-pipelines-tasks-docker-common/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/azure-pipelines-tasks-docker-common/node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "node_modules/azure-pipelines-tasks-docker-common/node_modules/azure-pipelines-task-lib": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-3.4.0.tgz", + "integrity": "sha512-3eC4OTFw+7xD7A2aUhxR/j+jRlTI+vVfS0CGxt1pCLs4c/KmY0tQWgbqjD3157kmiucWxELBvgZHaD2gCBe9fg==", + "dependencies": { + "minimatch": "3.0.5", + "mockery": "^2.1.0", + "q": "^1.5.1", + "semver": "^5.1.0", + "shelljs": "^0.8.5", + "sync-request": "6.1.0", + "uuid": "^3.0.1" + } + }, + "node_modules/azure-pipelines-tasks-docker-common/node_modules/azure-pipelines-task-lib/node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/azure-pipelines-tasks-docker-common/node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.0.tgz", + "integrity": "sha512-AZDiRb78EEGYCsAZTG3v+CM5q8J0BIs+wI7QeUtyosm+zIMm4XSmp6aI/K7cU9l+YaKpDKN9dYP1xTrNjLQ+LA==", + "dependencies": { + "globby": "^4.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esprima": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.1.tgz", + "integrity": "sha512-hqecf5XLJSqSSCUlVYnz8EAuD5DIfdGbORUyZmA+93aqVzVaOZgqkhTumf0N65HdDDtDHmiieDGh8iERIGsaug==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha512-JPDtMSr0bt25W64q792rvlrSwIaZwqUAhqdYKSr57Wh/xBcQ5JDWLM85ndn+Q1WdBQXLb9YGCl0QN/T0HpqU0A==", + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/https-proxy-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "dependencies": { + "agent-base": "5", + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mockery": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz", + "integrity": "sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodejs-file-downloader": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.13.0.tgz", + "integrity": "sha512-nI2fKnmJWWFZF6SgMPe1iBodKhfpztLKJTtCtNYGhm/9QXmWa/Pk9Sv00qHgzEvNLe1x7hjGDRor7gcm/ChaIQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "https-proxy-agent": "^5.0.0", + "mime-types": "^2.1.27", + "sanitize-filename": "^1.6.3" + } + }, + "node_modules/nodejs-file-downloader/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.4.tgz", + "integrity": "sha512-MyfKKYzk3I6/QQp6e1T50py4qg+c+9BzOEl2rBmQIpStwNUoqQ73An+Tkfy9YuV7O+o2mpVVJpe+fH//POZkbg==", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + } +} diff --git a/Tasks/DockerComposeV1/package.json b/Tasks/DockerComposeV1/package.json new file mode 100644 index 000000000000..7255c8f9739a --- /dev/null +++ b/Tasks/DockerComposeV1/package.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^16.11.39", + "@types/q": "^1.5.0", + "agent-base": "^6.0.2", + "azure-pipelines-task-lib": "^4.13.0", + "azure-pipelines-tasks-docker-common": "2.226.0", + "del": "2.2.0", + "esprima": "2.7.1", + "js-yaml": "3.13.1" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/Tasks/DockerComposeV1/task.json b/Tasks/DockerComposeV1/task.json new file mode 100644 index 000000000000..ab781d031210 --- /dev/null +++ b/Tasks/DockerComposeV1/task.json @@ -0,0 +1,359 @@ +{ + "id": "6975E2D1-96D3-4AFC-8A41-498B5D34EA19", + "name": "DockerCompose", + "friendlyName": "Docker Compose", + "description": "Build, push or run multi-container Docker applications. Task can be used with Docker or Azure Container registry.", + "helpUrl": "https://aka.ms/azpipes-docker-compose-tsg", + "helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?linkid=848006) or [see the Docker Compose documentation](https://docs.docker.com/)", + "category": "Build", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 242, + "Patch": 0 + }, + "demands": [], + "preview": "false", + "groups": [ + { + "name": "advanced", + "displayName": "Advanced Options", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "containerregistrytype", + "type": "pickList", + "label": "Container Registry Type", + "defaultValue": "Azure Container Registry", + "required": true, + "options": { + "Azure Container Registry": "Azure Container Registry", + "Container Registry": "Container Registry" + }, + "helpMarkDown": "Select a Container Registry Type." + }, + { + "name": "dockerRegistryEndpoint", + "type": "connectedService:dockerregistry", + "label": "Docker Registry Service Connection", + "helpMarkDown": "Select a Docker registry service connection. Required for commands that need to authenticate with a registry.", + "visibleRule": "containerregistrytype = Container Registry" + }, + { + "name": "azureSubscriptionEndpoint", + "aliases": [ + "azureSubscription" + ], + "type": "connectedService:AzureRM", + "label": "Azure subscription", + "helpMarkDown": "Select an Azure subscription", + "visibleRule": "containerregistrytype = Azure Container Registry" + }, + { + "name": "azureContainerRegistry", + "label": "Azure Container Registry", + "type": "pickList", + "helpMarkDown": "Select an Azure Container Registry", + "visibleRule": "containerregistrytype = Azure Container Registry", + "defaultValue": "", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "dockerComposeFile", + "type": "filePath", + "label": "Docker Compose File", + "defaultValue": "**/docker-compose.yml", + "required": true, + "helpMarkDown": "Path to the primary Docker Compose file to use." + }, + { + "name": "additionalDockerComposeFiles", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "Additional Docker Compose Files", + "helpMarkDown": "Additional Docker Compose files to be combined with the primary Docker Compose file. Relative paths are resolved relative to the directory containing the primary Docker Compose file. If a specified file is not found, it is ignored. Specify each file path on a new line." + }, + { + "name": "dockerComposeFileArgs", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "Environment Variables", + "helpMarkDown": "Environment variables to be set during the command. Specify each name=value pair on a new line." + }, + { + "name": "projectName", + "type": "string", + "label": "Project Name", + "defaultValue": "$(Build.Repository.Name)", + "helpMarkDown": "Project name used for default naming of images and containers." + }, + { + "name": "qualifyImageNames", + "type": "boolean", + "label": "Qualify Image Names", + "defaultValue": "true", + "helpMarkDown": "Qualify image names for built services with the Docker registry service connection's hostname if not otherwise specified." + }, + { + "name": "action", + "type": "pickList", + "label": "Action", + "required": true, + "options": { + "Build services": "Build service images", + "Push services": "Push service images", + "Run services": "Run service images", + "Run a specific service": "Run a specific service image", + "Lock services": "Lock service images", + "Write service image digests": "Write service image digests", + "Combine configuration": "Combine configuration", + "Run a Docker Compose command": "Run a Docker Compose command" + }, + "defaultValue": "Run a Docker Compose command", + "helpMarkDown": "Select a Docker Compose action." + }, + { + "name": "additionalImageTags", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "Additional Image Tags", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "Additional tags for the Docker images being built or pushed." + }, + { + "name": "includeSourceTags", + "type": "boolean", + "label": "Include Source Tags", + "defaultValue": "false", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "Include Git tags when building or pushing Docker images." + }, + { + "name": "includeLatestTag", + "type": "boolean", + "label": "Include Latest Tag", + "defaultValue": "false", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "Include the 'latest' tag when building or pushing Docker images." + }, + { + "name": "buildImages", + "type": "boolean", + "label": "Build Images", + "defaultValue": "true", + "visibleRule": "action = Run services", + "helpMarkDown": "Build images before starting service containers." + }, + { + "name": "serviceName", + "type": "string", + "label": "Service Name", + "required": true, + "visibleRule": "action = Run a specific service", + "helpMarkDown": "Name of the specific service to run." + }, + { + "name": "containerName", + "type": "string", + "label": "Container Name", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "Name of the specific service container to run." + }, + { + "name": "ports", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "Ports", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "Ports in the specific service container to publish to the host. Specify each host-port:container-port binding on a new line." + }, + { + "name": "workDir", + "aliases": [ + "workingDirectory" + ], + "type": "string", + "label": "Working Directory", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "The working directory for the specific service container." + }, + { + "name": "entrypoint", + "type": "string", + "label": "Entry Point Override", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "Override the default entry point for the specific service container." + }, + { + "name": "containerCommand", + "type": "string", + "label": "Command", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "Command to run in the specific service container. For example, if the image contains a simple Python Flask web application you can specify 'python app.py' to launch the web application." + }, + { + "name": "detached", + "type": "boolean", + "label": "Run in Background", + "defaultValue": "true", + "visibleRule": "action = Run services || action = Run a specific service", + "helpMarkDown": "Run the service containers in the background." + }, + { + "name": "abortOnContainerExit", + "type": "boolean", + "label": "Abort on Container Exit", + "defaultValue": "true", + "visibleRule": "action = Run services && detached == false", + "helpMarkDown": "Stop all containers when any container exits." + }, + { + "name": "imageDigestComposeFile", + "type": "filePath", + "label": "Image Digest Compose File", + "defaultValue": "$(Build.StagingDirectory)/docker-compose.images.yml", + "required": true, + "visibleRule": "action = Write service image digests", + "helpMarkDown": "Path to a Docker Compose file that is created and populated with the full image repository digests of each service's Docker image." + }, + { + "name": "removeBuildOptions", + "type": "boolean", + "label": "Remove Build Options", + "defaultValue": "false", + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "Remove the build options from the output Docker Compose file." + }, + { + "name": "baseResolveDirectory", + "type": "filePath", + "label": "Base Resolve Directory", + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "The base directory from which relative paths in the output Docker Compose file should be resolved." + }, + { + "name": "outputDockerComposeFile", + "type": "filePath", + "label": "Output Docker Compose File", + "defaultValue": "$(Build.StagingDirectory)/docker-compose.yml", + "required": true, + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "Path to an output Docker Compose file." + }, + { + "name": "dockerComposeCommand", + "type": "string", + "label": "Command", + "required": true, + "visibleRule": "action = Run a Docker Compose command", + "helpMarkDown": "Docker Compose command to execute with arguments. For example, 'rm --all' to remove all stopped service containers." + }, + { + "name": "arguments", + "type": "string", + "visibleRule": "action != Lock services && action != Combine configuration && action != Write service image digests", + "label": "Arguments", + "helpMarkDown": "Docker Compose command options. Ex:
For build command,
--pull --compress --parallel" + }, + { + "name": "dockerHostEndpoint", + "type": "connectedService:dockerhost", + "label": "Docker Host Service Connection", + "helpMarkDown": "Select a Docker host service connection. Defaults to the agent's host.", + "groupName": "advanced" + }, + { + "name": "nopIfNoDockerComposeFile", + "type": "boolean", + "label": "No-op if no Docker Compose File", + "defaultValue": "false", + "helpMarkDown": "If the Docker Compose file does not exist, skip this task. This is useful when the task offers optional behavior based on the existence of a Docker Compose file in the repository.", + "groupName": "advanced" + }, + { + "name": "requireAdditionalDockerComposeFiles", + "type": "boolean", + "label": "Require Additional Docker Compose Files", + "defaultValue": "false", + "helpMarkDown": "Produces an error if the additional Docker Compose files do not exist. This overrides the default behavior which is to ignore a file if it does not exist.", + "groupName": "advanced" + }, + { + "name": "cwd", + "aliases": [ + "currentWorkingDirectory" + ], + "type": "filePath", + "label": "Working Directory", + "defaultValue": "$(System.DefaultWorkingDirectory)", + "helpMarkDown": "Working directory for the Docker Compose command.", + "groupName": "advanced" + }, + { + "name": "dockerComposePath", + "label": "Docker Compose executable Path", + "type": "string", + "required": false, + "groupName": "advanced", + "helpMarkDown": "This docker-compose executable will be used if the path is provided", + "defaultValue": "" + } + ], + "dataSourceBindings": [ + { + "target": "azureContainerRegistry", + "endpointId": "$(azureSubscriptionEndpoint)", + "dataSourceName": "AzureRMContainerRegistries", + "resultTemplate": "{\"Value\":\"{\\\"loginServer\\\":\\\"{{{properties.loginServer}}}\\\", \\\"id\\\" : \\\"{{{id}}}\\\"}\",\"DisplayValue\":\"{{{name}}}\"}" + } + ], + "outputVariables": [ + { + "name": "DockerComposeOutput", + "description": "The path of the file(s) which contains the output of the command. This can contain multiple file paths (separated by newline characters) in case of dockerComposeRun command (one for running and one for down), dockerPush (one for each image pushed), dockerBuild (the build itself and all the tag commands) and dockerDigest (one for each image pulled). The other commands only output one file." + } + ], + "instanceNameFormat": "$(action)", + "execution": { + "Node20_1": { + "target": "dockercompose.js", + "argumentFormat": "" + } + }, + "messages": { + "ConnectingToDockerHost": "DOCKER_HOST variable is set. Docker will try to connect to the Docker host: %s", + "ContainerPatternFound": "Pattern found in docker compose filepath parameter", + "ContainerPatternNotFound": "No pattern found in docker compose filepath parameter", + "ContainerDockerFileNotFound": "No Docker file matching %s was found.", + "AdditionalDockerComposeFileDoesNotExists": "Additional Docker Compose File %s does not exists.", + "CantWriteDataToFile": "Can not write data to the file %s. Error: %s", + "DockerHostVariableWarning": "DOCKER_HOST variable is set. Please ensure that the Docker daemon is running on: %s", + "NoDataWrittenOnFile": "No data was written into the file %s", + "FileContentSynced": "Synced the file content to the disk. The content is %s.", + "ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", + "WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", + "DeprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", + "InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", + "MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" + } +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/task.loc.json b/Tasks/DockerComposeV1/task.loc.json new file mode 100644 index 000000000000..2615d283d070 --- /dev/null +++ b/Tasks/DockerComposeV1/task.loc.json @@ -0,0 +1,359 @@ +{ + "id": "6975E2D1-96D3-4AFC-8A41-498B5D34EA19", + "name": "DockerCompose", + "friendlyName": "ms-resource:loc.friendlyName", + "description": "ms-resource:loc.description", + "helpUrl": "https://aka.ms/azpipes-docker-compose-tsg", + "helpMarkDown": "ms-resource:loc.helpMarkDown", + "category": "Build", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 242, + "Patch": 0 + }, + "demands": [], + "preview": "false", + "groups": [ + { + "name": "advanced", + "displayName": "ms-resource:loc.group.displayName.advanced", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "containerregistrytype", + "type": "pickList", + "label": "ms-resource:loc.input.label.containerregistrytype", + "defaultValue": "Azure Container Registry", + "required": true, + "options": { + "Azure Container Registry": "Azure Container Registry", + "Container Registry": "Container Registry" + }, + "helpMarkDown": "ms-resource:loc.input.help.containerregistrytype" + }, + { + "name": "dockerRegistryEndpoint", + "type": "connectedService:dockerregistry", + "label": "ms-resource:loc.input.label.dockerRegistryEndpoint", + "helpMarkDown": "ms-resource:loc.input.help.dockerRegistryEndpoint", + "visibleRule": "containerregistrytype = Container Registry" + }, + { + "name": "azureSubscriptionEndpoint", + "aliases": [ + "azureSubscription" + ], + "type": "connectedService:AzureRM", + "label": "ms-resource:loc.input.label.azureSubscriptionEndpoint", + "helpMarkDown": "ms-resource:loc.input.help.azureSubscriptionEndpoint", + "visibleRule": "containerregistrytype = Azure Container Registry" + }, + { + "name": "azureContainerRegistry", + "label": "ms-resource:loc.input.label.azureContainerRegistry", + "type": "pickList", + "helpMarkDown": "ms-resource:loc.input.help.azureContainerRegistry", + "visibleRule": "containerregistrytype = Azure Container Registry", + "defaultValue": "", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "dockerComposeFile", + "type": "filePath", + "label": "ms-resource:loc.input.label.dockerComposeFile", + "defaultValue": "**/docker-compose.yml", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.dockerComposeFile" + }, + { + "name": "additionalDockerComposeFiles", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "ms-resource:loc.input.label.additionalDockerComposeFiles", + "helpMarkDown": "ms-resource:loc.input.help.additionalDockerComposeFiles" + }, + { + "name": "dockerComposeFileArgs", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "ms-resource:loc.input.label.dockerComposeFileArgs", + "helpMarkDown": "ms-resource:loc.input.help.dockerComposeFileArgs" + }, + { + "name": "projectName", + "type": "string", + "label": "ms-resource:loc.input.label.projectName", + "defaultValue": "$(Build.Repository.Name)", + "helpMarkDown": "ms-resource:loc.input.help.projectName" + }, + { + "name": "qualifyImageNames", + "type": "boolean", + "label": "ms-resource:loc.input.label.qualifyImageNames", + "defaultValue": "true", + "helpMarkDown": "ms-resource:loc.input.help.qualifyImageNames" + }, + { + "name": "action", + "type": "pickList", + "label": "ms-resource:loc.input.label.action", + "required": true, + "options": { + "Build services": "Build service images", + "Push services": "Push service images", + "Run services": "Run service images", + "Run a specific service": "Run a specific service image", + "Lock services": "Lock service images", + "Write service image digests": "Write service image digests", + "Combine configuration": "Combine configuration", + "Run a Docker Compose command": "Run a Docker Compose command" + }, + "defaultValue": "Run a Docker Compose command", + "helpMarkDown": "ms-resource:loc.input.help.action" + }, + { + "name": "additionalImageTags", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "ms-resource:loc.input.label.additionalImageTags", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "ms-resource:loc.input.help.additionalImageTags" + }, + { + "name": "includeSourceTags", + "type": "boolean", + "label": "ms-resource:loc.input.label.includeSourceTags", + "defaultValue": "false", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "ms-resource:loc.input.help.includeSourceTags" + }, + { + "name": "includeLatestTag", + "type": "boolean", + "label": "ms-resource:loc.input.label.includeLatestTag", + "defaultValue": "false", + "visibleRule": "action = Build services || action = Push services", + "helpMarkDown": "ms-resource:loc.input.help.includeLatestTag" + }, + { + "name": "buildImages", + "type": "boolean", + "label": "ms-resource:loc.input.label.buildImages", + "defaultValue": "true", + "visibleRule": "action = Run services", + "helpMarkDown": "ms-resource:loc.input.help.buildImages" + }, + { + "name": "serviceName", + "type": "string", + "label": "ms-resource:loc.input.label.serviceName", + "required": true, + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.serviceName" + }, + { + "name": "containerName", + "type": "string", + "label": "ms-resource:loc.input.label.containerName", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.containerName" + }, + { + "name": "ports", + "type": "multiLine", + "properties": { + "resizable": "true", + "rows": "2" + }, + "label": "ms-resource:loc.input.label.ports", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.ports" + }, + { + "name": "workDir", + "aliases": [ + "workingDirectory" + ], + "type": "string", + "label": "ms-resource:loc.input.label.workDir", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.workDir" + }, + { + "name": "entrypoint", + "type": "string", + "label": "ms-resource:loc.input.label.entrypoint", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.entrypoint" + }, + { + "name": "containerCommand", + "type": "string", + "label": "ms-resource:loc.input.label.containerCommand", + "visibleRule": "action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.containerCommand" + }, + { + "name": "detached", + "type": "boolean", + "label": "ms-resource:loc.input.label.detached", + "defaultValue": "true", + "visibleRule": "action = Run services || action = Run a specific service", + "helpMarkDown": "ms-resource:loc.input.help.detached" + }, + { + "name": "abortOnContainerExit", + "type": "boolean", + "label": "ms-resource:loc.input.label.abortOnContainerExit", + "defaultValue": "true", + "visibleRule": "action = Run services && detached == false", + "helpMarkDown": "ms-resource:loc.input.help.abortOnContainerExit" + }, + { + "name": "imageDigestComposeFile", + "type": "filePath", + "label": "ms-resource:loc.input.label.imageDigestComposeFile", + "defaultValue": "$(Build.StagingDirectory)/docker-compose.images.yml", + "required": true, + "visibleRule": "action = Write service image digests", + "helpMarkDown": "ms-resource:loc.input.help.imageDigestComposeFile" + }, + { + "name": "removeBuildOptions", + "type": "boolean", + "label": "ms-resource:loc.input.label.removeBuildOptions", + "defaultValue": "false", + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "ms-resource:loc.input.help.removeBuildOptions" + }, + { + "name": "baseResolveDirectory", + "type": "filePath", + "label": "ms-resource:loc.input.label.baseResolveDirectory", + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "ms-resource:loc.input.help.baseResolveDirectory" + }, + { + "name": "outputDockerComposeFile", + "type": "filePath", + "label": "ms-resource:loc.input.label.outputDockerComposeFile", + "defaultValue": "$(Build.StagingDirectory)/docker-compose.yml", + "required": true, + "visibleRule": "action = Lock services || action = Combine configuration", + "helpMarkDown": "ms-resource:loc.input.help.outputDockerComposeFile" + }, + { + "name": "dockerComposeCommand", + "type": "string", + "label": "ms-resource:loc.input.label.dockerComposeCommand", + "required": true, + "visibleRule": "action = Run a Docker Compose command", + "helpMarkDown": "ms-resource:loc.input.help.dockerComposeCommand" + }, + { + "name": "arguments", + "type": "string", + "visibleRule": "action != Lock services && action != Combine configuration && action != Write service image digests", + "label": "ms-resource:loc.input.label.arguments", + "helpMarkDown": "ms-resource:loc.input.help.arguments" + }, + { + "name": "dockerHostEndpoint", + "type": "connectedService:dockerhost", + "label": "ms-resource:loc.input.label.dockerHostEndpoint", + "helpMarkDown": "ms-resource:loc.input.help.dockerHostEndpoint", + "groupName": "advanced" + }, + { + "name": "nopIfNoDockerComposeFile", + "type": "boolean", + "label": "ms-resource:loc.input.label.nopIfNoDockerComposeFile", + "defaultValue": "false", + "helpMarkDown": "ms-resource:loc.input.help.nopIfNoDockerComposeFile", + "groupName": "advanced" + }, + { + "name": "requireAdditionalDockerComposeFiles", + "type": "boolean", + "label": "ms-resource:loc.input.label.requireAdditionalDockerComposeFiles", + "defaultValue": "false", + "helpMarkDown": "ms-resource:loc.input.help.requireAdditionalDockerComposeFiles", + "groupName": "advanced" + }, + { + "name": "cwd", + "aliases": [ + "currentWorkingDirectory" + ], + "type": "filePath", + "label": "ms-resource:loc.input.label.cwd", + "defaultValue": "$(System.DefaultWorkingDirectory)", + "helpMarkDown": "ms-resource:loc.input.help.cwd", + "groupName": "advanced" + }, + { + "name": "dockerComposePath", + "label": "ms-resource:loc.input.label.dockerComposePath", + "type": "string", + "required": false, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.dockerComposePath", + "defaultValue": "" + } + ], + "dataSourceBindings": [ + { + "target": "azureContainerRegistry", + "endpointId": "$(azureSubscriptionEndpoint)", + "dataSourceName": "AzureRMContainerRegistries", + "resultTemplate": "{\"Value\":\"{\\\"loginServer\\\":\\\"{{{properties.loginServer}}}\\\", \\\"id\\\" : \\\"{{{id}}}\\\"}\",\"DisplayValue\":\"{{{name}}}\"}" + } + ], + "outputVariables": [ + { + "name": "DockerComposeOutput", + "description": "The path of the file(s) which contains the output of the command. This can contain multiple file paths (separated by newline characters) in case of dockerComposeRun command (one for running and one for down), dockerPush (one for each image pushed), dockerBuild (the build itself and all the tag commands) and dockerDigest (one for each image pulled). The other commands only output one file." + } + ], + "instanceNameFormat": "ms-resource:loc.instanceNameFormat", + "execution": { + "Node20_1": { + "target": "dockercompose.js", + "argumentFormat": "" + } + }, + "messages": { + "ConnectingToDockerHost": "ms-resource:loc.messages.ConnectingToDockerHost", + "ContainerPatternFound": "ms-resource:loc.messages.ContainerPatternFound", + "ContainerPatternNotFound": "ms-resource:loc.messages.ContainerPatternNotFound", + "ContainerDockerFileNotFound": "ms-resource:loc.messages.ContainerDockerFileNotFound", + "AdditionalDockerComposeFileDoesNotExists": "ms-resource:loc.messages.AdditionalDockerComposeFileDoesNotExists", + "CantWriteDataToFile": "ms-resource:loc.messages.CantWriteDataToFile", + "DockerHostVariableWarning": "ms-resource:loc.messages.DockerHostVariableWarning", + "NoDataWrittenOnFile": "ms-resource:loc.messages.NoDataWrittenOnFile", + "FileContentSynced": "ms-resource:loc.messages.FileContentSynced", + "ImageNameWithoutTag": "ms-resource:loc.messages.ImageNameWithoutTag", + "WritingDockerConfigToTempFile": "ms-resource:loc.messages.WritingDockerConfigToTempFile", + "DeprecationMessage": "ms-resource:loc.messages.DeprecationMessage", + "InvalidProjectName": "ms-resource:loc.messages.InvalidProjectName", + "MigrateToDockerComposeV2": "ms-resource:loc.messages.MigrateToDockerComposeV2" + } +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/tsconfig.json b/Tasks/DockerComposeV1/tsconfig.json new file mode 100644 index 000000000000..79a868c8d1e3 --- /dev/null +++ b/Tasks/DockerComposeV1/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/Tasks/DockerComposeV1/utils.ts b/Tasks/DockerComposeV1/utils.ts new file mode 100644 index 000000000000..604193949e14 --- /dev/null +++ b/Tasks/DockerComposeV1/utils.ts @@ -0,0 +1,36 @@ +"use strict"; +import * as tl from "azure-pipelines-task-lib/task"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import * as fileutils from "azure-pipelines-tasks-docker-common/fileutils"; + +export function getFinalComposeFileName(): string { + return ".docker-compose." + Date.now() + ".yml" +} + +export function writeFileSync(filename: string, data: any, options?: { encoding?: BufferEncoding; mode?: number; flag?: string; }): void { + fs.writeFileSync(filename, data, options); +} + +function getTaskOutputDir(command: string): string { + let tempDirectory = tl.getVariable('agent.tempDirectory') || os.tmpdir(); + let taskOutputDir = path.join(tempDirectory, "task_outputs"); + return taskOutputDir; +} + +export function writeTaskOutput(commandName: string, output: string): string { + let escapedCommandName = commandName.replace(/[\\\/:\*\?"<>\|]/g, ""); + let taskOutputDir = getTaskOutputDir(escapedCommandName); + if (!fs.existsSync(taskOutputDir)) { + fs.mkdirSync(taskOutputDir); + } + + let outputFileName = escapedCommandName + "_" + Date.now() + ".log"; + let taskOutputPath = path.join(taskOutputDir, outputFileName); + if (fileutils.writeFileSync(taskOutputPath, output) == 0) { + tl.warning(tl.loc('NoDataWrittenOnFile', taskOutputPath)); + } + + return taskOutputPath; +} diff --git a/_generated/DockerComposeV0.versionmap.txt b/_generated/DockerComposeV0.versionmap.txt index 33dd2668511c..b96232092cd9 100644 --- a/_generated/DockerComposeV0.versionmap.txt +++ b/_generated/DockerComposeV0.versionmap.txt @@ -1,2 +1,2 @@ -Default|0.242.2 -Node20_229_2|0.242.3 +Default|0.242.4 +Node20_229_2|0.242.5 diff --git a/_generated/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson b/_generated/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson index da4e38ca91f4..6983106d2444 100644 --- a/_generated/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/DockerComposeV0/Strings/resources.resjson/en-US/resources.resjson @@ -82,5 +82,5 @@ "loc.messages.ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "loc.messages.WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "loc.messages.InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "loc.messages.MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "loc.messages.MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" } \ No newline at end of file diff --git a/_generated/DockerComposeV0/Tests/L0.ts b/_generated/DockerComposeV0/Tests/L0.ts index bed9005ab92a..9b9d50a8fbcc 100644 --- a/_generated/DockerComposeV0/Tests/L0.ts +++ b/_generated/DockerComposeV0/Tests/L0.ts @@ -3,348 +3,318 @@ import * as assert from 'assert'; import * as ttm from 'azure-pipelines-task-lib/mock-test'; import tl = require('azure-pipelines-task-lib'); -[{ - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: true -}, { - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: false -}].forEach((featureFlag) => { - process.env.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE = String(featureFlag.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE); - - describe('Docker Compose Suite', function() { - this.timeout(30000); - let composeCommand: string; - - before(async() => { - composeCommand = tl.getBoolFeatureFlag('USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE') ? "docker compose --compatibility" : "docker-compose"; +describe('Docker Compose Suite', function() { + this.timeout(30000); + let composeCommand: string; + + before(() => { + composeCommand = "docker compose"; + }); + + beforeEach(() => { + delete process.env["__command__"]; + delete process.env["__container_type__"]; + delete process.env["__qualifyImageNames__"]; + delete process.env["__additionalDockerComposeFiles__"]; + delete process.env["__composeFilePath__"]; + delete process.env["__dockerComposeCommand__"]; + delete process.env["__arguments__"]; + delete process.env["__dockerComposePath__"]; + }); + + if (tl.getPlatform() === tl.Platform.Windows) { + it('Runs successfully for windows docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); }); - beforeEach(() => { - delete process.env["__command__"]; - delete process.env["__container_type__"]; - delete process.env["__qualifyImageNames__"]; - delete process.env["__additionalDockerComposeFiles__"]; - delete process.env["__composeFilePath__"]; - delete process.env["__dockerComposeCommand__"]; - delete process.env["__arguments__"]; - delete process.env["__dockerComposePath__"]; + it('Runs successfully for windows docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); }); - if (tl.osType().match(/^Win/)) { - it('Runs successfully for windows docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - }); - - it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for windows docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose run service', async() => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - console.log(tr.stdout); - console.log(tr.stderr); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); - - }); - - it('Runs successfully for windows docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - } else { - it('Runs successfully for linux docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose run service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - - it('Runs successfully for linux docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - } - }); + it('Runs successfully for windows docker compose run service', async() => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); + }); + + it('Runs successfully for windows docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + } else { + it('Runs successfully for linux docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose run service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + + it('Runs successfully for linux docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + } }); \ No newline at end of file diff --git a/_generated/DockerComposeV0/Tests/L0Linux.ts b/_generated/DockerComposeV0/Tests/L0Linux.ts index b4a6cc18a864..4e0addc60f08 100644 --- a/_generated/DockerComposeV0/Tests/L0Linux.ts +++ b/_generated/DockerComposeV0/Tests/L0Linux.ts @@ -33,8 +33,8 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; -process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; -process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; process.env['AGENT_HOMEDIRECTORY'] = '/tmp/tempdir/100/'; // provide answers for task mock @@ -48,27 +48,27 @@ let a: any = { "docker-compose": true }, "exec": { - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, @@ -80,59 +80,59 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up": { + "docker compose -f /tmp/tempdir/100/docker-compose.yml up": { "code": 0, "stdout": "sucessfully ran services" }, - "docker push ajgtestacr1.azurecr.io/100_web":{ + "docker push ajgtestacr1.azurecr.io/100_web": { "code": 0, "stdout": "successfully pushed with qualified image" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" } diff --git a/_generated/DockerComposeV0/Tests/L0Windows.ts b/_generated/DockerComposeV0/Tests/L0Windows.ts index 91fda927a028..34352fc1e454 100644 --- a/_generated/DockerComposeV0/Tests/L0Windows.ts +++ b/_generated/DockerComposeV0/Tests/L0Windows.ts @@ -52,7 +52,7 @@ let a: any = { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build" : { + "docker compose -f F:\\dir2\\docker-compose.yml build" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -64,7 +64,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml config" : { + "docker compose -f F:\\dir2\\docker-compose.yml config" : { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, @@ -80,11 +80,15 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, + "docker compose -f F:\\dir2\\docker-compose.yml up": { + "code": 0, + "stdout": "sucessfully ran services" + }, "docker-compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { + "docker compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -92,7 +96,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -104,7 +108,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -112,7 +116,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -120,7 +124,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -128,7 +132,7 @@ let a: any = { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml pull service1 service2" : { + "docker compose -f F:\\dir2\\docker-compose.yml pull service1 service2" : { "code": 0, "stdout": "successfully pulled the passed service images" } diff --git a/_generated/DockerComposeV0/dockercomposeconnection.ts b/_generated/DockerComposeV0/dockercomposeconnection.ts index 893fb8812a09..5bd24c20e5f9 100644 --- a/_generated/DockerComposeV0/dockercomposeconnection.ts +++ b/_generated/DockerComposeV0/dockercomposeconnection.ts @@ -19,11 +19,9 @@ export default class DockerComposeConnection extends ContainerConnection { private requireAdditionalDockerComposeFiles: boolean; private projectName: string; private finalComposeFile: string; - private useDockerComposeV2: boolean; constructor() { super(); - this.useDockerComposeV2 = tl.getBoolFeatureFlag("USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE"); this.setDockerComposePath(); this.dockerComposeFile = DockerComposeUtils.findDockerFile(tl.getInput("dockerComposeFile", true), tl.getInput("cwd")); if (!this.dockerComposeFile) { @@ -95,7 +93,7 @@ export default class DockerComposeConnection extends ContainerConnection { public createComposeCommand(): tr.ToolRunner { var command = tl.tool(this.dockerComposePath); - if (this.useDockerComposeV2 && !tl.getInput('dockerComposePath')) { + if (!tl.getInput('dockerComposePath')) { command.arg("compose"); process.env["COMPOSE_COMPATIBILITY"] = "true"; } @@ -186,10 +184,9 @@ export default class DockerComposeConnection extends ContainerConnection { //Priority to docker-compose path provided by user this.dockerComposePath = tl.getInput('dockerComposePath'); if (!this.dockerComposePath) { - // If not use the docker-compose avilable on agent - if (this.useDockerComposeV2) { - this.dockerComposePath = tl.which("docker"); - } else { + this.dockerComposePath = tl.which("docker"); + + if (!this.dockerComposePath) { this.dockerComposePath = tl.which("docker-compose"); } @@ -204,7 +201,7 @@ export default class DockerComposeConnection extends ContainerConnection { private validateProjectNameDockerComposeV2() { tl.debug(`Start validating project name ${this.projectName}`); - if (this.dockerComposePath.includes("docker-compose") || !this.useDockerComposeV2) { + if (this.dockerComposePath.includes("docker-compose")) { tl.warning(tl.loc("MigrateToDockerComposeV2")); return; } diff --git a/_generated/DockerComposeV0/task.json b/_generated/DockerComposeV0/task.json index 833b30eab4f1..b4f09ea4204e 100644 --- a/_generated/DockerComposeV0/task.json +++ b/_generated/DockerComposeV0/task.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 2 + "Patch": 4 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", @@ -357,10 +359,10 @@ "ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" }, "_buildConfigMapping": { - "Default": "0.242.2", - "Node20_229_2": "0.242.3" + "Default": "0.242.4", + "Node20_229_2": "0.242.5" } } \ No newline at end of file diff --git a/_generated/DockerComposeV0/task.loc.json b/_generated/DockerComposeV0/task.loc.json index 48d1b5c7176f..26958220b4a8 100644 --- a/_generated/DockerComposeV0/task.loc.json +++ b/_generated/DockerComposeV0/task.loc.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 2 + "Patch": 4 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", @@ -360,7 +362,7 @@ "MigrateToDockerComposeV2": "ms-resource:loc.messages.MigrateToDockerComposeV2" }, "_buildConfigMapping": { - "Default": "0.242.2", - "Node20_229_2": "0.242.3" + "Default": "0.242.4", + "Node20_229_2": "0.242.5" } } \ No newline at end of file diff --git a/_generated/DockerComposeV0_Node20/Strings/resources.resjson/en-US/resources.resjson b/_generated/DockerComposeV0_Node20/Strings/resources.resjson/en-US/resources.resjson index da4e38ca91f4..6983106d2444 100644 --- a/_generated/DockerComposeV0_Node20/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/DockerComposeV0_Node20/Strings/resources.resjson/en-US/resources.resjson @@ -82,5 +82,5 @@ "loc.messages.ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "loc.messages.WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "loc.messages.InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "loc.messages.MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "loc.messages.MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" } \ No newline at end of file diff --git a/_generated/DockerComposeV0_Node20/Tests/L0.ts b/_generated/DockerComposeV0_Node20/Tests/L0.ts index bed9005ab92a..9b9d50a8fbcc 100644 --- a/_generated/DockerComposeV0_Node20/Tests/L0.ts +++ b/_generated/DockerComposeV0_Node20/Tests/L0.ts @@ -3,348 +3,318 @@ import * as assert from 'assert'; import * as ttm from 'azure-pipelines-task-lib/mock-test'; import tl = require('azure-pipelines-task-lib'); -[{ - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: true -}, { - USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE: false -}].forEach((featureFlag) => { - process.env.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE = String(featureFlag.USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE); - - describe('Docker Compose Suite', function() { - this.timeout(30000); - let composeCommand: string; - - before(async() => { - composeCommand = tl.getBoolFeatureFlag('USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE') ? "docker compose --compatibility" : "docker-compose"; +describe('Docker Compose Suite', function() { + this.timeout(30000); + let composeCommand: string; + + before(() => { + composeCommand = "docker compose"; + }); + + beforeEach(() => { + delete process.env["__command__"]; + delete process.env["__container_type__"]; + delete process.env["__qualifyImageNames__"]; + delete process.env["__additionalDockerComposeFiles__"]; + delete process.env["__composeFilePath__"]; + delete process.env["__dockerComposeCommand__"]; + delete process.env["__arguments__"]; + delete process.env["__dockerComposePath__"]; + }); + + if (tl.getPlatform() === tl.Platform.Windows) { + it('Runs successfully for windows docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); }); - beforeEach(() => { - delete process.env["__command__"]; - delete process.env["__container_type__"]; - delete process.env["__qualifyImageNames__"]; - delete process.env["__additionalDockerComposeFiles__"]; - delete process.env["__composeFilePath__"]; - delete process.env["__dockerComposeCommand__"]; - delete process.env["__arguments__"]; - delete process.env["__dockerComposePath__"]; + it('Runs successfully for windows docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); }); - if (tl.osType().match(/^Win/)) { - it('Runs successfully for windows docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - }); - - it('Runs successfully for windows docker compose service build, using user defined docker compose exe', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f F:\\dir2\\docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for windows docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose run service', async() => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - console.log(tr.stdout); - console.log(tr.stderr); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); - - }); - - it('Runs successfully for windows docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - - it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for windows docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Windows.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - } else { - it('Runs successfully for linux docker compose service build', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__dockerComposePath__"] = "docker-compose-userdefined"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); - - }); - - it('Runs successfully for linux docker compose push service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose run service', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run services"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose push service with ACR', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Push services"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__qualifyImageNames__"] = "true"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); - assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); - - }); - - it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry"; - process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; - process.env["__dockerComposeCommand__"] = "up -d" - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); - - }); - - it('Runs successfully for linux docker compose service build with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Build services"; - process.env["__arguments__"] = "--pull --parallel"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); - - }); - - it('Runs successfully for linux docker compose command with arguments', async () => { - let tp = path.join(__dirname, 'L0Linux.js'); - let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); - process.env["__command__"] = "Run a Docker Compose command"; - process.env["__container_type__"] = "Azure Container Registry" - process.env["__dockerComposeCommand__"] = "pull" - process.env["__arguments__"] = "service1 service2"; - - await tr.runAsync(); - - assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); - assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); - assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); - - }); - } - }); + it('Runs successfully for windows docker compose run service', async() => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/dir2_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "F:\\dir2\\docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists F:\\dir2\\docker-compose.override-notpresent.yml") != -1, "successfully identified missing override file."); + }); + + it('Runs successfully for windows docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + + it('Runs successfully for windows docker compose up command with ACR and additional docker compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for windows docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Windows.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f F:\\dir2\\docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + } else { + it('Runs successfully for linux docker compose service build', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose service build, using user defined docker compose path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__dockerComposePath__"] = "docker-compose-userdefined"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build") != -1, "docker compose build should run"); + }); + + it('Runs successfully for linux docker compose push service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push 100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose run service', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run services"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool three times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose push service with ACR', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Push services"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__qualifyImageNames__"] = "true"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]docker push ajgtestacr1.azurecr.io/100_web") != -1, "docker compose push should run"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose file not present warning', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "/tmp/tempdir/100/docker-compose.override-notpresent.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml up -d") != -1, "successfully ran up command"); + assert(tr.stdout.indexOf("vso[task.issue type=warning;source=TaskInternal;]loc_mock_AdditionalDockerComposeFileDoesNotExists /tmp/tempdir/100/docker-compose.override-notpresent.yml") != -1, "successfully identifed missing additional compose file."); + }); + + it('Runs successfully for linux docker compose up command with ACR and additonal compose relative file path', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry"; + process.env["__additionalDockerComposeFiles__"] = "docker-compose.override.yml"; + process.env["__dockerComposeCommand__"] = "up -d" + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d") != -1, "successfully ran up command"); + }); + + it('Runs successfully for linux docker compose service build with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Build services"; + process.env["__arguments__"] = "--pull --parallel"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel") != -1, "docker compose build should run with argumentss"); + }); + + it('Runs successfully for linux docker compose command with arguments', async () => { + let tp = path.join(__dirname, 'L0Linux.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env["__command__"] = "Run a Docker Compose command"; + process.env["__container_type__"] = "Azure Container Registry" + process.env["__dockerComposeCommand__"] = "pull" + process.env["__arguments__"] = "service1 service2"; + + await tr.runAsync(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf("[command]" + composeCommand + " -f /tmp/tempdir/100/docker-compose.yml pull service1 service2") != -1, "docker compose should run with arguments"); + }); + } }); \ No newline at end of file diff --git a/_generated/DockerComposeV0_Node20/Tests/L0Linux.ts b/_generated/DockerComposeV0_Node20/Tests/L0Linux.ts index b4a6cc18a864..4e0addc60f08 100644 --- a/_generated/DockerComposeV0_Node20/Tests/L0Linux.ts +++ b/_generated/DockerComposeV0_Node20/Tests/L0Linux.ts @@ -33,8 +33,8 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; -process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; -process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; process.env['AGENT_HOMEDIRECTORY'] = '/tmp/tempdir/100/'; // provide answers for task mock @@ -48,27 +48,27 @@ let a: any = { "docker-compose": true }, "exec": { - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml build": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config" :{ + "docker-compose-userdefined -f /tmp/tempdir/100/docker-compose.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, @@ -80,59 +80,59 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up": { + "docker compose -f /tmp/tempdir/100/docker-compose.yml up": { "code": 0, "stdout": "sucessfully ran services" }, - "docker push ajgtestacr1.azurecr.io/100_web":{ + "docker push ajgtestacr1.azurecr.io/100_web": { "code": 0, "stdout": "successfully pushed with qualified image" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml up -d":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml build --pull --parallel": { "code": 0, "stdout": "sucessfully built the service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml pull service1 service2" :{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml pull service1 service2": { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker-compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config":{ + "docker compose -f /tmp/tempdir/100/docker-compose.yml -f /tmp/tempdir/100/docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: /tmp/tempdir/100\n image: ajgtestacr1.azurecr.io/100_web\n ports:\n - 5000:5000/tcp\n volumes:\n - /tmp/tempdir/100:/code:rw\nversion: '2.0'" } diff --git a/_generated/DockerComposeV0_Node20/Tests/L0Windows.ts b/_generated/DockerComposeV0_Node20/Tests/L0Windows.ts index 91fda927a028..34352fc1e454 100644 --- a/_generated/DockerComposeV0_Node20/Tests/L0Windows.ts +++ b/_generated/DockerComposeV0_Node20/Tests/L0Windows.ts @@ -52,7 +52,7 @@ let a: any = { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build" : { + "docker compose -f F:\\dir2\\docker-compose.yml build" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -64,7 +64,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml config" : { + "docker compose -f F:\\dir2\\docker-compose.yml config" : { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: C:\\docketest\n ports:\n - 5000:5000/tcp\n volumes:\n - C:\\docketest:/code:rw\nversion: '2.0'" }, @@ -80,11 +80,15 @@ let a: any = { "code": 0, "stdout": "sucessfully ran services" }, + "docker compose -f F:\\dir2\\docker-compose.yml up": { + "code": 0, + "stdout": "sucessfully ran services" + }, "docker-compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { + "docker compose -f F:\\dir2\\docker-compose.yml build --pull --parallel" : { "code": 0, "stdout": "sucessfully built the service images" }, @@ -92,7 +96,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\.docker-compose.12345.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -104,7 +108,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -112,7 +116,7 @@ let a: any = { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { + "docker compose -f F:\\dir2\\docker-compose.yml -f F:\\dir2\\docker-compose.override.yml config": { "code": 0, "stdout": "services:\n redis:\n image: redis:alpine\n web:\n build:\n context: F:\\dir2\n image: ajgtestacr1.azurecr.io/dir2_web\n ports:\n - 5000:5000/tcp\n volumes:\n - F:\\dir2:/code:rw\nversion: '2.0'" }, @@ -120,7 +124,7 @@ let a: any = { "code": 0, "stdout": "successfully ran up command" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml up -d": { + "docker compose -f F:\\dir2\\docker-compose.yml up -d": { "code": 0, "stdout": "successfully ran up command" }, @@ -128,7 +132,7 @@ let a: any = { "code": 0, "stdout": "successfully pulled the passed service images" }, - "docker compose --compatibility -f F:\\dir2\\docker-compose.yml pull service1 service2" : { + "docker compose -f F:\\dir2\\docker-compose.yml pull service1 service2" : { "code": 0, "stdout": "successfully pulled the passed service images" } diff --git a/_generated/DockerComposeV0_Node20/dockercomposeconnection.ts b/_generated/DockerComposeV0_Node20/dockercomposeconnection.ts index 893fb8812a09..5bd24c20e5f9 100644 --- a/_generated/DockerComposeV0_Node20/dockercomposeconnection.ts +++ b/_generated/DockerComposeV0_Node20/dockercomposeconnection.ts @@ -19,11 +19,9 @@ export default class DockerComposeConnection extends ContainerConnection { private requireAdditionalDockerComposeFiles: boolean; private projectName: string; private finalComposeFile: string; - private useDockerComposeV2: boolean; constructor() { super(); - this.useDockerComposeV2 = tl.getBoolFeatureFlag("USE_DOCKER_COMPOSE_V2_COMPATIBLE_MODE"); this.setDockerComposePath(); this.dockerComposeFile = DockerComposeUtils.findDockerFile(tl.getInput("dockerComposeFile", true), tl.getInput("cwd")); if (!this.dockerComposeFile) { @@ -95,7 +93,7 @@ export default class DockerComposeConnection extends ContainerConnection { public createComposeCommand(): tr.ToolRunner { var command = tl.tool(this.dockerComposePath); - if (this.useDockerComposeV2 && !tl.getInput('dockerComposePath')) { + if (!tl.getInput('dockerComposePath')) { command.arg("compose"); process.env["COMPOSE_COMPATIBILITY"] = "true"; } @@ -186,10 +184,9 @@ export default class DockerComposeConnection extends ContainerConnection { //Priority to docker-compose path provided by user this.dockerComposePath = tl.getInput('dockerComposePath'); if (!this.dockerComposePath) { - // If not use the docker-compose avilable on agent - if (this.useDockerComposeV2) { - this.dockerComposePath = tl.which("docker"); - } else { + this.dockerComposePath = tl.which("docker"); + + if (!this.dockerComposePath) { this.dockerComposePath = tl.which("docker-compose"); } @@ -204,7 +201,7 @@ export default class DockerComposeConnection extends ContainerConnection { private validateProjectNameDockerComposeV2() { tl.debug(`Start validating project name ${this.projectName}`); - if (this.dockerComposePath.includes("docker-compose") || !this.useDockerComposeV2) { + if (this.dockerComposePath.includes("docker-compose")) { tl.warning(tl.loc("MigrateToDockerComposeV2")); return; } diff --git a/_generated/DockerComposeV0_Node20/task.json b/_generated/DockerComposeV0_Node20/task.json index da41c95474ea..5699d3587ed5 100644 --- a/_generated/DockerComposeV0_Node20/task.json +++ b/_generated/DockerComposeV0_Node20/task.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 3 + "Patch": 5 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", @@ -361,10 +363,10 @@ "ImageNameWithoutTag": "Image name not specified with tag, pushing all tags of the image specified.", "WritingDockerConfigToTempFile": "Writing Docker config to temp file. File path: %s, Docker config: %s", "InvalidProjectName": "The project name \"%s\" must be a valid docker compose project name. Follow the link for more details: https://docs.docker.com/compose/project-name/#set-a-project-name", - "MigrateToDockerComposeV2": "The task is utilizing Docker Compose V1, but there are plans to transition to Docker Compose V2. It's necessary to investigate how to adapt your project to Docker Compose V2. For guidance on this migration, you can refer to the official Docker Compose documentation at the provided link: https://docs.docker.com/compose/migrate/" + "MigrateToDockerComposeV2": "The task is using Docker Compose V1, which is end-of-life and will be removed from Microsoft-hosted agents July 24. Pipelines running on Microsoft-hosted agents should be updated for Docker Compose v2 compatibility e.g. use compatible container names. For guidance on required updates, please refer to the official Docker Compose documentation at https://docs.docker.com/compose/migrate/" }, "_buildConfigMapping": { - "Default": "0.242.2", - "Node20_229_2": "0.242.3" + "Default": "0.242.4", + "Node20_229_2": "0.242.5" } } \ No newline at end of file diff --git a/_generated/DockerComposeV0_Node20/task.loc.json b/_generated/DockerComposeV0_Node20/task.loc.json index c7e4de4c41c5..4a05f80575e0 100644 --- a/_generated/DockerComposeV0_Node20/task.loc.json +++ b/_generated/DockerComposeV0_Node20/task.loc.json @@ -14,10 +14,12 @@ "version": { "Major": 0, "Minor": 242, - "Patch": 3 + "Patch": 5 }, "demands": [], "preview": "false", + "deprecated": true, + "deprecationMessage": "The DockerCompose@0 task is deprecated. The task uses docker-compose v1 which no longer receives update since July 2023. To use docker compose v2, use the DockerCompose@1 task.", "groups": [ { "name": "advanced", @@ -364,7 +366,7 @@ "MigrateToDockerComposeV2": "ms-resource:loc.messages.MigrateToDockerComposeV2" }, "_buildConfigMapping": { - "Default": "0.242.2", - "Node20_229_2": "0.242.3" + "Default": "0.242.4", + "Node20_229_2": "0.242.5" } } \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8bfbe8d81dde..6fca8a8d93d8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,6 +35,7 @@ variables: taskName: ${{ parameters.task_name }} taskNameIsSet: ${{ not(eq(parameters.task_name, 'TaskNameVN')) }} runCodeQl: ${{ eq(parameters.enableCodeQL, true) }} + system.debug: true extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates diff --git a/make-options.json b/make-options.json index 733484f70dff..00b48c22435d 100644 --- a/make-options.json +++ b/make-options.json @@ -76,6 +76,7 @@ "DockerV1", "DockerV2", "DockerComposeV0", + "DockerComposeV1", "DockerInstallerV0", "DotNetCoreCLIV2", "DotNetCoreInstallerV0",