mirror of
https://github.com/markwylde/claude-code-gitea-action.git
synced 2026-02-20 02:22:49 +08:00
Attempt to make this work
This commit is contained in:
16
MIGRATION.md
16
MIGRATION.md
@@ -97,8 +97,10 @@ Be aware of these Gitea Actions limitations:
|
|||||||
|
|
||||||
- **`issue_comment` on PRs**: May not trigger reliably in some Gitea versions
|
- **`issue_comment` on PRs**: May not trigger reliably in some Gitea versions
|
||||||
- **`pull_request_review_comment`**: Limited support compared to GitHub
|
- **`pull_request_review_comment`**: Limited support compared to GitHub
|
||||||
|
- **GraphQL API**: Not supported - action automatically falls back to REST API
|
||||||
- **Cross-repository access**: Token permissions may be more restrictive
|
- **Cross-repository access**: Token permissions may be more restrictive
|
||||||
- **Workflow triggers**: Some advanced trigger conditions may not work
|
- **Workflow triggers**: Some advanced trigger conditions may not work
|
||||||
|
- **Permission checking**: Simplified for Gitea compatibility
|
||||||
|
|
||||||
### 3. Gitea Workarounds
|
### 3. Gitea Workarounds
|
||||||
|
|
||||||
@@ -136,6 +138,8 @@ Gitea has limited support for code review comment webhooks. Consider using:
|
|||||||
|
|
||||||
### 4. Gitea Support
|
### 4. Gitea Support
|
||||||
- Compatible with self-hosted Gitea
|
- Compatible with self-hosted Gitea
|
||||||
|
- Automatic fallback to REST API (no GraphQL dependency)
|
||||||
|
- Simplified permission checking for Gitea environments
|
||||||
- Reduced external dependencies
|
- Reduced external dependencies
|
||||||
- Standard Actions workflow patterns
|
- Standard Actions workflow patterns
|
||||||
|
|
||||||
@@ -163,18 +167,26 @@ permissions:
|
|||||||
|
|
||||||
### Gitea-Specific Issues
|
### Gitea-Specific Issues
|
||||||
|
|
||||||
#### 1. Limited Event Support
|
#### 1. Authentication Errors
|
||||||
|
**Error**: "Failed to check permissions: HttpError: Bad credentials"
|
||||||
|
**Solution**: This is normal in Gitea environments. The action automatically detects Gitea and bypasses GitHub-specific permission checks.
|
||||||
|
|
||||||
|
#### 2. Limited Event Support
|
||||||
Some GitHub Events may not be fully supported in Gitea. Use basic triggers:
|
Some GitHub Events may not be fully supported in Gitea. Use basic triggers:
|
||||||
- `issue_comment` for comments
|
- `issue_comment` for comments
|
||||||
- `issues` for issue events
|
- `issues` for issue events
|
||||||
- `push` for code changes
|
- `push` for code changes
|
||||||
|
|
||||||
#### 2. Token Scope Limitations
|
#### 3. Token Scope Limitations
|
||||||
Gitea tokens may have different scope limitations. Ensure your Gitea instance allows:
|
Gitea tokens may have different scope limitations. Ensure your Gitea instance allows:
|
||||||
- Repository write access
|
- Repository write access
|
||||||
- Issue/PR comment creation
|
- Issue/PR comment creation
|
||||||
- Branch creation and updates
|
- Branch creation and updates
|
||||||
|
|
||||||
|
#### 4. GraphQL Not Supported
|
||||||
|
**Error**: GraphQL queries failing
|
||||||
|
**Solution**: The action automatically detects Gitea and uses REST API instead of GraphQL. No manual configuration needed.
|
||||||
|
|
||||||
## Migration Checklist
|
## Migration Checklist
|
||||||
|
|
||||||
- [ ] Update workflow permissions to include `write` access
|
- [ ] Update workflow permissions to include `write` access
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ export type Octokits = {
|
|||||||
|
|
||||||
export function createOctokit(token: string): Octokits {
|
export function createOctokit(token: string): Octokits {
|
||||||
return {
|
return {
|
||||||
rest: new Octokit({ auth: token }),
|
rest: new Octokit({
|
||||||
|
auth: token,
|
||||||
|
baseUrl: GITHUB_API_URL,
|
||||||
|
}),
|
||||||
graphql: graphql.defaults({
|
graphql: graphql.defaults({
|
||||||
baseUrl: GITHUB_API_URL,
|
baseUrl: GITHUB_API_URL,
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -44,52 +44,154 @@ export async function fetchGitHubData({
|
|||||||
throw new Error("Invalid repository format. Expected 'owner/repo'.");
|
throw new Error("Invalid repository format. Expected 'owner/repo'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're in a Gitea environment (no GraphQL support)
|
||||||
|
const isGitea = process.env.GITHUB_API_URL && !process.env.GITHUB_API_URL.includes('api.github.com');
|
||||||
|
|
||||||
let contextData: GitHubPullRequest | GitHubIssue | null = null;
|
let contextData: GitHubPullRequest | GitHubIssue | null = null;
|
||||||
let comments: GitHubComment[] = [];
|
let comments: GitHubComment[] = [];
|
||||||
let changedFiles: GitHubFile[] = [];
|
let changedFiles: GitHubFile[] = [];
|
||||||
let reviewData: { nodes: GitHubReview[] } | null = null;
|
let reviewData: { nodes: GitHubReview[] } | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isPR) {
|
if (isGitea) {
|
||||||
// Fetch PR data with all comments and file information
|
// Use REST API for Gitea compatibility
|
||||||
const prResult = await octokits.graphql<PullRequestQueryResponse>(
|
if (isPR) {
|
||||||
PR_QUERY,
|
console.log(`Fetching PR #${prNumber} data using REST API (Gitea mode)`);
|
||||||
{
|
const prResponse = await octokits.rest.pulls.get({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
number: parseInt(prNumber),
|
pull_number: parseInt(prNumber),
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (prResult.repository.pullRequest) {
|
contextData = {
|
||||||
const pullRequest = prResult.repository.pullRequest;
|
title: prResponse.data.title,
|
||||||
contextData = pullRequest;
|
body: prResponse.data.body || "",
|
||||||
changedFiles = pullRequest.files.nodes || [];
|
author: { login: prResponse.data.user?.login || "" },
|
||||||
comments = pullRequest.comments?.nodes || [];
|
baseRefName: prResponse.data.base.ref,
|
||||||
reviewData = pullRequest.reviews || [];
|
headRefName: prResponse.data.head.ref,
|
||||||
|
headRefOid: prResponse.data.head.sha,
|
||||||
|
createdAt: prResponse.data.created_at,
|
||||||
|
additions: prResponse.data.additions || 0,
|
||||||
|
deletions: prResponse.data.deletions || 0,
|
||||||
|
state: prResponse.data.state.toUpperCase(),
|
||||||
|
commits: { totalCount: 0, nodes: [] },
|
||||||
|
files: { nodes: [] },
|
||||||
|
comments: { nodes: [] },
|
||||||
|
reviews: { nodes: [] },
|
||||||
|
};
|
||||||
|
|
||||||
console.log(`Successfully fetched PR #${prNumber} data`);
|
// Fetch comments separately
|
||||||
|
try {
|
||||||
|
const commentsResponse = await octokits.rest.issues.listComments({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: parseInt(prNumber),
|
||||||
|
});
|
||||||
|
comments = commentsResponse.data.map(comment => ({
|
||||||
|
id: comment.id.toString(),
|
||||||
|
databaseId: comment.id.toString(),
|
||||||
|
body: comment.body || "",
|
||||||
|
author: { login: comment.user?.login || "" },
|
||||||
|
createdAt: comment.created_at,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to fetch PR comments:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to fetch files
|
||||||
|
try {
|
||||||
|
const filesResponse = await octokits.rest.pulls.listFiles({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: parseInt(prNumber),
|
||||||
|
});
|
||||||
|
changedFiles = filesResponse.data.map(file => ({
|
||||||
|
path: file.filename,
|
||||||
|
additions: file.additions,
|
||||||
|
deletions: file.deletions,
|
||||||
|
changeType: file.status,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to fetch PR files:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
reviewData = { nodes: [] }; // Simplified for Gitea
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`PR #${prNumber} not found`);
|
console.log(`Fetching issue #${prNumber} data using REST API (Gitea mode)`);
|
||||||
|
const issueResponse = await octokits.rest.issues.get({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: parseInt(prNumber),
|
||||||
|
});
|
||||||
|
|
||||||
|
contextData = {
|
||||||
|
title: issueResponse.data.title,
|
||||||
|
body: issueResponse.data.body || "",
|
||||||
|
author: { login: issueResponse.data.user?.login || "" },
|
||||||
|
createdAt: issueResponse.data.created_at,
|
||||||
|
state: issueResponse.data.state.toUpperCase(),
|
||||||
|
comments: { nodes: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch comments
|
||||||
|
try {
|
||||||
|
const commentsResponse = await octokits.rest.issues.listComments({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: parseInt(prNumber),
|
||||||
|
});
|
||||||
|
comments = commentsResponse.data.map(comment => ({
|
||||||
|
id: comment.id.toString(),
|
||||||
|
databaseId: comment.id.toString(),
|
||||||
|
body: comment.body || "",
|
||||||
|
author: { login: comment.user?.login || "" },
|
||||||
|
createdAt: comment.created_at,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to fetch issue comments:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fetch issue data
|
// Use GraphQL for GitHub
|
||||||
const issueResult = await octokits.graphql<IssueQueryResponse>(
|
if (isPR) {
|
||||||
ISSUE_QUERY,
|
const prResult = await octokits.graphql<PullRequestQueryResponse>(
|
||||||
{
|
PR_QUERY,
|
||||||
owner,
|
{
|
||||||
repo,
|
owner,
|
||||||
number: parseInt(prNumber),
|
repo,
|
||||||
},
|
number: parseInt(prNumber),
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (issueResult.repository.issue) {
|
if (prResult.repository.pullRequest) {
|
||||||
contextData = issueResult.repository.issue;
|
const pullRequest = prResult.repository.pullRequest;
|
||||||
comments = contextData?.comments?.nodes || [];
|
contextData = pullRequest;
|
||||||
|
changedFiles = pullRequest.files.nodes || [];
|
||||||
|
comments = pullRequest.comments?.nodes || [];
|
||||||
|
reviewData = pullRequest.reviews || [];
|
||||||
|
|
||||||
console.log(`Successfully fetched issue #${prNumber} data`);
|
console.log(`Successfully fetched PR #${prNumber} data`);
|
||||||
|
} else {
|
||||||
|
throw new Error(`PR #${prNumber} not found`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Issue #${prNumber} not found`);
|
const issueResult = await octokits.graphql<IssueQueryResponse>(
|
||||||
|
ISSUE_QUERY,
|
||||||
|
{
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
number: parseInt(prNumber),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (issueResult.repository.issue) {
|
||||||
|
contextData = issueResult.repository.issue;
|
||||||
|
comments = contextData?.comments?.nodes || [];
|
||||||
|
|
||||||
|
console.log(`Successfully fetched issue #${prNumber} data`);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Issue #${prNumber} not found`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ export async function checkWritePermissions(
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { repository, actor } = context;
|
const { repository, actor } = context;
|
||||||
|
|
||||||
|
// For Gitea compatibility, check if we're in a non-GitHub environment
|
||||||
|
const isGitea = process.env.GITHUB_API_URL && !process.env.GITHUB_API_URL.includes('api.github.com');
|
||||||
|
|
||||||
|
if (isGitea) {
|
||||||
|
core.info(`Detected Gitea environment, assuming actor has permissions`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
core.info(`Checking permissions for actor: ${actor}`);
|
core.info(`Checking permissions for actor: ${actor}`);
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export function checkContainsTrigger(context: ParsedGitHubContext): boolean {
|
|||||||
inputs: { assigneeTrigger, triggerPhrase, directPrompt },
|
inputs: { assigneeTrigger, triggerPhrase, directPrompt },
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
|
console.log(`Checking trigger: event=${context.eventName}, action=${context.eventAction}, phrase='${triggerPhrase}', assignee='${assigneeTrigger}', direct='${directPrompt}'`);
|
||||||
|
|
||||||
// If direct prompt is provided, always trigger
|
// If direct prompt is provided, always trigger
|
||||||
if (directPrompt) {
|
if (directPrompt) {
|
||||||
console.log(`Direct prompt provided, triggering action`);
|
console.log(`Direct prompt provided, triggering action`);
|
||||||
@@ -24,9 +26,11 @@ export function checkContainsTrigger(context: ParsedGitHubContext): boolean {
|
|||||||
// Check for assignee trigger
|
// Check for assignee trigger
|
||||||
if (isIssuesEvent(context) && context.eventAction === "assigned") {
|
if (isIssuesEvent(context) && context.eventAction === "assigned") {
|
||||||
// Remove @ symbol from assignee_trigger if present
|
// Remove @ symbol from assignee_trigger if present
|
||||||
let triggerUser = assigneeTrigger.replace(/^@/, "");
|
let triggerUser = assigneeTrigger?.replace(/^@/, "") || "";
|
||||||
const assigneeUsername = context.payload.issue.assignee?.login || "";
|
const assigneeUsername = context.payload.issue.assignee?.login || "";
|
||||||
|
|
||||||
|
console.log(`Checking assignee trigger: user='${triggerUser}', assignee='${assigneeUsername}'`);
|
||||||
|
|
||||||
if (triggerUser && assigneeUsername === triggerUser) {
|
if (triggerUser && assigneeUsername === triggerUser) {
|
||||||
console.log(`Issue assigned to trigger user '${triggerUser}'`);
|
console.log(`Issue assigned to trigger user '${triggerUser}'`);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user