From 230873bac2ae22a749e0822a1545f06b8a6d8d92 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 13:19:53 +0000 Subject: [PATCH 01/13] feat: prefix env variables with ENVBUILDER --- README.md | 60 +++++++++++++++++------------------ options.go | 93 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 90 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 0b230cb8..f2818312 100644 --- a/README.md +++ b/README.md @@ -305,36 +305,36 @@ On MacOS or Windows systems, we recommend either using a VM or the provided `.de | Flag | Environment variable | Default | Description | | - | - | - | - | -| `--setup-script` | `SETUP_SCRIPT` | | The script to run before the init script. It runs as the root user regardless of the user specified in the devcontainer.json file. SetupScript is ran as the root user prior to the init script. It is used to configure envbuilder dynamically during the runtime. e.g. specifying whether to start systemd or tiny init for PID 1. | -| `--init-script` | `INIT_SCRIPT` | `sleep infinity` | The script to run to initialize the workspace. | -| `--init-command` | `INIT_COMMAND` | `/bin/sh` | The command to run to initialize the workspace. | -| `--init-args` | `INIT_ARGS` | | The arguments to pass to the init command. They are split according to /bin/sh rules with https://github.com/kballard/go-shellquote. | -| `--cache-repo` | `CACHE_REPO` | | The name of the container registry to push the cache image to. If this is empty, the cache will not be pushed. | -| `--base-image-cache-dir` | `BASE_IMAGE_CACHE_DIR` | | The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image. | -| `--layer-cache-dir` | `LAYER_CACHE_DIR` | | The path to a directory where built layers will be stored. This spawns an in-memory registry to serve the layers from. | -| `--devcontainer-dir` | `DEVCONTAINER_DIR` | | The path to the folder containing the devcontainer.json file that will be used to build the workspace and can either be an absolute path or a path relative to the workspace folder. If not provided, defaults to `.devcontainer`. | -| `--devcontainer-json-path` | `DEVCONTAINER_JSON_PATH` | | The path to a devcontainer.json file that is either an absolute path or a path relative to DevcontainerDir. This can be used in cases where one wants to substitute an edited devcontainer.json file for the one that exists in the repo. | -| `--dockerfile-path` | `DOCKERFILE_PATH` | | The relative path to the Dockerfile that will be used to build the workspace. This is an alternative to using a devcontainer that some might find simpler. | -| `--build-context-path` | `BUILD_CONTEXT_PATH` | | Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned. | -| `--cache-ttl-days` | `CACHE_TTL_DAYS` | | The number of days to use cached layers before expiring them. Defaults to 7 days. | -| `--docker-config-base64` | `DOCKER_CONFIG_BASE64` | | The base64 encoded Docker config file that will be used to pull images from private container registries. | -| `--fallback-image` | `FALLBACK_IMAGE` | | Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a misconfiguration, this image will be the substitute. Set ExitOnBuildFailure to true to halt the container if the build faces an issue. | -| `--exit-on-build-failure` | `EXIT_ON_BUILD_FAILURE` | | Terminates the container upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error. | -| `--force-safe` | `FORCE_SAFE` | | Ignores any filesystem safety checks. This could cause serious harm to your system! This is used in cases where bypass is needed to unblock customers. | -| `--insecure` | `INSECURE` | | Bypass TLS verification when cloning and pulling from container registries. | -| `--ignore-paths` | `IGNORE_PATHS` | `/var/run` | The comma separated list of paths to ignore when building the workspace. | -| `--skip-rebuild` | `SKIP_REBUILD` | | Skip building if the MagicFile exists. This is used to skip building when a container is restarting. e.g. docker stop -> docker start This value can always be set to true - even if the container is being started for the first time. | -| `--git-url` | `GIT_URL` | | The URL of the Git repository to clone. This is optional. | -| `--git-clone-depth` | `GIT_CLONE_DEPTH` | | The depth to use when cloning the Git repository. | -| `--git-clone-single-branch` | `GIT_CLONE_SINGLE_BRANCH` | | Clone only a single branch of the Git repository. | -| `--git-username` | `GIT_USERNAME` | | The username to use for Git authentication. This is optional. | -| `--git-password` | `GIT_PASSWORD` | | The password to use for Git authentication. This is optional. | -| `--git-ssh-private-key-path` | `GIT_SSH_PRIVATE_KEY_PATH` | | Path to an SSH private key to be used for Git authentication. | -| `--git-http-proxy-url` | `GIT_HTTP_PROXY_URL` | | The URL for the HTTP proxy. This is optional. | -| `--workspace-folder` | `WORKSPACE_FOLDER` | | The path to the workspace folder that will be built. This is optional. | -| `--ssl-cert-base64` | `SSL_CERT_BASE64` | | The content of an SSL cert file. This is useful for self-signed certificates. | -| `--export-env-file` | `EXPORT_ENV_FILE` | | Optional file path to a .env file where envbuilder will dump environment variables from devcontainer.json and the built container image. | -| `--post-start-script-path` | `POST_START_SCRIPT_PATH` | | The path to a script that will be created by envbuilder based on the postStartCommand in devcontainer.json, if any is specified (otherwise the script is not created). If this is set, the specified InitCommand should check for the presence of this script and execute it after successful startup. | +| `--setup-script` | `ENVBUILDER_SETUP_SCRIPT` | | The script to run before the init script. It runs as the root user regardless of the user specified in the devcontainer.json file. SetupScript is ran as the root user prior to the init script. It is used to configure envbuilder dynamically during the runtime. e.g. specifying whether to start systemd or tiny init for PID 1. | +| `--init-script` | `ENVBUILDER_INIT_SCRIPT` | `sleep infinity` | The script to run to initialize the workspace. | +| `--init-command` | `ENVBUILDER_INIT_COMMAND` | `/bin/sh` | The command to run to initialize the workspace. | +| `--init-args` | `ENVBUILDER_INIT_ARGS` | | The arguments to pass to the init command. They are split according to /bin/sh rules with https://github.com/kballard/go-shellquote. | +| `--cache-repo` | `ENVBUILDER_CACHE_REPO` | | The name of the container registry to push the cache image to. If this is empty, the cache will not be pushed. | +| `--base-image-cache-dir` | `ENVBUILDER_BASE_IMAGE_CACHE_DIR` | | The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image. | +| `--layer-cache-dir` | `ENVBUILDER_LAYER_CACHE_DIR` | | The path to a directory where built layers will be stored. This spawns an in-memory registry to serve the layers from. | +| `--devcontainer-dir` | `ENVBUILDER_DEVCONTAINER_DIR` | | The path to the folder containing the devcontainer.json file that will be used to build the workspace and can either be an absolute path or a path relative to the workspace folder. If not provided, defaults to `.devcontainer`. | +| `--devcontainer-json-path` | `ENVBUILDER_DEVCONTAINER_JSON_PATH` | | The path to a devcontainer.json file that is either an absolute path or a path relative to DevcontainerDir. This can be used in cases where one wants to substitute an edited devcontainer.json file for the one that exists in the repo. | +| `--dockerfile-path` | `ENVBUILDER_DOCKERFILE_PATH` | | The relative path to the Dockerfile that will be used to build the workspace. This is an alternative to using a devcontainer that some might find simpler. | +| `--build-context-path` | `ENVBUILDER_BUILD_CONTEXT_PATH` | | Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned. | +| `--cache-ttl-days` | `ENVBUILDER_CACHE_TTL_DAYS` | | The number of days to use cached layers before expiring them. Defaults to 7 days. | +| `--docker-config-base64` | `ENVBUILDER_DOCKER_CONFIG_BASE64` | | The base64 encoded Docker config file that will be used to pull images from private container registries. | +| `--fallback-image` | `ENVBUILDER_FALLBACK_IMAGE` | | Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a misconfiguration, this image will be the substitute. Set ExitOnBuildFailure to true to halt the container if the build faces an issue. | +| `--exit-on-build-failure` | `ENVBUILDER_EXIT_ON_BUILD_FAILURE` | | Terminates the container upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error. | +| `--force-safe` | `ENVBUILDER_FORCE_SAFE` | | Ignores any filesystem safety checks. This could cause serious harm to your system! This is used in cases where bypass is needed to unblock customers. | +| `--insecure` | `ENVBUILDER_INSECURE` | | Bypass TLS verification when cloning and pulling from container registries. | +| `--ignore-paths` | `ENVBUILDER_IGNORE_PATHS` | `/var/run` | The comma separated list of paths to ignore when building the workspace. | +| `--skip-rebuild` | `ENVBUILDER_SKIP_REBUILD` | | Skip building if the MagicFile exists. This is used to skip building when a container is restarting. e.g. docker stop -> docker start This value can always be set to true - even if the container is being started for the first time. | +| `--git-url` | `ENVBUILDER_GIT_URL` | | The URL of the Git repository to clone. This is optional. | +| `--git-clone-depth` | `ENVBUILDER_GIT_CLONE_DEPTH` | | The depth to use when cloning the Git repository. | +| `--git-clone-single-branch` | `ENVBUILDER_GIT_CLONE_SINGLE_BRANCH` | | Clone only a single branch of the Git repository. | +| `--git-username` | `ENVBUILDER_GIT_USERNAME` | | The username to use for Git authentication. This is optional. | +| `--git-password` | `ENVBUILDER_GIT_PASSWORD` | | The password to use for Git authentication. This is optional. | +| `--git-ssh-private-key-path` | `ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH` | | Path to an SSH private key to be used for Git authentication. | +| `--git-http-proxy-url` | `ENVBUILDER_GIT_HTTP_PROXY_URL` | | The URL for the HTTP proxy. This is optional. | +| `--workspace-folder` | `ENVBUILDER_WORKSPACE_FOLDER` | | The path to the workspace folder that will be built. This is optional. | +| `--ssl-cert-base64` | `ENVBUILDER_SSL_CERT_BASE64` | | The content of an SSL cert file. This is useful for self-signed certificates. | +| `--export-env-file` | `ENVBUILDER_EXPORT_ENV_FILE` | | Optional file path to a .env file where envbuilder will dump environment variables from devcontainer.json and the built container image. | +| `--post-start-script-path` | `ENVBUILDER_POST_START_SCRIPT_PATH` | | The path to a script that will be created by envbuilder based on the postStartCommand in devcontainer.json, if any is specified (otherwise the script is not created). If this is set, the specified InitCommand should check for the presence of this script and execute it after successful startup. | | `--coder-agent-url` | `CODER_AGENT_URL` | | URL of the Coder deployment. If CODER_AGENT_TOKEN is also set, logs from envbuilder will be forwarded here and will be visible in the workspace build logs. | | `--coder-agent-token` | `CODER_AGENT_TOKEN` | | Authentication token for a Coder agent. If this is set, then CODER_AGENT_URL must also be set. | | `--coder-agent-subsystem` | `CODER_AGENT_SUBSYSTEM` | | Coder agent subsystems to report when forwarding logs. The envbuilder subsystem is always included. | diff --git a/options.go b/options.go index 032a015a..f6efbc9e 100644 --- a/options.go +++ b/options.go @@ -142,10 +142,12 @@ type Options struct { // Generate CLI options for the envbuilder command. func (o *Options) CLI() serpent.OptionSet { - return serpent.OptionSet{ + const prefix = "ENVBUILDER_" + + set := serpent.OptionSet{ { Flag: "setup-script", - Env: "SETUP_SCRIPT", + Env: prefix + "SETUP_SCRIPT", Value: serpent.StringOf(&o.SetupScript), Description: "The script to run before the init script. It runs as " + "the root user regardless of the user specified in the devcontainer.json " + @@ -155,21 +157,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "init-script", - Env: "INIT_SCRIPT", + Env: prefix + "INIT_SCRIPT", Default: "sleep infinity", Value: serpent.StringOf(&o.InitScript), Description: "The script to run to initialize the workspace.", }, { Flag: "init-command", - Env: "INIT_COMMAND", + Env: prefix + "INIT_COMMAND", Default: "/bin/sh", Value: serpent.StringOf(&o.InitCommand), Description: "The command to run to initialize the workspace.", }, { Flag: "init-args", - Env: "INIT_ARGS", + Env: prefix + "INIT_ARGS", Value: serpent.StringOf(&o.InitArgs), Description: "The arguments to pass to the init command. They are " + "split according to /bin/sh rules with " + @@ -177,14 +179,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-repo", - Env: "CACHE_REPO", + Env: prefix + "CACHE_REPO", Value: serpent.StringOf(&o.CacheRepo), Description: "The name of the container registry to push the cache " + "image to. If this is empty, the cache will not be pushed.", }, { Flag: "base-image-cache-dir", - Env: "BASE_IMAGE_CACHE_DIR", + Env: prefix + "BASE_IMAGE_CACHE_DIR", Value: serpent.StringOf(&o.BaseImageCacheDir), Description: "The path to a directory where the base image " + "can be found. This should be a read-only directory solely mounted " + @@ -192,7 +194,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "layer-cache-dir", - Env: "LAYER_CACHE_DIR", + Env: prefix + "LAYER_CACHE_DIR", Value: serpent.StringOf(&o.LayerCacheDir), Description: "The path to a directory where built layers will " + "be stored. This spawns an in-memory registry to serve the layers " + @@ -200,7 +202,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-dir", - Env: "DEVCONTAINER_DIR", + Env: prefix + "DEVCONTAINER_DIR", Value: serpent.StringOf(&o.DevcontainerDir), Description: "The path to the folder containing the " + "devcontainer.json file that will be used to build the workspace " + @@ -209,7 +211,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-json-path", - Env: "DEVCONTAINER_JSON_PATH", + Env: prefix + "DEVCONTAINER_JSON_PATH", Value: serpent.StringOf(&o.DevcontainerJSONPath), Description: "The path to a devcontainer.json file that " + "is either an absolute path or a path relative to DevcontainerDir. " + @@ -218,7 +220,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "dockerfile-path", - Env: "DOCKERFILE_PATH", + Env: prefix + "DOCKERFILE_PATH", Value: serpent.StringOf(&o.DockerfilePath), Description: "The relative path to the Dockerfile that will " + "be used to build the workspace. This is an alternative to using " + @@ -226,7 +228,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "build-context-path", - Env: "BUILD_CONTEXT_PATH", + Env: prefix + "BUILD_CONTEXT_PATH", Value: serpent.StringOf(&o.BuildContextPath), Description: "Can be specified when a DockerfilePath is " + "specified outside the base WorkspaceFolder. This path MUST be " + @@ -234,21 +236,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-ttl-days", - Env: "CACHE_TTL_DAYS", + Env: prefix + "CACHE_TTL_DAYS", Value: serpent.Int64Of(&o.CacheTTLDays), Description: "The number of days to use cached layers before " + "expiring them. Defaults to 7 days.", }, { Flag: "docker-config-base64", - Env: "DOCKER_CONFIG_BASE64", + Env: prefix + "DOCKER_CONFIG_BASE64", Value: serpent.StringOf(&o.DockerConfigBase64), Description: "The base64 encoded Docker config file that " + "will be used to pull images from private container registries.", }, { Flag: "fallback-image", - Env: "FALLBACK_IMAGE", + Env: prefix + "FALLBACK_IMAGE", Value: serpent.StringOf(&o.FallbackImage), Description: "Specifies an alternative image to use when neither " + "an image is declared in the devcontainer.json file nor a Dockerfile " + @@ -259,7 +261,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "exit-on-build-failure", - Env: "EXIT_ON_BUILD_FAILURE", + Env: prefix + "EXIT_ON_BUILD_FAILURE", Value: serpent.BoolOf(&o.ExitOnBuildFailure), Description: "Terminates the container upon a build failure. " + "This is handy when preferring the FALLBACK_IMAGE in cases where " + @@ -268,7 +270,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "force-safe", - Env: "FORCE_SAFE", + Env: prefix + "FORCE_SAFE", Value: serpent.BoolOf(&o.ForceSafe), Description: "Ignores any filesystem safety checks. This could cause " + "serious harm to your system! This is used in cases where bypass " + @@ -276,14 +278,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "insecure", - Env: "INSECURE", + Env: prefix + "INSECURE", Value: serpent.BoolOf(&o.Insecure), Description: "Bypass TLS verification when cloning and pulling from " + "container registries.", }, { Flag: "ignore-paths", - Env: "IGNORE_PATHS", + Env: prefix + "IGNORE_PATHS", Value: serpent.StringArrayOf(&o.IgnorePaths), Default: "/var/run", Description: "The comma separated list of paths to ignore when " + @@ -291,7 +293,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "skip-rebuild", - Env: "SKIP_REBUILD", + Env: prefix + "SKIP_REBUILD", Value: serpent.BoolOf(&o.SkipRebuild), Description: "Skip building if the MagicFile exists. This is used " + "to skip building when a container is restarting. e.g. docker stop -> " + @@ -300,63 +302,63 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "git-url", - Env: "GIT_URL", + Env: prefix + "GIT_URL", Value: serpent.StringOf(&o.GitURL), Description: "The URL of the Git repository to clone. This is optional.", }, { Flag: "git-clone-depth", - Env: "GIT_CLONE_DEPTH", + Env: prefix + "GIT_CLONE_DEPTH", Value: serpent.Int64Of(&o.GitCloneDepth), Description: "The depth to use when cloning the Git repository.", }, { Flag: "git-clone-single-branch", - Env: "GIT_CLONE_SINGLE_BRANCH", + Env: prefix + "GIT_CLONE_SINGLE_BRANCH", Value: serpent.BoolOf(&o.GitCloneSingleBranch), Description: "Clone only a single branch of the Git repository.", }, { Flag: "git-username", - Env: "GIT_USERNAME", + Env: prefix + "GIT_USERNAME", Value: serpent.StringOf(&o.GitUsername), Description: "The username to use for Git authentication. This is optional.", }, { Flag: "git-password", - Env: "GIT_PASSWORD", + Env: prefix + "GIT_PASSWORD", Value: serpent.StringOf(&o.GitPassword), Description: "The password to use for Git authentication. This is optional.", }, { Flag: "git-ssh-private-key-path", - Env: "GIT_SSH_PRIVATE_KEY_PATH", + Env: prefix + "GIT_SSH_PRIVATE_KEY_PATH", Value: serpent.StringOf(&o.GitSSHPrivateKeyPath), Description: "Path to an SSH private key to be used for Git authentication.", }, { Flag: "git-http-proxy-url", - Env: "GIT_HTTP_PROXY_URL", + Env: prefix + "GIT_HTTP_PROXY_URL", Value: serpent.StringOf(&o.GitHTTPProxyURL), Description: "The URL for the HTTP proxy. This is optional.", }, { Flag: "workspace-folder", - Env: "WORKSPACE_FOLDER", + Env: prefix + "WORKSPACE_FOLDER", Value: serpent.StringOf(&o.WorkspaceFolder), Description: "The path to the workspace folder that will " + "be built. This is optional.", }, { Flag: "ssl-cert-base64", - Env: "SSL_CERT_BASE64", + Env: prefix + "SSL_CERT_BASE64", Value: serpent.StringOf(&o.SSLCertBase64), Description: "The content of an SSL cert file. This is useful " + "for self-signed certificates.", }, { Flag: "export-env-file", - Env: "EXPORT_ENV_FILE", + Env: prefix + "EXPORT_ENV_FILE", Value: serpent.StringOf(&o.ExportEnvFile), Description: "Optional file path to a .env file where " + "envbuilder will dump environment variables from devcontainer.json " + @@ -364,7 +366,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "post-start-script-path", - Env: "POST_START_SCRIPT_PATH", + Env: prefix + "POST_START_SCRIPT_PATH", Value: serpent.StringOf(&o.PostStartScriptPath), Description: "The path to a script that will be created " + "by envbuilder based on the postStartCommand in devcontainer.json, " + @@ -395,15 +397,40 @@ func (o *Options) CLI() serpent.OptionSet { "The envbuilder subsystem is always included.", }, } + + // Add options without the prefix for backward compatibility. These options + // are marked as deprecated and will be removed in future versions. Note: + // Future versions will require the 'ENVBUILDER' prefix for default + // environment variables. + for _, o := range set { + if strings.HasPrefix(o.Env, prefix) { + prevOption := o + prevOption.Env = strings.TrimPrefix(o.Env, prefix) + prevOption.UseInstead = []serpent.Option{o} + prevOption.Hidden = true + set = append(set, prevOption) + } + } + + return set } func (o *Options) Markdown() string { - cliOptions := o.CLI() + var visibleOpts serpent.OptionSet + + // We don't want to include deprecated opts in the README. + for _, opt := range o.CLI() { + isDeprecated := len(opt.UseInstead) > 0 + if !isDeprecated { + visibleOpts = append(visibleOpts, opt) + } + } + var sb strings.Builder _, _ = sb.WriteString("| Flag | Environment variable | Default | Description |\n") _, _ = sb.WriteString("| - | - | - | - |\n") - for _, opt := range cliOptions { + for _, opt := range visibleOpts { d := opt.Default if d != "" { d = "`" + d + "`" From f44c836d6862e85a53ef5e5a6a89814a6eb4c016 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 13:36:47 +0000 Subject: [PATCH 02/13] Fix flags and update golden files --- options.go | 1 + testdata/options.golden | 60 ++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/options.go b/options.go index f6efbc9e..fd031c62 100644 --- a/options.go +++ b/options.go @@ -405,6 +405,7 @@ func (o *Options) CLI() serpent.OptionSet { for _, o := range set { if strings.HasPrefix(o.Env, prefix) { prevOption := o + prevOption.Flag = "legacy-" + o.Flag prevOption.Env = strings.TrimPrefix(o.Env, prefix) prevOption.UseInstead = []serpent.Option{o} prevOption.Hidden = true diff --git a/testdata/options.golden b/testdata/options.golden index 53921814..1f1378b9 100644 --- a/testdata/options.golden +++ b/testdata/options.golden @@ -2,21 +2,21 @@ USAGE: envbuilder OPTIONS: - --base-image-cache-dir string, $BASE_IMAGE_CACHE_DIR + --base-image-cache-dir string, $ENVBUILDER_BASE_IMAGE_CACHE_DIR The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image. - --build-context-path string, $BUILD_CONTEXT_PATH + --build-context-path string, $ENVBUILDER_BUILD_CONTEXT_PATH Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned. - --cache-repo string, $CACHE_REPO + --cache-repo string, $ENVBUILDER_CACHE_REPO The name of the container registry to push the cache image to. If this is empty, the cache will not be pushed. - --cache-ttl-days int, $CACHE_TTL_DAYS + --cache-ttl-days int, $ENVBUILDER_CACHE_TTL_DAYS The number of days to use cached layers before expiring them. Defaults to 7 days. @@ -33,39 +33,39 @@ OPTIONS: from envbuilder will be forwarded here and will be visible in the workspace build logs. - --devcontainer-dir string, $DEVCONTAINER_DIR + --devcontainer-dir string, $ENVBUILDER_DEVCONTAINER_DIR The path to the folder containing the devcontainer.json file that will be used to build the workspace and can either be an absolute path or a path relative to the workspace folder. If not provided, defaults to `.devcontainer`. - --devcontainer-json-path string, $DEVCONTAINER_JSON_PATH + --devcontainer-json-path string, $ENVBUILDER_DEVCONTAINER_JSON_PATH The path to a devcontainer.json file that is either an absolute path or a path relative to DevcontainerDir. This can be used in cases where one wants to substitute an edited devcontainer.json file for the one that exists in the repo. - --docker-config-base64 string, $DOCKER_CONFIG_BASE64 + --docker-config-base64 string, $ENVBUILDER_DOCKER_CONFIG_BASE64 The base64 encoded Docker config file that will be used to pull images from private container registries. - --dockerfile-path string, $DOCKERFILE_PATH + --dockerfile-path string, $ENVBUILDER_DOCKERFILE_PATH The relative path to the Dockerfile that will be used to build the workspace. This is an alternative to using a devcontainer that some might find simpler. - --exit-on-build-failure bool, $EXIT_ON_BUILD_FAILURE + --exit-on-build-failure bool, $ENVBUILDER_EXIT_ON_BUILD_FAILURE Terminates the container upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error. - --export-env-file string, $EXPORT_ENV_FILE + --export-env-file string, $ENVBUILDER_EXPORT_ENV_FILE Optional file path to a .env file where envbuilder will dump environment variables from devcontainer.json and the built container image. - --fallback-image string, $FALLBACK_IMAGE + --fallback-image string, $ENVBUILDER_FALLBACK_IMAGE Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a @@ -73,78 +73,78 @@ OPTIONS: ExitOnBuildFailure to true to halt the container if the build faces an issue. - --force-safe bool, $FORCE_SAFE + --force-safe bool, $ENVBUILDER_FORCE_SAFE Ignores any filesystem safety checks. This could cause serious harm to your system! This is used in cases where bypass is needed to unblock customers. - --git-clone-depth int, $GIT_CLONE_DEPTH + --git-clone-depth int, $ENVBUILDER_GIT_CLONE_DEPTH The depth to use when cloning the Git repository. - --git-clone-single-branch bool, $GIT_CLONE_SINGLE_BRANCH + --git-clone-single-branch bool, $ENVBUILDER_GIT_CLONE_SINGLE_BRANCH Clone only a single branch of the Git repository. - --git-http-proxy-url string, $GIT_HTTP_PROXY_URL + --git-http-proxy-url string, $ENVBUILDER_GIT_HTTP_PROXY_URL The URL for the HTTP proxy. This is optional. - --git-password string, $GIT_PASSWORD + --git-password string, $ENVBUILDER_GIT_PASSWORD The password to use for Git authentication. This is optional. - --git-ssh-private-key-path string, $GIT_SSH_PRIVATE_KEY_PATH + --git-ssh-private-key-path string, $ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH Path to an SSH private key to be used for Git authentication. - --git-url string, $GIT_URL + --git-url string, $ENVBUILDER_GIT_URL The URL of the Git repository to clone. This is optional. - --git-username string, $GIT_USERNAME + --git-username string, $ENVBUILDER_GIT_USERNAME The username to use for Git authentication. This is optional. - --ignore-paths string-array, $IGNORE_PATHS (default: /var/run) + --ignore-paths string-array, $ENVBUILDER_IGNORE_PATHS (default: /var/run) The comma separated list of paths to ignore when building the workspace. - --init-args string, $INIT_ARGS + --init-args string, $ENVBUILDER_INIT_ARGS The arguments to pass to the init command. They are split according to /bin/sh rules with https://github.com/kballard/go-shellquote. - --init-command string, $INIT_COMMAND (default: /bin/sh) + --init-command string, $ENVBUILDER_INIT_COMMAND (default: /bin/sh) The command to run to initialize the workspace. - --init-script string, $INIT_SCRIPT (default: sleep infinity) + --init-script string, $ENVBUILDER_INIT_SCRIPT (default: sleep infinity) The script to run to initialize the workspace. - --insecure bool, $INSECURE + --insecure bool, $ENVBUILDER_INSECURE Bypass TLS verification when cloning and pulling from container registries. - --layer-cache-dir string, $LAYER_CACHE_DIR + --layer-cache-dir string, $ENVBUILDER_LAYER_CACHE_DIR The path to a directory where built layers will be stored. This spawns an in-memory registry to serve the layers from. - --post-start-script-path string, $POST_START_SCRIPT_PATH + --post-start-script-path string, $ENVBUILDER_POST_START_SCRIPT_PATH The path to a script that will be created by envbuilder based on the postStartCommand in devcontainer.json, if any is specified (otherwise the script is not created). If this is set, the specified InitCommand should check for the presence of this script and execute it after successful startup. - --setup-script string, $SETUP_SCRIPT + --setup-script string, $ENVBUILDER_SETUP_SCRIPT The script to run before the init script. It runs as the root user regardless of the user specified in the devcontainer.json file. SetupScript is ran as the root user prior to the init script. It is used to configure envbuilder dynamically during the runtime. e.g. specifying whether to start systemd or tiny init for PID 1. - --skip-rebuild bool, $SKIP_REBUILD + --skip-rebuild bool, $ENVBUILDER_SKIP_REBUILD Skip building if the MagicFile exists. This is used to skip building when a container is restarting. e.g. docker stop -> docker start This value can always be set to true - even if the container is being started for the first time. - --ssl-cert-base64 string, $SSL_CERT_BASE64 + --ssl-cert-base64 string, $ENVBUILDER_SSL_CERT_BASE64 The content of an SSL cert file. This is useful for self-signed certificates. - --workspace-folder string, $WORKSPACE_FOLDER + --workspace-folder string, $ENVBUILDER_WORKSPACE_FOLDER The path to the workspace folder that will be built. This is optional. From 90eeceb1d216b44b2fec609116be3864990fa3a6 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 14:01:05 +0000 Subject: [PATCH 03/13] Apply marcin suggestions --- options.go | 110 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/options.go b/options.go index fd031c62..6eddb65c 100644 --- a/options.go +++ b/options.go @@ -140,14 +140,15 @@ type Options struct { CoderAgentSubsystem []string } +const prefix = "ENVBUILDER_" + // Generate CLI options for the envbuilder command. func (o *Options) CLI() serpent.OptionSet { - const prefix = "ENVBUILDER_" - set := serpent.OptionSet{ + options := serpent.OptionSet{ { Flag: "setup-script", - Env: prefix + "SETUP_SCRIPT", + Env: withPrefix("SETUP_SCRIPT"), Value: serpent.StringOf(&o.SetupScript), Description: "The script to run before the init script. It runs as " + "the root user regardless of the user specified in the devcontainer.json " + @@ -157,21 +158,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "init-script", - Env: prefix + "INIT_SCRIPT", + Env: withPrefix("INIT_SCRIPT"), Default: "sleep infinity", Value: serpent.StringOf(&o.InitScript), Description: "The script to run to initialize the workspace.", }, { Flag: "init-command", - Env: prefix + "INIT_COMMAND", + Env: withPrefix("INIT_COMMAND"), Default: "/bin/sh", Value: serpent.StringOf(&o.InitCommand), Description: "The command to run to initialize the workspace.", }, { Flag: "init-args", - Env: prefix + "INIT_ARGS", + Env: withPrefix("INIT_ARGS"), Value: serpent.StringOf(&o.InitArgs), Description: "The arguments to pass to the init command. They are " + "split according to /bin/sh rules with " + @@ -179,14 +180,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-repo", - Env: prefix + "CACHE_REPO", + Env: withPrefix("CACHE_REPO"), Value: serpent.StringOf(&o.CacheRepo), Description: "The name of the container registry to push the cache " + "image to. If this is empty, the cache will not be pushed.", }, { Flag: "base-image-cache-dir", - Env: prefix + "BASE_IMAGE_CACHE_DIR", + Env: withPrefix("BASE_IMAGE_CACHE_DIR"), Value: serpent.StringOf(&o.BaseImageCacheDir), Description: "The path to a directory where the base image " + "can be found. This should be a read-only directory solely mounted " + @@ -194,7 +195,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "layer-cache-dir", - Env: prefix + "LAYER_CACHE_DIR", + Env: withPrefix("LAYER_CACHE_DIR"), Value: serpent.StringOf(&o.LayerCacheDir), Description: "The path to a directory where built layers will " + "be stored. This spawns an in-memory registry to serve the layers " + @@ -202,7 +203,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-dir", - Env: prefix + "DEVCONTAINER_DIR", + Env: withPrefix("DEVCONTAINER_DIR"), Value: serpent.StringOf(&o.DevcontainerDir), Description: "The path to the folder containing the " + "devcontainer.json file that will be used to build the workspace " + @@ -211,7 +212,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-json-path", - Env: prefix + "DEVCONTAINER_JSON_PATH", + Env: withPrefix("DEVCONTAINER_JSON_PATH"), Value: serpent.StringOf(&o.DevcontainerJSONPath), Description: "The path to a devcontainer.json file that " + "is either an absolute path or a path relative to DevcontainerDir. " + @@ -220,7 +221,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "dockerfile-path", - Env: prefix + "DOCKERFILE_PATH", + Env: withPrefix("DOCKERFILE_PATH"), Value: serpent.StringOf(&o.DockerfilePath), Description: "The relative path to the Dockerfile that will " + "be used to build the workspace. This is an alternative to using " + @@ -228,7 +229,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "build-context-path", - Env: prefix + "BUILD_CONTEXT_PATH", + Env: withPrefix("BUILD_CONTEXT_PATH"), Value: serpent.StringOf(&o.BuildContextPath), Description: "Can be specified when a DockerfilePath is " + "specified outside the base WorkspaceFolder. This path MUST be " + @@ -236,21 +237,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-ttl-days", - Env: prefix + "CACHE_TTL_DAYS", + Env: withPrefix("CACHE_TTL_DAYS"), Value: serpent.Int64Of(&o.CacheTTLDays), Description: "The number of days to use cached layers before " + "expiring them. Defaults to 7 days.", }, { Flag: "docker-config-base64", - Env: prefix + "DOCKER_CONFIG_BASE64", + Env: withPrefix("DOCKER_CONFIG_BASE64"), Value: serpent.StringOf(&o.DockerConfigBase64), Description: "The base64 encoded Docker config file that " + "will be used to pull images from private container registries.", }, { Flag: "fallback-image", - Env: prefix + "FALLBACK_IMAGE", + Env: withPrefix("FALLBACK_IMAGE"), Value: serpent.StringOf(&o.FallbackImage), Description: "Specifies an alternative image to use when neither " + "an image is declared in the devcontainer.json file nor a Dockerfile " + @@ -261,7 +262,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "exit-on-build-failure", - Env: prefix + "EXIT_ON_BUILD_FAILURE", + Env: withPrefix("EXIT_ON_BUILD_FAILURE"), Value: serpent.BoolOf(&o.ExitOnBuildFailure), Description: "Terminates the container upon a build failure. " + "This is handy when preferring the FALLBACK_IMAGE in cases where " + @@ -270,7 +271,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "force-safe", - Env: prefix + "FORCE_SAFE", + Env: withPrefix("FORCE_SAFE"), Value: serpent.BoolOf(&o.ForceSafe), Description: "Ignores any filesystem safety checks. This could cause " + "serious harm to your system! This is used in cases where bypass " + @@ -278,14 +279,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "insecure", - Env: prefix + "INSECURE", + Env: withPrefix("INSECURE"), Value: serpent.BoolOf(&o.Insecure), Description: "Bypass TLS verification when cloning and pulling from " + "container registries.", }, { Flag: "ignore-paths", - Env: prefix + "IGNORE_PATHS", + Env: withPrefix("IGNORE_PATHS"), Value: serpent.StringArrayOf(&o.IgnorePaths), Default: "/var/run", Description: "The comma separated list of paths to ignore when " + @@ -293,7 +294,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "skip-rebuild", - Env: prefix + "SKIP_REBUILD", + Env: withPrefix("SKIP_REBUILD"), Value: serpent.BoolOf(&o.SkipRebuild), Description: "Skip building if the MagicFile exists. This is used " + "to skip building when a container is restarting. e.g. docker stop -> " + @@ -302,63 +303,63 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "git-url", - Env: prefix + "GIT_URL", + Env: withPrefix("GIT_URL"), Value: serpent.StringOf(&o.GitURL), Description: "The URL of the Git repository to clone. This is optional.", }, { Flag: "git-clone-depth", - Env: prefix + "GIT_CLONE_DEPTH", + Env: withPrefix("GIT_CLONE_DEPTH"), Value: serpent.Int64Of(&o.GitCloneDepth), Description: "The depth to use when cloning the Git repository.", }, { Flag: "git-clone-single-branch", - Env: prefix + "GIT_CLONE_SINGLE_BRANCH", + Env: withPrefix("GIT_CLONE_SINGLE_BRANCH"), Value: serpent.BoolOf(&o.GitCloneSingleBranch), Description: "Clone only a single branch of the Git repository.", }, { Flag: "git-username", - Env: prefix + "GIT_USERNAME", + Env: withPrefix("GIT_USERNAME"), Value: serpent.StringOf(&o.GitUsername), Description: "The username to use for Git authentication. This is optional.", }, { Flag: "git-password", - Env: prefix + "GIT_PASSWORD", + Env: withPrefix("GIT_PASSWORD"), Value: serpent.StringOf(&o.GitPassword), Description: "The password to use for Git authentication. This is optional.", }, { Flag: "git-ssh-private-key-path", - Env: prefix + "GIT_SSH_PRIVATE_KEY_PATH", + Env: withPrefix("GIT_SSH_PRIVATE_KEY_PATH"), Value: serpent.StringOf(&o.GitSSHPrivateKeyPath), Description: "Path to an SSH private key to be used for Git authentication.", }, { Flag: "git-http-proxy-url", - Env: prefix + "GIT_HTTP_PROXY_URL", + Env: withPrefix("GIT_HTTP_PROXY_URL"), Value: serpent.StringOf(&o.GitHTTPProxyURL), Description: "The URL for the HTTP proxy. This is optional.", }, { Flag: "workspace-folder", - Env: prefix + "WORKSPACE_FOLDER", + Env: withPrefix("WORKSPACE_FOLDER"), Value: serpent.StringOf(&o.WorkspaceFolder), Description: "The path to the workspace folder that will " + "be built. This is optional.", }, { Flag: "ssl-cert-base64", - Env: prefix + "SSL_CERT_BASE64", + Env: withPrefix("SSL_CERT_BASE64"), Value: serpent.StringOf(&o.SSLCertBase64), Description: "The content of an SSL cert file. This is useful " + "for self-signed certificates.", }, { Flag: "export-env-file", - Env: prefix + "EXPORT_ENV_FILE", + Env: withPrefix("EXPORT_ENV_FILE"), Value: serpent.StringOf(&o.ExportEnvFile), Description: "Optional file path to a .env file where " + "envbuilder will dump environment variables from devcontainer.json " + @@ -366,7 +367,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "post-start-script-path", - Env: prefix + "POST_START_SCRIPT_PATH", + Env: withPrefix("POST_START_SCRIPT_PATH"), Value: serpent.StringOf(&o.PostStartScriptPath), Description: "The path to a script that will be created " + "by envbuilder based on the postStartCommand in devcontainer.json, " + @@ -400,38 +401,42 @@ func (o *Options) CLI() serpent.OptionSet { // Add options without the prefix for backward compatibility. These options // are marked as deprecated and will be removed in future versions. Note: - // Future versions will require the 'ENVBUILDER' prefix for default + // Future versions will require the 'ENVBUILDER_' prefix for default // environment variables. - for _, o := range set { + options = supportLegacyEnvWithoutPrefixes(options) + + return options +} + +func withPrefix(str string) string { + return prefix + str +} + +func supportLegacyEnvWithoutPrefixes(opts serpent.OptionSet) serpent.OptionSet { + withLegacyOpts := opts + + for _, o := range opts { if strings.HasPrefix(o.Env, prefix) { prevOption := o prevOption.Flag = "legacy-" + o.Flag prevOption.Env = strings.TrimPrefix(o.Env, prefix) prevOption.UseInstead = []serpent.Option{o} prevOption.Hidden = true - set = append(set, prevOption) + withLegacyOpts = append(withLegacyOpts, prevOption) } } - return set + return withLegacyOpts } func (o *Options) Markdown() string { - var visibleOpts serpent.OptionSet - - // We don't want to include deprecated opts in the README. - for _, opt := range o.CLI() { - isDeprecated := len(opt.UseInstead) > 0 - if !isDeprecated { - visibleOpts = append(visibleOpts, opt) - } - } + cliOptions := skipDeprecatedOptions(o.CLI()) var sb strings.Builder _, _ = sb.WriteString("| Flag | Environment variable | Default | Description |\n") _, _ = sb.WriteString("| - | - | - | - |\n") - for _, opt := range visibleOpts { + for _, opt := range cliOptions { d := opt.Default if d != "" { d = "`" + d + "`" @@ -449,3 +454,16 @@ func (o *Options) Markdown() string { return sb.String() } + +func skipDeprecatedOptions(options []serpent.Option) []serpent.Option { + var activeOptions []serpent.Option + + for _, opt := range options { + isDeprecated := len(opt.UseInstead) > 0 + if !isDeprecated { + activeOptions = append(activeOptions, opt) + } + } + + return activeOptions +} From b9fa850f54f723079946892bf7533a67cd45d808 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 14:04:51 +0000 Subject: [PATCH 04/13] Fix fmt --- options.go | 1 - 1 file changed, 1 deletion(-) diff --git a/options.go b/options.go index 6eddb65c..2316b682 100644 --- a/options.go +++ b/options.go @@ -144,7 +144,6 @@ const prefix = "ENVBUILDER_" // Generate CLI options for the envbuilder command. func (o *Options) CLI() serpent.OptionSet { - options := serpent.OptionSet{ { Flag: "setup-script", From 482a99351e3622bcefdd423aaceeb3d4fb299d70 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 14:18:56 +0000 Subject: [PATCH 05/13] Fix tests --- options.go | 70 ++++++++++++++++++++++++------------------------- options_test.go | 20 +++++++------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/options.go b/options.go index 2316b682..4f8be7ad 100644 --- a/options.go +++ b/options.go @@ -140,14 +140,14 @@ type Options struct { CoderAgentSubsystem []string } -const prefix = "ENVBUILDER_" +const optionPrefix = "ENVBUILDER_" // Generate CLI options for the envbuilder command. func (o *Options) CLI() serpent.OptionSet { options := serpent.OptionSet{ { Flag: "setup-script", - Env: withPrefix("SETUP_SCRIPT"), + Env: WithOptionPrefix("SETUP_SCRIPT"), Value: serpent.StringOf(&o.SetupScript), Description: "The script to run before the init script. It runs as " + "the root user regardless of the user specified in the devcontainer.json " + @@ -157,21 +157,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "init-script", - Env: withPrefix("INIT_SCRIPT"), + Env: WithOptionPrefix("INIT_SCRIPT"), Default: "sleep infinity", Value: serpent.StringOf(&o.InitScript), Description: "The script to run to initialize the workspace.", }, { Flag: "init-command", - Env: withPrefix("INIT_COMMAND"), + Env: WithOptionPrefix("INIT_COMMAND"), Default: "/bin/sh", Value: serpent.StringOf(&o.InitCommand), Description: "The command to run to initialize the workspace.", }, { Flag: "init-args", - Env: withPrefix("INIT_ARGS"), + Env: WithOptionPrefix("INIT_ARGS"), Value: serpent.StringOf(&o.InitArgs), Description: "The arguments to pass to the init command. They are " + "split according to /bin/sh rules with " + @@ -179,14 +179,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-repo", - Env: withPrefix("CACHE_REPO"), + Env: WithOptionPrefix("CACHE_REPO"), Value: serpent.StringOf(&o.CacheRepo), Description: "The name of the container registry to push the cache " + "image to. If this is empty, the cache will not be pushed.", }, { Flag: "base-image-cache-dir", - Env: withPrefix("BASE_IMAGE_CACHE_DIR"), + Env: WithOptionPrefix("BASE_IMAGE_CACHE_DIR"), Value: serpent.StringOf(&o.BaseImageCacheDir), Description: "The path to a directory where the base image " + "can be found. This should be a read-only directory solely mounted " + @@ -194,7 +194,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "layer-cache-dir", - Env: withPrefix("LAYER_CACHE_DIR"), + Env: WithOptionPrefix("LAYER_CACHE_DIR"), Value: serpent.StringOf(&o.LayerCacheDir), Description: "The path to a directory where built layers will " + "be stored. This spawns an in-memory registry to serve the layers " + @@ -202,7 +202,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-dir", - Env: withPrefix("DEVCONTAINER_DIR"), + Env: WithOptionPrefix("DEVCONTAINER_DIR"), Value: serpent.StringOf(&o.DevcontainerDir), Description: "The path to the folder containing the " + "devcontainer.json file that will be used to build the workspace " + @@ -211,7 +211,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-json-path", - Env: withPrefix("DEVCONTAINER_JSON_PATH"), + Env: WithOptionPrefix("DEVCONTAINER_JSON_PATH"), Value: serpent.StringOf(&o.DevcontainerJSONPath), Description: "The path to a devcontainer.json file that " + "is either an absolute path or a path relative to DevcontainerDir. " + @@ -220,7 +220,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "dockerfile-path", - Env: withPrefix("DOCKERFILE_PATH"), + Env: WithOptionPrefix("DOCKERFILE_PATH"), Value: serpent.StringOf(&o.DockerfilePath), Description: "The relative path to the Dockerfile that will " + "be used to build the workspace. This is an alternative to using " + @@ -228,7 +228,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "build-context-path", - Env: withPrefix("BUILD_CONTEXT_PATH"), + Env: WithOptionPrefix("BUILD_CONTEXT_PATH"), Value: serpent.StringOf(&o.BuildContextPath), Description: "Can be specified when a DockerfilePath is " + "specified outside the base WorkspaceFolder. This path MUST be " + @@ -236,21 +236,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-ttl-days", - Env: withPrefix("CACHE_TTL_DAYS"), + Env: WithOptionPrefix("CACHE_TTL_DAYS"), Value: serpent.Int64Of(&o.CacheTTLDays), Description: "The number of days to use cached layers before " + "expiring them. Defaults to 7 days.", }, { Flag: "docker-config-base64", - Env: withPrefix("DOCKER_CONFIG_BASE64"), + Env: WithOptionPrefix("DOCKER_CONFIG_BASE64"), Value: serpent.StringOf(&o.DockerConfigBase64), Description: "The base64 encoded Docker config file that " + "will be used to pull images from private container registries.", }, { Flag: "fallback-image", - Env: withPrefix("FALLBACK_IMAGE"), + Env: WithOptionPrefix("FALLBACK_IMAGE"), Value: serpent.StringOf(&o.FallbackImage), Description: "Specifies an alternative image to use when neither " + "an image is declared in the devcontainer.json file nor a Dockerfile " + @@ -261,7 +261,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "exit-on-build-failure", - Env: withPrefix("EXIT_ON_BUILD_FAILURE"), + Env: WithOptionPrefix("EXIT_ON_BUILD_FAILURE"), Value: serpent.BoolOf(&o.ExitOnBuildFailure), Description: "Terminates the container upon a build failure. " + "This is handy when preferring the FALLBACK_IMAGE in cases where " + @@ -270,7 +270,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "force-safe", - Env: withPrefix("FORCE_SAFE"), + Env: WithOptionPrefix("FORCE_SAFE"), Value: serpent.BoolOf(&o.ForceSafe), Description: "Ignores any filesystem safety checks. This could cause " + "serious harm to your system! This is used in cases where bypass " + @@ -278,14 +278,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "insecure", - Env: withPrefix("INSECURE"), + Env: WithOptionPrefix("INSECURE"), Value: serpent.BoolOf(&o.Insecure), Description: "Bypass TLS verification when cloning and pulling from " + "container registries.", }, { Flag: "ignore-paths", - Env: withPrefix("IGNORE_PATHS"), + Env: WithOptionPrefix("IGNORE_PATHS"), Value: serpent.StringArrayOf(&o.IgnorePaths), Default: "/var/run", Description: "The comma separated list of paths to ignore when " + @@ -293,7 +293,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "skip-rebuild", - Env: withPrefix("SKIP_REBUILD"), + Env: WithOptionPrefix("SKIP_REBUILD"), Value: serpent.BoolOf(&o.SkipRebuild), Description: "Skip building if the MagicFile exists. This is used " + "to skip building when a container is restarting. e.g. docker stop -> " + @@ -302,63 +302,63 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "git-url", - Env: withPrefix("GIT_URL"), + Env: WithOptionPrefix("GIT_URL"), Value: serpent.StringOf(&o.GitURL), Description: "The URL of the Git repository to clone. This is optional.", }, { Flag: "git-clone-depth", - Env: withPrefix("GIT_CLONE_DEPTH"), + Env: WithOptionPrefix("GIT_CLONE_DEPTH"), Value: serpent.Int64Of(&o.GitCloneDepth), Description: "The depth to use when cloning the Git repository.", }, { Flag: "git-clone-single-branch", - Env: withPrefix("GIT_CLONE_SINGLE_BRANCH"), + Env: WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), Value: serpent.BoolOf(&o.GitCloneSingleBranch), Description: "Clone only a single branch of the Git repository.", }, { Flag: "git-username", - Env: withPrefix("GIT_USERNAME"), + Env: WithOptionPrefix("GIT_USERNAME"), Value: serpent.StringOf(&o.GitUsername), Description: "The username to use for Git authentication. This is optional.", }, { Flag: "git-password", - Env: withPrefix("GIT_PASSWORD"), + Env: WithOptionPrefix("GIT_PASSWORD"), Value: serpent.StringOf(&o.GitPassword), Description: "The password to use for Git authentication. This is optional.", }, { Flag: "git-ssh-private-key-path", - Env: withPrefix("GIT_SSH_PRIVATE_KEY_PATH"), + Env: WithOptionPrefix("GIT_SSH_PRIVATE_KEY_PATH"), Value: serpent.StringOf(&o.GitSSHPrivateKeyPath), Description: "Path to an SSH private key to be used for Git authentication.", }, { Flag: "git-http-proxy-url", - Env: withPrefix("GIT_HTTP_PROXY_URL"), + Env: WithOptionPrefix("GIT_HTTP_PROXY_URL"), Value: serpent.StringOf(&o.GitHTTPProxyURL), Description: "The URL for the HTTP proxy. This is optional.", }, { Flag: "workspace-folder", - Env: withPrefix("WORKSPACE_FOLDER"), + Env: WithOptionPrefix("WORKSPACE_FOLDER"), Value: serpent.StringOf(&o.WorkspaceFolder), Description: "The path to the workspace folder that will " + "be built. This is optional.", }, { Flag: "ssl-cert-base64", - Env: withPrefix("SSL_CERT_BASE64"), + Env: WithOptionPrefix("SSL_CERT_BASE64"), Value: serpent.StringOf(&o.SSLCertBase64), Description: "The content of an SSL cert file. This is useful " + "for self-signed certificates.", }, { Flag: "export-env-file", - Env: withPrefix("EXPORT_ENV_FILE"), + Env: WithOptionPrefix("EXPORT_ENV_FILE"), Value: serpent.StringOf(&o.ExportEnvFile), Description: "Optional file path to a .env file where " + "envbuilder will dump environment variables from devcontainer.json " + @@ -366,7 +366,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "post-start-script-path", - Env: withPrefix("POST_START_SCRIPT_PATH"), + Env: WithOptionPrefix("POST_START_SCRIPT_PATH"), Value: serpent.StringOf(&o.PostStartScriptPath), Description: "The path to a script that will be created " + "by envbuilder based on the postStartCommand in devcontainer.json, " + @@ -407,18 +407,18 @@ func (o *Options) CLI() serpent.OptionSet { return options } -func withPrefix(str string) string { - return prefix + str +func WithOptionPrefix(str string) string { + return optionPrefix + str } func supportLegacyEnvWithoutPrefixes(opts serpent.OptionSet) serpent.OptionSet { withLegacyOpts := opts for _, o := range opts { - if strings.HasPrefix(o.Env, prefix) { + if strings.HasPrefix(o.Env, optionPrefix) { prevOption := o prevOption.Flag = "legacy-" + o.Flag - prevOption.Env = strings.TrimPrefix(o.Env, prefix) + prevOption.Env = strings.TrimPrefix(o.Env, optionPrefix) prevOption.UseInstead = []serpent.Option{o} prevOption.Hidden = true withLegacyOpts = append(withLegacyOpts, prevOption) diff --git a/options_test.go b/options_test.go index af510619..c4e8b99f 100644 --- a/options_test.go +++ b/options_test.go @@ -15,50 +15,50 @@ import ( func TestEnvOptionParsing(t *testing.T) { t.Run("string", func(t *testing.T) { const val = "setup.sh" - t.Setenv("SETUP_SCRIPT", val) + t.Setenv(envbuilder.WithOptionPrefix("SETUP_SCRIPT"), val) o := runCLI() require.Equal(t, o.SetupScript, val) }) t.Run("int", func(t *testing.T) { - t.Setenv("CACHE_TTL_DAYS", "7") + t.Setenv(envbuilder.WithOptionPrefix("CACHE_TTL_DAYS"), "7") o := runCLI() require.Equal(t, o.CacheTTLDays, int64(7)) }) t.Run("string array", func(t *testing.T) { - t.Setenv("IGNORE_PATHS", "/var,/temp") + t.Setenv(envbuilder.WithOptionPrefix("IGNORE_PATHS"), "/var,/temp") o := runCLI() require.Equal(t, o.IgnorePaths, []string{"/var", "/temp"}) }) t.Run("bool", func(t *testing.T) { t.Run("lowercase", func(t *testing.T) { - t.Setenv("SKIP_REBUILD", "true") - t.Setenv("GIT_CLONE_SINGLE_BRANCH", "false") + t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "true") + t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "false") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("uppercase", func(t *testing.T) { - t.Setenv("SKIP_REBUILD", "TRUE") - t.Setenv("GIT_CLONE_SINGLE_BRANCH", "FALSE") + t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "TRUE") + t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "FALSE") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("numeric", func(t *testing.T) { - t.Setenv("SKIP_REBUILD", "1") - t.Setenv("GIT_CLONE_SINGLE_BRANCH", "0") + t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "1") + t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "0") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("empty", func(t *testing.T) { - t.Setenv("GIT_CLONE_SINGLE_BRANCH", "") + t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "") o := runCLI() require.False(t, o.GitCloneSingleBranch) }) From 2c54629c8b638db9d35532add4d647ccb3e817dd Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 14:34:04 +0000 Subject: [PATCH 06/13] Fix unit tests --- options.go | 1 + 1 file changed, 1 insertion(+) diff --git a/options.go b/options.go index 4f8be7ad..f9b8746c 100644 --- a/options.go +++ b/options.go @@ -421,6 +421,7 @@ func supportLegacyEnvWithoutPrefixes(opts serpent.OptionSet) serpent.OptionSet { prevOption.Env = strings.TrimPrefix(o.Env, optionPrefix) prevOption.UseInstead = []serpent.Option{o} prevOption.Hidden = true + prevOption.Default = "" withLegacyOpts = append(withLegacyOpts, prevOption) } } From dfebd0220aa5900777a9c658913feee3b49e0346 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 16:09:13 +0000 Subject: [PATCH 07/13] Fix integration test env variables --- integration/integration_test.go | 134 ++++++++++++++++---------------- options.go | 70 ++++++++--------- options_test.go | 20 ++--- 3 files changed, 114 insertions(+), 110 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index a8c12640..ebff7921 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -55,9 +55,9 @@ func TestForceSafe(t *testing.T) { }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "KANIKO_DIR=/not/envbuilder", - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("KANIKO_DIR", "/not/envbuilder"), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.ErrorContains(t, err, "delete filesystem: safety check failed") }) @@ -71,10 +71,10 @@ func TestForceSafe(t *testing.T) { }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "KANIKO_DIR=/not/envbuilder", - "FORCE_SAFE=true", - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("KANIKO_DIR", "/not/envbuilder"), + envbuilderEnv("FORCE_SAGE", "true"), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) }) @@ -90,7 +90,7 @@ func TestFailsGitAuth(t *testing.T) { password: "testing", }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.ErrorContains(t, err, "authentication required") } @@ -105,10 +105,10 @@ func TestSucceedsGitAuth(t *testing.T) { password: "testing", }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "GIT_USERNAME=kyle", - "GIT_PASSWORD=testing", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("GIT_USERNAME", "kyle"), + envbuilderEnv("GIT_PASSWORD", "testing"), }}) require.NoError(t, err) gitConfig := execContainer(t, ctr, "cat /workspaces/.git/config") @@ -129,8 +129,8 @@ func TestSucceedsGitAuthInURL(t *testing.T) { require.NoError(t, err) u.User = url.UserPassword("kyle", "testing") ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + u.String(), - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_UR", u.String()), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) gitConfig := execContainer(t, ctr, "cat /workspaces/.git/config") @@ -207,7 +207,7 @@ func TestBuildFromDevcontainerWithFeatures(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.NoError(t, err) @@ -229,9 +229,9 @@ func TestBuildFromDockerfile(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "DOCKER_CONFIG_BASE64=" + base64.StdEncoding.EncodeToString([]byte(`{"experimental": "enabled"}`)), + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("DOCKER_CONFIG_BASE64", base64.StdEncoding.EncodeToString([]byte(`{"experimental": "enabled"}`))), }}) require.NoError(t, err) @@ -251,8 +251,8 @@ func TestBuildPrintBuildOutput(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) @@ -283,8 +283,8 @@ func TestBuildIgnoreVarRunSecrets(t *testing.T) { require.NoError(t, err) ctr, err := runEnvbuilder(t, options{ env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }, binds: []string{fmt.Sprintf("%s:/var/run/secrets", dir)}, }) @@ -302,9 +302,9 @@ func TestBuildWithSetupScript(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "SETUP_SCRIPT=echo \"INIT_ARGS=-c 'echo hi > /wow && sleep infinity'\" >> $ENVBUILDER_ENV", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("SETUP_SCRIPT", "echo \"INIT_ARGS=-c 'echo hi > /wow && sleep infinity'\" >> $ENVBUILDER_ENV"), }}) require.NoError(t, err) @@ -328,8 +328,8 @@ func TestBuildFromDevcontainerInCustomPath(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DEVCONTAINER_DIR=.devcontainer/custom", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DEVCONTAINER_DIR", ".devcontainer/custom"), }}) require.NoError(t, err) @@ -353,7 +353,7 @@ func TestBuildFromDevcontainerInSubfolder(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.NoError(t, err) @@ -377,7 +377,7 @@ func TestBuildFromDevcontainerInRoot(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.NoError(t, err) @@ -393,12 +393,12 @@ func TestBuildCustomCertificates(t *testing.T) { tls: true, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "SSL_CERT_BASE64=" + base64.StdEncoding.EncodeToString(pem.EncodeToMemory(&pem.Block{ + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("SSL_CERT_BASE64", base64.StdEncoding.EncodeToString(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: srv.TLS.Certificates[0].Certificate[0], - })), + }))), }}) require.NoError(t, err) @@ -414,9 +414,9 @@ func TestBuildStopStartCached(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "SKIP_REBUILD=true", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("SKIP_REBUILD", "true"), }}) require.NoError(t, err) @@ -445,7 +445,7 @@ func TestCloneFailsFallback(t *testing.T) { t.Run("BadRepo", func(t *testing.T) { t.Parallel() _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=bad-value", + envbuilderEnv("GIT_URL", "bad-value"), }}) require.ErrorContains(t, err, envbuilder.ErrNoFallbackImage.Error()) }) @@ -462,8 +462,8 @@ func TestBuildFailsFallback(t *testing.T) { }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.ErrorContains(t, err, envbuilder.ErrNoFallbackImage.Error()) require.ErrorContains(t, err, "dockerfile parse error") @@ -478,8 +478,8 @@ RUN exit 1`, }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.ErrorContains(t, err, envbuilder.ErrNoFallbackImage.Error()) }) @@ -492,7 +492,7 @@ RUN exit 1`, }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.ErrorContains(t, err, envbuilder.ErrNoFallbackImage.Error()) }) @@ -504,8 +504,8 @@ RUN exit 1`, }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "FALLBACK_IMAGE=" + testImageAlpine, + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("FALLBACK_IMAGE", testImageAlpine), }}) require.NoError(t, err) @@ -522,11 +522,11 @@ func TestExitBuildOnFailure(t *testing.T) { }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "FALLBACK_IMAGE=" + testImageAlpine, + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("FALLBACK_IMAGE", testImageAlpine), // Ensures that the fallback doesn't work when an image is specified. - "EXIT_ON_BUILD_FAILURE=true", + envbuilderEnv("EXIT_ON_BUILD_FAILURE", "true"), }}) require.ErrorContains(t, err, "parsing dockerfile") } @@ -556,8 +556,8 @@ func TestContainerEnv(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "EXPORT_ENV_FILE=/env", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("EXPORT_ENV_FILE", "/env"), }}) require.NoError(t, err) @@ -593,7 +593,7 @@ func TestLifecycleScripts(t *testing.T) { }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, + envbuilderEnv("GIT_URL", srv.URL), }}) require.NoError(t, err) @@ -632,9 +632,9 @@ USER nobody`, }, }) ctr, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "POST_START_SCRIPT_PATH=/tmp/post-start.sh", - "INIT_COMMAND=/bin/init.sh", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("POST_START_SCRIPT_PATH", "/tmp/post-start.sh"), + envbuilderEnv("INIT_COMMAND", "/bin/init.sh"), }}) require.NoError(t, err) @@ -666,8 +666,8 @@ func TestPrivateRegistry(t *testing.T) { }, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.ErrorContains(t, err, "Unauthorized") }) @@ -695,9 +695,9 @@ func TestPrivateRegistry(t *testing.T) { require.NoError(t, err) _, err = runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "DOCKER_CONFIG_BASE64=" + base64.StdEncoding.EncodeToString(config), + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("DOCKER_CONFIG_BASE64", base64.StdEncoding.EncodeToString(config)), }}) require.NoError(t, err) }) @@ -727,9 +727,9 @@ func TestPrivateRegistry(t *testing.T) { require.NoError(t, err) _, err = runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=Dockerfile", - "DOCKER_CONFIG_BASE64=" + base64.StdEncoding.EncodeToString(config), + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), + envbuilderEnv("DOCKER_CONFIG_BASE64", base64.StdEncoding.EncodeToString(config)), }}) require.ErrorContains(t, err, "Unauthorized") }) @@ -853,9 +853,9 @@ COPY %s .`, testImageAlpine, inclFile) files: tc.files, }) _, err := runEnvbuilder(t, options{env: []string{ - "GIT_URL=" + srv.URL, - "DOCKERFILE_PATH=" + tc.dockerfilePath, - "BUILD_CONTEXT_PATH=" + tc.buildContextPath, + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("DOCKERFILE_PATH", tc.dockerfilePath), + envbuilderEnv("BUILD_CONTEXT_PATH", tc.buildContextPath), }}) if tc.expectedErr == "" { @@ -1052,3 +1052,7 @@ func streamContainerLogs(t *testing.T, cli *client.Client, containerID string) ( return logChan, errChan } + +func envbuilderEnv(env string, value string) string { + return fmt.Sprintf("%s=%s", envbuilder.WithEnvPrefix(env), value) +} diff --git a/options.go b/options.go index f9b8746c..6c052691 100644 --- a/options.go +++ b/options.go @@ -140,14 +140,14 @@ type Options struct { CoderAgentSubsystem []string } -const optionPrefix = "ENVBUILDER_" +const envPrefix = "ENVBUILDER_" // Generate CLI options for the envbuilder command. func (o *Options) CLI() serpent.OptionSet { options := serpent.OptionSet{ { Flag: "setup-script", - Env: WithOptionPrefix("SETUP_SCRIPT"), + Env: WithEnvPrefix("SETUP_SCRIPT"), Value: serpent.StringOf(&o.SetupScript), Description: "The script to run before the init script. It runs as " + "the root user regardless of the user specified in the devcontainer.json " + @@ -157,21 +157,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "init-script", - Env: WithOptionPrefix("INIT_SCRIPT"), + Env: WithEnvPrefix("INIT_SCRIPT"), Default: "sleep infinity", Value: serpent.StringOf(&o.InitScript), Description: "The script to run to initialize the workspace.", }, { Flag: "init-command", - Env: WithOptionPrefix("INIT_COMMAND"), + Env: WithEnvPrefix("INIT_COMMAND"), Default: "/bin/sh", Value: serpent.StringOf(&o.InitCommand), Description: "The command to run to initialize the workspace.", }, { Flag: "init-args", - Env: WithOptionPrefix("INIT_ARGS"), + Env: WithEnvPrefix("INIT_ARGS"), Value: serpent.StringOf(&o.InitArgs), Description: "The arguments to pass to the init command. They are " + "split according to /bin/sh rules with " + @@ -179,14 +179,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-repo", - Env: WithOptionPrefix("CACHE_REPO"), + Env: WithEnvPrefix("CACHE_REPO"), Value: serpent.StringOf(&o.CacheRepo), Description: "The name of the container registry to push the cache " + "image to. If this is empty, the cache will not be pushed.", }, { Flag: "base-image-cache-dir", - Env: WithOptionPrefix("BASE_IMAGE_CACHE_DIR"), + Env: WithEnvPrefix("BASE_IMAGE_CACHE_DIR"), Value: serpent.StringOf(&o.BaseImageCacheDir), Description: "The path to a directory where the base image " + "can be found. This should be a read-only directory solely mounted " + @@ -194,7 +194,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "layer-cache-dir", - Env: WithOptionPrefix("LAYER_CACHE_DIR"), + Env: WithEnvPrefix("LAYER_CACHE_DIR"), Value: serpent.StringOf(&o.LayerCacheDir), Description: "The path to a directory where built layers will " + "be stored. This spawns an in-memory registry to serve the layers " + @@ -202,7 +202,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-dir", - Env: WithOptionPrefix("DEVCONTAINER_DIR"), + Env: WithEnvPrefix("DEVCONTAINER_DIR"), Value: serpent.StringOf(&o.DevcontainerDir), Description: "The path to the folder containing the " + "devcontainer.json file that will be used to build the workspace " + @@ -211,7 +211,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "devcontainer-json-path", - Env: WithOptionPrefix("DEVCONTAINER_JSON_PATH"), + Env: WithEnvPrefix("DEVCONTAINER_JSON_PATH"), Value: serpent.StringOf(&o.DevcontainerJSONPath), Description: "The path to a devcontainer.json file that " + "is either an absolute path or a path relative to DevcontainerDir. " + @@ -220,7 +220,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "dockerfile-path", - Env: WithOptionPrefix("DOCKERFILE_PATH"), + Env: WithEnvPrefix("DOCKERFILE_PATH"), Value: serpent.StringOf(&o.DockerfilePath), Description: "The relative path to the Dockerfile that will " + "be used to build the workspace. This is an alternative to using " + @@ -228,7 +228,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "build-context-path", - Env: WithOptionPrefix("BUILD_CONTEXT_PATH"), + Env: WithEnvPrefix("BUILD_CONTEXT_PATH"), Value: serpent.StringOf(&o.BuildContextPath), Description: "Can be specified when a DockerfilePath is " + "specified outside the base WorkspaceFolder. This path MUST be " + @@ -236,21 +236,21 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "cache-ttl-days", - Env: WithOptionPrefix("CACHE_TTL_DAYS"), + Env: WithEnvPrefix("CACHE_TTL_DAYS"), Value: serpent.Int64Of(&o.CacheTTLDays), Description: "The number of days to use cached layers before " + "expiring them. Defaults to 7 days.", }, { Flag: "docker-config-base64", - Env: WithOptionPrefix("DOCKER_CONFIG_BASE64"), + Env: WithEnvPrefix("DOCKER_CONFIG_BASE64"), Value: serpent.StringOf(&o.DockerConfigBase64), Description: "The base64 encoded Docker config file that " + "will be used to pull images from private container registries.", }, { Flag: "fallback-image", - Env: WithOptionPrefix("FALLBACK_IMAGE"), + Env: WithEnvPrefix("FALLBACK_IMAGE"), Value: serpent.StringOf(&o.FallbackImage), Description: "Specifies an alternative image to use when neither " + "an image is declared in the devcontainer.json file nor a Dockerfile " + @@ -261,7 +261,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "exit-on-build-failure", - Env: WithOptionPrefix("EXIT_ON_BUILD_FAILURE"), + Env: WithEnvPrefix("EXIT_ON_BUILD_FAILURE"), Value: serpent.BoolOf(&o.ExitOnBuildFailure), Description: "Terminates the container upon a build failure. " + "This is handy when preferring the FALLBACK_IMAGE in cases where " + @@ -270,7 +270,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "force-safe", - Env: WithOptionPrefix("FORCE_SAFE"), + Env: WithEnvPrefix("FORCE_SAFE"), Value: serpent.BoolOf(&o.ForceSafe), Description: "Ignores any filesystem safety checks. This could cause " + "serious harm to your system! This is used in cases where bypass " + @@ -278,14 +278,14 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "insecure", - Env: WithOptionPrefix("INSECURE"), + Env: WithEnvPrefix("INSECURE"), Value: serpent.BoolOf(&o.Insecure), Description: "Bypass TLS verification when cloning and pulling from " + "container registries.", }, { Flag: "ignore-paths", - Env: WithOptionPrefix("IGNORE_PATHS"), + Env: WithEnvPrefix("IGNORE_PATHS"), Value: serpent.StringArrayOf(&o.IgnorePaths), Default: "/var/run", Description: "The comma separated list of paths to ignore when " + @@ -293,7 +293,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "skip-rebuild", - Env: WithOptionPrefix("SKIP_REBUILD"), + Env: WithEnvPrefix("SKIP_REBUILD"), Value: serpent.BoolOf(&o.SkipRebuild), Description: "Skip building if the MagicFile exists. This is used " + "to skip building when a container is restarting. e.g. docker stop -> " + @@ -302,63 +302,63 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "git-url", - Env: WithOptionPrefix("GIT_URL"), + Env: WithEnvPrefix("GIT_URL"), Value: serpent.StringOf(&o.GitURL), Description: "The URL of the Git repository to clone. This is optional.", }, { Flag: "git-clone-depth", - Env: WithOptionPrefix("GIT_CLONE_DEPTH"), + Env: WithEnvPrefix("GIT_CLONE_DEPTH"), Value: serpent.Int64Of(&o.GitCloneDepth), Description: "The depth to use when cloning the Git repository.", }, { Flag: "git-clone-single-branch", - Env: WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), + Env: WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), Value: serpent.BoolOf(&o.GitCloneSingleBranch), Description: "Clone only a single branch of the Git repository.", }, { Flag: "git-username", - Env: WithOptionPrefix("GIT_USERNAME"), + Env: WithEnvPrefix("GIT_USERNAME"), Value: serpent.StringOf(&o.GitUsername), Description: "The username to use for Git authentication. This is optional.", }, { Flag: "git-password", - Env: WithOptionPrefix("GIT_PASSWORD"), + Env: WithEnvPrefix("GIT_PASSWORD"), Value: serpent.StringOf(&o.GitPassword), Description: "The password to use for Git authentication. This is optional.", }, { Flag: "git-ssh-private-key-path", - Env: WithOptionPrefix("GIT_SSH_PRIVATE_KEY_PATH"), + Env: WithEnvPrefix("GIT_SSH_PRIVATE_KEY_PATH"), Value: serpent.StringOf(&o.GitSSHPrivateKeyPath), Description: "Path to an SSH private key to be used for Git authentication.", }, { Flag: "git-http-proxy-url", - Env: WithOptionPrefix("GIT_HTTP_PROXY_URL"), + Env: WithEnvPrefix("GIT_HTTP_PROXY_URL"), Value: serpent.StringOf(&o.GitHTTPProxyURL), Description: "The URL for the HTTP proxy. This is optional.", }, { Flag: "workspace-folder", - Env: WithOptionPrefix("WORKSPACE_FOLDER"), + Env: WithEnvPrefix("WORKSPACE_FOLDER"), Value: serpent.StringOf(&o.WorkspaceFolder), Description: "The path to the workspace folder that will " + "be built. This is optional.", }, { Flag: "ssl-cert-base64", - Env: WithOptionPrefix("SSL_CERT_BASE64"), + Env: WithEnvPrefix("SSL_CERT_BASE64"), Value: serpent.StringOf(&o.SSLCertBase64), Description: "The content of an SSL cert file. This is useful " + "for self-signed certificates.", }, { Flag: "export-env-file", - Env: WithOptionPrefix("EXPORT_ENV_FILE"), + Env: WithEnvPrefix("EXPORT_ENV_FILE"), Value: serpent.StringOf(&o.ExportEnvFile), Description: "Optional file path to a .env file where " + "envbuilder will dump environment variables from devcontainer.json " + @@ -366,7 +366,7 @@ func (o *Options) CLI() serpent.OptionSet { }, { Flag: "post-start-script-path", - Env: WithOptionPrefix("POST_START_SCRIPT_PATH"), + Env: WithEnvPrefix("POST_START_SCRIPT_PATH"), Value: serpent.StringOf(&o.PostStartScriptPath), Description: "The path to a script that will be created " + "by envbuilder based on the postStartCommand in devcontainer.json, " + @@ -407,18 +407,18 @@ func (o *Options) CLI() serpent.OptionSet { return options } -func WithOptionPrefix(str string) string { - return optionPrefix + str +func WithEnvPrefix(str string) string { + return envPrefix + str } func supportLegacyEnvWithoutPrefixes(opts serpent.OptionSet) serpent.OptionSet { withLegacyOpts := opts for _, o := range opts { - if strings.HasPrefix(o.Env, optionPrefix) { + if strings.HasPrefix(o.Env, envPrefix) { prevOption := o prevOption.Flag = "legacy-" + o.Flag - prevOption.Env = strings.TrimPrefix(o.Env, optionPrefix) + prevOption.Env = strings.TrimPrefix(o.Env, envPrefix) prevOption.UseInstead = []serpent.Option{o} prevOption.Hidden = true prevOption.Default = "" diff --git a/options_test.go b/options_test.go index c4e8b99f..df29880f 100644 --- a/options_test.go +++ b/options_test.go @@ -15,50 +15,50 @@ import ( func TestEnvOptionParsing(t *testing.T) { t.Run("string", func(t *testing.T) { const val = "setup.sh" - t.Setenv(envbuilder.WithOptionPrefix("SETUP_SCRIPT"), val) + t.Setenv(envbuilder.WithEnvPrefix("SETUP_SCRIPT"), val) o := runCLI() require.Equal(t, o.SetupScript, val) }) t.Run("int", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("CACHE_TTL_DAYS"), "7") + t.Setenv(envbuilder.WithEnvPrefix("CACHE_TTL_DAYS"), "7") o := runCLI() require.Equal(t, o.CacheTTLDays, int64(7)) }) t.Run("string array", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("IGNORE_PATHS"), "/var,/temp") + t.Setenv(envbuilder.WithEnvPrefix("IGNORE_PATHS"), "/var,/temp") o := runCLI() require.Equal(t, o.IgnorePaths, []string{"/var", "/temp"}) }) t.Run("bool", func(t *testing.T) { t.Run("lowercase", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "true") - t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "false") + t.Setenv(envbuilder.WithEnvPrefix("SKIP_REBUILD"), "true") + t.Setenv(envbuilder.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "false") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("uppercase", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "TRUE") - t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "FALSE") + t.Setenv(envbuilder.WithEnvPrefix("SKIP_REBUILD"), "TRUE") + t.Setenv(envbuilder.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "FALSE") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("numeric", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("SKIP_REBUILD"), "1") - t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "0") + t.Setenv(envbuilder.WithEnvPrefix("SKIP_REBUILD"), "1") + t.Setenv(envbuilder.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "0") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) }) t.Run("empty", func(t *testing.T) { - t.Setenv(envbuilder.WithOptionPrefix("GIT_CLONE_SINGLE_BRANCH"), "") + t.Setenv(envbuilder.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "") o := runCLI() require.False(t, o.GitCloneSingleBranch) }) From 30063601cbc7990e9bc645774b5bbdd1ee11d0ae Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 16:26:17 +0000 Subject: [PATCH 08/13] Fix remaining issues on integration tests --- integration/integration_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index ebff7921..0d1e0480 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -56,7 +56,7 @@ func TestForceSafe(t *testing.T) { }) _, err := runEnvbuilder(t, options{env: []string{ envbuilderEnv("GIT_URL", srv.URL), - envbuilderEnv("KANIKO_DIR", "/not/envbuilder"), + "KANIKO_DIR=/not/envbuilder", envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.ErrorContains(t, err, "delete filesystem: safety check failed") @@ -72,8 +72,8 @@ func TestForceSafe(t *testing.T) { }) _, err := runEnvbuilder(t, options{env: []string{ envbuilderEnv("GIT_URL", srv.URL), - envbuilderEnv("KANIKO_DIR", "/not/envbuilder"), - envbuilderEnv("FORCE_SAGE", "true"), + "KANIKO_DIR=/not/envbuilder", + envbuilderEnv("FORCE_SAFE", "true"), envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) @@ -129,7 +129,7 @@ func TestSucceedsGitAuthInURL(t *testing.T) { require.NoError(t, err) u.User = url.UserPassword("kyle", "testing") ctr, err := runEnvbuilder(t, options{env: []string{ - envbuilderEnv("GIT_UR", u.String()), + envbuilderEnv("GIT_URL", u.String()), envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) From 96784635ab921c902172e3cc95e399224f223286 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 16:30:33 +0000 Subject: [PATCH 09/13] Add test for legacy env variables --- options_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/options_test.go b/options_test.go index df29880f..e9bc2ddf 100644 --- a/options_test.go +++ b/options_test.go @@ -63,6 +63,18 @@ func TestEnvOptionParsing(t *testing.T) { require.False(t, o.GitCloneSingleBranch) }) }) + + t.Run("legacy", func(t *testing.T) { + legacyEnvValue := "./setup-legacy-script.sh" + t.Setenv("SETUP_SCRIPT", legacyEnvValue) + o := runCLI() + require.Equal(t, o.SetupScript, legacyEnvValue) + + envValue := "./setup-script.sh" + t.Setenv(envbuilder.WithEnvPrefix("SETUP_SCRIPT"), envValue) + o = runCLI() + require.Equal(t, o.SetupScript, envValue) + }) } // UpdateGoldenFiles indicates golden files should be updated. From 40ef16c67382865b1c9420fbae69c0aa69fa4865 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 10 May 2024 18:20:27 +0000 Subject: [PATCH 10/13] Add tests to legacy vars --- envbuilder.go | 7 +++++ options.go | 7 ++--- options_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/envbuilder.go b/envbuilder.go index 48c7e986..3ecbf88a 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -376,6 +376,13 @@ func Run(ctx context.Context, options Options) error { options.CacheRepo = fmt.Sprintf("localhost:%d/local/cache", tcpAddr.Port) } + // Temporarily removed this from the default settings to prevent conflicts + // between current and legacy environment variables that add default values. + // Once the legacy environment variables are phased out, this can be + // reinstated to the IGNORE_PATHS default. + if len(options.IgnorePaths) == 0 { + options.IgnorePaths = []string{"/var/run"} + } // IgnorePaths in the Kaniko options doesn't properly ignore paths. // So we add them to the default ignore list. See: // https://github.com/GoogleContainerTools/kaniko/blob/63be4990ca5a60bdf06ddc4d10aa4eca0c0bc714/cmd/executor/cmd/root.go#L136 diff --git a/options.go b/options.go index 6c052691..6ac9802a 100644 --- a/options.go +++ b/options.go @@ -284,10 +284,9 @@ func (o *Options) CLI() serpent.OptionSet { "container registries.", }, { - Flag: "ignore-paths", - Env: WithEnvPrefix("IGNORE_PATHS"), - Value: serpent.StringArrayOf(&o.IgnorePaths), - Default: "/var/run", + Flag: "ignore-paths", + Env: WithEnvPrefix("IGNORE_PATHS"), + Value: serpent.StringArrayOf(&o.IgnorePaths), Description: "The comma separated list of paths to ignore when " + "building the workspace.", }, diff --git a/options_test.go b/options_test.go index e9bc2ddf..993ea323 100644 --- a/options_test.go +++ b/options_test.go @@ -64,17 +64,77 @@ func TestEnvOptionParsing(t *testing.T) { }) }) - t.Run("legacy", func(t *testing.T) { - legacyEnvValue := "./setup-legacy-script.sh" - t.Setenv("SETUP_SCRIPT", legacyEnvValue) - o := runCLI() - require.Equal(t, o.SetupScript, legacyEnvValue) +} - envValue := "./setup-script.sh" - t.Setenv(envbuilder.WithEnvPrefix("SETUP_SCRIPT"), envValue) - o = runCLI() - require.Equal(t, o.SetupScript, envValue) - }) +func TestLegacyEnvVars(t *testing.T) { + legacyEnvs := map[string]string{ + "SETUP_SCRIPT": "./setup-legacy-script.sh", + "INIT_SCRIPT": "sleep infinity", + "INIT_COMMAND": "/bin/sh", + "INIT_ARGS": "arg1 arg2", + "CACHE_REPO": "example-cache-repo", + "BASE_IMAGE_CACHE_DIR": "/path/to/base/image/cache", + "LAYER_CACHE_DIR": "/path/to/layer/cache", + "DEVCONTAINER_DIR": "/path/to/devcontainer/dir", + "DEVCONTAINER_JSON_PATH": "/path/to/devcontainer.json", + "DOCKERFILE_PATH": "/path/to/Dockerfile", + "BUILD_CONTEXT_PATH": "/path/to/build/context", + "CACHE_TTL_DAYS": "7", + "DOCKER_CONFIG_BASE64": "base64encodedconfig", + "FALLBACK_IMAGE": "fallback-image:latest", + "EXIT_ON_BUILD_FAILURE": "true", + "FORCE_SAFE": "true", + "INSECURE": "true", + "IGNORE_PATHS": "/var/run,/tmp", + "SKIP_REBUILD": "true", + "GIT_URL": "https://github.com/example/repo.git", + "GIT_CLONE_DEPTH": "1", + "GIT_CLONE_SINGLE_BRANCH": "true", + "GIT_USERNAME": "gituser", + "GIT_PASSWORD": "gitpassword", + "GIT_SSH_PRIVATE_KEY_PATH": "/path/to/private/key", + "GIT_HTTP_PROXY_URL": "http://proxy.example.com", + "WORKSPACE_FOLDER": "/path/to/workspace/folder", + "SSL_CERT_BASE64": "base64encodedcert", + "EXPORT_ENV_FILE": "/path/to/export/env/file", + "POST_START_SCRIPT_PATH": "/path/to/post/start/script", + } + for k, v := range legacyEnvs { + t.Setenv(k, v) + } + + o := runCLI() + + require.Equal(t, o.SetupScript, legacyEnvs["SETUP_SCRIPT"]) + require.Equal(t, o.InitScript, legacyEnvs["INIT_SCRIPT"]) + require.Equal(t, o.InitCommand, legacyEnvs["INIT_COMMAND"]) + require.Equal(t, o.InitArgs, legacyEnvs["INIT_ARGS"]) + require.Equal(t, o.CacheRepo, legacyEnvs["CACHE_REPO"]) + require.Equal(t, o.BaseImageCacheDir, legacyEnvs["BASE_IMAGE_CACHE_DIR"]) + require.Equal(t, o.LayerCacheDir, legacyEnvs["LAYER_CACHE_DIR"]) + require.Equal(t, o.DevcontainerDir, legacyEnvs["DEVCONTAINER_DIR"]) + require.Equal(t, o.DevcontainerJSONPath, legacyEnvs["DEVCONTAINER_JSON_PATH"]) + require.Equal(t, o.DockerfilePath, legacyEnvs["DOCKERFILE_PATH"]) + require.Equal(t, o.BuildContextPath, legacyEnvs["BUILD_CONTEXT_PATH"]) + require.Equal(t, o.CacheTTLDays, int64(7)) + require.Equal(t, o.DockerConfigBase64, legacyEnvs["DOCKER_CONFIG_BASE64"]) + require.Equal(t, o.FallbackImage, legacyEnvs["FALLBACK_IMAGE"]) + require.Equal(t, o.ExitOnBuildFailure, true) + require.Equal(t, o.ForceSafe, true) + require.Equal(t, o.Insecure, true) + require.Equal(t, o.IgnorePaths, []string{"/var/run", "/tmp"}) + require.Equal(t, o.SkipRebuild, true) + require.Equal(t, o.GitURL, legacyEnvs["GIT_URL"]) + require.Equal(t, o.GitCloneDepth, int64(1)) + require.Equal(t, o.GitCloneSingleBranch, true) + require.Equal(t, o.GitUsername, legacyEnvs["GIT_USERNAME"]) + require.Equal(t, o.GitPassword, legacyEnvs["GIT_PASSWORD"]) + require.Equal(t, o.GitSSHPrivateKeyPath, legacyEnvs["GIT_SSH_PRIVATE_KEY_PATH"]) + require.Equal(t, o.GitHTTPProxyURL, legacyEnvs["GIT_HTTP_PROXY_URL"]) + require.Equal(t, o.WorkspaceFolder, legacyEnvs["WORKSPACE_FOLDER"]) + require.Equal(t, o.SSLCertBase64, legacyEnvs["SSL_CERT_BASE64"]) + require.Equal(t, o.ExportEnvFile, legacyEnvs["EXPORT_ENV_FILE"]) + require.Equal(t, o.PostStartScriptPath, legacyEnvs["POST_START_SCRIPT_PATH"]) } // UpdateGoldenFiles indicates golden files should be updated. From 042f29a5842516574965048734e8821c4152f9ee Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 13 May 2024 13:58:16 +0000 Subject: [PATCH 11/13] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2818312..ec5d8a27 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ On MacOS or Windows systems, we recommend either using a VM or the provided `.de | `--exit-on-build-failure` | `ENVBUILDER_EXIT_ON_BUILD_FAILURE` | | Terminates the container upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error. | | `--force-safe` | `ENVBUILDER_FORCE_SAFE` | | Ignores any filesystem safety checks. This could cause serious harm to your system! This is used in cases where bypass is needed to unblock customers. | | `--insecure` | `ENVBUILDER_INSECURE` | | Bypass TLS verification when cloning and pulling from container registries. | -| `--ignore-paths` | `ENVBUILDER_IGNORE_PATHS` | `/var/run` | The comma separated list of paths to ignore when building the workspace. | +| `--ignore-paths` | `ENVBUILDER_IGNORE_PATHS` | | The comma separated list of paths to ignore when building the workspace. | | `--skip-rebuild` | `ENVBUILDER_SKIP_REBUILD` | | Skip building if the MagicFile exists. This is used to skip building when a container is restarting. e.g. docker stop -> docker start This value can always be set to true - even if the container is being started for the first time. | | `--git-url` | `ENVBUILDER_GIT_URL` | | The URL of the Git repository to clone. This is optional. | | `--git-clone-depth` | `ENVBUILDER_GIT_CLONE_DEPTH` | | The depth to use when cloning the Git repository. | From c4bdd6ee463d451fd17da2e0da93929602875055 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 13 May 2024 13:58:32 +0000 Subject: [PATCH 12/13] Fix fmt --- options_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/options_test.go b/options_test.go index 993ea323..6d0185b8 100644 --- a/options_test.go +++ b/options_test.go @@ -63,7 +63,6 @@ func TestEnvOptionParsing(t *testing.T) { require.False(t, o.GitCloneSingleBranch) }) }) - } func TestLegacyEnvVars(t *testing.T) { From 84fc31d590e0b5417858aa3f7dfbca4186daf687 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 13 May 2024 14:35:03 +0000 Subject: [PATCH 13/13] Update golden files --- testdata/options.golden | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/options.golden b/testdata/options.golden index 1f1378b9..da242ec4 100644 --- a/testdata/options.golden +++ b/testdata/options.golden @@ -99,7 +99,7 @@ OPTIONS: --git-username string, $ENVBUILDER_GIT_USERNAME The username to use for Git authentication. This is optional. - --ignore-paths string-array, $ENVBUILDER_IGNORE_PATHS (default: /var/run) + --ignore-paths string-array, $ENVBUILDER_IGNORE_PATHS The comma separated list of paths to ignore when building the workspace.