diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index 1149084cbd2..4d67f9ba637 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -151,6 +151,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" @@ -569,6 +570,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Set runtime paths id: set-runtime-paths @@ -1312,6 +1314,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Download agent output artifact id: download-agent-output @@ -1589,6 +1592,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Download agent output artifact id: download-agent-output @@ -1838,6 +1842,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Check team membership for command workflow id: check_membership @@ -1891,6 +1896,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -1959,6 +1965,7 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "gemini" GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} + GH_AW_ENGINE_VERSION: "0.39.1" GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e ✨ *[{workflow_name}]({run_url}) — Powered by Gemini*{ai_credits_suffix}{history_link}\",\"runStarted\":\"✨ Gemini awakens... [{workflow_name}]({run_url}) begins its journey on this {event_type}...\",\"runSuccess\":\"🚀 [{workflow_name}]({run_url}) **MISSION COMPLETE!** Gemini has spoken. ✨\",\"runFailure\":\"⚠️ [{workflow_name}]({run_url}) {status}. Gemini encountered unexpected challenges...\"}" @@ -1998,6 +2005,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" @@ -2086,6 +2094,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Gemini" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-gemini.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Download cache-memory artifact (default) id: download_cache_default diff --git a/docs/adr/48519-honor-engine-version-for-copilot.md b/docs/adr/48519-honor-engine-version-for-copilot.md new file mode 100644 index 00000000000..9498c030260 --- /dev/null +++ b/docs/adr/48519-honor-engine-version-for-copilot.md @@ -0,0 +1,44 @@ +# ADR-48519: Honor engine.version for Copilot CLI Installation + +**Date**: 2026-07-28 +**Status**: Draft +**Deciders**: pelikhan, copilot-swe-agent + +--- + +### Context + +The `engine.version` field in workflow configuration allows users to pin the version of a coding engine (Copilot, Claude, codex, gemini) installed during workflow execution. Claude, codex, and gemini engines all honored this field correctly, using the user-specified value when present and falling back to a default pinned constant otherwise. However, the Copilot engine's `GetInstallationSteps` silently ignored `engine.version` and always installed `DefaultCopilotVersion`, logging only a message that it was ignoring the user-set value. This inconsistency made `engine.version` a no-op for Copilot, violating the principle of least surprise and breaking user expectations established by the other three engine implementations. + +### Decision + +We will make the Copilot engine honor `engine.version` exactly as Claude, codex, and gemini do: when `EngineConfig.Version` is set, use it as the installation target; when it is absent, fall back to `DefaultCopilotVersion` and normalize `EngineConfig.Version` to that default so all downstream consumers in the same compile flow observe the effective installed value. The install script already skips `compat.json` resolution when an explicit version is provided, so no additional bypass logic is required. + +### Alternatives Considered + +#### Alternative 1: Keep Copilot always pinned to DefaultCopilotVersion + +The existing behavior guaranteed a stable, tested Copilot CLI version regardless of user configuration, which reduced risk of users accidentally installing untested or incompatible versions. This was rejected because it made `engine.version` silently ineffective for Copilot while working for all other engines, creating inconsistent and confusing behavior. The field's documented contract implies it controls the installed version across all engines. + +#### Alternative 2: Introduce a Copilot-specific version override field + +A dedicated field (e.g., `engine.copilot_version`) could allow Copilot-specific version control without changing the behavior of `engine.version`. This was rejected because it adds unnecessary API surface, conflicts with the established cross-engine convention of `engine.version`, and would require documentation and migration for any users who expect the unified field to work. + +### Consequences + +#### Positive +- `engine.version` now behaves consistently across all four supported engines (Copilot, Claude, codex, gemini), fulfilling user expectations. +- Users can pin or override the Copilot CLI version in their workflow definitions, enabling version-locked reproducible builds or controlled rollouts of new CLI versions. +- Test coverage explicitly asserts the honored-version contract, preventing silent regression back to the ignore-version behavior. + +#### Negative +- `compat.json` compatibility-matrix resolution is skipped when an explicit `engine.version` is provided, meaning users who specify an incompatible or unsupported version will receive no automated correction and may encounter runtime failures. +- Any workflow that previously set `engine.version` for Copilot expecting it to be ignored (e.g., to document an intent without changing behavior) will now have that version actually installed — a silent behavioral change at the call site. + +#### Neutral +- `EngineConfig.Version` is still normalized to the effective installed version when no explicit version is set, preserving the existing contract for downstream consumers in the compile flow. +- The change is contained to `copilot_engine_installation.go`; the install script and `compat.json` integration path are unchanged. + +--- + +*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.* diff --git a/pkg/workflow/compiler_yaml_lookups.go b/pkg/workflow/compiler_yaml_lookups.go index f40bfb0facd..a245afd04b2 100644 --- a/pkg/workflow/compiler_yaml_lookups.go +++ b/pkg/workflow/compiler_yaml_lookups.go @@ -39,6 +39,8 @@ func getVersionForSetup(data *WorkflowData) string { return string(constants.DefaultClaudeCodeVersion) case string(constants.CodexEngine): return string(constants.DefaultCodexVersion) + case string(constants.GeminiEngine): + return string(constants.DefaultGeminiVersion) case string(constants.OpenCodeEngine): return string(constants.DefaultOpenCodeVersion) case string(constants.PiEngine): diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index 4aaab348c09..17eb9c4b4b6 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -708,6 +708,9 @@ func (e *CopilotEngine) buildCopilotExecutionStep(workflowData *WorkflowData, co // DefaultCopilotVersion. This preserves existing behavior while avoiding drift if // DefaultCopilotVersion is ever lowered below CopilotNoAskUserMinVersion. // - "latest": always returns true (latest is always a new release). +// - Expression (e.g. "${{ inputs.engine-version }}"): returns true. The version resolves +// at runtime so we cannot gate at compile time; the installer already handles expression +// versions via ENGINE_VERSION env-var injection, ensuring the right binary is installed. // - Any semver string ≥ CopilotNoAskUserMinVersion: returns true. // - Any semver string < CopilotNoAskUserMinVersion: returns false. // - Non-semver string (e.g. a branch name): returns false (conservative). @@ -716,6 +719,12 @@ func copilotSupportsNoAskUser(engineConfig *EngineConfig) bool { if engineConfig != nil && engineConfig.Version != "" { versionStr = engineConfig.Version } + // Expression versions resolve at runtime; treat as supported so the generated execution + // flags match the installed binary for any version >= CopilotNoAskUserMinVersion. + if containsExpression(versionStr) { + copilotExecLog.Printf("copilotSupportsNoAskUser: expression version %q treated as supported", versionStr) + return true + } return versionAtLeast( versionStr, string(constants.DefaultCopilotVersion), diff --git a/pkg/workflow/copilot_engine_installation.go b/pkg/workflow/copilot_engine_installation.go index aa8a9eb3077..ca4135865b2 100644 --- a/pkg/workflow/copilot_engine_installation.go +++ b/pkg/workflow/copilot_engine_installation.go @@ -142,21 +142,22 @@ func (e *CopilotEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHu return appendCopilotLSPInstallSteps(steps, workflowData) } - // Copilot CLI is pinned to the default version constant. + // Use engine.version if provided, otherwise fall back to the default pinned version. + // When no explicit version is set, normalize the engine config so downstream + // consumers observe the effective installed value. copilotVersion := string(constants.DefaultCopilotVersion) if workflowData.EngineConfig != nil { if workflowData.EngineConfig.Version != "" { - copilotInstallLog.Printf("Ignoring pinned engine.version (%s): Copilot CLI install version is pinned to %s", workflowData.EngineConfig.Version, copilotVersion) + copilotVersion = workflowData.EngineConfig.Version + copilotInstallLog.Printf("Using engine.version for Copilot CLI installation: %s", copilotVersion) + } else { + // Normalize engine config version to the effective installed version so + // downstream checks that consult EngineConfig.Version stay consistent. + // This mutates workflowData by design because subsequent generation steps + // in the same compile flow should observe the effective installed version. + workflowData.EngineConfig.Version = copilotVersion + copilotInstallLog.Printf("No engine.version specified, using default Copilot CLI version: %s", copilotVersion) } - // Normalize engine config version to effective installed version so - // downstream checks that consult EngineConfig.Version stay consistent. - // This applies even when the original version was empty (unset), so all - // downstream consumers observe the effective installed value. - // This mutates workflowData by design because subsequent generation steps - // in the same compile flow should observe the effective installed version. - // Callers that reuse the same WorkflowData instance should expect this - // field to be rewritten after installation-step generation. - workflowData.EngineConfig.Version = copilotVersion } // Use the installer script for global installation diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 2ace2457b02..0d73dccbd86 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -2978,6 +2978,16 @@ func TestCopilotSupportsNoAskUser(t *testing.T) { engineConfig: &EngineConfig{Version: "main"}, expected: false, }, + { + name: "expression version is treated as supported", + engineConfig: &EngineConfig{Version: "${{ inputs.engine-version }}"}, + expected: true, + }, + { + name: "github event input expression is treated as supported", + engineConfig: &EngineConfig{Version: "${{ github.event.inputs.engine-version }}"}, + expected: true, + }, } for _, tt := range tests { diff --git a/pkg/workflow/copilot_installer_test.go b/pkg/workflow/copilot_installer_test.go index 72072020b41..4592205324e 100644 --- a/pkg/workflow/copilot_installer_test.go +++ b/pkg/workflow/copilot_installer_test.go @@ -108,8 +108,9 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) { } } -func TestCopilotInstallerCustomVersion(t *testing.T) { - // Test that pinned version from engine config is ignored in favor of default pinned version +func TestCopilotEngineWithVersion(t *testing.T) { + // engine.version must be honored: when an explicit version is set it should be + // passed to the installer and compat.json resolution must be skipped. engine := NewCopilotEngine() customVersion := "1.0.0" @@ -119,14 +120,12 @@ func TestCopilotInstallerCustomVersion(t *testing.T) { Version: customVersion, }, } - if workflowData.EngineConfig.Version != customVersion { - t.Fatalf("Expected initial engine config version %q, got: %q", customVersion, workflowData.EngineConfig.Version) - } steps := engine.GetInstallationSteps(workflowData) - if workflowData.EngineConfig.Version != string(constants.DefaultCopilotVersion) { - t.Fatalf("Expected engine config version to be normalized to default Copilot version %q, got: %q", constants.DefaultCopilotVersion, workflowData.EngineConfig.Version) + // EngineConfig.Version must remain as the user-specified value. + if workflowData.EngineConfig.Version != customVersion { + t.Fatalf("Expected engine config version to remain %q, got: %q", customVersion, workflowData.EngineConfig.Version) } // Find the install step @@ -143,23 +142,58 @@ func TestCopilotInstallerCustomVersion(t *testing.T) { t.Fatal("Could not find install step with install_copilot_cli.sh") } - // Should contain default pinned version - if !strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { - t.Errorf("Expected default Copilot version in install step, got:\n%s", installStep) + // Should pass the user-specified version to the installer (compat.json skipped). + if !strings.Contains(installStep, `install_copilot_cli.sh" `+customVersion) { + t.Errorf("Expected user-specified version %q in install step, got:\n%s", customVersion, installStep) } - if strings.Contains(installStep, `install_copilot_cli.sh" `+customVersion) { - t.Errorf("Expected pinned version to be ignored, got:\n%s", installStep) + if strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { + t.Errorf("Expected user-specified version, not default version, in install step:\n%s", installStep) } // Must pin GH_HOST: github.com to prevent workflow-level GHES overrides from - // leaking into the Copilot CLI install step. Without this pin, a workflow with - // env.GH_HOST set to a GHES host would cause the install/auth path to target - // the wrong host. + // leaking into the Copilot CLI install step. if !strings.Contains(installStep, "GH_HOST: github.com") { t.Errorf("Install step should pin GH_HOST: github.com to prevent GHES workflow-level overrides, got:\n%s", installStep) } } +func TestCopilotEngineWithoutVersion(t *testing.T) { + // When engine.version is not set, the default pinned version must be used and + // EngineConfig.Version must be normalized to the effective installed value. + engine := NewCopilotEngine() + + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{}, + } + + steps := engine.GetInstallationSteps(workflowData) + + // EngineConfig.Version must be normalized to the default version. + if workflowData.EngineConfig.Version != string(constants.DefaultCopilotVersion) { + t.Fatalf("Expected engine config version to be normalized to default Copilot version %q, got: %q", constants.DefaultCopilotVersion, workflowData.EngineConfig.Version) + } + + // Find the install step + var installStep string + for _, step := range steps { + stepContent := strings.Join(step, "\n") + if strings.Contains(stepContent, "install_copilot_cli.sh") { + installStep = stepContent + break + } + } + + if installStep == "" { + t.Fatal("Could not find install step with install_copilot_cli.sh") + } + + // Should use the default pinned version. + if !strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { + t.Errorf("Expected default Copilot version in install step, got:\n%s", installStep) + } +} + func TestGenerateCopilotInstallerSteps_ExpressionVersion(t *testing.T) { tests := []struct { name string @@ -248,8 +282,9 @@ func TestGenerateCopilotInstallerSteps_RootlessWithExpression(t *testing.T) { } } -func TestCopilotInstallerExpressionVersion_ViaEngineConfig(t *testing.T) { - // Test that expression version from engine config is ignored in favor of default pinned version +func TestCopilotEngineWithExpressionVersion(t *testing.T) { + // expression engine.version must be honored: the value must flow through env-var + // injection (not embedded directly in the shell command) and compat.json must be skipped. engine := NewCopilotEngine() expressionVersion := "${{ inputs.engine-version }}" @@ -262,8 +297,9 @@ func TestCopilotInstallerExpressionVersion_ViaEngineConfig(t *testing.T) { steps := engine.GetInstallationSteps(workflowData) - if workflowData.EngineConfig.Version != string(constants.DefaultCopilotVersion) { - t.Fatalf("Expected engine config version to be normalized to default Copilot version %q, got: %q", constants.DefaultCopilotVersion, workflowData.EngineConfig.Version) + // EngineConfig.Version must remain as the expression value. + if workflowData.EngineConfig.Version != expressionVersion { + t.Fatalf("Expected engine config version to remain %q, got: %q", expressionVersion, workflowData.EngineConfig.Version) } // Find the install step @@ -280,18 +316,20 @@ func TestCopilotInstallerExpressionVersion_ViaEngineConfig(t *testing.T) { t.Fatal("Could not find install step with install_copilot_cli.sh") } - // Should not use expression wiring - if strings.Contains(installStep, "ENGINE_VERSION: ${{ inputs.engine-version }}") { - t.Errorf("Expected expression version to be ignored, got:\n%s", installStep) + // Should use env var injection (not embed expression directly in shell command). + if !strings.Contains(installStep, "ENGINE_VERSION: "+expressionVersion) { + t.Errorf("Expected ENGINE_VERSION env var with expression, got:\n%s", installStep) } - - // Should install default pinned version - if !strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { - t.Errorf("Expected default Copilot version in install step, got:\n%s", installStep) + if !strings.Contains(installStep, `"${ENGINE_VERSION}"`) { + t.Errorf(`Expected step to reference "$ENGINE_VERSION" in run command, got:\n%s`, installStep) + } + if strings.Contains(installStep, "install_copilot_cli.sh "+expressionVersion) { + t.Errorf("Expression version should NOT be embedded directly in shell command, got:\n%s", installStep) } } -func TestCopilotInstallerByokFeatureStillUsesDefaultPinnedVersion(t *testing.T) { +func TestCopilotEngineWithVersionAndByokFeature(t *testing.T) { + // engine.version must be honored even when the BYOK feature flag is enabled. engine := NewCopilotEngine() workflowData := &WorkflowData{ Name: "test-workflow", @@ -318,10 +356,10 @@ func TestCopilotInstallerByokFeatureStillUsesDefaultPinnedVersion(t *testing.T) t.Fatal("Could not find install step with install_copilot_cli.sh") } - if !strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { - t.Errorf("Expected default pinned Copilot CLI install, got:\n%s", installStep) + if !strings.Contains(installStep, `install_copilot_cli.sh" 1.0.0`) { + t.Errorf("Expected user-specified version in install step, got:\n%s", installStep) } - if strings.Contains(installStep, `install_copilot_cli.sh" 1.0.0`) { - t.Errorf("Expected pinned version to be ignored, got:\n%s", installStep) + if strings.Contains(installStep, `install_copilot_cli.sh" `+string(constants.DefaultCopilotVersion)) { + t.Errorf("Expected user-specified version, not default version, in install step:\n%s", installStep) } } diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index 27c6c0be82a..6205be4575b 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -123,6 +123,13 @@ func (e *GeminiEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHub return []GitHubActionStep{} } + // Normalize engine config version when not explicitly set, so downstream consumers + // (e.g. execution steps) observe the effective installed version. + if workflowData.EngineConfig != nil && workflowData.EngineConfig.Version == "" { + workflowData.EngineConfig.Version = string(constants.DefaultGeminiVersion) + geminiLog.Printf("No engine.version specified, using default Gemini CLI version: %s", workflowData.EngineConfig.Version) + } + npmSteps := BuildStandardNpmEngineInstallStepsNoCooldown( "@google/gemini-cli", string(constants.DefaultGeminiVersion), diff --git a/pkg/workflow/gemini_engine_test.go b/pkg/workflow/gemini_engine_test.go index a476afeb142..b3c2e2a07f5 100644 --- a/pkg/workflow/gemini_engine_test.go +++ b/pkg/workflow/gemini_engine_test.go @@ -6,6 +6,7 @@ import ( "strings" "testing" + "github.com/github/gh-aw/pkg/constants" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -779,3 +780,71 @@ func TestGeminiEngineWithExpressionVersion(t *testing.T) { t.Errorf("Expression should NOT be embedded directly in npm install command, got:\n%s", installStep) } } + +func TestGeminiEngineWithVersion(t *testing.T) { + engine := NewGeminiEngine() + + customVersion := "0.99.0" + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{ + ID: "gemini", + Version: customVersion, + }, + } + + installSteps := engine.GetInstallationSteps(workflowData) + + var installStep string + for _, step := range installSteps { + stepContent := strings.Join([]string(step), "\n") + if strings.Contains(stepContent, "npm install") { + installStep = stepContent + break + } + } + + if installStep == "" { + t.Fatal("Could not find npm install step") + } + + if !strings.Contains(installStep, "@google/gemini-cli@"+customVersion) { + t.Errorf("Expected custom version %q in install step, got:\n%s", customVersion, installStep) + } + if strings.Contains(installStep, "@google/gemini-cli@"+string(constants.DefaultGeminiVersion)) { + t.Errorf("Expected user-specified version, not default, in install step:\n%s", installStep) + } +} + +func TestGeminiEngineWithoutVersion(t *testing.T) { + engine := NewGeminiEngine() + + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{}, + } + + installSteps := engine.GetInstallationSteps(workflowData) + + // EngineConfig.Version must be normalized to the default version. + if workflowData.EngineConfig.Version != string(constants.DefaultGeminiVersion) { + t.Fatalf("Expected engine config version to be normalized to default Gemini version %q, got: %q", constants.DefaultGeminiVersion, workflowData.EngineConfig.Version) + } + + var installStep string + for _, step := range installSteps { + stepContent := strings.Join([]string(step), "\n") + if strings.Contains(stepContent, "npm install") { + installStep = stepContent + break + } + } + + if installStep == "" { + t.Fatal("Could not find npm install step") + } + + if !strings.Contains(installStep, "@google/gemini-cli@"+string(constants.DefaultGeminiVersion)) { + t.Errorf("Expected default version %q in install step when no engine.version set, got:\n%s", constants.DefaultGeminiVersion, installStep) + } +} diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden index 4b7dbf1cebe..d1d87a3564d 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden @@ -62,6 +62,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "engine-gemini-test" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/workflow.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Generate agentic run info id: generate_aw_info @@ -356,6 +357,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "engine-gemini-test" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/workflow.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -688,6 +690,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "engine-gemini-test" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/workflow.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.39.1" GH_AW_INFO_ENGINE_ID: "gemini" - name: Check team membership for workflow id: check_membership