diff --git a/.azure-devops/azure-pipelines.yml b/.azure-devops/azure-pipelines.yml index fdb64f34f..16aa52efa 100644 --- a/.azure-devops/azure-pipelines.yml +++ b/.azure-devops/azure-pipelines.yml @@ -49,6 +49,33 @@ jobs: imageTag: $(IMAGE_TAG) sourceBranchFilterForPush: '' + - job: test_env_vars_on_post_create + displayName: Test env-vars-on-post-create + steps: + - script: | + docker login -u $ACR_USERNAME -p $ACR_TOKEN $(ACR_NAME).azurecr.io + displayName: 'Log in to Azure Container Registry' + env: + ACR_NAME: $(ACR_NAME) + ACR_TOKEN: $(ACR_TOKEN) + ACR_USERNAME: $(ACR_USERNAME) + + - task: DevcontainersCi@0 + env: # TEST_ENV_VALUE1 is set via devcontainer.json using a localEnv reference + TEST_ENV_VALUE1: SetViaDevcontainerJsonLocalEnv + inputs: + imageName: '$(ACR_NAME).azurecr.io/devcontainers-ci/azdo-devcontainer-build-run/test/env-vars-on-post-create' + subFolder: github-tests/Dockerfile/env-vars-on-post-create + runCmd: | + cat marker.txt + cat marker.txt | grep 'post-create: TEST_ENV_VALUE1=SetViaDevcontainerJsonLocalEnv' + cat marker.txt | grep 'post-create: TEST_ENV_VALUE2=AdditionalEnvVar' + env: | # TEST_ENV_VALUE2 is an additional env var to pass to the container + TEST_ENV_VALUE2=AdditionalEnvVar + imageTag: $(IMAGE_TAG) + sourceBranchFilterForPush: '' + + - job: test_simple displayName: Test simple steps: diff --git a/.github/workflows/ci_common.yml b/.github/workflows/ci_common.yml index e68056a0e..fa3a570c1 100644 --- a/.github/workflows/ci_common.yml +++ b/.github/workflows/ci_common.yml @@ -204,6 +204,7 @@ jobs: - test-simple - test-no-run - test-platform-with-runcmd + - test-env-vars-on-post-create - test-multiple-tags-job2 runs-on: ubuntu-latest steps: @@ -499,6 +500,58 @@ jobs: push pull_request + test-env-vars-on-post-create: + name: Run GitHub env-vars-on-post-create test + runs-on: ubuntu-latest + needs: build + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + persist-credentials: false + # if the following value is missing (i.e. not triggered via comment workflow) + # then the default checkout will apply + ref: ${{ inputs.prRef }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + if: ${{ needs.build.outputs.image_push_option == 'filter' }} + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Published action contains compiled JS, but we need to compile it here + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Compile GH action + run: | + (cd common && npm install && npm run build) + (cd github-action/ && npm install && npm run build && npm run package) + + - name: Run test + uses: ./ + env: # TEST_ENV_VALUE1 is set via devcontainer.json using a localEnv reference + TEST_ENV_VALUE1: SetViaDevcontainerJsonLocalEnv + with: + subFolder: github-tests/Dockerfile/env-vars-on-post-create + imageName: ghcr.io/devcontainers/ci/tests/env-vars-on-post-create + env: | # TEST_ENV_VALUE2 is an additional env var to pass to the container + TEST_ENV_VALUE2=AdditionalEnvVar + runCmd: | + cat marker.txt + cat marker.txt | grep 'post-create: TEST_ENV_VALUE1=SetViaDevcontainerJsonLocalEnv' + cat marker.txt | grep 'post-create: TEST_ENV_VALUE2=AdditionalEnvVar' + imageTag: ${{ needs.build.outputs.image_tag }} + push: ${{ needs.build.outputs.image_push_option }} + eventFilterForPush: | + push + pull_request + test-gh-build-args: name: Run GitHub build-args test runs-on: ubuntu-latest diff --git a/azdo-task/DevcontainersCi/src/main.ts b/azdo-task/DevcontainersCi/src/main.ts index a305da60b..424cd24f1 100644 --- a/azdo-task/DevcontainersCi/src/main.ts +++ b/azdo-task/DevcontainersCi/src/main.ts @@ -120,6 +120,7 @@ export async function runMain(): Promise { workspaceFolder, additionalCacheFroms: cacheFrom, skipContainerUserIdUpdate, + env: inputEnvsWithDefaults, }; const upResult = await devcontainer.up(upArgs, log); if (upResult.outcome !== 'success') { diff --git a/azdo-task/README.md b/azdo-task/README.md index 360349ff9..4eb1affbd 100644 --- a/azdo-task/README.md +++ b/azdo-task/README.md @@ -128,10 +128,64 @@ If you want to pass additional environment variables to the dev container when i WORLD ``` -In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard action's `env` configuration. +In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard task's `env` configuration. The result from running the container is to output "Hello - World". +## Environment Variables + +If you want to pass additional environment variables to the dev container when it is run, you can use the `env` input as shown below. + + +```yaml +- task: DevcontainersCi@0 + env: + WORLD: World + inputs: + imageName: 'yourregistry.azurecr.io/example-dev-container' + runCmd: echo "$HELLO - $WORLD" + env: | + HELLO=Hello + WORLD +``` + +In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard action's `env` configuration (it could also be picked up from the job environment variables - see the [Azure DevOps Piplines Environment Variables docs](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#variable-scopes) for more information). + +The result from running the container is to output "Hello - World". + +The environment variables specified in the workflow step are passed along when the run-command is executed. Therefore, they replace environment variables with the same name that are set either directly in the Dockerfile or the `devcontainer.json` under the [`containerEnv`](https://code.visualstudio.com/remote/advancedcontainers/environment-variables#_option-1-add-individual-variables) key. + +### remoteEnv + +If you have environment variables set in the `remoteEnv` section of your `devcontainer.json` file using `localEnv` references, you need to pass the environment variables in a specific way. + +Since `localEnv` references are resolved by the `devcontainer` CLI, we need to ensure that we set the values in the correct context for `localEnv`. To do this, the values should be set using the `env` property on the task, not using the `env` nested under the `with` block. + +For example, if you have the following section in your `devcontainer.json`: + +```json +{ + "remoteEnv": { + "HELLO": "${localEnv:HELLO}" + } +} +``` + +You should set the `HELLO` environment variable using the `env` property on the task, not using the `env` nested under the `with` block. + +```yaml +- task: DevcontainersCi@0 + env: + # Set HELLO here so that it is resolved via the localEnv context + HELLO: hello + inputs: + imageName: 'yourregistry.azurecr.io/example-dev-container' + runCmd: echo "$HELLO" + # Don't use the env block here to set the HELLO environment variable + # as it will be overridden by the value from localEnv context + # when the CLI starts the container +``` + ## Multi-Platform Builds Builds for multiple platforms have special considerations, detailed at [mutli-platform-builds.md](multi-platform-builds.md). diff --git a/common/src/dev-container-cli.ts b/common/src/dev-container-cli.ts index eea50dd92..b9a5ae604 100644 --- a/common/src/dev-container-cli.ts +++ b/common/src/dev-container-cli.ts @@ -185,16 +185,19 @@ export interface DevContainerCliUpArgs { workspaceFolder: string; additionalCacheFroms?: string[]; skipContainerUserIdUpdate?: boolean; + env?: string[]; userDataFolder?: string; } async function devContainerUp( args: DevContainerCliUpArgs, log: (data: string) => void, ): Promise { + const remoteEnvArgs = getRemoteEnvArray(args.env); const commandArgs: string[] = [ 'up', '--workspace-folder', args.workspaceFolder, + ...remoteEnvArgs, ]; if (args.additionalCacheFroms) { args.additionalCacheFroms.forEach(cacheFrom => diff --git a/docs/azure-devops-task.md b/docs/azure-devops-task.md index 360349ff9..4eb1affbd 100644 --- a/docs/azure-devops-task.md +++ b/docs/azure-devops-task.md @@ -128,10 +128,64 @@ If you want to pass additional environment variables to the dev container when i WORLD ``` -In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard action's `env` configuration. +In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard task's `env` configuration. The result from running the container is to output "Hello - World". +## Environment Variables + +If you want to pass additional environment variables to the dev container when it is run, you can use the `env` input as shown below. + + +```yaml +- task: DevcontainersCi@0 + env: + WORLD: World + inputs: + imageName: 'yourregistry.azurecr.io/example-dev-container' + runCmd: echo "$HELLO - $WORLD" + env: | + HELLO=Hello + WORLD +``` + +In this example, the `HELLO` environment variable is specified with the value `Hello` in the `env` input on the devcontainer-build-run step. The `WORLD` environment variable is specified without a value so will pick up the value that is assigned in the standard action's `env` configuration (it could also be picked up from the job environment variables - see the [Azure DevOps Piplines Environment Variables docs](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#variable-scopes) for more information). + +The result from running the container is to output "Hello - World". + +The environment variables specified in the workflow step are passed along when the run-command is executed. Therefore, they replace environment variables with the same name that are set either directly in the Dockerfile or the `devcontainer.json` under the [`containerEnv`](https://code.visualstudio.com/remote/advancedcontainers/environment-variables#_option-1-add-individual-variables) key. + +### remoteEnv + +If you have environment variables set in the `remoteEnv` section of your `devcontainer.json` file using `localEnv` references, you need to pass the environment variables in a specific way. + +Since `localEnv` references are resolved by the `devcontainer` CLI, we need to ensure that we set the values in the correct context for `localEnv`. To do this, the values should be set using the `env` property on the task, not using the `env` nested under the `with` block. + +For example, if you have the following section in your `devcontainer.json`: + +```json +{ + "remoteEnv": { + "HELLO": "${localEnv:HELLO}" + } +} +``` + +You should set the `HELLO` environment variable using the `env` property on the task, not using the `env` nested under the `with` block. + +```yaml +- task: DevcontainersCi@0 + env: + # Set HELLO here so that it is resolved via the localEnv context + HELLO: hello + inputs: + imageName: 'yourregistry.azurecr.io/example-dev-container' + runCmd: echo "$HELLO" + # Don't use the env block here to set the HELLO environment variable + # as it will be overridden by the value from localEnv context + # when the CLI starts the container +``` + ## Multi-Platform Builds Builds for multiple platforms have special considerations, detailed at [mutli-platform-builds.md](multi-platform-builds.md). diff --git a/docs/github-action.md b/docs/github-action.md index b4197e5d6..8284f2e3d 100644 --- a/docs/github-action.md +++ b/docs/github-action.md @@ -198,6 +198,38 @@ The result from running the container is to output "Hello - World". The environment variables specified in the workflow step are passed along when the run-command is executed. Therefore, they replace environment variables with the same name that are set either directly in the Dockerfile or the `devcontainer.json` under the [`containerEnv`](https://code.visualstudio.com/remote/advancedcontainers/environment-variables#_option-1-add-individual-variables) key. +### remoteEnv + +If you have environment variables set in the `remoteEnv` section of your `devcontainer.json` file using `localEnv` references, you need to pass the environment variables in a specific way. + +Since `localEnv` references are resolved by the `devcontainer` CLI, we need to ensure that we set the values in the correct context for `localEnv`. To do this, the values should be set using the `env` property on the action, not using the `env` nested under the `with` block. + +For example, if you have the following section in your `devcontainer.json`: + +```json +{ + "remoteEnv": { + "HELLO": "${localEnv:HELLO}" + } +} +``` + +You should set the `HELLO` environment variable using the `env` property on the action, not using the `env` nested under the `with` block. + +```yaml + - name: Build and run dev container task + uses: devcontainers/ci@v0.3 + env: + # Set HELLO here so that it is resolved via the localEnv context + HELLO: hello + with: + imageName: ghcr.io/example/example-devcontainer + runCmd: echo "$HELLO" + # Don't use the env block here to set the HELLO environment variable + # as it will be overridden by the value from localEnv context + # when the CLI starts the container +``` + ## Multi-Platform Builds Builds for multiple platforms have special considerations, detailed at [mutli-platform-builds.md](multi-platform-builds.md). diff --git a/github-action/src/main.ts b/github-action/src/main.ts index f75f18f85..eef38426a 100644 --- a/github-action/src/main.ts +++ b/github-action/src/main.ts @@ -125,6 +125,7 @@ export async function runMain(): Promise { workspaceFolder, additionalCacheFroms: cacheFrom, skipContainerUserIdUpdate, + env: inputEnvsWithDefaults, userDataFolder, }; const result = await devcontainer.up(args, log); diff --git a/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/Dockerfile b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/Dockerfile new file mode 100644 index 000000000..754d20d6b --- /dev/null +++ b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/Dockerfile @@ -0,0 +1,46 @@ +# [Choice] Debian / Ubuntu version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 +# See https://github.com/microsoft/vscode-dev-containers/tree/master/containers/debian +ARG VARIANT=debian-10 +FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT} + + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# Set env for tracking that we're running in a devcontainer +ENV DEVCONTAINER=true + +# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" +# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs +# will be updated to match your local UID/GID (when using the dockerFile property). +# See https://aka.ms/vscode-remote/containers/non-root-user for details. +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +USER $USERNAME +RUN \ + mkdir -p ~/.local/bin \ + && echo "export PATH=\$PATH:~/.local/bin" >> ~/.bashrc + +# Configure apt, install packages and general tools +RUN sudo apt-get update \ + && sudo apt-get -y install --no-install-recommends apt-utils dialog nano bash-completion sudo bsdmainutils \ + # + # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed + && sudo apt-get -y install git iproute2 procps lsb-release figlet build-essential + +# Save command line history +RUN echo "export HISTFILE=/home/$USERNAME/commandhistory/.bash_history" >> "/home/$USERNAME/.bashrc" \ + && echo "export PROMPT_COMMAND='history -a'" >> "/home/$USERNAME/.bashrc" \ + && mkdir -p /home/$USERNAME/commandhistory \ + && touch /home/$USERNAME/commandhistory/.bash_history \ + && chown -R $USERNAME /home/$USERNAME/commandhistory + +# Set env for tracking that we're running in a devcontainer +ENV DEVCONTAINER=true + +# __DEVCONTAINER_SNIPPET_INSERT__ (control where snippets get inserted using the devcontainer CLI) + +# Switch back to dialog for any ad-hoc use of apt-get +ENV DEBIAN_FRONTEND=dialog diff --git a/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/devcontainer.json b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/devcontainer.json new file mode 100644 index 000000000..45363d71d --- /dev/null +++ b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/go +{ + "name": "env-vars-on-post-create", + "dockerFile": "Dockerfile", + "build": { + "cacheFrom": "ghcr.io/devcontainers/ci/tests/env-vars-on-post-create:latest" + }, + "remoteEnv": { + "TEST_ENV_VALUE": "${localEnv:TEST_ENV_VALUE}" + }, + + // Add the IDs of extensions you want installed when the container is created. + // "extensions": [], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "./.devcontainer/post-create.sh", + + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/post-create.sh b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/post-create.sh new file mode 100755 index 000000000..32d0e468f --- /dev/null +++ b/github-tests/Dockerfile/env-vars-on-post-create/.devcontainer/post-create.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +echo "**************************************************" +echo "*** In post-create.sh " +echo "*** TEST_ENV_VALUE=${TEST_ENV_VALUE}" +echo "*** TEST_ENV_VALUE2=${TEST_ENV_VALUE2}" +echo "**************************************************" +echo "post-create: TEST_ENV_VALUE=${TEST_ENV_VALUE}" > marker.txt +echo "post-create: TEST_ENV_VALUE2=${TEST_ENV_VALUE2}" >> marker.txt +