From 43d6d5f5b44c6b972a2fdd1702f77895ea1334a9 Mon Sep 17 00:00:00 2001 From: Olgethorpe Date: Thu, 27 Jul 2023 20:59:07 +0000 Subject: [PATCH 1/4] allow for cli version specification --- .devcontainer/devcontainer.json | 7 ++++++- action.yml | 4 ++++ azdo-task/DevcontainersCi/src/main.ts | 7 ++++--- common/src/dev-container-cli.ts | 14 +++++--------- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e629bb760..8af25a346 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,12 @@ "name": "devcontainers-ci", "dockerFile": "Dockerfile", "build": { - "cacheFrom": "ghcr.io/devcontainers/ci-devcontainer:latest" + "cacheFrom": "ghcr.io/devcontainers/ci-devcontainer:latest", + "args": { + // This is a temporary workaround when developing from host -> remote host -> devcontainer + // see: https://github.com/microsoft/vscode-remote-release/issues/7958 + "BUILDKIT_INLINE_CACHE": "0" + } }, "mounts": [ // Keep command history diff --git a/action.yml b/action.yml index 375807429..4b620c844 100644 --- a/action.yml +++ b/action.yml @@ -55,6 +55,10 @@ inputs: required: false default: false description: Builds the image with `--no-cache` (takes precedence over `cacheFrom`) + cliVersion: + required: false + default: latest + description: The version of the devcontainer CLI to use outputs: runCmdOutput: description: The output of the command specified in the runCmd input diff --git a/azdo-task/DevcontainersCi/src/main.ts b/azdo-task/DevcontainersCi/src/main.ts index 93d040d43..70f3710a6 100644 --- a/azdo-task/DevcontainersCi/src/main.ts +++ b/azdo-task/DevcontainersCi/src/main.ts @@ -23,10 +23,11 @@ export async function runMain(): Promise { ); return; } - const devContainerCliInstalled = await devcontainer.isCliInstalled(exec); + const specifiedDevContainerCliVersion = task.getInput('cliVersion') + const devContainerCliInstalled = await devcontainer.isCliInstalled(exec, specifiedDevContainerCliVersion); if (!devContainerCliInstalled) { console.log('Installing @devcontainers/cli...'); - const success = await devcontainer.installCli(exec); + const success = await devcontainer.installCli(exec, specifiedDevContainerCliVersion); if (!success) { task.setResult( task.TaskResult.Failed, @@ -265,4 +266,4 @@ export async function runPost(): Promise { await pushImage(imageName, tag); } } -} +} \ No newline at end of file diff --git a/common/src/dev-container-cli.ts b/common/src/dev-container-cli.ts index 08af45c85..7ecf51218 100644 --- a/common/src/dev-container-cli.ts +++ b/common/src/dev-container-cli.ts @@ -5,8 +5,6 @@ import {env} from 'process'; import {promisify} from 'util'; import {ExecFunction} from './exec'; -const cliVersion = "0"; // Use 'latest' to get latest CLI version, or pin to specific version e.g. '0.14.1' if required - export interface DevContainerCliError { outcome: 'error'; code: number; @@ -25,18 +23,16 @@ function getSpecCliInfo() { }; } -async function isCliInstalled(exec: ExecFunction): Promise { +async function isCliInstalled(exec: ExecFunction, cliVersion: string): Promise { try { - const {exitCode} = await exec(getSpecCliInfo().command, ['--help'], { - silent: true, - }); - return exitCode === 0; + const {exitCode, stdout} = await exec(getSpecCliInfo().command, ['--version'], {}); + return exitCode === 0 && stdout === cliVersion; } catch (error) { return false; } } const fstat = promisify(fs.stat); -async function installCli(exec: ExecFunction): Promise { +async function installCli(exec: ExecFunction, cliVersion: string): Promise { // if we have a local 'cli' folder, then use that as we're testing a private cli build let cliStat = null; try { @@ -52,7 +48,7 @@ async function installCli(exec: ExecFunction): Promise { } return exitCode === 0; } - console.log('** Installing @devcontainers/cli'); + console.log(`** Installing @devcontainers/cli@${cliVersion}`); const {exitCode, stdout, stderr} = await exec('bash', ['-c', `npm install -g @devcontainers/cli@${cliVersion}`], {}); if (exitCode != 0) { console.log(stdout); From b1b980fe1c3fdcec00812061b83aeaf2486a48fa Mon Sep 17 00:00:00 2001 From: Olgethorpe Date: Thu, 27 Jul 2023 21:05:53 +0000 Subject: [PATCH 2/4] have local passing build --- azdo-task/DevcontainersCi/src/main.ts | 2 +- azdo-task/README.md | 2 +- github-action/src/main.ts | 11 +++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/azdo-task/DevcontainersCi/src/main.ts b/azdo-task/DevcontainersCi/src/main.ts index 70f3710a6..6379489b8 100644 --- a/azdo-task/DevcontainersCi/src/main.ts +++ b/azdo-task/DevcontainersCi/src/main.ts @@ -23,7 +23,7 @@ export async function runMain(): Promise { ); return; } - const specifiedDevContainerCliVersion = task.getInput('cliVersion') + const specifiedDevContainerCliVersion = task.getInput('cliVersion') ?? 'latest'; const devContainerCliInstalled = await devcontainer.isCliInstalled(exec, specifiedDevContainerCliVersion); if (!devContainerCliInstalled) { console.log('Installing @devcontainers/cli...'); diff --git a/azdo-task/README.md b/azdo-task/README.md index 67dab1bfb..4775fbc8a 100644 --- a/azdo-task/README.md +++ b/azdo-task/README.md @@ -73,7 +73,7 @@ In the example above, the devcontainer-build-run will perform the following step | subFolder | false | Use this to specify the repo-relative path to the folder containing the dev container (i.e. the folder that contains the `.devcontainer` folder). Defaults to repo root | | runCmd | true | The command to run after building the dev container image | | env | false | Specify environment variables to pass to the dev container when run | -| push | false | One of: `never`, `filter`, `always`. When set to `filter`, the image is pushed if the `sourceBranchFilterForPush`, `buildReasonsForPush`, and `pushOnFailedBuild` conditions are met. Defaults to `filter` if `imageName` is set, `never` otherwise. | +| push | false | One of: `never`, `filter`, `always`. When set to `filter`, the image if pushed if the `sourceBranchFilterForPush`, `buildReasonsForPush`, and `pushOnFailedBuild` conditions are met. Defaults to `filter` if `imageName` is set, `never` otherwise. | | pushOnFailedBuild | false | If `false` (default), only push if the build is successful. Set to true to push on failed builds | | sourceBranchFilterForPush | false | Allows you to limit which branch's builds are pushed to the registry (only specified branches are allowed to push). If empty, all branches are allowed | | buildReasonsForPush | false | Allows you to limit the Build.Reason values that are allowed to push to the registry. Defaults to Manual, IndividualCI, BatchedCI. See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&viewFallbackFrom=vsts&tabs=yaml | diff --git a/github-action/src/main.ts b/github-action/src/main.ts index eca23d88b..883e852b7 100644 --- a/github-action/src/main.ts +++ b/github-action/src/main.ts @@ -33,10 +33,17 @@ export async function runMain(): Promise { ); return; } - const devContainerCliInstalled = await devcontainer.isCliInstalled(exec); + const specifiedDevContainerCliVersion = core.getInput('cliVersion') ?? 'latest'; + const devContainerCliInstalled = await devcontainer.isCliInstalled( + exec, + specifiedDevContainerCliVersion, + ); if (!devContainerCliInstalled) { core.info('Installing @devcontainers/cli...'); - const success = await devcontainer.installCli(exec); + const success = await devcontainer.installCli( + exec, + specifiedDevContainerCliVersion, + ); if (!success) { core.setFailed('@devcontainers/cli install failed!'); return; From ff84ddfe1679f9173267af5b3ce54e4955770480 Mon Sep 17 00:00:00 2001 From: Olgethorpe Date: Fri, 28 Jul 2023 13:16:17 +0000 Subject: [PATCH 3/4] address review comment & add docs --- azdo-task/DevcontainersCi/src/main.ts | 15 +++++++++++---- azdo-task/README.md | 5 +++-- docs/azure-devops-task.md | 3 ++- docs/github-action.md | 3 ++- github-action/src/main.ts | 3 ++- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/azdo-task/DevcontainersCi/src/main.ts b/azdo-task/DevcontainersCi/src/main.ts index 6379489b8..026f1dab7 100644 --- a/azdo-task/DevcontainersCi/src/main.ts +++ b/azdo-task/DevcontainersCi/src/main.ts @@ -23,11 +23,18 @@ export async function runMain(): Promise { ); return; } - const specifiedDevContainerCliVersion = task.getInput('cliVersion') ?? 'latest'; - const devContainerCliInstalled = await devcontainer.isCliInstalled(exec, specifiedDevContainerCliVersion); + const specifiedDevContainerCliVersion = + task.getInput('cliVersion') ?? 'latest'; + const devContainerCliInstalled = await devcontainer.isCliInstalled( + exec, + specifiedDevContainerCliVersion, + ); if (!devContainerCliInstalled) { console.log('Installing @devcontainers/cli...'); - const success = await devcontainer.installCli(exec, specifiedDevContainerCliVersion); + const success = await devcontainer.installCli( + exec, + specifiedDevContainerCliVersion, + ); if (!success) { task.setResult( task.TaskResult.Failed, @@ -266,4 +273,4 @@ export async function runPost(): Promise { await pushImage(imageName, tag); } } -} \ No newline at end of file +} diff --git a/azdo-task/README.md b/azdo-task/README.md index 4775fbc8a..046fedf89 100644 --- a/azdo-task/README.md +++ b/azdo-task/README.md @@ -73,14 +73,15 @@ In the example above, the devcontainer-build-run will perform the following step | subFolder | false | Use this to specify the repo-relative path to the folder containing the dev container (i.e. the folder that contains the `.devcontainer` folder). Defaults to repo root | | runCmd | true | The command to run after building the dev container image | | env | false | Specify environment variables to pass to the dev container when run | -| push | false | One of: `never`, `filter`, `always`. When set to `filter`, the image if pushed if the `sourceBranchFilterForPush`, `buildReasonsForPush`, and `pushOnFailedBuild` conditions are met. Defaults to `filter` if `imageName` is set, `never` otherwise. | +| push | false | One of: `never`, `filter`, `always`. When set to `filter`, the image is pushed if the `sourceBranchFilterForPush`, `buildReasonsForPush`, and `pushOnFailedBuild` conditions are met. Defaults to `filter` if `imageName` is set, `never` otherwise. | | pushOnFailedBuild | false | If `false` (default), only push if the build is successful. Set to true to push on failed builds | | sourceBranchFilterForPush | false | Allows you to limit which branch's builds are pushed to the registry (only specified branches are allowed to push). If empty, all branches are allowed | | buildReasonsForPush | false | Allows you to limit the Build.Reason values that are allowed to push to the registry. Defaults to Manual, IndividualCI, BatchedCI. See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&viewFallbackFrom=vsts&tabs=yaml | | skipContainerUserIdUpdate | false | For non-root Dev Containers (i.e. where `remoteUser` is specified), the action attempts to make the container user UID and GID match those of the host user. Set this to true to skip this step (defaults to false) | | cacheFrom | false | Specify additional images to use for build caching | | noCache | false | Builds the image with `--no-cache` (takes precedence over `cacheFrom`) | -| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| cliVersion | false | The version of the [devcontainers CLI](https://github.com/devcontainers/cli) to use | ## Outputs diff --git a/docs/azure-devops-task.md b/docs/azure-devops-task.md index 4775fbc8a..a7972ef8f 100644 --- a/docs/azure-devops-task.md +++ b/docs/azure-devops-task.md @@ -80,7 +80,8 @@ In the example above, the devcontainer-build-run will perform the following step | skipContainerUserIdUpdate | false | For non-root Dev Containers (i.e. where `remoteUser` is specified), the action attempts to make the container user UID and GID match those of the host user. Set this to true to skip this step (defaults to false) | | cacheFrom | false | Specify additional images to use for build caching | | noCache | false | Builds the image with `--no-cache` (takes precedence over `cacheFrom`) | -| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| cliVersion | false | The version of the [devcontainers CLI](https://github.com/devcontainers/cli) to use | ## Outputs diff --git a/docs/github-action.md b/docs/github-action.md index 3725d2f92..45f1351b8 100644 --- a/docs/github-action.md +++ b/docs/github-action.md @@ -139,7 +139,8 @@ The [`devcontainers/ci` action](https://github.com/marketplace/actions/devcontai | skipContainerUserIdUpdate | false | For non-root Dev Containers (i.e. where `remoteUser` is specified), the action attempts to make the container user UID and GID match those of the host user. Set this to true to skip this step (defaults to false) | | cacheFrom | false | Specify additional images to use for build caching | | noCache | false | Builds the image with `--no-cache` (takes precedence over `cacheFrom`) | -| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. | +| cliVersion | false | The version of the [devcontainers CLI](https://github.com/devcontainers/cli) to use | ## Outputs diff --git a/github-action/src/main.ts b/github-action/src/main.ts index 883e852b7..b535291c2 100644 --- a/github-action/src/main.ts +++ b/github-action/src/main.ts @@ -33,7 +33,8 @@ export async function runMain(): Promise { ); return; } - const specifiedDevContainerCliVersion = core.getInput('cliVersion') ?? 'latest'; + const specifiedDevContainerCliVersion = + core.getInput('cliVersion') ?? 'latest'; const devContainerCliInstalled = await devcontainer.isCliInstalled( exec, specifiedDevContainerCliVersion, From cf25e32717a8ac6d35bcea81a1fddfecac8b8863 Mon Sep 17 00:00:00 2001 From: Olgethorpe Date: Wed, 20 Dec 2023 16:07:03 +0000 Subject: [PATCH 4/4] PR feedback --- azdo-task/DevcontainersCi/src/main.ts | 3 ++- common/src/dev-container-cli.ts | 4 +++- github-action/src/main.ts | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/azdo-task/DevcontainersCi/src/main.ts b/azdo-task/DevcontainersCi/src/main.ts index 026f1dab7..87d891cff 100644 --- a/azdo-task/DevcontainersCi/src/main.ts +++ b/azdo-task/DevcontainersCi/src/main.ts @@ -7,6 +7,7 @@ import { DevContainerCliBuildArgs, DevContainerCliExecArgs, DevContainerCliUpArgs, + MAJOR_VERSION_FALLBACK } from '../../../common/src/dev-container-cli'; import {isDockerBuildXInstalled, pushImage} from './docker'; @@ -24,7 +25,7 @@ export async function runMain(): Promise { return; } const specifiedDevContainerCliVersion = - task.getInput('cliVersion') ?? 'latest'; + task.getInput('cliVersion') ?? MAJOR_VERSION_FALLBACK; const devContainerCliInstalled = await devcontainer.isCliInstalled( exec, specifiedDevContainerCliVersion, diff --git a/common/src/dev-container-cli.ts b/common/src/dev-container-cli.ts index 7ecf51218..8181fa0c1 100644 --- a/common/src/dev-container-cli.ts +++ b/common/src/dev-container-cli.ts @@ -5,6 +5,8 @@ import {env} from 'process'; import {promisify} from 'util'; import {ExecFunction} from './exec'; +export const MAJOR_VERSION_FALLBACK = '0'; + export interface DevContainerCliError { outcome: 'error'; code: number; @@ -26,7 +28,7 @@ function getSpecCliInfo() { async function isCliInstalled(exec: ExecFunction, cliVersion: string): Promise { try { const {exitCode, stdout} = await exec(getSpecCliInfo().command, ['--version'], {}); - return exitCode === 0 && stdout === cliVersion; + return exitCode === 0 && stdout.trim() === cliVersion; } catch (error) { return false; } diff --git a/github-action/src/main.ts b/github-action/src/main.ts index b535291c2..5934096d5 100644 --- a/github-action/src/main.ts +++ b/github-action/src/main.ts @@ -7,6 +7,7 @@ import { DevContainerCliBuildArgs, DevContainerCliExecArgs, DevContainerCliUpArgs, + MAJOR_VERSION_FALLBACK } from '../../common/src/dev-container-cli'; import {isDockerBuildXInstalled, pushImage} from './docker'; @@ -34,7 +35,7 @@ export async function runMain(): Promise { return; } const specifiedDevContainerCliVersion = - core.getInput('cliVersion') ?? 'latest'; + core.getInput('cliVersion') ?? MAJOR_VERSION_FALLBACK; const devContainerCliInstalled = await devcontainer.isCliInstalled( exec, specifiedDevContainerCliVersion,