### Summary of Changes - Grant `actions: write` workflow permission so that `actions/cache@v4` can save conversation context keys on self-hosted runners. - Set `--print-timeout 25m` (and `20m` for plan) on all `agy` CLI invocations to avoid premature cancellation during initial workspace verification checks. - Sharpen the headless mode prompt in `/implement` and `/fix` steps to instruct the agent not to exit before applying edits directly to the source code. Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.
320 lines
15 KiB
YAML
320 lines
15 KiB
YAML
name: gemini - manual
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
pull_request_review_comment:
|
|
types: [created]
|
|
|
|
concurrency:
|
|
group: agent-issue-${{ github.event.issue.number || github.event.pull_request.number }}
|
|
cancel-in-progress: false
|
|
|
|
permissions:
|
|
contents: write
|
|
issues: write
|
|
pull-requests: write
|
|
actions: write
|
|
|
|
jobs:
|
|
agent-dispatch:
|
|
name: Agent / Dispatch
|
|
if: >
|
|
(github.event.comment.author_association == 'OWNER' ||
|
|
github.event.comment.author_association == 'MEMBER' ||
|
|
github.event.comment.author_association == 'COLLABORATOR') &&
|
|
(contains(github.event.comment.body, '/plan') ||
|
|
contains(github.event.comment.body, '/implement') ||
|
|
contains(github.event.comment.body, '/fix'))
|
|
runs-on: [self-hosted, eliza]
|
|
container:
|
|
image: node:20-bookworm
|
|
options: --add-host host.docker.internal:host-gateway
|
|
env:
|
|
HTTP_PROXY: http://host.docker.internal:1080
|
|
HTTPS_PROXY: http://host.docker.internal:1080
|
|
ALL_PROXY: http://host.docker.internal:1080
|
|
NO_PROXY: localhost,127.0.0.1
|
|
timeout-minutes: 30
|
|
|
|
steps:
|
|
- name: Verify Collaborator Authorization
|
|
id: auth-check
|
|
run: |
|
|
AUTHOR_ASSOC="${{ github.event.comment.author_association }}"
|
|
echo "Author association: $AUTHOR_ASSOC"
|
|
if [[ "$AUTHOR_ASSOC" != "OWNER" && "$AUTHOR_ASSOC" != "MEMBER" && "$AUTHOR_ASSOC" != "COLLABORATOR" ]]; then
|
|
echo "::error::Unauthorized trigger by user with association '$AUTHOR_ASSOC'."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Ensure GitHub CLI is Installed
|
|
run: |
|
|
if ! command -v gh &> /dev/null; then
|
|
echo "Installing GitHub CLI..."
|
|
curl -fsSL https://github.com/cli/cli/releases/download/v2.67.0/gh_2.67.0_linux_amd64.tar.gz | tar -xz -C /usr/local --strip-components=1
|
|
fi
|
|
|
|
- name: Acknowledge Command (Add Eyes Reaction)
|
|
if: success()
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
run: |
|
|
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}/reactions" -f content=eyes || true
|
|
|
|
- name: Checkout Codebase
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Install pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 9.12.3
|
|
|
|
- name: Setup Node.js with pnpm cache
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: 'pnpm'
|
|
|
|
- name: Install Dependencies & Build Workspace Packages
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
pnpm build:deps
|
|
|
|
- name: Restore Agent Conversation Context
|
|
uses: actions/cache@v4
|
|
id: agent-context-cache
|
|
with:
|
|
path: |
|
|
~/.gemini/antigravity-cli/conversations
|
|
~/.gemini/antigravity-cli/brain
|
|
key: agent-context-issue-${{ github.event.issue.number }}-${{ github.run_id }}
|
|
restore-keys: |
|
|
agent-context-issue-${{ github.event.issue.number }}-
|
|
|
|
- name: Setup Antigravity CLI & Credentials
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
AGY_AUTH_TOKEN: ${{ secrets.AGY_AUTH_TOKEN }}
|
|
run: |
|
|
# 1. Download official Antigravity CLI binary from google-antigravity/antigravity-cli
|
|
mkdir -p "$HOME/.local/bin"
|
|
if ! command -v agy &> /dev/null; then
|
|
echo "Fetching Antigravity CLI from google-antigravity/antigravity-cli..."
|
|
gh release download --repo google-antigravity/antigravity-cli -p "agy_cli_linux_x64.tar.gz" -O - | tar -xz -C "$HOME/.local/bin"
|
|
if [ -f "$HOME/.local/bin/antigravity" ] && [ ! -f "$HOME/.local/bin/agy" ]; then
|
|
mv "$HOME/.local/bin/antigravity" "$HOME/.local/bin/agy"
|
|
fi
|
|
ln -sf "$HOME/.local/bin/agy" "$HOME/.local/bin/antigravity"
|
|
chmod +x "$HOME/.local/bin/agy" "$HOME/.local/bin/antigravity"
|
|
fi
|
|
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
echo "$HOME/.gemini/antigravity-cli/bin" >> $GITHUB_PATH
|
|
export PATH="$HOME/.local/bin:$HOME/.gemini/antigravity-cli/bin:$PATH"
|
|
|
|
# 2. Patch location eligibility check (open-antigravity-patcher CLI_GATE_X64)
|
|
node -e '
|
|
const fs = require("fs");
|
|
const file = process.env.HOME + "/.local/bin/agy";
|
|
if (!fs.existsSync(file)) process.exit(0);
|
|
const buf = fs.readFileSync(file);
|
|
let count = 0;
|
|
for (let i = 0; i < buf.length - 18; i++) {
|
|
if (buf[i] === 0x48 && buf[i+1] === 0x85 && buf[i+2] === 0xc0 && buf[i+3] === 0x0f && buf[i+4] === 0x84 &&
|
|
buf[i+9] === 0x80 && buf[i+10] === 0x78 && buf[i+11] === 0x08 && buf[i+12] === 0x00 &&
|
|
buf[i+13] === 0x0f && buf[i+14] === 0x85) {
|
|
buf[i+9] = 0x48; buf[i+10] = 0x85; buf[i+11] = 0xc0; buf[i+12] = 0x90;
|
|
count++;
|
|
}
|
|
}
|
|
if (count > 0) {
|
|
fs.writeFileSync(file, buf);
|
|
console.log(`Applied open-antigravity-patcher: ${count} gates patched.`);
|
|
} else {
|
|
console.log("Antigravity CLI is already patched or signature not found.");
|
|
}
|
|
'
|
|
|
|
# 3. Inject OAuth Token
|
|
mkdir -p ~/.gemini/antigravity-cli
|
|
if [ -n "$AGY_AUTH_TOKEN" ]; then
|
|
if echo "$AGY_AUTH_TOKEN" | base64 --decode >/dev/null 2>&1; then
|
|
echo "$AGY_AUTH_TOKEN" | base64 --decode > ~/.gemini/antigravity-cli/antigravity-oauth-token
|
|
else
|
|
echo "$AGY_AUTH_TOKEN" > ~/.gemini/antigravity-cli/antigravity-oauth-token
|
|
fi
|
|
chmod 600 ~/.gemini/antigravity-cli/antigravity-oauth-token
|
|
else
|
|
echo "::warning::AGY_AUTH_TOKEN secret is not set."
|
|
fi
|
|
|
|
- name: Configure Git Bot Identity
|
|
run: |
|
|
git config --global user.name "Adam Jensen [bot]"
|
|
git config --global user.email "adam@ermnvldmr.com"
|
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
|
|
# --- COMMAND: /plan ---
|
|
- name: Handle /plan (Issue Planning)
|
|
if: contains(github.event.comment.body, '/plan') && github.event.issue.pull_request == null
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
run: |
|
|
echo "Generating plan for Issue #${ISSUE_NUMBER}..."
|
|
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --comments --json title,body,comments)
|
|
|
|
CONTINUE_FLAG=""
|
|
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
|
|
CONTINUE_FLAG="--continue"
|
|
fi
|
|
|
|
PROMPT="You are operating via GitHub Actions in non-interactive headless mode on Issue #${ISSUE_NUMBER}.\n\nIssue Context:\n${ISSUE_DATA}\n\nLatest instruction:\n${COMMENT_BODY}\n\nPlease generate/update a detailed technical implementation plan adhering to project GEMINI.md guidelines."
|
|
|
|
PLAN_OUTPUT=$(agy $CONTINUE_FLAG --mode plan --print-timeout 20m -p "$PROMPT" || echo "Agent encountered an error generating the plan.")
|
|
|
|
DISCLOSURE="\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
|
|
gh issue comment "$ISSUE_NUMBER" --body "${PLAN_OUTPUT}${DISCLOSURE}"
|
|
|
|
# --- COMMAND: /implement ---
|
|
- name: Handle /implement (Issue Implementation & PR)
|
|
if: contains(github.event.comment.body, '/implement') && github.event.issue.pull_request == null
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
run: |
|
|
BRANCH_NAME="agent/issue-${ISSUE_NUMBER}"
|
|
git checkout -b "$BRANCH_NAME"
|
|
|
|
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --comments --json title,body,comments)
|
|
|
|
CONTINUE_FLAG=""
|
|
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
|
|
CONTINUE_FLAG="--continue"
|
|
fi
|
|
|
|
PROMPT="You are operating via GitHub Actions in non-interactive headless mode to implement Issue #${ISSUE_NUMBER}.\n\nContext:\n${ISSUE_DATA}\n\nLatest instruction:\n${COMMENT_BODY}\n\nTask: Inspect relevant files, implement the necessary code changes directly in the workspace adhering to GEMINI.md rules, and verify them. Do not exit or stop before applying edits to the source code."
|
|
agy $CONTINUE_FLAG --dangerously-skip-permissions --print-timeout 25m -p "$PROMPT"
|
|
|
|
echo "Running repository verification gates..."
|
|
pnpm typecheck
|
|
pnpm lint
|
|
pnpm test
|
|
|
|
ISSUE_TITLE=$(gh issue view "$ISSUE_NUMBER" --json title -q .title)
|
|
git add -A
|
|
if git diff --staged --quiet; then
|
|
echo "No changes produced by agent."
|
|
gh issue comment "$ISSUE_NUMBER" --body "No changes were produced for implementation.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
|
|
exit 0
|
|
fi
|
|
|
|
git commit -m "feat: implement issue #${ISSUE_NUMBER} (${ISSUE_TITLE})"
|
|
git push -u origin "$BRANCH_NAME" --force
|
|
|
|
PR_BODY="Resolves #${ISSUE_NUMBER}\n\nAutomated implementation generated via \`/implement\`.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
|
|
gh pr create \
|
|
--title "feat: ${ISSUE_TITLE}" \
|
|
--body "$PR_BODY" \
|
|
--base next \
|
|
--head "$BRANCH_NAME"
|
|
|
|
# --- COMMAND: /fix ---
|
|
- name: Handle /fix (PR Review Adjustments & CI Failure Debugging)
|
|
if: contains(github.event.comment.body, '/fix')
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
run: |
|
|
IS_PR="${{ github.event.issue.pull_request != null }}"
|
|
if [ "$IS_PR" = "true" ]; then
|
|
# Verify PR is not from an untrusted cross-repository fork
|
|
IS_CROSS_REPO=$(gh pr view "$ISSUE_OR_PR_NUMBER" --json isCrossRepository -q .isCrossRepository)
|
|
if [ "$IS_CROSS_REPO" = "true" ]; then
|
|
echo "::error::Cross-repository fork PRs cannot be automatically processed in privileged context."
|
|
exit 1
|
|
fi
|
|
|
|
gh pr checkout "$ISSUE_OR_PR_NUMBER"
|
|
BRANCH_NAME=$(git branch --show-current)
|
|
PR_DIFF=$(gh pr diff "$ISSUE_OR_PR_NUMBER" || true)
|
|
|
|
FAILED_RUN_ID=$(gh run list --branch "$BRANCH_NAME" --status failure --limit 1 --json databaseId -q '.[0].databaseId' || true)
|
|
CI_LOGS=""
|
|
if [ -n "$FAILED_RUN_ID" ] && [ "$FAILED_RUN_ID" != "null" ]; then
|
|
echo "Found failed CI run: #$FAILED_RUN_ID. Fetching failure logs..."
|
|
CI_LOGS=$(gh run view "$FAILED_RUN_ID" --log-failed | tail -n 200 || true)
|
|
fi
|
|
|
|
CONTINUE_FLAG=""
|
|
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
|
|
CONTINUE_FLAG="--continue"
|
|
fi
|
|
|
|
PROMPT="You are operating via GitHub Actions in non-interactive headless mode to fix/adjust PR #${ISSUE_OR_PR_NUMBER} on branch ${BRANCH_NAME}.\n\nFeedback / Comment:\n${COMMENT_BODY}\n\nPR Diff:\n${PR_DIFF}\n\nFailed CI Logs (if any):\n${CI_LOGS}\n\nTask: Apply the necessary fixes directly to the files, ensuring all lint, typecheck, and test checks pass. Do not exit without applying the changes."
|
|
agy $CONTINUE_FLAG --dangerously-skip-permissions --print-timeout 25m -p "$PROMPT"
|
|
|
|
echo "Running repository verification gates..."
|
|
pnpm typecheck
|
|
pnpm lint
|
|
pnpm test
|
|
|
|
git add -A
|
|
if git diff --staged --quiet; then
|
|
echo "No additional changes were needed."
|
|
gh pr comment "$ISSUE_OR_PR_NUMBER" --body "Analyzed the request; no additional changes were needed.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
|
|
exit 0
|
|
fi
|
|
|
|
git commit -m "fix: apply review adjustments and CI fixes"
|
|
git push origin "$BRANCH_NAME"
|
|
|
|
gh pr comment "$ISSUE_OR_PR_NUMBER" --body "Applied adjustments and verified build/test suite.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
|
|
else
|
|
echo "Handling /fix on Issue #${ISSUE_OR_PR_NUMBER}..."
|
|
BRANCH_NAME="agent/fix-issue-${ISSUE_OR_PR_NUMBER}"
|
|
git checkout -b "$BRANCH_NAME"
|
|
|
|
ISSUE_DATA=$(gh issue view "$ISSUE_OR_PR_NUMBER" --comments --json title,body,comments)
|
|
PROMPT="You are operating via GitHub Actions in non-interactive headless mode to fix issue #${ISSUE_OR_PR_NUMBER}.\n\nContext:\n${ISSUE_DATA}\n\nInstruction:\n${COMMENT_BODY}\n\nTask: Apply the necessary fixes directly to the files adhering to GEMINI.md rules."
|
|
agy --dangerously-skip-permissions --print-timeout 25m -p "$PROMPT"
|
|
|
|
pnpm typecheck
|
|
pnpm lint
|
|
pnpm test
|
|
|
|
git add -A
|
|
git commit -m "fix: resolve issue #${ISSUE_OR_PR_NUMBER}"
|
|
git push -u origin "$BRANCH_NAME" --force
|
|
|
|
gh pr create \
|
|
--title "fix: resolve issue #${ISSUE_OR_PR_NUMBER}" \
|
|
--body "Resolves #${ISSUE_OR_PR_NUMBER}\n\nFix applied via \`/fix\`.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*" \
|
|
--base next \
|
|
--head "$BRANCH_NAME"
|
|
fi
|
|
|
|
- name: Complete Command (Add Rocket Reaction)
|
|
if: success()
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
run: |
|
|
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}/reactions" -f content=rocket || true
|
|
|
|
- name: Report Failure on Comment
|
|
if: failure()
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
run: |
|
|
gh issue comment "$ISSUE_NUMBER" --body "⚠️ Workflow failed during execution. View run logs: [Action Run #${{ github.run_id }}](${RUN_URL})\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*" || true
|