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
43 changes: 43 additions & 0 deletions .github/workflows/build-release-binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# src https://github.com/akrabat/rodeo/blob/main/.github/workflows/build-release-binaries.yml
name: Build Release Binaries

on:
release:
types:
- created

jobs:
build:
name: Build Release Assets
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.22

- name: Display the version of go that we have installed
run: go version

- name: Display the release tag
run: echo ${{ github.event.release.tag_name }}

- name: "DEBUG: What's our directory & what's in it?"
run: pwd && ls

- name: Build the Kompanion executables
run: ./build-executables.sh ${{ github.event.release.tag_name }}

- name: List the Kompanion executables
run: ls -l ./release

- name: Upload the Kompanion binaries
uses: actions/svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
file: ./release/kompanion-*
file_glob: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ go.work
# Data for local development
data/
bin/
release/
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ FROM golang:1.22.5-alpine
ENV GIN_MODE=release

WORKDIR /
COPY --from=builder /app/config /config
COPY --from=builder /app/migrations /migrations
COPY --from=builder /app/web /web
COPY --from=builder /bin/app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@ Features, that can buy you in:
1. you need a postgresql instance
2. run `docker run -e KOMPANION_PG_URL=postgres://... -e KOMPANION_AUTH_PASSWORD=password -e KOMPANION_AUTH_USERNAME=username kompanion` , where you pass pg url and admin username and password to init

### Pre-compiled binary

1. download archive with latest binary from [Releases page](https://github.com/vanadium23/kompanion/releases)
2. run `KOMPANION_PG_URL=postgres://... -e KOMPANION_AUTH_PASSWORD=password -e KOMPANION_AUTH_USERNAME=username ./kompanion`, it will start server with provided postgresql and admin credentials

### Configuration

- `KOMPANION_AUTH_USERNAME` - required for setup
- `KOMPANION_AUTH_PASSWORD` - required for setup
- `KOMPANION_AUTH_STORAGE` - postgres or memory (default: postgres)
- `KOMPANION_HTTP_PORT` - port for service (default: postgres)
- `KOMPANION_HTTP_PORT` - port for service (default: 8080)
- `KOMPANION_LOG_LEVEL` - debug, info, error (default: info)
- `KOMPANION_PG_POOL_MAX` - integer number for pooling connections (default: 2)
- `KOMPANION_PG_URL` - postgresql link
Expand Down
11 changes: 11 additions & 0 deletions assets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kompanion

import (
"embed"
)

//go:embed migrations/*.sql
var Migrations embed.FS

//go:embed web/*
var WebAssets embed.FS
61 changes: 61 additions & 0 deletions build_release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash

# src: https://github.com/akrabat/rodeo/blob/main/build-executables.sh

version=$1
if [[ -z "$version" ]]; then
echo "usage: $0 <version>"
exit 1
fi
package_name=kompanion

#
# The full list of the platforms is at: https://golang.org/doc/install/source#environment
platforms=(
"darwin/amd64"
"darwin/arm64"
"linux/amd64"
"linux/arm"
"linux/arm64"
"windows/amd64"
)

rm -rf release/
mkdir -p release

for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
os=${platform_split[0]}
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}

if [ $os = "darwin" ]; then
os="macOS"
fi

output_name=$package_name'-'$version'-'$os'-'$GOARCH
zip_name=$output_name
if [ $os = "windows" ]; then
output_name+='.exe'
fi

echo "Building release/$output_name..."
env GOOS=$GOOS GOARCH=$GOARCH go build \
-ldflags "-X main.Version=$version" \
-o release/$output_name
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi

pushd release > /dev/null
if [ $os = "windows" ]; then
zip $zip_name.zip $output_name
rm $output_name
else
chmod a+x $output_name
gzip $output_name
fi
popd > /dev/null
done
29 changes: 29 additions & 0 deletions docs/adr/0010-provide-single-binary-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 10. Provide single binary setup

Date: 2025-03-02

## Status

Accepted

## Context

Not all software are good to be distributed via docker. Some users may prefer to just run a single binary, and have something like `curl && ./run`. But currently we need to be migrations and web folders to be placed with binaries.

## Decision

Embed static files inside binary and adjust code to it. Also, we need to adjust CI to place the binary in release.

We have several options to implement embeded FS:
- `go:embed`
- `go-bindata`
- `go-rice`

We use `go:embed`, because it will not introduce new dependencies.


## Consequences

- (+) Single binary is enough, but still requires postgresql
- (-) Binary will be larger

11 changes: 9 additions & 2 deletions internal/app/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"time"

"github.com/golang-migrate/migrate/v4"
"github.com/vanadium23/kompanion"

// migrate tools
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/golang-migrate/migrate/v4/source/iofs"
)

const (
Expand All @@ -33,8 +35,13 @@ func init() {
m *migrate.Migrate
)

d, err := iofs.New(kompanion.Migrations, "migrations")
if err != nil {
log.Fatal(err)
}

for attempts > 0 {
m, err = migrate.New("file://migrations", databaseURL)
m, err = migrate.NewWithSourceInstance("iofs", d, databaseURL)
if err == nil {
break
}
Expand Down
49 changes: 33 additions & 16 deletions internal/controller/http/web/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,22 @@ import (
"encoding/json"
"fmt"
"html/template"
"io/fs"
"net/http"
"path/filepath"
"time"

"github.com/foolin/goview"
"github.com/foolin/goview/supports/ginview"
"github.com/gin-gonic/gin"
"github.com/vanadium23/kompanion"
"github.com/vanadium23/kompanion/internal/auth"
"github.com/vanadium23/kompanion/internal/library"
"github.com/vanadium23/kompanion/internal/stats"
"github.com/vanadium23/kompanion/internal/sync"
"github.com/vanadium23/kompanion/pkg/logger"
)

func formatDuration(seconds int) string {
duration := time.Duration(seconds) * time.Second
hours := int(duration.Hours())
minutes := int(duration.Minutes()) % 60
secs := int(duration.Seconds()) % 60

if hours > 0 {
return fmt.Sprintf("%dh %dm %ds", hours, minutes, secs)
} else if minutes > 0 {
return fmt.Sprintf("%dm %ds", minutes, secs)
}
return fmt.Sprintf("%ds", secs)
}

func NewRouter(
handler *gin.Engine,
l logger.Interface,
Expand All @@ -46,7 +36,11 @@ func NewRouter(
c.Set("startTime", time.Now())
})
// static files
handler.Static("/static", "web/static")
staticFs, err := fs.Sub(kompanion.WebAssets, "web/static")
if err != nil {
l.Error("Failed to get static files: %v", err)
}
handler.StaticFS("/static", http.FS(staticFs))

config := goview.DefaultConfig
config.Root = "web/templates"
Expand All @@ -71,7 +65,9 @@ func NewRouter(
return template.HTMLEscapeString(version)
},
}
handler.HTMLRender = ginview.New(config)
gv := ginview.New(config)
gv.SetFileHandler(embeddedFH)
handler.HTMLRender = gv

// Home
handler.GET("/", func(c *gin.Context) {
Expand Down Expand Up @@ -103,3 +99,24 @@ func passStandartContext(c *gin.Context, data gin.H) gin.H {
data["startTime"] = c.GetTime("startTime")
return data
}

func formatDuration(seconds int) string {
duration := time.Duration(seconds) * time.Second
hours := int(duration.Hours())
minutes := int(duration.Minutes()) % 60
secs := int(duration.Seconds()) % 60

if hours > 0 {
return fmt.Sprintf("%dh %dm %ds", hours, minutes, secs)
} else if minutes > 0 {
return fmt.Sprintf("%dm %ds", minutes, secs)
}
return fmt.Sprintf("%ds", secs)
}

// https://github.com/foolin/goview/issues/25#issuecomment-876889943
func embeddedFH(config goview.Config, tmpl string) (string, error) {
path := filepath.Join(config.Root, tmpl)
bytes, err := kompanion.WebAssets.ReadFile(path + config.Extension)
return string(bytes), err
}
Loading