Skip to content

Commit

Permalink
ZEA-4209: Allow setting specific Bun version (#367)
Browse files Browse the repository at this point in the history
#### Description (required)

- **feat(planner/bun): Implement Determine[Bun]Version**
- **feat(planner/nodejs): Pick BunVersion**

#### Related issues & labels (optional)

- Closes #348, Closes ZEA-4209
- Suggested label: enhancement
  • Loading branch information
yuaanlin authored Oct 19, 2024
2 parents f96c602 + 27eb67e commit 1d9ea28
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 20 deletions.
2 changes: 1 addition & 1 deletion internal/bun/bun.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// GenerateDockerfile generates the Dockerfile for Bun projects.
func GenerateDockerfile(meta types.PlanMeta) (string, error) {
if meta["framework"] == string(types.BunFrameworkHono) {
return `FROM oven/bun:1 as base
return `FROM oven/bun:` + meta["bunVersion"] + ` as base
WORKDIR /src
COPY package.json bun.lockb* ./
RUN bun install
Expand Down
6 changes: 3 additions & 3 deletions internal/bun/identify.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ func (i *identify) Match(fs afero.Fs) bool {
func (i *identify) PlanMeta(options plan.NewPlannerOptions) types.PlanMeta {
return GetMeta(
GetMetaOptions{
Src: options.Source,
Config: options.Config,
Bun: true,
Src: options.Source,
Config: options.Config,
Bun: true,
},
)
}
Expand Down
40 changes: 27 additions & 13 deletions internal/bun/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
"github.com/zeabur/zbpack/pkg/types"
)

type bunPlanContext struct {
// PlanContext is the context for Bun project planning.
type PlanContext struct {
PackageJSON nodejs.PackageJSON
Src afero.Fs

Expand All @@ -23,22 +24,16 @@ type GetMetaOptions nodejs.GetMetaOptions

// GetMeta gets the metadata of the Node.js project.
func GetMeta(opt GetMetaOptions) types.PlanMeta {
packageJSON, err := nodejs.DeserializePackageJSON(opt.Src)
if err != nil {
log.Printf("Failed to read package.json: %v", err)
// not fatal
}

ctx := &bunPlanContext{
PackageJSON: packageJSON,
Src: opt.Src,
}
ctx := CreateBunContext(GetMetaOptions(opt))

meta := types.PlanMeta{}

framework := DetermineFramework(ctx)
meta["framework"] = string(framework)

bunVersion := DetermineVersion(ctx)
meta["bunVersion"] = bunVersion

if framework == types.BunFrameworkHono {
entry := determineEntry(ctx)
if entry != "" {
Expand All @@ -52,8 +47,22 @@ func GetMeta(opt GetMetaOptions) types.PlanMeta {
return meta
}

// CreateBunContext creates a new [PlanContext].
func CreateBunContext(opt GetMetaOptions) *PlanContext {
packageJSON, err := nodejs.DeserializePackageJSON(opt.Src)
if err != nil {
log.Printf("Failed to read package.json: %v", err)
// not fatal
}

return &PlanContext{
PackageJSON: packageJSON,
Src: opt.Src,
}
}

// DetermineFramework determines the framework of the Bun project.
func DetermineFramework(ctx *bunPlanContext) types.BunFramework {
func DetermineFramework(ctx *PlanContext) types.BunFramework {
fw := &ctx.Framework
packageJSON := ctx.PackageJSON

Expand Down Expand Up @@ -85,7 +94,7 @@ func DetermineFramework(ctx *bunPlanContext) types.BunFramework {
return fw.Unwrap()
}

func determineEntry(ctx *bunPlanContext) string {
func determineEntry(ctx *PlanContext) string {
if strings.HasPrefix(ctx.PackageJSON.Scripts["dev"], "bun run --hot") {
return strings.TrimPrefix(ctx.PackageJSON.Scripts["dev"], "bun run --hot ")
}
Expand All @@ -100,3 +109,8 @@ func determineEntry(ctx *bunPlanContext) string {

return ""
}

// DetermineVersion determines the Bun version to use.
func DetermineVersion(ctx *PlanContext) string {
return utils.ConstraintToVersion(ctx.PackageJSON.Engines.Bun, "latest")
}
59 changes: 59 additions & 0 deletions internal/bun/plan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package bun_test

import (
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/zeabur/zbpack/internal/bun"
"github.com/zeabur/zbpack/pkg/plan"
)

func TestBunVersion(t *testing.T) {
t.Parallel()

t.Run("unspecified", func(t *testing.T) {
t.Parallel()

fs := afero.NewMemMapFs()
_ = afero.WriteFile(fs, "package.json", []byte(`{}`), 0o644)

ctx := bun.CreateBunContext(bun.GetMetaOptions{
Src: fs,
Config: plan.NewProjectConfigurationFromFs(fs, ""),
})

version := bun.DetermineVersion(ctx)
assert.Equal(t, "latest", version)
})

t.Run("exact version", func(t *testing.T) {
t.Parallel()

fs := afero.NewMemMapFs()
_ = afero.WriteFile(fs, "package.json", []byte(`{"engines":{"bun":"1.2.3"}}`), 0o644)

ctx := bun.CreateBunContext(bun.GetMetaOptions{
Src: fs,
Config: plan.NewProjectConfigurationFromFs(fs, ""),
})

version := bun.DetermineVersion(ctx)
assert.Equal(t, "1.2", version)
})

t.Run("range version", func(t *testing.T) {
t.Parallel()

fs := afero.NewMemMapFs()
_ = afero.WriteFile(fs, "package.json", []byte(`{"engines":{"bun":"^1.2.3"}}`), 0o644)

ctx := bun.CreateBunContext(bun.GetMetaOptions{
Src: fs,
Config: plan.NewProjectConfigurationFromFs(fs, ""),
})

version := bun.DetermineVersion(ctx)
assert.Equal(t, "1", version)
})
}
31 changes: 30 additions & 1 deletion internal/nodejs/__snapshots__/template_test.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

[TestTemplate_BuildCmd_Bun - 1]
# Install bun if we need it
FROM oven/bun:1.0 as bun-runtime
FROM oven/bun:1.2 as bun-runtime
FROM node:18 as build

ENV PORT=8080
Expand Down Expand Up @@ -74,6 +74,35 @@ FROM scratch as output
COPY --from=build /src///app/dist /


---

[TestTemplate_Bun - 1]
# Install bun if we need it
FROM oven/bun:1 as bun-runtime
FROM node:18 as build

ENV PORT=8080
WORKDIR /src
# Copy the bun binary from the bun-runtime stage directly.
# A bit hacky but it works.
COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin
COPY --from=bun-runtime /usr/local/bin/bunx /usr/local/bin

# Check if we have 'corepack' available; if none, we
# install corepack@0.10.0.
RUN which corepack || npm install -g --force corepack@0.10.0
RUN corepack enable

RUN bun install

COPY . .

# Build if we can build it


EXPOSE 8080
CMD bun start main.ts

---

[TestTemplate_Monorepo - 1]
Expand Down
4 changes: 3 additions & 1 deletion internal/nodejs/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
// TemplateContext is the context for the Node.js Dockerfile template.
type TemplateContext struct {
NodeVersion string
BunVersion string

AppDir string

Expand Down Expand Up @@ -60,7 +61,8 @@ func getContextBasedOnMeta(meta types.PlanMeta) TemplateContext {
OutputDir: meta["outputDir"],

// The flag specific to planner/bun.
Bun: meta["bun"] == "true" || meta["packageManager"] == "bun",
Bun: meta["bun"] == "true" || meta["packageManager"] == "bun",
BunVersion: meta["bunVersion"],
}

return context
Expand Down
1 change: 1 addition & 0 deletions internal/nodejs/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
// PackageJSONEngine is the structure of `package.json`'s `engines` field.
type PackageJSONEngine struct {
Node string `json:"node"`
Bun string `json:"bun,omitempty"`
}

// PackageJSON is the structure of `package.json`.
Expand Down
15 changes: 15 additions & 0 deletions internal/nodejs/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func TestTemplate_BuildCmd_OutputDir(t *testing.T) {
func TestTemplate_BuildCmd_Bun(t *testing.T) {
ctx := nodejs.TemplateContext{
Bun: true,
BunVersion: "1.2",
NodeVersion: "18",
InstallCmd: "RUN bun install",
StartCmd: "bun start main.ts",
Expand Down Expand Up @@ -227,3 +228,17 @@ func TestTemplate_NitroPreset(t *testing.T) {
assert.NotContains(t, result, "ENV NITRO_PRESET")
})
}

func TestTemplate_Bun(t *testing.T) {
ctx := nodejs.TemplateContext{
NodeVersion: "18",
InstallCmd: "RUN bun install",
StartCmd: "bun start main.ts",
Bun: true,
BunVersion: "1",
}

result, err := ctx.Execute()
assert.NoError(t, err)
snaps.MatchSnapshot(t, result)
}
2 changes: 1 addition & 1 deletion internal/nodejs/templates/template.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{- if .Bun -}}
# Install bun if we need it
FROM oven/bun:1.0 as bun-runtime
FROM oven/bun:{{.BunVersion}} as bun-runtime
{{ end -}}
FROM node:{{.NodeVersion}} as build

Expand Down

0 comments on commit 1d9ea28

Please sign in to comment.