From fdb9e16df4a1b54e286b7b365f49341b9412c4c5 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Mon, 21 Mar 2022 18:23:50 +0100 Subject: [PATCH 1/2] feat: Allow specifying the version with command:version #5 --- .env | 4 +- README.md | 8 +++ docker-compose.yml | 4 +- lib/dockerized.go | 130 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/.env b/.env index 194b024..a412be9 100644 --- a/.env +++ b/.env @@ -13,11 +13,14 @@ GIT_VERSION=2.32.0 GH_VERSION=2.5.2 GO_VERSION=1.17.8 HASKELL_VERSION=9.2.2 +GHCI_VERSION=${HASKELL_VERSION} HELM_VERSION=3.8.1 HTTPIE_VERSION=2.6.0 +HTTP_VERSION=${HTTPIE_VERSION} JAVA_VERSION=17.0.2 LUA_IMAGE=nickblah/lua LUA_VERSION=5.4.3 +NODE_VERSION=17.7.2 PERL_VERSION=5.34.1 PHP_VERSION=8.1.3 PROTOC_VERSION=3.9.1 @@ -32,7 +35,6 @@ S3CMD_BASE=python:${PYTHON_VERSION}-${DEFAULT_BASE_IMAGE} S3CMD_VERSION=2.2.0 SWAGGER_CODEGEN_VERSION=3.0.29 SWIPL_VERSION=8.5.5 -NODE_VERSION=16.13.0 TYPESCRIPT_VERSION=4.6.2 TSC_VERSION=${TYPESCRIPT_VERSION} VUE_VERSION=5.0.1 diff --git a/README.md b/README.md index 4ca6c6e..3934bdc 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,14 @@ This allows you to "lock" your tools to specific versions for your project. - Create a `dockerized.env` file in your project directory. - All commands executed within this directory will use the settings specified in this file. +**Ad-hoc** + +Add `:` to the end of the command to override the version. + +```shell +dockerized node:15 +``` + **Ad-hoc (Unix)** - Override the environment variable before the command, to specify the version for that command. diff --git a/docker-compose.yml b/docker-compose.yml index 6ff0c11..062c03b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -71,7 +71,7 @@ services: image: "alpine/git:v${GIT_VERSION}" entrypoint: [ "git" ] ghci: - image: "haskell:${HASKELL_VERSION}" + image: "haskell:${GHCI_VERSION}" entrypoint: [ "ghci" ] go: image: "golang:${GO_VERSION}" @@ -84,7 +84,7 @@ services: helm: image: "alpine/helm:${HELM_VERSION}" http: - image: "alpine/httpie:${HTTPIE_VERSION}" + image: "alpine/httpie:${HTTP_VERSION}" entrypoint: [ "http" ] java: image: "openjdk:${JAVA_VERSION}" diff --git a/lib/dockerized.go b/lib/dockerized.go index 91d8467..e7e9f5f 100644 --- a/lib/dockerized.go +++ b/lib/dockerized.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/flags" @@ -14,6 +15,7 @@ import ( "os" "os/signal" "path/filepath" + "regexp" "sort" "strings" "syscall" @@ -34,7 +36,7 @@ var options = []string{ func main() { normalizeEnvironment() - dockerizedOptions, commandName, commandArgs := parseArguments() + dockerizedOptions, commandName, commandVersion, commandArgs := parseArguments() var optionHelp = contains(dockerizedOptions, "--help") || contains(dockerizedOptions, "-h") var optionVerbose = contains(dockerizedOptions, "--verbose") || contains(dockerizedOptions, "-v") @@ -76,7 +78,55 @@ func main() { panic(err) } - project, err := getProject(dockerizedDockerComposeFilePath) + if commandVersion != "" { + rawProject, err := getRawProject(dockerizedDockerComposeFilePath) + if err != nil { + panic(err) + } + + rawService, err := rawProject.GetService(commandName) + + var versionVariableExpected = strings.ToUpper(commandName) + "_VERSION" + var versionVariablesUsed []string + for _, variable := range ExtractVariables(rawService) { + if strings.HasSuffix(variable, "_VERSION") { + versionVariablesUsed = append(versionVariablesUsed, variable) + } + } + + if len(versionVariablesUsed) == 0 { + fmt.Printf("Error: Version selection for %s is currently not supported.\n", commandName) + os.Exit(1) + } + + versionKey := versionVariableExpected + + if !contains(versionVariablesUsed, versionVariableExpected) { + if len(versionVariablesUsed) == 1 { + fmt.Printf("Error: To specify the version of %s, please set %s.\n", + commandName, + versionVariablesUsed[0], + ) + os.Exit(1) + } else if len(versionVariablesUsed) > 1 { + fmt.Println("Multiple version variables found:") + for _, variable := range versionVariablesUsed { + fmt.Println(" " + variable) + } + os.Exit(1) + } + } + + if optionVerbose { + fmt.Printf("Setting %s to %s...\n", versionKey, commandVersion) + } + err = os.Setenv(versionKey, commandVersion) + if err != nil { + panic(err) + } + } + + project, err := getProject(dockerizedDockerComposeFilePath, true) if err != nil { panic(err) } @@ -302,7 +352,26 @@ func newSigContext() (context.Context, func()) { return ctx, cancel } -func getProject(dockerComposeFilePath string) (*types.Project, error) { +func getRawProject(dockerComposeFilePath string) (*types.Project, error) { + options, err := cli.NewProjectOptions([]string{ + dockerComposeFilePath, + }, + cli.WithInterpolation(false), + cli.WithLoadOptions(func(l *loader.Options) { + l.SkipValidation = true + l.SkipConsistencyCheck = true + l.SkipNormalization = true + }), + ) + + if err != nil { + return nil, nil + } + + return cli.ProjectFromOptions(options) +} + +func getProject(dockerComposeFilePath string, interpolation bool) (*types.Project, error) { options, err := cli.NewProjectOptions([]string{ dockerComposeFilePath, }, @@ -363,7 +432,7 @@ func getBackend() (*api.ServiceProxy, error) { } func dockerComposeBuild(dockerComposeFilePath string, buildOptions api.BuildOptions) error { - project, err := getProject(dockerComposeFilePath) + project, err := getProject(dockerComposeFilePath, true) if err != nil { return err } @@ -422,12 +491,17 @@ func dockerComposeRun(project *types.Project, runOptions api.RunOptions, volumes } func help(dockerComposeFilePath string) error { - project, err := getProject(dockerComposeFilePath) + project, err := getProject(dockerComposeFilePath, false) if err != nil { return err } - fmt.Println("Usage: dockerized [options] [arguments]") + fmt.Println("Usage: dockerized [options] [:version] [arguments]") + fmt.Println("") + fmt.Println("Examples:") + fmt.Println(" dockerized go") + fmt.Println(" dockerized go:1.8 build") + fmt.Println(" dockerized --shell go") fmt.Println("") fmt.Println("Commands:") @@ -455,10 +529,11 @@ func help(dockerComposeFilePath string) error { return nil } -func parseArguments() ([]string, string, []string) { +func parseArguments() ([]string, string, string, []string) { commandName := "" var commandArgs []string var dockerizedOptions []string + var commandVersion string for _, arg := range os.Args[1:] { if arg[0] == '-' && commandName == "" { if contains(options, arg) { @@ -475,5 +550,44 @@ func parseArguments() ([]string, string, []string) { } } } - return dockerizedOptions, commandName, commandArgs + if strings.ContainsRune(commandName, ':') { + commandSplit := strings.Split(commandName, ":") + commandName = commandSplit[0] + commandVersion = commandSplit[1] + } + return dockerizedOptions, commandName, commandVersion, commandArgs +} + +func unique(s []string) []string { + keys := make(map[string]bool) + var list []string + for _, entry := range s { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func ExtractVariables(rawService types.ServiceConfig) []string { + var usedVariables []string + for envKey := range rawService.Environment { + usedVariables = append(usedVariables, envKey) + } + if rawService.Build != nil { + for argKey := range rawService.Build.Args { + usedVariables = append(usedVariables, argKey) + } + } + + pattern := regexp.MustCompile(`\$\{([^}]+)\}`) + for _, match := range pattern.FindAllStringSubmatch(rawService.Image, -1) { + usedVariables = append(usedVariables, match[1]) + } + + usedVariables = unique(usedVariables) + sort.Strings(usedVariables) + + return usedVariables } From 34749db41edb34adb928e00126b3635a498dc4e8 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Mon, 21 Mar 2022 18:29:28 +0100 Subject: [PATCH 2/2] Ensure swagger, psql, rustc, tsc can set their version through :version syntax --- .env | 3 +++ docker-compose.yml | 4 ++-- lib/dockerized.go | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.env b/.env index a412be9..45a18d4 100644 --- a/.env +++ b/.env @@ -27,15 +27,18 @@ PROTOC_VERSION=3.9.1 PROTOC_ARCH=${DEFAULT_ARCH} PROTOC_BASE=${DEFAULT_BASE} POSTGRES_VERSION=14.2 +PSQL_VERSION=${POSTGRES_VERSION} PYTHON_VERSION=3.10 PYTHON2_VERSION=2.7.18 RUBY_VERSION=3.1.1 RUST_VERSION=1.59.0 +RUSTC_VERSION=${RUST_VERSION} S3CMD_BASE=python:${PYTHON_VERSION}-${DEFAULT_BASE_IMAGE} S3CMD_VERSION=2.2.0 SWAGGER_CODEGEN_VERSION=3.0.29 SWIPL_VERSION=8.5.5 TYPESCRIPT_VERSION=4.6.2 TSC_VERSION=${TYPESCRIPT_VERSION} +TSC_VERSION=${TYPESCRIPT_VERSION} VUE_VERSION=5.0.1 YOUTUBE_DL_VERSION=2022.03.08.2 diff --git a/docker-compose.yml b/docker-compose.yml index 062c03b..56d3c7e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -113,7 +113,7 @@ services: php: image: "php:${PHP_VERSION}" psql: - image: "postgres:${POSTGRES_VERSION}" + image: "postgres:${PSQL_VERSION}" entrypoint: [ "psql" ] protoc: image: "protoc:${PROTOC_VERSION}" @@ -145,7 +145,7 @@ services: <<: *ruby entrypoint: [ "gem" ] rustc: - image: "rust:${RUST_VERSION}" + image: "rust:${RUSTC_VERSION}" entrypoint: [ "rustc" ] s3cmd: build: diff --git a/lib/dockerized.go b/lib/dockerized.go index e7e9f5f..375eb23 100644 --- a/lib/dockerized.go +++ b/lib/dockerized.go @@ -86,7 +86,7 @@ func main() { rawService, err := rawProject.GetService(commandName) - var versionVariableExpected = strings.ToUpper(commandName) + "_VERSION" + var versionVariableExpected = strings.ReplaceAll(strings.ToUpper(commandName), "-", "_") + "_VERSION" var versionVariablesUsed []string for _, variable := range ExtractVariables(rawService) { if strings.HasSuffix(variable, "_VERSION") {