diff --git a/cmd/build/cmd_project.go b/cmd/build/cmd_project.go index cebdc5d..84c5307 100644 --- a/cmd/build/cmd_project.go +++ b/cmd/build/cmd_project.go @@ -312,6 +312,9 @@ func selectOutputArtifacts(rawArtifacts, archivedArtifacts, checksummedArtifacts // writeArtifactMetadata writes artifact_meta.json files next to built artifacts when CI metadata is available. func writeArtifactMetadata(filesystem io.Medium, buildName string, artifacts []build.Artifact) error { ci := build.DetectCI() + if ci == nil { + ci = build.DetectGitHubMetadata() + } if ci == nil { return nil } diff --git a/pkg/build/ci.go b/pkg/build/ci.go index 4a1b594..2af6996 100644 --- a/pkg/build/ci.go +++ b/pkg/build/ci.go @@ -79,7 +79,20 @@ func FormatGitHubAnnotation(level, file string, line int, message string) string // // upload release assets // } func DetectCI() *CIContext { - if core.Env("GITHUB_ACTIONS") == "" { + return detectGitHubContext(true) +} + +// DetectGitHubMetadata returns GitHub CI metadata when the standard environment +// variables are present, even if GITHUB_ACTIONS is unset. +// +// This is useful for metadata emission paths that only need the GitHub ref/SHA +// shape and should not be coupled to a specific runner environment. +func DetectGitHubMetadata() *CIContext { + return detectGitHubContext(false) +} + +func detectGitHubContext(requireActions bool) *CIContext { + if requireActions && core.Env("GITHUB_ACTIONS") == "" { return nil } @@ -97,17 +110,26 @@ func DetectCI() *CIContext { Repo: repo, } + populateGitHubContext(ctx) + return ctx +} + +func populateGitHubContext(ctx *CIContext) { + if ctx == nil { + return + } + // ShortSHA is first 7 chars of SHA. - runes := []rune(sha) + runes := []rune(ctx.SHA) if len(runes) >= 7 { ctx.ShortSHA = string(runes[:7]) } else { - ctx.ShortSHA = sha + ctx.ShortSHA = ctx.SHA } // Derive owner from "owner/repo" format. - if repo != "" { - parts := core.SplitN(repo, "/", 2) + if ctx.Repo != "" { + parts := core.SplitN(ctx.Repo, "/", 2) if len(parts) == 2 { ctx.Owner = parts[0] } @@ -117,14 +139,12 @@ func DetectCI() *CIContext { const tagPrefix = "refs/tags/" const branchPrefix = "refs/heads/" - if core.HasPrefix(ref, tagPrefix) { + if core.HasPrefix(ctx.Ref, tagPrefix) { ctx.IsTag = true - ctx.Tag = core.TrimPrefix(ref, tagPrefix) - } else if core.HasPrefix(ref, branchPrefix) { - ctx.Branch = core.TrimPrefix(ref, branchPrefix) + ctx.Tag = core.TrimPrefix(ctx.Ref, tagPrefix) + } else if core.HasPrefix(ctx.Ref, branchPrefix) { + ctx.Branch = core.TrimPrefix(ctx.Ref, branchPrefix) } - - return ctx } // ArtifactName generates a canonical artifact filename from the build name, CI context, and target. diff --git a/pkg/build/ci_test.go b/pkg/build/ci_test.go index 988e218..9606342 100644 --- a/pkg/build/ci_test.go +++ b/pkg/build/ci_test.go @@ -146,6 +146,22 @@ func TestCi_DetectCI_Bad(t *testing.T) { }) } +func TestCi_DetectGitHubMetadata_Good(t *testing.T) { + t.Run("detects GitHub metadata without GITHUB_ACTIONS", func(t *testing.T) { + t.Setenv("GITHUB_ACTIONS", "") + t.Setenv("GITHUB_SHA", "abc1234def5678901234567890123456789012345") + t.Setenv("GITHUB_REF", "refs/heads/main") + t.Setenv("GITHUB_REPOSITORY", "org/repo") + + ci := DetectGitHubMetadata() + require.NotNil(t, ci) + assert.Equal(t, "abc1234", ci.ShortSHA) + assert.Equal(t, "main", ci.Branch) + assert.Equal(t, "org/repo", ci.Repo) + assert.Equal(t, "org", ci.Owner) + }) +} + func TestCi_DetectCI_Ugly(t *testing.T) { t.Run("SHA shorter than 7 chars still works", func(t *testing.T) { setenvCI(t, "abc", "refs/heads/main", "org/repo")