Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredLunde committed May 15, 2024
1 parent 574fb72 commit ce1b887
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
golang 1.21
golang 1.21.5
71 changes: 63 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,39 @@ Node.js, Python, Ruby, Java/Spring Boot, Go, Elixir/Phoenix, and more.

For detailed documentation, visit the [FlexStack Documentation](https://flexstack.com/docs/languages-and-frameworks/autogenerate-dockerfile) page.

## Features

- [x] Automatically detect the runtime and framework used by your project
- [x] Use version managers like [asdf](https://github.com/asdf-vm), nvm, rbenv, and pyenv to install the correct version of the runtime
- [x] Make a best effort to detect any install, build, and run commands
- [x] Generate a Dockerfile with sensible defaults that are configurable via [Docker Build Args](https://docs.docker.com/build/guide/build-args/)
- [x] Support for a wide range of the most popular languages and frameworks including Next.js, Phoenix, Spring Boot, Django, and more
- [x] Use Debian Slim as the runtime image for a smaller image size and better security, while still supporting the most common dependencies and avoiding deployment headaches caused by Alpine Linux gotchas
- [x] Use multi-stage builds to reduce the size of the final image
- [x] Supports multi-platform images that run on both x86 and ARM CPU architectures

## Supported Runtimes

- Bun
- Deno
- Docker
- Elixir
- Go
- Java
- Next.js
- Node.js
- PHP
- Python
- Ruby
- Rust
- Static (HTML, CSS, JS)

## Installation

### cURL

```sh
curl -sSL https://flexstack.com/install/new-dockerfile | sh
curl -fsSL https://flexstack.com/install/new-dockerfile | bash
```

### Go Package
Expand All @@ -20,12 +47,6 @@ curl -sSL https://flexstack.com/install/new-dockerfile | sh
go get github.com/flexstack/new-dockerfile
```

## Supported Platforms for CLI

- **macOS** (arm64, x86_64)
- **Linux** (arm64, x86_64)
- **Windows** (x86_64, i386)

## CLI Usage

```sh
Expand All @@ -38,4 +59,38 @@ new-dockerfile [options]
- `--write` - Write the generated Dockerfile to the project at the specified path (default: `false`)
- `--runtime` - Force a specific runtime, e.g. `node` (default: `auto`)
- `--quiet` - Disable all logging except for errors (default: `false`)
- `--help` - Show help
- `--help` - Show help

## CLI Examples

Write a Dockerfile to the current directory:
```sh
new-dockerfile --write
```

Write a Dockerfile to a specific directory:
```sh
new-dockerfile > path/to/Dockerfile
```

Force a specific runtime:
```sh
new-dockerfile --runtime next.js
```

List the supported runtimes:
```sh
new-dockerfile --runtime list
```

## How it Works

The tool searches for common files and directories in your project to determine the runtime and framework.
For example, if it finds a `package.json` file, it will assume the project is a Node.js project unless
a `next.config.js` file is present, in which case it will assume the project is a Next.js project.

From there, it will read any `.tool-versions` or other version manager files to determine the correct version
of the runtime to install. It will then make a best effort to detect any install, build, and run commands.
For example, a `serve`, `start`, `start:prod` command in a `package.json` file will be used as the run command.

Read on to see runtime-specific examples and how to configure the generated Dockerfile.
11 changes: 9 additions & 2 deletions cmd/new-dockerfile/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,23 @@ func main() {
runtimes := df.ListRuntimes()

for _, rt := range runtimes {
if strings.ToLower(string(rt.Name())) == strings.ToLower(runtimeArg) {
if strings.EqualFold(string(rt.Name()), runtimeArg) {
r = rt
break
}
}
if r == nil {
runtimeNames := make([]string, len(runtimes))
for i, rt := range runtimes {
runtimeNames[i] = string(rt.Name())
runtimeNames[i] = strings.ToLower(string(rt.Name()))
}

if runtimeArg == "list" {
fmt.Println("Available runtimes:")
fmt.Println(" - " + strings.Join(runtimeNames, "\n - "))
os.Exit(0)
}

log.Error(fmt.Sprintf(`Runtime "%s" not found. Expected one of: %s`, runtimeArg, "\n - "+strings.Join(runtimeNames, "\n - ")))
os.Exit(1)
}
Expand Down
79 changes: 71 additions & 8 deletions node/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,90 @@
# Autogenerate a Dockerfile

FlexStack's `new-dockerfile` CLI tool automatically generates a configurable Dockerfile based on your project source code.
It supports a wide range of languages and frameworks, including Next.js, Node.js, Python, Ruby, Java/Spring Boot, Go,
Elixir/Phoenix, and more.
FlexStack's `new-dockerfile` CLI tool and Go package automatically generates a configurable Dockerfile
based on your project source code. It supports a wide range of languages and frameworks, including Next.js,
Node.js, Python, Ruby, Java/Spring Boot, Go, Elixir/Phoenix, and more.

For detailed documentation, visit the [FlexStack Documentation](https://flexstack.com/docs/languages-and-frameworks/autogenerate-dockerfile) page.

## Supported Platforms
## Features

- **macOS** (arm64, x86_64)
- **Linux** (arm64, x86_64)
- **Windows** (x86_64, i386)
- [x] Automatically detect the runtime and framework used by your project
- [x] Use version managers like [asdf](https://github.com/asdf-vm), nvm, rbenv, and pyenv to install the correct version of the runtime
- [x] Make a best effort to detect any install, build, and run commands
- [x] Generate a Dockerfile with sensible defaults that are configurable via [Docker Build Args](https://docs.docker.com/build/guide/build-args/)
- [x] Support for a wide range of the most popular languages and frameworks including Next.js, Phoenix, Spring Boot, Django, and more
- [x] Use Debian Slim as the runtime image for a smaller image size and better security, while still supporting the most common dependencies and avoiding deployment headaches caused by Alpine Linux gotchas
- [x] Use multi-stage builds to reduce the size of the final image
- [x] Supports multi-platform images that run on both x86 and ARM CPU architectures

## Supported Runtimes

- Bun
- Deno
- Docker
- Elixir
- Go
- Java
- Next.js
- Node.js
- PHP
- Python
- Ruby
- Rust
- Static (HTML, CSS, JS)

## Usage

Using `npx`:

```sh
npx new-dockerfile [options]
```

Install the CLI globally:

```sh
npm install -g new-dockerfile
```

## Options

- `--path` - Path to the project source code (default: `.`)
- `--write` - Write the generated Dockerfile to the project at the specified path (default: `false`)
- `--runtime` - Force a specific runtime, e.g. `node` (default: `auto`)
- `--quiet` - Disable all logging except for errors (default: `false`)
- `--help` - Show help
- `--help` - Show help

## Examples

Write a Dockerfile to the current directory:
```sh
new-dockerfile --write
```

Write a Dockerfile to a specific directory:
```sh
new-dockerfile > path/to/Dockerfile
```

Force a specific runtime:
```sh
new-dockerfile --runtime next.js
```

List the supported runtimes:
```sh
new-dockerfile --runtime list
```

## How it Works

The tool searches for common files and directories in your project to determine the runtime and framework.
For example, if it finds a `package.json` file, it will assume the project is a Node.js project unless
a `next.config.js` file is present, in which case it will assume the project is a Next.js project.

From there, it will read any `.tool-versions` or other version manager files to determine the correct version
of the runtime to install. It will then make a best effort to detect any install, build, and run commands.
For example, a `serve`, `start`, `start:prod` command in a `package.json` file will be used as the run command.

Read on to see runtime-specific examples and how to configure the generated Dockerfile.
51 changes: 20 additions & 31 deletions runtime/bun.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,22 @@ func (d *Bun) GenerateDockerfile(path string) ([]byte, error) {
if ok {
d.Log.Info("Detected scripts in package.json")

if _, ok := scripts["start:prod"].(string); ok {
startCMD = "bun run start:prod"
} else if _, ok := scripts["start:production"].(string); ok {
startCMD = "bun run start:production"
} else if _, ok := scripts["start-prod"].(string); ok {
startCMD = "bun run start-prod"
} else if _, ok := scripts["start-production"].(string); ok {
startCMD = "bun run start-production"
} else if _, ok := scripts["start"].(string); ok {
startCMD = "bun run start"
startCommands := []string{"serve", "start:prod", "start:production", "start-prod", "start-production", "start"}
for _, cmd := range startCommands {
if _, ok := scripts[cmd].(string); ok {
d.Log.Info("Detected start command in package.json: " + cmd)
startCMD = fmt.Sprintf("bun run %s", cmd)
break
}
}

if _, ok := scripts["build:prod"].(string); ok {
buildCMD = "bun run build:prod"
} else if _, ok := scripts["build:production"].(string); ok {
buildCMD = "bun run build:production"
} else if _, ok := scripts["build-prod"].(string); ok {
buildCMD = "bun run build-prod"
} else if _, ok := scripts["build-production"].(string); ok {
buildCMD = "bun run build-production"
} else if _, ok := scripts["build"].(string); ok {
buildCMD = "bun run build"
buildCommands := []string{"build:prod", "build:production", "build-prod", "build-production", "build"}
for _, cmd := range buildCommands {
if _, ok := scripts[cmd].(string); ok {
d.Log.Info("Detected build command in package.json: " + cmd)
buildCMD = fmt.Sprintf("bun run %s", cmd)
break
}
}
}

Expand All @@ -100,6 +94,7 @@ func (d *Bun) GenerateDockerfile(path string) ([]byte, error) {
}

if startCMD == "" && mainFile != "" {
d.Log.Info("Detected start command via main file: " + mainFile)
startCMD = fmt.Sprintf("bun %s", mainFile)
}

Expand All @@ -124,17 +119,11 @@ func (d *Bun) GenerateDockerfile(path string) ([]byte, error) {
startCMD = string(startCMDJSON)
}

finalVersion := "slim"
if *version != "latest" {
finalVersion = *version + "-slim"
}

var buf bytes.Buffer
if err := tmpl.Option("missingkey=zero").Execute(&buf, map[string]string{
"Version": *version,
"FinalVersion": finalVersion,
"BuildCMD": buildCMD,
"StartCMD": startCMD,
"Version": *version,
"BuildCMD": buildCMD,
"StartCMD": startCMD,
}); err != nil {
return nil, fmt.Errorf("Failed to execute template")
}
Expand Down Expand Up @@ -162,7 +151,7 @@ ARG BUILD_CMD={{.BuildCMD}}
ENV BUILD_CMD=${BUILD_CMD}
RUN if [ ! -z "${BUILD_CMD}" ]; then $BUILD_CMD; fi
FROM oven/bun:{{.FinalVersion}} AS final
FROM oven/bun:${VERSION}-slim AS runtime
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends wget && apt-get clean && rm -f /var/lib/apt/lists/*_*
Expand Down Expand Up @@ -224,7 +213,7 @@ func findBunVersion(path string, log *slog.Logger) (*string, error) {
}

if version == "" {
version = "latest"
version = "1"
log.Info(fmt.Sprintf("No Bun version detected. Using: %s", version))
}

Expand Down
8 changes: 6 additions & 2 deletions runtime/deno.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,17 @@ func (d *Deno) GenerateDockerfile(path string) ([]byte, error) {

scripts, ok := denoJSON["tasks"].(map[string]interface{})
if ok {
d.Log.Info("Detected tasks in deno.json")
startCommands := []string{"start:prod", "start:production", "start-prod", "start-production", "start"}
startCommands := []string{"serve", "start:prod", "start:production", "start-prod", "start-production", "start"}
for _, cmd := range startCommands {
if _, ok := scripts[cmd].(string); ok {
d.Log.Info("Detected start command in deno.json: " + cmd)
startCMD = fmt.Sprintf("deno task %s", cmd)
break
}
}

if _, ok := scripts["cache"].(string); ok {
d.Log.Info("Detected install command in deno.json: cache")
installCMD = "deno task cache"
}
}
Expand All @@ -115,8 +116,11 @@ func (d *Deno) GenerateDockerfile(path string) ([]byte, error) {
mainFiles := []string{"mod.ts", "src/mod.ts", "main.ts", "src/main.ts", "index.ts", "src/index.ts"}
for _, mainFile := range mainFiles {
if _, err := os.Stat(filepath.Join(path, mainFile)); err == nil {
d.Log.Info("Detected start command via main/mod file: " + mainFile)

startCMD = fmt.Sprintf("deno run --allow-all %s", mainFile)
if installCMD == "" {
d.Log.Info("Detected install command via main/mod file: " + mainFile)
installCMD = "deno cache " + mainFile
}
break
Expand Down
4 changes: 2 additions & 2 deletions runtime/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (d *Node) GenerateDockerfile(path string) ([]byte, error) {
scripts, ok := packageJSON["scripts"].(map[string]interface{})
if ok {
d.Log.Info("Detected scripts in package.json")
startCommands := []string{"start:prod", "start:production", "start-prod", "start-production", "start"}
startCommands := []string{"serve", "start:prod", "start:production", "start-prod", "start-production", "start"}
for _, cmd := range startCommands {
if _, ok := scripts[cmd].(string); ok {
startCMD = fmt.Sprintf("%s run %s", packageManager, cmd)
Expand Down Expand Up @@ -176,7 +176,7 @@ ENV NODE_ENV=production
ARG BUILD_CMD={{.BuildCMD}}
RUN if [ ! -z "${BUILD_CMD}" ]; then $BUILD_CMD; fi
FROM base AS final
FROM base AS runtime
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends wget && apt-get clean && rm -f /var/lib/apt/lists/*_*
Expand Down

0 comments on commit ce1b887

Please sign in to comment.