From d6c1b0fb89e8335d83ce9ba1d690cd88e753a7bb Mon Sep 17 00:00:00 2001 From: Jared Lunde Date: Sun, 29 Sep 2024 11:19:29 -0700 Subject: [PATCH] feat: add mise and node engines support (#25) --- README.md | 9 +++ go.mod | 7 ++- go.sum | 4 ++ runtime/bun.go | 21 +++++++ runtime/bun_test.go | 5 ++ runtime/deno.go | 22 ++++++++ runtime/deno_test.go | 5 ++ runtime/elixir.go | 26 ++++++++- runtime/elixir_test.go | 5 ++ runtime/golang.go | 20 +++++++ runtime/golang_test.go | 5 ++ runtime/nextjs.go | 4 +- runtime/node.go | 73 +++++++++++++++++++++++++ runtime/node_test.go | 10 ++++ runtime/python.go | 18 ++++++ runtime/python_test.go | 9 +++ runtime/ruby.go | 20 +++++++ runtime/ruby_test.go | 10 ++++ testdata/bun-mise/.mise.toml | 2 + testdata/bun-mise/bun.lockb | 0 testdata/bun-mise/package.json | 3 + testdata/deno-mise/.mise.toml | 2 + testdata/deno-mise/deps.ts | 1 + testdata/deno-mise/main.ts | 22 ++++++++ testdata/elixir-mise/.elixir-version | 1 + testdata/elixir-mise/.mise.toml | 2 + testdata/elixir-mise/mix.exs | 15 +++++ testdata/go-mise/.mise.toml | 2 + testdata/go-mise/main.go | 0 testdata/node-engines/package-lock.json | 1 + testdata/node-engines/package.json | 6 ++ testdata/node-mise/.mise.toml | 2 + testdata/node-mise/package-lock.json | 0 testdata/node-mise/package.json | 3 + testdata/python-mise/.mise.toml | 2 + testdata/python-mise/main.py | 0 testdata/python-mise/requirements.txt | 0 testdata/ruby-mise/.mise.toml | 2 + testdata/ruby-mise/Gemfile | 5 ++ 39 files changed, 337 insertions(+), 7 deletions(-) create mode 100644 testdata/bun-mise/.mise.toml create mode 100644 testdata/bun-mise/bun.lockb create mode 100644 testdata/bun-mise/package.json create mode 100644 testdata/deno-mise/.mise.toml create mode 100644 testdata/deno-mise/deps.ts create mode 100644 testdata/deno-mise/main.ts create mode 100644 testdata/elixir-mise/.elixir-version create mode 100644 testdata/elixir-mise/.mise.toml create mode 100644 testdata/elixir-mise/mix.exs create mode 100644 testdata/go-mise/.mise.toml create mode 100644 testdata/go-mise/main.go create mode 100644 testdata/node-engines/package-lock.json create mode 100644 testdata/node-engines/package.json create mode 100644 testdata/node-mise/.mise.toml create mode 100644 testdata/node-mise/package-lock.json create mode 100644 testdata/node-mise/package.json create mode 100644 testdata/python-mise/.mise.toml create mode 100644 testdata/python-mise/main.py create mode 100644 testdata/python-mise/requirements.txt create mode 100644 testdata/ruby-mise/.mise.toml create mode 100644 testdata/ruby-mise/Gemfile diff --git a/README.md b/README.md index ca6fbc1..8ade124 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,12 @@ Read on to see runtime-specific examples and how to configure the generated Dock #### Detected Files - `bun.lockb` + - `bun.lock` - `bunfig.toml` #### Version Detection - `.tool-versions` - `bun {VERSION}` + - `.mise.toml` - `bun = "{VERSION}"` #### Runtime Image `oven/bun:${VERSION}-slim` @@ -174,6 +176,7 @@ Detected in order of precedence: #### Version Detection - `.tool-versions` - `deno {VERSION}` + - `.mise.toml` - `deno = "{VERSION}"` #### Runtime Image `debian:stable-slim` @@ -209,6 +212,7 @@ Detected in order of precedence: - `.tool-versions` - `erlang {VERSION}` - `.elixir-version` - `{VERSION}` - `.erlang-version` - `{VERSION}` + - `.mise.toml` - `erlang = "{VERSION}"` #### Runtime Image `debian:stable-slim` @@ -233,6 +237,7 @@ Detected in order of precedence: #### Version Detection - `.tool-versions` - `golang {VERSION}` + - `.mise.toml` - `go = "{VERSION}"` - `go.mod` - `go {VERSION}` #### Runtime Image @@ -309,6 +314,8 @@ Maven version: - `.tool-versions` - `nodejs {VERSION}` - `.nvmrc` - `v{VERSION}` - `.node-version` - `v{VERSION}` + - `.mise.toml` - `node = "{VERSION}"` + - `package.json` - `"engines": {"node": "{VERSION}"}` #### Runtime Image `node:${VERSION}-slim` @@ -437,6 +444,7 @@ In order of precedence: #### Version Detection - `.tool-versions` - `python {VERSION}` - `.python-version` - `{VERSION}` + - `.mise.toml` - `python = "{VERSION}"` - `runtime.txt` - `python-{VERSION}` #### Runtime Image @@ -477,6 +485,7 @@ In order of precedence: #### Version Detection - `.tool-versions` - `ruby {VERSION}` - `.ruby-version` - `{VERSION}` + - `.mise.toml` - `ruby = "{VERSION}"` - `Gemfile` - `ruby '{VERSION}'` #### Runtime Image diff --git a/go.mod b/go.mod index 900388a..72bfe29 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/flexstack/new-dockerfile -go 1.21 +go 1.21.0 + +toolchain go1.21.5 require ( github.com/lmittmann/tint v1.0.4 @@ -10,11 +12,12 @@ require ( ) require ( + github.com/Masterminds/semver/v3 v3.3.0 github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/go.sum b/go.sum index 27aa8b9..fcb7054 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -24,6 +26,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/runtime/bun.go b/runtime/bun.go index 2f18b4a..20b2dec 100644 --- a/runtime/bun.go +++ b/runtime/bun.go @@ -11,6 +11,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/pelletier/go-toml/v2" ) type Bun struct { @@ -24,6 +26,7 @@ func (d *Bun) Name() RuntimeName { func (d *Bun) Match(path string) bool { checkPaths := []string{ filepath.Join(path, "bun.lockb"), + filepath.Join(path, "bun.lock"), filepath.Join(path, "bunfig.toml"), } @@ -185,6 +188,7 @@ func findBunVersion(path string, log *slog.Logger) (*string, error) { version := "" versionFiles := []string{ ".tool-versions", + ".mise.toml", } for _, file := range versionFiles { @@ -214,6 +218,23 @@ func findBunVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read .tool-versions file") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + bunVersion, ok := mise.Tools["bun"].(string) + if !ok { + versions, ok := mise.Tools["bun"].([]string) + if ok { + bunVersion = versions[0] + } + } + if bunVersion != "" { + version = bunVersion + log.Info("Detected Bun version in .mise.toml: " + version) + break + } } f.Close() diff --git a/runtime/bun_test.go b/runtime/bun_test.go index 144dab0..0a2d871 100644 --- a/runtime/bun_test.go +++ b/runtime/bun_test.go @@ -53,6 +53,11 @@ func TestBunGenerateDockerfile(t *testing.T) { path: "../testdata/bun", expected: []any{`ARG VERSION=1`, `ARG INSTALL_CMD="bun install"`, regexp.MustCompile(`^ARG BUILD_CMD=$`), `ARG START_CMD="bun index.ts"`}, }, + { + name: "Bun project w/ mise", + path: "../testdata/bun-mise", + expected: []any{`ARG VERSION=1.1.3`, `ARG INSTALL_CMD="bun install"`, regexp.MustCompile(`^ARG BUILD_CMD=$`), `ARG START_CMD="bun index.ts"`}, + }, { name: "Bun project with .ts file", path: "../testdata/bun-bunfig", diff --git a/runtime/deno.go b/runtime/deno.go index 0e77944..3321989 100644 --- a/runtime/deno.go +++ b/runtime/deno.go @@ -12,6 +12,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/pelletier/go-toml/v2" ) type Deno struct { @@ -209,6 +211,7 @@ func findDenoVersion(path string, log *slog.Logger) (*string, error) { version := "" versionFiles := []string{ ".tool-versions", + ".mise.toml", } for _, file := range versionFiles { @@ -237,12 +240,31 @@ func findDenoVersion(path string, log *slog.Logger) (*string, error) { if err := scanner.Err(); err != nil { return nil, fmt.Errorf("Failed to read .tool-versions file") } + + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + denoVersion, ok := mise.Tools["deno"].(string) + if !ok { + versions, ok := mise.Tools["deno"].([]string) + if ok { + denoVersion = versions[0] + } + } + if denoVersion != "" { + version = denoVersion + log.Info("Detected Deno version in .mise.toml: " + version) + break + } } f.Close() if version != "" { break } + } } diff --git a/runtime/deno_test.go b/runtime/deno_test.go index d4c6f8f..924ed57 100644 --- a/runtime/deno_test.go +++ b/runtime/deno_test.go @@ -53,6 +53,11 @@ func TestDenoGenerateDockerfile(t *testing.T) { path: "../testdata/deno", expected: []any{`ARG VERSION=latest`, `ARG INSTALL_CMD="deno cache main.ts"`, `ARG START_CMD="deno run --allow-all main.ts"`}, }, + { + name: "Deno project w/ mise", + path: "../testdata/deno-mise", + expected: []any{`ARG VERSION=1.43.2`, `ARG INSTALL_CMD="deno cache main.ts"`, `ARG START_CMD="deno run --allow-all main.ts"`}, + }, { name: "Deno project with .ts file", path: "../testdata/deno-jsonc", diff --git a/runtime/elixir.go b/runtime/elixir.go index 3fe6aba..6e144fe 100644 --- a/runtime/elixir.go +++ b/runtime/elixir.go @@ -10,6 +10,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/pelletier/go-toml/v2" ) type Elixir struct { @@ -120,9 +122,9 @@ RUN addgroup --system nonroot && adduser --system --ingroup nonroot nonroot RUN chown -R nonroot:nonroot /app RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 ENV MIX_ENV="prod" @@ -210,6 +212,7 @@ func findOTPVersion(path string, log *slog.Logger) (*string, error) { versionFiles := []string{ ".tool-versions", ".erlang-version", + ".mise.toml", } for _, file := range versionFiles { @@ -254,6 +257,23 @@ func findOTPVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read .erlang-version file") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + erlangVersion, ok := mise.Tools["erlang"].(string) + if !ok { + versions, ok := mise.Tools["erlang"].([]string) + if ok { + erlangVersion = versions[0] + } + } + if erlangVersion != "" { + version = erlangVersion + log.Info("Detected Erlang version in .mise.toml: " + version) + break + } } f.Close() diff --git a/runtime/elixir_test.go b/runtime/elixir_test.go index 96eddb4..4826a25 100644 --- a/runtime/elixir_test.go +++ b/runtime/elixir_test.go @@ -52,6 +52,11 @@ func TestElixirGenerateDockerfile(t *testing.T) { path: "../testdata/elixir", expected: []any{`ARG VERSION=1.10`, `ARG OTP_VERSION=22`, `ARG BIN_NAME=hello`}, }, + { + name: "Elixir project w/ mise", + path: "../testdata/elixir-mise", + expected: []any{`ARG VERSION=1.10`, `ARG OTP_VERSION=23`, `ARG BIN_NAME=hello`}, + }, { name: "Elixir project with .tool-versions", path: "../testdata/elixir-tool-versions", diff --git a/runtime/golang.go b/runtime/golang.go index 46dd03c..5d41b5c 100644 --- a/runtime/golang.go +++ b/runtime/golang.go @@ -10,6 +10,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/pelletier/go-toml/v2" ) type Golang struct { @@ -145,6 +147,7 @@ func findGoVersion(path string, log *slog.Logger) (*string, error) { version := "" versionFiles := []string{ ".tool-versions", + ".mise.toml", "go.mod", } @@ -190,6 +193,23 @@ func findGoVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read go.mod file") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + goVersion, ok := mise.Tools["go"].(string) + if !ok { + versions, ok := mise.Tools["go"].([]string) + if ok { + goVersion = versions[0] + } + } + if goVersion != "" { + version = goVersion + log.Info("Detected Python version in .mise.toml: " + version) + break + } } f.Close() diff --git a/runtime/golang_test.go b/runtime/golang_test.go index 21f7c0f..14f7e6a 100644 --- a/runtime/golang_test.go +++ b/runtime/golang_test.go @@ -52,6 +52,11 @@ func TestGolangGenerateDockerfile(t *testing.T) { path: "../testdata/go", expected: []any{`ARG VERSION=1.16.3`, `ARG PACKAGE=./main.go`}, }, + { + name: "Golang project w/ mise", + path: "../testdata/go-mise", + expected: []any{`ARG VERSION=1.16`, `ARG PACKAGE=./main.go`}, + }, { name: "Golang project with go.mod file", path: "../testdata/go-mod", diff --git a/runtime/nextjs.go b/runtime/nextjs.go index ab272e0..f4fa213 100644 --- a/runtime/nextjs.go +++ b/runtime/nextjs.go @@ -133,7 +133,7 @@ COPY . . # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. -ENV NEXT_TELEMETRY_DISABLED 1 +ENV NEXT_TELEMETRY_DISABLED=1 RUN {{.BuildMounts}}if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ @@ -222,7 +222,7 @@ COPY --from=builder --chown=nonroot:nonroot /app/node_modules ./node_modules USER nonroot ENV NODE_ENV=production -ENV NEXT_TELEMETRY_DISABLED 1 +ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=8080 EXPOSE ${PORT} CMD ["node_modules/.bin/next", "start", "-H", "0.0.0.0"] diff --git a/runtime/node.go b/runtime/node.go index 9d2606a..54ef1b3 100644 --- a/runtime/node.go +++ b/runtime/node.go @@ -13,6 +13,9 @@ import ( "regexp" "strings" "text/template" + + "github.com/Masterminds/semver/v3" + "github.com/pelletier/go-toml/v2" ) type Node struct { @@ -220,6 +223,18 @@ func findNodeVersion(path string, log *slog.Logger) (*string, error) { ".nvmrc", ".node-version", ".tool-versions", + ".mise.toml", + "package.json", + } + + // This is really jank but it should be fine + nodeVersionsToCheck := []string{} + for i := 0; i < 60; i++ { + for j := 0; j < 60; j++ { + for k := 0; k < 60; k++ { + nodeVersionsToCheck = append(nodeVersionsToCheck, fmt.Sprintf("%d.%d.%d", i, j, k)) + } + } } for _, file := range versionFiles { @@ -234,6 +249,42 @@ func findNodeVersion(path string, log *slog.Logger) (*string, error) { defer f.Close() switch file { + case "package.json": + // Check package.json for engines.node + var packageJSON map[string]interface{} + if err := json.NewDecoder(f).Decode(&packageJSON); err != nil { + return nil, fmt.Errorf("Failed to decode package.json file") + } + + if engines, ok := packageJSON["engines"].(map[string]interface{}); ok { + if nodeVersion, ok := engines["node"].(string); ok { + ver, err := semver.NewVersion(nodeVersion) + if err != nil { + constraints, err := semver.NewConstraint(nodeVersion) + if err != nil { + continue + } + + for _, v := range nodeVersionsToCheck { + semv, _ := semver.NewVersion(v) + if constraints.Check(semv) { + ver = semv + break + } + } + } + if ver != nil { + if ver.Minor() > 0 { + version = fmt.Sprintf("%d.%d", ver.Major(), ver.Minor()) + } else { + version = fmt.Sprint(ver.Major()) + } + log.Info("Detected Node version in package.json: " + version) + break + } + } + } + case ".tool-versions": scanner := bufio.NewScanner(f) for scanner.Scan() { @@ -249,6 +300,24 @@ func findNodeVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read .tool-versions file") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + nodeVersion, ok := mise.Tools["node"].(string) + if !ok { + nodeVersions, ok := mise.Tools["node"].([]string) + if ok { + nodeVersion = nodeVersions[0] + } + } + if nodeVersion != "" { + version = nodeVersion + log.Info("Detected Node version in .mise.toml: " + version) + break + } + case ".nvmrc", ".node-version": scanner := bufio.NewScanner(f) for scanner.Scan() { @@ -283,3 +352,7 @@ func findNodeVersion(path string, log *slog.Logger) (*string, error) { return &version, nil } + +type MiseToml struct { + Tools map[string]interface{} `toml:"tools"` +} diff --git a/runtime/node_test.go b/runtime/node_test.go index ff8e7e4..3c7b080 100644 --- a/runtime/node_test.go +++ b/runtime/node_test.go @@ -58,6 +58,16 @@ func TestNodeGenerateDockerfile(t *testing.T) { path: "../testdata/node", expected: []any{`ARG VERSION=lts`, `ARG INSTALL_CMD="npm ci"`, regexp.MustCompile(`^ARG BUILD_CMD=$`), `ARG START_CMD="node index.ts"`}, }, + { + name: "Node project w/ mise", + path: "../testdata/node-mise", + expected: []any{`ARG VERSION=14`, `ARG INSTALL_CMD="npm ci"`, regexp.MustCompile(`^ARG BUILD_CMD=$`), `ARG START_CMD="node index.ts"`}, + }, + { + name: "Node project with engines", + path: "../testdata/node-engines", + expected: []any{`ARG VERSION=14.5`, `ARG INSTALL_CMD="npm ci"`, regexp.MustCompile(`^ARG BUILD_CMD=$`), `ARG START_CMD="node index.ts"`}, + }, { name: "Node project with pnpm", path: "../testdata/node-pnpm", diff --git a/runtime/python.go b/runtime/python.go index 6ec547f..a361c36 100644 --- a/runtime/python.go +++ b/runtime/python.go @@ -235,6 +235,7 @@ func findPythonVersion(path string, log *slog.Logger) (*string, error) { versionFiles := []string{ ".tool-versions", ".python-version", + ".mise.toml", "runtime.txt", } @@ -295,6 +296,23 @@ func findPythonVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read runtime.txt file") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + pythonVersion, ok := mise.Tools["python"].(string) + if !ok { + versions, ok := mise.Tools["python"].([]string) + if ok { + pythonVersion = versions[0] + } + } + if pythonVersion != "" { + version = pythonVersion + log.Info("Detected Python version in .mise.toml: " + version) + break + } } f.Close() diff --git a/runtime/python_test.go b/runtime/python_test.go index 1c7e9f4..bae9d64 100644 --- a/runtime/python_test.go +++ b/runtime/python_test.go @@ -71,6 +71,15 @@ func TestPythonGenerateDockerfile(t *testing.T) { `ARG START_CMD="python main.py"`, }, }, + { + name: "Python project w/ mise", + path: "../testdata/python-mise", + expected: []any{ + `ARG VERSION=3.8`, + `ARG INSTALL_CMD="pip install --no-cache -r requirements.txt"`, + `ARG START_CMD="python main.py"`, + }, + }, { name: "Python project with django", path: "../testdata/python-django", diff --git a/runtime/ruby.go b/runtime/ruby.go index a4ab902..89f6bda 100644 --- a/runtime/ruby.go +++ b/runtime/ruby.go @@ -10,6 +10,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/pelletier/go-toml/v2" ) type Ruby struct { @@ -168,6 +170,7 @@ func findRubyVersion(path string, log *slog.Logger) (*string, error) { versionFiles := []string{ ".tool-versions", ".ruby-version", + ".mise.toml", "Gemfile", } @@ -243,6 +246,23 @@ func findRubyVersion(path string, log *slog.Logger) (*string, error) { return nil, fmt.Errorf("Failed to read Gemfile") } + case ".mise.toml": + var mise MiseToml + if err := toml.NewDecoder(f).Decode(&mise); err != nil { + return nil, fmt.Errorf("Failed to decode .mise.toml file") + } + rubyVersion, ok := mise.Tools["ruby"].(string) + if !ok { + versions, ok := mise.Tools["ruby"].([]string) + if ok { + rubyVersion = versions[0] + } + } + if rubyVersion != "" { + version = rubyVersion + log.Info("Detected Python version in .mise.toml: " + version) + break + } } f.Close() diff --git a/runtime/ruby_test.go b/runtime/ruby_test.go index 60ec8ae..a934fe7 100644 --- a/runtime/ruby_test.go +++ b/runtime/ruby_test.go @@ -68,6 +68,16 @@ func TestRubyGenerateDockerfile(t *testing.T) { regexp.MustCompile(`^ARG START_CMD=$`), }, }, + { + name: "Ruby project w/ mise", + path: "../testdata/ruby-mise", + expected: []any{ + `ARG VERSION=2.7`, + regexp.MustCompile(`^ARG INSTALL_CMD="bundle install"$`), + regexp.MustCompile(`^ARG BUILD_CMD=$`), + regexp.MustCompile(`^ARG START_CMD=$`), + }, + }, { name: "Ruby project with config/environment.rb", path: "../testdata/ruby-config-environment", diff --git a/testdata/bun-mise/.mise.toml b/testdata/bun-mise/.mise.toml new file mode 100644 index 0000000..d950aa4 --- /dev/null +++ b/testdata/bun-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +bun = "1.1.3" \ No newline at end of file diff --git a/testdata/bun-mise/bun.lockb b/testdata/bun-mise/bun.lockb new file mode 100644 index 0000000..e69de29 diff --git a/testdata/bun-mise/package.json b/testdata/bun-mise/package.json new file mode 100644 index 0000000..cd30c4d --- /dev/null +++ b/testdata/bun-mise/package.json @@ -0,0 +1,3 @@ +{ + "module": "index.ts" +} diff --git a/testdata/deno-mise/.mise.toml b/testdata/deno-mise/.mise.toml new file mode 100644 index 0000000..d0f7fe2 --- /dev/null +++ b/testdata/deno-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +deno = "1.43.2" \ No newline at end of file diff --git a/testdata/deno-mise/deps.ts b/testdata/deno-mise/deps.ts new file mode 100644 index 0000000..b7b9f5d --- /dev/null +++ b/testdata/deno-mise/deps.ts @@ -0,0 +1 @@ +export { serve } from "https://deno.land/std@0.77.0/http/server.ts"; diff --git a/testdata/deno-mise/main.ts b/testdata/deno-mise/main.ts new file mode 100644 index 0000000..4781387 --- /dev/null +++ b/testdata/deno-mise/main.ts @@ -0,0 +1,22 @@ +import { serve } from "./deps.ts"; + +const PORT = Deno.env.get("PORT") || "8000"; +const s = serve(`0.0.0.0:${PORT}`); +const body = new TextEncoder().encode("Hello World\n"); + +console.log(`Server started on port ${PORT}`); +for await (const req of s) { + req.respond({ body }); +} + +Deno.addSignalListener("SIGINT", () => { + console.log("\nServer stopped."); + s.close(); + Deno.exit(); +}); + +Deno.addSignalListener("SIGTERM", () => { + console.log("\nServer stopped."); + s.close(); + Deno.exit(); +}); diff --git a/testdata/elixir-mise/.elixir-version b/testdata/elixir-mise/.elixir-version new file mode 100644 index 0000000..578c71b --- /dev/null +++ b/testdata/elixir-mise/.elixir-version @@ -0,0 +1 @@ +1.10 \ No newline at end of file diff --git a/testdata/elixir-mise/.mise.toml b/testdata/elixir-mise/.mise.toml new file mode 100644 index 0000000..50cd32c --- /dev/null +++ b/testdata/elixir-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +erlang = "23" \ No newline at end of file diff --git a/testdata/elixir-mise/mix.exs b/testdata/elixir-mise/mix.exs new file mode 100644 index 0000000..7cf543f --- /dev/null +++ b/testdata/elixir-mise/mix.exs @@ -0,0 +1,15 @@ +defmodule Hello.MixProject do + use Mix.Project + + def project do + [ + app: :hello, + version: "0.1.0", + elixir: "~> 1.14", + elixirc_paths: elixirc_paths(Mix.env()), + start_permanent: Mix.env() == :prod, + aliases: aliases(), + deps: deps() + ] + end +end \ No newline at end of file diff --git a/testdata/go-mise/.mise.toml b/testdata/go-mise/.mise.toml new file mode 100644 index 0000000..9ebe91f --- /dev/null +++ b/testdata/go-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +go = "1.16" \ No newline at end of file diff --git a/testdata/go-mise/main.go b/testdata/go-mise/main.go new file mode 100644 index 0000000..e69de29 diff --git a/testdata/node-engines/package-lock.json b/testdata/node-engines/package-lock.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/testdata/node-engines/package-lock.json @@ -0,0 +1 @@ +{} diff --git a/testdata/node-engines/package.json b/testdata/node-engines/package.json new file mode 100644 index 0000000..9a7478b --- /dev/null +++ b/testdata/node-engines/package.json @@ -0,0 +1,6 @@ +{ + "main": "index.ts", + "engines": { + "node": ">=14.5.3 <=15.2.1" + } +} diff --git a/testdata/node-mise/.mise.toml b/testdata/node-mise/.mise.toml new file mode 100644 index 0000000..d9a3dde --- /dev/null +++ b/testdata/node-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +node = "14" \ No newline at end of file diff --git a/testdata/node-mise/package-lock.json b/testdata/node-mise/package-lock.json new file mode 100644 index 0000000..e69de29 diff --git a/testdata/node-mise/package.json b/testdata/node-mise/package.json new file mode 100644 index 0000000..145cf8b --- /dev/null +++ b/testdata/node-mise/package.json @@ -0,0 +1,3 @@ +{ + "main": "index.ts" +} diff --git a/testdata/python-mise/.mise.toml b/testdata/python-mise/.mise.toml new file mode 100644 index 0000000..cc67ae5 --- /dev/null +++ b/testdata/python-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +python = "3.8" \ No newline at end of file diff --git a/testdata/python-mise/main.py b/testdata/python-mise/main.py new file mode 100644 index 0000000..e69de29 diff --git a/testdata/python-mise/requirements.txt b/testdata/python-mise/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/testdata/ruby-mise/.mise.toml b/testdata/ruby-mise/.mise.toml new file mode 100644 index 0000000..8aca44a --- /dev/null +++ b/testdata/ruby-mise/.mise.toml @@ -0,0 +1,2 @@ +[tools] +ruby = "2.7" \ No newline at end of file diff --git a/testdata/ruby-mise/Gemfile b/testdata/ruby-mise/Gemfile new file mode 100644 index 0000000..fcb5909 --- /dev/null +++ b/testdata/ruby-mise/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +ruby '2.0.0', :patchlevel => '353'