Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ jobs:
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# The tag, branch or commit the image is published as, so lightchain-worker version names it.
build-args: VERSION=${{ steps.meta.outputs.version }}

# A version tag only, and after the image, so a release never exists without the image of the same version. A tag
# with a hyphen (v1.2.3-rc.1) becomes a pre-release, which GitHub does not serve as "latest", so the installer's
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ COPY worker/go.mod worker/go.sum* worker/
RUN cd worker && GOWORK=off go mod download
COPY pkg/ pkg/
COPY worker/ worker/
ARG VERSION=dev
RUN printf 'go 1.26\n\nuse (\n\t./pkg\n\t./worker\n)\n' > go.work
RUN cd worker && CGO_ENABLED=0 GOOS=linux go build -o /bin/worker ./cmd/sidecar
RUN cd worker && CGO_ENABLED=0 GOOS=linux go build -o /bin/lightchain-worker ./cmd/cli
RUN cd worker && CGO_ENABLED=0 GOOS=linux go build -ldflags="-X github.com/lightchain/worker/internal/buildinfo.Version=${VERSION}" -o /bin/worker ./cmd/sidecar
RUN cd worker && CGO_ENABLED=0 GOOS=linux go build -ldflags="-X github.com/lightchain/worker/internal/buildinfo.Version=${VERSION}" -o /bin/lightchain-worker ./cmd/cli

FROM alpine:3.19
RUN apk add --no-cache ca-certificates && adduser -D -u 1000 appuser
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
# from the orchestrator checkout, where go.work already joins both.
DIST ?= dist
PLATFORMS ?= linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
# The release tag on a tag build in CI, otherwise the nearest tag from git (or
# "dev"). Override with make release VERSION=v1.2.3.
VERSION ?= $(shell if [ "$$GITHUB_REF_TYPE" = tag ]; then echo "$$GITHUB_REF_NAME"; else git describe --tags --always --dirty 2>/dev/null || echo dev; fi)
LDFLAGS := -s -w -X github.com/lightchain/worker/internal/buildinfo.Version=$(VERSION)
SHA256 := $(shell command -v sha256sum >/dev/null 2>&1 && echo sha256sum || echo shasum -a 256)

.PHONY: release test-install

release:
rm -rf $(DIST) && mkdir -p $(DIST)
for p in $(PLATFORMS); do \
CGO_ENABLED=0 GOOS=$${p%/*} GOARCH=$${p#*/} go build -trimpath -ldflags="-s -w" \
CGO_ENABLED=0 GOOS=$${p%/*} GOARCH=$${p#*/} go build -trimpath -ldflags="$(LDFLAGS)" \
-o $(DIST)/lightchain-worker-$${p%/*}-$${p#*/} ./cmd/cli || exit 1; \
done
cp scripts/install.sh $(DIST)/
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ git tag v1.2.3 <commit> && git push origin v1.2.3

CI runs the checks on the tag, publishes the image as `ghcr.io/lightchain-protocol/worker:v1.2.3`, and creates the GitHub release with everything `make release` writes to `dist/`: one CLI binary per platform (`lightchain-worker-{linux,darwin}-{amd64,arm64}`), `install.sh`, and a `checksums.txt` (SHA-256) covering them all. The release notes are generated from the merged pull requests and can be edited afterwards.

Release binaries and the image carry the tag: `lightchain-worker version` prints it, with the commit when the build recorded one, and the worker logs it on start. A plain `go build` reports `dev`.

The installer downloads from `…/releases/latest/download/` (or `…/releases/download/<tag>/` with `LIGHTCHAIN_WORKER_VERSION`), so a release must carry all of those files. A tag with a hyphen (`v1.2.3-rc.1`) becomes a pre-release: it can be installed by naming it, and `latest` stays where it was.
6 changes: 6 additions & 0 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
// status Check on-chain registration status
// preflight Read-only go-live checks (RPC, registration, stake, models, gateway, Ollama, beacon)
// watch Read-only daemon that posts webhook alerts when the worker stops being able to claim
// version Print the build: release tag, commit, Go version, platform
package main

import (
Expand All @@ -42,6 +43,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/redis/go-redis/v9"

"github.com/lightchain/worker/internal/buildinfo"
"github.com/lightchain/worker/internal/chain"
"github.com/lightchain/worker/internal/cli"
"github.com/lightchain/worker/internal/config"
Expand Down Expand Up @@ -100,6 +102,8 @@ func main() {
runWithdraw()
case "release":
runRelease()
case "version", "--version":
fmt.Println(buildinfo.String())
case "help", "-h", "--help":
printUsage()
default:
Expand Down Expand Up @@ -143,6 +147,8 @@ Commands:
watch Daemon: alert a Discord-compatible webhook when liveness, heartbeat,
Ollama, registration, suspension, stake or recent claims go bad,
and again on recovery. Never remediates (no tx, no drain).
version Print the build: release tag, commit, Go version, platform.
Include it when reporting a problem.

balance/withdraw/release additionally require JOB_REGISTRY_ADDRESS.
release additionally reads RELEASE_STATE_PATH (and other RELEASE_* vars).
Expand Down
81 changes: 81 additions & 0 deletions internal/buildinfo/buildinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Package buildinfo reports which build of the worker is running, so an
// operator (and anyone helping them) can tell which release a host is on.
package buildinfo

import (
"fmt"
"runtime"
"runtime/debug"
)

// Version is the release tag, stamped at build time:
//
// go build -ldflags "-X github.com/lightchain/worker/internal/buildinfo.Version=v1.2.3"
//
// make release and the Dockerfile set it. A plain go build leaves "dev".
var Version = "dev"

// Info describes the running build.
type Info struct {
Version string // release tag, or "dev"
Revision string // VCS commit, when the Go toolchain recorded one
Modified bool // the build's working tree had uncommitted changes
Go string // Go toolchain version
Platform string // GOOS/GOARCH
}

// Get returns the running build's Info.
func Get() Info {
return fromBuildInfo(Version, debug.ReadBuildInfo)
}

// String is Get formatted for one line of output.
func String() string {
return Get().String()
}

func fromBuildInfo(version string, read func() (*debug.BuildInfo, bool)) Info {
info := Info{
Version: version,
Go: runtime.Version(),
Platform: runtime.GOOS + "/" + runtime.GOARCH,
}
if version == "" {
info.Version = "dev"
}
bi, ok := read()
if !ok || bi == nil {
return info
}
if bi.GoVersion != "" {
info.Go = bi.GoVersion
}
for _, s := range bi.Settings {
switch s.Key {
case "vcs.revision":
info.Revision = s.Value
case "vcs.modified":
info.Modified = s.Value == "true"
}
}
return info
}

// String formats the build as, for example,
// "lightchain-worker v1.2.3 (commit 1a2b3c4d5e6f, go1.26.0, linux/amd64)".
// The commit is left out when the build did not record one, as in the
// Docker image, which builds without the .git directory.
func (i Info) String() string {
details := ""
if i.Revision != "" {
rev := i.Revision
if len(rev) > 12 {
rev = rev[:12]
}
if i.Modified {
rev += "-dirty"
}
details = "commit " + rev + ", "
}
return fmt.Sprintf("lightchain-worker %s (%s%s, %s)", i.Version, details, i.Go, i.Platform)
}
90 changes: 90 additions & 0 deletions internal/buildinfo/buildinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package buildinfo

import (
"runtime"
"runtime/debug"
"testing"
)

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

platform := runtime.GOOS + "/" + runtime.GOARCH

tests := []struct {
name string
version string
read func() (*debug.BuildInfo, bool)
want Info
str string
}{
{
name: "release with commit",
version: "v1.2.3",
read: func() (*debug.BuildInfo, bool) {
return &debug.BuildInfo{
GoVersion: "go1.26.0",
Settings: []debug.BuildSetting{
{Key: "vcs.revision", Value: "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b"},
{Key: "vcs.modified", Value: "false"},
},
}, true
},
want: Info{Version: "v1.2.3", Revision: "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b", Go: "go1.26.0", Platform: platform},
str: "lightchain-worker v1.2.3 (commit 1a2b3c4d5e6f, go1.26.0, " + platform + ")",
},
{
name: "local build with uncommitted changes",
version: "dev",
read: func() (*debug.BuildInfo, bool) {
return &debug.BuildInfo{
GoVersion: "go1.26.0",
Settings: []debug.BuildSetting{
{Key: "vcs.revision", Value: "abc123"},
{Key: "vcs.modified", Value: "true"},
},
}, true
},
want: Info{Version: "dev", Revision: "abc123", Modified: true, Go: "go1.26.0", Platform: platform},
str: "lightchain-worker dev (commit abc123-dirty, go1.26.0, " + platform + ")",
},
{
name: "no VCS data, as in the Docker image",
version: "v1.2.3",
read: func() (*debug.BuildInfo, bool) {
return &debug.BuildInfo{GoVersion: "go1.26.0"}, true
},
want: Info{Version: "v1.2.3", Go: "go1.26.0", Platform: platform},
str: "lightchain-worker v1.2.3 (go1.26.0, " + platform + ")",
},
{
name: "build info unavailable, empty version",
version: "",
read: func() (*debug.BuildInfo, bool) { return nil, false },
want: Info{Version: "dev", Go: runtime.Version(), Platform: platform},
str: "lightchain-worker dev (" + runtime.Version() + ", " + platform + ")",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got := fromBuildInfo(tc.version, tc.read)
if got != tc.want {
t.Errorf("fromBuildInfo() = %+v, want %+v", got, tc.want)
}
if s := got.String(); s != tc.str {
t.Errorf("String() = %q, want %q", s, tc.str)
}
})
}
}

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

// Tests are built without the release ldflags, so Version keeps its default.
if got := Get().Version; got != "dev" {
t.Errorf("Get().Version = %q, want %q", got, "dev")
}
}
3 changes: 3 additions & 0 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
pkgtypes "github.com/lightchain/pkg/types"

"github.com/lightchain/worker/internal/blob"
"github.com/lightchain/worker/internal/buildinfo"
"github.com/lightchain/worker/internal/chain"
"github.com/lightchain/worker/internal/config"
gw "github.com/lightchain/worker/internal/gateway"
Expand Down Expand Up @@ -1461,6 +1462,8 @@ func checksumHexNoPrefix(addr common.Address) string {
// down and returns the process exit code. The sidecar binary and the CLI's
// `run` command both use it, so a host can serve with the CLI binary alone.
func Main() int {
slog.Info("lightchain worker starting", "build", buildinfo.String())

cfg, err := config.Load()
if err != nil {
slog.Error("config load failed", "error", err)
Expand Down