Compare commits

...

10 Commits

Author SHA1 Message Date
yiqingxiong
d37f4f79b4 pass USE_GITEA_API env var to prepare 2026-01-31 16:06:50 +08:00
yiqingxiong
2008050d0a fix: always resolve Gitea run ID via API when in Gitea mode 2026-01-31 15:55:51 +08:00
yiqingxiong
8bbd3ab4ec fix run id in gitea
Some checks failed
CI All / test-custom-executables (pull_request) Failing after 26s
CI All / test-base-action (pull_request) Failing after 1m37s
CI All / ci (pull_request) Failing after 2m0s
CI All / test-settings (pull_request) Failing after 2m38s
CI All / test-mcp-servers (pull_request) Failing after 2m48s
CI / test (pull_request) Failing after 1m14s
CI / prettier (pull_request) Failing after 1m0s
CI / typecheck (pull_request) Failing after 43s
Test Claude Code Action / test-inline-prompt (pull_request) Failing after 41s
CI All / test-structured-output (pull_request) Failing after 3m8s
Test Custom Executables / test-custom-executables (pull_request) Failing after 11s
Test Claude Code Action / test-prompt-file (pull_request) Failing after 41s
PR Review / review (pull_request) Failing after 1m23s
Test Settings Feature / test-settings-inline-allow (pull_request) Failing after 47s
Test Settings Feature / test-settings-inline-deny (pull_request) Failing after 49s
Test MCP Servers / test-mcp-integration (pull_request) Failing after 59s
Test Settings Feature / test-settings-file-allow (pull_request) Failing after 39s
Test MCP Servers / test-mcp-config-flag (pull_request) Failing after 1m18s
Test Settings Feature / test-settings-file-deny (pull_request) Failing after 30s
Test Structured Outputs / Test Basic Type Conversions (pull_request) Failing after 31s
Test Structured Outputs / Test Arrays and Objects (pull_request) Failing after 31s
Test Structured Outputs / Test Edge Cases (pull_request) Failing after 28s
Test Structured Outputs / Test Output Name Sanitization (pull_request) Failing after 28s
Test Structured Outputs / Test Execution File Format (pull_request) Failing after 32s
Test Structured Outputs / Summary (pull_request) Failing after 1s
2026-01-31 14:49:12 +08:00
Anonymous
99eb1fd72f update readme 2026-01-30 08:40:56 +08:00
Anonymous
011bea4cdc cleanup GITEA_BOT_USERNAMES 2026-01-30 08:22:13 +08:00
Anonymous
f70d7d5217 fix style 2026-01-29 23:11:02 +08:00
Anonymous
99469dc2cb fix typo and simplify reviewData 2026-01-29 23:09:09 +08:00
Anonymous
e5ee232989 simplify createBranchUrl 2026-01-29 23:07:16 +08:00
Anonymous
7904dcc331 update readme 2026-01-29 23:00:24 +08:00
Anonymous
3fcf15fd15 gitea support 2026-01-29 22:56:05 +08:00
14 changed files with 495 additions and 36 deletions

View File

@ -4,6 +4,21 @@
A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs and issues that can answer questions and implement code changes. This action intelligently detects when to activate based on your workflow context—whether responding to @claude mentions, issue assignments, or executing automation tasks with explicit prompts. It supports multiple authentication methods including Anthropic direct API, Amazon Bedrock, Google Vertex AI, and Microsoft Foundry.
## 🍵 Gitea Support
This action now supports Gitea through the GITEA API. Key features:
**Configuration Example for Gitea:**
```yaml
env:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} # your gitea PAT
GITHUB_API_URL: "https://gitea.example.com/api/v1"
GITHUB_SERVER_URL: "https://gitea.example.com"
USE_GITEA_API: "true"
input:
allowed_bots: "bot" # comma-separated, or `*`
```
## Features
- 🎯 **Intelligent Mode Detection**: Automatically selects the appropriate execution mode based on your workflow context—no configuration needed
@ -16,6 +31,7 @@ A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs an
- 📊 **Structured Outputs**: Get validated JSON results that automatically become GitHub Action outputs for complex automations
- 🏃 **Runs on Your Infrastructure**: The action executes entirely on your own GitHub runner (Anthropic API calls go to your chosen provider)
- ⚙️ **Simplified Configuration**: Unified `prompt` and `claude_args` inputs provide clean, powerful configuration aligned with Claude Code SDK
- 🍵 **Gitea Support**: Support with Gitea through GITEA API.
## 📦 Upgrading from v0.x?

View File

@ -197,6 +197,8 @@ runs:
INCLUDE_COMMENTS_BY_ACTOR: ${{ inputs.include_comments_by_actor }}
EXCLUDE_COMMENTS_BY_ACTOR: ${{ inputs.exclude_comments_by_actor }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITEA_RUN_NUMBER: ${{ env.GITEA_RUN_NUMBER }}
USE_GITEA_API: ${{ env.USE_GITEA_API }}
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
@ -321,7 +323,9 @@ runs:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
CLAUDE_COMMENT_ID: ${{ steps.prepare.outputs.claude_comment_id }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_RUN_ID: ${{ steps.prepare.outputs.run_id || github.run_id }}
GITEA_RUN_NUMBER: ${{ env.GITEA_RUN_NUMBER }}
USE_GITEA_API: ${{ env.USE_GITEA_API }}
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
GITHUB_EVENT_NAME: ${{ github.event_name }}

View File

@ -6,6 +6,7 @@
*/
import * as core from "@actions/core";
import * as github from "@actions/github";
import { setupGitHubToken } from "../github/token";
import { checkWritePermissions } from "../github/validation/permissions";
import { createOctokit } from "../github/api/client";
@ -13,11 +14,26 @@ import { parseGitHubContext, isEntityContext } from "../github/context";
import { getMode } from "../modes/registry";
import { prepare } from "../prepare";
import { collectActionInputsPresence } from "./collect-inputs";
import { setupGiteaRunId } from "../github/api/gitea-run-id";
async function run() {
try {
collectActionInputsPresence();
// Resolve Gitea run ID before parsing context (uses GITEA_RUN_NUMBER if available)
// We need a token for this - use either the override token or the default workflow token
const earlyToken =
process.env.OVERRIDE_GITHUB_TOKEN || process.env.DEFAULT_WORKFLOW_TOKEN;
if (earlyToken) {
const { owner, repo } = github.context.repo;
await setupGiteaRunId(earlyToken, owner, repo);
}
// Output the resolved run ID for subsequent steps
if (process.env.GITHUB_RUN_ID) {
core.setOutput("run_id", process.env.GITHUB_RUN_ID);
}
// Parse GitHub context first to enable mode detection
const context = parseGitHubContext();

View File

@ -1,5 +1,6 @@
#!/usr/bin/env bun
import * as github from "@actions/github";
import { createOctokit } from "../github/api/client";
import * as fs from "fs/promises";
import {
@ -14,6 +15,7 @@ import {
import { GITHUB_SERVER_URL } from "../github/api/config";
import { checkAndCommitOrDeleteBranch } from "../github/operations/branch-cleanup";
import { updateClaudeComment } from "../github/operations/comments/update-claude-comment";
import { setupGiteaRunId } from "../github/api/gitea-run-id";
async function run() {
try {
@ -23,6 +25,10 @@ async function run() {
const baseBranch = process.env.BASE_BRANCH || "main";
const triggerUsername = process.env.TRIGGER_USERNAME;
// Resolve Gitea run ID before parsing context (uses GITEA_RUN_NUMBER if available)
const { owner, repo } = github.context.repo;
await setupGiteaRunId(githubToken, owner, repo);
const context = parseGitHubContext();
// This script is only called for entity-based events
@ -30,8 +36,6 @@ async function run() {
throw new Error("update-comment-link requires an entity context");
}
const { owner, repo } = context.repository;
const octokit = createOctokit(githubToken);
const serverUrl = GITHUB_SERVER_URL;

View File

@ -2,3 +2,4 @@ export const GITHUB_API_URL =
process.env.GITHUB_API_URL || "https://api.github.com";
export const GITHUB_SERVER_URL =
process.env.GITHUB_SERVER_URL || "https://github.com";
export const USE_GITEA_API = process.env.USE_GITEA_API === "true" || process.env.USE_GITEA_API === "1";

View File

@ -0,0 +1,91 @@
import { GITHUB_API_URL, USE_GITEA_API } from "./config";
/**
* Resolves the Gitea run ID from the run number.
* In Gitea, the environment variable GITHUB_RUN_ID is not available,
* so we need to fetch it via the API using GITEA_RUN_NUMBER.
*
* @param token - GitHub/Gitea token for authentication
* @param owner - Repository owner
* @param repo - Repository name
* @returns The resolved run ID, or undefined if not found
*/
export async function resolveGiteaRunId(
token: string,
owner: string,
repo: string,
): Promise<string | undefined> {
if (!USE_GITEA_API) {
return undefined;
}
const runNumber = process.env.GITEA_RUN_NUMBER;
if (!runNumber) {
console.log("GITEA_RUN_NUMBER not set, cannot resolve Gitea run ID");
return undefined;
}
try {
// Call Gitea API to get action runs
// GET /api/v1/repos/{owner}/{repo}/actions/runs
const url = `${GITHUB_API_URL}/repos/${owner}/${repo}/actions/runs`;
const response = await fetch(url, {
headers: {
Authorization: `token ${token}`,
Accept: "application/json",
},
});
if (!response.ok) {
console.error(
`Failed to fetch Gitea action runs: ${response.status} ${response.statusText}`,
);
return undefined;
}
const data = (await response.json()) as {
workflow_runs?: Array<{ id: number; run_number: number }>;
};
// Find the run with matching run_number
const runs = data.workflow_runs || [];
const targetRunNumber = parseInt(runNumber, 10);
const matchingRun = runs.find((run) => run.run_number === targetRunNumber);
if (matchingRun) {
console.log(
`Resolved Gitea run ID: ${matchingRun.id} from run number: ${runNumber}`,
);
return String(matchingRun.id);
}
console.log(
`Could not find Gitea run with run_number: ${runNumber} in ${runs.length} runs`,
);
return undefined;
} catch (error) {
console.error("Error resolving Gitea run ID:", error);
return undefined;
}
}
/**
* Sets up the GITHUB_RUN_ID environment variable for Gitea.
* This should be called early in the action execution.
*/
export async function setupGiteaRunId(
token: string,
owner: string,
repo: string,
): Promise<void> {
// Only proceed if we're in Gitea mode
if (!USE_GITEA_API) {
return;
}
const runId = await resolveGiteaRunId(token, owner, repo);
if (runId) {
process.env.GITHUB_RUN_ID = runId;
console.log(`Set GITHUB_RUN_ID to ${runId} from Gitea API`);
}
}

View File

@ -0,0 +1,292 @@
import type { Octokit } from "@octokit/rest";
// Type definitions for REST API responses
type RestFile = Awaited<
ReturnType<Octokit["rest"]["pulls"]["listFiles"]>
>["data"][number];
type RestComment = Awaited<
ReturnType<Octokit["rest"]["issues"]["listComments"]>
>["data"][number];
type RestCommit = Awaited<
ReturnType<Octokit["rest"]["pulls"]["listCommits"]>
>["data"][number];
type RestReview = Awaited<
ReturnType<Octokit["rest"]["pulls"]["listReviews"]>
>["data"][number];
type RestReviewComment = Awaited<
ReturnType<Octokit["rest"]["pulls"]["listReviewComments"]>
>["data"][number];
/**
* Fetch complete Pull Request data including commits, files, comments, and reviews
*/
export async function fetchPullRequest(
octokit: Octokit,
owner: string,
repo: string,
number: number,
) {
// Fetch all PR data in parallel for better performance
const [prData, prFiles, prComments, prCommits, prReviews] = await Promise.all(
[
// Basic PR information
octokit.rest.pulls.get({
owner,
repo,
pull_number: number,
}),
// Changed files
octokit.rest.pulls.listFiles({
owner,
repo,
pull_number: number,
per_page: 100,
}),
// Issue comments (PR general comments)
octokit.rest.issues.listComments({
owner,
repo,
issue_number: number,
per_page: 100,
}),
// PR commits
octokit.rest.pulls.listCommits({
owner,
repo,
pull_number: number,
per_page: 100,
}),
// PR reviews
octokit.rest.pulls.listReviews({
owner,
repo,
pull_number: number,
per_page: 100,
}),
],
);
// Fetch review comments for each review using Gitea API
// Gitea endpoint: GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments
const reviewsWithComments = await Promise.all(
prReviews.data.map(async (review: RestReview) => {
try {
// Use Gitea-specific endpoint to get comments for each review
const response = await octokit.request(
"GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments",
{
owner,
repo,
pull_number: number,
review_id: review.id,
per_page: 100,
},
);
return {
...review,
comments: response.data,
};
} catch (error) {
// If fetching comments fails, return review with empty comments
// @ts-expect-error - console is available at runtime
console.warn(
`Failed to fetch comments for review ${review.id}:`,
error,
);
return {
...review,
comments: [],
};
}
}),
);
// Transform REST API response to match GraphQL-like structure
return {
repository: {
pullRequest: {
title: prData.data.title,
body: prData.data.body || "",
author: {
login: prData.data.user?.login || "",
name: prData.data.user?.name || undefined,
},
baseRefName: prData.data.base.ref,
headRefName: prData.data.head.ref,
headRefOid: prData.data.head.sha,
createdAt: prData.data.created_at,
updatedAt: prData.data.updated_at,
lastEditedAt: prData.data.updated_at, // Gitea may not have separate lastEditedAt
additions: prData.data.additions || 0,
deletions: prData.data.deletions || 0,
state: prData.data.state.toUpperCase(), // "open" -> "OPEN"
commits: {
totalCount: prCommits.data.length,
nodes: prCommits.data.map((commit: RestCommit) => ({
commit: {
oid: commit.sha,
message: commit.commit.message,
author: {
name: commit.commit.author?.name || "",
email: commit.commit.author?.email || "",
},
},
})),
},
files: {
nodes: prFiles.data.map((file: RestFile) => ({
path: file.filename,
additions: file.additions,
deletions: file.deletions,
changeType: mapFileStatus(file.status),
})),
},
comments: {
nodes: prComments.data.map((comment: RestComment) => ({
id: `comment_${comment.id}`,
databaseId: comment.id,
body: comment.body || "",
author: {
login: comment.user?.login || "",
},
createdAt: comment.created_at,
updatedAt: comment.updated_at,
lastEditedAt: comment.updated_at,
isMinimized: false, // Gitea may not support this
})),
},
reviews: {
nodes: reviewsWithComments.map(
(review: RestReview & { comments: RestReviewComment[] }) => ({
id: `review_${review.id}`,
databaseId: review.id,
author: {
login: review.user?.login || "",
},
body: review.body || "",
state: review.state.toUpperCase(), // "APPROVED", "CHANGES_REQUESTED", etc.
submittedAt: review.submitted_at || review.created_at || "",
updatedAt: review.submitted_at || review.created_at || "",
lastEditedAt: review.submitted_at || review.created_at || "",
comments: {
nodes: review.comments.map((comment: RestReviewComment) => ({
id: `review_comment_${comment.id}`,
databaseId: comment.id,
body: comment.body || "",
path: comment.path,
line: comment.line || comment.original_line || null,
author: {
login: comment.user?.login || "",
},
createdAt: comment.created_at,
updatedAt: comment.updated_at,
lastEditedAt: comment.updated_at,
isMinimized: false,
})),
},
}),
),
},
},
},
};
}
/**
* Fetch complete Issue data including comments
*/
export async function fetchIssue(
octokit: Octokit,
owner: string,
repo: string,
number: number,
) {
// Fetch issue data and comments in parallel
const [issueData, issueComments] = await Promise.all([
octokit.rest.issues.get({
owner,
repo,
issue_number: number,
}),
octokit.rest.issues.listComments({
owner,
repo,
issue_number: number,
per_page: 100,
}),
]);
// Transform REST API response to match GraphQL-like structure
return {
repository: {
issue: {
title: issueData.data.title,
body: issueData.data.body || "",
author: {
login: issueData.data.user?.login || "",
},
createdAt: issueData.data.created_at,
updatedAt: issueData.data.updated_at,
lastEditedAt: issueData.data.updated_at,
state: issueData.data.state.toUpperCase(),
comments: {
nodes: issueComments.data.map((comment: RestComment) => ({
id: `comment_${comment.id}`,
databaseId: comment.id,
body: comment.body || "",
author: {
login: comment.user?.login || "",
},
createdAt: comment.created_at,
updatedAt: comment.updated_at,
lastEditedAt: comment.updated_at,
isMinimized: false,
})),
},
},
},
};
}
/**
* Fetch user display name
*/
export async function fetchUser(octokit: Octokit, login: string) {
try {
const userData = await octokit.rest.users.getByUsername({
username: login,
});
return {
user: {
name: userData.data.name || userData.data.full_name || null,
},
};
} catch (error) {
// Note: console is available at runtime in Node.js environment
// @ts-expect-error - console is not in lib but available at runtime
console.warn(`Failed to fetch user ${login}:`, error);
return {
user: {
name: null,
},
};
}
}
/**
* Map Gitea file status to GraphQL changeType format
*/
function mapFileStatus(status: string): string {
const statusMap: Record<string, string> = {
added: "ADDED",
modified: "MODIFIED",
removed: "DELETED",
renamed: "RENAMED",
copied: "COPIED",
changed: "MODIFIED",
};
return statusMap[status] || status.toUpperCase();
}

View File

@ -1,6 +1,8 @@
import { execFileSync } from "child_process";
import type { Octokits } from "../api/client";
import { USE_GITEA_API } from "../api/config";
import { ISSUE_QUERY, PR_QUERY, USER_QUERY } from "../api/queries/github";
import { fetchPullRequest, fetchIssue, fetchUser } from "../api/queries/gitea";
import {
isIssueCommentEvent,
isIssuesEvent,
@ -249,12 +251,14 @@ export async function fetchGitHubData({
try {
if (isPR) {
// Fetch PR data with all comments and file information
const prResult = await octokits.graphql<PullRequestQueryResponse>(
PR_QUERY,
{
owner,
repo,
number: parseInt(prNumber),
const prResult = USE_GITEA_API
? await fetchPullRequest(octokits.rest, owner, repo, parseInt(prNumber))
: await octokits.graphql<PullRequestQueryResponse>(
PR_QUERY,
{
owner,
repo,
number: parseInt(prNumber),
},
);
@ -270,7 +274,7 @@ export async function fetchGitHubData({
includeCommentsByActor,
excludeCommentsByActor,
);
reviewData = pullRequest.reviews || [];
reviewData = pullRequest.reviews || (USE_GITEA_API ? null : [])
console.log(`Successfully fetched PR #${prNumber} data`);
} else {
@ -278,12 +282,14 @@ export async function fetchGitHubData({
}
} else {
// Fetch issue data
const issueResult = await octokits.graphql<IssueQueryResponse>(
ISSUE_QUERY,
{
owner,
repo,
number: parseInt(prNumber),
const issueResult = USE_GITEA_API
? await fetchIssue(octokits.rest, owner, repo, parseInt(prNumber))
: await octokits.graphql<IssueQueryResponse>(
ISSUE_QUERY,
{
owner,
repo,
number: parseInt(prNumber),
},
);
@ -475,9 +481,11 @@ export async function fetchUserDisplayName(
login: string,
): Promise<string | null> {
try {
const result = await octokits.graphql<UserQueryResponse>(USER_QUERY, {
login,
});
const result = USE_GITEA_API
? await fetchUser(octokits.rest, login)
: await octokits.graphql<UserQueryResponse>(USER_QUERY, {
login,
});
return result.user.name;
} catch (error) {
console.warn(`Failed to fetch user display name for ${login}:`, error);

View File

@ -1,5 +1,6 @@
import type { Octokits } from "../api/client";
import { GITHUB_SERVER_URL } from "../api/config";
import { GITHUB_SERVER_URL, USE_GITEA_API } from "../api/config";
import { createBranchUrl } from "./comments/common";
import { $ } from "bun";
export async function checkAndCommitOrDeleteBranch(
@ -80,7 +81,7 @@ export async function checkAndCommitOrDeleteBranch(
);
// Set branch link since we now have commits
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
const branchUrl = createBranchUrl(owner, repo, claudeBranch);
branchLink = `\n[View branch](${branchUrl})`;
} else {
console.log(
@ -91,7 +92,7 @@ export async function checkAndCommitOrDeleteBranch(
} catch (gitError) {
console.error("Error checking/committing changes:", gitError);
// If we can't check git status, assume the branch might have changes
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
const branchUrl = createBranchUrl(owner, repo, claudeBranch);
branchLink = `\n[View branch](${branchUrl})`;
}
} else {
@ -102,13 +103,13 @@ export async function checkAndCommitOrDeleteBranch(
}
} else {
// Only add branch link if there are commits
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
const branchUrl = createBranchUrl(owner, repo, claudeBranch);
branchLink = `\n[View branch](${branchUrl})`;
}
} catch (error) {
console.error("Error comparing commits on Claude branch:", error);
// If we can't compare but the branch exists remotely, include the branch link
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
const branchUrl = createBranchUrl(owner, repo, claudeBranch);
branchLink = `\n[View branch](${branchUrl})`;
}
}

View File

@ -1,4 +1,5 @@
import { GITHUB_SERVER_URL } from "../api/config";
import { GITHUB_SERVER_URL, USE_GITEA_API } from "../api/config";
import { createBranchUrl } from "./comments/common";
export type ExecutionDetails = {
total_cost_usd?: number;
@ -157,10 +158,12 @@ export function updateCommentBody(input: CommentUpdateInput): string {
// If we don't have a URL yet but have a branch name, construct it
if (!branchUrl && finalBranchName) {
// Extract owner/repo from jobUrl
const repoMatch = jobUrl.match(/github\.com\/([^\/]+)\/([^\/]+)\//);
// Extract owner/repo from jobUrl (works for both GitHub and Gitea URLs)
const repoMatch = USE_GITEA_API
? jobUrl.match(/\/([^\/]+)\/([^\/]+)\/(?:actions|tree|src)/)
: jobUrl.match(/github\.com\/([^\/]+)\/([^\/]+)\//);
if (repoMatch) {
branchUrl = `${GITHUB_SERVER_URL}/${repoMatch[1]}/${repoMatch[2]}/tree/${finalBranchName}`;
branchUrl = createBranchUrl(repoMatch[1], repoMatch[2], finalBranchName);
}
}

View File

@ -1,4 +1,4 @@
import { GITHUB_SERVER_URL } from "../../api/config";
import { GITHUB_SERVER_URL, USE_GITEA_API } from "../../api/config";
export const SPINNER_HTML =
'<img src="https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />';
@ -17,10 +17,23 @@ export function createBranchLink(
repo: string,
branchName: string,
): string {
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${branchName}`;
const branchUrl = createBranchUrl(owner, repo, branchName);
return `\n[View branch](${branchUrl})`;
}
/**
* Create a branch URL for the current platform
*/
export function createBranchUrl(
owner: string,
repo: string,
branchName: string,
): string {
return USE_GITEA_API
? `${GITHUB_SERVER_URL}/${owner}/${repo}/src/branch/${branchName}`
: `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${branchName}`;
}
export function createCommentBody(
jobRunLink: string,
branchLink: string = "",

View File

@ -7,17 +7,25 @@
import type { Octokit } from "@octokit/rest";
import type { GitHubContext } from "../context";
import { USE_GITEA_API } from "../api/config.ts";
export async function checkHumanActor(
octokit: Octokit,
githubContext: GitHubContext,
) {
// Fetch user information from GitHub API
const { data: userData } = await octokit.users.getByUsername({
username: githubContext.actor,
});
const actorType = userData.type;
let actorType: string
if (USE_GITEA_API) {
actorType = "Bot"
console.log("Gitea does not provide an API to determine whether a user is a bot. Assuming the user is a bot and verify whether it is listed in allowedBots.")
}else{
// Fetch user information from GitHub API
const { data: userData } = await octokit.users.getByUsername({
username: githubContext.actor,
});
actorType = userData.type;
}
console.log(`Actor type: ${actorType}`);

View File

@ -59,7 +59,7 @@ export async function checkWritePermissions(
const permissionLevel = response.data.permission;
core.info(`Permission level retrieved: ${permissionLevel}`);
if (permissionLevel === "admin" || permissionLevel === "write") {
if (permissionLevel === "admin" || permissionLevel === "write" || permissionLevel === "owner") {
core.info(`Actor has write access: ${permissionLevel}`);
return true;
} else {

View File

@ -65,6 +65,7 @@ export function detectMode(context: GitHubContext): AutoDetectedMode {
const supportedActions = [
"opened",
"synchronize",
"synchronized",
"ready_for_review",
"reopened",
];
@ -112,6 +113,7 @@ function validateTrackProgressEvent(context: GitHubContext): void {
const validActions = [
"opened",
"synchronize",
"synchronized",
"ready_for_review",
"reopened",
];