fix(build): emit artifact metadata from GitHub env

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 21:19:50 +00:00
parent 06e00c7d13
commit 892901dee2
3 changed files with 50 additions and 11 deletions

View file

@ -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
}

View file

@ -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.

View file

@ -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")