mirror of
https://github.com/markwylde/claude-code-gitea-action.git
synced 2026-01-29 11:23:31 +08:00
chore: pull in latest
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
|
dist
|
||||||
|
|
||||||
**/.claude/settings.local.json
|
**/.claude/settings.local.json
|
||||||
|
|||||||
100
README.md
100
README.md
@@ -19,7 +19,7 @@ A Gitea action that provides a general-purpose [Claude Code](https://claude.ai/c
|
|||||||
|
|
||||||
**Requirements**: You must be a repository admin to complete these steps.
|
**Requirements**: You must be a repository admin to complete these steps.
|
||||||
|
|
||||||
1. Add `ANTHROPIC_API_KEY` or `CLAUDE_CREDENTIALS` to your repository secrets
|
1. Add `ANTHROPIC_API_KEY` to your repository secrets
|
||||||
2. Add `GITEA_TOKEN` to your repository secrets (a personal access token with repository read/write permissions)
|
2. Add `GITEA_TOKEN` to your repository secrets (a personal access token with repository read/write permissions)
|
||||||
3. Copy the workflow file from [`examples/gitea-claude.yml`](./examples/gitea-claude.yml) into your repository's `.gitea/workflows/`
|
3. Copy the workflow file from [`examples/gitea-claude.yml`](./examples/gitea-claude.yml) into your repository's `.gitea/workflows/`
|
||||||
|
|
||||||
@@ -47,7 +47,6 @@ jobs:
|
|||||||
- uses: markwylde/claude-code-gitea-action@v1.0.5
|
- uses: markwylde/claude-code-gitea-action@v1.0.5
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} # if you want to use direct API
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} # if you want to use direct API
|
||||||
claude_credentials: ${{ secrets.CLAUDE_CREDENTIALS }} # if you have a Claude Max subscription
|
|
||||||
gitea_token: ${{ secrets.GITEA_TOKEN }} # could be another users token (specific Claude user?)
|
gitea_token: ${{ secrets.GITEA_TOKEN }} # could be another users token (specific Claude user?)
|
||||||
claude_git_name: Claude # optional
|
claude_git_name: Claude # optional
|
||||||
claude_git_email: claude@anthropic.com # optional
|
claude_git_email: claude@anthropic.com # optional
|
||||||
@@ -57,8 +56,8 @@ jobs:
|
|||||||
|
|
||||||
| Input | Description | Required | Default |
|
| Input | Description | Required | Default |
|
||||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------- |
|
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------- |
|
||||||
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex). Set to 'use-oauth' when using claude_credentials | No\* | - |
|
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
|
||||||
| `claude_credentials` | Claude OAuth credentials JSON for Claude AI Max subscription authentication | No | - |
|
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No | - |
|
||||||
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
|
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
|
||||||
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
|
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
|
||||||
| `gitea_token` | Gitea token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
|
| `gitea_token` | Gitea token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
|
||||||
@@ -78,45 +77,6 @@ jobs:
|
|||||||
|
|
||||||
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
|
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
|
||||||
|
|
||||||
## Claude Max Authentication
|
|
||||||
|
|
||||||
This action supports authentication using Claude Max OAuth credentials. This allows users with Claude Max subscriptions to use their existing authentication.
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
|
|
||||||
1. **Get OAuth Credentials**: Use Claude Code to generate OAuth credentials:
|
|
||||||
|
|
||||||
```
|
|
||||||
/auth-setup
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add Credentials to Repository**: Add the generated JSON credentials as a repository secret named `CLAUDE_CREDENTIALS`.
|
|
||||||
|
|
||||||
It should look like this:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"claudeAiOauth": {
|
|
||||||
"accessToken": "sk-ant-xxx",
|
|
||||||
"refreshToken": "sk-ant-xxx",
|
|
||||||
"expiresAt": 1748707000000,
|
|
||||||
"scopes": ["user:inference", "user:profile"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Configure Workflow**: Set up your workflow to use OAuth authentication:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: markwylde/claude-code-gitea-action@v1.0.5
|
|
||||||
with:
|
|
||||||
anthropic_api_key: "use-oauth"
|
|
||||||
claude_credentials: ${{ secrets.CLAUDE_CREDENTIALS }}
|
|
||||||
gitea_token: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
```
|
|
||||||
|
|
||||||
When `anthropic_api_key` is set to `'use-oauth'`, the action will use the OAuth credentials provided in `claude_credentials` instead of a direct API key.
|
|
||||||
|
|
||||||
## Gitea Configuration
|
## Gitea Configuration
|
||||||
|
|
||||||
This action has been enhanced to work with Gitea installations. The main differences from GitHub are:
|
This action has been enhanced to work with Gitea installations. The main differences from GitHub are:
|
||||||
@@ -125,6 +85,31 @@ This action has been enhanced to work with Gitea installations. The main differe
|
|||||||
|
|
||||||
2. **API URL Configuration**: You must specify your Gitea server URL using the `gitea_api_url` input.
|
2. **API URL Configuration**: You must specify your Gitea server URL using the `gitea_api_url` input.
|
||||||
|
|
||||||
|
3. **Custom Server URL**: For Gitea instances running in containers, you can override link generation using the `GITEA_SERVER_URL` environment variable.
|
||||||
|
|
||||||
|
### Custom Server URL Configuration
|
||||||
|
|
||||||
|
When running Gitea in containers, the action may generate links using internal container URLs (e.g., `http://gitea:3000`) instead of your public URL. To fix this, set the `GITEA_SERVER_URL` environment variable:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: markwylde/claude-code-gitea-action@v1.0.5
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
gitea_token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
env:
|
||||||
|
# Override the internal container URL with your public URL
|
||||||
|
GITEA_SERVER_URL: https://gitea.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- The action first checks for `GITEA_SERVER_URL` (user-configurable)
|
||||||
|
- Falls back to `GITHUB_SERVER_URL` (automatically set by Gitea Actions)
|
||||||
|
- Uses `https://github.com` as final fallback
|
||||||
|
|
||||||
|
This ensures that all links in Claude's comments (job runs, branches, etc.) point to your public Gitea instance instead of internal container addresses.
|
||||||
|
|
||||||
|
See [`examples/gitea-custom-url.yml`](./examples/gitea-custom-url.yml) for a complete example.
|
||||||
|
|
||||||
### Gitea Setup Notes
|
### Gitea Setup Notes
|
||||||
|
|
||||||
- Use a Gitea personal access token "GITEA_TOKEN"
|
- Use a Gitea personal access token "GITEA_TOKEN"
|
||||||
@@ -575,10 +560,33 @@ For a complete list of available settings and their descriptions, see the [Claud
|
|||||||
|
|
||||||
## Cloud Providers
|
## Cloud Providers
|
||||||
|
|
||||||
You can authenticate with Claude using any of these three methods:
|
You can authenticate with Claude using any of these methods:
|
||||||
|
|
||||||
1. Direct Anthropic API (default)
|
1. **Direct Anthropic API** (default) - Use your Anthropic API key
|
||||||
2. Anthropic OAuth credentials (Claude Max subscription)
|
2. **Claude Code OAuth Token** - Use OAuth token from Claude Code application
|
||||||
|
|
||||||
|
### Using Claude Code OAuth Token
|
||||||
|
|
||||||
|
If you have access to [Claude Code](https://claude.ai/code), you can use OAuth authentication instead of an API key:
|
||||||
|
|
||||||
|
1. **Generate OAuth Token**: run the following command and follow instructions:
|
||||||
|
```
|
||||||
|
claude setup-token
|
||||||
|
```
|
||||||
|
This will generate an OAuth token that you can use for authentication.
|
||||||
|
|
||||||
|
2. **Add Token to Repository**: Add the generated token as a repository secret named `CLAUDE_CODE_OAUTH_TOKEN`.
|
||||||
|
|
||||||
|
3. **Configure Workflow**: Use the OAuth token in your workflow:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: markwylde/claude-code-gitea-action@v1.0.5
|
||||||
|
with:
|
||||||
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||||
|
gitea_token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
When `claude_code_oauth_token` is provided, it will be used instead of `anthropic_api_key` for authentication.
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
|
|||||||
118
action.yml
118
action.yml
@@ -16,19 +16,17 @@ inputs:
|
|||||||
description: "The label that triggers the action (e.g. claude)"
|
description: "The label that triggers the action (e.g. claude)"
|
||||||
required: false
|
required: false
|
||||||
default: "claude"
|
default: "claude"
|
||||||
base_branch:
|
|
||||||
description: "The branch to use as the base/source when creating new branches (defaults to repository default branch)"
|
|
||||||
required: false
|
|
||||||
branch_prefix:
|
branch_prefix:
|
||||||
description: "The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format)"
|
description: "The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format)"
|
||||||
required: false
|
required: false
|
||||||
default: "claude/"
|
default: "claude/"
|
||||||
|
|
||||||
# Mode configuration
|
|
||||||
mode:
|
mode:
|
||||||
description: "Execution mode for the action. Valid modes: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation with no trigger checking)"
|
description: "Execution mode for the action. Valid modes: 'tag' (default) or 'agent'"
|
||||||
required: false
|
required: false
|
||||||
default: "tag"
|
default: "tag"
|
||||||
|
base_branch:
|
||||||
|
description: "The branch to use as the base/source when creating new branches (defaults to repository default branch)"
|
||||||
|
required: false
|
||||||
|
|
||||||
# Claude Code configuration
|
# Claude Code configuration
|
||||||
model:
|
model:
|
||||||
@@ -37,9 +35,6 @@ inputs:
|
|||||||
anthropic_model:
|
anthropic_model:
|
||||||
description: "DEPRECATED: Use 'model' instead. Model to use (provider-specific format required for Bedrock/Vertex)"
|
description: "DEPRECATED: Use 'model' instead. Model to use (provider-specific format required for Bedrock/Vertex)"
|
||||||
required: false
|
required: false
|
||||||
fallback_model:
|
|
||||||
description: "Enable automatic fallback to specified model when primary model is unavailable"
|
|
||||||
required: false
|
|
||||||
allowed_tools:
|
allowed_tools:
|
||||||
description: "Additional tools for Claude to use (the base GitHub tools will always be included)"
|
description: "Additional tools for Claude to use (the base GitHub tools will always be included)"
|
||||||
required: false
|
required: false
|
||||||
@@ -60,31 +55,48 @@ inputs:
|
|||||||
description: "Complete replacement of Claude's prompt with custom template (supports variable substitution)"
|
description: "Complete replacement of Claude's prompt with custom template (supports variable substitution)"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
mcp_config:
|
|
||||||
description: "Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers"
|
# New Claude Code settings
|
||||||
|
settings:
|
||||||
|
description: "Path to Claude Code settings JSON file, or settings JSON string"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
system_prompt:
|
||||||
|
description: "Override system prompt"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
append_system_prompt:
|
||||||
|
description: "Append to system prompt"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
claude_env:
|
||||||
|
description: "Custom environment variables to pass to Claude Code execution (YAML multiline format)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
additional_permissions:
|
additional_permissions:
|
||||||
description: "Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results"
|
description: "Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
claude_env:
|
fallback_model:
|
||||||
description: "Custom environment variables to pass to Claude Code execution (YAML format)"
|
description: "Enable automatic fallback to specified model when default model is overloaded"
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
settings:
|
|
||||||
description: "Claude Code settings as JSON string or path to settings JSON file"
|
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
|
|
||||||
# Auth configuration
|
# Auth configuration
|
||||||
anthropic_api_key:
|
anthropic_api_key:
|
||||||
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex). Set to 'use-oauth' when using claude_credentials"
|
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex)"
|
||||||
required: false
|
required: false
|
||||||
claude_credentials:
|
claude_code_oauth_token:
|
||||||
description: "Claude OAuth credentials JSON for Claude AI Max subscription authentication"
|
description: "Claude Code OAuth token (alternative to anthropic_api_key)"
|
||||||
required: false
|
required: false
|
||||||
|
default: ""
|
||||||
gitea_token:
|
gitea_token:
|
||||||
description: "Gitea token with repo and pull request permissions (defaults to GITHUB_TOKEN)"
|
description: "Gitea token with repo and pull request permissions (defaults to GITHUB_TOKEN)"
|
||||||
required: false
|
required: false
|
||||||
|
path_to_claude_code_executable:
|
||||||
|
description: "Path to a custom Claude Code executable to use instead of installing"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
use_bedrock:
|
use_bedrock:
|
||||||
description: "Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API"
|
description: "Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API"
|
||||||
required: false
|
required: false
|
||||||
@@ -93,6 +105,10 @@ inputs:
|
|||||||
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: "false"
|
||||||
|
use_node_cache:
|
||||||
|
description: "Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files)"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
max_turns:
|
max_turns:
|
||||||
description: "Maximum number of conversation turns"
|
description: "Maximum number of conversation turns"
|
||||||
@@ -130,14 +146,14 @@ runs:
|
|||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cd ${GITHUB_ACTION_PATH}
|
cd ${{ github.action_path }}
|
||||||
bun install
|
bun install
|
||||||
|
|
||||||
- name: Prepare action
|
- name: Prepare action
|
||||||
id: prepare
|
id: prepare
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/prepare.ts
|
bun run ${{ github.action_path }}/src/entrypoints/prepare.ts
|
||||||
env:
|
env:
|
||||||
MODE: ${{ inputs.mode }}
|
MODE: ${{ inputs.mode }}
|
||||||
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
|
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
|
||||||
@@ -149,21 +165,53 @@ runs:
|
|||||||
DISALLOWED_TOOLS: ${{ inputs.disallowed_tools }}
|
DISALLOWED_TOOLS: ${{ inputs.disallowed_tools }}
|
||||||
CUSTOM_INSTRUCTIONS: ${{ inputs.custom_instructions }}
|
CUSTOM_INSTRUCTIONS: ${{ inputs.custom_instructions }}
|
||||||
DIRECT_PROMPT: ${{ inputs.direct_prompt }}
|
DIRECT_PROMPT: ${{ inputs.direct_prompt }}
|
||||||
|
OVERRIDE_PROMPT: ${{ inputs.override_prompt }}
|
||||||
|
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||||
OVERRIDE_GITHUB_TOKEN: ${{ inputs.gitea_token }}
|
OVERRIDE_GITHUB_TOKEN: ${{ inputs.gitea_token }}
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||||
GITEA_API_URL: ${{ env.GITHUB_SERVER_URL }}
|
GITEA_API_URL: ${{ env.GITHUB_SERVER_URL }}
|
||||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||||
CLAUDE_CREDENTIALS: ${{ inputs.claude_credentials }}
|
|
||||||
|
- name: Install Claude
|
||||||
|
if: steps.prepare.outputs.contains_trigger == 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Install Claude Code if no custom executable is provided
|
||||||
|
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||||
|
echo "Installing Claude Code..."
|
||||||
|
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.117
|
||||||
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||||
|
else
|
||||||
|
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||||
|
# Add the directory containing the custom executable to PATH
|
||||||
|
CLAUDE_DIR=$(dirname "${{ inputs.path_to_claude_code_executable }}")
|
||||||
|
echo "$CLAUDE_DIR" >> "$GITHUB_PATH"
|
||||||
|
fi
|
||||||
|
# TODO pass claude_code_executable as input and use it here
|
||||||
|
|
||||||
- name: Run Claude Code
|
- name: Run Claude Code
|
||||||
id: claude-code
|
id: claude-code
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true'
|
if: steps.prepare.outputs.contains_trigger == 'true'
|
||||||
shell: bash
|
uses: anthropics/claude-code-base-action@v0.0.63
|
||||||
run: |
|
with:
|
||||||
|
prompt_file: /tmp/claude-prompts/claude-prompt.txt
|
||||||
# Run the base-action
|
allowed_tools: ${{ env.ALLOWED_TOOLS }}
|
||||||
bun run ${GITHUB_ACTION_PATH}/base-action/src/index.ts
|
disallowed_tools: ${{ env.DISALLOWED_TOOLS }}
|
||||||
|
max_turns: ${{ inputs.max_turns }}
|
||||||
|
timeout_minutes: ${{ inputs.timeout_minutes }}
|
||||||
|
model: ${{ inputs.model || inputs.anthropic_model }}
|
||||||
|
mcp_config: ${{ steps.prepare.outputs.mcp_config }}
|
||||||
|
use_bedrock: ${{ inputs.use_bedrock }}
|
||||||
|
use_vertex: ${{ inputs.use_vertex }}
|
||||||
|
anthropic_api_key: ${{ inputs.anthropic_api_key }}
|
||||||
|
claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }}
|
||||||
|
settings: ${{ inputs.settings }}
|
||||||
|
system_prompt: ${{ inputs.system_prompt }}
|
||||||
|
append_system_prompt: ${{ inputs.append_system_prompt }}
|
||||||
|
claude_env: ${{ inputs.claude_env }}
|
||||||
|
fallback_model: ${{ inputs.fallback_model }}
|
||||||
|
use_node_cache: ${{ inputs.use_node_cache }}
|
||||||
env:
|
env:
|
||||||
# Core configuration
|
# Core configuration
|
||||||
PROMPT_FILE: /tmp/claude-prompts/claude-prompt.txt
|
PROMPT_FILE: /tmp/claude-prompts/claude-prompt.txt
|
||||||
@@ -176,7 +224,15 @@ runs:
|
|||||||
USE_BEDROCK: ${{ inputs.use_bedrock }}
|
USE_BEDROCK: ${{ inputs.use_bedrock }}
|
||||||
USE_VERTEX: ${{ inputs.use_vertex }}
|
USE_VERTEX: ${{ inputs.use_vertex }}
|
||||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||||
CLAUDE_CREDENTIALS: ${{ inputs.claude_credentials }}
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
|
||||||
|
|
||||||
|
# New settings support
|
||||||
|
SETTINGS: ${{ inputs.settings }}
|
||||||
|
SYSTEM_PROMPT: ${{ inputs.system_prompt }}
|
||||||
|
APPEND_SYSTEM_PROMPT: ${{ inputs.append_system_prompt }}
|
||||||
|
CLAUDE_ENV: ${{ inputs.claude_env }}
|
||||||
|
FALLBACK_MODEL: ${{ inputs.fallback_model }}
|
||||||
|
USE_NODE_CACHE: ${{ inputs.use_node_cache }}
|
||||||
|
|
||||||
# GitHub token for repository access
|
# GitHub token for repository access
|
||||||
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
||||||
@@ -193,8 +249,6 @@ runs:
|
|||||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
||||||
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL }}
|
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL }}
|
||||||
|
|
||||||
# GCP configuration
|
|
||||||
ANTHROPIC_VERTEX_PROJECT_ID: ${{ env.ANTHROPIC_VERTEX_PROJECT_ID }}
|
ANTHROPIC_VERTEX_PROJECT_ID: ${{ env.ANTHROPIC_VERTEX_PROJECT_ID }}
|
||||||
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
|
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
|
||||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
|
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||||
@@ -207,7 +261,7 @@ runs:
|
|||||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/update-comment-link.ts
|
bun run ${{ github.action_path }}/src/entrypoints/update-comment-link.ts
|
||||||
env:
|
env:
|
||||||
REPOSITORY: ${{ github.repository }}
|
REPOSITORY: ${{ github.repository }}
|
||||||
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||||
|
|||||||
23
examples/gitea-custom-url.yml
Normal file
23
examples/gitea-custom-url.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Claude PR Review with Custom Gitea URL
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
claude-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Claude Code Analysis
|
||||||
|
uses: markwylde/claude-code-gitea-action@gitea
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
claude-api-key: ${{ secrets.CLAUDE_API_KEY }}
|
||||||
|
env:
|
||||||
|
# Set this to your public Gitea URL to override the internal container URL
|
||||||
|
# This ensures that links in comments point to the correct public URL
|
||||||
|
GITEA_SERVER_URL: https://gitea.example.com
|
||||||
|
|
||||||
|
# Note: GITHUB_SERVER_URL is automatically set by Gitea Actions to the internal URL
|
||||||
|
# but it will be overridden by GITEA_SERVER_URL if set above
|
||||||
273
package-lock.json
generated
273
package-lock.json
generated
@@ -12,8 +12,6 @@
|
|||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@anthropic-ai/sdk": "^0.30.0",
|
"@anthropic-ai/sdk": "^0.30.0",
|
||||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||||
"@octokit/graphql": "^8.2.2",
|
|
||||||
"@octokit/rest": "^21.1.1",
|
|
||||||
"@octokit/webhooks-types": "^7.6.1",
|
"@octokit/webhooks-types": "^7.6.1",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"zod": "^3.24.4"
|
"zod": "^3.24.4"
|
||||||
@@ -211,82 +209,6 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/graphql": {
|
|
||||||
"version": "8.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz",
|
|
||||||
"integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/request": "^9.2.3",
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"universal-user-agent": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/@octokit/endpoint": {
|
|
||||||
"version": "10.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz",
|
|
||||||
"integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"universal-user-agent": "^7.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
|
|
||||||
"version": "25.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
|
|
||||||
"integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/@octokit/request": {
|
|
||||||
"version": "9.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz",
|
|
||||||
"integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/endpoint": "^10.1.4",
|
|
||||||
"@octokit/request-error": "^6.1.8",
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"fast-content-type-parse": "^2.0.0",
|
|
||||||
"universal-user-agent": "^7.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/@octokit/request-error": {
|
|
||||||
"version": "6.1.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz",
|
|
||||||
"integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^14.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/openapi-types": "^25.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/graphql/node_modules/universal-user-agent": {
|
|
||||||
"version": "7.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
|
||||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/openapi-types": {
|
"node_modules/@octokit/openapi-types": {
|
||||||
"version": "24.2.0",
|
"version": "24.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||||
@@ -382,185 +304,6 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/rest": {
|
|
||||||
"version": "21.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz",
|
|
||||||
"integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/core": "^6.1.4",
|
|
||||||
"@octokit/plugin-paginate-rest": "^11.4.2",
|
|
||||||
"@octokit/plugin-request-log": "^5.3.1",
|
|
||||||
"@octokit/plugin-rest-endpoint-methods": "^13.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/auth-token": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/core": {
|
|
||||||
"version": "6.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz",
|
|
||||||
"integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/auth-token": "^5.0.0",
|
|
||||||
"@octokit/graphql": "^8.2.2",
|
|
||||||
"@octokit/request": "^9.2.3",
|
|
||||||
"@octokit/request-error": "^6.1.8",
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"before-after-hook": "^3.0.2",
|
|
||||||
"universal-user-agent": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/core/node_modules/@octokit/types": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/openapi-types": "^25.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/endpoint": {
|
|
||||||
"version": "10.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz",
|
|
||||||
"integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"universal-user-agent": "^7.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/endpoint/node_modules/@octokit/types": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/openapi-types": "^25.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/openapi-types": {
|
|
||||||
"version": "25.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
|
|
||||||
"integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": {
|
|
||||||
"version": "11.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz",
|
|
||||||
"integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^13.10.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@octokit/core": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
|
|
||||||
"version": "5.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz",
|
|
||||||
"integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@octokit/core": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
|
|
||||||
"version": "13.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.5.0.tgz",
|
|
||||||
"integrity": "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^13.10.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@octokit/core": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/request": {
|
|
||||||
"version": "9.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz",
|
|
||||||
"integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/endpoint": "^10.1.4",
|
|
||||||
"@octokit/request-error": "^6.1.8",
|
|
||||||
"@octokit/types": "^14.0.0",
|
|
||||||
"fast-content-type-parse": "^2.0.0",
|
|
||||||
"universal-user-agent": "^7.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/request-error": {
|
|
||||||
"version": "6.1.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz",
|
|
||||||
"integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/types": "^14.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/request-error/node_modules/@octokit/types": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/openapi-types": "^25.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/@octokit/request/node_modules/@octokit/types": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@octokit/openapi-types": "^25.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/before-after-hook": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/rest/node_modules/universal-user-agent": {
|
|
||||||
"version": "7.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
|
||||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/@octokit/types": {
|
"node_modules/@octokit/types": {
|
||||||
"version": "13.10.0",
|
"version": "13.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||||
@@ -1042,22 +785,6 @@
|
|||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fast-content-type-parse": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fastify"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/fastify"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import { mkdir, writeFile } from "fs/promises";
|
|
||||||
import { join } from "path";
|
|
||||||
import { homedir } from "os";
|
|
||||||
|
|
||||||
interface OAuthCredentials {
|
|
||||||
accessToken: string;
|
|
||||||
refreshToken: string;
|
|
||||||
expiresAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ClaudeCredentialsInput {
|
|
||||||
claudeAiOauth: {
|
|
||||||
accessToken: string;
|
|
||||||
refreshToken: string;
|
|
||||||
expiresAt: number;
|
|
||||||
scopes: string[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setupOAuthCredentials(credentialsJson: string) {
|
|
||||||
try {
|
|
||||||
// Parse the credentials JSON
|
|
||||||
const parsedCredentials: ClaudeCredentialsInput =
|
|
||||||
JSON.parse(credentialsJson);
|
|
||||||
|
|
||||||
if (!parsedCredentials.claudeAiOauth) {
|
|
||||||
throw new Error("Invalid credentials format: missing claudeAiOauth");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { accessToken, refreshToken, expiresAt } =
|
|
||||||
parsedCredentials.claudeAiOauth;
|
|
||||||
|
|
||||||
if (!accessToken || !refreshToken || !expiresAt) {
|
|
||||||
throw new Error(
|
|
||||||
"Invalid credentials format: missing required OAuth fields",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const claudeDir = join(homedir(), ".claude");
|
|
||||||
const credentialsPath = join(claudeDir, ".credentials.json");
|
|
||||||
|
|
||||||
// Create the .claude directory if it doesn't exist
|
|
||||||
await mkdir(claudeDir, { recursive: true });
|
|
||||||
|
|
||||||
// Create the credentials JSON structure
|
|
||||||
const credentialsData = {
|
|
||||||
claudeAiOauth: {
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
expiresAt,
|
|
||||||
scopes: ["user:inference", "user:profile"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write the credentials file
|
|
||||||
await writeFile(credentialsPath, JSON.stringify(credentialsData, null, 2));
|
|
||||||
|
|
||||||
console.log(`OAuth credentials written to ${credentialsPath}`);
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
throw new Error(`Failed to setup OAuth credentials: ${errorMessage}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,29 +18,17 @@ import { createPrompt } from "../create-prompt";
|
|||||||
import { createClient } from "../github/api/client";
|
import { createClient } from "../github/api/client";
|
||||||
import { fetchGitHubData } from "../github/data/fetcher";
|
import { fetchGitHubData } from "../github/data/fetcher";
|
||||||
import { parseGitHubContext } from "../github/context";
|
import { parseGitHubContext } from "../github/context";
|
||||||
import { setupOAuthCredentials } from "../claude/oauth-setup";
|
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
try {
|
||||||
// Step 1: Setup OAuth credentials if provided
|
// Step 1: Setup GitHub token
|
||||||
const claudeCredentials = process.env.CLAUDE_CREDENTIALS;
|
|
||||||
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
||||||
|
|
||||||
if (claudeCredentials && anthropicApiKey === "use-oauth") {
|
|
||||||
await setupOAuthCredentials(claudeCredentials);
|
|
||||||
console.log(
|
|
||||||
"OAuth credentials configured for Claude AI Max subscription",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Setup GitHub token
|
|
||||||
const githubToken = await setupGitHubToken();
|
const githubToken = await setupGitHubToken();
|
||||||
const client = createClient(githubToken);
|
const client = createClient(githubToken);
|
||||||
|
|
||||||
// Step 3: Parse GitHub context (once for all operations)
|
// Step 2: Parse GitHub context (once for all operations)
|
||||||
const context = parseGitHubContext();
|
const context = parseGitHubContext();
|
||||||
|
|
||||||
// Step 4: Check write permissions
|
// Step 3: Check write permissions
|
||||||
const hasWritePermissions = await checkWritePermissions(
|
const hasWritePermissions = await checkWritePermissions(
|
||||||
client.api,
|
client.api,
|
||||||
context,
|
context,
|
||||||
@@ -51,7 +39,7 @@ async function run() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Check trigger conditions
|
// Step 4: Check trigger conditions
|
||||||
const containsTrigger = await checkTriggerAction(context);
|
const containsTrigger = await checkTriggerAction(context);
|
||||||
|
|
||||||
// Set outputs that are always needed
|
// Set outputs that are always needed
|
||||||
@@ -63,14 +51,14 @@ async function run() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Check if actor is human
|
// Step 5: Check if actor is human
|
||||||
await checkHumanActor(client.api, context);
|
await checkHumanActor(client.api, context);
|
||||||
|
|
||||||
// Step 7: Create initial tracking comment
|
// Step 6: Create initial tracking comment
|
||||||
const commentId = await createInitialComment(client.api, context);
|
const commentId = await createInitialComment(client.api, context);
|
||||||
core.setOutput("claude_comment_id", commentId.toString());
|
core.setOutput("claude_comment_id", commentId.toString());
|
||||||
|
|
||||||
// Step 8: Fetch GitHub data (once for both branch setup and prompt creation)
|
// Step 7: Fetch GitHub data (once for both branch setup and prompt creation)
|
||||||
const githubData = await fetchGitHubData({
|
const githubData = await fetchGitHubData({
|
||||||
client: client,
|
client: client,
|
||||||
repository: `${context.repository.owner}/${context.repository.repo}`,
|
repository: `${context.repository.owner}/${context.repository.repo}`,
|
||||||
@@ -78,14 +66,14 @@ async function run() {
|
|||||||
isPR: context.isPR,
|
isPR: context.isPR,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 9: Setup branch
|
// Step 8: Setup branch
|
||||||
const branchInfo = await setupBranch(client, githubData, context);
|
const branchInfo = await setupBranch(client, githubData, context);
|
||||||
core.setOutput("BASE_BRANCH", branchInfo.baseBranch);
|
core.setOutput("BASE_BRANCH", branchInfo.baseBranch);
|
||||||
if (branchInfo.claudeBranch) {
|
if (branchInfo.claudeBranch) {
|
||||||
core.setOutput("CLAUDE_BRANCH", branchInfo.claudeBranch);
|
core.setOutput("CLAUDE_BRANCH", branchInfo.claudeBranch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 10: Update initial comment with branch link (only if a claude branch was created)
|
// Step 9: Update initial comment with branch link (only if a claude branch was created)
|
||||||
if (branchInfo.claudeBranch) {
|
if (branchInfo.claudeBranch) {
|
||||||
await updateTrackingComment(
|
await updateTrackingComment(
|
||||||
client,
|
client,
|
||||||
@@ -95,7 +83,7 @@ async function run() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 11: Create prompt file
|
// Step 10: Create prompt file
|
||||||
await createPrompt(
|
await createPrompt(
|
||||||
commentId,
|
commentId,
|
||||||
branchInfo.baseBranch,
|
branchInfo.baseBranch,
|
||||||
@@ -104,7 +92,7 @@ async function run() {
|
|||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 12: Get MCP configuration
|
// Step 11: Get MCP configuration
|
||||||
const mcpConfig = await prepareMcpConfig(
|
const mcpConfig = await prepareMcpConfig(
|
||||||
githubToken,
|
githubToken,
|
||||||
context.repository.owner,
|
context.repository.owner,
|
||||||
|
|||||||
@@ -7,8 +7,25 @@ function deriveApiUrl(serverUrl: string): string {
|
|||||||
return `${serverUrl}/api/v1`;
|
return `${serverUrl}/api/v1`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GITEA_SERVER_URL =
|
// Get the appropriate server URL, prioritizing GITEA_SERVER_URL for custom Gitea instances
|
||||||
process.env.GITHUB_SERVER_URL || "https://github.com";
|
function getServerUrl(): string {
|
||||||
|
// First check for GITEA_SERVER_URL (can be set by user)
|
||||||
|
const giteaServerUrl = process.env.GITEA_SERVER_URL;
|
||||||
|
if (giteaServerUrl && giteaServerUrl !== "") {
|
||||||
|
return giteaServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to GITHUB_SERVER_URL (set by Gitea/GitHub Actions environment)
|
||||||
|
const githubServerUrl = process.env.GITHUB_SERVER_URL;
|
||||||
|
if (githubServerUrl && githubServerUrl !== "") {
|
||||||
|
return githubServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default fallback
|
||||||
|
return "https://github.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GITEA_SERVER_URL = getServerUrl();
|
||||||
|
|
||||||
export const GITEA_API_URL =
|
export const GITEA_API_URL =
|
||||||
process.env.GITEA_API_URL || deriveApiUrl(GITEA_SERVER_URL);
|
process.env.GITEA_API_URL || deriveApiUrl(GITEA_SERVER_URL);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export async function setupBranch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check out the base branch and let Claude create branches as needed
|
// Check out the base branch and let Claude create branches as needed
|
||||||
await $`git fetch origin ${sourceBranch}`;
|
await $`git fetch origin --depth=1 ${sourceBranch}`;
|
||||||
await $`git checkout ${sourceBranch}`;
|
await $`git checkout ${sourceBranch}`;
|
||||||
await $`git pull origin ${sourceBranch}`;
|
await $`git pull origin ${sourceBranch}`;
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ export async function setupBranch(
|
|||||||
|
|
||||||
// Ensure we have the latest version of the source branch
|
// Ensure we have the latest version of the source branch
|
||||||
console.log(`Fetching latest ${sourceBranch}...`);
|
console.log(`Fetching latest ${sourceBranch}...`);
|
||||||
await $`git fetch origin ${sourceBranch}`;
|
await $`git fetch origin --depth=1 ${sourceBranch}`;
|
||||||
|
|
||||||
// Checkout the source branch
|
// Checkout the source branch
|
||||||
console.log(`Checking out ${sourceBranch}...`);
|
console.log(`Checking out ${sourceBranch}...`);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export async function branchHasChanges(
|
|||||||
*/
|
*/
|
||||||
export async function fetchBranch(branchName: string): Promise<boolean> {
|
export async function fetchBranch(branchName: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await $`git fetch origin ${branchName}`;
|
await $`git fetch origin --depth=1 ${branchName}`;
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
83
test/gitea-server-url.test.ts
Normal file
83
test/gitea-server-url.test.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
||||||
|
|
||||||
|
describe("GITEA_SERVER_URL configuration", () => {
|
||||||
|
const originalEnv = process.env;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset environment variables
|
||||||
|
process.env = { ...originalEnv };
|
||||||
|
delete process.env.GITEA_SERVER_URL;
|
||||||
|
delete process.env.GITHUB_SERVER_URL;
|
||||||
|
|
||||||
|
// Clear module cache to force re-evaluation
|
||||||
|
delete require.cache[require.resolve("../src/github/api/config")];
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prioritize GITEA_SERVER_URL over GITHUB_SERVER_URL", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "https://gitea.example.com";
|
||||||
|
process.env.GITHUB_SERVER_URL = "http://gitea:3000";
|
||||||
|
|
||||||
|
const { GITEA_SERVER_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_SERVER_URL).toBe("https://gitea.example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fall back to GITHUB_SERVER_URL when GITEA_SERVER_URL is not set", async () => {
|
||||||
|
process.env.GITHUB_SERVER_URL = "http://gitea:3000";
|
||||||
|
|
||||||
|
const { GITEA_SERVER_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_SERVER_URL).toBe("http://gitea:3000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use default when neither GITEA_SERVER_URL nor GITHUB_SERVER_URL is set", async () => {
|
||||||
|
const { GITEA_SERVER_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_SERVER_URL).toBe("https://github.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore empty GITEA_SERVER_URL and use GITHUB_SERVER_URL", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "";
|
||||||
|
process.env.GITHUB_SERVER_URL = "http://gitea:3000";
|
||||||
|
|
||||||
|
const { GITEA_SERVER_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_SERVER_URL).toBe("http://gitea:3000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should derive correct API URL from custom GITEA_SERVER_URL", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "https://gitea.example.com";
|
||||||
|
|
||||||
|
const { GITEA_API_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_API_URL).toBe("https://gitea.example.com/api/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle GitHub.com URLs correctly", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "https://github.com";
|
||||||
|
|
||||||
|
const { GITEA_API_URL } = await import("../src/github/api/config");
|
||||||
|
expect(GITEA_API_URL).toBe("https://api.github.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create correct job run links with custom GITEA_SERVER_URL", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "https://gitea.example.com";
|
||||||
|
|
||||||
|
// Clear module cache and re-import
|
||||||
|
delete require.cache[require.resolve("../src/github/operations/comments/common")];
|
||||||
|
const { createJobRunLink } = await import("../src/github/operations/comments/common");
|
||||||
|
|
||||||
|
const link = createJobRunLink("owner", "repo", "123");
|
||||||
|
expect(link).toBe("[View job run](https://gitea.example.com/owner/repo/actions/runs/123)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create correct branch links with custom GITEA_SERVER_URL", async () => {
|
||||||
|
process.env.GITEA_SERVER_URL = "https://gitea.example.com";
|
||||||
|
|
||||||
|
// Clear module cache and re-import
|
||||||
|
delete require.cache[require.resolve("../src/github/operations/comments/common")];
|
||||||
|
const { createBranchLink } = await import("../src/github/operations/comments/common");
|
||||||
|
|
||||||
|
const link = createBranchLink("owner", "repo", "feature-branch");
|
||||||
|
expect(link).toBe("\n[View branch](https://gitea.example.com/owner/repo/src/branch/feature-branch/)");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// Environment setup & latest features
|
// Environment setup & latest features
|
||||||
"lib": ["ESNext"],
|
"lib": ["ESNext", "DOM"],
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
|
|||||||
Reference in New Issue
Block a user