diff --git a/.github/workflows/ci-cd-gaps-assessment.lock.yml b/.github/workflows/ci-cd-gaps-assessment.lock.yml index 9c6384fe..fbfdaff5 100644 --- a/.github/workflows/ci-cd-gaps-assessment.lock.yml +++ b/.github/workflows/ci-cd-gaps-assessment.lock.yml @@ -449,23 +449,23 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | - # Check if gh-aw extension is already installed - if gh extension list | grep -q "github/gh-aw"; then - echo "gh-aw extension already installed, upgrading..." - gh extension upgrade gh-aw || true - else + # Install gh-aw if binary not already present. + # NOTE: Do NOT use 'gh aw --version' to detect installation — newer gh CLI versions + # return exit code 0 with an "available as official extension" message even when the + # extension binary is not installed, which would silently skip the install step. + GH_AW_BIN=$(command -v gh-aw 2>/dev/null || find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f -executable 2>/dev/null | head -1) + if [ -z "$GH_AW_BIN" ] || [ ! -x "$GH_AW_BIN" ]; then echo "Installing gh-aw extension..." - gh extension install github/gh-aw + # Download to a temp file first so curl failures are detected (avoids silent pipe failure) + curl -fsSL https://raw.githubusercontent.com/github/gh-aw/refs/heads/main/install-gh-aw.sh -o /tmp/install-gh-aw.sh + bash /tmp/install-gh-aw.sh + rm -f /tmp/install-gh-aw.sh + GH_AW_BIN=$(command -v gh-aw 2>/dev/null || find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f -executable 2>/dev/null | head -1) fi gh aw --version # Copy the gh-aw binary to ${RUNNER_TEMP}/gh-aw for MCP server containerization mkdir -p "${RUNNER_TEMP}/gh-aw" - GH_AW_BIN="" - GH_AW_BIN=$(command -v gh-aw 2>/dev/null) || true - if [ -z "$GH_AW_BIN" ]; then - GH_AW_BIN=$(find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f 2>/dev/null | head -1) || true - fi - if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then + if [ -n "$GH_AW_BIN" ] && [ -x "$GH_AW_BIN" ]; then cp "$GH_AW_BIN" "${RUNNER_TEMP}/gh-aw/gh-aw" chmod +x "${RUNNER_TEMP}/gh-aw/gh-aw" echo "Copied gh-aw binary to ${RUNNER_TEMP}/gh-aw/gh-aw" @@ -1177,4 +1177,3 @@ jobs: /tmp/gh-aw/safe-output-items.jsonl /tmp/gh-aw/temporary-id-map.json if-no-files-found: ignore - diff --git a/.github/workflows/pelis-agent-factory-advisor.lock.yml b/.github/workflows/pelis-agent-factory-advisor.lock.yml index 1455d78e..7280be7c 100644 --- a/.github/workflows/pelis-agent-factory-advisor.lock.yml +++ b/.github/workflows/pelis-agent-factory-advisor.lock.yml @@ -505,23 +505,23 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | - # Check if gh-aw extension is already installed - if gh extension list | grep -q "github/gh-aw"; then - echo "gh-aw extension already installed, upgrading..." - gh extension upgrade gh-aw || true - else + # Install gh-aw if binary not already present. + # NOTE: Do NOT use 'gh aw --version' to detect installation — newer gh CLI versions + # return exit code 0 with an "available as official extension" message even when the + # extension binary is not installed, which would silently skip the install step. + GH_AW_BIN=$(command -v gh-aw 2>/dev/null || find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f -executable 2>/dev/null | head -1) + if [ -z "$GH_AW_BIN" ] || [ ! -x "$GH_AW_BIN" ]; then echo "Installing gh-aw extension..." - gh extension install github/gh-aw + # Download to a temp file first so curl failures are detected (avoids silent pipe failure) + curl -fsSL https://raw.githubusercontent.com/github/gh-aw/refs/heads/main/install-gh-aw.sh -o /tmp/install-gh-aw.sh + bash /tmp/install-gh-aw.sh + rm -f /tmp/install-gh-aw.sh + GH_AW_BIN=$(command -v gh-aw 2>/dev/null || find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f -executable 2>/dev/null | head -1) fi gh aw --version # Copy the gh-aw binary to ${RUNNER_TEMP}/gh-aw for MCP server containerization mkdir -p "${RUNNER_TEMP}/gh-aw" - GH_AW_BIN="" - GH_AW_BIN=$(command -v gh-aw 2>/dev/null) || true - if [ -z "$GH_AW_BIN" ]; then - GH_AW_BIN=$(find "${HOME}/.local/share/gh/extensions/gh-aw" -name 'gh-aw' -type f 2>/dev/null | head -1) || true - fi - if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then + if [ -n "$GH_AW_BIN" ] && [ -x "$GH_AW_BIN" ]; then cp "$GH_AW_BIN" "${RUNNER_TEMP}/gh-aw/gh-aw" chmod +x "${RUNNER_TEMP}/gh-aw/gh-aw" echo "Copied gh-aw binary to ${RUNNER_TEMP}/gh-aw/gh-aw" @@ -1286,4 +1286,3 @@ jobs: /tmp/gh-aw/safe-output-items.jsonl /tmp/gh-aw/temporary-id-map.json if-no-files-found: ignore - diff --git a/scripts/ci/workflow-gh-aw-install.test.ts b/scripts/ci/workflow-gh-aw-install.test.ts new file mode 100644 index 00000000..3e915b44 --- /dev/null +++ b/scripts/ci/workflow-gh-aw-install.test.ts @@ -0,0 +1,35 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const workflowsDir = path.resolve(__dirname, '../../.github/workflows'); +const lockFiles = fs.readdirSync(workflowsDir).filter(file => file.endsWith('.lock.yml')); + +describe('workflow gh-aw extension installs', () => { + it('uses the resilient gh-aw installer in every lock workflow', () => { + const workflowsWithLegacyInstall: string[] = []; + let foundGhAwInstallStep = false; + + for (const lockFile of lockFiles) { + const workflowContent = fs.readFileSync(path.join(workflowsDir, lockFile), 'utf-8'); + + if ( + workflowContent.includes('gh extension install github/gh-aw') || + workflowContent.includes('gh extension upgrade gh-aw || true') + ) { + workflowsWithLegacyInstall.push(lockFile); + } + + if (!workflowContent.includes('name: Install gh-aw extension')) { + continue; + } + + foundGhAwInstallStep = true; + expect(workflowContent).toMatch(/install-gh-aw\.sh/); + expect(workflowContent).toMatch(/-type f -executable/); + expect(workflowContent).toMatch(/Failed to find gh-aw binary for MCP server/); + } + + expect(foundGhAwInstallStep).toBe(true); + expect(workflowsWithLegacyInstall).toEqual([]); + }); +});