From 9f920bfd7d0e6f6067da76fa66bc82c502f47874 Mon Sep 17 00:00:00 2001 From: Peter Baumgartner Date: Tue, 17 Mar 2026 13:39:14 -0600 Subject: [PATCH 1/2] Pass standard CI vars as build args into Docker/buildpack builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maps CodeBuild-specific env vars to neutral industry-standard names and injects them into the build environment: - CODEBUILD_WEBHOOK_HEAD_REF (or CODEBUILD_SOURCE_VERSION) → CI_COMMIT_REF - CODEBUILD_RESOLVED_SOURCE_VERSION → CI_COMMIT_SHA - CODEBUILD_START_TIME → CI_BUILD_STARTED_AT - CODEBUILD_SOURCE_REPO_URL → CI_REPOSITORY_URL Closes #13 Co-Authored-By: Claude Sonnet 4.6 --- builder/build/build.go | 5 +++ builder/build/build_test.go | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/builder/build/build.go b/builder/build/build.go index 3a7605d..b7d1fd5 100644 --- a/builder/build/build.go +++ b/builder/build/build.go @@ -38,6 +38,11 @@ func (b *Build) LoadBuildEnv() (map[string]string, error) { if val, ok := os.LookupEnv("ALLOW_EOL_SHIMMED_BUILDER"); ok { env["ALLOW_EOL_SHIMMED_BUILDER"] = val } + // map CodeBuild-specific vars to neutral CI vars and pass as build args + env["CI_COMMIT_REF"] = GetenvFallback([]string{"CODEBUILD_WEBHOOK_HEAD_REF", "CODEBUILD_SOURCE_VERSION"}) + env["CI_COMMIT_SHA"] = os.Getenv("CODEBUILD_RESOLVED_SOURCE_VERSION") + env["CI_BUILD_STARTED_AT"] = os.Getenv("CODEBUILD_START_TIME") + env["CI_REPOSITORY_URL"] = os.Getenv("CODEBUILD_SOURCE_REPO_URL") params, err := b.aws.GetParametersByPath(paths[0]) stripParamPrefix(params, paths[0], &env) if err != nil { diff --git a/builder/build/build_test.go b/builder/build/build_test.go index 7e760e5..86816ff 100644 --- a/builder/build/build_test.go +++ b/builder/build/build_test.go @@ -80,6 +80,71 @@ func TestLoadEnvInheritance(t *testing.T) { } } +func TestLoadBuildEnvCIVars(t *testing.T) { + appName := "test-app" + mockedAWS := new(MockAWS) + appConfigPrefix := fmt.Sprintf("/apppack/apps/%s/config/", appName) + mockedAWS.On("GetParametersByPath", appConfigPrefix).Return(map[string]string{}, nil) + mockedState := emptyState() + mockedState.On("ReadEnvFile").Return(&map[string]string{}, nil) + + t.Setenv("CODEBUILD_WEBHOOK_HEAD_REF", "refs/heads/main") + t.Setenv("CODEBUILD_RESOLVED_SOURCE_VERSION", "deadbeef1234") + t.Setenv("CODEBUILD_START_TIME", "1234567890") + t.Setenv("CODEBUILD_SOURCE_REPO_URL", "https://github.com/org/repo") + + b := Build{ + Appname: appName, + CodebuildBuildId: CodebuildBuildId, + aws: mockedAWS, + state: mockedState, + Ctx: testContext, + } + env, err := b.LoadBuildEnv() + if err != nil { + t.Fatalf("expected no error, got %s", err) + } + if env["CI_COMMIT_REF"] != "refs/heads/main" { + t.Errorf("expected CI_COMMIT_REF=refs/heads/main, got %s", env["CI_COMMIT_REF"]) + } + if env["CI_COMMIT_SHA"] != "deadbeef1234" { + t.Errorf("expected CI_COMMIT_SHA=deadbeef1234, got %s", env["CI_COMMIT_SHA"]) + } + if env["CI_BUILD_STARTED_AT"] != "1234567890" { + t.Errorf("expected CI_BUILD_STARTED_AT=1234567890, got %s", env["CI_BUILD_STARTED_AT"]) + } + if env["CI_REPOSITORY_URL"] != "https://github.com/org/repo" { + t.Errorf("expected CI_REPOSITORY_URL=https://github.com/org/repo, got %s", env["CI_REPOSITORY_URL"]) + } +} + +func TestLoadBuildEnvCIVarsFallback(t *testing.T) { + // When CODEBUILD_WEBHOOK_HEAD_REF is not set, CI_COMMIT_REF should fall back to CODEBUILD_SOURCE_VERSION + appName := "test-app" + mockedAWS := new(MockAWS) + appConfigPrefix := fmt.Sprintf("/apppack/apps/%s/config/", appName) + mockedAWS.On("GetParametersByPath", appConfigPrefix).Return(map[string]string{}, nil) + mockedState := emptyState() + mockedState.On("ReadEnvFile").Return(&map[string]string{}, nil) + + t.Setenv("CODEBUILD_SOURCE_VERSION", "refs/heads/feature-branch") + + b := Build{ + Appname: appName, + CodebuildBuildId: CodebuildBuildId, + aws: mockedAWS, + state: mockedState, + Ctx: testContext, + } + env, err := b.LoadBuildEnv() + if err != nil { + t.Fatalf("expected no error, got %s", err) + } + if env["CI_COMMIT_REF"] != "refs/heads/feature-branch" { + t.Errorf("expected CI_COMMIT_REF=refs/heads/feature-branch, got %s", env["CI_COMMIT_REF"]) + } +} + func TestGenerateDockerEnvStrings(t *testing.T) { env := map[string]string{ "FOO": "bar", From 5b8f723161436cbe5c4a4828f73b6524e113c8f9 Mon Sep 17 00:00:00 2001 From: Peter Baumgartner Date: Wed, 18 Mar 2026 13:51:34 -0600 Subject: [PATCH 2/2] Address Copilot review feedback - Only set CI_* vars when source env var is non-empty - Fix comment: vars are build args in Docker, env vars in buildpacks - Explicitly unset CODEBUILD_WEBHOOK_HEAD_REF in fallback test for determinism Co-Authored-By: Claude Sonnet 4.6 --- builder/build/build.go | 17 ++++++++++++----- builder/build/build_test.go | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/builder/build/build.go b/builder/build/build.go index ece6a12..5dbf495 100644 --- a/builder/build/build.go +++ b/builder/build/build.go @@ -38,11 +38,18 @@ func (b *Build) LoadBuildEnv() (map[string]string, error) { if val, ok := os.LookupEnv("ALLOW_EOL_SHIMMED_BUILDER"); ok { env["ALLOW_EOL_SHIMMED_BUILDER"] = val } - // map CodeBuild-specific vars to neutral CI vars and pass as build args - env["CI_COMMIT_REF"] = GetenvFallback([]string{"CODEBUILD_WEBHOOK_HEAD_REF", "CODEBUILD_SOURCE_VERSION"}) - env["CI_COMMIT_SHA"] = os.Getenv("CODEBUILD_RESOLVED_SOURCE_VERSION") - env["CI_BUILD_STARTED_AT"] = os.Getenv("CODEBUILD_START_TIME") - env["CI_REPOSITORY_URL"] = os.Getenv("CODEBUILD_SOURCE_REPO_URL") + // map CodeBuild-specific vars to neutral CI vars and pass into Docker builds as build args + // and into buildpack builds as env vars + for k, v := range map[string]string{ + "CI_COMMIT_REF": GetenvFallback([]string{"CODEBUILD_WEBHOOK_HEAD_REF", "CODEBUILD_SOURCE_VERSION"}), + "CI_COMMIT_SHA": os.Getenv("CODEBUILD_RESOLVED_SOURCE_VERSION"), + "CI_BUILD_STARTED_AT": os.Getenv("CODEBUILD_START_TIME"), + "CI_REPOSITORY_URL": os.Getenv("CODEBUILD_SOURCE_REPO_URL"), + } { + if v != "" { + env[k] = v + } + } params, err := b.aws.GetParametersByPath(paths[0]) stripParamPrefix(params, paths[0], &env) if err != nil { diff --git a/builder/build/build_test.go b/builder/build/build_test.go index 86816ff..0c2cdeb 100644 --- a/builder/build/build_test.go +++ b/builder/build/build_test.go @@ -127,6 +127,7 @@ func TestLoadBuildEnvCIVarsFallback(t *testing.T) { mockedState := emptyState() mockedState.On("ReadEnvFile").Return(&map[string]string{}, nil) + t.Setenv("CODEBUILD_WEBHOOK_HEAD_REF", "") t.Setenv("CODEBUILD_SOURCE_VERSION", "refs/heads/feature-branch") b := Build{