diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index 20b4257..6f8bd87 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -29,6 +29,19 @@ jobs: - name: Check diff run: git diff --exit-code + generate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version-file: "go.mod" + cache: false + - name: go generate + run: go generate ./... + - name: Check diff + run: git diff --exit-code + lint: runs-on: ubuntu-latest steps: @@ -52,4 +65,4 @@ jobs: go-version-file: "go.mod" cache: false - name: Run tests - run: go test ./... \ No newline at end of file + run: go test ./... diff --git a/client.go b/client.go index fea9978..309b367 100644 --- a/client.go +++ b/client.go @@ -15,7 +15,9 @@ import ( "math/rand" "net/http" "net/url" + "runtime/debug" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -30,6 +32,15 @@ const ( scaleSetEndpoint = "_apis/runtime/runnerscalesets" ) +var ( + packageVersion string + commitSHA string +) + +func init() { + packageVersion, commitSHA = detectModuleVersionAndCommit() +} + type atomicValue[T any] struct { v atomic.Value } @@ -129,7 +140,16 @@ func (u UserAgentInfo) String() string { scaleSetID = strconv.Itoa(u.ScaleSetID) } - return fmt.Sprintf("%s/%s (%s; %s) ScaleSetID/%s", u.System, u.Version, u.CommitSHA, u.Subsystem, scaleSetID) + return fmt.Sprintf( + "%s/%s (%s; %s) ScaleSetID/%s; client (%s; %s)", + u.System, + u.Version, + u.CommitSHA, + u.Subsystem, + scaleSetID, + packageVersion, + commitSHA, + ) } func WithLogger(logger slog.Logger) Option { @@ -184,11 +204,13 @@ func NewClient(githubConfigURL string, creds *ActionsAuth, options ...Option) (* retryWaitMax: 30 * time.Second, } + version, sha := detectModuleVersionAndCommit() + ac.userAgent.Store( UserAgentInfo{ System: "scaleset-client", - Version: "NA", - CommitSHA: "NA", + Version: version, + CommitSHA: sha, Subsystem: "NA", ScaleSetID: 0, }.String()) @@ -1170,3 +1192,62 @@ func (c *Client) updateTokenIfNeeded(ctx context.Context) error { return nil } + +func detectModuleVersionAndCommit() (version string, commit string) { + const modulePath = "github.com/actions/scaleset" + + bi, ok := debug.ReadBuildInfo() + if !ok { + return "unknown", "unknown" + } + + // If we are the main module (built from source in this repo), use vcs settings. + if bi.Main.Path == modulePath { + version = bi.Main.Version + commit = "unknown" + for _, s := range bi.Settings { + switch s.Key { + case "vcs.revision": + commit = s.Value + case "vcs.modified": + // Optionally append a marker if the tree was dirty. + if s.Value == "true" && commit != "unknown" { + commit = commit + "-dirty" + } + } + } + if version == "" || version == "(devel)" { + version = "devel" + } + if commit == "" { + commit = "unknown" + } + return version, commit + } + + // Otherwise search dependency list for our module. + for _, dep := range bi.Deps { + if dep.Path == modulePath { + version = dep.Version + commit = extractCommitFromVersion(version) + return version, commit + } + } + + return "unknown", "unknown" +} + +// new: parse commit from a pseudo-version (e.g. v0.0.0-20251031142550-8104f571eba7) +func extractCommitFromVersion(v string) string { + // Semantic versions without pseudo part can't yield commit; return v directly. + // Pseudo format: -- + parts := strings.Split(v, "-") + if len(parts) < 3 { + return v + } + commit := parts[len(parts)-1] + if len(commit) >= 7 { + return commit + } + return v +} diff --git a/client_test.go b/client_test.go index d91f692..ce1a04a 100644 --- a/client_test.go +++ b/client_test.go @@ -1673,10 +1673,12 @@ func TestUserAgentInfoString(t *testing.T) { } userAgent := userAgentInfo.String() - expectedProduct := "actions-runner-controller/0.1.0 (1234567890abcdef; test)" + expectedProduct := fmt.Sprintf( + "actions-runner-controller/0.1.0 (1234567890abcdef; test) ScaleSetID/10; client (%s; %s)", + packageVersion, + commitSHA, + ) assert.Contains(t, userAgent, expectedProduct) - expectedScaleSet := "ScaleSetID/10" - assert.Contains(t, userAgent, expectedScaleSet) } const samplePrivateKey = `-----BEGIN PRIVATE KEY-----