From 563d4f9fe17579a06ebcae6368ae8df4ef137041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20B=C3=A4riswyl?= Date: Thu, 29 Feb 2024 15:13:32 +0100 Subject: [PATCH] Added initial Dev Container CLI tool --- gttools/devcontainer_cli.go | 221 ++++++++++++++++++++++++++++++++++++ gttools/gttools.go | 42 +++---- 2 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 gttools/devcontainer_cli.go diff --git a/gttools/devcontainer_cli.go b/gttools/devcontainer_cli.go new file mode 100644 index 0000000..c93a76d --- /dev/null +++ b/gttools/devcontainer_cli.go @@ -0,0 +1,221 @@ +package gttools + +import ( + "os/exec" + + "github.com/roemer/gotaskr/execr" + "github.com/roemer/gotaskr/goext" +) + +// This tool allows interacting with the Dev Container CLI. +type DevContainerCliTool struct { +} + +func CreateDevContainerCliTool() *DevContainerCliTool { + return &DevContainerCliTool{} +} + +type DevContainerCliBuildSettings struct { + ToolSettingsBase + // Host path to a directory that is intended to be persisted and share state between sessions. + UserDataFolder string + // Docker CLI path. + DockerPath string + // Docker Compose CLI path. + DockerComposePath string + // Workspace folder path. The devcontainer.json will be looked up relative to this path. + WorkspaceFolder string + // devcontainer.json path. + Config string + // Log level. + LogLevel DevContainerCliLogLevel + // Log format. + LogFormat DevContainerCliLogFormat + // Builds the image with `--no-cache`. + NoCache *bool + // Image name. + ImageName string + // Additional image to use as potential layer cache. + CacheFrom string + // A destination of buildx cache. + CacheTo string + // Control whether BuildKit should be used. + Buildkit DevContainerCliBuildkit + // Set target platforms. + Platform string + // Push to a container registry. + Push *bool + // Overrides the default behavior to load built images into the local docker registry. Valid options are the same ones provided to the --output option of docker buildx build. + Output string + // Additional features to apply to the dev container (JSON as per "features" section in devcontainer.json). + AdditionalFeatures string +} + +func (tool *DevContainerCliTool) Build(settings *DevContainerCliBuildSettings) error { + args := []string{ + "build", + } + + args = addString(args, settings.UserDataFolder, addSettings{prependElements: []string{"--user-data-folder"}}) + args = addString(args, settings.DockerPath, addSettings{prependElements: []string{"--docker-path"}}) + args = addString(args, settings.DockerComposePath, addSettings{prependElements: []string{"--docker-compose-path"}}) + args = addString(args, settings.WorkspaceFolder, addSettings{prependElements: []string{"--workspace-folder"}}) + args = addString(args, settings.Config, addSettings{prependElements: []string{"--config"}}) + args = addString(args, string(settings.LogLevel), addSettings{prependElements: []string{"--log-level"}}) + args = addString(args, string(settings.LogFormat), addSettings{prependElements: []string{"--log-format"}}) + args = addBoolean(args, settings.NoCache, addSettings{prependElements: []string{"--no-cache"}}) + args = addString(args, settings.ImageName, addSettings{prependElements: []string{"--image-name"}}) + args = addString(args, settings.CacheFrom, addSettings{prependElements: []string{"--cache-from"}}) + args = addString(args, settings.CacheTo, addSettings{prependElements: []string{"--cache-to"}}) + args = addString(args, string(settings.Buildkit), addSettings{prependElements: []string{"--buildkit"}}) + args = addString(args, settings.Platform, addSettings{prependElements: []string{"--platform"}}) + args = addBoolean(args, settings.Push, addSettings{prependElements: []string{"--push"}}) + args = addString(args, settings.Output, addSettings{prependElements: []string{"--output"}}) + args = addString(args, settings.AdditionalFeatures, addSettings{prependElements: []string{"--additional-features"}}) + + args = append(args, settings.CustomArguments...) + cmd := exec.Command("devcontainer", goext.RemoveEmpty(args)...) + cmd.Dir = settings.WorkingDirectory + return execr.RunCommand(settings.OutputToConsole, cmd) +} + +type DevContainerCliFeaturesTestSettings struct { + ToolSettingsBase + // Path to folder containing 'src' and 'test' sub-folders. + ProjectFolder string + // Feature(s) to test . + Features []string + // Filter current tests to only run scenarios containing this string. + Filter string + // Run only scenario tests under 'tests/_global' + GlobalScenariosOnly *bool + // Skip all 'scenario' style tests. + SkipScenarios *bool + // Skip all 'autogenerated' style tests (test.sh) + SkipAutogenerated *bool + // Skip all 'duplicate' style tests (duplicate.sh). + SkipDuplicated *bool + // Allow an element of randomness in test cases. + PermitRandomization *bool + // Base Image. Not used for scenarios. + BaseImage string + // Remote user. Not used for scenarios. + RemoteUser string + // Log level. + LogLevel DevContainerCliLogLevel + // Do not remove test containers after running tests. + PreserveTestContainers *bool + // Quiets output. + Quiet *bool +} + +func (tool *DevContainerCliTool) FeaturesTest(settings *DevContainerCliFeaturesTestSettings) error { + args := []string{ + "features", + "test", + } + + args = addString(args, settings.ProjectFolder, addSettings{prependElements: []string{"--project-folder"}}) + args = addStringList(args, settings.Features, " ", addSettings{prependElements: []string{"--features"}}) + args = addString(args, settings.Filter, addSettings{prependElements: []string{"--filter"}}) + args = addBoolean(args, settings.GlobalScenariosOnly, addSettings{prependElements: []string{"--global-scenarios-only"}}) + args = addBoolean(args, settings.SkipScenarios, addSettings{prependElements: []string{"--skip-scenarios"}}) + args = addBoolean(args, settings.SkipAutogenerated, addSettings{prependElements: []string{"--skip-autogenerated"}}) + args = addBoolean(args, settings.SkipDuplicated, addSettings{prependElements: []string{"--skip-duplicated"}}) + args = addBoolean(args, settings.PermitRandomization, addSettings{prependElements: []string{"--permit-randomization"}}) + args = addString(args, settings.BaseImage, addSettings{prependElements: []string{"--base-image"}}) + args = addString(args, settings.RemoteUser, addSettings{prependElements: []string{"--remote-user"}}) + args = addString(args, string(settings.LogLevel), addSettings{prependElements: []string{"--log-level"}}) + args = addBoolean(args, settings.PreserveTestContainers, addSettings{prependElements: []string{"--preserve-test-containers"}}) + args = addBoolean(args, settings.Quiet, addSettings{prependElements: []string{"--quiet"}}) + + args = append(args, settings.CustomArguments...) + cmd := exec.Command("devcontainer", goext.RemoveEmpty(args)...) + cmd.Dir = settings.WorkingDirectory + return execr.RunCommand(settings.OutputToConsole, cmd) +} + +type DevContainerCliFeaturesPackageSettings struct { + ToolSettingsBase + // Path to a feature or folder of features to package. + Target string + // Path to output directory. Will create directories as needed. + OutputFolder string + // Automatically delete previous output directory before packaging. + ForceCleanOutputFolder *bool + // Log level. + LogLevel DevContainerCliLogLevel +} + +func (tool *DevContainerCliTool) FeaturesPackage(settings *DevContainerCliFeaturesPackageSettings) error { + args := []string{ + "features", + "package", + settings.Target, + } + + args = addString(args, settings.OutputFolder, addSettings{prependElements: []string{"--output-folder"}}) + args = addBoolean(args, settings.ForceCleanOutputFolder, addSettings{prependElements: []string{"--force-clean-output-folder"}}) + args = addString(args, string(settings.LogLevel), addSettings{prependElements: []string{"--log-level"}}) + + args = append(args, settings.CustomArguments...) + cmd := exec.Command("devcontainer", goext.RemoveEmpty(args)...) + cmd.Dir = settings.WorkingDirectory + return execr.RunCommand(settings.OutputToConsole, cmd) +} + +type DevContainerCliFeaturesPublishSettings struct { + ToolSettingsBase + // Path to a feature or folder of features to publish. + Target string + // Name of the OCI registry. + Registry string + // Unique indentifier for the collection of features. + Namespace string + // Log level. + LogLevel DevContainerCliLogLevel +} + +func (tool *DevContainerCliTool) FeaturesPublish(settings *DevContainerCliFeaturesPublishSettings) error { + args := []string{ + "features", + "publish", + settings.Target, + } + + args = addString(args, settings.Registry, addSettings{prependElements: []string{"--registry"}}) + args = addString(args, settings.Namespace, addSettings{prependElements: []string{"--namespace"}}) + args = addString(args, string(settings.LogLevel), addSettings{prependElements: []string{"--log-level"}}) + + args = append(args, settings.CustomArguments...) + cmd := exec.Command("devcontainer", goext.RemoveEmpty(args)...) + cmd.Dir = settings.WorkingDirectory + return execr.RunCommand(settings.OutputToConsole, cmd) +} + +////////// Types + +type DevContainerCliLogLevel string + +const ( + DEV_CONTAINER_CLI_LOG_LEVEL_DEFAULT DevContainerCliLogLevel = "" + DEV_CONTAINER_CLI_LOG_LEVEL_INFO DevContainerCliLogLevel = "info" + DEV_CONTAINER_CLI_LOG_LEVEL_DEBUG DevContainerCliLogLevel = "debug" + DEV_CONTAINER_CLI_LOG_LEVEL_TRACE DevContainerCliLogLevel = "trace" +) + +type DevContainerCliLogFormat string + +const ( + DEV_CONTAINER_CLI_LOG_FORMAT_DEFAULT DevContainerCliLogFormat = "" + DEV_CONTAINER_CLI_LOG_FORMAT_TEXT DevContainerCliLogFormat = "text" + DEV_CONTAINER_CLI_LOG_FORMAT_JSON DevContainerCliLogFormat = "json" +) + +type DevContainerCliBuildkit string + +const ( + DEV_CONTAINER_CLI_BUILDKIT_DEFAULT DevContainerCliBuildkit = "" + DEV_CONTAINER_CLI_BUILDKIT_AUTO DevContainerCliBuildkit = "auto" + DEV_CONTAINER_CLI_BUILDKIT_NEVER DevContainerCliBuildkit = "never" +) diff --git a/gttools/gttools.go b/gttools/gttools.go index eace249..b98c65d 100644 --- a/gttools/gttools.go +++ b/gttools/gttools.go @@ -8,31 +8,33 @@ import ( // ToolsClient provides typed access to the different tools. type ToolsClient struct { - Cypress *CypressTool - Docker *DockerTool - DotNet *DotNetTool - EsLint *EsLintTool - Flyway *FlywayTool - GitLab *GitLabTool - JFrog *JFrogTool - Mvn *MvnTool - Npm *NpmTool - Nx *NxTool + Cypress *CypressTool + DevContainerCli *DevContainerCliTool + Docker *DockerTool + DotNet *DotNetTool + EsLint *EsLintTool + Flyway *FlywayTool + GitLab *GitLabTool + JFrog *JFrogTool + Mvn *MvnTool + Npm *NpmTool + Nx *NxTool } // CreateToolsClient creates a new client to access the different tools. func CreateToolsClient() *ToolsClient { return &ToolsClient{ - Cypress: CreateCypressTool(), - Docker: CreateDockerTool(), - DotNet: CreateDotNetTool(), - EsLint: CreateEsLintTool(), - Flyway: CreateFlywayTool(), - GitLab: CreateGitLabTool(), - JFrog: CreateJFrogTool(), - Mvn: CreateMvnTool(), - Npm: CreateNpmTool(), - Nx: CreateNxTool(), + Cypress: CreateCypressTool(), + DevContainerCli: CreateDevContainerCliTool(), + Docker: CreateDockerTool(), + DotNet: CreateDotNetTool(), + EsLint: CreateEsLintTool(), + Flyway: CreateFlywayTool(), + GitLab: CreateGitLabTool(), + JFrog: CreateJFrogTool(), + Mvn: CreateMvnTool(), + Npm: CreateNpmTool(), + Nx: CreateNxTool(), } }