From 4a15fabff4cd4feb238a5ebe7208f13a6ab9ee65 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 19 Sep 2023 16:46:31 -0700 Subject: [PATCH 1/6] Convert docker action to typescript --- .github/workflows/run-validation.yml | 6 +- .gitignore | 2 + action.yml | 554 +-- azurecontainerapps.ts | 522 +++ dist/index.js | 6061 ++++++++++++++++++++++++++ dist/licenses.txt | 97 + package-lock.json | 491 +++ package.json | 29 + src/CommandHelper.ts | 89 + src/ContainerAppHelper.ts | 439 ++ src/ContainerRegistryHelper.ts | 55 + src/TelemetryHelper.ts | 108 + src/Utility.ts | 58 + tsconfig.json | 9 + 14 files changed, 7968 insertions(+), 552 deletions(-) create mode 100644 .gitignore create mode 100644 azurecontainerapps.ts create mode 100644 dist/index.js create mode 100644 dist/licenses.txt create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/CommandHelper.ts create mode 100644 src/ContainerAppHelper.ts create mode 100644 src/ContainerRegistryHelper.ts create mode 100644 src/TelemetryHelper.ts create mode 100644 src/Utility.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/run-validation.yml b/.github/workflows/run-validation.yml index efb5b534..9495df46 100644 --- a/.github/workflows/run-validation.yml +++ b/.github/workflows/run-validation.yml @@ -1,6 +1,7 @@ name: Run validation on action on: + workflow_dispatch: push: branches: - main @@ -270,11 +271,6 @@ jobs: WORKSPACE_NAME=$(az monitor log-analytics workspace list -g ${{ vars.TEST_RESOURCE_GROUP_NAME }} --query '[?customerId == `${{ env.CUSTOMER_ID }}`].name | [0]') echo "WORKSPACE_NAME=${WORKSPACE_NAME}" >> $GITHUB_ENV - - name: Delete created Azure Container App environment - if: ${{ always() }} - shell: bash - run: az containerapp env delete -g ${{ vars.TEST_RESOURCE_GROUP_NAME }} -n ${{ env.TEST_NEW_CONTAINER_APP_ENV }} -y - - name: Delete created workspace if: ${{ always() }} shell: bash diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..187c09f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# .gitignore +node_modules/ \ No newline at end of file diff --git a/action.yml b/action.yml index 472c5ef7..6897a644 100644 --- a/action.yml +++ b/action.yml @@ -22,13 +22,13 @@ inputs: access token will be generated via "az acr login" and provided to "docker login" to authenticate the requests.' required: false registryUrl: - description: 'The base URL of the Container Registry that the runnable application image will be pushed to.' + description: 'The base URL of the Container Registry that the runnable application image will be pushed to.' required: false - registryUsername: - description: 'The username used to authenticate push requests to the provided Container Registry using the "docker login" action.' + registryUsername: + description: 'The username used to authenticate push requests to the provided Container Registry using the "docker login" action.' required: false - registryPassword: - description: 'The password used to authenticate push requests to the provided Container Registry using the "docker login" action.' + registryPassword: + description: 'The password used to authenticate push requests to the provided Container Registry using the "docker login" action.' required: false azureCredentials: description: | @@ -126,545 +126,5 @@ inputs: default: false runs: - using: "composite" - steps: - - name: Start timer to track action execution length, in milliseconds - if: ${{ always() && inputs.disableTelemetry == 'false' }} - shell: bash - run: | - CA_GH_ACTION_START_MILLISECONDS=$(date +%s%N | cut -b1-13) - echo "CA_GH_ACTION_START_MILLISECONDS=${CA_GH_ACTION_START_MILLISECONDS}" >> $GITHUB_ENV - CA_GH_ACTION_RESULT_ARG="--property 'result=failed'" - echo "CA_GH_ACTION_RESULT_ARG=${CA_GH_ACTION_RESULT_ARG}" >> $GITHUB_ENV - - - name: Check that an ACR name and a registry URL are not provided together - if: ${{ inputs.acrName != '' && inputs.registryUrl != '' }} - shell: bash - run: | - echo "Both 'acrName' and 'registryUrl' arguments cannot be provided together." - exit 1 - - - name: Check for ACR name or any other registry url provided with application source path - if: ${{ inputs.appSourcePath != '' && inputs.acrName == '' && inputs.registryUrl == '' }} - shell: bash - run: | - echo "The 'acrName' argument or the 'registryUrl' argument must also be provided if the 'appSourcePath' argument is provided." - exit 1 - - - name: Check for application source path, a previously built image or a YAML configuration file - if: ${{ inputs.appSourcePath == '' && inputs.imageToDeploy == '' && inputs.yamlConfigPath == '' }} - shell: bash - run: | - echo "One of the following arguments must be provided: appSourcePath, imageToDeploy, yamlConfigPath" - exit 1 - - - name: Check if the YAML configuration file is provided - if: ${{ inputs.yamlConfigPath != '' }} - shell: bash - run: | - CA_GH_ACTION_YAML_PROVIDED=true - echo "CA_GH_ACTION_YAML_PROVIDED=${CA_GH_ACTION_YAML_PROVIDED}" >> $GITHUB_ENV - - - name: Check if only the YAML configuration file is being used - if: ${{ inputs.appSourcePath == '' && inputs.imageToDeploy == '' && inputs.yamlConfigPath != '' }} - shell: bash - run: | - CA_GH_ACTION_ONLY_YAML=true - echo "CA_GH_ACTION_ONLY_YAML=${CA_GH_ACTION_ONLY_YAML}" >> $GITHUB_ENV - - - name: Set Azure CLI to dynamically install missing extensions - shell: bash - run: az config set extension.use_dynamic_install=yes_without_prompt - - - name: Log in to Azure - if: ${{ inputs.azureCredentials != '' }} - uses: azure/login@v1 - with: - creds: ${{ inputs.azureCredentials }} - - - name: Log in to Azure Container Registry - uses: docker/login-action@v2.2.0 - if: ${{ inputs.acrName != '' && inputs.acrUsername != '' && inputs.acrPassword != '' }} - with: - registry: ${{ inputs.acrName }}.azurecr.io - username: ${{ inputs.acrUsername }} - password: ${{ inputs.acrPassword }} - - - name: Log in to Container Registry - uses: docker/login-action@v2.2.0 - if: ${{ inputs.registryUrl != '' && inputs.registryUsername != '' && inputs.registryPassword != '' }} - with: - registry: ${{ inputs.registryUrl }} - username: ${{ inputs.registryUsername }} - password: ${{ inputs.registryPassword }} - - - name: Export Container Registry information to environment variable - if: ${{ inputs.registryUrl != '' && inputs.registryUsername != '' && inputs.registryPassword != '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_REGISTRY_LOGIN_ARG="--registry-server ${{ inputs.registryUrl }} --registry-username ${{ inputs.registryUsername }} --registry-password ${{ inputs.registryPassword }}" - CA_GH_ACTION_REGISTRY_URL=${{ inputs.registryUrl }} - CA_GH_ACTION_REGISTRY_USERNAME=${{ inputs.registryUsername }} - CA_GH_ACTION_REGISTRY_PASSWORD=${{ inputs.registryPassword }} - echo "CA_GH_ACTION_REGISTRY_LOGIN_ARG=${CA_GH_ACTION_REGISTRY_LOGIN_ARG}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_URL=${CA_GH_ACTION_REGISTRY_URL}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_USERNAME=${CA_GH_ACTION_REGISTRY_USERNAME}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_PASSWORD=${CA_GH_ACTION_REGISTRY_PASSWORD}" >> $GITHUB_ENV - - - name: Export Azure Container Registry information to environment variable for Azure CLI command - if: ${{ inputs.acrName != '' && inputs.acrUsername != '' && inputs.acrPassword != '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_REGISTRY_LOGIN_ARG="--registry-server ${{ inputs.acrName }}.azurecr.io --registry-username ${{ inputs.acrUsername }} --registry-password ${{ inputs.acrPassword }}" - CA_GH_ACTION_REGISTRY_URL=${{ inputs.acrName }}.azurecr.io - CA_GH_ACTION_REGISTRY_USERNAME=${{ inputs.acrUsername }} - CA_GH_ACTION_REGISTRY_PASSWORD=${{ inputs.acrPassword }} - echo "CA_GH_ACTION_REGISTRY_LOGIN_ARG=${CA_GH_ACTION_REGISTRY_LOGIN_ARG}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_URL=${CA_GH_ACTION_REGISTRY_URL}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_USERNAME=${CA_GH_ACTION_REGISTRY_USERNAME}" >> $GITHUB_ENV - echo "CA_GH_ACTION_REGISTRY_PASSWORD=${CA_GH_ACTION_REGISTRY_PASSWORD}" >> $GITHUB_ENV - - - name: Get access token to log in to Azure Container Registry - if: ${{ inputs.acrName != '' && (inputs.acrUsername == '' || inputs.acrPassword == '') }} - shell: bash - run: | - CA_GH_ACTION_ACR_ACCESS_TOKEN=$(az acr login --name ${{ inputs.acrName }} --output json --expose-token | jq -r '.accessToken') - echo "CA_GH_ACTION_ACR_ACCESS_TOKEN=${CA_GH_ACTION_ACR_ACCESS_TOKEN}" >> $GITHUB_ENV - docker login ${{ inputs.acrName }}.azurecr.io -u 00000000-0000-0000-0000-000000000000 -p $CA_GH_ACTION_ACR_ACCESS_TOKEN - CA_GH_ACTION_REGISTRY_URL=${{ inputs.acrName }}.azurecr.io - echo "CA_GH_ACTION_REGISTRY_URL=${CA_GH_ACTION_REGISTRY_URL}" >> $GITHUB_ENV - - - name: Export Dockerfile path to environment variable - if: ${{ inputs.appSourcePath != '' && inputs.dockerfilePath != '' }} - shell: bash - run: | - CA_GH_ACTION_DOCKERFILE_PATH="${{ inputs.appSourcePath }}/${{ inputs.dockerfilePath }}" - echo "CA_GH_ACTION_DOCKERFILE_PATH=${CA_GH_ACTION_DOCKERFILE_PATH}" >> $GITHUB_ENV - - - name: Check for existing Dockerfile in application source - if: ${{ inputs.appSourcePath != '' && inputs.dockerfilePath == '' }} - shell: bash - run: | - dockerfilePath=${{ inputs.appSourcePath }}/Dockerfile - if [ -f "$dockerfilePath" ]; then echo "CA_GH_ACTION_DOCKERFILE_PATH=${dockerfilePath}" >> $GITHUB_ENV; fi - - - name: Export name of image to build to environment variable - if: ${{ inputs.imageToBuild != '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_IMAGE_TO_BUILD="${{ inputs.imageToBuild }}" - echo "CA_GH_ACTION_IMAGE_TO_BUILD=${CA_GH_ACTION_IMAGE_TO_BUILD}" >> $GITHUB_ENV - - - name: Determine name of image to build if not provided - if: ${{ env.CA_GH_ACTION_REGISTRY_URL != '' && inputs.imageToBuild == '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_IMAGE_TO_BUILD="${{ env.CA_GH_ACTION_REGISTRY_URL }}/github-action/container-app:${{ github.run_id }}.${{ github.run_attempt }}" - echo "CA_GH_ACTION_IMAGE_TO_BUILD=${CA_GH_ACTION_IMAGE_TO_BUILD}" >> $GITHUB_ENV - - - name: Export name of image to deploy to environment variable - if: ${{ inputs.imageToDeploy != '' }} - shell: bash - run: | - CA_GH_ACTION_IMAGE_TO_DEPLOY="${{ inputs.imageToDeploy }}" - echo "CA_GH_ACTION_IMAGE_TO_DEPLOY=${CA_GH_ACTION_IMAGE_TO_DEPLOY}" >> $GITHUB_ENV - - - name: Update name of image to deploy if not provided - if: ${{ inputs.imageToDeploy == '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_IMAGE_TO_DEPLOY="${{ env.CA_GH_ACTION_IMAGE_TO_BUILD }}" - echo "CA_GH_ACTION_IMAGE_TO_DEPLOY=${CA_GH_ACTION_IMAGE_TO_DEPLOY}" >> $GITHUB_ENV - - - name: Export resource group to environment variable - if: ${{ inputs.resourceGroup != '' }} - shell: bash - run: | - CA_GH_ACTION_RESOURCE_GROUP="${{ inputs.resourceGroup }}" - echo "CA_GH_ACTION_RESOURCE_GROUP=${CA_GH_ACTION_RESOURCE_GROUP}" >> $GITHUB_ENV - - - name: Determine resource group if not provided - if: ${{ inputs.resourceGroup == '' }} - shell: bash - run: | - CA_GH_ACTION_RESOURCE_GROUP="${{ inputs.containerAppName }}-rg" - echo "CA_GH_ACTION_RESOURCE_GROUP=${CA_GH_ACTION_RESOURCE_GROUP}" >> $GITHUB_ENV - - - name: Check if the previously generated resource group exists - if: ${{ inputs.resourceGroup == '' }} - shell: bash - run: | - az group show \ - -n ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -o none && CA_GH_ACTION_RG_EXISTS=true || CA_GH_ACTION_RG_EXISTS=false - echo "CA_GH_ACTION_RG_EXISTS=${CA_GH_ACTION_RG_EXISTS}" >> $GITHUB_ENV - - - name: Get default location for Container Apps to use if resource group doesn't exist and location wasn't provided - if: ${{ env.CA_GH_ACTION_RG_EXISTS == 'false' && inputs.location == '' }} - shell: bash - run: | - CA_GH_ACTION_DEFAULT_LOCATION=$(az provider show -n Microsoft.App --query "resourceTypes[?resourceType=='containerApps'].locations[] | [0]") - CA_GH_ACTION_DEFAULT_LOCATION=$(echo $CA_GH_ACTION_DEFAULT_LOCATION | sed 's/[ !()]//g') - CA_GH_ACTION_DEFAULT_LOCATION=${CA_GH_ACTION_DEFAULT_LOCATION,,} - echo "CA_GH_ACTION_DEFAULT_LOCATION=${CA_GH_ACTION_DEFAULT_LOCATION}" >> $GITHUB_ENV - - - name: Set default location for Container Apps if location was provided - if: ${{ env.CA_GH_ACTION_RG_EXISTS == 'false' && inputs.location != '' }} - shell: bash - run: | - CA_GH_ACTION_DEFAULT_LOCATION="${{ inputs.location }}" - echo "CA_GH_ACTION_DEFAULT_LOCATION=${CA_GH_ACTION_DEFAULT_LOCATION}" >> $GITHUB_ENV - - - name: Create resource group if it doesn't exist - if: ${{ env.CA_GH_ACTION_RG_EXISTS == 'false' }} - shell: bash - run: | - az group create \ - -n ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -l ${{ env.CA_GH_ACTION_DEFAULT_LOCATION }} - - - name: Export location argument to environment variable - if: ${{ inputs.location != '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_LOCATION_ARG="--location ${{ inputs.location }}" - echo "CA_GH_ACTION_CONTAINER_APP_LOCATION_ARG=${CA_GH_ACTION_CONTAINER_APP_LOCATION_ARG}" >> $GITHUB_ENV - - - name: Check if the Container App already exists in the resource group - shell: bash - run: | - az containerapp show \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - -o none && CA_GH_ACTION_RESOURCE_EXISTS=true || CA_GH_ACTION_RESOURCE_EXISTS=false - echo "CA_GH_ACTION_RESOURCE_EXISTS=${CA_GH_ACTION_RESOURCE_EXISTS}" >> $GITHUB_ENV - - - name: Export environment to environment variable - if: ${{ inputs.containerAppEnvironment != '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT="${{ inputs.containerAppEnvironment }}" - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT=${CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT}" >> $GITHUB_ENV - - - name: Determine environment name if not provided - if: ${{ inputs.containerAppEnvironment == '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT="${{ inputs.containerAppName }}-env" - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT=${CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT}" >> $GITHUB_ENV - - - name: Check if Container App environment already exists with the provided/determined name - if: ${{ env.CA_GH_ACTION_RESOURCE_EXISTS == 'false' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - az containerapp env show \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT }} \ - -o none && CA_GH_ACTION_ENVIRONMENT_EXISTS=true || CA_GH_ACTION_ENVIRONMENT_EXISTS=false - echo "CA_GH_ACTION_ENVIRONMENT_EXISTS=${CA_GH_ACTION_ENVIRONMENT_EXISTS}" >> $GITHUB_ENV - - - name: Reuse an existing Container App environment if found in the resource group - if: ${{ env.CA_GH_ACTION_ENVIRONMENT_EXISTS == 'false' && inputs.containerAppEnvironment == '' && env.CA_CH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_TEMP_ENV_NAME=$(az containerapp env list -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} --query '[0].name') - if [[ $CA_GH_ACTION_TEMP_ENV_NAME != '' ]]; then \ - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT=${CA_GH_ACTION_TEMP_ENV_NAME}" >> $GITHUB_ENV; \ - echo "CA_GH_ACTION_ENVIRONMENT_EXISTS=true" >> $GITHUB_ENV; \ - fi - - - name: Create Container App environment if the Container App environment doesn't exist - if: ${{ env.CA_GH_ACTION_ENVIRONMENT_EXISTS == 'false' }} - shell: bash - run: | - az containerapp env create \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT }} \ - ${{ env.CA_GH_ACTION_CONTAINER_APP_LOCATION_ARG }} - - - name: Export runtime stack to environment variable - if: ${{ inputs.runtimeStack != '' && env.CA_GH_ACTION_ONLY_YAML != 'true' }} - shell: bash - run: | - CA_GH_ACTION_RUNTIME_STACK="${{ inputs.runtimeStack }}" - echo "CA_GH_ACTION_RUNTIME_STACK=${CA_GH_ACTION_RUNTIME_STACK}" >> $GITHUB_ENV - - - name: Determine runtime stack if not provided - if: ${{ inputs.appSourcePath != '' && inputs.runtimeStack == '' }} - shell: bash - run: | - docker run \ - --rm \ - -v ${{ inputs.appSourcePath }}:/app \ - mcr.microsoft.com/oryx/cli:builder-debian-buster-20230208.1 \ - /bin/bash \ - -c "oryx dockerfile /app | head -n 1 | sed 's/ARG RUNTIME=//' >> /app/oryx-runtime.txt" - CA_GH_ACTION_RUNTIME_STACK=$(head -n 1 ${{ inputs.appSourcePath }}/oryx-runtime.txt) - echo "CA_GH_ACTION_RUNTIME_STACK=${CA_GH_ACTION_RUNTIME_STACK}" >> $GITHUB_ENV - rm ${{ inputs.appSourcePath }}/oryx-runtime.txt - - - name: Export disabled ingress value to environment variable - if: ${{ inputs.ingress == 'disabled' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_INGRESS_ENABLED="false" - echo "CA_GH_ACTION_INGRESS_ENABLED=${CA_GH_ACTION_INGRESS_ENABLED}" >> $GITHUB_ENV - - - name: Set default value of ingress to 'external' if not provided - if: ${{ inputs.ingress == '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_INGRESS_ENABLED="true" - echo "CA_GH_ACTION_INGRESS_ENABLED=${CA_GH_ACTION_INGRESS_ENABLED}" >> $GITHUB_ENV - CA_GH_ACTION_INGRESS_ARG="--ingress external" - echo "CA_GH_ACTION_INGRESS_ARG=${CA_GH_ACTION_INGRESS_ARG}" >> $GITHUB_ENV - - - name: Export ingress value to environment variable - if: ${{ inputs.ingress != '' && inputs.ingress != 'disabled' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_INGRESS_ENABLED="true" - echo "CA_GH_ACTION_INGRESS_ENABLED=${CA_GH_ACTION_INGRESS_ENABLED}" >> $GITHUB_ENV - CA_GH_ACTION_INGRESS_ARG="--ingress ${{ inputs.ingress }}" - echo "CA_GH_ACTION_INGRESS_ARG=${CA_GH_ACTION_INGRESS_ARG}" >> $GITHUB_ENV - - - name: Export target port to environment variable - if: ${{ env.CA_GH_ACTION_INGRESS_ENABLED == 'true' && inputs.targetPort != '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_TARGET_PORT="${{ inputs.targetPort }}" - echo "CA_GH_ACTION_TARGET_PORT=${CA_GH_ACTION_TARGET_PORT}" >> $GITHUB_ENV - - - name: Determine default target port if not provided and no Dockerfile provided/found - if: ${{ env.CA_GH_ACTION_INGRESS_ENABLED == 'true' && inputs.targetPort == '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' && inputs.appSourcePath != '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - if [[ "${{ env.CA_GH_ACTION_RUNTIME_STACK }}" == "python:"* ]]; then echo "CA_GH_ACTION_TARGET_PORT=80" >> $GITHUB_ENV; else echo "CA_GH_ACTION_TARGET_PORT=8080" >> $GITHUB_ENV; fi - - - name: Default to target port 80 if one wasn't provided or found, and ingress is enabled, and the Container App doesn't exist (prevent overriding) - if: ${{ env.CA_GH_ACTION_INGRESS_ENABLED == 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'false' && env.CA_GH_ACTION_TARGET_PORT == '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_TARGET_PORT="80" - echo "CA_GH_ACTION_TARGET_PORT=${CA_GH_ACTION_TARGET_PORT}" >> $GITHUB_ENV - - - name: Export target port information to environment variable for Azure CLI command - if: ${{ env.CA_GH_ACTION_INGRESS_ENABLED == 'true' && env.CA_GH_ACTION_TARGET_PORT != '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_TARGET_PORT_ARG="--target-port ${{ env.CA_GH_ACTION_TARGET_PORT }}" - echo "CA_GH_ACTION_TARGET_PORT_ARG=${CA_GH_ACTION_TARGET_PORT_ARG}" >> $GITHUB_ENV - - - name: Export environment variables argument to environment variable if the Container App does not exist - if: ${{ inputs.environmentVariables != '' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'false' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG="--env-vars ${{ inputs.environmentVariables }}" - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG=${CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG}" >> $GITHUB_ENV - - - name: Export environment variables argument to environment variable if the Container App does exist - if: ${{ inputs.environmentVariables != '' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG="--replace-env-vars ${{ inputs.environmentVariables }}" - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG=${CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG}" >> $GITHUB_ENV - - - name: Install pack CLI on non-Windows runner - if: ${{ runner.os != 'Windows' && inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' }} - shell: bash - run: (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-${{ runner.os }}.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack) - - - name: Install pack CLI on Windows runner - if: ${{ runner.os == 'Windows' && inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' }} - shell: bash - run: | - mkdir -p $PWD/pack && cd $PWD/pack - curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-windows.zip" -o "pack-windows.zip" - 7z x pack-windows.zip > /dev/null 2>&1 - rm pack-windows.zip && echo "$PWD" >> $GITHUB_PATH - - - name: Set Oryx++ Builder as default builder - if: ${{ inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' }} - shell: bash - run: pack config default-builder mcr.microsoft.com/oryx/builder:20230208.1 - - - name: Set telemetry for Oryx++ Builder - if: ${{ inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' && inputs.disableTelemetry == 'false' }} - shell: bash - run: | - CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG='--env "CALLER_ID=github-actions-v1"' - echo "CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG=${CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG}" >> $GITHUB_ENV - - - name: Disable telemetry for Oryx++ Builder - if: ${{ inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' && inputs.disableTelemetry == 'true' }} - shell: bash - run: | - CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG='--env "ORYX_DISABLE_TELEMETRY=true"' - echo "CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG=${CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG}" >> $GITHUB_ENV - - - name: Create runnable application image using Oryx++ Builder - if: ${{ inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' }} - shell: bash - run: | - pack build \ - ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} \ - --path ${{ inputs.appSourcePath }} \ - --builder mcr.microsoft.com/oryx/builder:20230208.1 \ - --run-image mcr.microsoft.com/oryx/${{ env.CA_GH_ACTION_RUNTIME_STACK }} \ - ${{ env.CA_GH_ACTION_ORYX_BUILDER_TELEMETRY_ARG }} - - - name: Create runnable application image using provided Dockerfile - if: ${{ inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH != '' }} - shell: bash - run: | - docker build \ - --tag ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} \ - --file ${{ env.CA_GH_ACTION_DOCKERFILE_PATH }} \ - ${{ inputs.appSourcePath }} - - - name: Push image to Container Registry - if: ${{ inputs.appSourcePath != '' }} - shell: bash - run: docker push ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} - - - name: Create the Container App from a YAML configuration file - if: ${{ env.CA_GH_ACTION_YAML_PROVIDED == 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'false' }} - shell: bash - run: | - az containerapp create \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - --yaml ${{ inputs.yamlConfigPath }} - - - name: Create the Container App from provided arguments - if: ${{ env.CA_GH_ACTION_YAML_PROVIDED != 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'false' }} - shell: bash - run: | - az containerapp create \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - -i ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} \ - --environment ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT }} \ - ${{ env.CA_GH_ACTION_INGRESS_ARG }} \ - ${{ env.CA_GH_ACTION_TARGET_PORT_ARG }} \ - ${{ env.CA_GH_ACTION_REGISTRY_LOGIN_ARG }} \ - ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG }} - - - name: Update the existing Container App from a YAML configuration file - if: ${{ env.CA_GH_ACTION_YAML_PROVIDED == 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' }} - shell: bash - run: | - az containerapp update \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - --yaml ${{ inputs.yamlConfigPath }} - - - name: Determine whether or not 'update' or 'up' should be used - if: ${{ env.CA_GH_ACTION_YAML_PROVIDED != 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' && (inputs.targetPort != '' || inputs.ingress != '') }} - shell: bash - run: | - CA_GH_ACTION_USE_UP="true" - echo "CA_GH_ACTION_USE_UP=${CA_GH_ACTION_USE_UP}" >> $GITHUB_ENV - - - name: Update the Container Registry details on the existing Container App - if: ${{ env.CA_GH_ACTION_REGISTRY_LOGIN_ARG != '' && env.CA_GH_ACTION_USE_UP != 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - az containerapp registry set \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - --server ${{ env.CA_GH_ACTION_REGISTRY_URL }} \ - --username ${{ env.CA_GH_ACTION_REGISTRY_USERNAME }} \ - --password ${{ env.CA_GH_ACTION_REGISTRY_PASSWORD }} - - - name: Update the existing Container App from provided arguments via 'update' (no ingress values provided) - if: ${{ env.CA_GH_ACTION_USE_UP != 'true' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' }} - shell: bash - run: | - az containerapp update \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - -i ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} \ - ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG }} - - - name: Reset the ingress argument environment variable if it wasn't provided (use default ingress value) - if: ${{ inputs.ingress == '' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' && env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' }} - shell: bash - run: | - CA_GH_ACTION_INGRESS_ARG="" - echo "CA_GH_ACTION_INGRESS_ARG=${CA_GH_ACTION_INGRESS_ARG}" >> $GITHUB_ENV - - - name: Reset the environment variables argument environment variable for the 'up' command - if: ${{ env.CA_GH_ACTION_USE_UP == 'true' && inputs.environmentVariables != '' }} - shell: bash - run: | - CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG="--env-vars ${{ inputs.environmentVariables }}" - echo "CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG=${CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG}" >> $GITHUB_ENV - - - name: Update the existing Container App from provided arguments via 'up' (ingress values provided) - if: ${{ env.CA_GH_ACTION_USE_UP == 'true' }} - shell: bash - run: | - az containerapp up \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} \ - -i ${{ env.CA_GH_ACTION_IMAGE_TO_DEPLOY }} \ - ${{ env.CA_GH_ACTION_TARGET_PORT_ARG }} \ - ${{ env.CA_GH_ACTION_INGRESS_ARG }} \ - ${{ env.CA_GH_ACTION_CONTAINER_APP_ENVIRONMENT_VARIABLES_ARG }} \ - ${{ env.CA_GH_ACTION_REGISTRY_LOGIN_ARG }} - - - name: Disable ingress on the existing Container App - if: ${{ env.CA_GH_ACTION_RESOURCE_EXISTS == 'true' && inputs.ingress == 'disabled' && env.CA_GH_ACTION_YAML_PROVIDED != 'true' }} - shell: bash - run: | - az containerapp ingress disable \ - -g ${{ env.CA_GH_ACTION_RESOURCE_GROUP }} \ - -n ${{ inputs.containerAppName }} - - - name: Mark action as 'succeeded' for telemetry - if: ${{ inputs.disableTelemetry == 'false' }} - shell: bash - run: | - CA_GH_ACTION_RESULT_ARG="--property 'result=succeeded'" - echo "CA_GH_ACTION_RESULT_ARG=${CA_GH_ACTION_RESULT_ARG}" >> $GITHUB_ENV - - - name: End timer that's tracking action execution length, in milliseconds - if: ${{ always() && inputs.disableTelemetry == 'false' }} - shell: bash - run: | - CA_GH_ACTION_END_MILLISECONDS=$(date +%s%N | cut -b1-13) - CA_GH_ACTION_START_MILLISECONDS=${{ env.CA_GH_ACTION_START_MILLISECONDS }} - CA_GH_ACTION_LENGTH_MILLISECONDS_ARG="--processing-time '$((CA_GH_ACTION_END_MILLISECONDS-CA_GH_ACTION_START_MILLISECONDS))'" - echo "CA_GH_ACTION_LENGTH_MILLISECONDS_ARG=${CA_GH_ACTION_LENGTH_MILLISECONDS_ARG}" >> $GITHUB_ENV - - - name: Set telemetry for previously built image scenario - if: ${{ inputs.disableTelemetry == 'false' && inputs.appSourcePath == '' }} - shell: bash - run: | - CA_GH_ACTION_SCENARIO_ARG="--property 'scenario=used-image'" - echo "CA_GH_ACTION_SCENARIO_ARG=${CA_GH_ACTION_SCENARIO_ARG}" >> $GITHUB_ENV - - - name: Set telemetry for built Dockerfile scenario - if: ${{ inputs.disableTelemetry == 'false' && env.CA_GH_ACTION_DOCKERFILE_PATH != '' }} - shell: bash - run: | - CA_GH_ACTION_SCENARIO_ARG="--property 'scenario=used-dockerfile'" - echo "CA_GH_ACTION_SCENARIO_ARG=${CA_GH_ACTION_SCENARIO_ARG}" >> $GITHUB_ENV - - - name: Set telemetry for no Dockerfile scenario - if: ${{ inputs.disableTelemetry == 'false' && inputs.appSourcePath != '' && env.CA_GH_ACTION_DOCKERFILE_PATH == '' }} - shell: bash - run: | - CA_GH_ACTION_SCENARIO_ARG="--property 'scenario=used-builder'" - echo "CA_GH_ACTION_SCENARIO_ARG=${CA_GH_ACTION_SCENARIO_ARG}" >> $GITHUB_ENV - - - name: Log telemetry for action - if: ${{ always() && inputs.disableTelemetry == 'false' }} - shell: bash - run: | - docker run \ - --rm \ - mcr.microsoft.com/oryx/cli:debian-buster-20230207.2 \ - /bin/bash \ - -c "oryx telemetry --event-name 'ContainerAppsGitHubAction' ${{ env.CA_GH_ACTION_LENGTH_MILLISECONDS_ARG }} ${{ env.CA_GH_ACTION_SCENARIO_ARG }} ${{ env.CA_GH_ACTION_RESULT_ARG }}" + using: 'node16' + main: 'dist/index.js' \ No newline at end of file diff --git a/azurecontainerapps.ts b/azurecontainerapps.ts new file mode 100644 index 00000000..ba92c6eb --- /dev/null +++ b/azurecontainerapps.ts @@ -0,0 +1,522 @@ +import * as core from '@actions/core'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ContainerAppHelper } from './src/ContainerAppHelper'; +import { ContainerRegistryHelper } from './src/ContainerRegistryHelper'; +import { TelemetryHelper } from './src/TelemetryHelper'; +import { Utility } from './src/Utility'; + +const util = new Utility(); + +export class azurecontainerapps { + + public static async runMain(): Promise { + let disableTelemetry = core.getInput('disableTelemetry').toLowerCase() === 'true'; + this.initializeHelpers(disableTelemetry); + + try { + // Validate that the arguments provided can be used for one of the supported scenarios + this.validateSupportedScenarioArguments(); + + // Set up the Azure CLI to be used for this task + this.setupAzureCli(); + + // Set up the resources required to deploy a Container App + await this.setupResources(); + + // If an Azure Container Registry name was provided, try to authenticate against it + if (!util.isNullOrEmpty(this.acrName)) { + await this.authenticateAzureContainerRegistryAsync(); + } + + // If the application source was provided, build a runnable application image from it + if (!util.isNullOrEmpty(this.appSourcePath)) { + await this.buildAndPushImageAsync(); + } + + // If no application source was provided, set up the scenario for deploying an existing image + if (util.isNullOrEmpty(this.appSourcePath)) { + this.setupExistingImageScenario(); + } + + // If no YAML configuration file was provided, set up the Container App properties + if (util.isNullOrEmpty(this.yamlConfigPath)) { + this.setupContainerAppProperties(); + } + + // Create/update the Container App + await this.createOrUpdateContainerApp(); + + // If telemetry is enabled, log that the task completed successfully + this.telemetryHelper.setSuccessfulResult(); + } catch (err) { + core.setFailed(err.message); + this.telemetryHelper.setFailedResult(err.message); + } finally { + // Logout of Azure if logged in during this task session + // this.authHelper.logoutAzure(); + + // If telemetry is enabled, will log metadata for this task run + await this.telemetryHelper.sendLogs(); + } + } + + // Azure DevOps build properties + private static buildId: string = process.env.GITHUB_RUN_ID; + private static buildNumber = process.env.GITHUB_RUN_NUMBER; + + // Supported scenario properties + private static appSourcePath: string; + private static acrName: string; + private static imageToDeploy: string; + private static yamlConfigPath: string; + + // Resource properties + private static containerAppName: string; + private static containerAppExists: boolean; + private static location: string; + private static resourceGroup: string; + private static containerAppEnvironment: string; + private static ingressEnabled: boolean; + + // ACR properties + private static acrUsername: string; + private static acrPassword: string; + + // Command line arguments + private static commandLineArgs: string[]; + + // Helper properties + private static telemetryHelper: TelemetryHelper; + //private static authHelper: AzureAuthenticationHelper; + private static appHelper: ContainerAppHelper; + private static registryHelper: ContainerRegistryHelper; + + // Miscellaneous properties + private static imageToBuild: string; + private static runtimeStack: string; + private static ingress: string; + private static targetPort: string; + private static shouldUseUpdateCommand: boolean; + + /** + * Initializes the helpers used by this task. + * @param disableTelemetry - Whether or not to disable telemetry for this task. + */ + private static initializeHelpers(disableTelemetry: boolean) { + // Set up TelemetryHelper for managing telemetry calls + this.telemetryHelper = new TelemetryHelper(disableTelemetry); + + // Set up AzureAuthenticationHelper for managing logging in and out of Azure CLI using provided service connection + // this.authHelper = new AzureAuthenticationHelper(); + + // Set up ContainerAppHelper for managing calls around the Container App + this.appHelper = new ContainerAppHelper(disableTelemetry); + + // Set up ContainerRegistryHelper for managing calls around ACR + this.registryHelper = new ContainerRegistryHelper(); + } + + /** + * Validates the arguments provided to the task for supported scenarios. + * @throws Error if a valid combination of the support scenario arguments is not provided. + */ + private static validateSupportedScenarioArguments() { + + // Get the path to the application source to build and run, if provided + this.appSourcePath = core.getInput('appSourcePath', {required: false}); + + // Get the name of the ACR instance to push images to, if provided + this.acrName = core.getInput('acrName', {required: false}); + + // Get the previously built image to deploy, if provided + this.imageToDeploy = core.getInput('imageToDeploy', {required: false}); + + // Get the YAML configuration file, if provided + this.yamlConfigPath = core.getInput('yamlConfigPath', {required: false}); + + // Ensure that acrName is also provided if appSourcePath is provided + if (!util.isNullOrEmpty(this.appSourcePath) && util.isNullOrEmpty(this.acrName)) { + core.error(`The 'acrName' argument must be provided when the 'appSourcePath' argument is provided.`); + throw Error(`The 'acrName' argument must be provided when the 'appSourcePath' argument is provided.`); + } + + // Ensure that one of appSourcePath, imageToDeploy, or yamlConfigPath is provided + if (util.isNullOrEmpty(this.appSourcePath) && util.isNullOrEmpty(this.imageToDeploy) && util.isNullOrEmpty(this.yamlConfigPath)) { + core.error(`One of the following arguments must be provided: 'appSourcePath', 'imageToDeploy', or 'yamlConfigPath'.`); + } + } + + /** + * Sets up the Azure CLI to be used for this task by logging in to Azure with the provided service connection and + * setting the Azure CLI to dynamically install missing extensions. + */ + private static async setupAzureCli() { + // Set the Azure CLI to dynamically install missing extensions + await util.setAzureCliDynamicInstall(); + } + + /** + * Sets up the resources required to deploy a Container App. This includes the following: + * - Getting or generating the Container App name + * - Getting or discovering the location to deploy resources to + * - Getting or creating the resource group + * - Getting or creating the Container App Environment + */ + private static async setupResources() { + // Get the Container App name if it was provided, or generate it from build variables + this.containerAppName = this.getContainerAppName(); + + // Get the location to deploy resources to, if provided, or use the default location + this.location = await this.getLocation(); + + // Get the resource group to deploy to if it was provided, or generate it from the Container App name + this.resourceGroup = await this.getOrCreateResourceGroup(this.containerAppName, this.location); + + // Determine if the Container App currently exists + this.containerAppExists = await this.appHelper.doesContainerAppExist(this.containerAppName, this.resourceGroup); + + // If the Container App doesn't exist, get/create the Container App Environment to use for the Container App + if (!this.containerAppExists) { + this.containerAppEnvironment = await this.getOrCreateContainerAppEnvironment(this.containerAppName, this.resourceGroup, this.location); + } + } + + /** + * Gets the name of the Container App to use for the task. If the 'containerAppName' argument is not provided, + * then a default name will be generated in the form 'ado-task-app--'. + * @returns The name of the Container App to use for the task. + */ + private static getContainerAppName(): string { + let containerAppName: string = core.getInput('containerAppName', {required: false}); + if (util.isNullOrEmpty(containerAppName)) { + containerAppName = `app-${this.buildId}-${this.buildNumber}`; + + // Replace all '.' characters with '-' characters in the Container App name + containerAppName = containerAppName.replace(/\./gi, "-"); + core.info(`Default Container App name: ${containerAppName}`); + } + + return containerAppName; + } + + /** + * Gets the location to deploy resources to. If the 'location' argument is not provided, then the default location + * for the Container App service will be used. + * @returns The location to deploy resources to. + */ + private static async getLocation(): Promise { + // Set deployment location, if provided + let location: string = core.getInput('location', {required: false}); + + // If no location was provided, use the default location for the Container App service + if (util.isNullOrEmpty(location)) { + location = await this.appHelper.getDefaultContainerAppLocation(); + } + + return location; + } + + /** + * Gets the name of the resource group to use for the task. If the 'resourceGroup' argument is not provided, + * then a default name will be generated in the form '-rg'. If the generated resource group does + * not exist, it will be created. + * @param containerAppName - The name of the Container App to use for the task. + * @param location - The location to deploy resources to. + * @returns The name of the resource group to use for the task. + */ + private static async getOrCreateResourceGroup(containerAppName: string, location: string): Promise { + // Get the resource group to deploy to if it was provided, or generate it from the Container App name + let resourceGroup: string = core.getInput('resourceGroup', {required: false}); + if (util.isNullOrEmpty(resourceGroup)) { + resourceGroup = `${containerAppName}-rg`; + core.info(`Default resource group name: ${resourceGroup}`); + + // Ensure that the resource group that the Container App will be created in exists + const resourceGroupExists = await this.appHelper.doesResourceGroupExist(resourceGroup); + if (!resourceGroupExists) { + await this.appHelper.createResourceGroup(resourceGroup, location); + } + } + + return resourceGroup; + } + + /** + * Gets the name of the Container App Environment to use for the task. If the 'containerAppEnvironment' argument + * is not provided, then the task will attempt to discover an existing Container App Environment in the resource + * group. If no existing Container App Environment is found, then a default name will be generated in the form + * '-env'. If the Container App Environment does not exist, it will be created. + * @param containerAppName - The name of the Container App to use for the task. + * @param resourceGroup - The name of the resource group to use for the task. + * @param location - The location to deploy resources to. + * @returns The name of the Container App Environment to use for the task. + */ + private static async getOrCreateContainerAppEnvironment( + containerAppName: string, + resourceGroup: string, + location: string): Promise { + // Get the Container App environment if it was provided + let containerAppEnvironment: string = core.getInput('containerAppEnvironment', {required: false}); + + // See if we can reuse an existing Container App environment found in the resource group + if (util.isNullOrEmpty(containerAppEnvironment)) { + const existingContainerAppEnvironment: string = await this.appHelper.getExistingContainerAppEnvironment(resourceGroup); + if (!util.isNullOrEmpty(existingContainerAppEnvironment)) { + core.info(`Existing Container App environment found in resource group: ${existingContainerAppEnvironment}`); + return existingContainerAppEnvironment + } + } + + // Generate the Container App environment name if it was not provided + if (util.isNullOrEmpty(containerAppEnvironment)) { + containerAppEnvironment = `${containerAppName}-env`; + core.info(`Default Container App environment name: ${containerAppEnvironment}`); + } + + // Determine if the Container App environment currently exists and create one if it doesn't + const containerAppEnvironmentExists: boolean = await this.appHelper.doesContainerAppEnvironmentExist(containerAppEnvironment, resourceGroup); + if (!containerAppEnvironmentExists) { + await this.appHelper.createContainerAppEnvironment(containerAppEnvironment, resourceGroup, location); + } + + return containerAppEnvironment; + } + + /** + * Authenticates calls to the provided Azure Container Registry. + */ + private static async authenticateAzureContainerRegistryAsync() { + this.acrUsername = core.getInput('acrUsername', {required: false}); + this.acrPassword = core.getInput('acrPassword', {required: false}); + + // Login to ACR if credentials were provided + if (!util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword)) { + core.info(`Logging in to ACR instance "${this.acrName}" with username and password credentials`); + await this.registryHelper.loginAcrWithUsernamePassword(this.acrName, this.acrUsername, this.acrPassword); + } else { + core.info(`No ACR credentials provided; attempting to log in to ACR instance "${this.acrName}" with access token`); + await this.registryHelper.loginAcrWithAccessTokenAsync(this.acrName); + } + } + + /** + * Sets up the scenario where an existing image is used for the Container App. + */ + private static setupExistingImageScenario() { + // If telemetry is enabled, log that the previously built image scenario was targeted for this task + this.telemetryHelper.setImageScenario(); + } + + /** + * Builds a runnable application image using a Dockerfile or the builder and pushes it to ACR. + */ + private static async buildAndPushImageAsync() { + // Get the name of the image to build if it was provided, or generate it from build variables + this.imageToBuild = core.getInput('imageToBuild', {required: false}); + if (util.isNullOrEmpty(this.imageToBuild)) { + this.imageToBuild = `${this.acrName}.azurecr.io/ado-task/container-app:${this.buildId}.${this.buildNumber}`; + core.info(`Default image to build: ${this.imageToBuild}`); + } + + // Get the name of the image to deploy if it was provided, or set it to the value of 'imageToBuild' + if (util.isNullOrEmpty(this.imageToDeploy)) { + this.imageToDeploy = this.imageToBuild; + core.info(`Default image to deploy: ${this.imageToDeploy}`); + } + + // Get Dockerfile to build, if provided, or check if one exists at the root of the provided application + let dockerfilePath: string = core.getInput('dockerfilePath', {required: false}); + if (util.isNullOrEmpty(dockerfilePath)) { + core.info(`No Dockerfile path provided; checking for Dockerfile at root of application source.`); + const rootDockerfilePath = path.join(this.appSourcePath, 'Dockerfile'); + if (fs.existsSync(rootDockerfilePath)) { + core.info(`Dockerfile found at root of application source.`) + dockerfilePath = rootDockerfilePath; + } else { + // No Dockerfile found or provided, build the image using the builder + await this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild); + } + } else { + dockerfilePath = path.join(this.appSourcePath, dockerfilePath); + } + + if (!util.isNullOrEmpty(dockerfilePath)) { + // Build the image from the provided/discovered Dockerfile + await this.builderImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild); + } + + // Push the image to ACR + await this.registryHelper.pushImageToAcr(this.imageToBuild); + } + + /** + * Builds a runnable application image using the builder. + * @param appSourcePath - The path to the application source code. + * @param imageToBuild - The name of the image to build. + */ + private static async buildImageFromBuilderAsync(appSourcePath: string, imageToBuild: string) { + // Install the pack CLI + await this.appHelper.installPackCliAsync(); + core.info(`Successfully installed the pack CLI.`) + + // Get the runtime stack if provided, or determine it using Oryx + this.runtimeStack = core.getInput('runtimeStack', {required: false}); + if (util.isNullOrEmpty(this.runtimeStack)) { + this.runtimeStack = await this.appHelper.determineRuntimeStackAsync(appSourcePath); + core.info(`Runtime stack determined to be: ${this.runtimeStack}`); + } + + core.info(`Building image "${imageToBuild}" using the Oryx++ Builder`); + + // Set the Oryx++ Builder as the default builder locally + await this.appHelper.setDefaultBuilder(); + + // Create a runnable application image + await this.appHelper.createRunnableAppImage(imageToBuild, appSourcePath, this.runtimeStack); + + // If telemetry is enabled, log that the builder scenario was targeted for this task + this.telemetryHelper.setBuilderScenario(); + } + + /** + * Builds a runnable application image using a provided or discovered Dockerfile. + * @param appSourcePath - The path to the application source code. + * @param dockerfilePath - The path to the Dockerfile to build. + * @param imageToBuild - The name of the image to build. + */ + private static async builderImageFromDockerfile(appSourcePath: string, dockerfilePath: string, imageToBuild: string) { + core.info(`Building image "${imageToBuild}" using the provided Dockerfile`); + await this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath); + + // If telemetry is enabled, log that the Dockerfile scenario was targeted for this task + this.telemetryHelper.setDockerfileScenario(); + } + + /** + * Sets up the Container App properties that will be passed through to the Azure CLI when a YAML configuration + * file is not provided. + */ + private static setupContainerAppProperties() { + this.commandLineArgs = []; + + // Get the ingress inputs + this.ingress = core.getInput('ingress', {required: false}); + this.targetPort = core.getInput('targetPort', {required: false}); + + // If both ingress and target port were not provided for an existing Container App, or if ingress is to be disabled, + // use the 'update' command, otherwise we should use the 'up' command that performs a PATCH operation on the ingress properties. + this.shouldUseUpdateCommand = this.containerAppExists && + util.isNullOrEmpty(this.targetPort) && + (util.isNullOrEmpty(this.ingress) || this.ingress == 'disabled'); + + // Pass the ACR credentials when creating a Container App or updating a Container App via the 'up' command + if (!util.isNullOrEmpty(this.acrName) && !util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword) && + (!this.containerAppExists || (this.containerAppExists && !this.shouldUseUpdateCommand))) { + this.commandLineArgs.push( + `--registry-server ${this.acrName}.azurecr.io`, + `--registry-username ${this.acrUsername}`, + `--registry-password ${this.acrPassword}`); + } + + // Determine default values only for the 'create' scenario to avoid overriding existing values for the 'update' scenario + if (!this.containerAppExists) { + this.ingressEnabled = true; + + // Set the ingress value to 'external' if it was not provided + if (util.isNullOrEmpty(this.ingress)) { + this.ingress = 'external'; + core.info(`Default ingress value: ${this.ingress}`); + } + + // Set the value of ingressEnabled to 'false' if ingress was provided as 'disabled' + if (this.ingress == 'disabled') { + this.ingressEnabled = false; + core.info(`Ingress is disabled for this Container App.`); + } + + // Handle setup for ingress values when enabled + if (this.ingressEnabled) { + // Get the target port if provided, or determine it based on the application type + this.targetPort = core.getInput('targetPort', {required: false}); + if (util.isNullOrEmpty(this.targetPort)) { + if (!util.isNullOrEmpty(this.runtimeStack) && this.runtimeStack.startsWith('python:')) { + this.targetPort = '80'; + } else { + this.targetPort = '8080'; + } + + core.info(`Default target port: ${this.targetPort}`); + } + + // Set the target port to 80 if it was not provided or determined + if (util.isNullOrEmpty(this.targetPort)) { + this.targetPort = '80'; + core.info(`Default target port: ${this.targetPort}`); + } + + // Add the ingress value and target port to the optional arguments array + // Note: this step should be skipped if we're updating an existing Container App (ingress is enabled via a separate command) + this.commandLineArgs.push(`--ingress ${this.ingress}`); + this.commandLineArgs.push(`--target-port ${this.targetPort}`); + } + } + + const environmentVariables: string = core.getInput('environmentVariables', {required: false}); + + // Add user-specified environment variables + if (!util.isNullOrEmpty(environmentVariables)) { + // The --replace-env-vars flag is only used for the 'update' command, + // otherwise --env-vars is used for 'create' and 'up' + if (this.shouldUseUpdateCommand) { + this.commandLineArgs.push(`--replace-env-vars ${environmentVariables}`); + } else { + this.commandLineArgs.push(`--env-vars ${environmentVariables}`); + } + } + } + + /** + * Creates or updates the Container App. + */ + private static async createOrUpdateContainerApp() { + if (!this.containerAppExists) { + if (!util.isNullOrEmpty(this.yamlConfigPath)) { + // Create the Container App from the YAML configuration file + await this.appHelper.createContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath); + } else { + // Create the Container App from command line arguments + await this.appHelper.createContainerApp(this.containerAppName, this.resourceGroup, this.containerAppEnvironment, this.imageToDeploy, this.commandLineArgs); + } + + return; + } + + if (!util.isNullOrEmpty(this.yamlConfigPath)) { + // Update the Container App from the YAML configuration file + await this.appHelper.updateContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath); + + return; + } + + if (this.shouldUseUpdateCommand) { + // Update the ACR details on the existing Container App, if provided as an input + if (!util.isNullOrEmpty(this.acrName) && !util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword)) { + await this.appHelper.updateContainerAppRegistryDetails(this.containerAppName, this.resourceGroup, this.acrName, this.acrUsername, this.acrPassword); + } + + // Update the Container App using the 'update' command + await this.appHelper.updateContainerApp(this.containerAppName, this.resourceGroup, this.imageToDeploy, this.commandLineArgs); + } else { + // Update the Container App using the 'up' command + await this.appHelper.updateContainerAppWithUp(this.containerAppName, this.resourceGroup, this.imageToDeploy, this.commandLineArgs, this.ingress, this.targetPort); + } + + // Disable ingress on the existing Container App, if provided as an input + if (this.ingress == 'disabled') { + await this.appHelper.disableContainerAppIngress(this.containerAppName, this.resourceGroup); + } + } +} + +azurecontainerapps.runMain(); \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 00000000..6c495437 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,6061 @@ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 3238: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.azurecontainerapps = void 0; +var core = __nccwpck_require__(3195); +var fs = __nccwpck_require__(7147); +var path = __nccwpck_require__(1017); +var ContainerAppHelper_1 = __nccwpck_require__(2929); +var ContainerRegistryHelper_1 = __nccwpck_require__(4769); +var TelemetryHelper_1 = __nccwpck_require__(7166); +var Utility_1 = __nccwpck_require__(2135); +var util = new Utility_1.Utility(); +var azurecontainerapps = /** @class */ (function () { + function azurecontainerapps() { + } + azurecontainerapps.runMain = function () { + return __awaiter(this, void 0, void 0, function () { + var disableTelemetry, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + disableTelemetry = core.getInput('disableTelemetry').toLowerCase() === 'true'; + this.initializeHelpers(disableTelemetry); + _a.label = 1; + case 1: + _a.trys.push([1, 8, 9, 11]); + // Validate that the arguments provided can be used for one of the supported scenarios + this.validateSupportedScenarioArguments(); + // Set up the Azure CLI to be used for this task + this.setupAzureCli(); + // Set up the resources required to deploy a Container App + return [4 /*yield*/, this.setupResources()]; + case 2: + // Set up the resources required to deploy a Container App + _a.sent(); + if (!!util.isNullOrEmpty(this.acrName)) return [3 /*break*/, 4]; + return [4 /*yield*/, this.authenticateAzureContainerRegistryAsync()]; + case 3: + _a.sent(); + _a.label = 4; + case 4: + if (!!util.isNullOrEmpty(this.appSourcePath)) return [3 /*break*/, 6]; + return [4 /*yield*/, this.buildAndPushImageAsync()]; + case 5: + _a.sent(); + _a.label = 6; + case 6: + // If no application source was provided, set up the scenario for deploying an existing image + if (util.isNullOrEmpty(this.appSourcePath)) { + this.setupExistingImageScenario(); + } + // If no YAML configuration file was provided, set up the Container App properties + if (util.isNullOrEmpty(this.yamlConfigPath)) { + this.setupContainerAppProperties(); + } + // Create/update the Container App + return [4 /*yield*/, this.createOrUpdateContainerApp()]; + case 7: + // Create/update the Container App + _a.sent(); + // If telemetry is enabled, log that the task completed successfully + this.telemetryHelper.setSuccessfulResult(); + return [3 /*break*/, 11]; + case 8: + err_1 = _a.sent(); + core.setFailed(err_1.message); + this.telemetryHelper.setFailedResult(err_1.message); + return [3 /*break*/, 11]; + case 9: + // Logout of Azure if logged in during this task session + // this.authHelper.logoutAzure(); + // If telemetry is enabled, will log metadata for this task run + return [4 /*yield*/, this.telemetryHelper.sendLogs()]; + case 10: + // Logout of Azure if logged in during this task session + // this.authHelper.logoutAzure(); + // If telemetry is enabled, will log metadata for this task run + _a.sent(); + return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; + } + }); + }); + }; + /** + * Initializes the helpers used by this task. + * @param disableTelemetry - Whether or not to disable telemetry for this task. + */ + azurecontainerapps.initializeHelpers = function (disableTelemetry) { + // Set up TelemetryHelper for managing telemetry calls + this.telemetryHelper = new TelemetryHelper_1.TelemetryHelper(disableTelemetry); + // Set up AzureAuthenticationHelper for managing logging in and out of Azure CLI using provided service connection + // this.authHelper = new AzureAuthenticationHelper(); + // Set up ContainerAppHelper for managing calls around the Container App + this.appHelper = new ContainerAppHelper_1.ContainerAppHelper(disableTelemetry); + // Set up ContainerRegistryHelper for managing calls around ACR + this.registryHelper = new ContainerRegistryHelper_1.ContainerRegistryHelper(); + }; + /** + * Validates the arguments provided to the task for supported scenarios. + * @throws Error if a valid combination of the support scenario arguments is not provided. + */ + azurecontainerapps.validateSupportedScenarioArguments = function () { + // Get the path to the application source to build and run, if provided + this.appSourcePath = core.getInput('appSourcePath', { required: false }); + // Get the name of the ACR instance to push images to, if provided + this.acrName = core.getInput('acrName', { required: false }); + // Get the previously built image to deploy, if provided + this.imageToDeploy = core.getInput('imageToDeploy', { required: false }); + // Get the YAML configuration file, if provided + this.yamlConfigPath = core.getInput('yamlConfigPath', { required: false }); + // Ensure that acrName is also provided if appSourcePath is provided + if (!util.isNullOrEmpty(this.appSourcePath) && util.isNullOrEmpty(this.acrName)) { + core.error("The 'acrName' argument must be provided when the 'appSourcePath' argument is provided."); + throw Error("The 'acrName' argument must be provided when the 'appSourcePath' argument is provided."); + } + // Ensure that one of appSourcePath, imageToDeploy, or yamlConfigPath is provided + if (util.isNullOrEmpty(this.appSourcePath) && util.isNullOrEmpty(this.imageToDeploy) && util.isNullOrEmpty(this.yamlConfigPath)) { + core.error("One of the following arguments must be provided: 'appSourcePath', 'imageToDeploy', or 'yamlConfigPath'."); + } + }; + /** + * Sets up the Azure CLI to be used for this task by logging in to Azure with the provided service connection and + * setting the Azure CLI to dynamically install missing extensions. + */ + azurecontainerapps.setupAzureCli = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + // Set the Azure CLI to dynamically install missing extensions + return [4 /*yield*/, util.setAzureCliDynamicInstall()]; + case 1: + // Set the Azure CLI to dynamically install missing extensions + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Sets up the resources required to deploy a Container App. This includes the following: + * - Getting or generating the Container App name + * - Getting or discovering the location to deploy resources to + * - Getting or creating the resource group + * - Getting or creating the Container App Environment + */ + azurecontainerapps.setupResources = function () { + return __awaiter(this, void 0, void 0, function () { + var _a, _b, _c, _d; + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + // Get the Container App name if it was provided, or generate it from build variables + this.containerAppName = this.getContainerAppName(); + // Get the location to deploy resources to, if provided, or use the default location + _a = this; + return [4 /*yield*/, this.getLocation()]; + case 1: + // Get the location to deploy resources to, if provided, or use the default location + _a.location = _e.sent(); + // Get the resource group to deploy to if it was provided, or generate it from the Container App name + _b = this; + return [4 /*yield*/, this.getOrCreateResourceGroup(this.containerAppName, this.location)]; + case 2: + // Get the resource group to deploy to if it was provided, or generate it from the Container App name + _b.resourceGroup = _e.sent(); + // Determine if the Container App currently exists + _c = this; + return [4 /*yield*/, this.appHelper.doesContainerAppExist(this.containerAppName, this.resourceGroup)]; + case 3: + // Determine if the Container App currently exists + _c.containerAppExists = _e.sent(); + if (!!this.containerAppExists) return [3 /*break*/, 5]; + _d = this; + return [4 /*yield*/, this.getOrCreateContainerAppEnvironment(this.containerAppName, this.resourceGroup, this.location)]; + case 4: + _d.containerAppEnvironment = _e.sent(); + _e.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + }; + /** + * Gets the name of the Container App to use for the task. If the 'containerAppName' argument is not provided, + * then a default name will be generated in the form 'ado-task-app--'. + * @returns The name of the Container App to use for the task. + */ + azurecontainerapps.getContainerAppName = function () { + var containerAppName = core.getInput('containerAppName', { required: false }); + if (util.isNullOrEmpty(containerAppName)) { + containerAppName = "app-" + this.buildId + "-" + this.buildNumber; + // Replace all '.' characters with '-' characters in the Container App name + containerAppName = containerAppName.replace(/\./gi, "-"); + core.info("Default Container App name: " + containerAppName); + } + return containerAppName; + }; + /** + * Gets the location to deploy resources to. If the 'location' argument is not provided, then the default location + * for the Container App service will be used. + * @returns The location to deploy resources to. + */ + azurecontainerapps.getLocation = function () { + return __awaiter(this, void 0, void 0, function () { + var location; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + location = core.getInput('location', { required: false }); + if (!util.isNullOrEmpty(location)) return [3 /*break*/, 2]; + return [4 /*yield*/, this.appHelper.getDefaultContainerAppLocation()]; + case 1: + location = _a.sent(); + _a.label = 2; + case 2: return [2 /*return*/, location]; + } + }); + }); + }; + /** + * Gets the name of the resource group to use for the task. If the 'resourceGroup' argument is not provided, + * then a default name will be generated in the form '-rg'. If the generated resource group does + * not exist, it will be created. + * @param containerAppName - The name of the Container App to use for the task. + * @param location - The location to deploy resources to. + * @returns The name of the resource group to use for the task. + */ + azurecontainerapps.getOrCreateResourceGroup = function (containerAppName, location) { + return __awaiter(this, void 0, void 0, function () { + var resourceGroup, resourceGroupExists; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + resourceGroup = core.getInput('resourceGroup', { required: false }); + if (!util.isNullOrEmpty(resourceGroup)) return [3 /*break*/, 3]; + resourceGroup = containerAppName + "-rg"; + core.info("Default resource group name: " + resourceGroup); + return [4 /*yield*/, this.appHelper.doesResourceGroupExist(resourceGroup)]; + case 1: + resourceGroupExists = _a.sent(); + if (!!resourceGroupExists) return [3 /*break*/, 3]; + return [4 /*yield*/, this.appHelper.createResourceGroup(resourceGroup, location)]; + case 2: + _a.sent(); + _a.label = 3; + case 3: return [2 /*return*/, resourceGroup]; + } + }); + }); + }; + /** + * Gets the name of the Container App Environment to use for the task. If the 'containerAppEnvironment' argument + * is not provided, then the task will attempt to discover an existing Container App Environment in the resource + * group. If no existing Container App Environment is found, then a default name will be generated in the form + * '-env'. If the Container App Environment does not exist, it will be created. + * @param containerAppName - The name of the Container App to use for the task. + * @param resourceGroup - The name of the resource group to use for the task. + * @param location - The location to deploy resources to. + * @returns The name of the Container App Environment to use for the task. + */ + azurecontainerapps.getOrCreateContainerAppEnvironment = function (containerAppName, resourceGroup, location) { + return __awaiter(this, void 0, void 0, function () { + var containerAppEnvironment, existingContainerAppEnvironment, containerAppEnvironmentExists; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + containerAppEnvironment = core.getInput('containerAppEnvironment', { required: false }); + if (!util.isNullOrEmpty(containerAppEnvironment)) return [3 /*break*/, 2]; + return [4 /*yield*/, this.appHelper.getExistingContainerAppEnvironment(resourceGroup)]; + case 1: + existingContainerAppEnvironment = _a.sent(); + if (!util.isNullOrEmpty(existingContainerAppEnvironment)) { + core.info("Existing Container App environment found in resource group: " + existingContainerAppEnvironment); + return [2 /*return*/, existingContainerAppEnvironment]; + } + _a.label = 2; + case 2: + // Generate the Container App environment name if it was not provided + if (util.isNullOrEmpty(containerAppEnvironment)) { + containerAppEnvironment = containerAppName + "-env"; + core.info("Default Container App environment name: " + containerAppEnvironment); + } + return [4 /*yield*/, this.appHelper.doesContainerAppEnvironmentExist(containerAppEnvironment, resourceGroup)]; + case 3: + containerAppEnvironmentExists = _a.sent(); + if (!!containerAppEnvironmentExists) return [3 /*break*/, 5]; + return [4 /*yield*/, this.appHelper.createContainerAppEnvironment(containerAppEnvironment, resourceGroup, location)]; + case 4: + _a.sent(); + _a.label = 5; + case 5: return [2 /*return*/, containerAppEnvironment]; + } + }); + }); + }; + /** + * Authenticates calls to the provided Azure Container Registry. + */ + azurecontainerapps.authenticateAzureContainerRegistryAsync = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this.acrUsername = core.getInput('acrUsername', { required: false }); + this.acrPassword = core.getInput('acrPassword', { required: false }); + if (!(!util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword))) return [3 /*break*/, 2]; + core.info("Logging in to ACR instance \"" + this.acrName + "\" with username and password credentials"); + return [4 /*yield*/, this.registryHelper.loginAcrWithUsernamePassword(this.acrName, this.acrUsername, this.acrPassword)]; + case 1: + _a.sent(); + return [3 /*break*/, 4]; + case 2: + core.info("No ACR credentials provided; attempting to log in to ACR instance \"" + this.acrName + "\" with access token"); + return [4 /*yield*/, this.registryHelper.loginAcrWithAccessTokenAsync(this.acrName)]; + case 3: + _a.sent(); + _a.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Sets up the scenario where an existing image is used for the Container App. + */ + azurecontainerapps.setupExistingImageScenario = function () { + // If telemetry is enabled, log that the previously built image scenario was targeted for this task + this.telemetryHelper.setImageScenario(); + }; + /** + * Builds a runnable application image using a Dockerfile or the builder and pushes it to ACR. + */ + azurecontainerapps.buildAndPushImageAsync = function () { + return __awaiter(this, void 0, void 0, function () { + var dockerfilePath, rootDockerfilePath; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + // Get the name of the image to build if it was provided, or generate it from build variables + this.imageToBuild = core.getInput('imageToBuild', { required: false }); + if (util.isNullOrEmpty(this.imageToBuild)) { + this.imageToBuild = this.acrName + ".azurecr.io/ado-task/container-app:" + this.buildId + "." + this.buildNumber; + core.info("Default image to build: " + this.imageToBuild); + } + // Get the name of the image to deploy if it was provided, or set it to the value of 'imageToBuild' + if (util.isNullOrEmpty(this.imageToDeploy)) { + this.imageToDeploy = this.imageToBuild; + core.info("Default image to deploy: " + this.imageToDeploy); + } + dockerfilePath = core.getInput('dockerfilePath', { required: false }); + if (!util.isNullOrEmpty(dockerfilePath)) return [3 /*break*/, 4]; + core.info("No Dockerfile path provided; checking for Dockerfile at root of application source."); + rootDockerfilePath = path.join(this.appSourcePath, 'Dockerfile'); + if (!fs.existsSync(rootDockerfilePath)) return [3 /*break*/, 1]; + core.info("Dockerfile found at root of application source."); + dockerfilePath = rootDockerfilePath; + return [3 /*break*/, 3]; + case 1: + // No Dockerfile found or provided, build the image using the builder + return [4 /*yield*/, this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild)]; + case 2: + // No Dockerfile found or provided, build the image using the builder + _a.sent(); + _a.label = 3; + case 3: return [3 /*break*/, 5]; + case 4: + dockerfilePath = path.join(this.appSourcePath, dockerfilePath); + _a.label = 5; + case 5: + if (!!util.isNullOrEmpty(dockerfilePath)) return [3 /*break*/, 7]; + // Build the image from the provided/discovered Dockerfile + return [4 /*yield*/, this.builderImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild)]; + case 6: + // Build the image from the provided/discovered Dockerfile + _a.sent(); + _a.label = 7; + case 7: + // Push the image to ACR + return [4 /*yield*/, this.registryHelper.pushImageToAcr(this.imageToBuild)]; + case 8: + // Push the image to ACR + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Builds a runnable application image using the builder. + * @param appSourcePath - The path to the application source code. + * @param imageToBuild - The name of the image to build. + */ + azurecontainerapps.buildImageFromBuilderAsync = function (appSourcePath, imageToBuild) { + return __awaiter(this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + // Install the pack CLI + return [4 /*yield*/, this.appHelper.installPackCliAsync()]; + case 1: + // Install the pack CLI + _b.sent(); + core.info("Successfully installed the pack CLI."); + // Get the runtime stack if provided, or determine it using Oryx + this.runtimeStack = core.getInput('runtimeStack', { required: false }); + if (!util.isNullOrEmpty(this.runtimeStack)) return [3 /*break*/, 3]; + _a = this; + return [4 /*yield*/, this.appHelper.determineRuntimeStackAsync(appSourcePath)]; + case 2: + _a.runtimeStack = _b.sent(); + core.info("Runtime stack determined to be: " + this.runtimeStack); + _b.label = 3; + case 3: + core.info("Building image \"" + imageToBuild + "\" using the Oryx++ Builder"); + // Set the Oryx++ Builder as the default builder locally + return [4 /*yield*/, this.appHelper.setDefaultBuilder()]; + case 4: + // Set the Oryx++ Builder as the default builder locally + _b.sent(); + // Create a runnable application image + return [4 /*yield*/, this.appHelper.createRunnableAppImage(imageToBuild, appSourcePath, this.runtimeStack)]; + case 5: + // Create a runnable application image + _b.sent(); + // If telemetry is enabled, log that the builder scenario was targeted for this task + this.telemetryHelper.setBuilderScenario(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Builds a runnable application image using a provided or discovered Dockerfile. + * @param appSourcePath - The path to the application source code. + * @param dockerfilePath - The path to the Dockerfile to build. + * @param imageToBuild - The name of the image to build. + */ + azurecontainerapps.builderImageFromDockerfile = function (appSourcePath, dockerfilePath, imageToBuild) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.info("Building image \"" + imageToBuild + "\" using the provided Dockerfile"); + return [4 /*yield*/, this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath)]; + case 1: + _a.sent(); + // If telemetry is enabled, log that the Dockerfile scenario was targeted for this task + this.telemetryHelper.setDockerfileScenario(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Sets up the Container App properties that will be passed through to the Azure CLI when a YAML configuration + * file is not provided. + */ + azurecontainerapps.setupContainerAppProperties = function () { + this.commandLineArgs = []; + // Get the ingress inputs + this.ingress = core.getInput('ingress', { required: false }); + this.targetPort = core.getInput('targetPort', { required: false }); + // If both ingress and target port were not provided for an existing Container App, or if ingress is to be disabled, + // use the 'update' command, otherwise we should use the 'up' command that performs a PATCH operation on the ingress properties. + this.shouldUseUpdateCommand = this.containerAppExists && + util.isNullOrEmpty(this.targetPort) && + (util.isNullOrEmpty(this.ingress) || this.ingress == 'disabled'); + // Pass the ACR credentials when creating a Container App or updating a Container App via the 'up' command + if (!util.isNullOrEmpty(this.acrName) && !util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword) && + (!this.containerAppExists || (this.containerAppExists && !this.shouldUseUpdateCommand))) { + this.commandLineArgs.push("--registry-server " + this.acrName + ".azurecr.io", "--registry-username " + this.acrUsername, "--registry-password " + this.acrPassword); + } + // Determine default values only for the 'create' scenario to avoid overriding existing values for the 'update' scenario + if (!this.containerAppExists) { + this.ingressEnabled = true; + // Set the ingress value to 'external' if it was not provided + if (util.isNullOrEmpty(this.ingress)) { + this.ingress = 'external'; + core.info("Default ingress value: " + this.ingress); + } + // Set the value of ingressEnabled to 'false' if ingress was provided as 'disabled' + if (this.ingress == 'disabled') { + this.ingressEnabled = false; + core.info("Ingress is disabled for this Container App."); + } + // Handle setup for ingress values when enabled + if (this.ingressEnabled) { + // Get the target port if provided, or determine it based on the application type + this.targetPort = core.getInput('targetPort', { required: false }); + if (util.isNullOrEmpty(this.targetPort)) { + if (!util.isNullOrEmpty(this.runtimeStack) && this.runtimeStack.startsWith('python:')) { + this.targetPort = '80'; + } + else { + this.targetPort = '8080'; + } + core.info("Default target port: " + this.targetPort); + } + // Set the target port to 80 if it was not provided or determined + if (util.isNullOrEmpty(this.targetPort)) { + this.targetPort = '80'; + core.info("Default target port: " + this.targetPort); + } + // Add the ingress value and target port to the optional arguments array + // Note: this step should be skipped if we're updating an existing Container App (ingress is enabled via a separate command) + this.commandLineArgs.push("--ingress " + this.ingress); + this.commandLineArgs.push("--target-port " + this.targetPort); + } + } + var environmentVariables = core.getInput('environmentVariables', { required: false }); + // Add user-specified environment variables + if (!util.isNullOrEmpty(environmentVariables)) { + // The --replace-env-vars flag is only used for the 'update' command, + // otherwise --env-vars is used for 'create' and 'up' + if (this.shouldUseUpdateCommand) { + this.commandLineArgs.push("--replace-env-vars " + environmentVariables); + } + else { + this.commandLineArgs.push("--env-vars " + environmentVariables); + } + } + }; + /** + * Creates or updates the Container App. + */ + azurecontainerapps.createOrUpdateContainerApp = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!!this.containerAppExists) return [3 /*break*/, 5]; + if (!!util.isNullOrEmpty(this.yamlConfigPath)) return [3 /*break*/, 2]; + // Create the Container App from the YAML configuration file + return [4 /*yield*/, this.appHelper.createContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath)]; + case 1: + // Create the Container App from the YAML configuration file + _a.sent(); + return [3 /*break*/, 4]; + case 2: + // Create the Container App from command line arguments + return [4 /*yield*/, this.appHelper.createContainerApp(this.containerAppName, this.resourceGroup, this.containerAppEnvironment, this.imageToDeploy, this.commandLineArgs)]; + case 3: + // Create the Container App from command line arguments + _a.sent(); + _a.label = 4; + case 4: return [2 /*return*/]; + case 5: + if (!!util.isNullOrEmpty(this.yamlConfigPath)) return [3 /*break*/, 7]; + // Update the Container App from the YAML configuration file + return [4 /*yield*/, this.appHelper.updateContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath)]; + case 6: + // Update the Container App from the YAML configuration file + _a.sent(); + return [2 /*return*/]; + case 7: + if (!this.shouldUseUpdateCommand) return [3 /*break*/, 11]; + if (!(!util.isNullOrEmpty(this.acrName) && !util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword))) return [3 /*break*/, 9]; + return [4 /*yield*/, this.appHelper.updateContainerAppRegistryDetails(this.containerAppName, this.resourceGroup, this.acrName, this.acrUsername, this.acrPassword)]; + case 8: + _a.sent(); + _a.label = 9; + case 9: + // Update the Container App using the 'update' command + return [4 /*yield*/, this.appHelper.updateContainerApp(this.containerAppName, this.resourceGroup, this.imageToDeploy, this.commandLineArgs)]; + case 10: + // Update the Container App using the 'update' command + _a.sent(); + return [3 /*break*/, 13]; + case 11: + // Update the Container App using the 'up' command + return [4 /*yield*/, this.appHelper.updateContainerAppWithUp(this.containerAppName, this.resourceGroup, this.imageToDeploy, this.commandLineArgs, this.ingress, this.targetPort)]; + case 12: + // Update the Container App using the 'up' command + _a.sent(); + _a.label = 13; + case 13: + if (!(this.ingress == 'disabled')) return [3 /*break*/, 15]; + return [4 /*yield*/, this.appHelper.disableContainerAppIngress(this.containerAppName, this.resourceGroup)]; + case 14: + _a.sent(); + _a.label = 15; + case 15: return [2 /*return*/]; + } + }); + }); + }; + // Azure DevOps build properties + azurecontainerapps.buildId = process.env.GITHUB_RUN_ID; + azurecontainerapps.buildNumber = process.env.GITHUB_RUN_NUMBER; + return azurecontainerapps; +}()); +exports.azurecontainerapps = azurecontainerapps; +azurecontainerapps.runMain(); + + +/***/ }), + +/***/ 5688: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.issue = exports.issueCommand = void 0; +const os = __importStar(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(869); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +function escapeData(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map + +/***/ }), + +/***/ 3195: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; +const command_1 = __nccwpck_require__(5688); +const file_command_1 = __nccwpck_require__(3930); +const utils_1 = __nccwpck_require__(869); +const os = __importStar(__nccwpck_require__(2037)); +const path = __importStar(__nccwpck_require__(1017)); +const oidc_utils_1 = __nccwpck_require__(1755); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = utils_1.toCommandValue(val); + process.env[name] = convertedVal; + const filePath = process.env['GITHUB_ENV'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); + } + command_1.issueCommand('set-env', { name }, convertedVal); +} +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); +} +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + const filePath = process.env['GITHUB_PATH'] || ''; + if (filePath) { + file_command_1.issueFileCommand('PATH', inputPath); + } + else { + command_1.issueCommand('add-path', {}, inputPath); + } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +exports.addPath = addPath; +/** + * Gets the value of an input. + * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. + * Returns an empty string if the value is not defined. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + if (options && options.trimWhitespace === false) { + return val; + } + return val.trim(); +} +exports.getInput = getInput; +/** + * Gets the values of an multiline input. Each value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string[] + * + */ +function getMultilineInput(name, options) { + const inputs = getInput(name, options) + .split('\n') + .filter(x => x !== ''); + if (options && options.trimWhitespace === false) { + return inputs; + } + return inputs.map(input => input.trim()); +} +exports.getMultilineInput = getMultilineInput; +/** + * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. + * Support boolean input list: `true | True | TRUE | false | False | FALSE` . + * The return value is also in boolean type. + * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns boolean + */ +function getBooleanInput(name, options) { + const trueValue = ['true', 'True', 'TRUE']; + const falseValue = ['false', 'False', 'FALSE']; + const val = getInput(name, options); + if (trueValue.includes(val)) + return true; + if (falseValue.includes(val)) + return false; + throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); +} +exports.getBooleanInput = getBooleanInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + const filePath = process.env['GITHUB_OUTPUT'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); + } + process.stdout.write(os.EOL); + command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); +} +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); +} +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); +} +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function error(message, properties = {}) { + command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.error = error; +/** + * Adds a warning issue + * @param message warning issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function warning(message, properties = {}) { + command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.warning = warning; +/** + * Adds a notice issue + * @param message notice issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function notice(message, properties = {}) { + command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.notice = notice; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + const filePath = process.env['GITHUB_STATE'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); + } + command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); +} +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +exports.getState = getState; +function getIDToken(aud) { + return __awaiter(this, void 0, void 0, function* () { + return yield oidc_utils_1.OidcClient.getIDToken(aud); + }); +} +exports.getIDToken = getIDToken; +/** + * Summary exports + */ +var summary_1 = __nccwpck_require__(8606); +Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); +/** + * @deprecated use core.summary + */ +var summary_2 = __nccwpck_require__(8606); +Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); +/** + * Path exports + */ +var path_utils_1 = __nccwpck_require__(397); +Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); +Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); +Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 3930: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +// For internal use, subject to change. +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +const fs = __importStar(__nccwpck_require__(7147)); +const os = __importStar(__nccwpck_require__(2037)); +const uuid_1 = __nccwpck_require__(5814); +const utils_1 = __nccwpck_require__(869); +function issueFileCommand(command, message) { + const filePath = process.env[`GITHUB_${command}`]; + if (!filePath) { + throw new Error(`Unable to find environment variable for file command ${command}`); + } + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`); + } + fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }); +} +exports.issueFileCommand = issueFileCommand; +function prepareKeyValueMessage(key, value) { + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + const convertedValue = utils_1.toCommandValue(value); + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedValue.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; +} +exports.prepareKeyValueMessage = prepareKeyValueMessage; +//# sourceMappingURL=file-command.js.map + +/***/ }), + +/***/ 1755: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.OidcClient = void 0; +const http_client_1 = __nccwpck_require__(9780); +const auth_1 = __nccwpck_require__(8833); +const core_1 = __nccwpck_require__(3195); +class OidcClient { + static createHttpClient(allowRetry = true, maxRetry = 10) { + const requestOptions = { + allowRetries: allowRetry, + maxRetries: maxRetry + }; + return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); + } + static getRequestToken() { + const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; + if (!token) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); + } + return token; + } + static getIDTokenUrl() { + const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); + } + return runtimeUrl; + } + static getCall(id_token_url) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const httpclient = OidcClient.createHttpClient(); + const res = yield httpclient + .getJson(id_token_url) + .catch(error => { + throw new Error(`Failed to get ID Token. \n + Error Code : ${error.statusCode}\n + Error Message: ${error.message}`); + }); + const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; + if (!id_token) { + throw new Error('Response json body do not have ID Token field'); + } + return id_token; + }); + } + static getIDToken(audience) { + return __awaiter(this, void 0, void 0, function* () { + try { + // New ID Token is requested from action service + let id_token_url = OidcClient.getIDTokenUrl(); + if (audience) { + const encodedAudience = encodeURIComponent(audience); + id_token_url = `${id_token_url}&audience=${encodedAudience}`; + } + core_1.debug(`ID token url is ${id_token_url}`); + const id_token = yield OidcClient.getCall(id_token_url); + core_1.setSecret(id_token); + return id_token; + } + catch (error) { + throw new Error(`Error message: ${error.message}`); + } + }); + } +} +exports.OidcClient = OidcClient; +//# sourceMappingURL=oidc-utils.js.map + +/***/ }), + +/***/ 397: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; +const path = __importStar(__nccwpck_require__(1017)); +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +function toPosixPath(pth) { + return pth.replace(/[\\]/g, '/'); +} +exports.toPosixPath = toPosixPath; +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +function toWin32Path(pth) { + return pth.replace(/[/]/g, '\\'); +} +exports.toWin32Path = toWin32Path; +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +function toPlatformPath(pth) { + return pth.replace(/[/\\]/g, path.sep); +} +exports.toPlatformPath = toPlatformPath; +//# sourceMappingURL=path-utils.js.map + +/***/ }), + +/***/ 8606: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; +const os_1 = __nccwpck_require__(2037); +const fs_1 = __nccwpck_require__(7147); +const { access, appendFile, writeFile } = fs_1.promises; +exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; +exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; +class Summary { + constructor() { + this._buffer = ''; + } + /** + * Finds the summary file path from the environment, rejects if env var is not found or file does not exist + * Also checks r/w permissions. + * + * @returns step summary file path + */ + filePath() { + return __awaiter(this, void 0, void 0, function* () { + if (this._filePath) { + return this._filePath; + } + const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; + if (!pathFromEnv) { + throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); + } + try { + yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); + } + catch (_a) { + throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); + } + this._filePath = pathFromEnv; + return this._filePath; + }); + } + /** + * Wraps content in an HTML tag, adding any HTML attributes + * + * @param {string} tag HTML tag to wrap + * @param {string | null} content content within the tag + * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add + * + * @returns {string} content wrapped in HTML element + */ + wrap(tag, content, attrs = {}) { + const htmlAttrs = Object.entries(attrs) + .map(([key, value]) => ` ${key}="${value}"`) + .join(''); + if (!content) { + return `<${tag}${htmlAttrs}>`; + } + return `<${tag}${htmlAttrs}>${content}`; + } + /** + * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. + * + * @param {SummaryWriteOptions} [options] (optional) options for write operation + * + * @returns {Promise} summary instance + */ + write(options) { + return __awaiter(this, void 0, void 0, function* () { + const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); + const filePath = yield this.filePath(); + const writeFunc = overwrite ? writeFile : appendFile; + yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); + return this.emptyBuffer(); + }); + } + /** + * Clears the summary buffer and wipes the summary file + * + * @returns {Summary} summary instance + */ + clear() { + return __awaiter(this, void 0, void 0, function* () { + return this.emptyBuffer().write({ overwrite: true }); + }); + } + /** + * Returns the current summary buffer as a string + * + * @returns {string} string of summary buffer + */ + stringify() { + return this._buffer; + } + /** + * If the summary buffer is empty + * + * @returns {boolen} true if the buffer is empty + */ + isEmptyBuffer() { + return this._buffer.length === 0; + } + /** + * Resets the summary buffer without writing to summary file + * + * @returns {Summary} summary instance + */ + emptyBuffer() { + this._buffer = ''; + return this; + } + /** + * Adds raw text to the summary buffer + * + * @param {string} text content to add + * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) + * + * @returns {Summary} summary instance + */ + addRaw(text, addEOL = false) { + this._buffer += text; + return addEOL ? this.addEOL() : this; + } + /** + * Adds the operating system-specific end-of-line marker to the buffer + * + * @returns {Summary} summary instance + */ + addEOL() { + return this.addRaw(os_1.EOL); + } + /** + * Adds an HTML codeblock to the summary buffer + * + * @param {string} code content to render within fenced code block + * @param {string} lang (optional) language to syntax highlight code + * + * @returns {Summary} summary instance + */ + addCodeBlock(code, lang) { + const attrs = Object.assign({}, (lang && { lang })); + const element = this.wrap('pre', this.wrap('code', code), attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML list to the summary buffer + * + * @param {string[]} items list of items to render + * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) + * + * @returns {Summary} summary instance + */ + addList(items, ordered = false) { + const tag = ordered ? 'ol' : 'ul'; + const listItems = items.map(item => this.wrap('li', item)).join(''); + const element = this.wrap(tag, listItems); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML table to the summary buffer + * + * @param {SummaryTableCell[]} rows table rows + * + * @returns {Summary} summary instance + */ + addTable(rows) { + const tableBody = rows + .map(row => { + const cells = row + .map(cell => { + if (typeof cell === 'string') { + return this.wrap('td', cell); + } + const { header, data, colspan, rowspan } = cell; + const tag = header ? 'th' : 'td'; + const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); + return this.wrap(tag, data, attrs); + }) + .join(''); + return this.wrap('tr', cells); + }) + .join(''); + const element = this.wrap('table', tableBody); + return this.addRaw(element).addEOL(); + } + /** + * Adds a collapsable HTML details element to the summary buffer + * + * @param {string} label text for the closed state + * @param {string} content collapsable content + * + * @returns {Summary} summary instance + */ + addDetails(label, content) { + const element = this.wrap('details', this.wrap('summary', label) + content); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML image tag to the summary buffer + * + * @param {string} src path to the image you to embed + * @param {string} alt text description of the image + * @param {SummaryImageOptions} options (optional) addition image attributes + * + * @returns {Summary} summary instance + */ + addImage(src, alt, options) { + const { width, height } = options || {}; + const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); + const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML section heading element + * + * @param {string} text heading text + * @param {number | string} [level=1] (optional) the heading level, default: 1 + * + * @returns {Summary} summary instance + */ + addHeading(text, level) { + const tag = `h${level}`; + const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) + ? tag + : 'h1'; + const element = this.wrap(allowedTag, text); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML thematic break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addSeparator() { + const element = this.wrap('hr', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML line break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addBreak() { + const element = this.wrap('br', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML blockquote to the summary buffer + * + * @param {string} text quote text + * @param {string} cite (optional) citation url + * + * @returns {Summary} summary instance + */ + addQuote(text, cite) { + const attrs = Object.assign({}, (cite && { cite })); + const element = this.wrap('blockquote', text, attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML anchor tag to the summary buffer + * + * @param {string} text link text/content + * @param {string} href hyperlink + * + * @returns {Summary} summary instance + */ + addLink(text, href) { + const element = this.wrap('a', text, { href }); + return this.addRaw(element).addEOL(); + } +} +const _summary = new Summary(); +/** + * @deprecated use `core.summary` + */ +exports.markdownSummary = _summary; +exports.summary = _summary; +//# sourceMappingURL=summary.js.map + +/***/ }), + +/***/ 869: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toCommandProperties = exports.toCommandValue = void 0; +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +exports.toCommandValue = toCommandValue; +/** + * + * @param annotationProperties + * @returns The command properties to send with the actual annotation command + * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 + */ +function toCommandProperties(annotationProperties) { + if (!Object.keys(annotationProperties).length) { + return {}; + } + return { + title: annotationProperties.title, + file: annotationProperties.file, + line: annotationProperties.startLine, + endLine: annotationProperties.endLine, + col: annotationProperties.startColumn, + endColumn: annotationProperties.endColumn + }; +} +exports.toCommandProperties = toCommandProperties; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 9714: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getExecOutput = exports.exec = void 0; +const string_decoder_1 = __nccwpck_require__(1576); +const tr = __importStar(__nccwpck_require__(5315)); +/** + * Exec a command. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @param commandLine command to execute (can include additional args). Must be correctly escaped. + * @param args optional arguments for tool. Escaping is handled by the lib. + * @param options optional exec options. See ExecOptions + * @returns Promise exit code + */ +function exec(commandLine, args, options) { + return __awaiter(this, void 0, void 0, function* () { + const commandArgs = tr.argStringToArray(commandLine); + if (commandArgs.length === 0) { + throw new Error(`Parameter 'commandLine' cannot be null or empty.`); + } + // Path to tool to execute should be first arg + const toolPath = commandArgs[0]; + args = commandArgs.slice(1).concat(args || []); + const runner = new tr.ToolRunner(toolPath, args, options); + return runner.exec(); + }); +} +exports.exec = exec; +/** + * Exec a command and get the output. + * Output will be streamed to the live console. + * Returns promise with the exit code and collected stdout and stderr + * + * @param commandLine command to execute (can include additional args). Must be correctly escaped. + * @param args optional arguments for tool. Escaping is handled by the lib. + * @param options optional exec options. See ExecOptions + * @returns Promise exit code, stdout, and stderr + */ +function getExecOutput(commandLine, args, options) { + var _a, _b; + return __awaiter(this, void 0, void 0, function* () { + let stdout = ''; + let stderr = ''; + //Using string decoder covers the case where a mult-byte character is split + const stdoutDecoder = new string_decoder_1.StringDecoder('utf8'); + const stderrDecoder = new string_decoder_1.StringDecoder('utf8'); + const originalStdoutListener = (_a = options === null || options === void 0 ? void 0 : options.listeners) === null || _a === void 0 ? void 0 : _a.stdout; + const originalStdErrListener = (_b = options === null || options === void 0 ? void 0 : options.listeners) === null || _b === void 0 ? void 0 : _b.stderr; + const stdErrListener = (data) => { + stderr += stderrDecoder.write(data); + if (originalStdErrListener) { + originalStdErrListener(data); + } + }; + const stdOutListener = (data) => { + stdout += stdoutDecoder.write(data); + if (originalStdoutListener) { + originalStdoutListener(data); + } + }; + const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); + const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); + //flush any remaining characters + stdout += stdoutDecoder.end(); + stderr += stderrDecoder.end(); + return { + exitCode, + stdout, + stderr + }; + }); +} +exports.getExecOutput = getExecOutput; +//# sourceMappingURL=exec.js.map + +/***/ }), + +/***/ 5315: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.argStringToArray = exports.ToolRunner = void 0; +const os = __importStar(__nccwpck_require__(2037)); +const events = __importStar(__nccwpck_require__(2361)); +const child = __importStar(__nccwpck_require__(2081)); +const path = __importStar(__nccwpck_require__(1017)); +const io = __importStar(__nccwpck_require__(9529)); +const ioUtil = __importStar(__nccwpck_require__(7821)); +const timers_1 = __nccwpck_require__(9512); +/* eslint-disable @typescript-eslint/unbound-method */ +const IS_WINDOWS = process.platform === 'win32'; +/* + * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. + */ +class ToolRunner extends events.EventEmitter { + constructor(toolPath, args, options) { + super(); + if (!toolPath) { + throw new Error("Parameter 'toolPath' cannot be null or empty."); + } + this.toolPath = toolPath; + this.args = args || []; + this.options = options || {}; + } + _debug(message) { + if (this.options.listeners && this.options.listeners.debug) { + this.options.listeners.debug(message); + } + } + _getCommandString(options, noPrefix) { + const toolPath = this._getSpawnFileName(); + const args = this._getSpawnArgs(options); + let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool + if (IS_WINDOWS) { + // Windows + cmd file + if (this._isCmdFile()) { + cmd += toolPath; + for (const a of args) { + cmd += ` ${a}`; + } + } + // Windows + verbatim + else if (options.windowsVerbatimArguments) { + cmd += `"${toolPath}"`; + for (const a of args) { + cmd += ` ${a}`; + } + } + // Windows (regular) + else { + cmd += this._windowsQuoteCmdArg(toolPath); + for (const a of args) { + cmd += ` ${this._windowsQuoteCmdArg(a)}`; + } + } + } + else { + // OSX/Linux - this can likely be improved with some form of quoting. + // creating processes on Unix is fundamentally different than Windows. + // on Unix, execvp() takes an arg array. + cmd += toolPath; + for (const a of args) { + cmd += ` ${a}`; + } + } + return cmd; + } + _processLineBuffer(data, strBuffer, onLine) { + try { + let s = strBuffer + data.toString(); + let n = s.indexOf(os.EOL); + while (n > -1) { + const line = s.substring(0, n); + onLine(line); + // the rest of the string ... + s = s.substring(n + os.EOL.length); + n = s.indexOf(os.EOL); + } + return s; + } + catch (err) { + // streaming lines to console is best effort. Don't fail a build. + this._debug(`error processing line. Failed with error ${err}`); + return ''; + } + } + _getSpawnFileName() { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + return process.env['COMSPEC'] || 'cmd.exe'; + } + } + return this.toolPath; + } + _getSpawnArgs(options) { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; + for (const a of this.args) { + argline += ' '; + argline += options.windowsVerbatimArguments + ? a + : this._windowsQuoteCmdArg(a); + } + argline += '"'; + return [argline]; + } + } + return this.args; + } + _endsWith(str, end) { + return str.endsWith(end); + } + _isCmdFile() { + const upperToolPath = this.toolPath.toUpperCase(); + return (this._endsWith(upperToolPath, '.CMD') || + this._endsWith(upperToolPath, '.BAT')); + } + _windowsQuoteCmdArg(arg) { + // for .exe, apply the normal quoting rules that libuv applies + if (!this._isCmdFile()) { + return this._uvQuoteCmdArg(arg); + } + // otherwise apply quoting rules specific to the cmd.exe command line parser. + // the libuv rules are generic and are not designed specifically for cmd.exe + // command line parser. + // + // for a detailed description of the cmd.exe command line parser, refer to + // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 + // need quotes for empty arg + if (!arg) { + return '""'; + } + // determine whether the arg needs to be quoted + const cmdSpecialChars = [ + ' ', + '\t', + '&', + '(', + ')', + '[', + ']', + '{', + '}', + '^', + '=', + ';', + '!', + "'", + '+', + ',', + '`', + '~', + '|', + '<', + '>', + '"' + ]; + let needsQuotes = false; + for (const char of arg) { + if (cmdSpecialChars.some(x => x === char)) { + needsQuotes = true; + break; + } + } + // short-circuit if quotes not needed + if (!needsQuotes) { + return arg; + } + // the following quoting rules are very similar to the rules that by libuv applies. + // + // 1) wrap the string in quotes + // + // 2) double-up quotes - i.e. " => "" + // + // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately + // doesn't work well with a cmd.exe command line. + // + // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. + // for example, the command line: + // foo.exe "myarg:""my val""" + // is parsed by a .NET console app into an arg array: + // [ "myarg:\"my val\"" ] + // which is the same end result when applying libuv quoting rules. although the actual + // command line from libuv quoting rules would look like: + // foo.exe "myarg:\"my val\"" + // + // 3) double-up slashes that precede a quote, + // e.g. hello \world => "hello \world" + // hello\"world => "hello\\""world" + // hello\\"world => "hello\\\\""world" + // hello world\ => "hello world\\" + // + // technically this is not required for a cmd.exe command line, or the batch argument parser. + // the reasons for including this as a .cmd quoting rule are: + // + // a) this is optimized for the scenario where the argument is passed from the .cmd file to an + // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. + // + // b) it's what we've been doing previously (by deferring to node default behavior) and we + // haven't heard any complaints about that aspect. + // + // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be + // escaped when used on the command line directly - even though within a .cmd file % can be escaped + // by using %%. + // + // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts + // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. + // + // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would + // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the + // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args + // to an external program. + // + // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. + // % can be escaped within a .cmd file. + let reverse = '"'; + let quoteHit = true; + for (let i = arg.length; i > 0; i--) { + // walk the string in reverse + reverse += arg[i - 1]; + if (quoteHit && arg[i - 1] === '\\') { + reverse += '\\'; // double the slash + } + else if (arg[i - 1] === '"') { + quoteHit = true; + reverse += '"'; // double the quote + } + else { + quoteHit = false; + } + } + reverse += '"'; + return reverse + .split('') + .reverse() + .join(''); + } + _uvQuoteCmdArg(arg) { + // Tool runner wraps child_process.spawn() and needs to apply the same quoting as + // Node in certain cases where the undocumented spawn option windowsVerbatimArguments + // is used. + // + // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV, + // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details), + // pasting copyright notice from Node within this function: + // + // Copyright Joyent, Inc. and other Node contributors. 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. + if (!arg) { + // Need double quotation for empty argument + return '""'; + } + if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) { + // No quotation needed + return arg; + } + if (!arg.includes('"') && !arg.includes('\\')) { + // No embedded double quotes or backslashes, so I can just wrap + // quote marks around the whole thing. + return `"${arg}"`; + } + // Expected input/output: + // input : hello"world + // output: "hello\"world" + // input : hello""world + // output: "hello\"\"world" + // input : hello\world + // output: hello\world + // input : hello\\world + // output: hello\\world + // input : hello\"world + // output: "hello\\\"world" + // input : hello\\"world + // output: "hello\\\\\"world" + // input : hello world\ + // output: "hello world\\" - note the comment in libuv actually reads "hello world\" + // but it appears the comment is wrong, it should be "hello world\\" + let reverse = '"'; + let quoteHit = true; + for (let i = arg.length; i > 0; i--) { + // walk the string in reverse + reverse += arg[i - 1]; + if (quoteHit && arg[i - 1] === '\\') { + reverse += '\\'; + } + else if (arg[i - 1] === '"') { + quoteHit = true; + reverse += '\\'; + } + else { + quoteHit = false; + } + } + reverse += '"'; + return reverse + .split('') + .reverse() + .join(''); + } + _cloneExecOptions(options) { + options = options || {}; + const result = { + cwd: options.cwd || process.cwd(), + env: options.env || process.env, + silent: options.silent || false, + windowsVerbatimArguments: options.windowsVerbatimArguments || false, + failOnStdErr: options.failOnStdErr || false, + ignoreReturnCode: options.ignoreReturnCode || false, + delay: options.delay || 10000 + }; + result.outStream = options.outStream || process.stdout; + result.errStream = options.errStream || process.stderr; + return result; + } + _getSpawnOptions(options, toolPath) { + options = options || {}; + const result = {}; + result.cwd = options.cwd; + result.env = options.env; + result['windowsVerbatimArguments'] = + options.windowsVerbatimArguments || this._isCmdFile(); + if (options.windowsVerbatimArguments) { + result.argv0 = `"${toolPath}"`; + } + return result; + } + /** + * Exec a tool. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @param tool path to tool to exec + * @param options optional exec options. See ExecOptions + * @returns number + */ + exec() { + return __awaiter(this, void 0, void 0, function* () { + // root the tool path if it is unrooted and contains relative pathing + if (!ioUtil.isRooted(this.toolPath) && + (this.toolPath.includes('/') || + (IS_WINDOWS && this.toolPath.includes('\\')))) { + // prefer options.cwd if it is specified, however options.cwd may also need to be rooted + this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath); + } + // if the tool is only a file name, then resolve it from the PATH + // otherwise verify it exists (add extension on Windows if necessary) + this.toolPath = yield io.which(this.toolPath, true); + return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { + this._debug(`exec tool: ${this.toolPath}`); + this._debug('arguments:'); + for (const arg of this.args) { + this._debug(` ${arg}`); + } + const optionsNonNull = this._cloneExecOptions(this.options); + if (!optionsNonNull.silent && optionsNonNull.outStream) { + optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); + } + const state = new ExecState(optionsNonNull, this.toolPath); + state.on('debug', (message) => { + this._debug(message); + }); + if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) { + return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`)); + } + const fileName = this._getSpawnFileName(); + const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName)); + let stdbuffer = ''; + if (cp.stdout) { + cp.stdout.on('data', (data) => { + if (this.options.listeners && this.options.listeners.stdout) { + this.options.listeners.stdout(data); + } + if (!optionsNonNull.silent && optionsNonNull.outStream) { + optionsNonNull.outStream.write(data); + } + stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => { + if (this.options.listeners && this.options.listeners.stdline) { + this.options.listeners.stdline(line); + } + }); + }); + } + let errbuffer = ''; + if (cp.stderr) { + cp.stderr.on('data', (data) => { + state.processStderr = true; + if (this.options.listeners && this.options.listeners.stderr) { + this.options.listeners.stderr(data); + } + if (!optionsNonNull.silent && + optionsNonNull.errStream && + optionsNonNull.outStream) { + const s = optionsNonNull.failOnStdErr + ? optionsNonNull.errStream + : optionsNonNull.outStream; + s.write(data); + } + errbuffer = this._processLineBuffer(data, errbuffer, (line) => { + if (this.options.listeners && this.options.listeners.errline) { + this.options.listeners.errline(line); + } + }); + }); + } + cp.on('error', (err) => { + state.processError = err.message; + state.processExited = true; + state.processClosed = true; + state.CheckComplete(); + }); + cp.on('exit', (code) => { + state.processExitCode = code; + state.processExited = true; + this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); + state.CheckComplete(); + }); + cp.on('close', (code) => { + state.processExitCode = code; + state.processExited = true; + state.processClosed = true; + this._debug(`STDIO streams have closed for tool '${this.toolPath}'`); + state.CheckComplete(); + }); + state.on('done', (error, exitCode) => { + if (stdbuffer.length > 0) { + this.emit('stdline', stdbuffer); + } + if (errbuffer.length > 0) { + this.emit('errline', errbuffer); + } + cp.removeAllListeners(); + if (error) { + reject(error); + } + else { + resolve(exitCode); + } + }); + if (this.options.input) { + if (!cp.stdin) { + throw new Error('child process missing stdin'); + } + cp.stdin.end(this.options.input); + } + })); + }); + } +} +exports.ToolRunner = ToolRunner; +/** + * Convert an arg string to an array of args. Handles escaping + * + * @param argString string of arguments + * @returns string[] array of arguments + */ +function argStringToArray(argString) { + const args = []; + let inQuotes = false; + let escaped = false; + let arg = ''; + function append(c) { + // we only escape double quotes. + if (escaped && c !== '"') { + arg += '\\'; + } + arg += c; + escaped = false; + } + for (let i = 0; i < argString.length; i++) { + const c = argString.charAt(i); + if (c === '"') { + if (!escaped) { + inQuotes = !inQuotes; + } + else { + append(c); + } + continue; + } + if (c === '\\' && escaped) { + append(c); + continue; + } + if (c === '\\' && inQuotes) { + escaped = true; + continue; + } + if (c === ' ' && !inQuotes) { + if (arg.length > 0) { + args.push(arg); + arg = ''; + } + continue; + } + append(c); + } + if (arg.length > 0) { + args.push(arg.trim()); + } + return args; +} +exports.argStringToArray = argStringToArray; +class ExecState extends events.EventEmitter { + constructor(options, toolPath) { + super(); + this.processClosed = false; // tracks whether the process has exited and stdio is closed + this.processError = ''; + this.processExitCode = 0; + this.processExited = false; // tracks whether the process has exited + this.processStderr = false; // tracks whether stderr was written to + this.delay = 10000; // 10 seconds + this.done = false; + this.timeout = null; + if (!toolPath) { + throw new Error('toolPath must not be empty'); + } + this.options = options; + this.toolPath = toolPath; + if (options.delay) { + this.delay = options.delay; + } + } + CheckComplete() { + if (this.done) { + return; + } + if (this.processClosed) { + this._setResult(); + } + else if (this.processExited) { + this.timeout = timers_1.setTimeout(ExecState.HandleTimeout, this.delay, this); + } + } + _debug(message) { + this.emit('debug', message); + } + _setResult() { + // determine whether there is an error + let error; + if (this.processExited) { + if (this.processError) { + error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`); + } + else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) { + error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`); + } + else if (this.processStderr && this.options.failOnStdErr) { + error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`); + } + } + // clear the timeout + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + this.done = true; + this.emit('done', error, this.processExitCode); + } + static HandleTimeout(state) { + if (state.done) { + return; + } + if (!state.processClosed && state.processExited) { + const message = `The STDIO streams did not close within ${state.delay / + 1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`; + state._debug(message); + } + state._setResult(); + } +} +//# sourceMappingURL=toolrunner.js.map + +/***/ }), + +/***/ 8833: +/***/ (function(__unused_webpack_module, exports) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.PersonalAccessTokenCredentialHandler = exports.BearerCredentialHandler = exports.BasicCredentialHandler = void 0; +class BasicCredentialHandler { + constructor(username, password) { + this.username = username; + this.password = password; + } + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.BasicCredentialHandler = BasicCredentialHandler; +class BearerCredentialHandler { + constructor(token) { + this.token = token; + } + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Bearer ${this.token}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.BearerCredentialHandler = BearerCredentialHandler; +class PersonalAccessTokenCredentialHandler { + constructor(token) { + this.token = token; + } + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; +//# sourceMappingURL=auth.js.map + +/***/ }), + +/***/ 9780: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; +const http = __importStar(__nccwpck_require__(3685)); +const https = __importStar(__nccwpck_require__(5687)); +const pm = __importStar(__nccwpck_require__(4492)); +const tunnel = __importStar(__nccwpck_require__(9041)); +var HttpCodes; +(function (HttpCodes) { + HttpCodes[HttpCodes["OK"] = 200] = "OK"; + HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; + HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; + HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; + HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; + HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; + HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; + HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; + HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; + HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; + HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; + HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; + HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; + HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; + HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; + HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; + HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; + HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; + HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; + HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; + HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; + HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; + HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; + HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; + HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; + HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; + HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; +})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); +var Headers; +(function (Headers) { + Headers["Accept"] = "accept"; + Headers["ContentType"] = "content-type"; +})(Headers = exports.Headers || (exports.Headers = {})); +var MediaTypes; +(function (MediaTypes) { + MediaTypes["ApplicationJson"] = "application/json"; +})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {})); +/** + * Returns the proxy URL, depending upon the supplied url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ +function getProxyUrl(serverUrl) { + const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); + return proxyUrl ? proxyUrl.href : ''; +} +exports.getProxyUrl = getProxyUrl; +const HttpRedirectCodes = [ + HttpCodes.MovedPermanently, + HttpCodes.ResourceMoved, + HttpCodes.SeeOther, + HttpCodes.TemporaryRedirect, + HttpCodes.PermanentRedirect +]; +const HttpResponseRetryCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout +]; +const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; +const ExponentialBackoffCeiling = 10; +const ExponentialBackoffTimeSlice = 5; +class HttpClientError extends Error { + constructor(message, statusCode) { + super(message); + this.name = 'HttpClientError'; + this.statusCode = statusCode; + Object.setPrototypeOf(this, HttpClientError.prototype); + } +} +exports.HttpClientError = HttpClientError; +class HttpClientResponse { + constructor(message) { + this.message = message; + } + readBody() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + let output = Buffer.alloc(0); + this.message.on('data', (chunk) => { + output = Buffer.concat([output, chunk]); + }); + this.message.on('end', () => { + resolve(output.toString()); + }); + })); + }); + } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } +} +exports.HttpClientResponse = HttpClientResponse; +function isHttps(requestUrl) { + const parsedUrl = new URL(requestUrl); + return parsedUrl.protocol === 'https:'; +} +exports.isHttps = isHttps; +class HttpClient { + constructor(userAgent, handlers, requestOptions) { + this._ignoreSslError = false; + this._allowRedirects = true; + this._allowRedirectDowngrade = false; + this._maxRedirects = 50; + this._allowRetries = false; + this._maxRetries = 1; + this._keepAlive = false; + this._disposed = false; + this.userAgent = userAgent; + this.handlers = handlers || []; + this.requestOptions = requestOptions; + if (requestOptions) { + if (requestOptions.ignoreSslError != null) { + this._ignoreSslError = requestOptions.ignoreSslError; + } + this._socketTimeout = requestOptions.socketTimeout; + if (requestOptions.allowRedirects != null) { + this._allowRedirects = requestOptions.allowRedirects; + } + if (requestOptions.allowRedirectDowngrade != null) { + this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; + } + if (requestOptions.maxRedirects != null) { + this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); + } + if (requestOptions.keepAlive != null) { + this._keepAlive = requestOptions.keepAlive; + } + if (requestOptions.allowRetries != null) { + this._allowRetries = requestOptions.allowRetries; + } + if (requestOptions.maxRetries != null) { + this._maxRetries = requestOptions.maxRetries; + } + } + } + options(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); + }); + } + get(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('GET', requestUrl, null, additionalHeaders || {}); + }); + } + del(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('DELETE', requestUrl, null, additionalHeaders || {}); + }); + } + post(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('POST', requestUrl, data, additionalHeaders || {}); + }); + } + patch(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('PATCH', requestUrl, data, additionalHeaders || {}); + }); + } + put(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('PUT', requestUrl, data, additionalHeaders || {}); + }); + } + head(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('HEAD', requestUrl, null, additionalHeaders || {}); + }); + } + sendStream(verb, requestUrl, stream, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request(verb, requestUrl, stream, additionalHeaders); + }); + } + /** + * Gets a typed object from an endpoint + * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise + */ + getJson(requestUrl, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + const res = yield this.get(requestUrl, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + postJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.post(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + putJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.put(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + patchJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.patch(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + /** + * Makes a raw http request. + * All other methods such as get, post, patch, and request ultimately call this. + * Prefer get, del, post and patch + */ + request(verb, requestUrl, data, headers) { + return __awaiter(this, void 0, void 0, function* () { + if (this._disposed) { + throw new Error('Client has already been disposed.'); + } + const parsedUrl = new URL(requestUrl); + let info = this._prepareRequest(verb, parsedUrl, headers); + // Only perform retries on reads since writes may not be idempotent. + const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) + ? this._maxRetries + 1 + : 1; + let numTries = 0; + let response; + do { + response = yield this.requestRaw(info, data); + // Check if it's an authentication challenge + if (response && + response.message && + response.message.statusCode === HttpCodes.Unauthorized) { + let authenticationHandler; + for (const handler of this.handlers) { + if (handler.canHandleAuthentication(response)) { + authenticationHandler = handler; + break; + } + } + if (authenticationHandler) { + return authenticationHandler.handleAuthentication(this, info, data); + } + else { + // We have received an unauthorized response but have no handlers to handle it. + // Let the response return to the caller. + return response; + } + } + let redirectsRemaining = this._maxRedirects; + while (response.message.statusCode && + HttpRedirectCodes.includes(response.message.statusCode) && + this._allowRedirects && + redirectsRemaining > 0) { + const redirectUrl = response.message.headers['location']; + if (!redirectUrl) { + // if there's no location to redirect to, we won't + break; + } + const parsedRedirectUrl = new URL(redirectUrl); + if (parsedUrl.protocol === 'https:' && + parsedUrl.protocol !== parsedRedirectUrl.protocol && + !this._allowRedirectDowngrade) { + throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); + } + // we need to finish reading the response before reassigning response + // which will leak the open socket. + yield response.readBody(); + // strip authorization header if redirected to a different hostname + if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { + for (const header in headers) { + // header names are case insensitive + if (header.toLowerCase() === 'authorization') { + delete headers[header]; + } + } + } + // let's make the request with the new redirectUrl + info = this._prepareRequest(verb, parsedRedirectUrl, headers); + response = yield this.requestRaw(info, data); + redirectsRemaining--; + } + if (!response.message.statusCode || + !HttpResponseRetryCodes.includes(response.message.statusCode)) { + // If not a retry code, return immediately instead of retrying + return response; + } + numTries += 1; + if (numTries < maxTries) { + yield response.readBody(); + yield this._performExponentialBackoff(numTries); + } + } while (numTries < maxTries); + return response; + }); + } + /** + * Needs to be called if keepAlive is set to true in request options. + */ + dispose() { + if (this._agent) { + this._agent.destroy(); + } + this._disposed = true; + } + /** + * Raw request. + * @param info + * @param data + */ + requestRaw(info, data) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + function callbackForResult(err, res) { + if (err) { + reject(err); + } + else if (!res) { + // If `err` is not passed, then `res` must be passed. + reject(new Error('Unknown error')); + } + else { + resolve(res); + } + } + this.requestRawWithCallback(info, data, callbackForResult); + }); + }); + } + /** + * Raw request with callback. + * @param info + * @param data + * @param onResult + */ + requestRawWithCallback(info, data, onResult) { + if (typeof data === 'string') { + if (!info.options.headers) { + info.options.headers = {}; + } + info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); + } + let callbackCalled = false; + function handleResult(err, res) { + if (!callbackCalled) { + callbackCalled = true; + onResult(err, res); + } + } + const req = info.httpModule.request(info.options, (msg) => { + const res = new HttpClientResponse(msg); + handleResult(undefined, res); + }); + let socket; + req.on('socket', sock => { + socket = sock; + }); + // If we ever get disconnected, we want the socket to timeout eventually + req.setTimeout(this._socketTimeout || 3 * 60000, () => { + if (socket) { + socket.end(); + } + handleResult(new Error(`Request timeout: ${info.options.path}`)); + }); + req.on('error', function (err) { + // err has statusCode property + // res should have headers + handleResult(err); + }); + if (data && typeof data === 'string') { + req.write(data, 'utf8'); + } + if (data && typeof data !== 'string') { + data.on('close', function () { + req.end(); + }); + data.pipe(req); + } + else { + req.end(); + } + } + /** + * Gets an http agent. This function is useful when you need an http agent that handles + * routing through a proxy server - depending upon the url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ + getAgent(serverUrl) { + const parsedUrl = new URL(serverUrl); + return this._getAgent(parsedUrl); + } + _prepareRequest(method, requestUrl, headers) { + const info = {}; + info.parsedUrl = requestUrl; + const usingSsl = info.parsedUrl.protocol === 'https:'; + info.httpModule = usingSsl ? https : http; + const defaultPort = usingSsl ? 443 : 80; + info.options = {}; + info.options.host = info.parsedUrl.hostname; + info.options.port = info.parsedUrl.port + ? parseInt(info.parsedUrl.port) + : defaultPort; + info.options.path = + (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); + info.options.method = method; + info.options.headers = this._mergeHeaders(headers); + if (this.userAgent != null) { + info.options.headers['user-agent'] = this.userAgent; + } + info.options.agent = this._getAgent(info.parsedUrl); + // gives handlers an opportunity to participate + if (this.handlers) { + for (const handler of this.handlers) { + handler.prepareRequest(info.options); + } + } + return info; + } + _mergeHeaders(headers) { + if (this.requestOptions && this.requestOptions.headers) { + return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers || {})); + } + return lowercaseKeys(headers || {}); + } + _getExistingOrDefaultHeader(additionalHeaders, header, _default) { + let clientHeader; + if (this.requestOptions && this.requestOptions.headers) { + clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; + } + return additionalHeaders[header] || clientHeader || _default; + } + _getAgent(parsedUrl) { + let agent; + const proxyUrl = pm.getProxyUrl(parsedUrl); + const useProxy = proxyUrl && proxyUrl.hostname; + if (this._keepAlive && useProxy) { + agent = this._proxyAgent; + } + if (this._keepAlive && !useProxy) { + agent = this._agent; + } + // if agent is already assigned use that agent. + if (agent) { + return agent; + } + const usingSsl = parsedUrl.protocol === 'https:'; + let maxSockets = 100; + if (this.requestOptions) { + maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; + } + // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. + if (proxyUrl && proxyUrl.hostname) { + const agentOptions = { + maxSockets, + keepAlive: this._keepAlive, + proxy: Object.assign(Object.assign({}, ((proxyUrl.username || proxyUrl.password) && { + proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` + })), { host: proxyUrl.hostname, port: proxyUrl.port }) + }; + let tunnelAgent; + const overHttps = proxyUrl.protocol === 'https:'; + if (usingSsl) { + tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; + } + else { + tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; + } + agent = tunnelAgent(agentOptions); + this._proxyAgent = agent; + } + // if reusing agent across request and tunneling agent isn't assigned create a new agent + if (this._keepAlive && !agent) { + const options = { keepAlive: this._keepAlive, maxSockets }; + agent = usingSsl ? new https.Agent(options) : new http.Agent(options); + this._agent = agent; + } + // if not using private agent and tunnel agent isn't setup then use global agent + if (!agent) { + agent = usingSsl ? https.globalAgent : http.globalAgent; + } + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + agent.options = Object.assign(agent.options || {}, { + rejectUnauthorized: false + }); + } + return agent; + } + _performExponentialBackoff(retryNumber) { + return __awaiter(this, void 0, void 0, function* () { + retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); + const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); + return new Promise(resolve => setTimeout(() => resolve(), ms)); + }); + } + _processResponse(res, options) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { + const statusCode = res.message.statusCode || 0; + const response = { + statusCode, + result: null, + headers: {} + }; + // not found leads to null obj returned + if (statusCode === HttpCodes.NotFound) { + resolve(response); + } + // get the result from the body + function dateTimeDeserializer(key, value) { + if (typeof value === 'string') { + const a = new Date(value); + if (!isNaN(a.valueOf())) { + return a; + } + } + return value; + } + let obj; + let contents; + try { + contents = yield res.readBody(); + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, dateTimeDeserializer); + } + else { + obj = JSON.parse(contents); + } + response.result = obj; + } + response.headers = res.message.headers; + } + catch (err) { + // Invalid resource (contents not json); leaving result obj null + } + // note that 3xx redirects are handled by the http layer. + if (statusCode > 299) { + let msg; + // if exception/error in body, attempt to get better error + if (obj && obj.message) { + msg = obj.message; + } + else if (contents && contents.length > 0) { + // it may be the case that the exception is in the body message as string + msg = contents; + } + else { + msg = `Failed request: (${statusCode})`; + } + const err = new HttpClientError(msg, statusCode); + err.result = response.result; + reject(err); + } + else { + resolve(response); + } + })); + }); + } +} +exports.HttpClient = HttpClient; +const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 4492: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.checkBypass = exports.getProxyUrl = void 0; +function getProxyUrl(reqUrl) { + const usingSsl = reqUrl.protocol === 'https:'; + if (checkBypass(reqUrl)) { + return undefined; + } + const proxyVar = (() => { + if (usingSsl) { + return process.env['https_proxy'] || process.env['HTTPS_PROXY']; + } + else { + return process.env['http_proxy'] || process.env['HTTP_PROXY']; + } + })(); + if (proxyVar) { + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } + } + else { + return undefined; + } +} +exports.getProxyUrl = getProxyUrl; +function checkBypass(reqUrl) { + if (!reqUrl.hostname) { + return false; + } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } + const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; + if (!noProxy) { + return false; + } + // Determine the request port + let reqPort; + if (reqUrl.port) { + reqPort = Number(reqUrl.port); + } + else if (reqUrl.protocol === 'http:') { + reqPort = 80; + } + else if (reqUrl.protocol === 'https:') { + reqPort = 443; + } + // Format the request hostname and hostname with port + const upperReqHosts = [reqUrl.hostname.toUpperCase()]; + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); + } + // Compare request host against noproxy + for (const upperNoProxyItem of noProxy + .split(',') + .map(x => x.trim().toUpperCase()) + .filter(x => x)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { + return true; + } + } + return false; +} +exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} +//# sourceMappingURL=proxy.js.map + +/***/ }), + +/***/ 7821: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var _a; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.READONLY = exports.UV_FS_O_EXLOCK = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rm = exports.rename = exports.readlink = exports.readdir = exports.open = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0; +const fs = __importStar(__nccwpck_require__(7147)); +const path = __importStar(__nccwpck_require__(1017)); +_a = fs.promises +// export const {open} = 'fs' +, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.open = _a.open, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rm = _a.rm, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink; +// export const {open} = 'fs' +exports.IS_WINDOWS = process.platform === 'win32'; +// See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691 +exports.UV_FS_O_EXLOCK = 0x10000000; +exports.READONLY = fs.constants.O_RDONLY; +function exists(fsPath) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield exports.stat(fsPath); + } + catch (err) { + if (err.code === 'ENOENT') { + return false; + } + throw err; + } + return true; + }); +} +exports.exists = exists; +function isDirectory(fsPath, useStat = false) { + return __awaiter(this, void 0, void 0, function* () { + const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath); + return stats.isDirectory(); + }); +} +exports.isDirectory = isDirectory; +/** + * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like: + * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases). + */ +function isRooted(p) { + p = normalizeSeparators(p); + if (!p) { + throw new Error('isRooted() parameter "p" cannot be empty'); + } + if (exports.IS_WINDOWS) { + return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello + ); // e.g. C: or C:\hello + } + return p.startsWith('/'); +} +exports.isRooted = isRooted; +/** + * Best effort attempt to determine whether a file exists and is executable. + * @param filePath file path to check + * @param extensions additional file extensions to try + * @return if file exists and is executable, returns the file path. otherwise empty string. + */ +function tryGetExecutablePath(filePath, extensions) { + return __awaiter(this, void 0, void 0, function* () { + let stats = undefined; + try { + // test file exists + stats = yield exports.stat(filePath); + } + catch (err) { + if (err.code !== 'ENOENT') { + // eslint-disable-next-line no-console + console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); + } + } + if (stats && stats.isFile()) { + if (exports.IS_WINDOWS) { + // on Windows, test for valid extension + const upperExt = path.extname(filePath).toUpperCase(); + if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) { + return filePath; + } + } + else { + if (isUnixExecutable(stats)) { + return filePath; + } + } + } + // try each extension + const originalFilePath = filePath; + for (const extension of extensions) { + filePath = originalFilePath + extension; + stats = undefined; + try { + stats = yield exports.stat(filePath); + } + catch (err) { + if (err.code !== 'ENOENT') { + // eslint-disable-next-line no-console + console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); + } + } + if (stats && stats.isFile()) { + if (exports.IS_WINDOWS) { + // preserve the case of the actual file (since an extension was appended) + try { + const directory = path.dirname(filePath); + const upperName = path.basename(filePath).toUpperCase(); + for (const actualName of yield exports.readdir(directory)) { + if (upperName === actualName.toUpperCase()) { + filePath = path.join(directory, actualName); + break; + } + } + } + catch (err) { + // eslint-disable-next-line no-console + console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`); + } + return filePath; + } + else { + if (isUnixExecutable(stats)) { + return filePath; + } + } + } + } + return ''; + }); +} +exports.tryGetExecutablePath = tryGetExecutablePath; +function normalizeSeparators(p) { + p = p || ''; + if (exports.IS_WINDOWS) { + // convert slashes on Windows + p = p.replace(/\//g, '\\'); + // remove redundant slashes + return p.replace(/\\\\+/g, '\\'); + } + // remove redundant slashes + return p.replace(/\/\/+/g, '/'); +} +// on Mac/Linux, test the execute bit +// R W X R W X R W X +// 256 128 64 32 16 8 4 2 1 +function isUnixExecutable(stats) { + return ((stats.mode & 1) > 0 || + ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || + ((stats.mode & 64) > 0 && stats.uid === process.getuid())); +} +// Get the path of cmd.exe in windows +function getCmdPath() { + var _a; + return (_a = process.env['COMSPEC']) !== null && _a !== void 0 ? _a : `cmd.exe`; +} +exports.getCmdPath = getCmdPath; +//# sourceMappingURL=io-util.js.map + +/***/ }), + +/***/ 9529: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0; +const assert_1 = __nccwpck_require__(9491); +const path = __importStar(__nccwpck_require__(1017)); +const ioUtil = __importStar(__nccwpck_require__(7821)); +/** + * Copies a file or folder. + * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js + * + * @param source source path + * @param dest destination path + * @param options optional. See CopyOptions. + */ +function cp(source, dest, options = {}) { + return __awaiter(this, void 0, void 0, function* () { + const { force, recursive, copySourceDirectory } = readCopyOptions(options); + const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null; + // Dest is an existing file, but not forcing + if (destStat && destStat.isFile() && !force) { + return; + } + // If dest is an existing directory, should copy inside. + const newDest = destStat && destStat.isDirectory() && copySourceDirectory + ? path.join(dest, path.basename(source)) + : dest; + if (!(yield ioUtil.exists(source))) { + throw new Error(`no such file or directory: ${source}`); + } + const sourceStat = yield ioUtil.stat(source); + if (sourceStat.isDirectory()) { + if (!recursive) { + throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`); + } + else { + yield cpDirRecursive(source, newDest, 0, force); + } + } + else { + if (path.relative(source, newDest) === '') { + // a file cannot be copied to itself + throw new Error(`'${newDest}' and '${source}' are the same file`); + } + yield copyFile(source, newDest, force); + } + }); +} +exports.cp = cp; +/** + * Moves a path. + * + * @param source source path + * @param dest destination path + * @param options optional. See MoveOptions. + */ +function mv(source, dest, options = {}) { + return __awaiter(this, void 0, void 0, function* () { + if (yield ioUtil.exists(dest)) { + let destExists = true; + if (yield ioUtil.isDirectory(dest)) { + // If dest is directory copy src into dest + dest = path.join(dest, path.basename(source)); + destExists = yield ioUtil.exists(dest); + } + if (destExists) { + if (options.force == null || options.force) { + yield rmRF(dest); + } + else { + throw new Error('Destination already exists'); + } + } + } + yield mkdirP(path.dirname(dest)); + yield ioUtil.rename(source, dest); + }); +} +exports.mv = mv; +/** + * Remove a path recursively with force + * + * @param inputPath path to remove + */ +function rmRF(inputPath) { + return __awaiter(this, void 0, void 0, function* () { + if (ioUtil.IS_WINDOWS) { + // Check for invalid characters + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + if (/[*"<>|]/.test(inputPath)) { + throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'); + } + } + try { + // note if path does not exist, error is silent + yield ioUtil.rm(inputPath, { + force: true, + maxRetries: 3, + recursive: true, + retryDelay: 300 + }); + } + catch (err) { + throw new Error(`File was unable to be removed ${err}`); + } + }); +} +exports.rmRF = rmRF; +/** + * Make a directory. Creates the full path with folders in between + * Will throw if it fails + * + * @param fsPath path to create + * @returns Promise + */ +function mkdirP(fsPath) { + return __awaiter(this, void 0, void 0, function* () { + assert_1.ok(fsPath, 'a path argument must be provided'); + yield ioUtil.mkdir(fsPath, { recursive: true }); + }); +} +exports.mkdirP = mkdirP; +/** + * Returns path of a tool had the tool actually been invoked. Resolves via paths. + * If you check and the tool does not exist, it will throw. + * + * @param tool name of the tool + * @param check whether to check if tool exists + * @returns Promise path to tool + */ +function which(tool, check) { + return __awaiter(this, void 0, void 0, function* () { + if (!tool) { + throw new Error("parameter 'tool' is required"); + } + // recursive when check=true + if (check) { + const result = yield which(tool, false); + if (!result) { + if (ioUtil.IS_WINDOWS) { + throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`); + } + else { + throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`); + } + } + return result; + } + const matches = yield findInPath(tool); + if (matches && matches.length > 0) { + return matches[0]; + } + return ''; + }); +} +exports.which = which; +/** + * Returns a list of all occurrences of the given tool on the system path. + * + * @returns Promise the paths of the tool + */ +function findInPath(tool) { + return __awaiter(this, void 0, void 0, function* () { + if (!tool) { + throw new Error("parameter 'tool' is required"); + } + // build the list of extensions to try + const extensions = []; + if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) { + for (const extension of process.env['PATHEXT'].split(path.delimiter)) { + if (extension) { + extensions.push(extension); + } + } + } + // if it's rooted, return it if exists. otherwise return empty. + if (ioUtil.isRooted(tool)) { + const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions); + if (filePath) { + return [filePath]; + } + return []; + } + // if any path separators, return empty + if (tool.includes(path.sep)) { + return []; + } + // build the list of directories + // + // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, + // it feels like we should not do this. Checking the current directory seems like more of a use + // case of a shell, and the which() function exposed by the toolkit should strive for consistency + // across platforms. + const directories = []; + if (process.env.PATH) { + for (const p of process.env.PATH.split(path.delimiter)) { + if (p) { + directories.push(p); + } + } + } + // find all matches + const matches = []; + for (const directory of directories) { + const filePath = yield ioUtil.tryGetExecutablePath(path.join(directory, tool), extensions); + if (filePath) { + matches.push(filePath); + } + } + return matches; + }); +} +exports.findInPath = findInPath; +function readCopyOptions(options) { + const force = options.force == null ? true : options.force; + const recursive = Boolean(options.recursive); + const copySourceDirectory = options.copySourceDirectory == null + ? true + : Boolean(options.copySourceDirectory); + return { force, recursive, copySourceDirectory }; +} +function cpDirRecursive(sourceDir, destDir, currentDepth, force) { + return __awaiter(this, void 0, void 0, function* () { + // Ensure there is not a run away recursive copy + if (currentDepth >= 255) + return; + currentDepth++; + yield mkdirP(destDir); + const files = yield ioUtil.readdir(sourceDir); + for (const fileName of files) { + const srcFile = `${sourceDir}/${fileName}`; + const destFile = `${destDir}/${fileName}`; + const srcFileStat = yield ioUtil.lstat(srcFile); + if (srcFileStat.isDirectory()) { + // Recurse + yield cpDirRecursive(srcFile, destFile, currentDepth, force); + } + else { + yield copyFile(srcFile, destFile, force); + } + } + // Change the mode for the newly created directory + yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode); + }); +} +// Buffered file copy +function copyFile(srcFile, destFile, force) { + return __awaiter(this, void 0, void 0, function* () { + if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) { + // unlink/re-link it + try { + yield ioUtil.lstat(destFile); + yield ioUtil.unlink(destFile); + } + catch (e) { + // Try to override file permission + if (e.code === 'EPERM') { + yield ioUtil.chmod(destFile, '0666'); + yield ioUtil.unlink(destFile); + } + // other errors = it doesn't exist, no work to do + } + // Copy over symlink + const symlinkFull = yield ioUtil.readlink(srcFile); + yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null); + } + else if (!(yield ioUtil.exists(destFile)) || force) { + yield ioUtil.copyFile(srcFile, destFile); + } + }); +} +//# sourceMappingURL=io.js.map + +/***/ }), + +/***/ 9041: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +module.exports = __nccwpck_require__(7111); + + +/***/ }), + +/***/ 7111: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +var net = __nccwpck_require__(1808); +var tls = __nccwpck_require__(4404); +var http = __nccwpck_require__(3685); +var https = __nccwpck_require__(5687); +var events = __nccwpck_require__(2361); +var assert = __nccwpck_require__(9491); +var util = __nccwpck_require__(3837); + + +exports.httpOverHttp = httpOverHttp; +exports.httpsOverHttp = httpsOverHttp; +exports.httpOverHttps = httpOverHttps; +exports.httpsOverHttps = httpsOverHttps; + + +function httpOverHttp(options) { + var agent = new TunnelingAgent(options); + agent.request = http.request; + return agent; +} + +function httpsOverHttp(options) { + var agent = new TunnelingAgent(options); + agent.request = http.request; + agent.createSocket = createSecureSocket; + agent.defaultPort = 443; + return agent; +} + +function httpOverHttps(options) { + var agent = new TunnelingAgent(options); + agent.request = https.request; + return agent; +} + +function httpsOverHttps(options) { + var agent = new TunnelingAgent(options); + agent.request = https.request; + agent.createSocket = createSecureSocket; + agent.defaultPort = 443; + return agent; +} + + +function TunnelingAgent(options) { + var self = this; + self.options = options || {}; + self.proxyOptions = self.options.proxy || {}; + self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets; + self.requests = []; + self.sockets = []; + + self.on('free', function onFree(socket, host, port, localAddress) { + var options = toOptions(host, port, localAddress); + for (var i = 0, len = self.requests.length; i < len; ++i) { + var pending = self.requests[i]; + if (pending.host === options.host && pending.port === options.port) { + // Detect the request to connect same origin server, + // reuse the connection. + self.requests.splice(i, 1); + pending.request.onSocket(socket); + return; + } + } + socket.destroy(); + self.removeSocket(socket); + }); +} +util.inherits(TunnelingAgent, events.EventEmitter); + +TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) { + var self = this; + var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress)); + + if (self.sockets.length >= this.maxSockets) { + // We are over limit so we'll add it to the queue. + self.requests.push(options); + return; + } + + // If we are under maxSockets create a new one. + self.createSocket(options, function(socket) { + socket.on('free', onFree); + socket.on('close', onCloseOrRemove); + socket.on('agentRemove', onCloseOrRemove); + req.onSocket(socket); + + function onFree() { + self.emit('free', socket, options); + } + + function onCloseOrRemove(err) { + self.removeSocket(socket); + socket.removeListener('free', onFree); + socket.removeListener('close', onCloseOrRemove); + socket.removeListener('agentRemove', onCloseOrRemove); + } + }); +}; + +TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { + var self = this; + var placeholder = {}; + self.sockets.push(placeholder); + + var connectOptions = mergeOptions({}, self.proxyOptions, { + method: 'CONNECT', + path: options.host + ':' + options.port, + agent: false, + headers: { + host: options.host + ':' + options.port + } + }); + if (options.localAddress) { + connectOptions.localAddress = options.localAddress; + } + if (connectOptions.proxyAuth) { + connectOptions.headers = connectOptions.headers || {}; + connectOptions.headers['Proxy-Authorization'] = 'Basic ' + + new Buffer(connectOptions.proxyAuth).toString('base64'); + } + + debug('making CONNECT request'); + var connectReq = self.request(connectOptions); + connectReq.useChunkedEncodingByDefault = false; // for v0.6 + connectReq.once('response', onResponse); // for v0.6 + connectReq.once('upgrade', onUpgrade); // for v0.6 + connectReq.once('connect', onConnect); // for v0.7 or later + connectReq.once('error', onError); + connectReq.end(); + + function onResponse(res) { + // Very hacky. This is necessary to avoid http-parser leaks. + res.upgrade = true; + } + + function onUpgrade(res, socket, head) { + // Hacky. + process.nextTick(function() { + onConnect(res, socket, head); + }); + } + + function onConnect(res, socket, head) { + connectReq.removeAllListeners(); + socket.removeAllListeners(); + + if (res.statusCode !== 200) { + debug('tunneling socket could not be established, statusCode=%d', + res.statusCode); + socket.destroy(); + var error = new Error('tunneling socket could not be established, ' + + 'statusCode=' + res.statusCode); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + return; + } + if (head.length > 0) { + debug('got illegal response body from proxy'); + socket.destroy(); + var error = new Error('got illegal response body from proxy'); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + return; + } + debug('tunneling connection has established'); + self.sockets[self.sockets.indexOf(placeholder)] = socket; + return cb(socket); + } + + function onError(cause) { + connectReq.removeAllListeners(); + + debug('tunneling socket could not be established, cause=%s\n', + cause.message, cause.stack); + var error = new Error('tunneling socket could not be established, ' + + 'cause=' + cause.message); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + } +}; + +TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { + var pos = this.sockets.indexOf(socket) + if (pos === -1) { + return; + } + this.sockets.splice(pos, 1); + + var pending = this.requests.shift(); + if (pending) { + // If we have pending requests and a socket gets closed a new one + // needs to be created to take over in the pool for the one that closed. + this.createSocket(pending, function(socket) { + pending.request.onSocket(socket); + }); + } +}; + +function createSecureSocket(options, cb) { + var self = this; + TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { + var hostHeader = options.request.getHeader('host'); + var tlsOptions = mergeOptions({}, self.options, { + socket: socket, + servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host + }); + + // 0 is dummy port for v0.6 + var secureSocket = tls.connect(0, tlsOptions); + self.sockets[self.sockets.indexOf(socket)] = secureSocket; + cb(secureSocket); + }); +} + + +function toOptions(host, port, localAddress) { + if (typeof host === 'string') { // since v0.10 + return { + host: host, + port: port, + localAddress: localAddress + }; + } + return host; // for v0.11 or later +} + +function mergeOptions(target) { + for (var i = 1, len = arguments.length; i < len; ++i) { + var overrides = arguments[i]; + if (typeof overrides === 'object') { + var keys = Object.keys(overrides); + for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { + var k = keys[j]; + if (overrides[k] !== undefined) { + target[k] = overrides[k]; + } + } + } + } + return target; +} + + +var debug; +if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { + debug = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof args[0] === 'string') { + args[0] = 'TUNNEL: ' + args[0]; + } else { + args.unshift('TUNNEL:'); + } + console.error.apply(console, args); + } +} else { + debug = function() {}; +} +exports.debug = debug; // for test + + +/***/ }), + +/***/ 5814: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "v1", ({ + enumerable: true, + get: function () { + return _v.default; + } +})); +Object.defineProperty(exports, "v3", ({ + enumerable: true, + get: function () { + return _v2.default; + } +})); +Object.defineProperty(exports, "v4", ({ + enumerable: true, + get: function () { + return _v3.default; + } +})); +Object.defineProperty(exports, "v5", ({ + enumerable: true, + get: function () { + return _v4.default; + } +})); +Object.defineProperty(exports, "NIL", ({ + enumerable: true, + get: function () { + return _nil.default; + } +})); +Object.defineProperty(exports, "version", ({ + enumerable: true, + get: function () { + return _version.default; + } +})); +Object.defineProperty(exports, "validate", ({ + enumerable: true, + get: function () { + return _validate.default; + } +})); +Object.defineProperty(exports, "stringify", ({ + enumerable: true, + get: function () { + return _stringify.default; + } +})); +Object.defineProperty(exports, "parse", ({ + enumerable: true, + get: function () { + return _parse.default; + } +})); + +var _v = _interopRequireDefault(__nccwpck_require__(6471)); + +var _v2 = _interopRequireDefault(__nccwpck_require__(3384)); + +var _v3 = _interopRequireDefault(__nccwpck_require__(5940)); + +var _v4 = _interopRequireDefault(__nccwpck_require__(9193)); + +var _nil = _interopRequireDefault(__nccwpck_require__(8654)); + +var _version = _interopRequireDefault(__nccwpck_require__(2362)); + +var _validate = _interopRequireDefault(__nccwpck_require__(9815)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(5183)); + +var _parse = _interopRequireDefault(__nccwpck_require__(5108)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), + +/***/ 9313: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function md5(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('md5').update(bytes).digest(); +} + +var _default = md5; +exports["default"] = _default; + +/***/ }), + +/***/ 8654: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = '00000000-0000-0000-0000-000000000000'; +exports["default"] = _default; + +/***/ }), + +/***/ 5108: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(9815)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function parse(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + let v; + const arr = new Uint8Array(16); // Parse ########-....-....-....-............ + + arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; + arr[1] = v >>> 16 & 0xff; + arr[2] = v >>> 8 & 0xff; + arr[3] = v & 0xff; // Parse ........-####-....-....-............ + + arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; + arr[5] = v & 0xff; // Parse ........-....-####-....-............ + + arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; + arr[7] = v & 0xff; // Parse ........-....-....-####-............ + + arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; + arr[9] = v & 0xff; // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + + arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; + arr[11] = v / 0x100000000 & 0xff; + arr[12] = v >>> 24 & 0xff; + arr[13] = v >>> 16 & 0xff; + arr[14] = v >>> 8 & 0xff; + arr[15] = v & 0xff; + return arr; +} + +var _default = parse; +exports["default"] = _default; + +/***/ }), + +/***/ 1629: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; +exports["default"] = _default; + +/***/ }), + +/***/ 9271: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = rng; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate + +let poolPtr = rnds8Pool.length; + +function rng() { + if (poolPtr > rnds8Pool.length - 16) { + _crypto.default.randomFillSync(rnds8Pool); + + poolPtr = 0; + } + + return rnds8Pool.slice(poolPtr, poolPtr += 16); +} + +/***/ }), + +/***/ 2017: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function sha1(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('sha1').update(bytes).digest(); +} + +var _default = sha1; +exports["default"] = _default; + +/***/ }), + +/***/ 5183: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(9815)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 0x100).toString(16).substr(1)); +} + +function stringify(arr, offset = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + + if (!(0, _validate.default)(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +var _default = stringify; +exports["default"] = _default; + +/***/ }), + +/***/ 6471: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9271)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(5183)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html +let _nodeId; + +let _clockseq; // Previous uuid creation time + + +let _lastMSecs = 0; +let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details + +function v1(options, buf, offset) { + let i = buf && offset || 0; + const b = buf || new Array(16); + options = options || {}; + let node = options.node || _nodeId; + let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not + // specified. We do this lazily to minimize issues related to insufficient + // system entropy. See #189 + + if (node == null || clockseq == null) { + const seedBytes = options.random || (options.rng || _rng.default)(); + + if (node == null) { + // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) + node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; + } + + if (clockseq == null) { + // Per 4.2.2, randomize (14 bit) clockseq + clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; + } + } // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + + + let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + + let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) + + const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression + + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + + + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } // Per 4.2.1.2 Throw error if too many uuids are requested + + + if (nsecs >= 10000) { + throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + + msecs += 12219292800000; // `time_low` + + const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; // `time_mid` + + const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; // `time_high_and_version` + + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + + b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + + b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` + + b[i++] = clockseq & 0xff; // `node` + + for (let n = 0; n < 6; ++n) { + b[i + n] = node[n]; + } + + return buf || (0, _stringify.default)(b); +} + +var _default = v1; +exports["default"] = _default; + +/***/ }), + +/***/ 3384: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5717)); + +var _md = _interopRequireDefault(__nccwpck_require__(9313)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v3 = (0, _v.default)('v3', 0x30, _md.default); +var _default = v3; +exports["default"] = _default; + +/***/ }), + +/***/ 5717: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = _default; +exports.URL = exports.DNS = void 0; + +var _stringify = _interopRequireDefault(__nccwpck_require__(5183)); + +var _parse = _interopRequireDefault(__nccwpck_require__(5108)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function stringToBytes(str) { + str = unescape(encodeURIComponent(str)); // UTF8 escape + + const bytes = []; + + for (let i = 0; i < str.length; ++i) { + bytes.push(str.charCodeAt(i)); + } + + return bytes; +} + +const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; +exports.DNS = DNS; +const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; +exports.URL = URL; + +function _default(name, version, hashfunc) { + function generateUUID(value, namespace, buf, offset) { + if (typeof value === 'string') { + value = stringToBytes(value); + } + + if (typeof namespace === 'string') { + namespace = (0, _parse.default)(namespace); + } + + if (namespace.length !== 16) { + throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); + } // Compute hash of namespace and value, Per 4.3 + // Future: Use spread syntax when supported on all platforms, e.g. `bytes = + // hashfunc([...namespace, ... value])` + + + let bytes = new Uint8Array(16 + value.length); + bytes.set(namespace); + bytes.set(value, namespace.length); + bytes = hashfunc(bytes); + bytes[6] = bytes[6] & 0x0f | version; + bytes[8] = bytes[8] & 0x3f | 0x80; + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = bytes[i]; + } + + return buf; + } + + return (0, _stringify.default)(bytes); + } // Function#name is not settable on some platforms (#270) + + + try { + generateUUID.name = name; // eslint-disable-next-line no-empty + } catch (err) {} // For CommonJS default export support + + + generateUUID.DNS = DNS; + generateUUID.URL = URL; + return generateUUID; +} + +/***/ }), + +/***/ 5940: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(9271)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(5183)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function v4(options, buf, offset) { + options = options || {}; + + const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + + + rnds[6] = rnds[6] & 0x0f | 0x40; + rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + + return buf; + } + + return (0, _stringify.default)(rnds); +} + +var _default = v4; +exports["default"] = _default; + +/***/ }), + +/***/ 9193: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5717)); + +var _sha = _interopRequireDefault(__nccwpck_require__(2017)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v5 = (0, _v.default)('v5', 0x50, _sha.default); +var _default = v5; +exports["default"] = _default; + +/***/ }), + +/***/ 9815: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _regex = _interopRequireDefault(__nccwpck_require__(1629)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function validate(uuid) { + return typeof uuid === 'string' && _regex.default.test(uuid); +} + +var _default = validate; +exports["default"] = _default; + +/***/ }), + +/***/ 2362: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(9815)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function version(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + return parseInt(uuid.substr(14, 1), 16); +} + +var _default = version; +exports["default"] = _default; + +/***/ }), + +/***/ 1988: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.CommandHelper = void 0; +var os = __nccwpck_require__(2037); +var core = __nccwpck_require__(3195); +var exec = __nccwpck_require__(9714); +var CommandHelper = /** @class */ (function () { + function CommandHelper() { + } + /** + * Runs a command based on the OS of the agent running this task. + * @param command - the command to execute + * @returns the string output from the command + */ + CommandHelper.prototype.execCommandAsync = function (command) { + return __awaiter(this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(os.platform() == 'win32')) return [3 /*break*/, 2]; + return [4 /*yield*/, this.execPwshCommandAsync(command)]; + case 1: + _a = _b.sent(); + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, this.execBashCommandAsync(command)]; + case 3: + _a = _b.sent(); + _b.label = 4; + case 4: return [2 /*return*/, _a]; + } + }); + }); + }; + /** + * @param command - the command to execute in Bash + * @returns the string output from the command + */ + CommandHelper.prototype.execBashCommandAsync = function (command) { + return __awaiter(this, void 0, void 0, function () { + var bashOutput, errorStream, options, exitCode, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + bashOutput = ''; + errorStream = ''; + options = { + listeners: { + stdout: function (data) { + bashOutput += data.toString(); + core.info(data.toString()); + }, + stderr: function (data) { + errorStream += data.toString(); + core.error(data.toString()); + } + }, + failOnStdErr: true, + ignoreReturnCode: false, + errStream: process.stderr, + outStream: process.stdout + }; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, exec.exec('bash', ['-c', command], options)]; + case 2: + exitCode = _a.sent(); + if (exitCode !== 0) { + throw new Error("Command failed with exit code " + exitCode + ". Error stream: " + errorStream); + } + return [2 /*return*/, bashOutput.trim()]; + case 3: + err_1 = _a.sent(); + core.setFailed(err_1.message); + throw err_1; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Executes a given command using the pwsh executable. + * @param command - the command to execute in PowerShell + * @returns the string output from the command + */ + CommandHelper.prototype.execPwshCommandAsync = function (command) { + return __awaiter(this, void 0, void 0, function () { + var pwshOutput, errorStream, options, exitCode, err_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + pwshOutput = ''; + errorStream = ''; + options = { + listeners: { + stdout: function (data) { + pwshOutput += data.toString(); + core.info(data.toString()); + }, + stderr: function (data) { + errorStream += data.toString(); + core.error(data.toString()); + } + }, + failOnStdErr: true, + ignoreReturnCode: false, + errStream: process.stderr, + outStream: process.stdout + }; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, exec.exec('pwsh', [command], options)]; + case 2: + exitCode = _a.sent(); + if (exitCode !== 0) { + throw new Error("Command failed with exit code " + exitCode + ". Error stream: " + errorStream); + } + return [2 /*return*/, pwshOutput.trim()]; + case 3: + err_2 = _a.sent(); + core.setFailed(err_2.message); + throw err_2; + case 4: return [2 /*return*/]; + } + }); + }); + }; + return CommandHelper; +}()); +exports.CommandHelper = CommandHelper; + + +/***/ }), + +/***/ 2929: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.ContainerAppHelper = void 0; +var core = __nccwpck_require__(3195); +var io = __nccwpck_require__(9529); +var path = __nccwpck_require__(1017); +var os = __nccwpck_require__(2037); +var CommandHelper_1 = __nccwpck_require__(1988); +var Utility_1 = __nccwpck_require__(2135); +var util = __nccwpck_require__(3837); +var cpExec = util.promisify((__nccwpck_require__(2081).exec)); +var ORYX_CLI_IMAGE = 'mcr.microsoft.com/oryx/cli:builder-debian-buster-20230208.1'; +var ORYX_BUILDER_IMAGE = 'mcr.microsoft.com/oryx/builder:20230208.1'; +var IS_WINDOWS_AGENT = os.platform() == 'win32'; +var PACK_CMD = IS_WINDOWS_AGENT ? path.join(os.tmpdir(), 'pack') : 'pack'; +var ContainerAppHelper = /** @class */ (function () { + function ContainerAppHelper(disableTelemetry) { + this.disableTelemetry = false; + this.disableTelemetry = disableTelemetry; + } + /** + * Creates an Azure Container App based from an image that was previously built. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param environment - the Container App Environment that will be associated with the Container App + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + */ + ContainerAppHelper.prototype.createContainerApp = function (containerAppName, resourceGroup, environment, imageToDeploy, optionalCmdArgs) { + return __awaiter(this, void 0, void 0, function () { + var command_1, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to create Container App with name \"" + containerAppName + "\" in resource group \"" + resourceGroup + "\" based from image \"" + imageToDeploy + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command_1 = "az containerapp create -n " + containerAppName + " -g " + resourceGroup + " -i " + imageToDeploy + " --environment " + environment; + optionalCmdArgs.forEach(function (val) { + command_1 += " " + val; + }); + return [4 /*yield*/, cpExec("" + command_1)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + core.error(err_1.message); + throw err_1; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Creates an Azure Container App based from a YAML configuration file. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param yamlConfigPath - the path to the YAML configuration file that the Container App properties will be based from + */ + ContainerAppHelper.prototype.createContainerAppFromYaml = function (containerAppName, resourceGroup, yamlConfigPath) { + return __awaiter(this, void 0, void 0, function () { + var command, err_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to create Container App with name \"" + containerAppName + "\" in resource group \"" + resourceGroup + "\" from provided YAML \"" + yamlConfigPath + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az containerapp create -n " + containerAppName + " -g " + resourceGroup + " --yaml " + yamlConfigPath; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_2 = _a.sent(); + core.error(err_2.message); + throw err_2; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Updates an existing Azure Container App based from an image that was previously built. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + */ + ContainerAppHelper.prototype.updateContainerApp = function (containerAppName, resourceGroup, imageToDeploy, optionalCmdArgs) { + return __awaiter(this, void 0, void 0, function () { + var command_2, err_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to update Container App with name \"" + containerAppName + "\" in resource group \"" + resourceGroup + "\" based from image \"" + imageToDeploy + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command_2 = "az containerapp update -n " + containerAppName + " -g " + resourceGroup + " -i " + imageToDeploy; + optionalCmdArgs.forEach(function (val) { + command_2 += " " + val; + }); + return [4 /*yield*/, cpExec("" + command_2)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_3 = _a.sent(); + core.error(err_3.message); + throw err_3; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Updates an existing Azure Container App using the 'az containerapp up' command. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + * @param ingress - the ingress that the Container App will be exposed on + * @param targetPort - the target port that the Container App will be exposed on + */ + ContainerAppHelper.prototype.updateContainerAppWithUp = function (containerAppName, resourceGroup, imageToDeploy, optionalCmdArgs, ingress, targetPort) { + return __awaiter(this, void 0, void 0, function () { + var util, command_3, err_4; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to update Container App with name \"" + containerAppName + "\" in resource group \"" + resourceGroup + "\" based from image \"" + imageToDeploy + "\""); + util = new Utility_1.Utility(); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command_3 = "az containerapp up -n " + containerAppName + " -g " + resourceGroup + " -i " + imageToDeploy; + optionalCmdArgs.forEach(function (val) { + command_3 += " " + val; + }); + if (!util.isNullOrEmpty(ingress)) { + command_3 += " --ingress " + ingress; + } + if (!util.isNullOrEmpty(targetPort)) { + command_3 += " --target-port " + targetPort; + } + return [4 /*yield*/, cpExec("" + command_3)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_4 = _a.sent(); + core.error(err_4.message); + throw err_4; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Updates an existing Azure Container App based from a YAML configuration file. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param yamlConfigPath - the path to the YAML configuration file that the Container App properties will be based from + */ + ContainerAppHelper.prototype.updateContainerAppFromYaml = function (containerAppName, resourceGroup, yamlConfigPath) { + return __awaiter(this, void 0, void 0, function () { + var command, err_5; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to update Container App with name \"" + containerAppName + "\" in resource group \"" + resourceGroup + "\" from provided YAML \"" + yamlConfigPath + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az containerapp update -n " + containerAppName + " -g " + resourceGroup + " --yaml " + yamlConfigPath; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_5 = _a.sent(); + core.error(err_5.message); + throw err_5; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Determines if the provided Container App exists in the provided resource group. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @returns true if the Container App exists, false otherwise + */ + ContainerAppHelper.prototype.doesContainerAppExist = function (containerAppName, resourceGroup) { + return __awaiter(this, void 0, void 0, function () { + var command, _a, stdout, stderr, err_6; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + core.debug("Attempting to determine if Container App with name \"" + containerAppName + "\" exists in resource group \"" + resourceGroup + "\""); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + command = "az containerapp show -n " + containerAppName + " -g " + resourceGroup + " -o none"; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a = _b.sent(), stdout = _a.stdout, stderr = _a.stderr; + return [2 /*return*/, !stderr]; + case 3: + err_6 = _b.sent(); + core.warning(err_6.message); + return [2 /*return*/, false]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Determines if the provided Container App Environment exists in the provided resource group. + * @param containerAppEnvironment - the name of the Container App Environment + * @param resourceGroup - the resource group that the Container App Environment is found in + * @returns true if the Container App Environment exists, false otherwise + */ + ContainerAppHelper.prototype.doesContainerAppEnvironmentExist = function (containerAppEnvironment, resourceGroup) { + return __awaiter(this, void 0, void 0, function () { + var command, _a, stdout, stderr, err_7; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + core.debug("Attempting to determine if Container App Environment with name \"" + containerAppEnvironment + "\" exists in resource group \"" + resourceGroup + "\""); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + command = "az containerapp env show -n " + containerAppEnvironment + " -g " + resourceGroup + " -o none"; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a = _b.sent(), stdout = _a.stdout, stderr = _a.stderr; + return [2 /*return*/, !stderr]; + case 3: + err_7 = _b.sent(); + core.warning(err_7.message); + return [2 /*return*/, false]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Determines if the provided resource group exists. + * @param resourceGroup - the name of the resource group + * @returns true if the resource group exists, false otherwise + */ + ContainerAppHelper.prototype.doesResourceGroupExist = function (resourceGroup) { + return __awaiter(this, void 0, void 0, function () { + var command, _a, stdout, stderr, err_8; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + core.debug("Attempting to determine if resource group \"" + resourceGroup + "\" exists"); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + command = "az group show -n " + resourceGroup + " -o none"; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a = _b.sent(), stdout = _a.stdout, stderr = _a.stderr; + return [2 /*return*/, !stderr]; + case 3: + err_8 = _b.sent(); + core.warning(err_8.message); + return [2 /*return*/, false]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Gets the default location for the Container App provider. + * @returns the default location if found, otherwise 'eastus2' + */ + ContainerAppHelper.prototype.getDefaultContainerAppLocation = function () { + return __awaiter(this, void 0, void 0, function () { + var command, _a, stdout, stderr, err_9; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + core.debug("Attempting to get the default location for the Container App service for the subscription."); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + command = "az provider show -n Microsoft.App --query \"resourceTypes[?resourceType=='containerApps'].locations[] | [0]\""; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a = _b.sent(), stdout = _a.stdout, stderr = _a.stderr; + // If successful, strip out double quotes, spaces and parentheses from the first location returned + return [2 /*return*/, !stderr ? stdout.toLowerCase().replace(/["() ]/g, "") : "eastus2"]; + case 3: + err_9 = _b.sent(); + core.warning(err_9.message); + return [2 /*return*/, "eastus2"]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Creates a new resource group in the provided location. + * @param name - the name of the resource group to create + * @param location - the location to create the resource group in + */ + ContainerAppHelper.prototype.createResourceGroup = function (name, location) { + return __awaiter(this, void 0, void 0, function () { + var command, err_10; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to create resource group \"" + name + "\" in location \"" + location + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az group create -n " + name + " -l " + location; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_10 = _a.sent(); + core.error(err_10.message); + throw err_10; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Gets the name of an existing Container App Environment in the provided resource group. + * @param resourceGroup - the resource group to check for an existing Container App Environment + * @returns the name of the existing Container App Environment, null if none exists + */ + ContainerAppHelper.prototype.getExistingContainerAppEnvironment = function (resourceGroup) { + return __awaiter(this, void 0, void 0, function () { + var command, _a, stdout, stderr, err_11; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + core.debug("Attempting to get the existing Container App Environment in resource group \"" + resourceGroup + "\""); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + command = "az containerapp env list -g " + resourceGroup + " --query [0].name\""; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a = _b.sent(), stdout = _a.stdout, stderr = _a.stderr; + return [2 /*return*/, !stderr ? stdout : null]; + case 3: + err_11 = _b.sent(); + core.warning(err_11.message); + return [2 /*return*/, null]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Creates a new Azure Container App Environment in the provided resource group. + * @param name - the name of the Container App Environment + * @param resourceGroup - the resource group that the Container App Environment will be created in + * @param location - the location that the Container App Environment will be created in + */ + ContainerAppHelper.prototype.createContainerAppEnvironment = function (name, resourceGroup, location) { + return __awaiter(this, void 0, void 0, function () { + var util, command, err_12; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + util = new Utility_1.Utility(); + core.debug("Attempting to create Container App Environment with name \"" + name + "\" in resource group \"" + resourceGroup + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az containerapp env create -n " + name + " -g " + resourceGroup; + if (!util.isNullOrEmpty(location)) { + command += " -l " + location; + } + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_12 = _a.sent(); + core.error(err_12.message); + throw err_12; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Disables ingress on an existing Container App. + * @param name - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + */ + ContainerAppHelper.prototype.disableContainerAppIngress = function (name, resourceGroup) { + return __awaiter(this, void 0, void 0, function () { + var command, err_13; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to disable ingress for Container App with name \"" + name + "\" in resource group \"" + resourceGroup + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az containerapp ingress disable -n " + name + " -g " + resourceGroup; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_13 = _a.sent(); + core.error(err_13.message); + throw err_13; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Updates the ACR details on an existing Container App. + * @param name - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param acrName - the name of the Azure Container Registry (without the .azurecr.io suffix) + * @param acrUsername - the username used to authenticate with the Azure Container Registry + * @param acrPassword - the password used to authenticate with the Azure Container Registry + */ + ContainerAppHelper.prototype.updateContainerAppRegistryDetails = function (name, resourceGroup, acrName, acrUsername, acrPassword) { + return __awaiter(this, void 0, void 0, function () { + var command, err_14; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to set the ACR details for Container App with name \"" + name + "\" in resource group \"" + resourceGroup + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "az containerapp registry set -n " + name + " -g " + resourceGroup + " --server " + acrName + ".azurecr.io --username " + acrUsername + " --password " + acrPassword; + return [4 /*yield*/, cpExec("" + command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_14 = _a.sent(); + core.error(err_14.message); + throw err_14; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Using the Oryx++ Builder, creates a runnable application image from the provided application source. + * @param imageToDeploy - the name of the runnable application image that is created and can be later deployed + * @param appSourcePath - the path to the application source on the machine + * @param runtimeStack - the runtime stack to use in the image layer that runs the application + */ + ContainerAppHelper.prototype.createRunnableAppImage = function (imageToDeploy, appSourcePath, runtimeStack) { + return __awaiter(this, void 0, void 0, function () { + var telemetryArg, err_15; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to create a runnable application image using the Oryx++ Builder with image name \"" + imageToDeploy + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + telemetryArg = "--env \"CALLER_ID=azure-pipelines-rc-v1\""; + if (this.disableTelemetry) { + telemetryArg = "--env \"ORYX_DISABLE_TELEMETRY=true\""; + } + return [4 /*yield*/, cpExec(PACK_CMD + " build " + imageToDeploy + " --path " + appSourcePath + " --builder " + ORYX_BUILDER_IMAGE + " --run-image mcr.microsoft.com/oryx/" + runtimeStack + " " + telemetryArg)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_15 = _a.sent(); + core.error(err_15.message); + throw err_15; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Using a Dockerfile that was provided or found at the root of the application source, + * creates a runable application image. + * @param imageToDeploy - the name of the runnable application image that is created and can be later deployed + * @param appSourcePath - the path to the application source on the machine + * @param dockerfilePath - the path to the Dockerfile to build and tag with the provided image name + */ + ContainerAppHelper.prototype.createRunnableAppImageFromDockerfile = function (imageToDeploy, appSourcePath, dockerfilePath) { + return __awaiter(this, void 0, void 0, function () { + var dockerTool, err_16; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to create a runnable application image from the provided/found Dockerfile \"" + dockerfilePath + "\" with image name \"" + imageToDeploy + "\""); + _a.label = 1; + case 1: + _a.trys.push([1, 4, , 5]); + return [4 /*yield*/, io.which("docker", true)]; + case 2: + dockerTool = _a.sent(); + return [4 /*yield*/, new Utility_1.Utility().executeAndthrowIfError(dockerTool, ['build', '--file', "" + dockerfilePath, "" + appSourcePath, '--tag', "" + imageToDeploy])]; + case 3: + _a.sent(); + core.debug("Successfully created runnable application image from the provided/found Dockerfile \"" + dockerfilePath + "\" with image name \"" + imageToDeploy + "\""); + return [3 /*break*/, 5]; + case 4: + err_16 = _a.sent(); + core.setFailed(err_16.message); + throw err_16; + case 5: return [2 /*return*/]; + } + }); + }); + }; + /** + * Determines the runtime stack to use for the runnable application image. + * @param appSourcePath - the path to the application source on the machine + * @returns a string representing the runtime stack that can be used for the Oryx MCR runtime images + */ + ContainerAppHelper.prototype.determineRuntimeStackAsync = function (appSourcePath) { + return __awaiter(this, void 0, void 0, function () { + var dockerTool, oryxRuntimeTxtPath, command, runtimeStack, err_17; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug('Attempting to determine the runtime stack needed for the provided application source'); + _a.label = 1; + case 1: + _a.trys.push([1, 6, , 7]); + return [4 /*yield*/, io.which("docker", true)]; + case 2: + dockerTool = _a.sent(); + // Use 'oryx dockerfile' command to determine the runtime stack to use and write it to a temp file + return [4 /*yield*/, new Utility_1.Utility().executeAndthrowIfError(dockerTool, ['run', '--rm', '-v', appSourcePath + ":/app", "" + ORYX_CLI_IMAGE, '/bin/bash', '-c', "oryx dockerfile /app | head -n 1 | sed 's/ARG RUNTIME=//' >> /app/oryx-runtime.txt"]) + // Read the temp file to get the runtime stack into a variable + ]; + case 3: + // Use 'oryx dockerfile' command to determine the runtime stack to use and write it to a temp file + _a.sent(); + oryxRuntimeTxtPath = path.join(appSourcePath, 'oryx-runtime.txt'); + command = "head -n 1 " + oryxRuntimeTxtPath; + if (IS_WINDOWS_AGENT) { + command = "Get-Content -Path " + oryxRuntimeTxtPath + " -Head 1"; + } + return [4 /*yield*/, new CommandHelper_1.CommandHelper().execCommandAsync(command)]; + case 4: + runtimeStack = _a.sent(); + // Delete the temp file + command = "rm " + oryxRuntimeTxtPath; + if (IS_WINDOWS_AGENT) { + command = "Remove-Item -Path " + oryxRuntimeTxtPath; + } + return [4 /*yield*/, new CommandHelper_1.CommandHelper().execCommandAsync(command)]; + case 5: + _a.sent(); + return [2 /*return*/, runtimeStack]; + case 6: + err_17 = _a.sent(); + core.setFailed(err_17.message); + throw err_17; + case 7: return [2 /*return*/]; + } + }); + }); + }; + /** + * Sets the default builder on the machine to the Oryx++ Builder to prevent an exception from being thrown due + * to no default builder set. + */ + ContainerAppHelper.prototype.setDefaultBuilder = function () { + return __awaiter(this, void 0, void 0, function () { + var err_18; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.info('Setting the Oryx++ Builder as the default builder via the pack CLI'); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, cpExec("pack config default-builder " + ORYX_BUILDER_IMAGE)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_18 = _a.sent(); + core.setFailed(err_18.message); + throw err_18; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Installs the pack CLI that will be used to build a runnable application image. + * For more information about the pack CLI can be found here: https://buildpacks.io/docs/tools/pack/ + */ + ContainerAppHelper.prototype.installPackCliAsync = function () { + return __awaiter(this, void 0, void 0, function () { + var command, packZipDownloadUri, packZipDownloadFilePath, tgzSuffix, err_19; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug('Attempting to install the pack CLI'); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = ''; + if (IS_WINDOWS_AGENT) { + packZipDownloadUri = 'https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-windows.zip'; + packZipDownloadFilePath = path.join(PACK_CMD, 'pack-windows.zip'); + command = "New-Item -ItemType Directory -Path " + PACK_CMD + " -Force | Out-Null;" + + ("Invoke-WebRequest -Uri " + packZipDownloadUri + " -OutFile " + packZipDownloadFilePath + "; ") + + ("Expand-Archive -LiteralPath " + packZipDownloadFilePath + " -DestinationPath " + PACK_CMD + "; ") + + ("Remove-Item -Path " + packZipDownloadFilePath); + } + else { + tgzSuffix = os.platform() == 'darwin' ? 'macos' : 'linux'; + command = "(curl -sSL \"https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-" + tgzSuffix + ".tgz\" | " + + 'tar -C /usr/local/bin/ --no-same-owner -xzv pack)'; + } + return [4 /*yield*/, new CommandHelper_1.CommandHelper().execCommandAsync(command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_19 = _a.sent(); + core.error("Unable to install the pack CLI. Error: " + err_19.message); + core.setFailed(err_19.message); + throw err_19; + case 4: return [2 /*return*/]; + } + }); + }); + }; + return ContainerAppHelper; +}()); +exports.ContainerAppHelper = ContainerAppHelper; + + +/***/ }), + +/***/ 4769: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.ContainerRegistryHelper = void 0; +var core = __nccwpck_require__(3195); +var exec = __nccwpck_require__(9714); +var io = __nccwpck_require__(9529); +var CommandHelper_1 = __nccwpck_require__(1988); +var Utility_1 = __nccwpck_require__(2135); +var ContainerRegistryHelper = /** @class */ (function () { + function ContainerRegistryHelper() { + } + /** + * Authorizes Docker to make calls to the provided ACR instance using username and password. + * @param acrName - the name of the ACR instance to authenticate calls to + * @param acrUsername - the username for authentication + * @param acrPassword - the password for authentication + */ + ContainerRegistryHelper.prototype.loginAcrWithUsernamePassword = function (acrName, acrUsername, acrPassword) { + return __awaiter(this, void 0, void 0, function () { + var err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to log in to ACR instance \"" + acrName + "\" with username and password credentials"); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, exec.exec('docker', ["login", "--password-stdin", "--username", "" + acrUsername, acrName + ".azurecr.io"], { input: Buffer.from(acrPassword) })]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + core.error("Failed to log in to ACR instance \"" + acrName + "\" with username and password credentials"); + throw err_1; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Authorizes Docker to make calls to the provided ACR instance using an access token that is generated via + * the 'az acr login --expose-token' command. + * @param acrName - the name of the ACR instance to authenticate calls to. + */ + ContainerRegistryHelper.prototype.loginAcrWithAccessTokenAsync = function (acrName) { + return __awaiter(this, void 0, void 0, function () { + var command, err_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to log in to ACR instance \"" + acrName + "\" with access token"); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + command = "CA_ADO_TASK_ACR_ACCESS_TOKEN=$(az acr login --name " + acrName + " --output json --expose-token --only-show-errors | jq -r '.accessToken'); docker login " + acrName + ".azurecr.io -u 00000000-0000-0000-0000-000000000000 -p $CA_ADO_TASK_ACR_ACCESS_TOKEN > /dev/null 2>&1"; + return [4 /*yield*/, new CommandHelper_1.CommandHelper().execCommandAsync(command)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_2 = _a.sent(); + core.error("Failed to log in to ACR instance \"" + acrName + "\" with access token"); + throw err_2; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Pushes an image to the ACR instance that was previously authenticated against. + * @param imageToPush - the name of the image to push to ACR + */ + ContainerRegistryHelper.prototype.pushImageToAcr = function (imageToPush) { + return __awaiter(this, void 0, void 0, function () { + var dockerTool, err_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + core.debug("Attempting to push image \"" + imageToPush + "\" to ACR"); + _a.label = 1; + case 1: + _a.trys.push([1, 4, , 5]); + return [4 /*yield*/, io.which("docker", true)]; + case 2: + dockerTool = _a.sent(); + return [4 /*yield*/, new Utility_1.Utility().executeAndthrowIfError(dockerTool, ["push", "" + imageToPush])]; + case 3: + _a.sent(); + return [3 /*break*/, 5]; + case 4: + err_3 = _a.sent(); + core.error("Failed to push image \"" + imageToPush + "\" to ACR. Error: " + err_3.message); + core.setFailed(err_3.message); + throw err_3; + case 5: return [2 /*return*/]; + } + }); + }); + }; + return ContainerRegistryHelper; +}()); +exports.ContainerRegistryHelper = ContainerRegistryHelper; + + +/***/ }), + +/***/ 7166: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.TelemetryHelper = void 0; +var core = __nccwpck_require__(3195); +var Utility_1 = __nccwpck_require__(2135); +var io = __nccwpck_require__(9529); +var ORYX_CLI_IMAGE = "mcr.microsoft.com/oryx/cli:debian-buster-20230207.2"; +var SUCCESSFUL_RESULT = "succeeded"; +var FAILED_RESULT = "failed"; +var BUILDER_SCENARIO = "used-builder"; +var DOCKERFILE_SCENARIO = "used-dockerfile"; +var IMAGE_SCENARIO = "used-image"; +var util = new Utility_1.Utility(); +var TelemetryHelper = /** @class */ (function () { + function TelemetryHelper(disableTelemetry) { + this.disableTelemetry = disableTelemetry; + this.taskStartMilliseconds = Date.now(); + } + /** + * Marks that the task was successful in telemetry. + */ + TelemetryHelper.prototype.setSuccessfulResult = function () { + this.result = SUCCESSFUL_RESULT; + }; + /** + * Marks that the task failed in telemetry. + */ + TelemetryHelper.prototype.setFailedResult = function (errorMessage) { + this.result = FAILED_RESULT; + this.errorMessage = errorMessage; + }; + /** + * Marks that the task used the builder scenario. + */ + TelemetryHelper.prototype.setBuilderScenario = function () { + this.scenario = BUILDER_SCENARIO; + }; + /** + * Marks that the task used the Dockerfile scenario. + */ + TelemetryHelper.prototype.setDockerfileScenario = function () { + this.scenario = DOCKERFILE_SCENARIO; + }; + /** + * Marks that the task used the previously built image scenario. + */ + TelemetryHelper.prototype.setImageScenario = function () { + this.scenario = IMAGE_SCENARIO; + }; + /** + * If telemetry is enabled, uses the "oryx telemetry" command to log metadata about this task execution. + */ + TelemetryHelper.prototype.sendLogs = function () { + return __awaiter(this, void 0, void 0, function () { + var taskLengthMilliseconds, resultArg, scenarioArg, errorMessageArg, args, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + taskLengthMilliseconds = Date.now() - this.taskStartMilliseconds; + if (!!this.disableTelemetry) return [3 /*break*/, 4]; + core.info("Telemetry enabled; logging metadata about task result, length and scenario targeted."); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + resultArg = ''; + if (!util.isNullOrEmpty(this.result)) { + resultArg = "--property 'result=" + this.result + "'"; + } + scenarioArg = ''; + if (!util.isNullOrEmpty(this.scenario)) { + scenarioArg = "--property 'scenario=" + this.scenario + "'"; + } + errorMessageArg = ''; + if (!util.isNullOrEmpty(this.errorMessage)) { + errorMessageArg = "--property 'errorMessage=" + this.errorMessage + "'"; + } + args = ["run", "--rm", "" + ORYX_CLI_IMAGE, "/bin/bash", "-c", "oryx telemetry --event-name 'ContainerAppsPipelinesTaskRCV1' " + ("--processing-time '" + taskLengthMilliseconds + "' " + resultArg + " " + scenarioArg + " " + errorMessageArg + "\"")]; + // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task + return [4 /*yield*/, executeDockerCommand(args, true)]; + case 2: + // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + core.warning("Skipping telemetry logging due to the following exception: " + err_1.message); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + return TelemetryHelper; +}()); +exports.TelemetryHelper = TelemetryHelper; +var executeDockerCommand = function (args, continueOnError) { + if (continueOnError === void 0) { continueOnError = false; } + return __awaiter(void 0, void 0, void 0, function () { + var dockerTool, err_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 3, , 4]); + return [4 /*yield*/, io.which("docker", true)]; + case 1: + dockerTool = _a.sent(); + return [4 /*yield*/, new Utility_1.Utility().executeAndthrowIfError(dockerTool, args, continueOnError)]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_2 = _a.sent(); + core.setFailed("Error: " + err_2.message); + throw err_2; // Re-throw the error + case 4: return [2 /*return*/]; + } + }); + }); +}; + + +/***/ }), + +/***/ 2135: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.Utility = void 0; +var core = __nccwpck_require__(3195); +var exec = __nccwpck_require__(9714); +var util = __nccwpck_require__(3837); +var cpExec = util.promisify((__nccwpck_require__(2081).exec)); +var Utility = /** @class */ (function () { + function Utility() { + } + /** + * @param commandLine - the command to execute + * @param args - the arguments to pass to the command + * @param continueOnError - whether or not to continue execution if the command fails + */ + Utility.prototype.executeAndthrowIfError = function (commandLine, args, continueOnError) { + if (continueOnError === void 0) { continueOnError = false; } + return __awaiter(this, void 0, void 0, function () { + var stdout_1, stderr_1, options, exitCode, error_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + stdout_1 = ''; + stderr_1 = ''; + options = { + listeners: { + stdout: function (data) { + stdout_1 += data.toString(); + core.info(data.toString()); + }, + stderr: function (data) { + stderr_1 += data.toString(); + core.error(data.toString()); + } + } + }; + return [4 /*yield*/, exec.exec(commandLine, args, options)]; + case 1: + exitCode = _a.sent(); + if (!continueOnError && exitCode !== 0) { + core.error("Command failed with exit code " + exitCode + ". Error stream: " + stderr_1); + throw new Error("Command failed with exit code " + exitCode + ". Error stream: " + stderr_1); + } + return [3 /*break*/, 3]; + case 2: + error_1 = _a.sent(); + core.setFailed("Error: " + error_1.message); + throw error_1; // Re-throw the error + case 3: return [2 /*return*/]; + } + }); + }); + }; + /** + * Sets the Azure CLI to dynamically install extensions that are missing. In this case, we care about the + * Azure Container Apps module being dynamically installed while it's still in preview. + */ + Utility.prototype.setAzureCliDynamicInstall = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, cpExec("az config set extension.use_dynamic_install=yes_without_prompt")]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Checks whether or not the provided string is null, undefined or empty. + * @param str - the string to validate + * @returns true if the string is null, undefined or empty, false otherwise + */ + Utility.prototype.isNullOrEmpty = function (str) { + return str === null || str === undefined || str === ""; + }; + return Utility; +}()); +exports.Utility = Utility; + + +/***/ }), + +/***/ 9491: +/***/ ((module) => { + +"use strict"; +module.exports = require("assert"); + +/***/ }), + +/***/ 2081: +/***/ ((module) => { + +"use strict"; +module.exports = require("child_process"); + +/***/ }), + +/***/ 6113: +/***/ ((module) => { + +"use strict"; +module.exports = require("crypto"); + +/***/ }), + +/***/ 2361: +/***/ ((module) => { + +"use strict"; +module.exports = require("events"); + +/***/ }), + +/***/ 7147: +/***/ ((module) => { + +"use strict"; +module.exports = require("fs"); + +/***/ }), + +/***/ 3685: +/***/ ((module) => { + +"use strict"; +module.exports = require("http"); + +/***/ }), + +/***/ 5687: +/***/ ((module) => { + +"use strict"; +module.exports = require("https"); + +/***/ }), + +/***/ 1808: +/***/ ((module) => { + +"use strict"; +module.exports = require("net"); + +/***/ }), + +/***/ 2037: +/***/ ((module) => { + +"use strict"; +module.exports = require("os"); + +/***/ }), + +/***/ 1017: +/***/ ((module) => { + +"use strict"; +module.exports = require("path"); + +/***/ }), + +/***/ 1576: +/***/ ((module) => { + +"use strict"; +module.exports = require("string_decoder"); + +/***/ }), + +/***/ 9512: +/***/ ((module) => { + +"use strict"; +module.exports = require("timers"); + +/***/ }), + +/***/ 4404: +/***/ ((module) => { + +"use strict"; +module.exports = require("tls"); + +/***/ }), + +/***/ 3837: +/***/ ((module) => { + +"use strict"; +module.exports = require("util"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nccwpck_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; +/******/ } +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module is referenced by other modules so it can't be inlined +/******/ var __webpack_exports__ = __nccwpck_require__(3238); +/******/ module.exports = __webpack_exports__; +/******/ +/******/ })() +; \ No newline at end of file diff --git a/dist/licenses.txt b/dist/licenses.txt new file mode 100644 index 00000000..d7718469 --- /dev/null +++ b/dist/licenses.txt @@ -0,0 +1,97 @@ +@actions/core +MIT +The MIT License (MIT) + +Copyright 2019 GitHub + +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. + +@actions/exec +MIT +The MIT License (MIT) + +Copyright 2019 GitHub + +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. + +@actions/http-client +MIT +Actions Http Client for Node.js + +Copyright (c) GitHub, Inc. + +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. + + +@actions/io +MIT +The MIT License (MIT) + +Copyright 2019 GitHub + +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. + +tunnel +MIT +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. + + +uuid +MIT +The MIT License (MIT) + +Copyright (c) 2010-2020 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. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..32e76251 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,491 @@ +{ + "name": "container-apps-deploy-action", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "container-apps-deploy-action", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/exec": "^1.1.1", + "@actions/github": "^5.1.1", + "typescript": "^5.2.2" + }, + "devDependencies": { + "@types/node": "^20.6.0" + } + }, + "node_modules/@actions/core": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", + "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", + "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@types/node": { + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==", + "dev": true + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "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/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==" + } + }, + "dependencies": { + "@actions/core": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", + "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", + "requires": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "requires": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "@actions/http-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", + "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "@types/node": { + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==", + "dev": true + }, + "before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "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==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "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==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..a80a3598 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "container-apps-deploy-action", + "version": "1.0.0", + "description": "This action allows users to easily deploy their application source to an\r [Azure Container App](https://azure.microsoft.com/en-us/services/container-apps/) in their GitHub workflow by either\r providing a previously built image, a Dockerfile that an image can be built from, or using a builder to create a\r runnable application image for the user.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Azure/container-apps-deploy-action.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Azure/container-apps-deploy-action/issues" + }, + "homepage": "https://github.com/Azure/container-apps-deploy-action#readme", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/exec": "^1.1.1", + "@actions/github": "^5.1.1", + "typescript": "^5.2.2" + }, + "devDependencies": { + "@types/node": "^20.6.0" + } +} diff --git a/src/CommandHelper.ts b/src/CommandHelper.ts new file mode 100644 index 00000000..5b23649e --- /dev/null +++ b/src/CommandHelper.ts @@ -0,0 +1,89 @@ +import * as os from 'os' +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; + +export class CommandHelper { + + /** + * Runs a command based on the OS of the agent running this task. + * @param command - the command to execute + * @returns the string output from the command + */ + public async execCommandAsync(command: string): Promise { + return os.platform() == 'win32' ? + await this.execPwshCommandAsync(command) : + await this.execBashCommandAsync(command); + } + + /** + * @param command - the command to execute in Bash + * @returns the string output from the command + */ + private async execBashCommandAsync(command: string): Promise { + var bashOutput: string = ''; + var errorStream: string = ''; + + const options: any = { + listeners: { + stdout: (data: Buffer) => { + bashOutput += data.toString(); + core.info(data.toString()); + }, + stderr: (data: Buffer) => { + errorStream += data.toString(); + core.error(data.toString()); + } + }, + failOnStdErr: true, + ignoreReturnCode: false, + errStream: process.stderr, + outStream: process.stdout, + } + try { + var exitCode = await exec.exec('bash', ['-c', command], options); + if (exitCode !== 0) { + throw new Error(`Command failed with exit code ${exitCode}. Error stream: ${errorStream}`); + } + return bashOutput.trim(); + } catch (err) { + core.setFailed(err.message); + throw err; + } + } + + /** + * Executes a given command using the pwsh executable. + * @param command - the command to execute in PowerShell + * @returns the string output from the command + */ + private async execPwshCommandAsync(command: string): Promise { + var pwshOutput: string = ''; + var errorStream: string = ''; + const options: any = { + listeners: { + stdout: (data: Buffer) => { + pwshOutput += data.toString(); + core.info(data.toString()); + }, + stderr: (data: Buffer) => { + errorStream += data.toString(); + core.error(data.toString()); + } + }, + failOnStdErr: true, + ignoreReturnCode: false, + errStream: process.stderr, + outStream: process.stdout, + } + try { + var exitCode = await exec.exec('pwsh', [command], options); + if (exitCode !== 0) { + throw new Error(`Command failed with exit code ${exitCode}. Error stream: ${errorStream}`); + } + return pwshOutput.trim(); + } catch (err) { + core.setFailed(err.message); + throw err; + } + } +} \ No newline at end of file diff --git a/src/ContainerAppHelper.ts b/src/ContainerAppHelper.ts new file mode 100644 index 00000000..4a456b48 --- /dev/null +++ b/src/ContainerAppHelper.ts @@ -0,0 +1,439 @@ +import * as core from '@actions/core'; +import * as io from '@actions/io'; +import * as path from 'path'; +import * as os from 'os'; +import { CommandHelper } from './CommandHelper'; +import { Utility } from './Utility'; +import util = require('util'); +const cpExec = util.promisify(require('child_process').exec); + +const ORYX_CLI_IMAGE: string = 'mcr.microsoft.com/oryx/cli:builder-debian-buster-20230208.1'; +const ORYX_BUILDER_IMAGE: string = 'mcr.microsoft.com/oryx/builder:20230208.1'; +const IS_WINDOWS_AGENT: boolean = os.platform() == 'win32'; +const PACK_CMD: string = IS_WINDOWS_AGENT ? path.join(os.tmpdir(), 'pack') : 'pack'; + +export class ContainerAppHelper { + readonly disableTelemetry: boolean = false; + + constructor(disableTelemetry: boolean) { + this.disableTelemetry = disableTelemetry; + } + + /** + * Creates an Azure Container App based from an image that was previously built. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param environment - the Container App Environment that will be associated with the Container App + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + */ + public async createContainerApp( + containerAppName: string, + resourceGroup: string, + environment: string, + imageToDeploy: string, + optionalCmdArgs: string[]) { + core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + try { + let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy} --environment ${environment}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Creates an Azure Container App based from a YAML configuration file. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param yamlConfigPath - the path to the YAML configuration file that the Container App properties will be based from + */ + public async createContainerAppFromYaml( + containerAppName: string, + resourceGroup: string, + yamlConfigPath: string) { + core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); + try { + let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Updates an existing Azure Container App based from an image that was previously built. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + */ + public async updateContainerApp( + containerAppName: string, + resourceGroup: string, + imageToDeploy: string, + optionalCmdArgs: string[]) { + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + try { + let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Updates an existing Azure Container App using the 'az containerapp up' command. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param imageToDeploy - the name of the runnable application image that the Container App will be based from + * @param optionalCmdArgs - a set of optional command line arguments + * @param ingress - the ingress that the Container App will be exposed on + * @param targetPort - the target port that the Container App will be exposed on + */ + public async updateContainerAppWithUp( + containerAppName: string, + resourceGroup: string, + imageToDeploy: string, + optionalCmdArgs: string[], + ingress?: string, + targetPort?: string) { + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + const util = new Utility(); + try { + let command = `az containerapp up -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + + if (!util.isNullOrEmpty(ingress)) { + command += ` --ingress ${ingress}`; + } + + if (!util.isNullOrEmpty(targetPort)) { + command += ` --target-port ${targetPort}`; + } + + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Updates an existing Azure Container App based from a YAML configuration file. + * @param containerAppName - the name of the existing Container App + * @param resourceGroup - the resource group that the existing Container App is found in + * @param yamlConfigPath - the path to the YAML configuration file that the Container App properties will be based from + */ + public async updateContainerAppFromYaml( + containerAppName: string, + resourceGroup: string, + yamlConfigPath: string) { + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); + try { + let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Determines if the provided Container App exists in the provided resource group. + * @param containerAppName - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @returns true if the Container App exists, false otherwise + */ + public async doesContainerAppExist(containerAppName: string, resourceGroup: string): Promise { + core.debug(`Attempting to determine if Container App with name "${containerAppName}" exists in resource group "${resourceGroup}"`); + try { + const command = `az containerapp show -n ${containerAppName} -g ${resourceGroup} -o none`; + const {stdout, stderr} = await cpExec(`${command}`); + return !stderr; + } catch (err) { + core.warning(err.message); + return false; + } + } + + /** + * Determines if the provided Container App Environment exists in the provided resource group. + * @param containerAppEnvironment - the name of the Container App Environment + * @param resourceGroup - the resource group that the Container App Environment is found in + * @returns true if the Container App Environment exists, false otherwise + */ + public async doesContainerAppEnvironmentExist(containerAppEnvironment: string, resourceGroup: string): Promise { + core.debug(`Attempting to determine if Container App Environment with name "${containerAppEnvironment}" exists in resource group "${resourceGroup}"`); + try { + const command = `az containerapp env show -n ${containerAppEnvironment} -g ${resourceGroup} -o none`; + const {stdout, stderr} = await cpExec(`${command}`); + return !stderr; + } catch (err) { + core.warning(err.message); + return false; + } + } + + /** + * Determines if the provided resource group exists. + * @param resourceGroup - the name of the resource group + * @returns true if the resource group exists, false otherwise + */ + public async doesResourceGroupExist(resourceGroup: string): Promise { + core.debug(`Attempting to determine if resource group "${resourceGroup}" exists`); + try { + const command = `az group show -n ${resourceGroup} -o none`; + const {stdout, stderr} = await cpExec(`${command}`); + return !stderr; + } catch (err) { + core.warning(err.message); + return false; + } + } + + /** + * Gets the default location for the Container App provider. + * @returns the default location if found, otherwise 'eastus2' + */ + public async getDefaultContainerAppLocation(): Promise { + core.debug(`Attempting to get the default location for the Container App service for the subscription.`); + try { + const command = `az provider show -n Microsoft.App --query "resourceTypes[?resourceType=='containerApps'].locations[] | [0]"` + const {stdout, stderr} = await cpExec(`${command}`); + // If successful, strip out double quotes, spaces and parentheses from the first location returned + return !stderr ? stdout.toLowerCase().replace(/["() ]/g, "") : `eastus2`; + } catch (err) { + core.warning(err.message); + return `eastus2`; + } + } + + /** + * Creates a new resource group in the provided location. + * @param name - the name of the resource group to create + * @param location - the location to create the resource group in + */ + public async createResourceGroup(name: string, location: string) { + core.debug(`Attempting to create resource group "${name}" in location "${location}"`); + try { + const command = `az group create -n ${name} -l ${location}`; + await cpExec(`${command}`); + + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Gets the name of an existing Container App Environment in the provided resource group. + * @param resourceGroup - the resource group to check for an existing Container App Environment + * @returns the name of the existing Container App Environment, null if none exists + */ + public async getExistingContainerAppEnvironment(resourceGroup: string) { + core.debug(`Attempting to get the existing Container App Environment in resource group "${resourceGroup}"`); + try { + const command = `az containerapp env list -g ${resourceGroup} --query [0].name"`; + const {stdout, stderr} = await cpExec(`${command}`); + return !stderr ? stdout : null; + } catch (err) { + core.warning(err.message); + return null; + } + } + + /** + * Creates a new Azure Container App Environment in the provided resource group. + * @param name - the name of the Container App Environment + * @param resourceGroup - the resource group that the Container App Environment will be created in + * @param location - the location that the Container App Environment will be created in + */ + public async createContainerAppEnvironment(name: string, resourceGroup: string, location?: string) { + const util = new Utility(); + core.debug(`Attempting to create Container App Environment with name "${name}" in resource group "${resourceGroup}"`); + try { + let command = `az containerapp env create -n ${name} -g ${resourceGroup}`; + if (!util.isNullOrEmpty(location)) { + command += ` -l ${location}`; + } + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Disables ingress on an existing Container App. + * @param name - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + */ + public async disableContainerAppIngress(name: string, resourceGroup: string) { + core.debug(`Attempting to disable ingress for Container App with name "${name}" in resource group "${resourceGroup}"`); + try { + const command = `az containerapp ingress disable -n ${name} -g ${resourceGroup}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Updates the ACR details on an existing Container App. + * @param name - the name of the Container App + * @param resourceGroup - the resource group that the Container App is found in + * @param acrName - the name of the Azure Container Registry (without the .azurecr.io suffix) + * @param acrUsername - the username used to authenticate with the Azure Container Registry + * @param acrPassword - the password used to authenticate with the Azure Container Registry + */ + public async updateContainerAppRegistryDetails(name: string, resourceGroup: string, acrName: string, acrUsername: string, acrPassword: string) { + core.debug(`Attempting to set the ACR details for Container App with name "${name}" in resource group "${resourceGroup}"`); + try { + const command = `az containerapp registry set -n ${name} -g ${resourceGroup} --server ${acrName}.azurecr.io --username ${acrUsername} --password ${acrPassword}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Using the Oryx++ Builder, creates a runnable application image from the provided application source. + * @param imageToDeploy - the name of the runnable application image that is created and can be later deployed + * @param appSourcePath - the path to the application source on the machine + * @param runtimeStack - the runtime stack to use in the image layer that runs the application + */ + public async createRunnableAppImage( + imageToDeploy: string, + appSourcePath: string, + runtimeStack: string) { + core.debug(`Attempting to create a runnable application image using the Oryx++ Builder with image name "${imageToDeploy}"`); + try { + let telemetryArg = `--env "CALLER_ID=azure-pipelines-rc-v1"`; + if (this.disableTelemetry) { + telemetryArg = `--env "ORYX_DISABLE_TELEMETRY=true"`; + } + await cpExec(`${PACK_CMD} build ${imageToDeploy} --path ${appSourcePath} --builder ${ORYX_BUILDER_IMAGE} --run-image mcr.microsoft.com/oryx/${runtimeStack} ${telemetryArg}`); + } catch (err) { + core.error(err.message); + throw err; + } + } + + /** + * Using a Dockerfile that was provided or found at the root of the application source, + * creates a runable application image. + * @param imageToDeploy - the name of the runnable application image that is created and can be later deployed + * @param appSourcePath - the path to the application source on the machine + * @param dockerfilePath - the path to the Dockerfile to build and tag with the provided image name + */ + public async createRunnableAppImageFromDockerfile( + imageToDeploy: string, + appSourcePath: string, + dockerfilePath: string) { + core.debug(`Attempting to create a runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); + try { + const dockerTool = await io.which("docker", true); + await new Utility().executeAndthrowIfError(dockerTool, ['build', '--file', `${dockerfilePath}`, `${appSourcePath}`, '--tag', `${imageToDeploy}`]); + core.debug(`Successfully created runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); + } catch (err) { + core.setFailed(err.message); + throw err; + } + } + + /** + * Determines the runtime stack to use for the runnable application image. + * @param appSourcePath - the path to the application source on the machine + * @returns a string representing the runtime stack that can be used for the Oryx MCR runtime images + */ + public async determineRuntimeStackAsync(appSourcePath: string): Promise { + core.debug('Attempting to determine the runtime stack needed for the provided application source'); + try { + const dockerTool: string = await io.which("docker", true); + // Use 'oryx dockerfile' command to determine the runtime stack to use and write it to a temp file + await new Utility().executeAndthrowIfError(dockerTool, ['run', '--rm', '-v', `${appSourcePath}:/app`, `${ORYX_CLI_IMAGE}`, '/bin/bash', '-c', `oryx dockerfile /app | head -n 1 | sed 's/ARG RUNTIME=//' >> /app/oryx-runtime.txt`]) + + // Read the temp file to get the runtime stack into a variable + const oryxRuntimeTxtPath = path.join(appSourcePath, 'oryx-runtime.txt'); + let command: string = `head -n 1 ${oryxRuntimeTxtPath}`; + if (IS_WINDOWS_AGENT) { + command = `Get-Content -Path ${oryxRuntimeTxtPath} -Head 1`; + } + + const runtimeStack = await new CommandHelper().execCommandAsync(command); + + // Delete the temp file + command = `rm ${oryxRuntimeTxtPath}`; + if (IS_WINDOWS_AGENT) { + command = `Remove-Item -Path ${oryxRuntimeTxtPath}`; + } + + await new CommandHelper().execCommandAsync(command); + + return runtimeStack; + } catch (err) { + core.setFailed(err.message); + throw err; + } + } + + /** + * Sets the default builder on the machine to the Oryx++ Builder to prevent an exception from being thrown due + * to no default builder set. + */ + public async setDefaultBuilder() { + core.info('Setting the Oryx++ Builder as the default builder via the pack CLI'); + try + { + await cpExec(`pack config default-builder ${ORYX_BUILDER_IMAGE}`); + } + catch (err) { + core.setFailed(err.message); + throw err; + } + } + + /** + * Installs the pack CLI that will be used to build a runnable application image. + * For more information about the pack CLI can be found here: https://buildpacks.io/docs/tools/pack/ + */ + public async installPackCliAsync() { + core.debug('Attempting to install the pack CLI'); + try { + let command: string = ''; + if (IS_WINDOWS_AGENT) { + const packZipDownloadUri: string = 'https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-windows.zip'; + const packZipDownloadFilePath: string = path.join(PACK_CMD, 'pack-windows.zip'); + + command = `New-Item -ItemType Directory -Path ${PACK_CMD} -Force | Out-Null;` + + `Invoke-WebRequest -Uri ${packZipDownloadUri} -OutFile ${packZipDownloadFilePath}; ` + + `Expand-Archive -LiteralPath ${packZipDownloadFilePath} -DestinationPath ${PACK_CMD}; ` + + `Remove-Item -Path ${packZipDownloadFilePath}`; + } else { + const tgzSuffix = os.platform() == 'darwin' ? 'macos' : 'linux'; + command = `(curl -sSL \"https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-${tgzSuffix}.tgz\" | ` + + 'tar -C /usr/local/bin/ --no-same-owner -xzv pack)'; + } + await new CommandHelper().execCommandAsync(command); + } catch (err) { + core.error(`Unable to install the pack CLI. Error: ${err.message}`); + core.setFailed(err.message); + throw err; + } + } +} \ No newline at end of file diff --git a/src/ContainerRegistryHelper.ts b/src/ContainerRegistryHelper.ts new file mode 100644 index 00000000..367b2dfe --- /dev/null +++ b/src/ContainerRegistryHelper.ts @@ -0,0 +1,55 @@ +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; +import * as io from '@actions/io'; +import { CommandHelper } from './CommandHelper'; +import { Utility } from './Utility'; + +export class ContainerRegistryHelper { + /** + * Authorizes Docker to make calls to the provided ACR instance using username and password. + * @param acrName - the name of the ACR instance to authenticate calls to + * @param acrUsername - the username for authentication + * @param acrPassword - the password for authentication + */ + public async loginAcrWithUsernamePassword(acrName: string, acrUsername: string, acrPassword: string) { + core.debug(`Attempting to log in to ACR instance "${acrName}" with username and password credentials`); + try { + await exec.exec('docker', [`login`, `--password-stdin`, `--username`, `${acrUsername}`, `${acrName}.azurecr.io`], { input: Buffer.from(acrPassword) }); + } catch (err) { + core.error(`Failed to log in to ACR instance "${acrName}" with username and password credentials`); + throw err; + } + } + + /** + * Authorizes Docker to make calls to the provided ACR instance using an access token that is generated via + * the 'az acr login --expose-token' command. + * @param acrName - the name of the ACR instance to authenticate calls to. + */ + public async loginAcrWithAccessTokenAsync(acrName: string) { + core.debug(`Attempting to log in to ACR instance "${acrName}" with access token`); + try { + const command: string = `CA_ADO_TASK_ACR_ACCESS_TOKEN=$(az acr login --name ${acrName} --output json --expose-token --only-show-errors | jq -r '.accessToken'); docker login ${acrName}.azurecr.io -u 00000000-0000-0000-0000-000000000000 -p $CA_ADO_TASK_ACR_ACCESS_TOKEN > /dev/null 2>&1`; + await new CommandHelper().execCommandAsync(command); + } catch (err) { + core.error(`Failed to log in to ACR instance "${acrName}" with access token`) + throw err; + } + } + + /** + * Pushes an image to the ACR instance that was previously authenticated against. + * @param imageToPush - the name of the image to push to ACR + */ + public async pushImageToAcr(imageToPush: string) { + core.debug(`Attempting to push image "${imageToPush}" to ACR`); + try { + const dockerTool: string = await io.which("docker", true); + await new Utility().executeAndthrowIfError(dockerTool, [`push`, `${imageToPush}`]); + } catch (err) { + core.error(`Failed to push image "${imageToPush}" to ACR. Error: ${err.message}`); + core.setFailed(err.message); + throw err; + } + } +} \ No newline at end of file diff --git a/src/TelemetryHelper.ts b/src/TelemetryHelper.ts new file mode 100644 index 00000000..e9dd9600 --- /dev/null +++ b/src/TelemetryHelper.ts @@ -0,0 +1,108 @@ +import * as core from '@actions/core'; +import { Utility } from './Utility'; +import * as io from '@actions/io'; + +const ORYX_CLI_IMAGE: string = "mcr.microsoft.com/oryx/cli:debian-buster-20230207.2"; + +const SUCCESSFUL_RESULT: string = "succeeded"; +const FAILED_RESULT: string = "failed"; + +const BUILDER_SCENARIO: string = "used-builder"; +const DOCKERFILE_SCENARIO: string = "used-dockerfile"; +const IMAGE_SCENARIO: string = "used-image"; + +const util = new Utility(); + +export class TelemetryHelper { + readonly disableTelemetry: boolean; + + private scenario: string; + private result: string; + private errorMessage: string; + private taskStartMilliseconds: number; + + constructor(disableTelemetry: boolean) { + this.disableTelemetry = disableTelemetry; + this.taskStartMilliseconds = Date.now(); + } + + /** + * Marks that the task was successful in telemetry. + */ + public setSuccessfulResult() { + this.result = SUCCESSFUL_RESULT; + } + + /** + * Marks that the task failed in telemetry. + */ + public setFailedResult(errorMessage: string) { + this.result = FAILED_RESULT; + this.errorMessage = errorMessage; + } + + /** + * Marks that the task used the builder scenario. + */ + public setBuilderScenario() { + this.scenario = BUILDER_SCENARIO; + } + + /** + * Marks that the task used the Dockerfile scenario. + */ + public setDockerfileScenario() { + this.scenario = DOCKERFILE_SCENARIO; + } + + /** + * Marks that the task used the previously built image scenario. + */ + public setImageScenario() { + this.scenario = IMAGE_SCENARIO; + } + + /** + * If telemetry is enabled, uses the "oryx telemetry" command to log metadata about this task execution. + */ + public async sendLogs() { + const taskLengthMilliseconds: number = Date.now() - this.taskStartMilliseconds; + if (!this.disableTelemetry) { + core.info(`Telemetry enabled; logging metadata about task result, length and scenario targeted.`); + try { + let resultArg: string = ''; + if (!util.isNullOrEmpty(this.result)) { + resultArg = `--property 'result=${this.result}'`; + } + + let scenarioArg: string = ''; + if (!util.isNullOrEmpty(this.scenario)) { + scenarioArg = `--property 'scenario=${this.scenario}'`; + } + + let errorMessageArg: string = ''; + if (!util.isNullOrEmpty(this.errorMessage)) { + errorMessageArg = `--property 'errorMessage=${this.errorMessage}'`; + } + + let args: string[] = [`run`, `--rm`, `${ORYX_CLI_IMAGE}`, `/bin/bash`, `-c`, `oryx telemetry --event-name 'ContainerAppsPipelinesTaskRCV1' ` + `--processing-time '${taskLengthMilliseconds}' ${resultArg} ${scenarioArg} ${errorMessageArg}"`]; + + // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task + await executeDockerCommand(args, true) + } catch (err) { + core.warning(`Skipping telemetry logging due to the following exception: ${err.message}`); + } + } + } +} + +const executeDockerCommand = async (args: string[], continueOnError: boolean = false): Promise => { + try { + const dockerTool: string = await io.which("docker", true); + await new Utility().executeAndthrowIfError(dockerTool, args, continueOnError); + } + catch (err) { + core.setFailed(`Error: ${err.message}`); + throw err; // Re-throw the error + } +} \ No newline at end of file diff --git a/src/Utility.ts b/src/Utility.ts new file mode 100644 index 00000000..9f9fa0eb --- /dev/null +++ b/src/Utility.ts @@ -0,0 +1,58 @@ +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; +import util = require('util'); +const cpExec = util.promisify(require('child_process').exec); +export class Utility { + /** + * @param commandLine - the command to execute + * @param args - the arguments to pass to the command + * @param continueOnError - whether or not to continue execution if the command fails + */ + + public async executeAndthrowIfError(commandLine: string, args: string[], continueOnError: boolean = false): Promise { + try { + let stdout = ''; + let stderr = ''; + + const options: exec.ExecOptions = { + listeners: { + stdout: (data: Buffer) => { + stdout += data.toString(); + core.info(data.toString()); + }, + stderr: (data: Buffer) => { + stderr += data.toString(); + core.error(data.toString()); + }, + }, + }; + + const exitCode = await exec.exec(commandLine, args, options); + + if (!continueOnError && exitCode !== 0) { + core.error(`Command failed with exit code ${exitCode}. Error stream: ${stderr}`); + throw new Error(`Command failed with exit code ${exitCode}. Error stream: ${stderr}`); + } + } catch (error) { + core.setFailed(`Error: ${error.message}`); + throw error; // Re-throw the error + } + } + + /** + * Sets the Azure CLI to dynamically install extensions that are missing. In this case, we care about the + * Azure Container Apps module being dynamically installed while it's still in preview. + */ + public async setAzureCliDynamicInstall() { + await cpExec(`az config set extension.use_dynamic_install=yes_without_prompt`); + } + + /** + * Checks whether or not the provided string is null, undefined or empty. + * @param str - the string to validate + * @returns true if the string is null, undefined or empty, false otherwise + */ + public isNullOrEmpty(str: string): boolean { + return str === null || str === undefined || str === ""; + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..27c1c391 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2016", + "module": "CommonJS", + "moduleResolution": "node", + "lib": ["es2018", "dom"], // Add any other required libraries + // ... + } +} From 7d220c530f7eca7e57b188c5d14326d724aeefe9 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 19 Sep 2023 16:55:15 -0700 Subject: [PATCH 2/6] Add step to delete container app env back --- .github/workflows/run-validation.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/run-validation.yml b/.github/workflows/run-validation.yml index 9495df46..42e26ffb 100644 --- a/.github/workflows/run-validation.yml +++ b/.github/workflows/run-validation.yml @@ -271,6 +271,11 @@ jobs: WORKSPACE_NAME=$(az monitor log-analytics workspace list -g ${{ vars.TEST_RESOURCE_GROUP_NAME }} --query '[?customerId == `${{ env.CUSTOMER_ID }}`].name | [0]') echo "WORKSPACE_NAME=${WORKSPACE_NAME}" >> $GITHUB_ENV + - name: Delete created Azure Container App environment + if: ${{ always() }} + shell: bash + run: az containerapp env delete -g ${{ vars.TEST_RESOURCE_GROUP_NAME }} -n ${{ env.TEST_NEW_CONTAINER_APP_ENV }} -y + - name: Delete created workspace if: ${{ always() }} shell: bash From 1c150f0f1a869a2bd4c26d5a2a196beef41927b6 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 19 Sep 2023 16:56:10 -0700 Subject: [PATCH 3/6] Remove workflow_dispatch --- .github/workflows/run-validation.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/run-validation.yml b/.github/workflows/run-validation.yml index 42e26ffb..efb5b534 100644 --- a/.github/workflows/run-validation.yml +++ b/.github/workflows/run-validation.yml @@ -1,7 +1,6 @@ name: Run validation on action on: - workflow_dispatch: push: branches: - main From 16a977bde70459017605a4d1379e627bbab61992 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 19 Sep 2023 17:12:24 -0700 Subject: [PATCH 4/6] Remove commented out code --- azurecontainerapps.ts | 3 --- dist/index.js | 6 ------ src/TelemetryHelper.ts | 1 - tsconfig.json | 3 +-- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/azurecontainerapps.ts b/azurecontainerapps.ts index ba92c6eb..6ddd9dea 100644 --- a/azurecontainerapps.ts +++ b/azurecontainerapps.ts @@ -53,9 +53,6 @@ export class azurecontainerapps { core.setFailed(err.message); this.telemetryHelper.setFailedResult(err.message); } finally { - // Logout of Azure if logged in during this task session - // this.authHelper.logoutAzure(); - // If telemetry is enabled, will log metadata for this task run await this.telemetryHelper.sendLogs(); } diff --git a/dist/index.js b/dist/index.js index 6c495437..8466529d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -109,13 +109,9 @@ var azurecontainerapps = /** @class */ (function () { this.telemetryHelper.setFailedResult(err_1.message); return [3 /*break*/, 11]; case 9: - // Logout of Azure if logged in during this task session - // this.authHelper.logoutAzure(); // If telemetry is enabled, will log metadata for this task run return [4 /*yield*/, this.telemetryHelper.sendLogs()]; case 10: - // Logout of Azure if logged in during this task session - // this.authHelper.logoutAzure(); // If telemetry is enabled, will log metadata for this task run _a.sent(); return [7 /*endfinally*/]; @@ -5729,10 +5725,8 @@ var TelemetryHelper = /** @class */ (function () { errorMessageArg = "--property 'errorMessage=" + this.errorMessage + "'"; } args = ["run", "--rm", "" + ORYX_CLI_IMAGE, "/bin/bash", "-c", "oryx telemetry --event-name 'ContainerAppsPipelinesTaskRCV1' " + ("--processing-time '" + taskLengthMilliseconds + "' " + resultArg + " " + scenarioArg + " " + errorMessageArg + "\"")]; - // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task return [4 /*yield*/, executeDockerCommand(args, true)]; case 2: - // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task _a.sent(); return [3 /*break*/, 4]; case 3: diff --git a/src/TelemetryHelper.ts b/src/TelemetryHelper.ts index e9dd9600..139b5086 100644 --- a/src/TelemetryHelper.ts +++ b/src/TelemetryHelper.ts @@ -87,7 +87,6 @@ export class TelemetryHelper { let args: string[] = [`run`, `--rm`, `${ORYX_CLI_IMAGE}`, `/bin/bash`, `-c`, `oryx telemetry --event-name 'ContainerAppsPipelinesTaskRCV1' ` + `--processing-time '${taskLengthMilliseconds}' ${resultArg} ${scenarioArg} ${errorMessageArg}"`]; - // Don't use Utility's throwIfError() since it will still record an error in the pipeline logs, but won't fail the task await executeDockerCommand(args, true) } catch (err) { core.warning(`Skipping telemetry logging due to the following exception: ${err.message}`); diff --git a/tsconfig.json b/tsconfig.json index 27c1c391..deab28d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,6 @@ "target": "ES2016", "module": "CommonJS", "moduleResolution": "node", - "lib": ["es2018", "dom"], // Add any other required libraries - // ... + "lib": ["es2018", "dom"], } } From d7cb5903dd03e5e38b41040e3c85011936564d90 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 19 Sep 2023 17:25:05 -0700 Subject: [PATCH 5/6] Fix formating --- azurecontainerapps.ts | 40 +++---- src/ContainerAppHelper.ts | 185 ++++++++++++++++----------------- src/ContainerRegistryHelper.ts | 8 +- 3 files changed, 116 insertions(+), 117 deletions(-) diff --git a/azurecontainerapps.ts b/azurecontainerapps.ts index 6ddd9dea..f80a61d0 100644 --- a/azurecontainerapps.ts +++ b/azurecontainerapps.ts @@ -105,7 +105,7 @@ export class azurecontainerapps { this.telemetryHelper = new TelemetryHelper(disableTelemetry); // Set up AzureAuthenticationHelper for managing logging in and out of Azure CLI using provided service connection - // this.authHelper = new AzureAuthenticationHelper(); + // this.authHelper = new AzureAuthenticationHelper(); // Set up ContainerAppHelper for managing calls around the Container App this.appHelper = new ContainerAppHelper(disableTelemetry); @@ -121,16 +121,16 @@ export class azurecontainerapps { private static validateSupportedScenarioArguments() { // Get the path to the application source to build and run, if provided - this.appSourcePath = core.getInput('appSourcePath', {required: false}); + this.appSourcePath = core.getInput('appSourcePath', { required: false }); // Get the name of the ACR instance to push images to, if provided - this.acrName = core.getInput('acrName', {required: false}); + this.acrName = core.getInput('acrName', { required: false }); // Get the previously built image to deploy, if provided - this.imageToDeploy = core.getInput('imageToDeploy', {required: false}); + this.imageToDeploy = core.getInput('imageToDeploy', { required: false }); // Get the YAML configuration file, if provided - this.yamlConfigPath = core.getInput('yamlConfigPath', {required: false}); + this.yamlConfigPath = core.getInput('yamlConfigPath', { required: false }); // Ensure that acrName is also provided if appSourcePath is provided if (!util.isNullOrEmpty(this.appSourcePath) && util.isNullOrEmpty(this.acrName)) { @@ -185,7 +185,7 @@ export class azurecontainerapps { * @returns The name of the Container App to use for the task. */ private static getContainerAppName(): string { - let containerAppName: string = core.getInput('containerAppName', {required: false}); + let containerAppName: string = core.getInput('containerAppName', { required: false }); if (util.isNullOrEmpty(containerAppName)) { containerAppName = `app-${this.buildId}-${this.buildNumber}`; @@ -204,7 +204,7 @@ export class azurecontainerapps { */ private static async getLocation(): Promise { // Set deployment location, if provided - let location: string = core.getInput('location', {required: false}); + let location: string = core.getInput('location', { required: false }); // If no location was provided, use the default location for the Container App service if (util.isNullOrEmpty(location)) { @@ -224,7 +224,7 @@ export class azurecontainerapps { */ private static async getOrCreateResourceGroup(containerAppName: string, location: string): Promise { // Get the resource group to deploy to if it was provided, or generate it from the Container App name - let resourceGroup: string = core.getInput('resourceGroup', {required: false}); + let resourceGroup: string = core.getInput('resourceGroup', { required: false }); if (util.isNullOrEmpty(resourceGroup)) { resourceGroup = `${containerAppName}-rg`; core.info(`Default resource group name: ${resourceGroup}`); @@ -254,7 +254,7 @@ export class azurecontainerapps { resourceGroup: string, location: string): Promise { // Get the Container App environment if it was provided - let containerAppEnvironment: string = core.getInput('containerAppEnvironment', {required: false}); + let containerAppEnvironment: string = core.getInput('containerAppEnvironment', { required: false }); // See if we can reuse an existing Container App environment found in the resource group if (util.isNullOrEmpty(containerAppEnvironment)) { @@ -284,8 +284,8 @@ export class azurecontainerapps { * Authenticates calls to the provided Azure Container Registry. */ private static async authenticateAzureContainerRegistryAsync() { - this.acrUsername = core.getInput('acrUsername', {required: false}); - this.acrPassword = core.getInput('acrPassword', {required: false}); + this.acrUsername = core.getInput('acrUsername', { required: false }); + this.acrPassword = core.getInput('acrPassword', { required: false }); // Login to ACR if credentials were provided if (!util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword)) { @@ -310,7 +310,7 @@ export class azurecontainerapps { */ private static async buildAndPushImageAsync() { // Get the name of the image to build if it was provided, or generate it from build variables - this.imageToBuild = core.getInput('imageToBuild', {required: false}); + this.imageToBuild = core.getInput('imageToBuild', { required: false }); if (util.isNullOrEmpty(this.imageToBuild)) { this.imageToBuild = `${this.acrName}.azurecr.io/ado-task/container-app:${this.buildId}.${this.buildNumber}`; core.info(`Default image to build: ${this.imageToBuild}`); @@ -323,7 +323,7 @@ export class azurecontainerapps { } // Get Dockerfile to build, if provided, or check if one exists at the root of the provided application - let dockerfilePath: string = core.getInput('dockerfilePath', {required: false}); + let dockerfilePath: string = core.getInput('dockerfilePath', { required: false }); if (util.isNullOrEmpty(dockerfilePath)) { core.info(`No Dockerfile path provided; checking for Dockerfile at root of application source.`); const rootDockerfilePath = path.join(this.appSourcePath, 'Dockerfile'); @@ -358,7 +358,7 @@ export class azurecontainerapps { core.info(`Successfully installed the pack CLI.`) // Get the runtime stack if provided, or determine it using Oryx - this.runtimeStack = core.getInput('runtimeStack', {required: false}); + this.runtimeStack = core.getInput('runtimeStack', { required: false }); if (util.isNullOrEmpty(this.runtimeStack)) { this.runtimeStack = await this.appHelper.determineRuntimeStackAsync(appSourcePath); core.info(`Runtime stack determined to be: ${this.runtimeStack}`); @@ -398,14 +398,14 @@ export class azurecontainerapps { this.commandLineArgs = []; // Get the ingress inputs - this.ingress = core.getInput('ingress', {required: false}); - this.targetPort = core.getInput('targetPort', {required: false}); + this.ingress = core.getInput('ingress', { required: false }); + this.targetPort = core.getInput('targetPort', { required: false }); // If both ingress and target port were not provided for an existing Container App, or if ingress is to be disabled, // use the 'update' command, otherwise we should use the 'up' command that performs a PATCH operation on the ingress properties. this.shouldUseUpdateCommand = this.containerAppExists && - util.isNullOrEmpty(this.targetPort) && - (util.isNullOrEmpty(this.ingress) || this.ingress == 'disabled'); + util.isNullOrEmpty(this.targetPort) && + (util.isNullOrEmpty(this.ingress) || this.ingress == 'disabled'); // Pass the ACR credentials when creating a Container App or updating a Container App via the 'up' command if (!util.isNullOrEmpty(this.acrName) && !util.isNullOrEmpty(this.acrUsername) && !util.isNullOrEmpty(this.acrPassword) && @@ -435,7 +435,7 @@ export class azurecontainerapps { // Handle setup for ingress values when enabled if (this.ingressEnabled) { // Get the target port if provided, or determine it based on the application type - this.targetPort = core.getInput('targetPort', {required: false}); + this.targetPort = core.getInput('targetPort', { required: false }); if (util.isNullOrEmpty(this.targetPort)) { if (!util.isNullOrEmpty(this.runtimeStack) && this.runtimeStack.startsWith('python:')) { this.targetPort = '80'; @@ -459,7 +459,7 @@ export class azurecontainerapps { } } - const environmentVariables: string = core.getInput('environmentVariables', {required: false}); + const environmentVariables: string = core.getInput('environmentVariables', { required: false }); // Add user-specified environment variables if (!util.isNullOrEmpty(environmentVariables)) { diff --git a/src/ContainerAppHelper.ts b/src/ContainerAppHelper.ts index 4a456b48..dfbf653d 100644 --- a/src/ContainerAppHelper.ts +++ b/src/ContainerAppHelper.ts @@ -27,23 +27,23 @@ export class ContainerAppHelper { * @param imageToDeploy - the name of the runnable application image that the Container App will be based from * @param optionalCmdArgs - a set of optional command line arguments */ - public async createContainerApp( + public async createContainerApp( containerAppName: string, resourceGroup: string, environment: string, imageToDeploy: string, optionalCmdArgs: string[]) { - core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); - try { - let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy} --environment ${environment}`; - optionalCmdArgs.forEach(function (val: string) { - command += ` ${val}`; - }); - await cpExec(`${command}`); - } catch (err) { - core.error(err.message); - throw err; - } + core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + try { + let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy} --environment ${environment}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } } /** @@ -56,14 +56,14 @@ export class ContainerAppHelper { containerAppName: string, resourceGroup: string, yamlConfigPath: string) { - core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); - try { - let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; - await cpExec(`${command}`); - } catch (err) { - core.error(err.message); - throw err; - } + core.debug(`Attempting to create Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); + try { + let command = `az containerapp create -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } } /** @@ -78,18 +78,18 @@ export class ContainerAppHelper { resourceGroup: string, imageToDeploy: string, optionalCmdArgs: string[]) { - core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); - try { - let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; - optionalCmdArgs.forEach(function (val: string) { - command += ` ${val}`; - }); - - await cpExec(`${command}`); - } catch (err) { - core.error(err.message); - throw err; - } + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + try { + let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } } /** @@ -108,28 +108,28 @@ export class ContainerAppHelper { optionalCmdArgs: string[], ingress?: string, targetPort?: string) { - core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); - const util = new Utility(); - try { - let command = `az containerapp up -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; - optionalCmdArgs.forEach(function (val: string) { - command += ` ${val}`; - }); - - if (!util.isNullOrEmpty(ingress)) { - command += ` --ingress ${ingress}`; - } - - if (!util.isNullOrEmpty(targetPort)) { - command += ` --target-port ${targetPort}`; - } - - await cpExec(`${command}`); - } catch (err) { - core.error(err.message); - throw err; + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" based from image "${imageToDeploy}"`); + const util = new Utility(); + try { + let command = `az containerapp up -n ${containerAppName} -g ${resourceGroup} -i ${imageToDeploy}`; + optionalCmdArgs.forEach(function (val: string) { + command += ` ${val}`; + }); + + if (!util.isNullOrEmpty(ingress)) { + command += ` --ingress ${ingress}`; + } + + if (!util.isNullOrEmpty(targetPort)) { + command += ` --target-port ${targetPort}`; } + + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; } + } /** * Updates an existing Azure Container App based from a YAML configuration file. @@ -141,14 +141,14 @@ export class ContainerAppHelper { containerAppName: string, resourceGroup: string, yamlConfigPath: string) { - core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); - try { - let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; - await cpExec(`${command}`); - } catch (err) { - core.error(err.message); - throw err; - } + core.debug(`Attempting to update Container App with name "${containerAppName}" in resource group "${resourceGroup}" from provided YAML "${yamlConfigPath}"`); + try { + let command = `az containerapp update -n ${containerAppName} -g ${resourceGroup} --yaml ${yamlConfigPath}`; + await cpExec(`${command}`); + } catch (err) { + core.error(err.message); + throw err; + } } /** @@ -161,7 +161,7 @@ export class ContainerAppHelper { core.debug(`Attempting to determine if Container App with name "${containerAppName}" exists in resource group "${resourceGroup}"`); try { const command = `az containerapp show -n ${containerAppName} -g ${resourceGroup} -o none`; - const {stdout, stderr} = await cpExec(`${command}`); + const { stdout, stderr } = await cpExec(`${command}`); return !stderr; } catch (err) { core.warning(err.message); @@ -179,7 +179,7 @@ export class ContainerAppHelper { core.debug(`Attempting to determine if Container App Environment with name "${containerAppEnvironment}" exists in resource group "${resourceGroup}"`); try { const command = `az containerapp env show -n ${containerAppEnvironment} -g ${resourceGroup} -o none`; - const {stdout, stderr} = await cpExec(`${command}`); + const { stdout, stderr } = await cpExec(`${command}`); return !stderr; } catch (err) { core.warning(err.message); @@ -196,7 +196,7 @@ export class ContainerAppHelper { core.debug(`Attempting to determine if resource group "${resourceGroup}" exists`); try { const command = `az group show -n ${resourceGroup} -o none`; - const {stdout, stderr} = await cpExec(`${command}`); + const { stdout, stderr } = await cpExec(`${command}`); return !stderr; } catch (err) { core.warning(err.message); @@ -212,7 +212,7 @@ export class ContainerAppHelper { core.debug(`Attempting to get the default location for the Container App service for the subscription.`); try { const command = `az provider show -n Microsoft.App --query "resourceTypes[?resourceType=='containerApps'].locations[] | [0]"` - const {stdout, stderr} = await cpExec(`${command}`); + const { stdout, stderr } = await cpExec(`${command}`); // If successful, strip out double quotes, spaces and parentheses from the first location returned return !stderr ? stdout.toLowerCase().replace(/["() ]/g, "") : `eastus2`; } catch (err) { @@ -247,7 +247,7 @@ export class ContainerAppHelper { core.debug(`Attempting to get the existing Container App Environment in resource group "${resourceGroup}"`); try { const command = `az containerapp env list -g ${resourceGroup} --query [0].name"`; - const {stdout, stderr} = await cpExec(`${command}`); + const { stdout, stderr } = await cpExec(`${command}`); return !stderr ? stdout : null; } catch (err) { core.warning(err.message); @@ -321,17 +321,17 @@ export class ContainerAppHelper { imageToDeploy: string, appSourcePath: string, runtimeStack: string) { - core.debug(`Attempting to create a runnable application image using the Oryx++ Builder with image name "${imageToDeploy}"`); - try { - let telemetryArg = `--env "CALLER_ID=azure-pipelines-rc-v1"`; - if (this.disableTelemetry) { - telemetryArg = `--env "ORYX_DISABLE_TELEMETRY=true"`; - } - await cpExec(`${PACK_CMD} build ${imageToDeploy} --path ${appSourcePath} --builder ${ORYX_BUILDER_IMAGE} --run-image mcr.microsoft.com/oryx/${runtimeStack} ${telemetryArg}`); - } catch (err) { - core.error(err.message); - throw err; + core.debug(`Attempting to create a runnable application image using the Oryx++ Builder with image name "${imageToDeploy}"`); + try { + let telemetryArg = `--env "CALLER_ID=azure-pipelines-rc-v1"`; + if (this.disableTelemetry) { + telemetryArg = `--env "ORYX_DISABLE_TELEMETRY=true"`; } + await cpExec(`${PACK_CMD} build ${imageToDeploy} --path ${appSourcePath} --builder ${ORYX_BUILDER_IMAGE} --run-image mcr.microsoft.com/oryx/${runtimeStack} ${telemetryArg}`); + } catch (err) { + core.error(err.message); + throw err; + } } /** @@ -345,15 +345,15 @@ export class ContainerAppHelper { imageToDeploy: string, appSourcePath: string, dockerfilePath: string) { - core.debug(`Attempting to create a runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); - try { - const dockerTool = await io.which("docker", true); - await new Utility().executeAndthrowIfError(dockerTool, ['build', '--file', `${dockerfilePath}`, `${appSourcePath}`, '--tag', `${imageToDeploy}`]); - core.debug(`Successfully created runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); - } catch (err) { - core.setFailed(err.message); - throw err; - } + core.debug(`Attempting to create a runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); + try { + const dockerTool = await io.which("docker", true); + await new Utility().executeAndthrowIfError(dockerTool, ['build', '--file', `${dockerfilePath}`, `${appSourcePath}`, '--tag', `${imageToDeploy}`]); + core.debug(`Successfully created runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`); + } catch (err) { + core.setFailed(err.message); + throw err; + } } /** @@ -361,7 +361,7 @@ export class ContainerAppHelper { * @param appSourcePath - the path to the application source on the machine * @returns a string representing the runtime stack that can be used for the Oryx MCR runtime images */ - public async determineRuntimeStackAsync(appSourcePath: string): Promise { + public async determineRuntimeStackAsync(appSourcePath: string): Promise { core.debug('Attempting to determine the runtime stack needed for the provided application source'); try { const dockerTool: string = await io.which("docker", true); @@ -396,10 +396,9 @@ export class ContainerAppHelper { * Sets the default builder on the machine to the Oryx++ Builder to prevent an exception from being thrown due * to no default builder set. */ - public async setDefaultBuilder() { + public async setDefaultBuilder() { core.info('Setting the Oryx++ Builder as the default builder via the pack CLI'); - try - { + try { await cpExec(`pack config default-builder ${ORYX_BUILDER_IMAGE}`); } catch (err) { @@ -412,7 +411,7 @@ export class ContainerAppHelper { * Installs the pack CLI that will be used to build a runnable application image. * For more information about the pack CLI can be found here: https://buildpacks.io/docs/tools/pack/ */ - public async installPackCliAsync() { + public async installPackCliAsync() { core.debug('Attempting to install the pack CLI'); try { let command: string = ''; @@ -421,13 +420,13 @@ export class ContainerAppHelper { const packZipDownloadFilePath: string = path.join(PACK_CMD, 'pack-windows.zip'); command = `New-Item -ItemType Directory -Path ${PACK_CMD} -Force | Out-Null;` + - `Invoke-WebRequest -Uri ${packZipDownloadUri} -OutFile ${packZipDownloadFilePath}; ` + - `Expand-Archive -LiteralPath ${packZipDownloadFilePath} -DestinationPath ${PACK_CMD}; ` + - `Remove-Item -Path ${packZipDownloadFilePath}`; + `Invoke-WebRequest -Uri ${packZipDownloadUri} -OutFile ${packZipDownloadFilePath}; ` + + `Expand-Archive -LiteralPath ${packZipDownloadFilePath} -DestinationPath ${PACK_CMD}; ` + + `Remove-Item -Path ${packZipDownloadFilePath}`; } else { const tgzSuffix = os.platform() == 'darwin' ? 'macos' : 'linux'; command = `(curl -sSL \"https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-${tgzSuffix}.tgz\" | ` + - 'tar -C /usr/local/bin/ --no-same-owner -xzv pack)'; + 'tar -C /usr/local/bin/ --no-same-owner -xzv pack)'; } await new CommandHelper().execCommandAsync(command); } catch (err) { diff --git a/src/ContainerRegistryHelper.ts b/src/ContainerRegistryHelper.ts index 367b2dfe..9aeb0514 100644 --- a/src/ContainerRegistryHelper.ts +++ b/src/ContainerRegistryHelper.ts @@ -11,10 +11,10 @@ export class ContainerRegistryHelper { * @param acrUsername - the username for authentication * @param acrPassword - the password for authentication */ - public async loginAcrWithUsernamePassword(acrName: string, acrUsername: string, acrPassword: string) { + public async loginAcrWithUsernamePassword(acrName: string, acrUsername: string, acrPassword: string) { core.debug(`Attempting to log in to ACR instance "${acrName}" with username and password credentials`); try { - await exec.exec('docker', [`login`, `--password-stdin`, `--username`, `${acrUsername}`, `${acrName}.azurecr.io`], { input: Buffer.from(acrPassword) }); + await exec.exec('docker', [`login`, `--password-stdin`, `--username`, `${acrUsername}`, `${acrName}.azurecr.io`], { input: Buffer.from(acrPassword) }); } catch (err) { core.error(`Failed to log in to ACR instance "${acrName}" with username and password credentials`); throw err; @@ -26,7 +26,7 @@ export class ContainerRegistryHelper { * the 'az acr login --expose-token' command. * @param acrName - the name of the ACR instance to authenticate calls to. */ - public async loginAcrWithAccessTokenAsync(acrName: string) { + public async loginAcrWithAccessTokenAsync(acrName: string) { core.debug(`Attempting to log in to ACR instance "${acrName}" with access token`); try { const command: string = `CA_ADO_TASK_ACR_ACCESS_TOKEN=$(az acr login --name ${acrName} --output json --expose-token --only-show-errors | jq -r '.accessToken'); docker login ${acrName}.azurecr.io -u 00000000-0000-0000-0000-000000000000 -p $CA_ADO_TASK_ACR_ACCESS_TOKEN > /dev/null 2>&1`; @@ -41,7 +41,7 @@ export class ContainerRegistryHelper { * Pushes an image to the ACR instance that was previously authenticated against. * @param imageToPush - the name of the image to push to ACR */ - public async pushImageToAcr(imageToPush: string) { + public async pushImageToAcr(imageToPush: string) { core.debug(`Attempting to push image "${imageToPush}" to ACR`); try { const dockerTool: string = await io.which("docker", true); From a0e5eff1c590234ed94c45c0c4581da038075811 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Wed, 20 Sep 2023 02:46:07 -0700 Subject: [PATCH 6/6] Update image name --- azurecontainerapps.ts | 2 +- dist/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azurecontainerapps.ts b/azurecontainerapps.ts index f80a61d0..6d93ba82 100644 --- a/azurecontainerapps.ts +++ b/azurecontainerapps.ts @@ -312,7 +312,7 @@ export class azurecontainerapps { // Get the name of the image to build if it was provided, or generate it from build variables this.imageToBuild = core.getInput('imageToBuild', { required: false }); if (util.isNullOrEmpty(this.imageToBuild)) { - this.imageToBuild = `${this.acrName}.azurecr.io/ado-task/container-app:${this.buildId}.${this.buildNumber}`; + this.imageToBuild = `${this.acrName}.azurecr.io/gh-action/container-app:${this.buildId}.${this.buildNumber}`; core.info(`Default image to build: ${this.imageToBuild}`); } diff --git a/dist/index.js b/dist/index.js index 8466529d..d70bc01b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -379,7 +379,7 @@ var azurecontainerapps = /** @class */ (function () { // Get the name of the image to build if it was provided, or generate it from build variables this.imageToBuild = core.getInput('imageToBuild', { required: false }); if (util.isNullOrEmpty(this.imageToBuild)) { - this.imageToBuild = this.acrName + ".azurecr.io/ado-task/container-app:" + this.buildId + "." + this.buildNumber; + this.imageToBuild = this.acrName + ".azurecr.io/gh-action/container-app:" + this.buildId + "." + this.buildNumber; core.info("Default image to build: " + this.imageToBuild); } // Get the name of the image to deploy if it was provided, or set it to the value of 'imageToBuild'