From 421b0bea7e93dc9b9619406200890b37ea4580d9 Mon Sep 17 00:00:00 2001 From: Jason Lernerman Date: Mon, 31 Aug 2026 19:33:59 -0400 Subject: [PATCH] feat(simulate): record the CI pipeline a run came from --- cmd/lk/simulate.go | 29 ++++++++++++++++ cmd/lk/simulate_ci_env_test.go | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 cmd/lk/simulate_ci_env_test.go diff --git a/cmd/lk/simulate.go b/cmd/lk/simulate.go index ef1cd6ba..3b482626 100644 --- a/cmd/lk/simulate.go +++ b/cmd/lk/simulate.go @@ -561,6 +561,34 @@ func startSimulationAgent(c *simulateConfig, forwardOutput io.Writer) (*AgentPro }) } +// ciFromEnv reports the pipeline that started this run, so the dashboard can +// link a run back to it. GitHub Actions is the only provider recognized; nil +// means the run did not come from one. +func ciFromEnv() *livekit.SimulationRun_CI { + if os.Getenv("GITHUB_ACTIONS") == "" { + return nil + } + // On a pull_request event GITHUB_REF_NAME is "/merge", which names + // neither a branch nor a tag; the head ref is the branch under test. + ref := os.Getenv("GITHUB_REF_NAME") + if head := os.Getenv("GITHUB_HEAD_REF"); head != "" { + ref = head + } + var pullRequest string + if gitRef := os.Getenv("GITHUB_REF"); strings.HasPrefix(gitRef, "refs/pull/") { + pullRequest, _, _ = strings.Cut(strings.TrimPrefix(gitRef, "refs/pull/"), "/") + } + return &livekit.SimulationRun_CI{ + Provider: "github_actions", + CommitSha: os.Getenv("GITHUB_SHA"), + Ref: ref, + PullRequest: pullRequest, + RunUrl: fmt.Sprintf("%s/%s/actions/runs/%s", + os.Getenv("GITHUB_SERVER_URL"), os.Getenv("GITHUB_REPOSITORY"), os.Getenv("GITHUB_RUN_ID")), + Actor: os.Getenv("GITHUB_ACTOR"), + } +} + func createSimulationRun(ctx context.Context, c *simulateConfig) (string, *livekit.PresignedPostRequest, error) { req := &livekit.SimulationRun_Create_Request{ AgentName: c.agentName, @@ -569,6 +597,7 @@ func createSimulationRun(ctx context.Context, c *simulateConfig) (string, *livek BackgroundNoise: c.backgroundNoise, LowQualityMicrophone: c.lowQualityMicrophone, PacketLoss: c.packetLoss, + Ci: ciFromEnv(), } if c.concurrency > 0 { req.Concurrency = &c.concurrency diff --git a/cmd/lk/simulate_ci_env_test.go b/cmd/lk/simulate_ci_env_test.go new file mode 100644 index 00000000..c92a1ee5 --- /dev/null +++ b/cmd/lk/simulate_ci_env_test.go @@ -0,0 +1,62 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "testing" + +func TestCIFromEnvOutsideGitHubActions(t *testing.T) { + t.Setenv("GITHUB_ACTIONS", "") + if ci := ciFromEnv(); ci != nil { + t.Fatalf("expected no CI source, got %v", ci) + } +} + +func TestCIFromEnvPullRequest(t *testing.T) { + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("GITHUB_SHA", "abc123") + t.Setenv("GITHUB_REF", "refs/pull/42/merge") + t.Setenv("GITHUB_REF_NAME", "42/merge") + t.Setenv("GITHUB_HEAD_REF", "jason/feature") + t.Setenv("GITHUB_SERVER_URL", "https://github.com") + t.Setenv("GITHUB_REPOSITORY", "livekit/e2e") + t.Setenv("GITHUB_RUN_ID", "77") + t.Setenv("GITHUB_ACTOR", "u9g") + + ci := ciFromEnv() + if ci.GetPullRequest() != "42" { + t.Errorf("pull request: got %q, want %q", ci.GetPullRequest(), "42") + } + if ci.GetRef() != "jason/feature" { + t.Errorf("ref: got %q, want %q", ci.GetRef(), "jason/feature") + } + if want := "https://github.com/livekit/e2e/actions/runs/77"; ci.GetRunUrl() != want { + t.Errorf("run url: got %q, want %q", ci.GetRunUrl(), want) + } +} + +func TestCIFromEnvBranchPush(t *testing.T) { + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("GITHUB_REF", "refs/heads/main") + t.Setenv("GITHUB_REF_NAME", "main") + t.Setenv("GITHUB_HEAD_REF", "") + + ci := ciFromEnv() + if ci.GetRef() != "main" { + t.Errorf("ref: got %q, want %q", ci.GetRef(), "main") + } + if ci.GetPullRequest() != "" { + t.Errorf("pull request: got %q, want empty", ci.GetPullRequest()) + } +}