diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index d4a716b..0000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Claude PR Assistant - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude-code-action: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && contains(github.event.issue.body, '@claude')) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude PR Action - uses: anthropics/claude-code-action@beta - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - timeout_minutes: "60" diff --git a/.github/workflows/gemini-dispatch.yml b/.github/workflows/gemini-dispatch.yml new file mode 100644 index 0000000..4cdec27 --- /dev/null +++ b/.github/workflows/gemini-dispatch.yml @@ -0,0 +1,135 @@ +name: '🔀 Gemini Dispatch' + +on: + pull_request_review_comment: + types: + - 'created' + pull_request_review: + types: + - 'submitted' + pull_request: + types: + - 'opened' + issue_comment: + types: + - 'created' + +defaults: + run: + shell: 'bash' + +jobs: + dispatch: + # For PRs: only if not from a fork + # For issues: only on open/reopen (removed triage logic so this part is simplified) + # For comments: only if user types @gemini-cli and is OWNER/MEMBER/COLLABORATOR + if: |- + ( + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.fork == false + ) || ( + github.event.sender.type == 'User' && + startsWith(github.event.comment.body || github.event.review.body || github.event.issue.body, '@gemini-cli') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association) + ) + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + issues: 'write' + pull-requests: 'write' + outputs: + command: '${{ steps.extract_command.outputs.command }}' + request: '${{ steps.extract_command.outputs.request }}' + additional_context: '${{ steps.extract_command.outputs.additional_context }}' + issue_number: '${{ github.event.pull_request.number || github.event.issue.number }}' + steps: + - name: 'Extract command' + id: 'extract_command' + uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7 + env: + EVENT_TYPE: '${{ github.event_name }}.${{ github.event.action }}' + REQUEST: '${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}' + with: + script: | + const eventType = process.env.EVENT_TYPE; + const request = process.env.REQUEST; + core.setOutput('request', request); + + if (eventType === 'pull_request.opened') { + core.setOutput('command', 'review'); + } else if (request && request.startsWith("@gemini-cli /review")) { + core.setOutput('command', 'review'); + const additionalContext = request.replace(/^@gemini-cli \/review/, '').trim(); + core.setOutput('additional_context', additionalContext); + } else if (request && request.startsWith("@gemini-cli")) { + const additionalContext = request.replace(/^@gemini-cli/, '').trim(); + core.setOutput('command', 'invoke'); + core.setOutput('additional_context', additionalContext); + } else { + core.setOutput('command', 'fallthrough'); + } + + - name: 'Acknowledge request' + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 Hi @${{ github.actor }}, I've received your request, and I'm working on it now! You can track my progress [in the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" + + review: + needs: 'dispatch' + if: |- + ${{ needs.dispatch.outputs.command == 'review' }} + uses: './.github/workflows/gemini-review.yml' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + with: + additional_context: '${{ needs.dispatch.outputs.additional_context }}' + secrets: 'inherit' # pragma: allowlist secret + + invoke: + needs: 'dispatch' + if: |- + ${{ needs.dispatch.outputs.command == 'invoke' }} + uses: './.github/workflows/gemini-invoke.yml' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + with: + additional_context: '${{ needs.dispatch.outputs.additional_context }}' + secrets: 'inherit' # pragma: allowlist secret + + fallthrough: + needs: + - 'dispatch' + - 'review' + - 'invoke' + if: |- + ${{ always() && !cancelled() && (failure() || needs.dispatch.outputs.command == 'fallthrough') }} + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Send failure comment' + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 I'm sorry @${{ github.actor }}, but I was unable to process your request. Please [see the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" diff --git a/.github/workflows/gemini-invoke.yml b/.github/workflows/gemini-invoke.yml new file mode 100644 index 0000000..dbd03da --- /dev/null +++ b/.github/workflows/gemini-invoke.yml @@ -0,0 +1,60 @@ +name: '▶️ Gemini Invoke' + +on: + workflow_call: + inputs: + additional_context: + type: 'string' + description: 'Any additional context from the request' + required: false + +concurrency: + group: '${{ github.workflow }}-invoke-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' + cancel-in-progress: false + +defaults: + run: + shell: 'bash' + +jobs: + invoke: + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Checkout repository' + uses: 'actions/checkout@v4' + with: + fetch-depth: 0 + + - name: 'Setup Node.js' + uses: 'actions/setup-node@v4' + with: + node-version: '20' + + - name: 'Install Gemini CLI' + run: | + npm install -g @google/gemini-cli@latest + + - name: 'Run Gemini CLI' + id: 'run_gemini' + env: + TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + DESCRIPTION: '${{ github.event.pull_request.body || github.event.issue.body }}' + EVENT_NAME: '${{ github.event_name }}' + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + IS_PULL_REQUEST: '${{ !!github.event.pull_request }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' + GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}' + GEMINI_MODEL: 'gemini-1.5-pro' + run: | + PROMPT="/gemini-invoke" + if [ -n "${ADDITIONAL_CONTEXT}" ]; then + PROMPT="${ADDITIONAL_CONTEXT}" + fi + gemini --yolo "$PROMPT" --output-format json diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml new file mode 100644 index 0000000..6f9f1a5 --- /dev/null +++ b/.github/workflows/gemini-review.yml @@ -0,0 +1,54 @@ +name: '🔎 Gemini Review' + +on: + workflow_call: + inputs: + additional_context: + type: 'string' + description: 'Any additional context from the request' + required: false + +concurrency: + group: '${{ github.workflow }}-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +jobs: + review: + runs-on: 'ubuntu-latest' + timeout-minutes: 10 + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Checkout repository' + uses: 'actions/checkout@v4' + with: + fetch-depth: 0 + + - name: 'Setup Node.js' + uses: 'actions/setup-node@v4' + with: + node-version: '20' + + - name: 'Install Gemini CLI' + run: | + npm install -g @google/gemini-cli@latest + + - name: 'Run Gemini pull request review' + id: 'gemini_pr_review' + env: + GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}' + ISSUE_TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' + GEMINI_MODEL: 'gemini-1.5-pro' + run: | + gemini --yolo "/gemini-review" --output-format json diff --git a/CLAUDE.md b/GEMINI.md similarity index 100% rename from CLAUDE.md rename to GEMINI.md diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 972031b..ada6dc7 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -8,4 +8,4 @@ images: # Option 2: Build from local wheel (uses local changes) # To use: gcloud builds submit --config cloudbuild-wheel.yaml -# See cloudbuild-wheel.yaml for building with local wheel file \ No newline at end of file +# See cloudbuild-wheel.yaml for building with local wheel file diff --git a/install_gemini.sh b/install_gemini.sh new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_yaml.py b/tests/test_yaml.py index 5d05e5c..2c2125a 100644 --- a/tests/test_yaml.py +++ b/tests/test_yaml.py @@ -101,9 +101,9 @@ def validate_base_time_format(filepath): ) dateformat = timestamp["dateformat"] - if dateformat == 'epoch': + if dateformat == "epoch": format_type = "epoch" - elif dateformat == 'windowsfiletime': + elif dateformat == "windowsfiletime": format_type = "windowsfiletime" else: format_type = "dateformat" @@ -191,7 +191,7 @@ def validate_epoch_dateformat_consistency(filepath): ) # Validate dateformat values - valid_magic_formats = ['epoch', 'windowsfiletime'] + valid_magic_formats = ["epoch", "windowsfiletime"] if dateformat not in valid_magic_formats and not isinstance(dateformat, str): raise ValueError( f"Entry '{entry_name}' timestamp {i}: dateformat must be a string"