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
2 changes: 2 additions & 0 deletions forge-cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var (
themeOverride string

appVersion = "dev"
appCommit = "none"
)

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -52,6 +53,7 @@ func init() {
// SetVersionInfo sets the version and commit for display.
func SetVersionInfo(version, commit string) {
appVersion = version
appCommit = commit
rootCmd.Version = version
rootCmd.SetVersionTemplate(fmt.Sprintf("forge %s (commit: %s)\n", version, commit))
}
Expand Down
1 change: 1 addition & 0 deletions forge-cli/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ func runRun(cmd *cobra.Command, args []string) error {
RateLimitOverride: rateLimitOverride,
TracingFlags: tracingFlags,
RuntimeVersion: appVersion,
RuntimeCommit: appCommit,
})
if err != nil {
return fmt.Errorf("creating runner: %w", err)
Expand Down
30 changes: 30 additions & 0 deletions forge-cli/runtime/banner_version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package runtime

import "testing"

// forgeVersionString is what the startup banner shows on the "Forge:" line —
// the forge binary/runtime version, distinct from the agent's forge.yaml
// version. It must degrade cleanly for dev builds (#335).
func TestForgeVersionString(t *testing.T) {
tests := []struct {
name string
version string
commit string
want string
}{
{"release with commit", "v0.17.0", "51df9a4", "v0.17.0 (commit: 51df9a4)"},
{"release, no commit baked", "v0.17.0", "none", "v0.17.0"},
{"release, empty commit", "v0.17.0", "", "v0.17.0"},
{"dev build (defaults)", "dev", "none", "dev"},
{"empty version defaults to dev", "", "none", "dev"},
{"empty version, real commit", "", "abc1234", "dev (commit: abc1234)"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
r := &Runner{cfg: RunnerConfig{RuntimeVersion: tc.version, RuntimeCommit: tc.commit}}
if got := r.forgeVersionString(); got != tc.want {
t.Errorf("forgeVersionString(%q, %q) = %q, want %q", tc.version, tc.commit, got, tc.want)
}
})
}
}
20 changes: 20 additions & 0 deletions forge-cli/runtime/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ type RunnerConfig struct {
// the `forge.runtime.version` OTel resource attribute so backends
// can compare agent runs across Forge upgrade waves. Empty = "dev".
RuntimeVersion string

// RuntimeCommit is the Forge cli's own build commit (short SHA),
// injected via `-X main.commit`. Shown on the startup banner next to
// RuntimeVersion so a running agent's exact binary is identifiable.
// "none"/"" = unset (a dev build) → the banner shows just the version.
RuntimeCommit string
}

// ScheduleNotifier is called after a scheduled task completes to deliver the
Expand Down Expand Up @@ -2848,6 +2854,19 @@ func (r *Runner) createProviderClient(provider string, cfg llm.ClientConfig) (ll
return providers.NewClient(provider, cfg)
}

// forgeVersionString formats the Forge binary/runtime version for the banner:
// "v0.17.0 (commit: 51df9a4)" when a real commit is baked in, else just the
// version. An empty version reads "dev"; the "none" sentinel (no -X commit)
// suppresses the commit suffix. This is the FORGE binary version, distinct
// from the agent's own forge.yaml version shown on the "Agent:" line.
func (r *Runner) forgeVersionString() string {
v := defaultStr(r.cfg.RuntimeVersion, "dev")
if c := r.cfg.RuntimeCommit; c != "" && c != "none" {
return fmt.Sprintf("%s (commit: %s)", v, c)
}
return v
}

func (r *Runner) printBanner(proxyURL string) {
title := "Forge Dev Server"
if r.cfg.Host != "" {
Expand All @@ -2859,6 +2878,7 @@ func (r *Runner) printBanner(proxyURL string) {
fmt.Fprintf(os.Stderr, " %s\n", title)
fmt.Fprintf(os.Stderr, " ────────────────────────────────────────\n")
fmt.Fprintf(os.Stderr, " Agent: %s (v%s)\n", r.cfg.Config.AgentID, r.cfg.Config.Version)
fmt.Fprintf(os.Stderr, " Forge: %s\n", r.forgeVersionString())
fmt.Fprintf(os.Stderr, " Framework: %s\n", r.cfg.Config.Framework)
fmt.Fprintf(os.Stderr, " Listen: %s:%d\n", host, r.cfg.Port)
if r.cfg.MockTools {
Expand Down
Loading