From f2977a990ef9176deefbc3391361e98072913fec Mon Sep 17 00:00:00 2001 From: Emil Date: Sun, 2 Aug 2026 17:54:06 +0300 Subject: [PATCH] Add versioned release builds for coordinator and worker --- .github/workflows/release.yml | 104 +++++++++++++++++++++++++++ coordinator/Dockerfile | 5 +- coordinator/Makefile | 8 ++- coordinator/cmd/coordinator/main.go | 12 ++++ coordinator/cmd/worker-agent/main.go | 12 ++++ mkdocs/index.md | 21 ++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..be2a07b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,104 @@ +name: release + +# Builds static coordinator and worker-agent binaries for every major +# platform on a v* tag push, attaches them (plus SHA-256 checksums) to the +# GitHub Release, and pushes the coordinator image to GHCR. +# +# git tag v1.0.0 && git push origin v1.0.0 +on: + push: + tags: ["v*"] + +permissions: + contents: write + packages: write + +jobs: + binaries: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [linux, darwin, windows] + arch: [amd64, arm64] + defaults: + run: + working-directory: coordinator + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: coordinator/go.mod + cache-dependency-path: coordinator/go.sum + + - name: vet + run: go vet ./... + + - name: build coordinator and worker-agent + env: + VERSION: ${{ github.ref_name }} + run: | + mkdir -p dist + for cmd in coordinator worker-agent; do + CGO_ENABLED=0 GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} \ + go build -trimpath \ + -ldflags="-s -w -X main.version=${VERSION#v}" \ + -o "dist/${cmd}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.os == 'windows' && '.exe' || '' }}" \ + "./cmd/${cmd}" + done + + - uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.arch }} + path: coordinator/dist/* + if-no-files-found: error + + release: + needs: binaries + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: checksums + working-directory: artifacts + run: sha256sum * > SHA256SUMS.txt + + - uses: softprops/action-gh-release@v2 + with: + files: artifacts/* + generate_release_notes: true + + image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/${{ github.repository }}/coordinator + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + + - uses: docker/build-push-action@v6 + with: + context: coordinator + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ github.ref_name }} diff --git a/coordinator/Dockerfile b/coordinator/Dockerfile index 1eafa17..3d9e552 100644 --- a/coordinator/Dockerfile +++ b/coordinator/Dockerfile @@ -16,6 +16,9 @@ RUN --mount=type=cache,target=/go/pkg/mod go mod download COPY . . +# Release builds inject the tag via build-arg; local builds stay "dev". +ARG VERSION=dev + # The cache mounts persist the module cache and the compiler's build cache # *across* builds, so a rebuild after a code edit recompiles only what changed # instead of the whole dependency tree. @@ -25,7 +28,7 @@ COPY . . RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 GOOS=linux go build \ - -trimpath -ldflags="-s -w" \ + -trimpath -ldflags="-s -w -X main.version=${VERSION#v}" \ -o /out/coordinator ./cmd/coordinator # --- runtime stage -------------------------------------------------------- diff --git a/coordinator/Makefile b/coordinator/Makefile index 835f360..6a92c10 100644 --- a/coordinator/Makefile +++ b/coordinator/Makefile @@ -31,16 +31,20 @@ DEMO_DIR ?= .demo # whenever workloads or their manifests change (requires the Python venv). WORKLOADS_JSON := internal/workloads/workloads.json +# Version injected into the binaries via -ldflags; falls back to "dev". +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +LDFLAGS := -s -w -X main.version=$(VERSION) + # The Go worker agent: a static coordinator client that executes SDK # workloads in a Python subprocess per claimed task. agent: - CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bin/worker-agent ./cmd/worker-agent + CGO_ENABLED=0 go build -trimpath -ldflags="$(LDFLAGS)" -o bin/worker-agent ./cmd/worker-agent @printf '%s\n' 'Built bin/worker-agent. Configure via environment:' ' COORDINATOR_URL, WORKER_AUTH_TOKEN, WORK_DIR, CPU_COUNT, MEMORY_MB,' ' POLL_INTERVAL, REQUEST_TIMEOUT, HEARTBEAT_INTERVAL, CAPABILITIES,' ' TASK_RUNNER, MAX_TASKS, EXIT_WHEN_IDLE, WORKER_NAME, WORKER_ID' # The coordinator server as a static binary, the same way the Docker image # builds it (CGO_ENABLED=0, trimmed). Requires PostgreSQL at runtime. coordinator: - CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bin/coordinator ./cmd/coordinator + CGO_ENABLED=0 go build -trimpath -ldflags="$(LDFLAGS)" -o bin/coordinator ./cmd/coordinator @printf '%s\n' \ 'Built bin/coordinator. Configure via environment:' \ ' DATABASE_URL, COORDINATOR_ADDR, COORDINATOR_TOKEN, UI_AUTH_TOKEN,' \ diff --git a/coordinator/cmd/coordinator/main.go b/coordinator/cmd/coordinator/main.go index 3607442..afc0477 100644 --- a/coordinator/cmd/coordinator/main.go +++ b/coordinator/cmd/coordinator/main.go @@ -2,6 +2,8 @@ package main import ( "context" + "flag" + "fmt" "log/slog" "os" "os/signal" @@ -17,7 +19,17 @@ import ( "github.com/emil28092005/SciMesh/coordinator/internal/workloads" ) +// version is injected at build time (-ldflags "-X main.version=...") and +// reported by --version. "dev" marks a local build. +var version = "dev" + func main() { + showVersion := flag.Bool("version", false, "print the build version and exit") + flag.Parse() + if *showVersion { + fmt.Println("coordinator " + version) + return + } // All work happens in run() so its defers (pool.Close, log flush, signal // stop) still execute: os.Exit skips deferred calls entirely. if err := run(); err != nil { diff --git a/coordinator/cmd/worker-agent/main.go b/coordinator/cmd/worker-agent/main.go index 225342d..b7e5b4e 100644 --- a/coordinator/cmd/worker-agent/main.go +++ b/coordinator/cmd/worker-agent/main.go @@ -3,13 +3,25 @@ package main import ( + "flag" + "fmt" "log/slog" "os" "github.com/emil28092005/SciMesh/coordinator/internal/agent" ) +// version is injected at build time (-ldflags "-X main.version=...") and +// reported by --version. "dev" marks a local build. +var version = "dev" + func main() { + showVersion := flag.Bool("version", false, "print the build version and exit") + flag.Parse() + if *showVersion { + fmt.Println("worker-agent " + version) + return + } config, err := agent.LoadConfig() if err != nil { slog.Error("invalid configuration", "error", err) diff --git a/mkdocs/index.md b/mkdocs/index.md index c4864f6..21f3c96 100644 --- a/mkdocs/index.md +++ b/mkdocs/index.md @@ -67,6 +67,27 @@ make demo-ui # open http://localhost:18080/ui (root@scimesh.local / rootpassword) ``` +## Prebuilt binaries + +Every `v*` tag pushes a GitHub Release with static binaries for +`coordinator` and `worker-agent` on linux/darwin/windows × amd64/arm64 +(plus SHA-256 checksums) and the `coordinator` image on GHCR. Download and +run: + +```bash +curl -L -o coordinator https://github.com/emil28092005/SciMesh/releases/latest/download/coordinator-linux-amd64 +chmod +x coordinator +``` + +- **worker-agent** runs anywhere with Python: it spawns + `python -m scimesh.worker.task`, so the machine needs the `scimesh` + package in a venv (`pip install scimesh`) and the usual environment: + `COORDINATOR_URL`, `WORKER_AUTH_TOKEN`, `WORK_DIR`. +- **coordinator** needs PostgreSQL with the applied migrations (the binary + never migrates on startup) plus `DATABASE_URL`, `COORDINATOR_STORAGE_DIR`, + and `JWT_SECRET`; the UI login additionally requires `USERSERVICE_URL`. + `coordinator --version` / `worker-agent --version` print the build tag. + Build and serve this documentation site: ```bash