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
8 changes: 7 additions & 1 deletion .github/workflows/release-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,13 @@ jobs:
mingw-w64-ucrt-x86_64-gcc

- name: Build shared library
run: go build -buildmode=c-shared -o ${{ matrix.output }} ./cmd/moss-ffi
# Stamp the tag so Moss_Version reports it. A host loads this library by
# path at runtime, so without the stamp there is no way to tell which
# build it actually got.
run: >-
go build -buildmode=c-shared
-ldflags "-X main.buildVersion=${{ github.ref_name }}"
-o ${{ matrix.output }} ./cmd/moss-ffi

- name: Package shared artifact
shell: pwsh
Expand Down
19 changes: 19 additions & 0 deletions cmd/moss-ffi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ import (

const relayFFITimeout = 5 * time.Second

// buildVersion is stamped at link time by the release workflow
// (-ldflags "-X main.buildVersion=v0.8.17"). A library built any other way says
// so rather than claiming a version it cannot know.
var buildVersion = "dev"

var (
handleCounter atomic.Int64
registryMu sync.RWMutex
Expand Down Expand Up @@ -371,6 +376,20 @@ func Moss_GetNATType(handle C.MossHandle) *C.char {
// allocated C string (free with Moss_Free), or NULL if the handle is unknown.
// Call it before Moss_Stop, which removes the handle from the registry.
//
// Moss_Version returns the version this library was built at, as a newly
// allocated C string (free with Moss_Free). Release builds carry their tag;
// anything else reports "dev".
//
// A host loads moss by path at runtime, so nothing stops an old library from
// sitting next to a new host — and the symptoms of that are transport bugs the
// host cannot diagnose. This lets a host say which library it got instead of
// guessing. Callers must treat a missing symbol as "older than v0.8.17".
//
//export Moss_Version
func Moss_Version() *C.char {
return C.CString(buildVersion)
}

//export Moss_LastError
func Moss_LastError(handle C.MossHandle) *C.char {
node, code := getNode(int64(handle))
Expand Down
69 changes: 69 additions & 0 deletions cmd/moss-ffi/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)

// An unstamped build must say "dev" rather than claim a version it cannot know.
// The release workflow is the only thing that knows the tag, so this is the
// value a hand-built library carries — and a host can tell the two apart.
func TestUnstampedBuildReportsDev(t *testing.T) {
if buildVersion != "dev" {
t.Fatalf("an unstamped build reports %q; the default must stay %q so a "+
"library built outside the release pipeline never claims a release",
buildVersion, "dev")
}
}

// The stamp has to survive the linker flag the release workflow passes, in a
// real c-shared build rather than a plain test binary. Without it Moss_Version
// reports "dev" for every published release and a host cannot tell which
// library it loaded — which is the whole reason the symbol exists.
func TestVersionStampReachesTheSharedLibrary(t *testing.T) {
if testing.Short() {
t.Skip("skipping shared build in short mode")
}
const stamp = "v9.9.9-stamp-probe"

outDir := t.TempDir()
var libraryName string
switch runtime.GOOS {
case "windows":
libraryName = "moss.dll"
case "darwin":
libraryName = "libmoss.dylib"
default:
libraryName = "libmoss.so"
}
output := filepath.Join(outDir, libraryName)
cacheDir := filepath.Join(outDir, "gocache")
tmpDir := filepath.Join(outDir, "gotmp")
if err := os.MkdirAll(cacheDir, 0o755); err != nil {
t.Fatalf("mkdir cache failed: %v", err)
}
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
t.Fatalf("mkdir tmp failed: %v", err)
}

cmd := exec.Command("go", "build", "-buildmode=c-shared",
"-ldflags", "-X main.buildVersion="+stamp, "-o", output, ".")
cmd.Dir = "."
cmd.Env = append(os.Environ(), "GOCACHE="+cacheDir, "GOTMPDIR="+tmpDir)
if combined, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("c-shared build failed: %v\n%s", err, combined)
}

built, err := os.ReadFile(output)
if err != nil {
t.Fatalf("reading the built library failed: %v", err)
}
if !bytes.Contains(built, []byte(stamp)) {
t.Fatal("the linker stamp did not reach the shared library — " +
"Moss_Version would report \"dev\" for every release")
}
}
Loading