Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions scripts/ci/postprocess-smoke-workflows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ const sessionStateDirInjectionRegex =
const copySessionStateStepRegex =
/^(\s+)- name: Copy Copilot session state files to logs\n\1 if: always\(\)\n\1 continue-on-error: true\n\1 run: bash "\$\{RUNNER_TEMP\}\/gh-aw\/actions\/copy_copilot_session_state\.sh"\n/m;

const copilotModelEmptyFallbackRegex =
/(COPILOT_MODEL:\s*\$\{\{\s*vars\.GH_AW_MODEL_AGENT_COPILOT\s*\|\|\s*)''(\s*\}\})/g;

function buildCopySessionStateStep(indent: string): string {
const i = indent;
const ri = `${i} `;
Expand Down Expand Up @@ -355,3 +358,31 @@ describe('buildCopySessionStateStep', () => {
});
});

describe('copilotModelEmptyFallbackRegex', () => {
const EXPECTED_COPILOT_MODEL_FALLBACK = 'claude-opus-4.6';

beforeEach(() => {
copilotModelEmptyFallbackRegex.lastIndex = 0;
});

it('should replace empty fallback with claude-opus-4.6 fallback', () => {
const input = " COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}\n";
const result = input.replace(
copilotModelEmptyFallbackRegex,
`$1'${EXPECTED_COPILOT_MODEL_FALLBACK}'$2`
);
expect(result).toBe(
` COPILOT_MODEL: \${{ vars.GH_AW_MODEL_AGENT_COPILOT || '${EXPECTED_COPILOT_MODEL_FALLBACK}' }}\n`
);
});

it('should not modify already-correct fallback', () => {
const input =
" COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-opus-4.6' }}\n";
const result = input.replace(
copilotModelEmptyFallbackRegex,
`$1'${EXPECTED_COPILOT_MODEL_FALLBACK}'$2`
);
expect(result).toBe(input);
});
});
26 changes: 26 additions & 0 deletions scripts/ci/postprocess-smoke-workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ const sessionStateDirInjectionRegex =
/--audit-dir \/tmp\/gh-aw\/sandbox\/firewall\/audit(?! --session-state-dir)/g;
const SESSION_STATE_DIR = '/tmp/gh-aw/sandbox/agent/session-state';

// Work around gh-aw compiler bug (gh-aw#26565) where Copilot model fallback is
// emitted as an empty string:
// COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
// In BYOK smoke workflows, this overrides workflow-level COPILOT_MODEL when the
// repo variable is unset, causing Copilot CLI startup failure.
const copilotModelEmptyFallbackRegex =
/(COPILOT_MODEL:\s*\$\{\{\s*vars\.GH_AW_MODEL_AGENT_COPILOT\s*\|\|\s*)''(\s*\}\})/g;

// Sentinel used to detect whether the "Copy Copilot session state" step has
// already been replaced with the AWF-aware inline script.
const copySessionStateSentinel = 'SESSION_STATE_SRC=';
Expand Down Expand Up @@ -445,6 +453,24 @@ for (const workflowPath of workflowPaths) {
}
}

// For smoke-copilot-byok: replace empty model fallbacks with the workflow-
// level COPILOT_MODEL env so the generated step inherits the shared default
// without hardcoding a duplicate model string here.
const isCopilotByokSmoke = workflowPath.includes('smoke-copilot-byok.lock.yml');
if (isCopilotByokSmoke) {
const emptyFallbackMatches = content.match(copilotModelEmptyFallbackRegex);
if (emptyFallbackMatches) {
content = content.replace(
copilotModelEmptyFallbackRegex,
'$1env.COPILOT_MODEL$2'
);
modified = true;
console.log(
` Replaced ${emptyFallbackMatches.length} empty COPILOT_MODEL fallback(s) for BYOK smoke`
);
}
}

// For smoke-services: inject GitHub Actions services block (Redis + PostgreSQL) into the
// agent job and replace --enable-host-access with --allow-host-service-ports 6379,5432.
// The gh-aw compiler does not natively support GitHub Actions `services:` in the
Expand Down
Loading