Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ jobs:
with:
go-version: 1.24

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest

- name: Build
run: make lint build
run: make build

- name: Test
run: |
Expand Down
20 changes: 18 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
FROM golang:1.24
# Stage 1: Build
FROM golang:1.24-alpine AS builder

ADD . /bert
# Install make and git (required for Makefile and versioning)
RUN apk add --no-cache make git

WORKDIR /bert
COPY . .

# Build the linux amd64 binary
RUN make go-build-linux-amd64

# Stage 2: Runtime
FROM alpine:latest

RUN apk add --no-cache ca-certificates

WORKDIR /bert
COPY --from=builder /bert/bin/bert-linux-amd64 /bert/bin/bert-linux-amd64
COPY --from=builder /bert/test /bert/test

# Set the binary as the entrypoint
ENTRYPOINT ["/bert/bin/bert-linux-amd64"]
48 changes: 44 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,22 @@ PID := $(GOBUILD)/.$(PROJECTNAME).pid
# Make is verbose in Linux. Make it silent.
MAKEFLAGS += --silent

.PHONY: default
default: install lint format test build

## install: Checks for missing dependencies and installs them
.PHONY: install
install: go-get

## format: Formats Go source files
.PHONY: format
format: go-format

lint: go-lint
## lint: Runs all linters including go vet and golangci-lint
.PHONY: lint
lint: go-lint golangci-lint

## build: Builds binaries for all supported platforms
.PHONY: build
build:
@[ -d $(GOBUILD) ] || mkdir -p $(GOBUILD)
Expand All @@ -56,84 +64,113 @@ build:
bin/bert-$(GOHOSTOS)-$(GOHOSTARCH) completion bash > $(GOBUILD)/completions/bert.bash
bin/bert-$(GOHOSTOS)-$(GOHOSTARCH) completion fish > $(GOBUILD)/completions/bert.fish

#@cat $(STDERR) | sed -e '1s/.*/\nError:\n/' | sed 's/make\[.*/ /' | sed "/^/s/^/ /" 1>&2
#@cat $(STDERR) | sed -e '1s/.*/\nError:\n/' | sed 's/make\[.*\]/ /' | sed "/^/s/^/ /" 1>&2


## test: Runs all Go tests
.PHONY: test
test: install go-test

## clean: Removes build artifacts
.PHONY: clean
clean:
@-rm $(GOBIN)/$(PROGRAMNAME)* 2> /dev/null
@-$(MAKE) go-clean

.PHONY: go-lint
go-lint:
@echo " > Linting source files..."
go vet $(MODFLAGS) -c=10 `go list $(MODFLAGS) ./...`

## golangci-lint: Runs golangci-lint
.PHONY: golangci-lint
golangci-lint:
@echo " > Running golangci-lint..."
golangci-lint run

.PHONY: go-format
go-format:
@echo " > Formating source files..."
gofmt -s -w $(GOFILES)

.PHONY: go-build-current
go-build-current:
@echo " > Building $(GOHOSTOS)/$(GOHOSTARCH) binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME) $(GOBASE)/cmd

.PHONY: go-build
go-build: go-get go-build-linux-amd64 go-build-linux-arm64 go-build-linux-arm go-build-darwin-amd64 go-build-darwin-arm64 go-build-windows-amd64 go-build-windows-arm

.PHONY: go-test
go-test:
@echo " > Running Go tests..."
go test $(MODFLAGS) -covermode=count `go list $(MODFLAGS) ./...`

.PHONY: go-build-linux-amd64
go-build-linux-amd64:
@echo " > Building linux amd64 binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_LINUX) GOARCH=$(GOARCH_AMD64) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_LINUX)-$(GOARCH_AMD64) $(GOBASE)/cmd

.PHONY: go-build-linux-arm64
go-build-linux-arm64:
@echo " > Building linux arm64 binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_LINUX) GOARCH=$(GOARCH_ARM64) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_LINUX)-$(GOARCH_ARM64) $(GOBASE)/cmd

.PHONY: go-build-linux-arm
go-build-linux-arm:
@echo " > Building linux arm binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_LINUX) GOARCH=$(GOARCH_ARM) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_LINUX)-$(GOARCH_ARM) $(GOBASE)/cmd

.PHONY: go-build-darwin-amd64
go-build-darwin-amd64:
@echo " > Building darwin amd64 binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_DARWIN) GOARCH=$(GOARCH_AMD64) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_DARWIN)-$(GOARCH_AMD64) $(GOBASE)/cmd

.PHONY: go-build-darwin-arm64
go-build-darwin-arm64:
@echo " > Building darwin arm64 binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_DARWIN) GOARCH=$(GOARCH_ARM64) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_DARWIN)-$(GOARCH_ARM64) $(GOBASE)/cmd

.PHONY: go-build-windows-amd64
go-build-windows-amd64:
@echo " > Building windows amd64 binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_WINDOWS) GOARCH=$(GOARCH_AMD64) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_WINDOWS)-$(GOARCH_AMD64).exe $(GOBASE)/cmd

.PHONY: go-build-windows-arm
go-build-windows-arm:
@echo " > Building windows arm binaries..."
@GOPATH=$(GOPATH) GOOS=$(GOOS_WINDOWS) GOARCH=$(GOARCH_ARM) GOBIN=$(GOBIN) go build $(MODFLAGS) $(LDFLAGS) -o $(GOBIN)/$(PROGRAMNAME)-$(GOOS_WINDOWS)-$(GOARCH_ARM).exe $(GOBASE)/cmd

.PHONY: go-generate
go-generate:
@echo " > Generating dependency files..."
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate)

.PHONY: go-get
go-get:
@echo " > Checking if there is any missing dependencies..."
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go mod tidy

.PHONY: go-install
go-install:
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES)

.PHONY: go-clean
go-clean:
@echo " > Cleaning build cache"
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean $(MODFLAGS) $(GOBASE)/cmd
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean -modcache

.PHONY: run-sanity-tests
run-sanity-tests: build-docker run-linux-dockerized-tests

.PHONY: build-docker
build-docker:
@echo " > Building docker image..."
docker build -t sha1n/$(PROJECTNAME):latest .
docker tag sha1n/$(PROJECTNAME):latest sha1n/$(PROJECTNAME):$(VERSION:v%=%)

.PHONY: run-linux-dockerized-tests
run-linux-dockerized-tests:
@echo " > Running with experimental UI..."
docker run --rm -ti sha1n/bert /bert/bin/bert-linux-amd64 -c /bert/test/data/spec_test_load.yaml
Expand All @@ -148,6 +185,7 @@ run-linux-dockerized-tests:
@echo " > Running with ad-hoc commands..."
docker run --rm -ti sha1n/bert /bert/bin/bert-linux-amd64 'ls' 'ls -laH' --executions 10

.PHONY: release
release:
ifdef GITHUB_TOKEN
@echo " > Releasing..."
Expand All @@ -156,11 +194,13 @@ else
$(error GITHUB_TOKEN is not set)
endif

.PHONY: help
.PHONY: all
all: help

.PHONY: help
help: Makefile
@echo
@echo " Choose a command run in "$(PROJECTNAME)":"
@echo
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
@echo
@echo
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func handlePanics(exitFn func(int)) {
) + "&labels=bug"

yellow := color.New(color.FgYellow)
yellow.Println("\nOh no... Please kindly report this issue by following this URL:")
_, _ = yellow.Println("\nOh no... Please kindly report this issue by following this URL:")
fmt.Printf(`

%s
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/abor_on_error_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ func NewAbortOnErrorListener(delegate api.Listener) api.Listener {
func (l abortOnErrorListener) OnError(id api.ID, err error) {
defer panic(NewAbortionError(id, err))
l.Listener.OnError(id, err)
l.Listener.OnScenarioEnd(id)
l.OnScenarioEnd(id)
}
6 changes: 4 additions & 2 deletions internal/cli/config_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ func runConfigToolFn(ctx api.IOContext) func(*cobra.Command, []string) {
configureOutput(cmd, slog.LevelError, ctx)

writeCloser := ResolveOutputArg(cmd, ArgNameOutputFile, ctx)
defer writeCloser.Close()
defer func() {
_ = writeCloser.Close()
}()

if GetBool(cmd, ArgNameConfigExample) {
io.WriteString(writeCloser, getExampleSpec())
_, _ = io.WriteString(writeCloser, getExampleSpec())

} else {
printHints()
Expand Down
7 changes: 3 additions & 4 deletions internal/cli/config_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cli
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -97,20 +96,20 @@ func configureExampleCommandWithOutFile(t *testing.T, ctx api.IOContext) (rootCm

rootCmd.SetArgs(args)

return rootCmd, configPath, func() { os.Remove(tmpFile.Name()) }
return rootCmd, configPath, func() { _ = os.Remove(tmpFile.Name()) }
}

func configureCommand(t *testing.T, ctx api.IOContext) (rootCmd *cobra.Command, configPath string, teardown func()) {
rootCmd = NewRootCommand(gommonstest.RandomString(), gommonstest.RandomString(), gommonstest.RandomString(), ctx)
cmd := CreateConfigCommand(ctx)
rootCmd.AddCommand(cmd)

tmpFile, err := ioutil.TempFile("", "configureCommand")
tmpFile, err := os.CreateTemp("", "configureCommand")
assert.NoError(t, err)

rootCmd.SetArgs([]string{"config", "--out-file", tmpFile.Name()})

return rootCmd, tmpFile.Name(), func() { os.Remove(tmpFile.Name()) }
return rootCmd, tmpFile.Name(), func() { _ = os.Remove(tmpFile.Name()) }
}

func userInput() string {
Expand Down
5 changes: 3 additions & 2 deletions internal/cli/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ func questionYN(prompt string, ctx api.IOContext) bool {
displayPrompt()
str, _ = reader.ReadString('\n')
str = strings.TrimSpace(strings.ToLower(str))
if str == "y" {
switch str {
case "y":
return true
} else if str == "n" || str == "" {
case "n", "":
return false
}
}
Expand Down
8 changes: 5 additions & 3 deletions internal/cli/main_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ func runFn(ctx api.IOContext) func(*cobra.Command, []string) {

var reportHandler api.ReportHandler
reportHandler, closer, err = resolveReportHandler(cmd, spec, ctx)
defer closer.Close()
defer func() {
_ = closer.Close()
}()

if err == nil {
tracer := exec.NewTracer(spec.Executions * len(spec.Scenarios))
Expand Down Expand Up @@ -215,7 +217,7 @@ func resolveReportHandler(cmd *cobra.Command, spec api.BenchmarkSpec, ctx api.IO
handler = reporthandlers.NewSummaryReportHandler(spec, reportCtx, report.NewTextReportWriter(writer, colorsOn))

default:
err = fmt.Errorf("Invalid report format '%s'", reportFormat)
err = fmt.Errorf("invalid report format '%s'", reportFormat)
}

return handler, writeCloser, err
Expand Down Expand Up @@ -263,7 +265,7 @@ func enableTerminalGUI(cmd *cobra.Command, ctx api.IOContext) bool {
pipeOutputsMode := GetBool(cmd, ArgNamePipeStdout)
pipeOutputsMode = pipeOutputsMode || GetBool(cmd, ArgNamePipeStderr)

return ctx.Tty && enableRichOut && !(silentMode || debugMode || pipeOutputsMode)
return ctx.Tty && enableRichOut && !silentMode && !debugMode && !pipeOutputsMode
}

func terminalDimensionsOrFake() (int, int) {
Expand Down
25 changes: 15 additions & 10 deletions internal/cli/main_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/sha1n/bert/api"
"github.com/sha1n/bert/pkg/specs"
"github.com/sha1n/gommons/pkg/test"
gommonstest "github.com/sha1n/gommons/pkg/test"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -97,8 +96,10 @@ func TestWithMissingConfigFile(t *testing.T) {

func TestWithWDConfigFile(t *testing.T) {
wd, _ := os.Getwd()
os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer os.Chdir(wd)
_ = os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer func() {
_ = os.Chdir(wd)
}()

expectedSpec, _ := specs.LoadSpec(itConfigFilePath)
expectedSpec.Executions = 1
Expand All @@ -113,8 +114,10 @@ func TestWithWDConfigFile(t *testing.T) {

func TestWithWDConfigFileAndExecutionsOptionOverride(t *testing.T) {
wd, _ := os.Getwd()
os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer os.Chdir(wd)
_ = os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer func() {
_ = os.Chdir(wd)
}()

expectedSpec, _ := specs.LoadSpec(itConfigFilePath)
expectedSpec.Executions = expectedSpec.Executions + rand.Intn(10)
Expand All @@ -129,8 +132,10 @@ func TestWithWDConfigFileAndExecutionsOptionOverride(t *testing.T) {

func TestWithWDConfigFileAndAlternateOptionOverride(t *testing.T) {
wd, _ := os.Getwd()
os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer os.Chdir(wd)
_ = os.Chdir("../../test/data") // expecting '../../test/data/.bertconfig' to be loaded
defer func() {
_ = os.Chdir(wd)
}()

expectedSpec, _ := specs.LoadSpec(itConfigFilePath)
expectedSpec.Executions = 1
Expand Down Expand Up @@ -257,23 +262,23 @@ func Test_validatePositionalArgs(t *testing.T) {
name: "call with positional and no executions param",
args: args{
cmd: newDummyCommandWith("-c", "/some-file"),
args: []string{test.RandomString(), test.RandomString()},
args: []string{gommonstest.RandomString(), gommonstest.RandomString()},
},
wantErr: true,
},
{
name: "call with positional and invalid executions param",
args: args{
cmd: newDummyCommandWith("--executions", "-1"),
args: []string{test.RandomString(), test.RandomString()},
args: []string{gommonstest.RandomString(), gommonstest.RandomString()},
},
wantErr: true,
},
{
name: "call with positional and valid executions param",
args: args{
cmd: newDummyCommandWith("--executions", "100"),
args: []string{test.RandomString(), test.RandomString()},
args: []string{gommonstest.RandomString(), gommonstest.RandomString()},
},
wantErr: false,
},
Expand Down
Loading
Loading