Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
55756a1
feat: add unified setup-api-client action and remediation plan
stranske Feb 2, 2026
6ae8264
refactor: apply setup-api-client action to keepalive workflow
stranske Feb 2, 2026
e501f75
chore: add setup-api-client action to sync manifest
stranske Feb 2, 2026
bbbc74d
fix: add explicit 'Agent Stopped: API capacity depleted' status
stranske Feb 2, 2026
9af4f3b
chore: sync template scripts
github-actions[bot] Feb 2, 2026
c7a18e6
chore(codex-autofix): apply updates (PR #1183)
github-actions[bot] Feb 2, 2026
cc565dd
fix: update API wrapper guard to accept setup-api-client action
stranske Feb 2, 2026
5593107
chore(autofix): formatting/lint
github-actions[bot] Feb 2, 2026
351305f
chore(autofix): formatting/lint
github-actions[bot] Feb 2, 2026
5102aea
fix: skip node_modules in API guard scan
stranske Feb 2, 2026
96ce88d
fix: recognize ensureRateLimitWrapped as valid wrapper pattern
stranske Feb 2, 2026
7e23ce0
fix: exclude node_modules from _is_target_file check
stranske Feb 2, 2026
7b10980
fix: address code review feedback from Copilot
stranske Feb 2, 2026
e1faa71
fix: sync setup-api-client and updated keepalive to templates
stranske Feb 2, 2026
5e40643
docs: add remaining work section to remediation plan
stranske Feb 2, 2026
514eded
refactor: migrate ALL workflows to setup-api-client with simplified p…
stranske Feb 2, 2026
1574f76
fix: escape template expressions in action description
stranske Feb 2, 2026
b4a68dc
fix: sync agents-auto-pilot.yml template from main workflow
stranske Feb 2, 2026
26a074d
chore(codex-autofix): apply updates (PR #1183)
github-actions[bot] Feb 2, 2026
73a1df2
chore: sync template scripts
github-actions[bot] Feb 2, 2026
274bb48
docs: add Rate Limiting Architecture section to CLAUDE.md
stranske Feb 2, 2026
96b8166
ci: add template drift check workflow
stranske Feb 2, 2026
a198864
docs: add copilot-instructions.md with mandatory read-first rule
stranske Feb 2, 2026
88db196
fix: rename template drift check to follow naming convention
stranske Feb 2, 2026
868fe82
docs: add health-74-template-drift to workflow inventory
stranske Feb 2, 2026
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
253 changes: 253 additions & 0 deletions .github/actions/setup-api-client/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
name: Setup API Client
description: |
Unified setup action for API client dependencies and token load balancer.

This action:
1. Installs @octokit/* dependencies at pinned versions
2. Exports all available tokens to environment variables
3. Ensures consistent API client setup across ALL workflow jobs

USAGE: Include this action in EVERY job that makes GitHub API calls:
- uses: ./.github/actions/setup-api-client
with:
secrets: ${{ toJSON(secrets) }}
github_token: ${{ secrets.GITHUB_TOKEN }}

inputs:
secrets:
description: 'JSON-encoded secrets object - use toJSON(secrets)'
required: false
github_token:
description: 'Primary GitHub token (from github.token or GITHUB_TOKEN)'
required: false
# Individual secrets as fallback for workflows that can't use toJSON(secrets)
service_bot_pat:
required: false
actions_bot_pat:
required: false
codespaces_workflows:
required: false
owner_pr_pat:
required: false
agents_automation_pat:
required: false
workflows_app_id:
required: false
workflows_app_private_key:
required: false
keepalive_app_id:
required: false
keepalive_app_private_key:
required: false
gh_app_id:
required: false
gh_app_private_key:
required: false
app_1_id:
required: false
app_1_private_key:
required: false
app_2_id:
required: false
app_2_private_key:
required: false
# Configuration options
skip_deps:
description: 'Skip npm install (if deps already installed in a prior step)'
required: false
default: 'false'
verbose:
description: 'Enable verbose logging of token setup'
required: false
default: 'false'

outputs:
token_count:
description: 'Number of tokens registered in the load balancer'
Comment thread
stranske marked this conversation as resolved.
Outdated
value: ${{ steps.export-tokens.outputs.token_count }}
available_tokens:
description: 'Comma-separated list of available token names'
value: ${{ steps.export-tokens.outputs.available_tokens }}

runs:
using: composite
steps:
- name: Install API client dependencies
if: inputs.skip_deps != 'true'
shell: bash
run: |
set -euo pipefail

# Find where to install - prefer scripts dir, fall back to workspace root
INSTALL_DIR="${GITHUB_WORKSPACE}/.github/scripts"
if [ ! -d "$INSTALL_DIR" ]; then
INSTALL_DIR="${GITHUB_WORKSPACE}"
fi

Comment thread
stranske marked this conversation as resolved.
Outdated
echo "📦 Installing @octokit dependencies in $INSTALL_DIR..."
cd "$INSTALL_DIR"

# Check if already installed
if [ -d "node_modules/@octokit/rest" ]; then
echo "✅ @octokit/rest already installed"
else
# Install with pinned versions for consistency
npm install --no-save \
@octokit/rest@20.0.2 \
@octokit/plugin-retry@6.0.1 \
@octokit/plugin-paginate-rest@9.1.5 \
@octokit/auth-app@6.0.3 \
2>/dev/null || {
echo "::warning::npm install failed, trying with --legacy-peer-deps"
npm install --no-save --legacy-peer-deps \
@octokit/rest@20.0.2 \
@octokit/plugin-retry@6.0.1 \
@octokit/plugin-paginate-rest@9.1.5 \
@octokit/auth-app@6.0.3
}
echo "✅ @octokit dependencies installed"
fi
Comment thread
stranske marked this conversation as resolved.
Outdated

- name: Export load balancer tokens
id: export-tokens
shell: bash
env:
INPUT_SECRETS: ${{ inputs.secrets }}
INPUT_GITHUB_TOKEN: ${{ inputs.github_token }}
INPUT_SERVICE_BOT_PAT: ${{ inputs.service_bot_pat }}
INPUT_ACTIONS_BOT_PAT: ${{ inputs.actions_bot_pat }}
INPUT_CODESPACES_WORKFLOWS: ${{ inputs.codespaces_workflows }}
INPUT_OWNER_PR_PAT: ${{ inputs.owner_pr_pat }}
INPUT_AGENTS_AUTOMATION_PAT: ${{ inputs.agents_automation_pat }}
INPUT_WORKFLOWS_APP_ID: ${{ inputs.workflows_app_id }}
INPUT_WORKFLOWS_APP_PRIVATE_KEY: ${{ inputs.workflows_app_private_key }}
INPUT_KEEPALIVE_APP_ID: ${{ inputs.keepalive_app_id }}
INPUT_KEEPALIVE_APP_PRIVATE_KEY: ${{ inputs.keepalive_app_private_key }}
INPUT_GH_APP_ID: ${{ inputs.gh_app_id }}
INPUT_GH_APP_PRIVATE_KEY: ${{ inputs.gh_app_private_key }}
INPUT_APP_1_ID: ${{ inputs.app_1_id }}
INPUT_APP_1_PRIVATE_KEY: ${{ inputs.app_1_private_key }}
INPUT_APP_2_ID: ${{ inputs.app_2_id }}
INPUT_APP_2_PRIVATE_KEY: ${{ inputs.app_2_private_key }}
INPUT_VERBOSE: ${{ inputs.verbose }}
run: |
set -euo pipefail

token_count=0
available_tokens=""

export_var() {
local name="$1"
local value="${2-}"
if [ -z "${value}" ]; then
return 0
fi

# Handle multiline values (like private keys)
if [[ "${value}" == *$'\n'* ]]; then
{
echo "${name}<<EOF"
printf '%s\n' "${value}"
echo "EOF"
} >>"${GITHUB_ENV}"
else
printf '%s=%s\n' "${name}" "${value}" >>"${GITHUB_ENV}"
fi

token_count=$((token_count + 1))
if [ -n "$available_tokens" ]; then
available_tokens="${available_tokens},${name}"
else
available_tokens="${name}"
fi

if [ "${INPUT_VERBOSE}" = "true" ]; then
echo " ✅ ${name} exported"
fi
}
Comment thread
stranske marked this conversation as resolved.

echo "🔐 Exporting tokens to environment..."

# Try to extract from JSON secrets first
if [ -n "${INPUT_SECRETS:-}" ] && [ "${INPUT_SECRETS}" != "null" ]; then
# Parse JSON secrets
extract_secret() {
local key="$1"
echo "${INPUT_SECRETS}" | jq -r ".${key} // empty" 2>/dev/null || echo ""
}

SERVICE_BOT_PAT=$(extract_secret "SERVICE_BOT_PAT")
ACTIONS_BOT_PAT=$(extract_secret "ACTIONS_BOT_PAT")
CODESPACES_WORKFLOWS=$(extract_secret "CODESPACES_WORKFLOWS")
OWNER_PR_PAT=$(extract_secret "OWNER_PR_PAT")
AGENTS_AUTOMATION_PAT=$(extract_secret "AGENTS_AUTOMATION_PAT")
WORKFLOWS_APP_ID=$(extract_secret "WORKFLOWS_APP_ID")
WORKFLOWS_APP_PRIVATE_KEY=$(extract_secret "WORKFLOWS_APP_PRIVATE_KEY")
KEEPALIVE_APP_ID=$(extract_secret "KEEPALIVE_APP_ID")
KEEPALIVE_APP_PRIVATE_KEY=$(extract_secret "KEEPALIVE_APP_PRIVATE_KEY")
GH_APP_ID=$(extract_secret "GH_APP_ID")
GH_APP_PRIVATE_KEY=$(extract_secret "GH_APP_PRIVATE_KEY")
APP_1_ID=$(extract_secret "APP_1_ID")
APP_1_PRIVATE_KEY=$(extract_secret "APP_1_PRIVATE_KEY")
APP_2_ID=$(extract_secret "APP_2_ID")
APP_2_PRIVATE_KEY=$(extract_secret "APP_2_PRIVATE_KEY")
Comment thread
stranske marked this conversation as resolved.
Outdated
else
# Fall back to individual inputs
SERVICE_BOT_PAT="${INPUT_SERVICE_BOT_PAT:-}"
ACTIONS_BOT_PAT="${INPUT_ACTIONS_BOT_PAT:-}"
CODESPACES_WORKFLOWS="${INPUT_CODESPACES_WORKFLOWS:-}"
OWNER_PR_PAT="${INPUT_OWNER_PR_PAT:-}"
AGENTS_AUTOMATION_PAT="${INPUT_AGENTS_AUTOMATION_PAT:-}"
WORKFLOWS_APP_ID="${INPUT_WORKFLOWS_APP_ID:-}"
WORKFLOWS_APP_PRIVATE_KEY="${INPUT_WORKFLOWS_APP_PRIVATE_KEY:-}"
KEEPALIVE_APP_ID="${INPUT_KEEPALIVE_APP_ID:-}"
KEEPALIVE_APP_PRIVATE_KEY="${INPUT_KEEPALIVE_APP_PRIVATE_KEY:-}"
GH_APP_ID="${INPUT_GH_APP_ID:-}"
GH_APP_PRIVATE_KEY="${INPUT_GH_APP_PRIVATE_KEY:-}"
APP_1_ID="${INPUT_APP_1_ID:-}"
APP_1_PRIVATE_KEY="${INPUT_APP_1_PRIVATE_KEY:-}"
APP_2_ID="${INPUT_APP_2_ID:-}"
APP_2_PRIVATE_KEY="${INPUT_APP_2_PRIVATE_KEY:-}"
fi

# Export GITHUB_TOKEN and GH_TOKEN
if [ -n "${INPUT_GITHUB_TOKEN:-}" ]; then
export_var "GITHUB_TOKEN" "${INPUT_GITHUB_TOKEN}"
export_var "GH_TOKEN" "${INPUT_GITHUB_TOKEN}"
fi
Comment thread
stranske marked this conversation as resolved.

# Export PATs
export_var "SERVICE_BOT_PAT" "${SERVICE_BOT_PAT:-}"
export_var "ACTIONS_BOT_PAT" "${ACTIONS_BOT_PAT:-}"
export_var "CODESPACES_WORKFLOWS" "${CODESPACES_WORKFLOWS:-}"
export_var "OWNER_PR_PAT" "${OWNER_PR_PAT:-}"
export_var "AGENTS_AUTOMATION_PAT" "${AGENTS_AUTOMATION_PAT:-}"

# Export App credentials
export_var "WORKFLOWS_APP_ID" "${WORKFLOWS_APP_ID:-}"
export_var "WORKFLOWS_APP_PRIVATE_KEY" "${WORKFLOWS_APP_PRIVATE_KEY:-}"
export_var "KEEPALIVE_APP_ID" "${KEEPALIVE_APP_ID:-}"
export_var "KEEPALIVE_APP_PRIVATE_KEY" "${KEEPALIVE_APP_PRIVATE_KEY:-}"
export_var "GH_APP_ID" "${GH_APP_ID:-}"
export_var "GH_APP_PRIVATE_KEY" "${GH_APP_PRIVATE_KEY:-}"
export_var "APP_1_ID" "${APP_1_ID:-}"
export_var "APP_1_PRIVATE_KEY" "${APP_1_PRIVATE_KEY:-}"
export_var "APP_2_ID" "${APP_2_ID:-}"
export_var "APP_2_PRIVATE_KEY" "${APP_2_PRIVATE_KEY:-}"

# Output summary
echo "token_count=${token_count}" >> "$GITHUB_OUTPUT"
echo "available_tokens=${available_tokens}" >> "$GITHUB_OUTPUT"

echo ""
echo "📊 Token Setup Summary:"
echo " Total tokens exported: ${token_count}"
if [ "${INPUT_VERBOSE}" = "true" ]; then
echo " Available: ${available_tokens}"
fi

# Warn if no tokens were exported
if [ "${token_count}" -eq 0 ]; then
echo "::warning::No tokens were exported! API calls may fail."
echo "::warning::Ensure secrets are passed to this action."
fi
Loading
Loading