diff --git a/.gitignore b/.gitignore index 66fd13c..e692b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.dll *.so *.dylib +bin # Test binary, built with `go test -c` *.test @@ -13,3 +14,8 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +# Other +.history +.vscode +*.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..daff999 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# builder image +FROM golang:alpine as builder +ARG TARGETPLATFORM +COPY . /src +WORKDIR /src +RUN apk add --no-cache make bash git +RUN make app-build PLATFORMS=$TARGETPLATFORM + +# running image +FROM alpine +WORKDIR /home +COPY --from=builder /src/bin/goobar-* /bin/goobar + +# executable +ENTRYPOINT [ "/bin/goobar" ] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3ad1339 --- /dev/null +++ b/Makefile @@ -0,0 +1,73 @@ +#!/bin/bash + +SHELL = /bin/bash +PLATFORMS ?= linux/amd64 darwin/amd64 windows/amd64 +IMAGE_PREFIX ?= igolaizola +REPO_NAME ?= goobar +COMMIT_SHORT ?= $(shell git rev-parse --verify --short HEAD) +VERSION ?= $(COMMIT_SHORT) +VERSION_NOPREFIX ?= $(shell echo $(VERSION) | sed -e 's/^[[v]]*//') + +# Build the binaries for the current platform +.PHONY: build +build: + os=$$(go env GOOS); \ + arch=$$(go env GOARCH); \ + PLATFORMS="$$os/$$arch" make app-build + +# Build the binaries +# Example: PLATFORMS=linux/amd64 make app-build +.PHONY: app-build +app-build: + @for platform in $(PLATFORMS) ; do \ + os=$$(echo $$platform | cut -f1 -d/); \ + arch=$$(echo $$platform | cut -f2 -d/); \ + arm=$$(echo $$platform | cut -f3 -d/); \ + arm=$${arm#v}; \ + ext=""; \ + if [ "$$os" == "windows" ]; then \ + ext=".exe"; \ + fi; \ + file=./bin/$(REPO_NAME)-$(VERSION_NOPREFIX)-$$(echo $$platform | tr / -)$$ext; \ + GOOS=$$os GOARCH=$$arch GOARM=$$arm CGO_ENABLED=0 \ + go build \ + -a -x -tags netgo,timetzdata -installsuffix cgo -installsuffix netgo \ + -ldflags " \ + -X main.Version=$(VERSION_NOPREFIX) \ + -X main.GitRev=$(COMMIT_SHORT) \ + " \ + -o $$file \ + ./cmd/$(REPO_NAME); \ + if [ $$? -ne 0 ]; then \ + exit 1; \ + fi; \ + chmod +x $$file; \ + done + +# Build the docker image +# Example: PLATFORMS=linux/amd64 make docker-build +.PHONY: docker-build +docker-build: + @platforms=($(PLATFORMS)); \ + platform=$${platforms[0]}; \ + if [[ $${#platforms[@]} -ne 1 ]]; then \ + echo "Multi-arch build not supported"; \ + exit 1; \ + fi; \ + docker build --platform $$platform -t $(IMAGE_PREFIX)/$(REPO_NAME):$(VERSION) .; \ + if [ $$? -ne 0 ]; then \ + exit 1; \ + fi + +# Build the docker images using buildx +# Example: PLATFORMS="linux/amd64 darwin/amd64 windows/amd64" make docker-buildx +.PHONY: docker-buildx +docker-buildx: + @platforms=($(PLATFORMS)); \ + platform=$$(IFS=, ; echo "$${platforms[*]}"); \ + docker buildx build --platform $$platform -t $(IMAGE_PREFIX)/$(REPO_NAME):$(VERSION) . + +# Clean binaries +.PHONY: clean +clean: + rm -rf bin diff --git a/cmd/goobar/main.go b/cmd/goobar/main.go new file mode 100644 index 0000000..cc33959 --- /dev/null +++ b/cmd/goobar/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "context" + "errors" + "flag" + "log" + "os" + "os/signal" + + "github.com/igolaizola/goobar" + "github.com/peterbourgon/ff/v3" + "github.com/peterbourgon/ff/v3/ffcli" +) + +func main() { + // Create signal based context + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + // Launch command + cmd := newCommand() + if err := cmd.ParseAndRun(ctx, os.Args[1:]); err != nil { + log.Fatal(err) + } +} + +func newCommand() *ffcli.Command { + fs := flag.NewFlagSet("goobar", flag.ExitOnError) + + return &ffcli.Command{ + ShortUsage: "goobar [flags] ", + FlagSet: fs, + Exec: func(context.Context, []string) error { + return flag.ErrHelp + }, + Subcommands: []*ffcli.Command{ + newServeCommand(), + newRunCommand(), + }, + } +} + +func newServeCommand() *ffcli.Command { + fs := flag.NewFlagSet("serve", flag.ExitOnError) + _ = fs.String("config", "", "config file (optional)") + + port := fs.Int("port", 0, "port number") + + return &ffcli.Command{ + Name: "serve", + ShortUsage: "goobar serve [flags] ", + Options: []ff.Option{ + ff.WithConfigFileFlag("config"), + ff.WithConfigFileParser(ff.PlainParser), + ff.WithEnvVarPrefix("GOOBAR"), + }, + ShortHelp: "run goobar server", + FlagSet: fs, + Exec: func(ctx context.Context, args []string) error { + if *port == 0 { + return errors.New("missing port") + } + return goobar.Serve(ctx, *port) + }, + } +} + +func newRunCommand() *ffcli.Command { + fs := flag.NewFlagSet("run", flag.ExitOnError) + _ = fs.String("config", "", "config file (optional)") + + return &ffcli.Command{ + Name: "run", + ShortUsage: "goobar serve [flags] ", + Options: []ff.Option{ + ff.WithConfigFileFlag("config"), + ff.WithConfigFileParser(ff.PlainParser), + ff.WithEnvVarPrefix("GOOBAR"), + }, + ShortHelp: "run goobar action", + FlagSet: fs, + Exec: func(ctx context.Context, args []string) error { + return goobar.Run(ctx) + }, + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..15cf8b0 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/igolaizola/goobar + +go 1.19 + +require github.com/peterbourgon/ff/v3 v3.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bdc8b56 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/peterbourgon/ff/v3 v3.3.0 h1:PaKe7GW8orVFh8Unb5jNHS+JZBwWUMa2se0HM6/BI24= +github.com/peterbourgon/ff/v3 v3.3.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= diff --git a/goobar.go b/goobar.go new file mode 100644 index 0000000..57e5979 --- /dev/null +++ b/goobar.go @@ -0,0 +1,25 @@ +package goobar + +import ( + "context" + "log" + "time" +) + +// Server serves the goobar server. +func Serve(ctx context.Context, port int) error { + log.Printf("server listening on port %d\n", port) + <-ctx.Done() + return nil +} + +// Run runs the goobar process. +func Run(ctx context.Context) error { + log.Println("running") + defer log.Println("finished") + select { + case <-ctx.Done(): + case <-time.After(5 * time.Second): + } + return nil +}