mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(tools): added 48 new github tools, 12 triggers (#1821)
* feat(tools): added 10 new github triggers * feat(tools): added 48 new github tools, 12 triggers * fix(logging): make logging safe start an upsert to prevent insertions of duplicate execution id records, remove layout from github block
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -133,16 +133,6 @@ async function executeWebhookJobInternal(
|
||||
const loggingSession = new LoggingSession(payload.workflowId, executionId, 'webhook', requestId)
|
||||
|
||||
try {
|
||||
await loggingSession.safeStart({
|
||||
userId: payload.userId,
|
||||
workspaceId: '', // Will be resolved below
|
||||
variables: {},
|
||||
triggerData: {
|
||||
isTest: payload.testMode === true,
|
||||
executionTarget: payload.executionTarget || 'deployed',
|
||||
},
|
||||
})
|
||||
|
||||
const workflowData =
|
||||
payload.executionTarget === 'live'
|
||||
? await loadWorkflowFromNormalizedTables(payload.workflowId)
|
||||
@@ -478,6 +468,17 @@ async function executeWebhookJobInternal(
|
||||
})
|
||||
|
||||
try {
|
||||
// Ensure logging session is started (safe to call multiple times)
|
||||
await loggingSession.safeStart({
|
||||
userId: payload.userId,
|
||||
workspaceId: '', // May not be available for early errors
|
||||
variables: {},
|
||||
triggerData: {
|
||||
isTest: payload.testMode === true,
|
||||
executionTarget: payload.executionTarget || 'deployed',
|
||||
},
|
||||
})
|
||||
|
||||
const executionResult = (error?.executionResult as ExecutionResult | undefined) || {
|
||||
success: false,
|
||||
output: {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,39 @@ export class ExecutionLogger implements IExecutionLoggerService {
|
||||
|
||||
logger.debug(`Starting workflow execution ${executionId} for workflow ${workflowId}`)
|
||||
|
||||
// Check if execution log already exists (idempotency check)
|
||||
const existingLog = await db
|
||||
.select()
|
||||
.from(workflowExecutionLogs)
|
||||
.where(eq(workflowExecutionLogs.executionId, executionId))
|
||||
.limit(1)
|
||||
|
||||
if (existingLog.length > 0) {
|
||||
logger.debug(
|
||||
`Execution log already exists for ${executionId}, skipping duplicate INSERT (idempotent)`
|
||||
)
|
||||
const snapshot = await snapshotService.getSnapshot(existingLog[0].stateSnapshotId)
|
||||
if (!snapshot) {
|
||||
throw new Error(`Snapshot ${existingLog[0].stateSnapshotId} not found for existing log`)
|
||||
}
|
||||
return {
|
||||
workflowLog: {
|
||||
id: existingLog[0].id,
|
||||
workflowId: existingLog[0].workflowId,
|
||||
executionId: existingLog[0].executionId,
|
||||
stateSnapshotId: existingLog[0].stateSnapshotId,
|
||||
level: existingLog[0].level as 'info' | 'error',
|
||||
trigger: existingLog[0].trigger as ExecutionTrigger['type'],
|
||||
startedAt: existingLog[0].startedAt.toISOString(),
|
||||
endedAt: existingLog[0].endedAt?.toISOString() || existingLog[0].startedAt.toISOString(),
|
||||
totalDurationMs: existingLog[0].totalDurationMs || 0,
|
||||
executionData: existingLog[0].executionData as WorkflowExecutionLog['executionData'],
|
||||
createdAt: existingLog[0].createdAt.toISOString(),
|
||||
},
|
||||
snapshot,
|
||||
}
|
||||
}
|
||||
|
||||
const snapshotResult = await snapshotService.createSnapshotWithDeduplication(
|
||||
workflowId,
|
||||
workflowState
|
||||
|
||||
@@ -590,6 +590,37 @@ export async function queueWebhookExecution(
|
||||
return NextResponse.json({ error: 'Workspace billing account required' }, { status: 402 })
|
||||
}
|
||||
|
||||
// GitHub event filtering for event-specific triggers
|
||||
if (foundWebhook.provider === 'github') {
|
||||
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
|
||||
if (triggerId && triggerId !== 'github_webhook') {
|
||||
const eventType = request.headers.get('x-github-event')
|
||||
const action = body.action
|
||||
|
||||
const { isGitHubEventMatch } = await import('@/triggers/github/utils')
|
||||
|
||||
if (!isGitHubEventMatch(triggerId, eventType || '', action, body)) {
|
||||
logger.debug(
|
||||
`[${options.requestId}] GitHub event mismatch for trigger ${triggerId}. Event: ${eventType}, Action: ${action}. Skipping execution.`,
|
||||
{
|
||||
webhookId: foundWebhook.id,
|
||||
workflowId: foundWorkflow.id,
|
||||
triggerId,
|
||||
receivedEvent: eventType,
|
||||
receivedAction: action,
|
||||
}
|
||||
)
|
||||
|
||||
// Return 200 OK to prevent GitHub from retrying
|
||||
return NextResponse.json({
|
||||
message: 'Event type does not match trigger configuration. Ignoring.',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const headers = Object.fromEntries(request.headers.entries())
|
||||
|
||||
// For Microsoft Teams Graph notifications, extract unique identifiers for idempotency
|
||||
|
||||
108
apps/sim/tools/github/add_assignees.ts
Normal file
108
apps/sim/tools/github/add_assignees.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { AddAssigneesParams, IssueResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const addAssigneesTool: ToolConfig<AddAssigneesParams, IssueResponse> = {
|
||||
id: 'github_add_assignees',
|
||||
name: 'GitHub Add Assignees',
|
||||
description: 'Add assignees to an issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
assignees: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of usernames to assign to the issue',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/assignees`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const assigneesArray = params.assignees
|
||||
.split(',')
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a)
|
||||
return {
|
||||
assignees: assigneesArray,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issue = await response.json()
|
||||
const labels = issue.labels?.map((label: any) => label.name) || []
|
||||
const assignees = issue.assignees?.map((assignee: any) => assignee.login) || []
|
||||
const content = `Assignees added to issue #${issue.number}: "${issue.title}"
|
||||
All assignees: ${assignees.join(', ')}
|
||||
URL: ${issue.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels,
|
||||
assignees,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
body: issue.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable assignees confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated issue metadata with assignees',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'All assignees on the issue' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
body: { type: 'string', description: 'Issue body/description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
96
apps/sim/tools/github/add_labels.ts
Normal file
96
apps/sim/tools/github/add_labels.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { AddLabelsParams, LabelsResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const addLabelsTool: ToolConfig<AddLabelsParams, LabelsResponse> = {
|
||||
id: 'github_add_labels',
|
||||
name: 'GitHub Add Labels',
|
||||
description: 'Add labels to an issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names to add to the issue',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/labels`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const labelsArray = params.labels
|
||||
.split(',')
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l)
|
||||
return {
|
||||
labels: labelsArray,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const labelsData = await response.json()
|
||||
|
||||
const labels = labelsData.map((label: any) => label.name)
|
||||
|
||||
const content = `Labels added to issue successfully!
|
||||
All labels on issue: ${labels.join(', ')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
labels,
|
||||
issue_number: 0, // Will be filled from params in actual implementation
|
||||
html_url: '', // Will be constructed from params
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable labels confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Labels metadata',
|
||||
properties: {
|
||||
labels: { type: 'array', description: 'All labels currently on the issue' },
|
||||
issue_number: { type: 'number', description: 'Issue number' },
|
||||
html_url: { type: 'string', description: 'GitHub issue URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
124
apps/sim/tools/github/cancel_workflow_run.ts
Normal file
124
apps/sim/tools/github/cancel_workflow_run.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { CancelWorkflowRunParams, CancelWorkflowRunResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const cancelWorkflowRunTool: ToolConfig<CancelWorkflowRunParams, CancelWorkflowRunResponse> =
|
||||
{
|
||||
id: 'github_cancel_workflow_run',
|
||||
name: 'GitHub Cancel Workflow Run',
|
||||
description:
|
||||
'Cancel a workflow run. Returns 202 Accepted if cancellation is initiated, or 409 Conflict if the run cannot be cancelled (already completed).',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
run_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Workflow run ID to cancel',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/runs/${params.run_id}/cancel`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!params) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Missing parameters',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
run_id: 0,
|
||||
status: 'error',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status === 202) {
|
||||
const content = `Workflow run #${params.run_id} cancellation initiated successfully.
|
||||
The run will be cancelled shortly.`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
run_id: params.run_id,
|
||||
status: 'cancellation_initiated',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if (response.status === 409) {
|
||||
const content = `Cannot cancel workflow run #${params.run_id}.
|
||||
The run may have already completed or been cancelled.`
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
run_id: params.run_id,
|
||||
status: 'cannot_cancel',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const content = `Workflow run #${params.run_id} cancellation request processed.`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
run_id: params.run_id,
|
||||
status: 'processed',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Cancellation status message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Cancellation metadata',
|
||||
properties: {
|
||||
run_id: { type: 'number', description: 'Workflow run ID' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Cancellation status (cancellation_initiated, cannot_cancel, processed)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
115
apps/sim/tools/github/close_issue.ts
Normal file
115
apps/sim/tools/github/close_issue.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { CloseIssueParams, IssueResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const closeIssueTool: ToolConfig<CloseIssueParams, IssueResponse> = {
|
||||
id: 'github_close_issue',
|
||||
name: 'GitHub Close Issue',
|
||||
description: 'Close an issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
state_reason: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reason for closing: completed or not_planned',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
state: 'closed',
|
||||
}
|
||||
if (params.state_reason) {
|
||||
body.state_reason = params.state_reason
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
const labels = issue.labels?.map((label: any) => label.name) || []
|
||||
|
||||
const assignees = issue.assignees?.map((assignee: any) => assignee.login) || []
|
||||
|
||||
const content = `Issue #${issue.number} closed: "${issue.title}"
|
||||
State: ${issue.state}
|
||||
${issue.state_reason ? `Reason: ${issue.state_reason}` : ''}
|
||||
Closed at: ${issue.closed_at}
|
||||
URL: ${issue.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels,
|
||||
assignees,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
closed_at: issue.closed_at,
|
||||
body: issue.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable issue close confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Closed issue metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state (closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'Array of assignee usernames' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
closed_at: { type: 'string', description: 'Closed timestamp' },
|
||||
body: { type: 'string', description: 'Issue body/description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
92
apps/sim/tools/github/close_pr.ts
Normal file
92
apps/sim/tools/github/close_pr.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { ClosePRParams, PRResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const closePRTool: ToolConfig<ClosePRParams, PRResponse> = {
|
||||
id: 'github_close_pr',
|
||||
name: 'GitHub Close Pull Request',
|
||||
description: 'Close a pull request in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: () => ({
|
||||
state: 'closed',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const pr = await response.json()
|
||||
|
||||
const content = `PR #${pr.number} closed: "${pr.title}"
|
||||
URL: ${pr.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
state: pr.state,
|
||||
html_url: pr.html_url,
|
||||
merged: pr.merged,
|
||||
draft: pr.draft,
|
||||
created_at: pr.created_at,
|
||||
updated_at: pr.updated_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable PR close confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Closed pull request metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Pull request number' },
|
||||
title: { type: 'string', description: 'PR title' },
|
||||
state: { type: 'string', description: 'PR state (should be closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
merged: { type: 'boolean', description: 'Whether PR is merged' },
|
||||
draft: { type: 'boolean', description: 'Whether PR is draft' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
93
apps/sim/tools/github/create_branch.ts
Normal file
93
apps/sim/tools/github/create_branch.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import type { CreateBranchParams, RefResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createBranchTool: ToolConfig<CreateBranchParams, RefResponse> = {
|
||||
id: 'github_create_branch',
|
||||
name: 'GitHub Create Branch',
|
||||
description:
|
||||
'Create a new branch in a GitHub repository by creating a git reference pointing to a specific commit SHA.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the branch to create',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Commit SHA to point the branch to',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/git/refs`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
ref: `refs/heads/${params.branch}`,
|
||||
sha: params.sha,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const ref = await response.json()
|
||||
|
||||
// Create a human-readable content string
|
||||
const content = `Branch created successfully:
|
||||
Branch: ${ref.ref.replace('refs/heads/', '')}
|
||||
SHA: ${ref.object.sha}
|
||||
URL: ${ref.url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
ref: ref.ref,
|
||||
url: ref.url,
|
||||
sha: ref.object.sha,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable branch creation confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Git reference metadata',
|
||||
properties: {
|
||||
ref: { type: 'string', description: 'Full reference name (refs/heads/branch)' },
|
||||
url: { type: 'string', description: 'API URL for the reference' },
|
||||
sha: { type: 'string', description: 'Commit SHA the branch points to' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
172
apps/sim/tools/github/create_file.ts
Normal file
172
apps/sim/tools/github/create_file.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import type { CreateFileParams, FileOperationResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createFileTool: ToolConfig<CreateFileParams, FileOperationResponse> = {
|
||||
id: 'github_create_file',
|
||||
name: 'GitHub Create File',
|
||||
description:
|
||||
'Create a new file in a GitHub repository. The file content will be automatically Base64 encoded. Supports files up to 1MB.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Path where the file will be created (e.g., "src/newfile.ts")',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Commit message for this file creation',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'File content (plain text, will be Base64 encoded automatically)',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch to create the file in (defaults to repository default branch)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/contents/${params.path}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const base64Content = Buffer.from(params.content).toString('base64')
|
||||
|
||||
const body: Record<string, any> = {
|
||||
message: params.message,
|
||||
content: base64Content,
|
||||
}
|
||||
|
||||
if (params.branch) {
|
||||
body.branch = params.branch
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `File created successfully!
|
||||
|
||||
Path: ${data.content.path}
|
||||
Name: ${data.content.name}
|
||||
Size: ${data.content.size} bytes
|
||||
SHA: ${data.content.sha}
|
||||
|
||||
Commit:
|
||||
- SHA: ${data.commit.sha}
|
||||
- Message: ${data.commit.message}
|
||||
- Author: ${data.commit.author.name}
|
||||
- Date: ${data.commit.author.date}
|
||||
|
||||
View file: ${data.content.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
file: {
|
||||
name: data.content.name,
|
||||
path: data.content.path,
|
||||
sha: data.content.sha,
|
||||
size: data.content.size,
|
||||
type: data.content.type,
|
||||
download_url: data.content.download_url,
|
||||
html_url: data.content.html_url,
|
||||
},
|
||||
commit: {
|
||||
sha: data.commit.sha,
|
||||
message: data.commit.message,
|
||||
author: {
|
||||
name: data.commit.author.name,
|
||||
email: data.commit.author.email,
|
||||
date: data.commit.author.date,
|
||||
},
|
||||
committer: {
|
||||
name: data.commit.committer.name,
|
||||
email: data.commit.committer.email,
|
||||
date: data.commit.committer.date,
|
||||
},
|
||||
html_url: data.commit.html_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable file creation confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'File and commit metadata',
|
||||
properties: {
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'Created file information',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
path: { type: 'string', description: 'Full path in repository' },
|
||||
sha: { type: 'string', description: 'Git blob SHA' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
type: { type: 'string', description: 'Content type' },
|
||||
download_url: { type: 'string', description: 'Direct download URL' },
|
||||
html_url: { type: 'string', description: 'GitHub web UI URL' },
|
||||
},
|
||||
},
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit information',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Author information',
|
||||
},
|
||||
committer: {
|
||||
type: 'object',
|
||||
description: 'Committer information',
|
||||
},
|
||||
html_url: { type: 'string', description: 'Commit URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
143
apps/sim/tools/github/create_issue.ts
Normal file
143
apps/sim/tools/github/create_issue.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import type { CreateIssueParams, IssueResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createIssueTool: ToolConfig<CreateIssueParams, IssueResponse> = {
|
||||
id: 'github_create_issue',
|
||||
name: 'GitHub Create Issue',
|
||||
description: 'Create a new issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue description/body',
|
||||
},
|
||||
assignees: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of usernames to assign to this issue',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names to add to this issue',
|
||||
},
|
||||
milestone: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone number to associate with this issue',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/issues`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
title: params.title,
|
||||
}
|
||||
if (params.body) body.body = params.body
|
||||
if (params.assignees) {
|
||||
const assigneesArray = params.assignees
|
||||
.split(',')
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a)
|
||||
if (assigneesArray.length > 0) body.assignees = assigneesArray
|
||||
}
|
||||
if (params.labels) {
|
||||
const labelsArray = params.labels
|
||||
.split(',')
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l)
|
||||
if (labelsArray.length > 0) body.labels = labelsArray
|
||||
}
|
||||
if (params.milestone) body.milestone = params.milestone
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
const labels = issue.labels?.map((label: any) => label.name) || []
|
||||
|
||||
const assignees = issue.assignees?.map((assignee: any) => assignee.login) || []
|
||||
|
||||
const content = `Issue #${issue.number} created: "${issue.title}"
|
||||
State: ${issue.state}
|
||||
URL: ${issue.html_url}
|
||||
${labels.length > 0 ? `Labels: ${labels.join(', ')}` : ''}
|
||||
${assignees.length > 0 ? `Assignees: ${assignees.join(', ')}` : ''}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels,
|
||||
assignees,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
body: issue.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable issue creation confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Issue metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'Array of assignee usernames' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
body: { type: 'string', description: 'Issue body/description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
120
apps/sim/tools/github/create_pr.ts
Normal file
120
apps/sim/tools/github/create_pr.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { CreatePRParams, PRResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createPRTool: ToolConfig<CreatePRParams, PRResponse> = {
|
||||
id: 'github_create_pr',
|
||||
name: 'GitHub Create Pull Request',
|
||||
description: 'Create a new pull request in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
head: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the branch where your changes are implemented',
|
||||
},
|
||||
base: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the branch you want the changes pulled into',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request description (Markdown)',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Create as draft pull request',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/pulls`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
title: params.title,
|
||||
head: params.head,
|
||||
base: params.base,
|
||||
body: params.body,
|
||||
draft: params.draft,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const pr = await response.json()
|
||||
|
||||
const content = `PR #${pr.number} created: "${pr.title}" (${pr.state}${pr.draft ? ', draft' : ''})
|
||||
From: ${pr.head.ref} → To: ${pr.base.ref}
|
||||
URL: ${pr.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
state: pr.state,
|
||||
html_url: pr.html_url,
|
||||
merged: pr.merged,
|
||||
draft: pr.draft,
|
||||
created_at: pr.created_at,
|
||||
updated_at: pr.updated_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable PR creation confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pull request metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Pull request number' },
|
||||
title: { type: 'string', description: 'PR title' },
|
||||
state: { type: 'string', description: 'PR state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
merged: { type: 'boolean', description: 'Whether PR is merged' },
|
||||
draft: { type: 'boolean', description: 'Whether PR is draft' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
148
apps/sim/tools/github/create_project.ts
Normal file
148
apps/sim/tools/github/create_project.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import type { CreateProjectParams, ProjectResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createProjectTool: ToolConfig<CreateProjectParams, ProjectResponse> = {
|
||||
id: 'github_create_project',
|
||||
name: 'GitHub Create Project',
|
||||
description:
|
||||
'Create a new GitHub Project V2. Requires the owner Node ID (not login name). Returns the created project with ID, title, and URL.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Owner Node ID (format: PVT_... or MDQ6...). Use GitHub GraphQL API to get this ID from organization or user login.',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project title',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with project write permissions',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.github.com/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const query = `
|
||||
mutation($ownerId: ID!, $title: String!) {
|
||||
createProjectV2(input: {
|
||||
ownerId: $ownerId
|
||||
title: $title
|
||||
}) {
|
||||
projectV2 {
|
||||
id
|
||||
title
|
||||
number
|
||||
url
|
||||
closed
|
||||
public
|
||||
shortDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
return {
|
||||
query,
|
||||
variables: {
|
||||
ownerId: params.owner_id,
|
||||
title: params.title,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: `GraphQL Error: ${data.errors[0].message}`,
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: data.errors[0].message,
|
||||
}
|
||||
}
|
||||
|
||||
const project = data.data?.createProjectV2?.projectV2
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: 'Failed to create project',
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: 'Failed to create project',
|
||||
}
|
||||
}
|
||||
|
||||
let content = `Project created successfully!\n`
|
||||
content += `Title: ${project.title}\n`
|
||||
content += `ID: ${project.id}\n`
|
||||
content += `Number: ${project.number}\n`
|
||||
content += `URL: ${project.url}\n`
|
||||
content += `Status: ${project.closed ? 'Closed' : 'Open'}\n`
|
||||
content += `Visibility: ${project.public ? 'Public' : 'Private'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: project.id,
|
||||
title: project.title,
|
||||
number: project.number,
|
||||
url: project.url,
|
||||
closed: project.closed,
|
||||
public: project.public,
|
||||
shortDescription: project.shortDescription || '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable confirmation message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Created project metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project node ID' },
|
||||
title: { type: 'string', description: 'Project title' },
|
||||
number: { type: 'number', description: 'Project number', optional: true },
|
||||
url: { type: 'string', description: 'Project URL' },
|
||||
closed: { type: 'boolean', description: 'Whether project is closed', optional: true },
|
||||
public: { type: 'boolean', description: 'Whether project is public', optional: true },
|
||||
shortDescription: {
|
||||
type: 'string',
|
||||
description: 'Project short description',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
157
apps/sim/tools/github/create_release.ts
Normal file
157
apps/sim/tools/github/create_release.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import type { CreateReleaseParams, ReleaseResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createReleaseTool: ToolConfig<CreateReleaseParams, ReleaseResponse> = {
|
||||
id: 'github_create_release',
|
||||
name: 'GitHub Create Release',
|
||||
description:
|
||||
'Create a new release for a GitHub repository. Specify tag name, target commit, title, description, and whether it should be a draft or prerelease.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
tag_name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the tag for this release',
|
||||
},
|
||||
target_commitish: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Specifies the commitish value that determines where the Git tag is created from. Can be any branch or commit SHA. Defaults to the repository default branch.',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the release',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Text describing the contents of the release (markdown supported)',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'true to create a draft (unpublished) release, false to create a published one',
|
||||
default: false,
|
||||
},
|
||||
prerelease: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'true to identify the release as a prerelease, false to identify as a full release',
|
||||
default: false,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/releases`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
tag_name: params.tag_name,
|
||||
}
|
||||
|
||||
if (params.target_commitish) {
|
||||
body.target_commitish = params.target_commitish
|
||||
}
|
||||
if (params.name) {
|
||||
body.name = params.name
|
||||
}
|
||||
if (params.body) {
|
||||
body.body = params.body
|
||||
}
|
||||
if (params.draft !== undefined) {
|
||||
body.draft = params.draft
|
||||
}
|
||||
if (params.prerelease !== undefined) {
|
||||
body.prerelease = params.prerelease
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const releaseType = data.draft ? 'Draft' : data.prerelease ? 'Prerelease' : 'Release'
|
||||
const content = `${releaseType} created: "${data.name || data.tag_name}"
|
||||
Tag: ${data.tag_name}
|
||||
URL: ${data.html_url}
|
||||
Created: ${data.created_at}
|
||||
${data.published_at ? `Published: ${data.published_at}` : 'Not yet published'}
|
||||
Download URLs:
|
||||
- Tarball: ${data.tarball_url}
|
||||
- Zipball: ${data.zipball_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
tag_name: data.tag_name,
|
||||
name: data.name || data.tag_name,
|
||||
html_url: data.html_url,
|
||||
tarball_url: data.tarball_url,
|
||||
zipball_url: data.zipball_url,
|
||||
draft: data.draft,
|
||||
prerelease: data.prerelease,
|
||||
created_at: data.created_at,
|
||||
published_at: data.published_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable release creation summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Release metadata including download URLs',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Release ID' },
|
||||
tag_name: { type: 'string', description: 'Git tag name' },
|
||||
name: { type: 'string', description: 'Release name' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL for the release' },
|
||||
tarball_url: { type: 'string', description: 'URL to download release as tarball' },
|
||||
zipball_url: { type: 'string', description: 'URL to download release as zipball' },
|
||||
draft: { type: 'boolean', description: 'Whether this is a draft release' },
|
||||
prerelease: { type: 'boolean', description: 'Whether this is a prerelease' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
published_at: { type: 'string', description: 'Publication timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
89
apps/sim/tools/github/delete_branch.ts
Normal file
89
apps/sim/tools/github/delete_branch.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import type { DeleteBranchParams, DeleteBranchResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteBranchTool: ToolConfig<DeleteBranchParams, DeleteBranchResponse> = {
|
||||
id: 'github_delete_branch',
|
||||
name: 'GitHub Delete Branch',
|
||||
description:
|
||||
'Delete a branch from a GitHub repository by removing its git reference. Protected branches cannot be deleted.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the branch to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/git/refs/heads/${params.branch}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!params) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Missing parameters',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
deleted: false,
|
||||
branch: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const content = `Branch "${params.branch}" has been successfully deleted from ${params.owner}/${params.repo}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
deleted: true,
|
||||
branch: params.branch,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable deletion confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Deletion metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether the branch was deleted' },
|
||||
branch: { type: 'string', description: 'Name of the deleted branch' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
74
apps/sim/tools/github/delete_comment.ts
Normal file
74
apps/sim/tools/github/delete_comment.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { DeleteCommentParams, DeleteCommentResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteCommentTool: ToolConfig<DeleteCommentParams, DeleteCommentResponse> = {
|
||||
id: 'github_delete_comment',
|
||||
name: 'GitHub Comment Deleter',
|
||||
description: 'Delete a comment on a GitHub issue or pull request',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
comment_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const content = `Comment #${response.url.split('/').pop()} successfully deleted`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
deleted: true,
|
||||
comment_id: Number(response.url.split('/').pop()),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable deletion confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Deletion result metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
|
||||
comment_id: { type: 'number', description: 'Deleted comment ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
149
apps/sim/tools/github/delete_file.ts
Normal file
149
apps/sim/tools/github/delete_file.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import type { DeleteFileParams, DeleteFileResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteFileTool: ToolConfig<DeleteFileParams, DeleteFileResponse> = {
|
||||
id: 'github_delete_file',
|
||||
name: 'GitHub Delete File',
|
||||
description:
|
||||
'Delete a file from a GitHub repository. Requires the file SHA. This operation cannot be undone through the API.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Path to the file to delete (e.g., "src/oldfile.ts")',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Commit message for this file deletion',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The blob SHA of the file being deleted (get from github_get_file_content)',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch to delete the file from (defaults to repository default branch)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/contents/${params.path}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
message: params.message,
|
||||
sha: params.sha, // Required for delete
|
||||
}
|
||||
|
||||
if (params.branch) {
|
||||
body.branch = params.branch
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const content = `File deleted successfully!
|
||||
|
||||
Path: ${data.commit.sha ? 'File removed from repository' : 'Unknown'}
|
||||
Deleted: Yes
|
||||
|
||||
Commit:
|
||||
- SHA: ${data.commit.sha}
|
||||
- Message: ${data.commit.message || 'N/A'}
|
||||
- Author: ${data.commit.author?.name || 'N/A'}
|
||||
- Date: ${data.commit.author?.date || 'N/A'}
|
||||
|
||||
View commit: ${data.commit.html_url || 'N/A'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
deleted: true,
|
||||
path: data.commit.tree?.sha || 'N/A',
|
||||
commit: {
|
||||
sha: data.commit.sha,
|
||||
message: data.commit.message || '',
|
||||
author: {
|
||||
name: data.commit.author?.name || '',
|
||||
email: data.commit.author?.email || '',
|
||||
date: data.commit.author?.date || '',
|
||||
},
|
||||
committer: {
|
||||
name: data.commit.committer?.name || '',
|
||||
email: data.commit.committer?.email || '',
|
||||
date: data.commit.committer?.date || '',
|
||||
},
|
||||
html_url: data.commit.html_url || '',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable file deletion confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Deletion confirmation and commit metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether the file was deleted' },
|
||||
path: { type: 'string', description: 'File path that was deleted' },
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit information',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Author information',
|
||||
},
|
||||
committer: {
|
||||
type: 'object',
|
||||
description: 'Committer information',
|
||||
},
|
||||
html_url: { type: 'string', description: 'Commit URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
128
apps/sim/tools/github/delete_project.ts
Normal file
128
apps/sim/tools/github/delete_project.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import type { DeleteProjectParams, ProjectResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteProjectTool: ToolConfig<DeleteProjectParams, ProjectResponse> = {
|
||||
id: 'github_delete_project',
|
||||
name: 'GitHub Delete Project',
|
||||
description:
|
||||
'Delete a GitHub Project V2. This action is permanent and cannot be undone. Requires the project Node ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
project_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project Node ID (format: PVT_...)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with project admin permissions',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.github.com/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const query = `
|
||||
mutation($projectId: ID!) {
|
||||
deleteProjectV2(input: {
|
||||
projectId: $projectId
|
||||
}) {
|
||||
projectV2 {
|
||||
id
|
||||
title
|
||||
number
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
return {
|
||||
query,
|
||||
variables: {
|
||||
projectId: params.project_id,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: `GraphQL Error: ${data.errors[0].message}`,
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: data.errors[0].message,
|
||||
}
|
||||
}
|
||||
|
||||
// Extract project data
|
||||
const project = data.data?.deleteProjectV2?.projectV2
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: 'Failed to delete project',
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: 'Failed to delete project',
|
||||
}
|
||||
}
|
||||
|
||||
// Create human-readable content
|
||||
let content = `Project deleted successfully!\n`
|
||||
content += `Title: ${project.title}\n`
|
||||
content += `ID: ${project.id}\n`
|
||||
if (project.number) {
|
||||
content += `Number: ${project.number}\n`
|
||||
}
|
||||
content += `URL: ${project.url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: project.id,
|
||||
title: project.title,
|
||||
number: project.number,
|
||||
url: project.url,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable confirmation message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Deleted project metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project node ID' },
|
||||
title: { type: 'string', description: 'Project title' },
|
||||
number: { type: 'number', description: 'Project number', optional: true },
|
||||
url: { type: 'string', description: 'Project URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
98
apps/sim/tools/github/delete_release.ts
Normal file
98
apps/sim/tools/github/delete_release.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import type { DeleteReleaseParams, DeleteReleaseResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteReleaseTool: ToolConfig<DeleteReleaseParams, DeleteReleaseResponse> = {
|
||||
id: 'github_delete_release',
|
||||
name: 'GitHub Delete Release',
|
||||
description:
|
||||
'Delete a GitHub release by ID. This permanently removes the release but does not delete the associated Git tag.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
release_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier of the release to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/releases/${params.release_id}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!params) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Missing parameters',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
deleted: false,
|
||||
release_id: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
const content = `Release deleted successfully
|
||||
Release ID: ${params.release_id}
|
||||
Repository: ${params.owner}/${params.repo}
|
||||
|
||||
Note: The associated Git tag has not been deleted and remains in the repository.`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
deleted: true,
|
||||
release_id: params.release_id,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.text()
|
||||
throw new Error(`Unexpected response: ${response.status} - ${data}`)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable deletion confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Deletion result metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether the release was successfully deleted' },
|
||||
release_id: { type: 'number', description: 'ID of the deleted release' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
92
apps/sim/tools/github/get_branch.ts
Normal file
92
apps/sim/tools/github/get_branch.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { BranchResponse, GetBranchParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getBranchTool: ToolConfig<GetBranchParams, BranchResponse> = {
|
||||
id: 'github_get_branch',
|
||||
name: 'GitHub Get Branch',
|
||||
description:
|
||||
'Get detailed information about a specific branch in a GitHub repository, including commit details and protection status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/branches/${params.branch}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const branch = await response.json()
|
||||
|
||||
const content = `Branch: ${branch.name}
|
||||
Commit SHA: ${branch.commit.sha}
|
||||
Commit URL: ${branch.commit.url}
|
||||
Protected: ${branch.protected ? 'Yes' : 'No'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
name: branch.name,
|
||||
commit: {
|
||||
sha: branch.commit.sha,
|
||||
url: branch.commit.url,
|
||||
},
|
||||
protected: branch.protected,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable branch details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Branch metadata',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Branch name' },
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit information',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
url: { type: 'string', description: 'Commit API URL' },
|
||||
},
|
||||
},
|
||||
protected: { type: 'boolean', description: 'Whether branch is protected' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
183
apps/sim/tools/github/get_branch_protection.ts
Normal file
183
apps/sim/tools/github/get_branch_protection.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import type { BranchProtectionResponse, GetBranchProtectionParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getBranchProtectionTool: ToolConfig<
|
||||
GetBranchProtectionParams,
|
||||
BranchProtectionResponse
|
||||
> = {
|
||||
id: 'github_get_branch_protection',
|
||||
name: 'GitHub Get Branch Protection',
|
||||
description:
|
||||
'Get the branch protection rules for a specific branch, including status checks, review requirements, and restrictions.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/branches/${params.branch}/protection`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const protection = await response.json()
|
||||
|
||||
let content = `Branch Protection for "${protection.url.split('/branches/')[1].split('/protection')[0]}":
|
||||
|
||||
Enforce Admins: ${protection.enforce_admins?.enabled ? 'Yes' : 'No'}`
|
||||
|
||||
if (protection.required_status_checks) {
|
||||
content += `\n\nRequired Status Checks:
|
||||
- Strict: ${protection.required_status_checks.strict}
|
||||
- Contexts: ${protection.required_status_checks.contexts.length > 0 ? protection.required_status_checks.contexts.join(', ') : 'None'}`
|
||||
} else {
|
||||
content += '\n\nRequired Status Checks: None'
|
||||
}
|
||||
|
||||
if (protection.required_pull_request_reviews) {
|
||||
content += `\n\nRequired Pull Request Reviews:
|
||||
- Required Approving Reviews: ${protection.required_pull_request_reviews.required_approving_review_count || 0}
|
||||
- Dismiss Stale Reviews: ${protection.required_pull_request_reviews.dismiss_stale_reviews ? 'Yes' : 'No'}
|
||||
- Require Code Owner Reviews: ${protection.required_pull_request_reviews.require_code_owner_reviews ? 'Yes' : 'No'}`
|
||||
} else {
|
||||
content += '\n\nRequired Pull Request Reviews: None'
|
||||
}
|
||||
|
||||
if (protection.restrictions) {
|
||||
const users = protection.restrictions.users?.map((u: any) => u.login) || []
|
||||
const teams = protection.restrictions.teams?.map((t: any) => t.slug) || []
|
||||
content += `\n\nRestrictions:
|
||||
- Users: ${users.length > 0 ? users.join(', ') : 'None'}
|
||||
- Teams: ${teams.length > 0 ? teams.join(', ') : 'None'}`
|
||||
} else {
|
||||
content += '\n\nRestrictions: None'
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
required_status_checks: protection.required_status_checks
|
||||
? {
|
||||
strict: protection.required_status_checks.strict,
|
||||
contexts: protection.required_status_checks.contexts,
|
||||
}
|
||||
: null,
|
||||
enforce_admins: {
|
||||
enabled: protection.enforce_admins?.enabled || false,
|
||||
},
|
||||
required_pull_request_reviews: protection.required_pull_request_reviews
|
||||
? {
|
||||
required_approving_review_count:
|
||||
protection.required_pull_request_reviews.required_approving_review_count || 0,
|
||||
dismiss_stale_reviews:
|
||||
protection.required_pull_request_reviews.dismiss_stale_reviews || false,
|
||||
require_code_owner_reviews:
|
||||
protection.required_pull_request_reviews.require_code_owner_reviews || false,
|
||||
}
|
||||
: null,
|
||||
restrictions: protection.restrictions
|
||||
? {
|
||||
users: protection.restrictions.users?.map((u: any) => u.login) || [],
|
||||
teams: protection.restrictions.teams?.map((t: any) => t.slug) || [],
|
||||
}
|
||||
: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable branch protection summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Branch protection configuration',
|
||||
properties: {
|
||||
required_status_checks: {
|
||||
type: 'object',
|
||||
description: 'Status check requirements (null if not configured)',
|
||||
properties: {
|
||||
strict: { type: 'boolean', description: 'Require branches to be up to date' },
|
||||
contexts: {
|
||||
type: 'array',
|
||||
description: 'Required status check contexts',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
enforce_admins: {
|
||||
type: 'object',
|
||||
description: 'Admin enforcement settings',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', description: 'Enforce for administrators' },
|
||||
},
|
||||
},
|
||||
required_pull_request_reviews: {
|
||||
type: 'object',
|
||||
description: 'Pull request review requirements (null if not configured)',
|
||||
properties: {
|
||||
required_approving_review_count: {
|
||||
type: 'number',
|
||||
description: 'Number of approving reviews required',
|
||||
},
|
||||
dismiss_stale_reviews: {
|
||||
type: 'boolean',
|
||||
description: 'Dismiss stale pull request approvals',
|
||||
},
|
||||
require_code_owner_reviews: {
|
||||
type: 'boolean',
|
||||
description: 'Require review from code owners',
|
||||
},
|
||||
},
|
||||
},
|
||||
restrictions: {
|
||||
type: 'object',
|
||||
description: 'Push restrictions (null if not configured)',
|
||||
properties: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'Users who can push',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams who can push',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
138
apps/sim/tools/github/get_file_content.ts
Normal file
138
apps/sim/tools/github/get_file_content.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { FileContentResponse, GetFileContentParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getFileContentTool: ToolConfig<GetFileContentParams, FileContentResponse> = {
|
||||
id: 'github_get_file_content',
|
||||
name: 'GitHub Get File Content',
|
||||
description:
|
||||
'Get the content of a file from a GitHub repository. Supports files up to 1MB. Content is returned decoded and human-readable.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Path to the file in the repository (e.g., "src/index.ts")',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch name, tag, or commit SHA (defaults to repository default branch)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/contents/${params.path}`
|
||||
return params.ref ? `${baseUrl}?ref=${params.ref}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Path points to a directory. Use github_get_tree to list directory contents.',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
name: '',
|
||||
path: '',
|
||||
sha: '',
|
||||
size: 0,
|
||||
type: 'dir',
|
||||
download_url: '',
|
||||
html_url: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let decodedContent = ''
|
||||
if (data.content) {
|
||||
try {
|
||||
decodedContent = Buffer.from(data.content, 'base64').toString('utf-8')
|
||||
} catch (error) {
|
||||
decodedContent = '[Binary file - content cannot be displayed as text]'
|
||||
}
|
||||
}
|
||||
|
||||
const contentPreview =
|
||||
decodedContent.length > 500
|
||||
? `${decodedContent.substring(0, 500)}...\n\n[Content truncated. Full content available in metadata]`
|
||||
: decodedContent
|
||||
|
||||
const content = `File: ${data.name}
|
||||
Path: ${data.path}
|
||||
Size: ${data.size} bytes
|
||||
Type: ${data.type}
|
||||
SHA: ${data.sha}
|
||||
|
||||
Content Preview:
|
||||
${contentPreview}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
name: data.name,
|
||||
path: data.path,
|
||||
sha: data.sha,
|
||||
size: data.size,
|
||||
type: data.type,
|
||||
download_url: data.download_url,
|
||||
html_url: data.html_url,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Human-readable file information with content preview',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'File metadata including name, path, SHA, size, and URLs',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
path: { type: 'string', description: 'Full path in repository' },
|
||||
sha: { type: 'string', description: 'Git blob SHA' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
type: { type: 'string', description: 'Content type (file or dir)' },
|
||||
download_url: { type: 'string', description: 'Direct download URL', optional: true },
|
||||
html_url: { type: 'string', description: 'GitHub web UI URL', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
106
apps/sim/tools/github/get_issue.ts
Normal file
106
apps/sim/tools/github/get_issue.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { GetIssueParams, IssueResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getIssueTool: ToolConfig<GetIssueParams, IssueResponse> = {
|
||||
id: 'github_get_issue',
|
||||
name: 'GitHub Get Issue',
|
||||
description: 'Get detailed information about a specific issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
const labels = issue.labels?.map((label: any) => label.name) || []
|
||||
|
||||
const assignees = issue.assignees?.map((assignee: any) => assignee.login) || []
|
||||
|
||||
const content = `Issue #${issue.number}: "${issue.title}"
|
||||
State: ${issue.state}
|
||||
Created: ${issue.created_at}
|
||||
Updated: ${issue.updated_at}
|
||||
${issue.closed_at ? `Closed: ${issue.closed_at}` : ''}
|
||||
URL: ${issue.html_url}
|
||||
${labels.length > 0 ? `Labels: ${labels.join(', ')}` : 'No labels'}
|
||||
${assignees.length > 0 ? `Assignees: ${assignees.join(', ')}` : 'No assignees'}
|
||||
|
||||
Description:
|
||||
${issue.body || 'No description provided'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels,
|
||||
assignees,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
closed_at: issue.closed_at,
|
||||
body: issue.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable issue details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Detailed issue metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'Array of assignee usernames' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
closed_at: { type: 'string', description: 'Closed timestamp' },
|
||||
body: { type: 'string', description: 'Issue body/description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
138
apps/sim/tools/github/get_pr_files.ts
Normal file
138
apps/sim/tools/github/get_pr_files.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { GetPRFilesParams, PRFilesListResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getPRFilesTool: ToolConfig<GetPRFilesParams, PRFilesListResponse> = {
|
||||
id: 'github_get_pr_files',
|
||||
name: 'GitHub Get PR Files',
|
||||
description: 'Get the list of files changed in a pull request',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/files`
|
||||
)
|
||||
if (params.per_page) url.searchParams.append('per_page', params.per_page.toString())
|
||||
if (params.page) url.searchParams.append('page', params.page.toString())
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const files = await response.json()
|
||||
|
||||
const totalAdditions = files.reduce((sum: number, file: any) => sum + file.additions, 0)
|
||||
const totalDeletions = files.reduce((sum: number, file: any) => sum + file.deletions, 0)
|
||||
const totalChanges = files.reduce((sum: number, file: any) => sum + file.changes, 0)
|
||||
|
||||
const content = `Found ${files.length} file(s) changed in PR
|
||||
Total additions: ${totalAdditions}, Total deletions: ${totalDeletions}, Total changes: ${totalChanges}
|
||||
|
||||
Files:
|
||||
${files
|
||||
.map(
|
||||
(file: any) =>
|
||||
`- ${file.filename} (${file.status})
|
||||
+${file.additions} -${file.deletions} (~${file.changes} changes)`
|
||||
)
|
||||
.join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
files: files.map((file: any) => ({
|
||||
filename: file.filename,
|
||||
status: file.status,
|
||||
additions: file.additions,
|
||||
deletions: file.deletions,
|
||||
changes: file.changes,
|
||||
patch: file.patch,
|
||||
blob_url: file.blob_url,
|
||||
raw_url: file.raw_url,
|
||||
})),
|
||||
total_count: files.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of files changed in PR' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'PR files metadata',
|
||||
properties: {
|
||||
files: {
|
||||
type: 'array',
|
||||
description: 'Array of file changes',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
filename: { type: 'string', description: 'File path' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Change type (added/modified/deleted/renamed)',
|
||||
},
|
||||
additions: { type: 'number', description: 'Lines added' },
|
||||
deletions: { type: 'number', description: 'Lines deleted' },
|
||||
changes: { type: 'number', description: 'Total changes' },
|
||||
patch: { type: 'string', description: 'File diff patch' },
|
||||
blob_url: { type: 'string', description: 'GitHub blob URL' },
|
||||
raw_url: { type: 'string', description: 'Raw file URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of files changed' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
163
apps/sim/tools/github/get_project.ts
Normal file
163
apps/sim/tools/github/get_project.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { GetProjectParams, ProjectResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getProjectTool: ToolConfig<GetProjectParams, ProjectResponse> = {
|
||||
id: 'github_get_project',
|
||||
name: 'GitHub Get Project',
|
||||
description:
|
||||
'Get detailed information about a specific GitHub Project V2 by its number. Returns project details including ID, title, description, URL, and status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Owner type: "org" for organization or "user" for user',
|
||||
},
|
||||
owner_login: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Organization or user login name',
|
||||
},
|
||||
project_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project number',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with project read permissions',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.github.com/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const ownerType = params.owner_type === 'org' ? 'organization' : 'user'
|
||||
const query = `
|
||||
query($login: String!, $number: Int!) {
|
||||
${ownerType}(login: $login) {
|
||||
projectV2(number: $number) {
|
||||
id
|
||||
title
|
||||
number
|
||||
url
|
||||
closed
|
||||
public
|
||||
shortDescription
|
||||
readme
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
return {
|
||||
query,
|
||||
variables: {
|
||||
login: params.owner_login,
|
||||
number: params.project_number,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: `GraphQL Error: ${data.errors[0].message}`,
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: data.errors[0].message,
|
||||
}
|
||||
}
|
||||
|
||||
const ownerData = data.data?.organization || data.data?.user
|
||||
if (!ownerData || !ownerData.projectV2) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: 'Project not found',
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: 'Project not found',
|
||||
}
|
||||
}
|
||||
|
||||
const project = ownerData.projectV2
|
||||
|
||||
let content = `Project: ${project.title} (#${project.number})\n`
|
||||
content += `ID: ${project.id}\n`
|
||||
content += `URL: ${project.url}\n`
|
||||
content += `Status: ${project.closed ? 'Closed' : 'Open'}\n`
|
||||
content += `Visibility: ${project.public ? 'Public' : 'Private'}\n`
|
||||
if (project.shortDescription) {
|
||||
content += `Description: ${project.shortDescription}\n`
|
||||
}
|
||||
if (project.createdAt) {
|
||||
content += `Created: ${project.createdAt}\n`
|
||||
}
|
||||
if (project.updatedAt) {
|
||||
content += `Updated: ${project.updatedAt}\n`
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: content.trim(),
|
||||
metadata: {
|
||||
id: project.id,
|
||||
title: project.title,
|
||||
number: project.number,
|
||||
url: project.url,
|
||||
closed: project.closed,
|
||||
public: project.public,
|
||||
shortDescription: project.shortDescription || '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable project details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Project metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project node ID' },
|
||||
title: { type: 'string', description: 'Project title' },
|
||||
number: { type: 'number', description: 'Project number', optional: true },
|
||||
url: { type: 'string', description: 'Project URL' },
|
||||
closed: { type: 'boolean', description: 'Whether project is closed', optional: true },
|
||||
public: { type: 'boolean', description: 'Whether project is public', optional: true },
|
||||
shortDescription: {
|
||||
type: 'string',
|
||||
description: 'Project short description',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
111
apps/sim/tools/github/get_release.ts
Normal file
111
apps/sim/tools/github/get_release.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { GetReleaseParams, ReleaseResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getReleaseTool: ToolConfig<GetReleaseParams, ReleaseResponse> = {
|
||||
id: 'github_get_release',
|
||||
name: 'GitHub Get Release',
|
||||
description:
|
||||
'Get detailed information about a specific GitHub release by ID. Returns release metadata including assets and download URLs.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
release_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier of the release',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/releases/${params.release_id}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const releaseType = data.draft ? 'Draft' : data.prerelease ? 'Prerelease' : 'Release'
|
||||
const assetsInfo =
|
||||
data.assets && data.assets.length > 0
|
||||
? `\n\nAssets (${data.assets.length}):\n${data.assets.map((asset: any) => `- ${asset.name} (${asset.size} bytes, downloaded ${asset.download_count} times)`).join('\n')}`
|
||||
: '\n\nNo assets attached'
|
||||
|
||||
const content = `${releaseType}: "${data.name || data.tag_name}"
|
||||
Tag: ${data.tag_name}
|
||||
Author: ${data.author?.login || 'Unknown'}
|
||||
Created: ${data.created_at}
|
||||
${data.published_at ? `Published: ${data.published_at}` : 'Not yet published'}
|
||||
URL: ${data.html_url}
|
||||
|
||||
Description:
|
||||
${data.body || 'No description provided'}
|
||||
|
||||
Download URLs:
|
||||
- Tarball: ${data.tarball_url}
|
||||
- Zipball: ${data.zipball_url}${assetsInfo}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
tag_name: data.tag_name,
|
||||
name: data.name || data.tag_name,
|
||||
html_url: data.html_url,
|
||||
tarball_url: data.tarball_url,
|
||||
zipball_url: data.zipball_url,
|
||||
draft: data.draft,
|
||||
prerelease: data.prerelease,
|
||||
created_at: data.created_at,
|
||||
published_at: data.published_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable release details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Release metadata including download URLs',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Release ID' },
|
||||
tag_name: { type: 'string', description: 'Git tag name' },
|
||||
name: { type: 'string', description: 'Release name' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL for the release' },
|
||||
tarball_url: { type: 'string', description: 'URL to download release as tarball' },
|
||||
zipball_url: { type: 'string', description: 'URL to download release as zipball' },
|
||||
draft: { type: 'boolean', description: 'Whether this is a draft release' },
|
||||
prerelease: { type: 'boolean', description: 'Whether this is a prerelease' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
published_at: { type: 'string', description: 'Publication timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
163
apps/sim/tools/github/get_tree.ts
Normal file
163
apps/sim/tools/github/get_tree.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { GetTreeParams, TreeResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getTreeTool: ToolConfig<GetTreeParams, TreeResponse> = {
|
||||
id: 'github_get_tree',
|
||||
name: 'GitHub Get Repository Tree',
|
||||
description:
|
||||
'Get the contents of a directory in a GitHub repository. Returns a list of files and subdirectories. Use empty path or omit to get root directory contents.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Directory path (e.g., "src/components"). Leave empty for root directory.',
|
||||
default: '',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch name, tag, or commit SHA (defaults to repository default branch)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const path = params.path || ''
|
||||
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/contents/${path}`
|
||||
return params.ref ? `${baseUrl}?ref=${params.ref}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Path points to a file. Use github_get_file_content to get file contents.',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
path: '',
|
||||
items: [],
|
||||
total_count: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const items = data.map((item: any) => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
sha: item.sha,
|
||||
size: item.size,
|
||||
type: item.type,
|
||||
download_url: item.download_url,
|
||||
html_url: item.html_url,
|
||||
}))
|
||||
|
||||
const files = items.filter((item) => item.type === 'file')
|
||||
const dirs = items.filter((item) => item.type === 'dir')
|
||||
const other = items.filter((item) => item.type !== 'file' && item.type !== 'dir')
|
||||
|
||||
let content = `Repository Tree: ${data[0]?.path ? data[0].path.split('/').slice(0, -1).join('/') || '/' : '/'}
|
||||
Total items: ${items.length}
|
||||
|
||||
`
|
||||
|
||||
if (dirs.length > 0) {
|
||||
content += `Directories (${dirs.length}):\n`
|
||||
dirs.forEach((dir) => {
|
||||
content += ` - ${dir.name}/\n`
|
||||
})
|
||||
content += '\n'
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
content += `Files (${files.length}):\n`
|
||||
files.forEach((file) => {
|
||||
const sizeKB = (file.size / 1024).toFixed(2)
|
||||
content += ` - ${file.name} (${sizeKB} KB)\n`
|
||||
})
|
||||
content += '\n'
|
||||
}
|
||||
|
||||
if (other.length > 0) {
|
||||
content += `Other (${other.length}):\n`
|
||||
other.forEach((item) => {
|
||||
content += ` - ${item.name} [${item.type}]\n`
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
path: data[0]?.path?.split('/').slice(0, -1).join('/') || '/',
|
||||
items,
|
||||
total_count: items.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Human-readable directory tree listing',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Directory contents metadata',
|
||||
properties: {
|
||||
path: { type: 'string', description: 'Directory path' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of files and directories',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File or directory name' },
|
||||
path: { type: 'string', description: 'Full path in repository' },
|
||||
sha: { type: 'string', description: 'Git object SHA' },
|
||||
size: { type: 'number', description: 'Size in bytes' },
|
||||
type: { type: 'string', description: 'Type (file, dir, symlink, submodule)' },
|
||||
download_url: { type: 'string', description: 'Direct download URL (files only)' },
|
||||
html_url: { type: 'string', description: 'GitHub web UI URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of items' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
89
apps/sim/tools/github/get_workflow.ts
Normal file
89
apps/sim/tools/github/get_workflow.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import type { GetWorkflowParams, WorkflowResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getWorkflowTool: ToolConfig<GetWorkflowParams, WorkflowResponse> = {
|
||||
id: 'github_get_workflow',
|
||||
name: 'GitHub Get Workflow',
|
||||
description:
|
||||
'Get details of a specific GitHub Actions workflow by ID or filename. Returns workflow information including name, path, state, and badge URL.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
workflow_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Workflow ID (number) or workflow filename (e.g., "main.yaml")',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/workflows/${params.workflow_id}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Workflow: ${data.name}
|
||||
State: ${data.state}
|
||||
Path: ${data.path}
|
||||
ID: ${data.id}
|
||||
Badge URL: ${data.badge_url}
|
||||
Created: ${data.created_at}
|
||||
Updated: ${data.updated_at}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
path: data.path,
|
||||
state: data.state,
|
||||
badge_url: data.badge_url,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable workflow details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Workflow metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Workflow ID' },
|
||||
name: { type: 'string', description: 'Workflow name' },
|
||||
path: { type: 'string', description: 'Path to workflow file' },
|
||||
state: { type: 'string', description: 'Workflow state (active/disabled)' },
|
||||
badge_url: { type: 'string', description: 'Badge URL for workflow' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
95
apps/sim/tools/github/get_workflow_run.ts
Normal file
95
apps/sim/tools/github/get_workflow_run.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { GetWorkflowRunParams, WorkflowRunResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getWorkflowRunTool: ToolConfig<GetWorkflowRunParams, WorkflowRunResponse> = {
|
||||
id: 'github_get_workflow_run',
|
||||
name: 'GitHub Get Workflow Run',
|
||||
description:
|
||||
'Get detailed information about a specific workflow run by ID. Returns status, conclusion, timing, and links to the run.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
run_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Workflow run ID',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/runs/${params.run_id}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Workflow Run #${data.run_number}: ${data.name}
|
||||
Status: ${data.status}${data.conclusion ? ` - ${data.conclusion}` : ''}
|
||||
Branch: ${data.head_branch}
|
||||
Commit: ${data.head_sha.substring(0, 7)}
|
||||
Event: ${data.event}
|
||||
Triggered by: ${data.triggering_actor?.login || 'Unknown'}
|
||||
Started: ${data.run_started_at || data.created_at}
|
||||
${data.updated_at ? `Updated: ${data.updated_at}` : ''}
|
||||
${data.run_attempt ? `Attempt: ${data.run_attempt}` : ''}
|
||||
URL: ${data.html_url}
|
||||
Logs: ${data.logs_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
status: data.status,
|
||||
conclusion: data.conclusion,
|
||||
html_url: data.html_url,
|
||||
run_number: data.run_number,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable workflow run details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Workflow run metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Workflow run ID' },
|
||||
name: { type: 'string', description: 'Workflow name' },
|
||||
status: { type: 'string', description: 'Run status' },
|
||||
conclusion: { type: 'string', description: 'Run conclusion' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
run_number: { type: 'number', description: 'Run number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,9 +1,105 @@
|
||||
import { addAssigneesTool } from '@/tools/github/add_assignees'
|
||||
import { addLabelsTool } from '@/tools/github/add_labels'
|
||||
import { cancelWorkflowRunTool } from '@/tools/github/cancel_workflow_run'
|
||||
import { closeIssueTool } from '@/tools/github/close_issue'
|
||||
import { closePRTool } from '@/tools/github/close_pr'
|
||||
import { commentTool } from '@/tools/github/comment'
|
||||
import { createBranchTool } from '@/tools/github/create_branch'
|
||||
import { createFileTool } from '@/tools/github/create_file'
|
||||
import { createIssueTool } from '@/tools/github/create_issue'
|
||||
import { createPRTool } from '@/tools/github/create_pr'
|
||||
import { createProjectTool } from '@/tools/github/create_project'
|
||||
import { createReleaseTool } from '@/tools/github/create_release'
|
||||
import { deleteBranchTool } from '@/tools/github/delete_branch'
|
||||
import { deleteCommentTool } from '@/tools/github/delete_comment'
|
||||
import { deleteFileTool } from '@/tools/github/delete_file'
|
||||
import { deleteProjectTool } from '@/tools/github/delete_project'
|
||||
import { deleteReleaseTool } from '@/tools/github/delete_release'
|
||||
import { getBranchTool } from '@/tools/github/get_branch'
|
||||
import { getBranchProtectionTool } from '@/tools/github/get_branch_protection'
|
||||
import { getFileContentTool } from '@/tools/github/get_file_content'
|
||||
import { getIssueTool } from '@/tools/github/get_issue'
|
||||
import { getPRFilesTool } from '@/tools/github/get_pr_files'
|
||||
import { getProjectTool } from '@/tools/github/get_project'
|
||||
import { getReleaseTool } from '@/tools/github/get_release'
|
||||
import { getTreeTool } from '@/tools/github/get_tree'
|
||||
import { getWorkflowTool } from '@/tools/github/get_workflow'
|
||||
import { getWorkflowRunTool } from '@/tools/github/get_workflow_run'
|
||||
import { issueCommentTool } from '@/tools/github/issue_comment'
|
||||
import { latestCommitTool } from '@/tools/github/latest_commit'
|
||||
import { listBranchesTool } from '@/tools/github/list_branches'
|
||||
import { listIssueCommentsTool } from '@/tools/github/list_issue_comments'
|
||||
import { listIssuesTool } from '@/tools/github/list_issues'
|
||||
import { listPRCommentsTool } from '@/tools/github/list_pr_comments'
|
||||
import { listProjectsTool } from '@/tools/github/list_projects'
|
||||
import { listPRsTool } from '@/tools/github/list_prs'
|
||||
import { listReleasesTool } from '@/tools/github/list_releases'
|
||||
import { listWorkflowRunsTool } from '@/tools/github/list_workflow_runs'
|
||||
import { listWorkflowsTool } from '@/tools/github/list_workflows'
|
||||
import { mergePRTool } from '@/tools/github/merge_pr'
|
||||
import { prTool } from '@/tools/github/pr'
|
||||
import { removeLabelTool } from '@/tools/github/remove_label'
|
||||
import { repoInfoTool } from '@/tools/github/repo_info'
|
||||
import { requestReviewersTool } from '@/tools/github/request_reviewers'
|
||||
import { rerunWorkflowTool } from '@/tools/github/rerun_workflow'
|
||||
import { triggerWorkflowTool } from '@/tools/github/trigger_workflow'
|
||||
import { updateBranchProtectionTool } from '@/tools/github/update_branch_protection'
|
||||
import { updateCommentTool } from '@/tools/github/update_comment'
|
||||
import { updateFileTool } from '@/tools/github/update_file'
|
||||
import { updateIssueTool } from '@/tools/github/update_issue'
|
||||
import { updatePRTool } from '@/tools/github/update_pr'
|
||||
import { updateProjectTool } from '@/tools/github/update_project'
|
||||
import { updateReleaseTool } from '@/tools/github/update_release'
|
||||
|
||||
export const githubCancelWorkflowRunTool = cancelWorkflowRunTool
|
||||
export const githubClosePRTool = closePRTool
|
||||
export const githubCommentTool = commentTool
|
||||
export const githubCreateBranchTool = createBranchTool
|
||||
export const githubCreateFileTool = createFileTool
|
||||
export const githubCreatePRTool = createPRTool
|
||||
export const githubCreateProjectTool = createProjectTool
|
||||
export const githubCreateReleaseTool = createReleaseTool
|
||||
export const githubDeleteBranchTool = deleteBranchTool
|
||||
export const githubDeleteCommentTool = deleteCommentTool
|
||||
export const githubDeleteFileTool = deleteFileTool
|
||||
export const githubDeleteProjectTool = deleteProjectTool
|
||||
export const githubDeleteReleaseTool = deleteReleaseTool
|
||||
export const githubGetBranchTool = getBranchTool
|
||||
export const githubGetBranchProtectionTool = getBranchProtectionTool
|
||||
export const githubGetFileContentTool = getFileContentTool
|
||||
export const githubGetPRFilesTool = getPRFilesTool
|
||||
export const githubGetProjectTool = getProjectTool
|
||||
export const githubGetReleaseTool = getReleaseTool
|
||||
export const githubGetTreeTool = getTreeTool
|
||||
export const githubGetWorkflowTool = getWorkflowTool
|
||||
export const githubGetWorkflowRunTool = getWorkflowRunTool
|
||||
export const githubIssueCommentTool = issueCommentTool
|
||||
export const githubLatestCommitTool = latestCommitTool
|
||||
export const githubListBranchesTool = listBranchesTool
|
||||
export const githubListIssueCommentsTool = listIssueCommentsTool
|
||||
export const githubListPRCommentsTool = listPRCommentsTool
|
||||
export const githubListPRsTool = listPRsTool
|
||||
export const githubListProjectsTool = listProjectsTool
|
||||
export const githubListReleasesTool = listReleasesTool
|
||||
export const githubListWorkflowRunsTool = listWorkflowRunsTool
|
||||
export const githubListWorkflowsTool = listWorkflowsTool
|
||||
export const githubMergePRTool = mergePRTool
|
||||
export const githubPrTool = prTool
|
||||
export const githubRepoInfoTool = repoInfoTool
|
||||
export const githubRequestReviewersTool = requestReviewersTool
|
||||
export const githubRerunWorkflowTool = rerunWorkflowTool
|
||||
export const githubTriggerWorkflowTool = triggerWorkflowTool
|
||||
export const githubUpdateBranchProtectionTool = updateBranchProtectionTool
|
||||
export const githubUpdateCommentTool = updateCommentTool
|
||||
export const githubUpdateFileTool = updateFileTool
|
||||
export const githubUpdatePRTool = updatePRTool
|
||||
export const githubUpdateProjectTool = updateProjectTool
|
||||
export const githubUpdateReleaseTool = updateReleaseTool
|
||||
export const githubAddAssigneesTool = addAssigneesTool
|
||||
export const githubAddLabelsTool = addLabelsTool
|
||||
export const githubCloseIssueTool = closeIssueTool
|
||||
export const githubCreateIssueTool = createIssueTool
|
||||
export const githubGetIssueTool = getIssueTool
|
||||
export const githubListIssuesTool = listIssuesTool
|
||||
export const githubRemoveLabelTool = removeLabelTool
|
||||
export const githubUpdateIssueTool = updateIssueTool
|
||||
|
||||
103
apps/sim/tools/github/issue_comment.ts
Normal file
103
apps/sim/tools/github/issue_comment.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { CreateIssueCommentParams, IssueCommentResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const issueCommentTool: ToolConfig<CreateIssueCommentParams, IssueCommentResponse> = {
|
||||
id: 'github_issue_comment',
|
||||
name: 'GitHub Issue Comment Creator',
|
||||
description: 'Create a comment on a GitHub issue',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment content',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/comments`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
body: params.body,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Comment created on issue #${data.issue_url.split('/').pop()}: "${data.body.substring(0, 100)}${data.body.length > 100 ? '...' : ''}"`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
body: data.body,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
user: {
|
||||
login: data.user.login,
|
||||
id: data.user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable comment confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Comment metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Comment ID' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
body: { type: 'string', description: 'Comment body' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User who created the comment',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'User login' },
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
137
apps/sim/tools/github/list_branches.ts
Normal file
137
apps/sim/tools/github/list_branches.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import type { BranchListResponse, ListBranchesParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listBranchesTool: ToolConfig<ListBranchesParams, BranchListResponse> = {
|
||||
id: 'github_list_branches',
|
||||
name: 'GitHub List Branches',
|
||||
description:
|
||||
'List all branches in a GitHub repository. Optionally filter by protected status and control pagination.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
protected: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter branches by protection status',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (max 100, default 30)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination (default 1)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/branches`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.protected !== undefined) {
|
||||
queryParams.append('protected', params.protected.toString())
|
||||
}
|
||||
if (params.per_page) {
|
||||
queryParams.append('per_page', params.per_page.toString())
|
||||
}
|
||||
if (params.page) {
|
||||
queryParams.append('page', params.page.toString())
|
||||
}
|
||||
|
||||
const query = queryParams.toString()
|
||||
return query ? `${baseUrl}?${query}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const branches = await response.json()
|
||||
|
||||
const branchList = branches
|
||||
.map(
|
||||
(branch: any) =>
|
||||
`- ${branch.name} (SHA: ${branch.commit.sha.substring(0, 7)}${branch.protected ? ', Protected' : ''})`
|
||||
)
|
||||
.join('\n')
|
||||
|
||||
const content = `Found ${branches.length} branch${branches.length !== 1 ? 'es' : ''}:
|
||||
${branchList}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
branches: branches.map((branch: any) => ({
|
||||
name: branch.name,
|
||||
commit: {
|
||||
sha: branch.commit.sha,
|
||||
url: branch.commit.url,
|
||||
},
|
||||
protected: branch.protected,
|
||||
})),
|
||||
total_count: branches.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of branches' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Branch list metadata',
|
||||
properties: {
|
||||
branches: {
|
||||
type: 'array',
|
||||
description: 'Array of branch objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Branch name' },
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit information',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
url: { type: 'string', description: 'Commit API URL' },
|
||||
},
|
||||
},
|
||||
protected: { type: 'boolean', description: 'Whether branch is protected' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of branches' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
140
apps/sim/tools/github/list_issue_comments.ts
Normal file
140
apps/sim/tools/github/list_issue_comments.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import type { CommentsListResponse, ListIssueCommentsParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listIssueCommentsTool: ToolConfig<ListIssueCommentsParams, CommentsListResponse> = {
|
||||
id: 'github_list_issue_comments',
|
||||
name: 'GitHub Issue Comments Lister',
|
||||
description: 'List all comments on a GitHub issue',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only show comments updated after this ISO 8601 timestamp',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (max 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/comments`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.since) queryParams.append('since', params.since)
|
||||
if (params.per_page) queryParams.append('per_page', params.per_page.toString())
|
||||
if (params.page) queryParams.append('page', params.page.toString())
|
||||
|
||||
const query = queryParams.toString()
|
||||
return query ? `${baseUrl}?${query}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Found ${data.length} comment${data.length !== 1 ? 's' : ''} on issue #${response.url.split('/').slice(-2, -1)[0]}${
|
||||
data.length > 0
|
||||
? `\n\nRecent comments:\n${data
|
||||
.slice(0, 5)
|
||||
.map(
|
||||
(c: any) =>
|
||||
`- ${c.user.login} (${new Date(c.created_at).toLocaleDateString()}): "${c.body.substring(0, 80)}${c.body.length > 80 ? '...' : ''}"`
|
||||
)
|
||||
.join('\n')}`
|
||||
: ''
|
||||
}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
comments: data.map((comment: any) => ({
|
||||
id: comment.id,
|
||||
body: comment.body,
|
||||
user: { login: comment.user.login },
|
||||
created_at: comment.created_at,
|
||||
html_url: comment.html_url,
|
||||
})),
|
||||
total_count: data.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable comments summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Comments list metadata',
|
||||
properties: {
|
||||
comments: {
|
||||
type: 'array',
|
||||
description: 'Array of comment objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Comment ID' },
|
||||
body: { type: 'string', description: 'Comment body' },
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User who created the comment',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'User login' },
|
||||
},
|
||||
},
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of comments' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
169
apps/sim/tools/github/list_issues.ts
Normal file
169
apps/sim/tools/github/list_issues.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import type { IssuesListResponse, ListIssuesParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listIssuesTool: ToolConfig<ListIssuesParams, IssuesListResponse> = {
|
||||
id: 'github_list_issues',
|
||||
name: 'GitHub List Issues',
|
||||
description:
|
||||
'List issues in a GitHub repository. Note: This includes pull requests as PRs are considered issues in GitHub',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by state: open, closed, or all (default: open)',
|
||||
default: 'open',
|
||||
},
|
||||
assignee: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by assignee username',
|
||||
},
|
||||
creator: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by creator username',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names to filter by',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: created, updated, or comments (default: created)',
|
||||
default: 'created',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction: asc or desc (default: desc)',
|
||||
default: 'desc',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/issues`)
|
||||
if (params.state) url.searchParams.append('state', params.state)
|
||||
if (params.assignee) url.searchParams.append('assignee', params.assignee)
|
||||
if (params.creator) url.searchParams.append('creator', params.creator)
|
||||
if (params.labels) url.searchParams.append('labels', params.labels)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.direction) url.searchParams.append('direction', params.direction)
|
||||
if (params.per_page) url.searchParams.append('per_page', params.per_page.toString())
|
||||
if (params.page) url.searchParams.append('page', params.page.toString())
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issues = await response.json()
|
||||
|
||||
const transformedIssues = issues.map((issue: any) => ({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels: issue.labels?.map((label: any) => label.name) || [],
|
||||
assignees: issue.assignees?.map((assignee: any) => assignee.login) || [],
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
}))
|
||||
|
||||
const content = `Found ${issues.length} issue(s):
|
||||
${transformedIssues
|
||||
.map(
|
||||
(issue: any) =>
|
||||
`#${issue.number}: "${issue.title}" (${issue.state}) - ${issue.html_url}
|
||||
${issue.labels.length > 0 ? `Labels: ${issue.labels.join(', ')}` : 'No labels'}
|
||||
${issue.assignees.length > 0 ? `Assignees: ${issue.assignees.join(', ')}` : 'No assignees'}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
issues: transformedIssues,
|
||||
total_count: issues.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of issues' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Issues list metadata',
|
||||
properties: {
|
||||
issues: {
|
||||
type: 'array',
|
||||
description: 'Array of issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'Array of assignee usernames' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of issues returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
156
apps/sim/tools/github/list_pr_comments.ts
Normal file
156
apps/sim/tools/github/list_pr_comments.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { CommentsListResponse, ListPRCommentsParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listPRCommentsTool: ToolConfig<ListPRCommentsParams, CommentsListResponse> = {
|
||||
id: 'github_list_pr_comments',
|
||||
name: 'GitHub PR Review Comments Lister',
|
||||
description: 'List all review comments on a GitHub pull request',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by created or updated',
|
||||
default: 'created',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc or desc)',
|
||||
default: 'desc',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only show comments updated after this ISO 8601 timestamp',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (max 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/comments`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.sort) queryParams.append('sort', params.sort)
|
||||
if (params.direction) queryParams.append('direction', params.direction)
|
||||
if (params.since) queryParams.append('since', params.since)
|
||||
if (params.per_page) queryParams.append('per_page', params.per_page.toString())
|
||||
if (params.page) queryParams.append('page', params.page.toString())
|
||||
|
||||
const query = queryParams.toString()
|
||||
return query ? `${baseUrl}?${query}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Found ${data.length} review comment${data.length !== 1 ? 's' : ''} on PR #${response.url.split('/').slice(-2, -1)[0]}${
|
||||
data.length > 0
|
||||
? `\n\nRecent review comments:\n${data
|
||||
.slice(0, 5)
|
||||
.map(
|
||||
(c: any) =>
|
||||
`- ${c.user.login} on ${c.path}${c.line ? `:${c.line}` : ''} (${new Date(c.created_at).toLocaleDateString()}): "${c.body.substring(0, 80)}${c.body.length > 80 ? '...' : ''}"`
|
||||
)
|
||||
.join('\n')}`
|
||||
: ''
|
||||
}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
comments: data.map((comment: any) => ({
|
||||
id: comment.id,
|
||||
body: comment.body,
|
||||
user: { login: comment.user.login },
|
||||
created_at: comment.created_at,
|
||||
html_url: comment.html_url,
|
||||
})),
|
||||
total_count: data.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable review comments summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Review comments list metadata',
|
||||
properties: {
|
||||
comments: {
|
||||
type: 'array',
|
||||
description: 'Array of review comment objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Comment ID' },
|
||||
body: { type: 'string', description: 'Comment body' },
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User who created the comment',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'User login' },
|
||||
},
|
||||
},
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of review comments' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
165
apps/sim/tools/github/list_projects.ts
Normal file
165
apps/sim/tools/github/list_projects.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import type { ListProjectsParams, ListProjectsResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listProjectsTool: ToolConfig<ListProjectsParams, ListProjectsResponse> = {
|
||||
id: 'github_list_projects',
|
||||
name: 'GitHub List Projects',
|
||||
description:
|
||||
'List GitHub Projects V2 for an organization or user. Returns up to 20 projects with their details including ID, title, number, URL, and status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Owner type: "org" for organization or "user" for user',
|
||||
},
|
||||
owner_login: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Organization or user login name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with project read permissions',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.github.com/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const ownerType = params.owner_type === 'org' ? 'organization' : 'user'
|
||||
const query = `
|
||||
query($login: String!) {
|
||||
${ownerType}(login: $login) {
|
||||
projectsV2(first: 20) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
number
|
||||
url
|
||||
closed
|
||||
public
|
||||
shortDescription
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
return {
|
||||
query,
|
||||
variables: {
|
||||
login: params.owner_login,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: `GraphQL Error: ${data.errors[0].message}`,
|
||||
metadata: {
|
||||
projects: [],
|
||||
totalCount: 0,
|
||||
},
|
||||
},
|
||||
error: data.errors[0].message,
|
||||
}
|
||||
}
|
||||
|
||||
const ownerData = data.data?.organization || data.data?.user
|
||||
if (!ownerData) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: 'No organization or user found',
|
||||
metadata: {
|
||||
projects: [],
|
||||
totalCount: 0,
|
||||
},
|
||||
},
|
||||
error: 'No organization or user found',
|
||||
}
|
||||
}
|
||||
|
||||
const projectsData = ownerData.projectsV2
|
||||
const projects = projectsData.nodes.map((project: any) => ({
|
||||
id: project.id,
|
||||
title: project.title,
|
||||
number: project.number,
|
||||
url: project.url,
|
||||
closed: project.closed,
|
||||
public: project.public,
|
||||
shortDescription: project.shortDescription || '',
|
||||
}))
|
||||
|
||||
let content = `Found ${projectsData.totalCount} project(s):\n\n`
|
||||
projects.forEach((project: any, index: number) => {
|
||||
content += `${index + 1}. ${project.title} (#${project.number})\n`
|
||||
content += ` ID: ${project.id}\n`
|
||||
content += ` URL: ${project.url}\n`
|
||||
content += ` Status: ${project.closed ? 'Closed' : 'Open'}\n`
|
||||
content += ` Visibility: ${project.public ? 'Public' : 'Private'}\n`
|
||||
if (project.shortDescription) {
|
||||
content += ` Description: ${project.shortDescription}\n`
|
||||
}
|
||||
content += '\n'
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: content.trim(),
|
||||
metadata: {
|
||||
projects,
|
||||
totalCount: projectsData.totalCount,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of projects' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Projects metadata',
|
||||
properties: {
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'Array of project objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project node ID' },
|
||||
title: { type: 'string', description: 'Project title' },
|
||||
number: { type: 'number', description: 'Project number' },
|
||||
url: { type: 'string', description: 'Project URL' },
|
||||
closed: { type: 'boolean', description: 'Whether project is closed' },
|
||||
public: { type: 'boolean', description: 'Whether project is public' },
|
||||
shortDescription: {
|
||||
type: 'string',
|
||||
description: 'Project short description',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: { type: 'number', description: 'Total number of projects' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
153
apps/sim/tools/github/list_prs.ts
Normal file
153
apps/sim/tools/github/list_prs.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import type { ListPRsParams, PRListResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listPRsTool: ToolConfig<ListPRsParams, PRListResponse> = {
|
||||
id: 'github_list_prs',
|
||||
name: 'GitHub List Pull Requests',
|
||||
description: 'List pull requests in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by state: open, closed, or all',
|
||||
default: 'open',
|
||||
},
|
||||
head: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by head user or branch name (format: user:ref-name or organization:ref-name)',
|
||||
},
|
||||
base: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by base branch name',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: created, updated, popularity, or long-running',
|
||||
default: 'created',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction: asc or desc',
|
||||
default: 'desc',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/pulls`)
|
||||
if (params.state) url.searchParams.append('state', params.state)
|
||||
if (params.head) url.searchParams.append('head', params.head)
|
||||
if (params.base) url.searchParams.append('base', params.base)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.direction) url.searchParams.append('direction', params.direction)
|
||||
if (params.per_page) url.searchParams.append('per_page', params.per_page.toString())
|
||||
if (params.page) url.searchParams.append('page', params.page.toString())
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const prs = await response.json()
|
||||
|
||||
const openCount = prs.filter((pr: any) => pr.state === 'open').length
|
||||
const closedCount = prs.filter((pr: any) => pr.state === 'closed').length
|
||||
|
||||
const content = `Found ${prs.length} pull request(s)
|
||||
Open: ${openCount}, Closed: ${closedCount}
|
||||
|
||||
${prs
|
||||
.map(
|
||||
(pr: any) =>
|
||||
`#${pr.number}: ${pr.title} (${pr.state})
|
||||
URL: ${pr.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
prs: prs.map((pr: any) => ({
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
state: pr.state,
|
||||
html_url: pr.html_url,
|
||||
created_at: pr.created_at,
|
||||
updated_at: pr.updated_at,
|
||||
})),
|
||||
total_count: prs.length,
|
||||
open_count: openCount,
|
||||
closed_count: closedCount,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of pull requests' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pull requests list metadata',
|
||||
properties: {
|
||||
prs: {
|
||||
type: 'array',
|
||||
description: 'Array of pull request summaries',
|
||||
},
|
||||
total_count: { type: 'number', description: 'Total number of PRs returned' },
|
||||
open_count: { type: 'number', description: 'Number of open PRs' },
|
||||
closed_count: { type: 'number', description: 'Number of closed PRs' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
137
apps/sim/tools/github/list_releases.ts
Normal file
137
apps/sim/tools/github/list_releases.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import type { ListReleasesParams, ListReleasesResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listReleasesTool: ToolConfig<ListReleasesParams, ListReleasesResponse> = {
|
||||
id: 'github_list_releases',
|
||||
name: 'GitHub List Releases',
|
||||
description:
|
||||
'List all releases for a GitHub repository. Returns release information including tags, names, and download URLs.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (max 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number of the results to fetch',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/releases`)
|
||||
if (params.per_page) {
|
||||
url.searchParams.append('per_page', params.per_page.toString())
|
||||
}
|
||||
if (params.page) {
|
||||
url.searchParams.append('page', params.page.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const releases = await response.json()
|
||||
|
||||
const totalReleases = releases.length
|
||||
const releasesList = releases
|
||||
.map(
|
||||
(release: any, index: number) =>
|
||||
`${index + 1}. ${release.name || release.tag_name} (${release.tag_name})
|
||||
${release.draft ? '[DRAFT] ' : ''}${release.prerelease ? '[PRERELEASE] ' : ''}
|
||||
Published: ${release.published_at || 'Not published'}
|
||||
URL: ${release.html_url}`
|
||||
)
|
||||
.join('\n\n')
|
||||
|
||||
const content = `Total releases: ${totalReleases}
|
||||
|
||||
${releasesList}
|
||||
|
||||
Summary of tags: ${releases.map((r: any) => r.tag_name).join(', ')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: totalReleases,
|
||||
releases: releases.map((release: any) => ({
|
||||
id: release.id,
|
||||
tag_name: release.tag_name,
|
||||
name: release.name || release.tag_name,
|
||||
html_url: release.html_url,
|
||||
tarball_url: release.tarball_url,
|
||||
zipball_url: release.zipball_url,
|
||||
draft: release.draft,
|
||||
prerelease: release.prerelease,
|
||||
created_at: release.created_at,
|
||||
published_at: release.published_at,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable list of releases with summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Releases metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total number of releases returned' },
|
||||
releases: {
|
||||
type: 'array',
|
||||
description: 'Array of release objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Release ID' },
|
||||
tag_name: { type: 'string', description: 'Git tag name' },
|
||||
name: { type: 'string', description: 'Release name' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
tarball_url: { type: 'string', description: 'Tarball download URL' },
|
||||
zipball_url: { type: 'string', description: 'Zipball download URL' },
|
||||
draft: { type: 'boolean', description: 'Is draft release' },
|
||||
prerelease: { type: 'boolean', description: 'Is prerelease' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
published_at: { type: 'string', description: 'Publication timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
186
apps/sim/tools/github/list_workflow_runs.ts
Normal file
186
apps/sim/tools/github/list_workflow_runs.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import type { ListWorkflowRunsParams, ListWorkflowRunsResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listWorkflowRunsTool: ToolConfig<ListWorkflowRunsParams, ListWorkflowRunsResponse> = {
|
||||
id: 'github_list_workflow_runs',
|
||||
name: 'GitHub List Workflow Runs',
|
||||
description:
|
||||
'List workflow runs for a repository. Supports filtering by actor, branch, event, and status. Returns run details including status, conclusion, and links.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
actor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by user who triggered the workflow',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by branch name',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by event type (e.g., push, pull_request, workflow_dispatch)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by status (queued, in_progress, completed, waiting, requested, pending)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default: 30, max: 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number of results to fetch (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/runs`
|
||||
)
|
||||
if (params.actor) {
|
||||
url.searchParams.append('actor', params.actor)
|
||||
}
|
||||
if (params.branch) {
|
||||
url.searchParams.append('branch', params.branch)
|
||||
}
|
||||
if (params.event) {
|
||||
url.searchParams.append('event', params.event)
|
||||
}
|
||||
if (params.status) {
|
||||
url.searchParams.append('status', params.status)
|
||||
}
|
||||
if (params.per_page) {
|
||||
url.searchParams.append('per_page', params.per_page.toString())
|
||||
}
|
||||
if (params.page) {
|
||||
url.searchParams.append('page', params.page.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const statusCounts = data.workflow_runs.reduce((acc: Record<string, number>, run: any) => {
|
||||
acc[run.status] = (acc[run.status] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
const conclusionCounts = data.workflow_runs.reduce((acc: Record<string, number>, run: any) => {
|
||||
if (run.conclusion) {
|
||||
acc[run.conclusion] = (acc[run.conclusion] || 0) + 1
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
const statusSummary = Object.entries(statusCounts)
|
||||
.map(([status, count]) => `${count} ${status}`)
|
||||
.join(', ')
|
||||
|
||||
const conclusionSummary = Object.entries(conclusionCounts)
|
||||
.map(([conclusion, count]) => `${count} ${conclusion}`)
|
||||
.join(', ')
|
||||
|
||||
const content = `Found ${data.total_count} workflow run(s)
|
||||
Status: ${statusSummary}
|
||||
${conclusionSummary ? `Conclusion: ${conclusionSummary}` : ''}
|
||||
|
||||
Recent runs:
|
||||
${data.workflow_runs
|
||||
.slice(0, 10)
|
||||
.map(
|
||||
(run: any) =>
|
||||
`- Run #${run.run_number}: ${run.name} (${run.status}${run.conclusion ? ` - ${run.conclusion}` : ''})
|
||||
Branch: ${run.head_branch}
|
||||
Triggered by: ${run.triggering_actor?.login || 'Unknown'}
|
||||
URL: ${run.html_url}`
|
||||
)
|
||||
.join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
workflow_runs: data.workflow_runs.map((run: any) => ({
|
||||
id: run.id,
|
||||
name: run.name,
|
||||
status: run.status,
|
||||
conclusion: run.conclusion,
|
||||
html_url: run.html_url,
|
||||
run_number: run.run_number,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable workflow runs summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Workflow runs metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total number of workflow runs' },
|
||||
workflow_runs: {
|
||||
type: 'array',
|
||||
description: 'Array of workflow run objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Workflow run ID' },
|
||||
name: { type: 'string', description: 'Workflow name' },
|
||||
status: { type: 'string', description: 'Run status' },
|
||||
conclusion: { type: 'string', description: 'Run conclusion' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
run_number: { type: 'number', description: 'Run number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
135
apps/sim/tools/github/list_workflows.ts
Normal file
135
apps/sim/tools/github/list_workflows.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type { ListWorkflowsParams, ListWorkflowsResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listWorkflowsTool: ToolConfig<ListWorkflowsParams, ListWorkflowsResponse> = {
|
||||
id: 'github_list_workflows',
|
||||
name: 'GitHub List Workflows',
|
||||
description:
|
||||
'List all workflows in a GitHub repository. Returns workflow details including ID, name, path, state, and badge URL.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default: 30, max: 100)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number of results to fetch (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/workflows`
|
||||
)
|
||||
if (params.per_page) {
|
||||
url.searchParams.append('per_page', params.per_page.toString())
|
||||
}
|
||||
if (params.page) {
|
||||
url.searchParams.append('page', params.page.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const stateCounts = data.workflows.reduce((acc: Record<string, number>, workflow: any) => {
|
||||
acc[workflow.state] = (acc[workflow.state] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
const statesSummary = Object.entries(stateCounts)
|
||||
.map(([state, count]) => `${count} ${state}`)
|
||||
.join(', ')
|
||||
|
||||
const content = `Found ${data.total_count} workflow(s) in ${data.workflows[0]?.path.split('/')[0] || '.github/workflows'}
|
||||
States: ${statesSummary}
|
||||
|
||||
Workflows:
|
||||
${data.workflows
|
||||
.map(
|
||||
(w: any) =>
|
||||
`- ${w.name} (${w.state})
|
||||
Path: ${w.path}
|
||||
ID: ${w.id}
|
||||
Badge: ${w.badge_url}`
|
||||
)
|
||||
.join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
workflows: data.workflows.map((workflow: any) => ({
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
path: workflow.path,
|
||||
state: workflow.state,
|
||||
badge_url: workflow.badge_url,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable workflows summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Workflows metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total number of workflows' },
|
||||
workflows: {
|
||||
type: 'array',
|
||||
description: 'Array of workflow objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Workflow ID' },
|
||||
name: { type: 'string', description: 'Workflow name' },
|
||||
path: { type: 'string', description: 'Path to workflow file' },
|
||||
state: { type: 'string', description: 'Workflow state (active/disabled)' },
|
||||
badge_url: { type: 'string', description: 'Badge URL for workflow' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
122
apps/sim/tools/github/merge_pr.ts
Normal file
122
apps/sim/tools/github/merge_pr.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { MergePRParams, MergeResultResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mergePRTool: ToolConfig<MergePRParams, MergeResultResponse> = {
|
||||
id: 'github_merge_pr',
|
||||
name: 'GitHub Merge Pull Request',
|
||||
description: 'Merge a pull request in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
commit_title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Title for the merge commit',
|
||||
},
|
||||
commit_message: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Extra detail to append to merge commit message',
|
||||
},
|
||||
merge_method: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge method: merge, squash, or rebase',
|
||||
default: 'merge',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/merge`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.commit_title !== undefined) body.commit_title = params.commit_title
|
||||
if (params.commit_message !== undefined) body.commit_message = params.commit_message
|
||||
if (params.merge_method !== undefined) body.merge_method = params.merge_method
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 405) {
|
||||
const error = await response.json()
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Pull request is not mergeable',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
sha: '',
|
||||
merged: false,
|
||||
message: error.message || 'Pull request is not mergeable',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
const content = `PR merged successfully!
|
||||
SHA: ${result.sha}
|
||||
Message: ${result.message}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
sha: result.sha,
|
||||
merged: result.merged,
|
||||
message: result.message,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable merge confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Merge result metadata',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Merge commit SHA' },
|
||||
merged: { type: 'boolean', description: 'Whether merge was successful' },
|
||||
message: { type: 'string', description: 'Response message' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -47,17 +47,14 @@ export const prTool: ToolConfig<PROperationParams, PullRequestResponse> = {
|
||||
transformResponse: async (response) => {
|
||||
const pr = await response.json()
|
||||
|
||||
// Fetch the PR diff
|
||||
const diffResponse = await fetch(pr.diff_url)
|
||||
const _diff = await diffResponse.text()
|
||||
|
||||
// Fetch files changed
|
||||
const filesResponse = await fetch(
|
||||
`https://api.github.com/repos/${pr.base.repo.owner.login}/${pr.base.repo.name}/pulls/${pr.number}/files`
|
||||
)
|
||||
const files = await filesResponse.json()
|
||||
|
||||
// Create a human-readable content string
|
||||
const content = `PR #${pr.number}: "${pr.title}" (${pr.state}) - Created: ${pr.created_at}, Updated: ${pr.updated_at}
|
||||
Description: ${pr.body || 'No description'}
|
||||
Files changed: ${files.length}
|
||||
|
||||
102
apps/sim/tools/github/remove_label.ts
Normal file
102
apps/sim/tools/github/remove_label.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { LabelsResponse, RemoveLabelParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const removeLabelTool: ToolConfig<RemoveLabelParams, LabelsResponse> = {
|
||||
id: 'github_remove_label',
|
||||
name: 'GitHub Remove Label',
|
||||
description: 'Remove a label from an issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Label name to remove',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/labels/${encodeURIComponent(params.name)}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!params) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Missing parameters',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
labels: [],
|
||||
issue_number: 0,
|
||||
html_url: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const labelsData = await response.json()
|
||||
|
||||
const labels = labelsData.map((label: any) => label.name)
|
||||
|
||||
const content = `Label "${params.name}" removed from issue #${params.issue_number}
|
||||
${labels.length > 0 ? `Remaining labels: ${labels.join(', ')}` : 'No labels remaining on this issue'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
labels,
|
||||
issue_number: params.issue_number,
|
||||
html_url: `https://github.com/${params.owner}/${params.repo}/issues/${params.issue_number}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable label removal confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Remaining labels metadata',
|
||||
properties: {
|
||||
labels: { type: 'array', description: 'Labels remaining on the issue after removal' },
|
||||
issue_number: { type: 'number', description: 'Issue number' },
|
||||
html_url: { type: 'string', description: 'GitHub issue URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -42,7 +42,6 @@ export const repoInfoTool: ToolConfig<BaseGitHubParams, RepoInfoResponse> = {
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
// Create a human-readable content string
|
||||
const content = `Repository: ${data.name}
|
||||
Description: ${data.description || 'No description'}
|
||||
Language: ${data.language || 'Not specified'}
|
||||
|
||||
147
apps/sim/tools/github/request_reviewers.ts
Normal file
147
apps/sim/tools/github/request_reviewers.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import type { RequestReviewersParams, ReviewersResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const requestReviewersTool: ToolConfig<RequestReviewersParams, ReviewersResponse> = {
|
||||
id: 'github_request_reviewers',
|
||||
name: 'GitHub Request Reviewers',
|
||||
description: 'Request reviewers for a pull request',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
reviewers: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of user logins to request reviews from',
|
||||
},
|
||||
team_reviewers: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of team slugs to request reviews from',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}/requested_reviewers`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const reviewersArray = params.reviewers
|
||||
.split(',')
|
||||
.map((r) => r.trim())
|
||||
.filter((r) => r)
|
||||
const body: Record<string, any> = {
|
||||
reviewers: reviewersArray,
|
||||
}
|
||||
if (params.team_reviewers) {
|
||||
const teamReviewersArray = params.team_reviewers
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter((t) => t)
|
||||
if (teamReviewersArray.length > 0) {
|
||||
body.team_reviewers = teamReviewersArray
|
||||
}
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const pr = await response.json()
|
||||
|
||||
const reviewers = pr.requested_reviewers || []
|
||||
const teams = pr.requested_teams || []
|
||||
|
||||
const reviewersList = reviewers.map((r: any) => r.login).join(', ')
|
||||
const teamsList = teams.map((t: any) => t.name).join(', ')
|
||||
|
||||
let content = `Review requested for PR #${pr.number}
|
||||
Reviewers: ${reviewersList || 'None'}`
|
||||
|
||||
if (teamsList) {
|
||||
content += `
|
||||
Team Reviewers: ${teamsList}`
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
requested_reviewers: reviewers.map((r: any) => ({
|
||||
login: r.login,
|
||||
id: r.id,
|
||||
})),
|
||||
requested_teams: teams.length
|
||||
? teams.map((t: any) => ({
|
||||
name: t.name,
|
||||
id: t.id,
|
||||
}))
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable reviewer request confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Requested reviewers metadata',
|
||||
properties: {
|
||||
requested_reviewers: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewer users',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'User login' },
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
requested_teams: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewer teams',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
id: { type: 'number', description: 'Team ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
102
apps/sim/tools/github/rerun_workflow.ts
Normal file
102
apps/sim/tools/github/rerun_workflow.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { RerunWorkflowParams, RerunWorkflowResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rerunWorkflowTool: ToolConfig<RerunWorkflowParams, RerunWorkflowResponse> = {
|
||||
id: 'github_rerun_workflow',
|
||||
name: 'GitHub Rerun Workflow',
|
||||
description:
|
||||
'Rerun a workflow run. Optionally enable debug logging for the rerun. Returns 201 Created on success.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
run_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Workflow run ID to rerun',
|
||||
},
|
||||
enable_debug_logging: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable debug logging for the rerun (default: false)',
|
||||
default: false,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/runs/${params.run_id}/rerun`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
...(params.enable_debug_logging !== undefined && {
|
||||
enable_debug_logging: params.enable_debug_logging,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!params) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Missing parameters',
|
||||
output: {
|
||||
content: '',
|
||||
metadata: {
|
||||
run_id: 0,
|
||||
status: 'error',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const content = `Workflow run #${params.run_id} has been queued for rerun.${params.enable_debug_logging ? '\nDebug logging is enabled for this rerun.' : ''}
|
||||
The rerun should start shortly.`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
run_id: params.run_id,
|
||||
status: 'rerun_initiated',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Rerun confirmation message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Rerun metadata',
|
||||
properties: {
|
||||
run_id: { type: 'number', description: 'Workflow run ID' },
|
||||
status: { type: 'string', description: 'Rerun status (rerun_initiated)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
85
apps/sim/tools/github/trigger_workflow.ts
Normal file
85
apps/sim/tools/github/trigger_workflow.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { TriggerWorkflowParams, TriggerWorkflowResponse } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const triggerWorkflowTool: ToolConfig<TriggerWorkflowParams, TriggerWorkflowResponse> = {
|
||||
id: 'github_trigger_workflow',
|
||||
name: 'GitHub Trigger Workflow',
|
||||
description:
|
||||
'Trigger a workflow dispatch event for a GitHub Actions workflow. The workflow must have a workflow_dispatch trigger configured. Returns 204 No Content on success.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
workflow_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Workflow ID (number) or workflow filename (e.g., "main.yaml")',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Git reference (branch or tag name) to run the workflow on',
|
||||
},
|
||||
inputs: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Input keys and values configured in the workflow file',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/actions/workflows/${params.workflow_id}/dispatches`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
ref: params.ref,
|
||||
...(params.inputs && { inputs: params.inputs }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const content = `Workflow dispatched successfully on ref: ${response.url.includes('ref') ? 'requested ref' : 'default branch'}
|
||||
The workflow run should start shortly.`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Confirmation message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Empty metadata object (204 No Content response)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -28,6 +28,62 @@ export interface LatestCommitParams extends BaseGitHubParams {
|
||||
branch?: string
|
||||
}
|
||||
|
||||
// Create PR parameters
|
||||
export interface CreatePRParams extends BaseGitHubParams {
|
||||
title: string
|
||||
head: string
|
||||
base: string
|
||||
body?: string
|
||||
draft?: boolean
|
||||
}
|
||||
|
||||
// Update PR parameters
|
||||
export interface UpdatePRParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
title?: string
|
||||
body?: string
|
||||
state?: 'open' | 'closed'
|
||||
base?: string
|
||||
}
|
||||
|
||||
// Merge PR parameters
|
||||
export interface MergePRParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
commit_title?: string
|
||||
commit_message?: string
|
||||
merge_method?: 'merge' | 'squash' | 'rebase'
|
||||
}
|
||||
|
||||
// List PRs parameters
|
||||
export interface ListPRsParams extends BaseGitHubParams {
|
||||
state?: 'open' | 'closed' | 'all'
|
||||
head?: string
|
||||
base?: string
|
||||
sort?: 'created' | 'updated' | 'popularity' | 'long-running'
|
||||
direction?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
// Get PR files parameters
|
||||
export interface GetPRFilesParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
// Close PR parameters
|
||||
export interface ClosePRParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
}
|
||||
|
||||
// Request reviewers parameters
|
||||
export interface RequestReviewersParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
reviewers: string
|
||||
team_reviewers?: string
|
||||
}
|
||||
|
||||
// Response metadata interfaces
|
||||
interface BasePRMetadata {
|
||||
number: number
|
||||
@@ -119,6 +175,64 @@ interface RepoMetadata {
|
||||
language: string
|
||||
}
|
||||
|
||||
// PR operation response metadata
|
||||
interface PRMetadata {
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
html_url: string
|
||||
merged: boolean
|
||||
draft: boolean
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
merge_commit_sha?: string
|
||||
}
|
||||
|
||||
interface MergeResultMetadata {
|
||||
sha: string
|
||||
merged: boolean
|
||||
message: string
|
||||
}
|
||||
|
||||
interface PRListMetadata {
|
||||
prs: Array<{
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
html_url: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}>
|
||||
total_count: number
|
||||
open_count?: number
|
||||
closed_count?: number
|
||||
}
|
||||
|
||||
interface PRFilesListMetadata {
|
||||
files: Array<{
|
||||
filename: string
|
||||
status: string
|
||||
additions: number
|
||||
deletions: number
|
||||
changes: number
|
||||
patch?: string
|
||||
blob_url: string
|
||||
raw_url: string
|
||||
}>
|
||||
total_count: number
|
||||
}
|
||||
|
||||
interface ReviewersMetadata {
|
||||
requested_reviewers: Array<{
|
||||
login: string
|
||||
id: number
|
||||
}>
|
||||
requested_teams?: Array<{
|
||||
name: string
|
||||
id: number
|
||||
}>
|
||||
}
|
||||
|
||||
// Response types
|
||||
export interface PullRequestResponse extends ToolResponse {
|
||||
output: {
|
||||
@@ -148,8 +262,771 @@ export interface RepoInfoResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue comment operation parameters
|
||||
export interface CreateIssueCommentParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
body: string
|
||||
}
|
||||
|
||||
export interface ListIssueCommentsParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
since?: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface UpdateCommentParams extends BaseGitHubParams {
|
||||
comment_id: number
|
||||
body: string
|
||||
}
|
||||
|
||||
export interface DeleteCommentParams extends BaseGitHubParams {
|
||||
comment_id: number
|
||||
}
|
||||
|
||||
export interface ListPRCommentsParams extends BaseGitHubParams {
|
||||
pullNumber: number
|
||||
sort?: 'created' | 'updated'
|
||||
direction?: 'asc' | 'desc'
|
||||
since?: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
// Branch operation parameters
|
||||
export interface ListBranchesParams extends BaseGitHubParams {
|
||||
protected?: boolean
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface GetBranchParams extends BaseGitHubParams {
|
||||
branch: string
|
||||
}
|
||||
|
||||
export interface CreateBranchParams extends BaseGitHubParams {
|
||||
branch: string
|
||||
sha: string // commit SHA to point to
|
||||
}
|
||||
|
||||
export interface DeleteBranchParams extends BaseGitHubParams {
|
||||
branch: string
|
||||
}
|
||||
|
||||
export interface GetBranchProtectionParams extends BaseGitHubParams {
|
||||
branch: string
|
||||
}
|
||||
|
||||
export interface UpdateBranchProtectionParams extends BaseGitHubParams {
|
||||
branch: string
|
||||
required_status_checks: {
|
||||
strict: boolean
|
||||
contexts: string[]
|
||||
} | null
|
||||
enforce_admins: boolean
|
||||
required_pull_request_reviews: {
|
||||
required_approving_review_count?: number
|
||||
dismiss_stale_reviews?: boolean
|
||||
require_code_owner_reviews?: boolean
|
||||
} | null
|
||||
restrictions: {
|
||||
users: string[]
|
||||
teams: string[]
|
||||
} | null
|
||||
}
|
||||
|
||||
// Issue comment response metadata
|
||||
interface IssueCommentMetadata {
|
||||
id: number
|
||||
html_url: string
|
||||
body: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
user: {
|
||||
login: string
|
||||
id: number
|
||||
}
|
||||
}
|
||||
|
||||
interface CommentsListMetadata {
|
||||
comments: Array<{
|
||||
id: number
|
||||
body: string
|
||||
user: { login: string }
|
||||
created_at: string
|
||||
html_url: string
|
||||
}>
|
||||
total_count: number
|
||||
}
|
||||
|
||||
// Response types for new tools
|
||||
export interface IssueCommentResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: IssueCommentMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommentsListResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: CommentsListMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteCommentResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
comment_id: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New PR operation response types
|
||||
export interface PRResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: PRMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface MergeResultResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: MergeResultMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface PRListResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: PRListMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface PRFilesListResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: PRFilesListMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReviewersResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: ReviewersMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// Branch response metadata
|
||||
interface BranchMetadata {
|
||||
name: string
|
||||
commit: {
|
||||
sha: string
|
||||
url: string
|
||||
}
|
||||
protected: boolean
|
||||
}
|
||||
|
||||
interface BranchListMetadata {
|
||||
branches: Array<{
|
||||
name: string
|
||||
commit: {
|
||||
sha: string
|
||||
url: string
|
||||
}
|
||||
protected: boolean
|
||||
}>
|
||||
total_count: number
|
||||
}
|
||||
|
||||
interface BranchProtectionMetadata {
|
||||
required_status_checks: {
|
||||
strict: boolean
|
||||
contexts: string[]
|
||||
} | null
|
||||
enforce_admins: {
|
||||
enabled: boolean
|
||||
}
|
||||
required_pull_request_reviews: {
|
||||
required_approving_review_count: number
|
||||
dismiss_stale_reviews: boolean
|
||||
require_code_owner_reviews: boolean
|
||||
} | null
|
||||
restrictions: {
|
||||
users: string[]
|
||||
teams: string[]
|
||||
} | null
|
||||
}
|
||||
|
||||
interface RefMetadata {
|
||||
ref: string
|
||||
url: string
|
||||
sha: string
|
||||
}
|
||||
|
||||
interface DeleteBranchMetadata {
|
||||
deleted: boolean
|
||||
branch: string
|
||||
}
|
||||
|
||||
// Branch response types
|
||||
export interface BranchResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: BranchMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface BranchListResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: BranchListMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface BranchProtectionResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: BranchProtectionMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface RefResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: RefMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteBranchResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: DeleteBranchMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// GitHub Projects V2 parameters
|
||||
export interface ListProjectsParams {
|
||||
owner_type: 'org' | 'user'
|
||||
owner_login: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface GetProjectParams {
|
||||
owner_type: 'org' | 'user'
|
||||
owner_login: string
|
||||
project_number: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface CreateProjectParams {
|
||||
owner_id: string // Node ID
|
||||
title: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface UpdateProjectParams {
|
||||
project_id: string // Node ID
|
||||
title?: string
|
||||
shortDescription?: string
|
||||
project_public?: boolean
|
||||
closed?: boolean
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface DeleteProjectParams {
|
||||
project_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
// GitHub Projects V2 response metadata
|
||||
interface ProjectMetadata {
|
||||
id: string
|
||||
title: string
|
||||
number?: number
|
||||
url: string
|
||||
closed?: boolean
|
||||
public?: boolean
|
||||
shortDescription?: string
|
||||
}
|
||||
|
||||
// GitHub Projects V2 response types
|
||||
export interface ListProjectsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
projects: ProjectMetadata[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProjectResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: ProjectMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// Workflow operation parameters
|
||||
export interface ListWorkflowsParams extends BaseGitHubParams {
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface GetWorkflowParams extends BaseGitHubParams {
|
||||
workflow_id: number | string
|
||||
}
|
||||
|
||||
export interface TriggerWorkflowParams extends BaseGitHubParams {
|
||||
workflow_id: number | string
|
||||
ref: string // branch or tag name
|
||||
inputs?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface ListWorkflowRunsParams extends BaseGitHubParams {
|
||||
actor?: string
|
||||
branch?: string
|
||||
event?: string
|
||||
status?: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface GetWorkflowRunParams extends BaseGitHubParams {
|
||||
run_id: number
|
||||
}
|
||||
|
||||
export interface CancelWorkflowRunParams extends BaseGitHubParams {
|
||||
run_id: number
|
||||
}
|
||||
|
||||
export interface RerunWorkflowParams extends BaseGitHubParams {
|
||||
run_id: number
|
||||
enable_debug_logging?: boolean
|
||||
}
|
||||
|
||||
// Workflow response metadata interfaces
|
||||
interface WorkflowMetadata {
|
||||
id: number
|
||||
name: string
|
||||
path: string
|
||||
state: string
|
||||
badge_url: string
|
||||
}
|
||||
|
||||
interface WorkflowRunMetadata {
|
||||
id: number
|
||||
name: string
|
||||
status: string
|
||||
conclusion: string
|
||||
html_url: string
|
||||
run_number: number
|
||||
}
|
||||
|
||||
interface ListWorkflowsMetadata {
|
||||
total_count: number
|
||||
workflows: Array<{
|
||||
id: number
|
||||
name: string
|
||||
path: string
|
||||
state: string
|
||||
badge_url: string
|
||||
}>
|
||||
}
|
||||
|
||||
interface ListWorkflowRunsMetadata {
|
||||
total_count: number
|
||||
workflow_runs: Array<{
|
||||
id: number
|
||||
name: string
|
||||
status: string
|
||||
conclusion: string
|
||||
html_url: string
|
||||
run_number: number
|
||||
}>
|
||||
}
|
||||
|
||||
// Workflow response types
|
||||
export interface WorkflowResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: WorkflowMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface WorkflowRunResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: WorkflowRunMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface ListWorkflowsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: ListWorkflowsMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface ListWorkflowRunsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: ListWorkflowRunsMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface TriggerWorkflowResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: Record<string, never>
|
||||
}
|
||||
}
|
||||
|
||||
export interface CancelWorkflowRunResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
run_id: number
|
||||
status: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface RerunWorkflowResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
run_id: number
|
||||
status: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type GitHubResponse =
|
||||
| PullRequestResponse
|
||||
| CreateCommentResponse
|
||||
| LatestCommitResponse
|
||||
| RepoInfoResponse
|
||||
| IssueCommentResponse
|
||||
| CommentsListResponse
|
||||
| DeleteCommentResponse
|
||||
| PRResponse
|
||||
| MergeResultResponse
|
||||
| PRListResponse
|
||||
| PRFilesListResponse
|
||||
| ReviewersResponse
|
||||
| ListProjectsResponse
|
||||
| ProjectResponse
|
||||
| BranchResponse
|
||||
| BranchListResponse
|
||||
| BranchProtectionResponse
|
||||
| RefResponse
|
||||
| DeleteBranchResponse
|
||||
| WorkflowResponse
|
||||
| WorkflowRunResponse
|
||||
| ListWorkflowsResponse
|
||||
| ListWorkflowRunsResponse
|
||||
| TriggerWorkflowResponse
|
||||
| CancelWorkflowRunResponse
|
||||
| RerunWorkflowResponse
|
||||
| IssueResponse
|
||||
| IssuesListResponse
|
||||
| LabelsResponse
|
||||
|
||||
// Release operation parameters
|
||||
export interface CreateReleaseParams extends BaseGitHubParams {
|
||||
tag_name: string
|
||||
target_commitish?: string
|
||||
name?: string
|
||||
body?: string
|
||||
draft?: boolean
|
||||
prerelease?: boolean
|
||||
}
|
||||
|
||||
export interface UpdateReleaseParams extends BaseGitHubParams {
|
||||
release_id: number
|
||||
tag_name?: string
|
||||
target_commitish?: string
|
||||
name?: string
|
||||
body?: string
|
||||
draft?: boolean
|
||||
prerelease?: boolean
|
||||
}
|
||||
|
||||
export interface ListReleasesParams extends BaseGitHubParams {
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface GetReleaseParams extends BaseGitHubParams {
|
||||
release_id: number
|
||||
}
|
||||
|
||||
export interface DeleteReleaseParams extends BaseGitHubParams {
|
||||
release_id: number
|
||||
}
|
||||
|
||||
// Release metadata interface
|
||||
interface ReleaseMetadata {
|
||||
id: number
|
||||
tag_name: string
|
||||
name: string
|
||||
html_url: string
|
||||
tarball_url: string
|
||||
zipball_url: string
|
||||
draft: boolean
|
||||
prerelease: boolean
|
||||
created_at: string
|
||||
published_at: string
|
||||
}
|
||||
|
||||
// Response types for releases
|
||||
export interface ReleaseResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: ReleaseMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface ListReleasesResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
releases: Array<ReleaseMetadata>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteReleaseResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
release_id: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue operation parameters
|
||||
export interface CreateIssueParams extends BaseGitHubParams {
|
||||
title: string
|
||||
body?: string
|
||||
assignees?: string
|
||||
labels?: string
|
||||
milestone?: number
|
||||
}
|
||||
|
||||
export interface UpdateIssueParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
title?: string
|
||||
body?: string
|
||||
state?: 'open' | 'closed'
|
||||
labels?: string[]
|
||||
assignees?: string[]
|
||||
}
|
||||
|
||||
export interface ListIssuesParams extends BaseGitHubParams {
|
||||
state?: 'open' | 'closed' | 'all'
|
||||
assignee?: string
|
||||
creator?: string
|
||||
labels?: string
|
||||
sort?: 'created' | 'updated' | 'comments'
|
||||
direction?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface GetIssueParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
}
|
||||
|
||||
export interface CloseIssueParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
state_reason?: 'completed' | 'not_planned'
|
||||
}
|
||||
|
||||
export interface AddLabelsParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
labels: string
|
||||
}
|
||||
|
||||
export interface RemoveLabelParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface AddAssigneesParams extends BaseGitHubParams {
|
||||
issue_number: number
|
||||
assignees: string
|
||||
}
|
||||
|
||||
// Issue response metadata
|
||||
interface IssueMetadata {
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
html_url: string
|
||||
labels: string[]
|
||||
assignees: string[]
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
closed_at?: string
|
||||
body?: string
|
||||
}
|
||||
|
||||
interface IssuesListMetadata {
|
||||
issues: Array<{
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
html_url: string
|
||||
labels: string[]
|
||||
assignees: string[]
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}>
|
||||
total_count: number
|
||||
page?: number
|
||||
}
|
||||
|
||||
interface LabelsMetadata {
|
||||
labels: string[]
|
||||
issue_number: number
|
||||
html_url: string
|
||||
}
|
||||
|
||||
// Issue response types
|
||||
export interface IssueResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: IssueMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface IssuesListResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: IssuesListMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface LabelsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: LabelsMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface GetFileContentParams extends BaseGitHubParams {
|
||||
path: string
|
||||
ref?: string // branch, tag, or commit SHA
|
||||
}
|
||||
|
||||
export interface CreateFileParams extends BaseGitHubParams {
|
||||
path: string
|
||||
message: string
|
||||
content: string // Plain text (will be Base64 encoded internally)
|
||||
branch?: string
|
||||
}
|
||||
|
||||
export interface UpdateFileParams extends BaseGitHubParams {
|
||||
path: string
|
||||
message: string
|
||||
content: string // Plain text (will be Base64 encoded internally)
|
||||
sha: string // Required for update
|
||||
branch?: string
|
||||
}
|
||||
|
||||
export interface DeleteFileParams extends BaseGitHubParams {
|
||||
path: string
|
||||
message: string
|
||||
sha: string // Required
|
||||
branch?: string
|
||||
}
|
||||
|
||||
export interface GetTreeParams extends BaseGitHubParams {
|
||||
path?: string
|
||||
ref?: string
|
||||
}
|
||||
|
||||
// File/Content metadata interfaces
|
||||
export interface FileContentMetadata {
|
||||
name: string
|
||||
path: string
|
||||
sha: string
|
||||
size: number
|
||||
type: 'file' | 'dir'
|
||||
download_url?: string
|
||||
html_url?: string
|
||||
}
|
||||
|
||||
export interface FileCommitMetadata {
|
||||
sha: string
|
||||
message: string
|
||||
author: {
|
||||
name: string
|
||||
email: string
|
||||
date: string
|
||||
}
|
||||
committer: {
|
||||
name: string
|
||||
email: string
|
||||
date: string
|
||||
}
|
||||
html_url: string
|
||||
}
|
||||
|
||||
export interface TreeItemMetadata {
|
||||
name: string
|
||||
path: string
|
||||
sha: string
|
||||
size: number
|
||||
type: 'file' | 'dir' | 'symlink' | 'submodule'
|
||||
download_url?: string
|
||||
html_url?: string
|
||||
}
|
||||
|
||||
// Response types
|
||||
export interface FileContentResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: FileContentMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface FileOperationResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
file: FileContentMetadata
|
||||
commit: FileCommitMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteFileResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
path: string
|
||||
commit: FileCommitMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface TreeResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
path: string
|
||||
items: TreeItemMetadata[]
|
||||
total_count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
221
apps/sim/tools/github/update_branch_protection.ts
Normal file
221
apps/sim/tools/github/update_branch_protection.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
import type { BranchProtectionResponse, UpdateBranchProtectionParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateBranchProtectionTool: ToolConfig<
|
||||
UpdateBranchProtectionParams,
|
||||
BranchProtectionResponse
|
||||
> = {
|
||||
id: 'github_update_branch_protection',
|
||||
name: 'GitHub Update Branch Protection',
|
||||
description:
|
||||
'Update branch protection rules for a specific branch, including status checks, review requirements, admin enforcement, and push restrictions.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch name',
|
||||
},
|
||||
required_status_checks: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Required status check configuration (null to disable). Object with strict (boolean) and contexts (string array)',
|
||||
},
|
||||
enforce_admins: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to enforce restrictions for administrators',
|
||||
},
|
||||
required_pull_request_reviews: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'PR review requirements (null to disable). Object with optional required_approving_review_count, dismiss_stale_reviews, require_code_owner_reviews',
|
||||
},
|
||||
restrictions: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Push restrictions (null to disable). Object with users (string array) and teams (string array)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/branches/${params.branch}/protection`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
required_status_checks: params.required_status_checks,
|
||||
enforce_admins: params.enforce_admins,
|
||||
required_pull_request_reviews: params.required_pull_request_reviews,
|
||||
restrictions: params.restrictions,
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const protection = await response.json()
|
||||
|
||||
let content = `Branch Protection updated successfully for "${protection.url.split('/branches/')[1].split('/protection')[0]}":
|
||||
|
||||
Enforce Admins: ${protection.enforce_admins?.enabled ? 'Yes' : 'No'}`
|
||||
|
||||
if (protection.required_status_checks) {
|
||||
content += `\n\nRequired Status Checks:
|
||||
- Strict: ${protection.required_status_checks.strict}
|
||||
- Contexts: ${protection.required_status_checks.contexts.length > 0 ? protection.required_status_checks.contexts.join(', ') : 'None'}`
|
||||
} else {
|
||||
content += '\n\nRequired Status Checks: Disabled'
|
||||
}
|
||||
|
||||
if (protection.required_pull_request_reviews) {
|
||||
content += `\n\nRequired Pull Request Reviews:
|
||||
- Required Approving Reviews: ${protection.required_pull_request_reviews.required_approving_review_count || 0}
|
||||
- Dismiss Stale Reviews: ${protection.required_pull_request_reviews.dismiss_stale_reviews ? 'Yes' : 'No'}
|
||||
- Require Code Owner Reviews: ${protection.required_pull_request_reviews.require_code_owner_reviews ? 'Yes' : 'No'}`
|
||||
} else {
|
||||
content += '\n\nRequired Pull Request Reviews: Disabled'
|
||||
}
|
||||
|
||||
if (protection.restrictions) {
|
||||
const users = protection.restrictions.users?.map((u: any) => u.login) || []
|
||||
const teams = protection.restrictions.teams?.map((t: any) => t.slug) || []
|
||||
content += `\n\nRestrictions:
|
||||
- Users: ${users.length > 0 ? users.join(', ') : 'None'}
|
||||
- Teams: ${teams.length > 0 ? teams.join(', ') : 'None'}`
|
||||
} else {
|
||||
content += '\n\nRestrictions: Disabled'
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
required_status_checks: protection.required_status_checks
|
||||
? {
|
||||
strict: protection.required_status_checks.strict,
|
||||
contexts: protection.required_status_checks.contexts,
|
||||
}
|
||||
: null,
|
||||
enforce_admins: {
|
||||
enabled: protection.enforce_admins?.enabled || false,
|
||||
},
|
||||
required_pull_request_reviews: protection.required_pull_request_reviews
|
||||
? {
|
||||
required_approving_review_count:
|
||||
protection.required_pull_request_reviews.required_approving_review_count || 0,
|
||||
dismiss_stale_reviews:
|
||||
protection.required_pull_request_reviews.dismiss_stale_reviews || false,
|
||||
require_code_owner_reviews:
|
||||
protection.required_pull_request_reviews.require_code_owner_reviews || false,
|
||||
}
|
||||
: null,
|
||||
restrictions: protection.restrictions
|
||||
? {
|
||||
users: protection.restrictions.users?.map((u: any) => u.login) || [],
|
||||
teams: protection.restrictions.teams?.map((t: any) => t.slug) || [],
|
||||
}
|
||||
: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable branch protection update summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated branch protection configuration',
|
||||
properties: {
|
||||
required_status_checks: {
|
||||
type: 'object',
|
||||
description: 'Status check requirements (null if disabled)',
|
||||
properties: {
|
||||
strict: { type: 'boolean', description: 'Require branches to be up to date' },
|
||||
contexts: {
|
||||
type: 'array',
|
||||
description: 'Required status check contexts',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
enforce_admins: {
|
||||
type: 'object',
|
||||
description: 'Admin enforcement settings',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', description: 'Enforce for administrators' },
|
||||
},
|
||||
},
|
||||
required_pull_request_reviews: {
|
||||
type: 'object',
|
||||
description: 'Pull request review requirements (null if disabled)',
|
||||
properties: {
|
||||
required_approving_review_count: {
|
||||
type: 'number',
|
||||
description: 'Number of approving reviews required',
|
||||
},
|
||||
dismiss_stale_reviews: {
|
||||
type: 'boolean',
|
||||
description: 'Dismiss stale pull request approvals',
|
||||
},
|
||||
require_code_owner_reviews: {
|
||||
type: 'boolean',
|
||||
description: 'Require review from code owners',
|
||||
},
|
||||
},
|
||||
},
|
||||
restrictions: {
|
||||
type: 'object',
|
||||
description: 'Push restrictions (null if disabled)',
|
||||
properties: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'Users who can push',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams who can push',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
103
apps/sim/tools/github/update_comment.ts
Normal file
103
apps/sim/tools/github/update_comment.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { IssueCommentResponse, UpdateCommentParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateCommentTool: ToolConfig<UpdateCommentParams, IssueCommentResponse> = {
|
||||
id: 'github_update_comment',
|
||||
name: 'GitHub Comment Updater',
|
||||
description: 'Update an existing comment on a GitHub issue or pull request',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
comment_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated comment content',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
body: params.body,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Comment #${data.id} updated: "${data.body.substring(0, 100)}${data.body.length > 100 ? '...' : ''}"`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
body: data.body,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
user: {
|
||||
login: data.user.login,
|
||||
id: data.user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable update confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated comment metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Comment ID' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
body: { type: 'string', description: 'Updated comment body' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User who created the comment',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'User login' },
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
179
apps/sim/tools/github/update_file.ts
Normal file
179
apps/sim/tools/github/update_file.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import type { FileOperationResponse, UpdateFileParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateFileTool: ToolConfig<UpdateFileParams, FileOperationResponse> = {
|
||||
id: 'github_update_file',
|
||||
name: 'GitHub Update File',
|
||||
description:
|
||||
'Update an existing file in a GitHub repository. Requires the file SHA. Content will be automatically Base64 encoded. Supports files up to 1MB.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Path to the file to update (e.g., "src/index.ts")',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Commit message for this file update',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New file content (plain text, will be Base64 encoded automatically)',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The blob SHA of the file being replaced (get from github_get_file_content)',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch to update the file in (defaults to repository default branch)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/contents/${params.path}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const base64Content = Buffer.from(params.content).toString('base64')
|
||||
|
||||
const body: Record<string, any> = {
|
||||
message: params.message,
|
||||
content: base64Content,
|
||||
sha: params.sha, // Required for update
|
||||
}
|
||||
|
||||
if (params.branch) {
|
||||
body.branch = params.branch
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `File updated successfully!
|
||||
|
||||
Path: ${data.content.path}
|
||||
Name: ${data.content.name}
|
||||
Size: ${data.content.size} bytes
|
||||
New SHA: ${data.content.sha}
|
||||
|
||||
Commit:
|
||||
- SHA: ${data.commit.sha}
|
||||
- Message: ${data.commit.message}
|
||||
- Author: ${data.commit.author.name}
|
||||
- Date: ${data.commit.author.date}
|
||||
|
||||
View file: ${data.content.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
file: {
|
||||
name: data.content.name,
|
||||
path: data.content.path,
|
||||
sha: data.content.sha,
|
||||
size: data.content.size,
|
||||
type: data.content.type,
|
||||
download_url: data.content.download_url,
|
||||
html_url: data.content.html_url,
|
||||
},
|
||||
commit: {
|
||||
sha: data.commit.sha,
|
||||
message: data.commit.message,
|
||||
author: {
|
||||
name: data.commit.author.name,
|
||||
email: data.commit.author.email,
|
||||
date: data.commit.author.date,
|
||||
},
|
||||
committer: {
|
||||
name: data.commit.committer.name,
|
||||
email: data.commit.committer.email,
|
||||
date: data.commit.committer.date,
|
||||
},
|
||||
html_url: data.commit.html_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable file update confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated file and commit metadata',
|
||||
properties: {
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'Updated file information',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
path: { type: 'string', description: 'Full path in repository' },
|
||||
sha: { type: 'string', description: 'New git blob SHA' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
type: { type: 'string', description: 'Content type' },
|
||||
download_url: { type: 'string', description: 'Direct download URL' },
|
||||
html_url: { type: 'string', description: 'GitHub web UI URL' },
|
||||
},
|
||||
},
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit information',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Author information',
|
||||
},
|
||||
committer: {
|
||||
type: 'object',
|
||||
description: 'Committer information',
|
||||
},
|
||||
html_url: { type: 'string', description: 'Commit URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
139
apps/sim/tools/github/update_issue.ts
Normal file
139
apps/sim/tools/github/update_issue.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import type { IssueResponse, UpdateIssueParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateIssueTool: ToolConfig<UpdateIssueParams, IssueResponse> = {
|
||||
id: 'github_update_issue',
|
||||
name: 'GitHub Update Issue',
|
||||
description: 'Update an existing issue in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New issue title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New issue description/body',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue state (open or closed)',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of label names (replaces all existing labels)',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of usernames (replaces all existing assignees)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
if (params.title !== undefined) body.title = params.title
|
||||
if (params.body !== undefined) body.body = params.body
|
||||
if (params.state !== undefined) body.state = params.state
|
||||
if (params.labels !== undefined) body.labels = params.labels
|
||||
if (params.assignees !== undefined) body.assignees = params.assignees
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
const labels = issue.labels?.map((label: any) => label.name) || []
|
||||
|
||||
const assignees = issue.assignees?.map((assignee: any) => assignee.login) || []
|
||||
|
||||
const content = `Issue #${issue.number} updated: "${issue.title}"
|
||||
State: ${issue.state}
|
||||
URL: ${issue.html_url}
|
||||
${labels.length > 0 ? `Labels: ${labels.join(', ')}` : ''}
|
||||
${assignees.length > 0 ? `Assignees: ${assignees.join(', ')}` : ''}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
html_url: issue.html_url,
|
||||
labels,
|
||||
assignees,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
closed_at: issue.closed_at,
|
||||
body: issue.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable issue update confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated issue metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
state: { type: 'string', description: 'Issue state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
labels: { type: 'array', description: 'Array of label names' },
|
||||
assignees: { type: 'array', description: 'Array of assignee usernames' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
closed_at: { type: 'string', description: 'Closed timestamp' },
|
||||
body: { type: 'string', description: 'Issue body/description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
121
apps/sim/tools/github/update_pr.ts
Normal file
121
apps/sim/tools/github/update_pr.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { PRResponse, UpdatePRParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updatePRTool: ToolConfig<UpdatePRParams, PRResponse> = {
|
||||
id: 'github_update_pr',
|
||||
name: 'GitHub Update Pull Request',
|
||||
description: 'Update an existing pull request in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
pullNumber: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New pull request description (Markdown)',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New state (open or closed)',
|
||||
},
|
||||
base: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New base branch name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/pulls/${params.pullNumber}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.title !== undefined) body.title = params.title
|
||||
if (params.body !== undefined) body.body = params.body
|
||||
if (params.state !== undefined) body.state = params.state
|
||||
if (params.base !== undefined) body.base = params.base
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const pr = await response.json()
|
||||
|
||||
const content = `PR #${pr.number} updated: "${pr.title}" (${pr.state})
|
||||
URL: ${pr.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
state: pr.state,
|
||||
html_url: pr.html_url,
|
||||
merged: pr.merged,
|
||||
draft: pr.draft,
|
||||
created_at: pr.created_at,
|
||||
updated_at: pr.updated_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable PR update confirmation' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated pull request metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Pull request number' },
|
||||
title: { type: 'string', description: 'PR title' },
|
||||
state: { type: 'string', description: 'PR state (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
merged: { type: 'boolean', description: 'Whether PR is merged' },
|
||||
draft: { type: 'boolean', description: 'Whether PR is draft' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
192
apps/sim/tools/github/update_project.ts
Normal file
192
apps/sim/tools/github/update_project.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import type { ProjectResponse, UpdateProjectParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateProjectTool: ToolConfig<UpdateProjectParams, ProjectResponse> = {
|
||||
id: 'github_update_project',
|
||||
name: 'GitHub Update Project',
|
||||
description:
|
||||
'Update an existing GitHub Project V2. Can update title, description, visibility (public), or status (closed). Requires the project Node ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
project_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project Node ID (format: PVT_...)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New project title',
|
||||
},
|
||||
shortDescription: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New project short description',
|
||||
},
|
||||
project_public: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Set project visibility (true = public, false = private)',
|
||||
},
|
||||
closed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Set project status (true = closed, false = open)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with project write permissions',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.github.com/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const inputFields: string[] = ['projectId: $projectId']
|
||||
const variables: Record<string, any> = {
|
||||
projectId: params.project_id,
|
||||
}
|
||||
|
||||
if (params.title !== undefined) {
|
||||
inputFields.push('title: $title')
|
||||
variables.title = params.title
|
||||
}
|
||||
if (params.shortDescription !== undefined) {
|
||||
inputFields.push('shortDescription: $shortDescription')
|
||||
variables.shortDescription = params.shortDescription
|
||||
}
|
||||
if (params.project_public !== undefined) {
|
||||
inputFields.push('public: $project_public')
|
||||
variables.project_public = params.project_public
|
||||
}
|
||||
if (params.closed !== undefined) {
|
||||
inputFields.push('closed: $closed')
|
||||
variables.closed = params.closed
|
||||
}
|
||||
|
||||
const variableDefs = ['$projectId: ID!']
|
||||
if (params.title !== undefined) variableDefs.push('$title: String')
|
||||
if (params.shortDescription !== undefined) variableDefs.push('$shortDescription: String')
|
||||
if (params.project_public !== undefined) variableDefs.push('$project_public: Boolean')
|
||||
if (params.closed !== undefined) variableDefs.push('$closed: Boolean')
|
||||
|
||||
const query = `
|
||||
mutation(${variableDefs.join(', ')}) {
|
||||
updateProjectV2(input: {
|
||||
${inputFields.join('\n ')}
|
||||
}) {
|
||||
projectV2 {
|
||||
id
|
||||
title
|
||||
number
|
||||
url
|
||||
closed
|
||||
public
|
||||
shortDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
return {
|
||||
query,
|
||||
variables,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: `GraphQL Error: ${data.errors[0].message}`,
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: data.errors[0].message,
|
||||
}
|
||||
}
|
||||
|
||||
const project = data.data?.updateProjectV2?.projectV2
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
content: 'Failed to update project',
|
||||
metadata: {
|
||||
id: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
error: 'Failed to update project',
|
||||
}
|
||||
}
|
||||
|
||||
let content = `Project updated successfully!\n`
|
||||
content += `Title: ${project.title}\n`
|
||||
content += `ID: ${project.id}\n`
|
||||
content += `Number: ${project.number}\n`
|
||||
content += `URL: ${project.url}\n`
|
||||
content += `Status: ${project.closed ? 'Closed' : 'Open'}\n`
|
||||
content += `Visibility: ${project.public ? 'Public' : 'Private'}\n`
|
||||
if (project.shortDescription) {
|
||||
content += `Description: ${project.shortDescription}`
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: content.trim(),
|
||||
metadata: {
|
||||
id: project.id,
|
||||
title: project.title,
|
||||
number: project.number,
|
||||
url: project.url,
|
||||
closed: project.closed,
|
||||
public: project.public,
|
||||
shortDescription: project.shortDescription || '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable confirmation message' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated project metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project node ID' },
|
||||
title: { type: 'string', description: 'Project title' },
|
||||
number: { type: 'number', description: 'Project number', optional: true },
|
||||
url: { type: 'string', description: 'Project URL' },
|
||||
closed: { type: 'boolean', description: 'Whether project is closed', optional: true },
|
||||
public: { type: 'boolean', description: 'Whether project is public', optional: true },
|
||||
shortDescription: {
|
||||
type: 'string',
|
||||
description: 'Project short description',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
161
apps/sim/tools/github/update_release.ts
Normal file
161
apps/sim/tools/github/update_release.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import type { ReleaseResponse, UpdateReleaseParams } from '@/tools/github/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateReleaseTool: ToolConfig<UpdateReleaseParams, ReleaseResponse> = {
|
||||
id: 'github_update_release',
|
||||
name: 'GitHub Update Release',
|
||||
description:
|
||||
'Update an existing GitHub release. Modify tag name, target commit, title, description, draft status, or prerelease status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner (user or organization)',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
release_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier of the release',
|
||||
},
|
||||
tag_name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the tag',
|
||||
},
|
||||
target_commitish: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Specifies the commitish value for where the tag is created from',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the release',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Text describing the contents of the release (markdown supported)',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'true to set as draft, false to publish',
|
||||
},
|
||||
prerelease: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'true to identify as a prerelease, false for a full release',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/releases/${params.release_id}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
|
||||
if (params.tag_name) {
|
||||
body.tag_name = params.tag_name
|
||||
}
|
||||
if (params.target_commitish) {
|
||||
body.target_commitish = params.target_commitish
|
||||
}
|
||||
if (params.name !== undefined) {
|
||||
body.name = params.name
|
||||
}
|
||||
if (params.body !== undefined) {
|
||||
body.body = params.body
|
||||
}
|
||||
if (params.draft !== undefined) {
|
||||
body.draft = params.draft
|
||||
}
|
||||
if (params.prerelease !== undefined) {
|
||||
body.prerelease = params.prerelease
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const releaseType = data.draft ? 'Draft' : data.prerelease ? 'Prerelease' : 'Release'
|
||||
const content = `${releaseType} updated: "${data.name || data.tag_name}"
|
||||
Tag: ${data.tag_name}
|
||||
URL: ${data.html_url}
|
||||
Last updated: ${data.updated_at || data.created_at}
|
||||
${data.published_at ? `Published: ${data.published_at}` : 'Not yet published'}
|
||||
Download URLs:
|
||||
- Tarball: ${data.tarball_url}
|
||||
- Zipball: ${data.zipball_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
tag_name: data.tag_name,
|
||||
name: data.name || data.tag_name,
|
||||
html_url: data.html_url,
|
||||
tarball_url: data.tarball_url,
|
||||
zipball_url: data.zipball_url,
|
||||
draft: data.draft,
|
||||
prerelease: data.prerelease,
|
||||
created_at: data.created_at,
|
||||
published_at: data.published_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable release update summary' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated release metadata including download URLs',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Release ID' },
|
||||
tag_name: { type: 'string', description: 'Git tag name' },
|
||||
name: { type: 'string', description: 'Release name' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL for the release' },
|
||||
tarball_url: { type: 'string', description: 'URL to download release as tarball' },
|
||||
zipball_url: { type: 'string', description: 'URL to download release as zipball' },
|
||||
draft: { type: 'boolean', description: 'Whether this is a draft release' },
|
||||
prerelease: { type: 'boolean', description: 'Whether this is a prerelease' },
|
||||
created_at: { type: 'string', description: 'Creation timestamp' },
|
||||
published_at: { type: 'string', description: 'Publication timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -27,10 +27,58 @@ import { fileParseTool } from '@/tools/file'
|
||||
import { crawlTool, scrapeTool, searchTool } from '@/tools/firecrawl'
|
||||
import { functionExecuteTool } from '@/tools/function'
|
||||
import {
|
||||
githubAddAssigneesTool,
|
||||
githubAddLabelsTool,
|
||||
githubCancelWorkflowRunTool,
|
||||
githubCloseIssueTool,
|
||||
githubClosePRTool,
|
||||
githubCommentTool,
|
||||
githubCreateBranchTool,
|
||||
githubCreateFileTool,
|
||||
githubCreateIssueTool,
|
||||
githubCreatePRTool,
|
||||
githubCreateProjectTool,
|
||||
githubCreateReleaseTool,
|
||||
githubDeleteBranchTool,
|
||||
githubDeleteCommentTool,
|
||||
githubDeleteFileTool,
|
||||
githubDeleteProjectTool,
|
||||
githubDeleteReleaseTool,
|
||||
githubGetBranchProtectionTool,
|
||||
githubGetBranchTool,
|
||||
githubGetFileContentTool,
|
||||
githubGetIssueTool,
|
||||
githubGetPRFilesTool,
|
||||
githubGetProjectTool,
|
||||
githubGetReleaseTool,
|
||||
githubGetTreeTool,
|
||||
githubGetWorkflowRunTool,
|
||||
githubGetWorkflowTool,
|
||||
githubIssueCommentTool,
|
||||
githubLatestCommitTool,
|
||||
githubListBranchesTool,
|
||||
githubListIssueCommentsTool,
|
||||
githubListIssuesTool,
|
||||
githubListPRCommentsTool,
|
||||
githubListPRsTool,
|
||||
githubListProjectsTool,
|
||||
githubListReleasesTool,
|
||||
githubListWorkflowRunsTool,
|
||||
githubListWorkflowsTool,
|
||||
githubMergePRTool,
|
||||
githubPrTool,
|
||||
githubRemoveLabelTool,
|
||||
githubRepoInfoTool,
|
||||
githubRequestReviewersTool,
|
||||
githubRerunWorkflowTool,
|
||||
githubTriggerWorkflowTool,
|
||||
githubUpdateBranchProtectionTool,
|
||||
githubUpdateCommentTool,
|
||||
githubUpdateFileTool,
|
||||
githubUpdateIssueTool,
|
||||
githubUpdatePRTool,
|
||||
githubUpdateProjectTool,
|
||||
githubUpdateReleaseTool,
|
||||
} from '@/tools/github'
|
||||
import {
|
||||
gmailAddLabelTool,
|
||||
@@ -391,6 +439,54 @@ export const tools: Record<string, ToolConfig> = {
|
||||
mysql_execute: mysqlExecuteTool,
|
||||
github_pr: githubPrTool,
|
||||
github_comment: githubCommentTool,
|
||||
github_issue_comment: githubIssueCommentTool,
|
||||
github_list_issue_comments: githubListIssueCommentsTool,
|
||||
github_update_comment: githubUpdateCommentTool,
|
||||
github_delete_comment: githubDeleteCommentTool,
|
||||
github_list_pr_comments: githubListPRCommentsTool,
|
||||
github_create_pr: githubCreatePRTool,
|
||||
github_update_pr: githubUpdatePRTool,
|
||||
github_merge_pr: githubMergePRTool,
|
||||
github_list_prs: githubListPRsTool,
|
||||
github_get_pr_files: githubGetPRFilesTool,
|
||||
github_close_pr: githubClosePRTool,
|
||||
github_request_reviewers: githubRequestReviewersTool,
|
||||
github_get_file_content: githubGetFileContentTool,
|
||||
github_create_file: githubCreateFileTool,
|
||||
github_update_file: githubUpdateFileTool,
|
||||
github_delete_file: githubDeleteFileTool,
|
||||
github_get_tree: githubGetTreeTool,
|
||||
github_list_branches: githubListBranchesTool,
|
||||
github_get_branch: githubGetBranchTool,
|
||||
github_create_branch: githubCreateBranchTool,
|
||||
github_delete_branch: githubDeleteBranchTool,
|
||||
github_get_branch_protection: githubGetBranchProtectionTool,
|
||||
github_update_branch_protection: githubUpdateBranchProtectionTool,
|
||||
github_create_issue: githubCreateIssueTool,
|
||||
github_update_issue: githubUpdateIssueTool,
|
||||
github_list_issues: githubListIssuesTool,
|
||||
github_get_issue: githubGetIssueTool,
|
||||
github_close_issue: githubCloseIssueTool,
|
||||
github_add_labels: githubAddLabelsTool,
|
||||
github_remove_label: githubRemoveLabelTool,
|
||||
github_add_assignees: githubAddAssigneesTool,
|
||||
github_create_release: githubCreateReleaseTool,
|
||||
github_update_release: githubUpdateReleaseTool,
|
||||
github_list_releases: githubListReleasesTool,
|
||||
github_get_release: githubGetReleaseTool,
|
||||
github_delete_release: githubDeleteReleaseTool,
|
||||
github_list_workflows: githubListWorkflowsTool,
|
||||
github_get_workflow: githubGetWorkflowTool,
|
||||
github_trigger_workflow: githubTriggerWorkflowTool,
|
||||
github_list_workflow_runs: githubListWorkflowRunsTool,
|
||||
github_get_workflow_run: githubGetWorkflowRunTool,
|
||||
github_cancel_workflow_run: githubCancelWorkflowRunTool,
|
||||
github_rerun_workflow: githubRerunWorkflowTool,
|
||||
github_list_projects: githubListProjectsTool,
|
||||
github_get_project: githubGetProjectTool,
|
||||
github_create_project: githubCreateProjectTool,
|
||||
github_update_project: githubUpdateProjectTool,
|
||||
github_delete_project: githubDeleteProjectTool,
|
||||
exa_search: exaSearchTool,
|
||||
exa_get_contents: exaGetContentsTool,
|
||||
exa_find_similar_links: exaFindSimilarLinksTool,
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
export { githubIssueClosedTrigger } from './issue_closed'
|
||||
export { githubIssueCommentTrigger } from './issue_comment'
|
||||
export { githubIssueOpenedTrigger } from './issue_opened'
|
||||
export { githubPRClosedTrigger } from './pr_closed'
|
||||
export { githubPRCommentTrigger } from './pr_comment'
|
||||
export { githubPRMergedTrigger } from './pr_merged'
|
||||
export { githubPROpenedTrigger } from './pr_opened'
|
||||
export { githubPRReviewedTrigger } from './pr_reviewed'
|
||||
export { githubPushTrigger } from './push'
|
||||
export { githubReleasePublishedTrigger } from './release_published'
|
||||
export { githubWebhookTrigger } from './webhook'
|
||||
export { githubWorkflowRunTrigger } from './workflow_run'
|
||||
|
||||
401
apps/sim/triggers/github/issue_closed.ts
Normal file
401
apps/sim/triggers/github/issue_closed.ts
Normal file
@@ -0,0 +1,401 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubIssueClosedTrigger: TriggerConfig = {
|
||||
id: 'github_issue_closed',
|
||||
name: 'GitHub Issue Closed',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when an issue is closed in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>issues</strong> (<strong>closed</strong> action).',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'closed',
|
||||
issue: {
|
||||
id: 1234567890,
|
||||
number: 123,
|
||||
title: 'Bug: Application crashes on startup',
|
||||
body: 'When I try to start the application, it immediately crashes with error code 500.',
|
||||
state: 'closed',
|
||||
state_reason: 'completed',
|
||||
html_url: 'https://github.com/owner/repo/issues/123',
|
||||
user: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
user_type: 'User',
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
name: 'bug',
|
||||
color: 'd73a4a',
|
||||
},
|
||||
],
|
||||
assignees: [],
|
||||
created_at: '2025-01-15T10:30:00Z',
|
||||
updated_at: '2025-01-15T14:20:00Z',
|
||||
closed_at: '2025-01-15T14:20:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
avatar_url: 'https://github.com/images/error/owner.gif',
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'maintainer',
|
||||
id: 2,
|
||||
avatar_url: 'https://github.com/images/error/maintainer.gif',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
||||
},
|
||||
issue: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Issue ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Issue node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Issue body/description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue state (open, closed)',
|
||||
},
|
||||
state_reason: {
|
||||
type: 'string',
|
||||
description: 'Reason for state (completed, not_planned, reopened)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue HTML URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
milestone: {
|
||||
type: 'object',
|
||||
description: 'Milestone object if assigned',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Issue creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Issue last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Issue closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'issues',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
409
apps/sim/triggers/github/issue_comment.ts
Normal file
409
apps/sim/triggers/github/issue_comment.ts
Normal file
@@ -0,0 +1,409 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubIssueCommentTrigger: TriggerConfig = {
|
||||
id: 'github_issue_comment',
|
||||
name: 'GitHub Issue Comment',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a comment is added to an issue (not pull requests)',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>issue_comment</strong> (<strong>created, edited, deleted</strong> actions).',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
'<strong>Note:</strong> This trigger filters for issue comments only. For PR comments, use the "GitHub PR Comment" trigger.',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'created',
|
||||
issue: {
|
||||
number: 123,
|
||||
title: 'Bug: Application crashes on startup',
|
||||
state: 'open',
|
||||
html_url: 'https://github.com/owner/repo/issues/123',
|
||||
user: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
id: 987654321,
|
||||
body: 'I can confirm this bug. It happens on my machine too.',
|
||||
html_url: 'https://github.com/owner/repo/issues/123#issuecomment-987654321',
|
||||
user: {
|
||||
login: 'commenter',
|
||||
id: 3,
|
||||
avatar_url: 'https://github.com/images/error/commenter.gif',
|
||||
user_type: 'User',
|
||||
},
|
||||
created_at: '2025-01-15T11:00:00Z',
|
||||
updated_at: '2025-01-15T11:00:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'commenter',
|
||||
id: 3,
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (created, edited, deleted)',
|
||||
},
|
||||
issue: {
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue state (open, closed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue HTML URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Comment node ID',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Comment text',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Comment HTML URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Comment creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Comment last update timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'issue_comment',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
420
apps/sim/triggers/github/issue_opened.ts
Normal file
420
apps/sim/triggers/github/issue_opened.ts
Normal file
@@ -0,0 +1,420 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubIssueOpenedTrigger: TriggerConfig = {
|
||||
id: 'github_issue_opened',
|
||||
name: 'GitHub Issue Opened',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a new issue is opened in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'selectedTriggerId',
|
||||
title: 'Trigger Type',
|
||||
type: 'dropdown',
|
||||
mode: 'trigger',
|
||||
options: [
|
||||
{ label: 'Issue Opened', id: 'github_issue_opened' },
|
||||
{ label: 'Issue Closed', id: 'github_issue_closed' },
|
||||
{ label: 'Issue Comment', id: 'github_issue_comment' },
|
||||
{ label: 'PR Opened', id: 'github_pr_opened' },
|
||||
{ label: 'PR Closed', id: 'github_pr_closed' },
|
||||
{ label: 'PR Merged', id: 'github_pr_merged' },
|
||||
{ label: 'PR Comment', id: 'github_pr_comment' },
|
||||
{ label: 'PR Reviewed', id: 'github_pr_reviewed' },
|
||||
{ label: 'Code Push', id: 'github_push' },
|
||||
{ label: 'Release Published', id: 'github_release_published' },
|
||||
{ label: 'Actions Workflow Run', id: 'github_workflow_run' },
|
||||
],
|
||||
value: () => 'github_issue_opened',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>issues</strong> (<strong>opened</strong> action).',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'opened',
|
||||
issue: {
|
||||
id: 1234567890,
|
||||
number: 123,
|
||||
title: 'Bug: Application crashes on startup',
|
||||
body: 'When I try to start the application, it immediately crashes with error code 500.',
|
||||
state: 'open',
|
||||
html_url: 'https://github.com/owner/repo/issues/123',
|
||||
user: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
user_type: 'User',
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
name: 'bug',
|
||||
color: 'd73a4a',
|
||||
},
|
||||
],
|
||||
assignees: [],
|
||||
created_at: '2025-01-15T10:30:00Z',
|
||||
updated_at: '2025-01-15T10:30:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
avatar_url: 'https://github.com/images/error/owner.gif',
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
||||
},
|
||||
issue: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Issue ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Issue node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Issue body/description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue state (open, closed)',
|
||||
},
|
||||
state_reason: {
|
||||
type: 'string',
|
||||
description: 'Reason for state (completed, not_planned, reopened)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue HTML URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
milestone: {
|
||||
type: 'object',
|
||||
description: 'Milestone object if assigned',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Issue creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Issue last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Issue closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'issues',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
518
apps/sim/triggers/github/pr_closed.ts
Normal file
518
apps/sim/triggers/github/pr_closed.ts
Normal file
@@ -0,0 +1,518 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPRClosedTrigger: TriggerConfig = {
|
||||
id: 'github_pr_closed',
|
||||
name: 'GitHub PR Closed',
|
||||
provider: 'github',
|
||||
description:
|
||||
'Trigger workflow when a pull request is closed without being merged (e.g., abandoned) in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>pull_request</strong> (<strong>closed</strong> action).',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'closed',
|
||||
number: 42,
|
||||
pull_request: {
|
||||
id: 1234567890,
|
||||
number: 42,
|
||||
title: 'Add new feature',
|
||||
body: 'This PR adds a new feature that improves performance.',
|
||||
state: 'closed',
|
||||
merged: false,
|
||||
draft: false,
|
||||
html_url: 'https://github.com/owner/repo/pull/42',
|
||||
diff_url: 'https://github.com/owner/repo/pull/42.diff',
|
||||
patch_url: 'https://github.com/owner/repo/pull/42.patch',
|
||||
user: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
user_type: 'User',
|
||||
},
|
||||
head: {
|
||||
ref: 'feature-branch',
|
||||
sha: 'abc123def456',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: 'main',
|
||||
sha: '789ghi012jkl',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
additions: 245,
|
||||
deletions: 67,
|
||||
changed_files: 12,
|
||||
labels: [],
|
||||
assignees: [],
|
||||
requested_reviewers: [
|
||||
{
|
||||
login: 'reviewer1',
|
||||
id: 6,
|
||||
},
|
||||
],
|
||||
created_at: '2025-01-15T12:00:00Z',
|
||||
updated_at: '2025-01-15T14:30:00Z',
|
||||
closed_at: '2025-01-15T14:30:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
pull_request: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Pull request ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Pull request node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Pull request description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
merged: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR was merged',
|
||||
},
|
||||
merged_at: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when PR was merged',
|
||||
},
|
||||
merged_by: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR is a draft',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Source branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Source repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Source repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Target branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Target repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Target repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
additions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines added',
|
||||
},
|
||||
deletions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines deleted',
|
||||
},
|
||||
changed_files: {
|
||||
type: 'number',
|
||||
description: 'Number of files changed',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
requested_reviewers: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewers',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'pull_request',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
450
apps/sim/triggers/github/pr_comment.ts
Normal file
450
apps/sim/triggers/github/pr_comment.ts
Normal file
@@ -0,0 +1,450 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPRCommentTrigger: TriggerConfig = {
|
||||
id: 'github_pr_comment',
|
||||
name: 'GitHub PR Comment',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a comment is added to a pull request in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>Issue comments</strong>.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
'Note: Pull request comments use the issue_comment event. You can filter for PR comments by checking if issue.pull_request exists.',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'created',
|
||||
issue: {
|
||||
id: 1234567890,
|
||||
number: 42,
|
||||
title: 'Add new feature',
|
||||
body: 'This PR adds a new feature that improves performance.',
|
||||
state: 'open',
|
||||
html_url: 'https://github.com/owner/repo/issues/42',
|
||||
user: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
node_id: 'MDQ6VXNlcjU=',
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
type: 'User',
|
||||
},
|
||||
labels: [],
|
||||
assignees: [],
|
||||
pull_request: {
|
||||
url: 'https://api.github.com/repos/owner/repo/pulls/42',
|
||||
html_url: 'https://github.com/owner/repo/pull/42',
|
||||
diff_url: 'https://github.com/owner/repo/pull/42.diff',
|
||||
patch_url: 'https://github.com/owner/repo/pull/42.patch',
|
||||
},
|
||||
created_at: '2025-01-15T12:00:00Z',
|
||||
updated_at: '2025-01-15T12:15:00Z',
|
||||
},
|
||||
comment: {
|
||||
id: 987654321,
|
||||
node_id: 'MDEyOklzc3VlQ29tbWVudDk4NzY1NDMyMQ==',
|
||||
url: 'https://api.github.com/repos/owner/repo/issues/comments/987654321',
|
||||
html_url: 'https://github.com/owner/repo/issues/42#issuecomment-987654321',
|
||||
body: 'Great work! This looks good to me.',
|
||||
user: {
|
||||
login: 'reviewer',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/reviewer.gif',
|
||||
html_url: 'https://github.com/reviewer',
|
||||
type: 'User',
|
||||
},
|
||||
created_at: '2025-01-15T12:15:00Z',
|
||||
updated_at: '2025-01-15T12:15:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjM0NTY=',
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
node_id: 'MDQ6VXNlcjc4OTA=',
|
||||
avatar_url: 'https://github.com/images/error/owner.gif',
|
||||
html_url: 'https://github.com/owner',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'reviewer',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/reviewer.gif',
|
||||
html_url: 'https://github.com/reviewer',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (created, edited, deleted)',
|
||||
},
|
||||
issue: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Issue ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Issue node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue/PR number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR state (open, closed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR HTML URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
pull_request: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Pull request API URL (present only for PR comments)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Issue/PR last update timestamp',
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Comment node ID',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Comment API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Comment HTML URL',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Comment text',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Comment creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Comment last update timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Owner node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'issue_comment',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
525
apps/sim/triggers/github/pr_merged.ts
Normal file
525
apps/sim/triggers/github/pr_merged.ts
Normal file
@@ -0,0 +1,525 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPRMergedTrigger: TriggerConfig = {
|
||||
id: 'github_pr_merged',
|
||||
name: 'GitHub PR Merged',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a pull request is successfully merged in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>pull_request</strong>. Note: Merged PRs have <strong>action=\'closed\'</strong> AND <strong>merged=true</strong>.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_merged',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'closed',
|
||||
number: 42,
|
||||
pull_request: {
|
||||
id: 1234567890,
|
||||
number: 42,
|
||||
title: 'Add new feature',
|
||||
body: 'This PR adds a new feature that improves performance.',
|
||||
state: 'closed',
|
||||
merged: true,
|
||||
merge_commit_sha: 'mno345pqr678',
|
||||
merged_at: '2025-01-15T13:30:00Z',
|
||||
merged_by: {
|
||||
login: 'maintainer',
|
||||
id: 8,
|
||||
avatar_url: 'https://github.com/images/error/maintainer.gif',
|
||||
html_url: 'https://github.com/maintainer',
|
||||
user_type: 'User',
|
||||
},
|
||||
draft: false,
|
||||
html_url: 'https://github.com/owner/repo/pull/42',
|
||||
diff_url: 'https://github.com/owner/repo/pull/42.diff',
|
||||
patch_url: 'https://github.com/owner/repo/pull/42.patch',
|
||||
user: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
user_type: 'User',
|
||||
},
|
||||
head: {
|
||||
ref: 'feature-branch',
|
||||
sha: 'abc123def456',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: 'main',
|
||||
sha: '789ghi012jkl',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
additions: 245,
|
||||
deletions: 67,
|
||||
changed_files: 12,
|
||||
labels: [],
|
||||
assignees: [],
|
||||
requested_reviewers: [
|
||||
{
|
||||
login: 'reviewer1',
|
||||
id: 6,
|
||||
},
|
||||
],
|
||||
created_at: '2025-01-15T12:00:00Z',
|
||||
updated_at: '2025-01-15T13:30:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
pull_request: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Pull request ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Pull request node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Pull request description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
merged: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR was merged',
|
||||
},
|
||||
merged_at: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when PR was merged',
|
||||
},
|
||||
merged_by: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR is a draft',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Source branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Source repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Source repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Target branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Target repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Target repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
additions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines added',
|
||||
},
|
||||
deletions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines deleted',
|
||||
},
|
||||
changed_files: {
|
||||
type: 'number',
|
||||
description: 'Number of files changed',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
requested_reviewers: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewers',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'pull_request',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
520
apps/sim/triggers/github/pr_opened.ts
Normal file
520
apps/sim/triggers/github/pr_opened.ts
Normal file
@@ -0,0 +1,520 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPROpenedTrigger: TriggerConfig = {
|
||||
id: 'github_pr_opened',
|
||||
name: 'GitHub PR Opened',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a new pull request is opened in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>pull_request</strong> (<strong>opened</strong> action).',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'opened',
|
||||
number: 42,
|
||||
pull_request: {
|
||||
id: 1234567890,
|
||||
number: 42,
|
||||
title: 'Add new feature',
|
||||
body: 'This PR adds a new feature that improves performance.',
|
||||
state: 'open',
|
||||
merged: false,
|
||||
draft: false,
|
||||
html_url: 'https://github.com/owner/repo/pull/42',
|
||||
diff_url: 'https://github.com/owner/repo/pull/42.diff',
|
||||
patch_url: 'https://github.com/owner/repo/pull/42.patch',
|
||||
user: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
user_type: 'User',
|
||||
},
|
||||
head: {
|
||||
ref: 'feature-branch',
|
||||
sha: 'abc123def456',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: 'main',
|
||||
sha: '789ghi012jkl',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
additions: 245,
|
||||
deletions: 67,
|
||||
changed_files: 12,
|
||||
labels: [],
|
||||
assignees: [],
|
||||
requested_reviewers: [
|
||||
{
|
||||
login: 'reviewer1',
|
||||
id: 6,
|
||||
},
|
||||
],
|
||||
created_at: '2025-01-15T12:00:00Z',
|
||||
updated_at: '2025-01-15T12:00:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
pull_request: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Pull request ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Pull request node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Pull request description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
merged: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR was merged',
|
||||
},
|
||||
merged_at: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when PR was merged',
|
||||
},
|
||||
merged_by: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR is a draft',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Source branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Source repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Source repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Target branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Target repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Target repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
additions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines added',
|
||||
},
|
||||
deletions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines deleted',
|
||||
},
|
||||
changed_files: {
|
||||
type: 'number',
|
||||
description: 'Number of files changed',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
requested_reviewers: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewers',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'pull_request',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
498
apps/sim/triggers/github/pr_reviewed.ts
Normal file
498
apps/sim/triggers/github/pr_reviewed.ts
Normal file
@@ -0,0 +1,498 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPRReviewedTrigger: TriggerConfig = {
|
||||
id: 'github_pr_reviewed',
|
||||
name: 'GitHub PR Reviewed',
|
||||
provider: 'github',
|
||||
description:
|
||||
'Trigger workflow when a pull request review is submitted, edited, or dismissed in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>Pull request reviews</strong>.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_reviewed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'submitted',
|
||||
review: {
|
||||
id: 80,
|
||||
node_id: 'MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=',
|
||||
user: {
|
||||
login: 'reviewer',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/reviewer.gif',
|
||||
html_url: 'https://github.com/reviewer',
|
||||
type: 'User',
|
||||
},
|
||||
body: 'This looks great! Nice work.',
|
||||
state: 'approved',
|
||||
html_url: 'https://github.com/owner/repo/pull/42#pullrequestreview-80',
|
||||
submitted_at: '2025-01-15T14:00:00Z',
|
||||
commit_id: 'abc123def456',
|
||||
author_association: 'COLLABORATOR',
|
||||
},
|
||||
pull_request: {
|
||||
id: 1234567890,
|
||||
number: 42,
|
||||
node_id: 'MDExOlB1bGxSZXF1ZXN0MQ==',
|
||||
title: 'Add new feature',
|
||||
body: 'This PR adds a new feature that improves performance.',
|
||||
state: 'open',
|
||||
merged: false,
|
||||
draft: false,
|
||||
html_url: 'https://github.com/owner/repo/pull/42',
|
||||
diff_url: 'https://github.com/owner/repo/pull/42.diff',
|
||||
patch_url: 'https://github.com/owner/repo/pull/42.patch',
|
||||
user: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
node_id: 'MDQ6VXNlcjU=',
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
type: 'User',
|
||||
},
|
||||
head: {
|
||||
ref: 'feature-branch',
|
||||
sha: 'abc123def456',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: 'main',
|
||||
sha: '789ghi012jkl',
|
||||
repo: {
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
},
|
||||
},
|
||||
created_at: '2025-01-15T12:00:00Z',
|
||||
updated_at: '2025-01-15T14:00:00Z',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjM0NTY=',
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
description: 'A sample repository',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
node_id: 'MDQ6VXNlcjc4OTA=',
|
||||
avatar_url: 'https://github.com/images/error/owner.gif',
|
||||
html_url: 'https://github.com/owner',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'reviewer',
|
||||
id: 6,
|
||||
node_id: 'MDQ6VXNlcjY=',
|
||||
avatar_url: 'https://github.com/images/error/reviewer.gif',
|
||||
html_url: 'https://github.com/reviewer',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (submitted, edited, dismissed)',
|
||||
},
|
||||
review: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Review ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Review node ID',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Reviewer username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Reviewer user ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Reviewer node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Reviewer avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Reviewer profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Review comment text',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Review state (approved, changes_requested, commented, dismissed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Review HTML URL',
|
||||
},
|
||||
submitted_at: {
|
||||
type: 'string',
|
||||
description: 'Review submission timestamp',
|
||||
},
|
||||
commit_id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA that was reviewed',
|
||||
},
|
||||
author_association: {
|
||||
type: 'string',
|
||||
description: 'Author association (OWNER, MEMBER, COLLABORATOR, CONTRIBUTOR, etc.)',
|
||||
},
|
||||
},
|
||||
pull_request: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Pull request ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Pull request node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Pull request description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
merged: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR was merged',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR is a draft',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
user: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'PR author username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'PR author user ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'PR author node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'PR author avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'PR author profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Source branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Source repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Source repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Target branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Target repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Target repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request last update timestamp',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Owner node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'pull_request_review',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
560
apps/sim/triggers/github/push.ts
Normal file
560
apps/sim/triggers/github/push.ts
Normal file
@@ -0,0 +1,560 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubPushTrigger: TriggerConfig = {
|
||||
id: 'github_push',
|
||||
name: 'GitHub Push',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when code is pushed to a repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>Pushes</strong>.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_push',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
ref: 'refs/heads/main',
|
||||
before: '0000000000000000000000000000000000000000',
|
||||
after: 'abc123def456789ghi012jkl345mno678pqr901',
|
||||
created: true,
|
||||
deleted: false,
|
||||
forced: false,
|
||||
base_ref: null,
|
||||
compare: 'https://github.com/owner/repo-name/compare/0000000000000000...abc123def456',
|
||||
commits: [
|
||||
{
|
||||
id: 'abc123def456789ghi012jkl345mno678pqr901',
|
||||
tree_id: 'tree123abc456def789ghi012jkl345mno678',
|
||||
distinct: true,
|
||||
message: 'Add new feature to improve performance',
|
||||
timestamp: '2025-01-15T12:00:00Z',
|
||||
url: 'https://github.com/owner/repo-name/commit/abc123def456789ghi012jkl345mno678pqr901',
|
||||
author: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
committer: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
added: ['src/features/new-feature.ts'],
|
||||
removed: [],
|
||||
modified: ['src/index.ts', 'README.md'],
|
||||
},
|
||||
{
|
||||
id: 'def456ghi789jkl012mno345pqr678stu901vwx',
|
||||
tree_id: 'tree456def789ghi012jkl345mno678pqr901',
|
||||
distinct: true,
|
||||
message: 'Update documentation',
|
||||
timestamp: '2025-01-15T12:15:00Z',
|
||||
url: 'https://github.com/owner/repo-name/commit/def456ghi789jkl012mno345pqr678stu901vwx',
|
||||
author: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
committer: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
added: [],
|
||||
removed: [],
|
||||
modified: ['docs/API.md'],
|
||||
},
|
||||
],
|
||||
head_commit: {
|
||||
id: 'def456ghi789jkl012mno345pqr678stu901vwx',
|
||||
tree_id: 'tree456def789ghi012jkl345mno678pqr901',
|
||||
distinct: true,
|
||||
message: 'Update documentation',
|
||||
timestamp: '2025-01-15T12:15:00Z',
|
||||
url: 'https://github.com/owner/repo-name/commit/def456ghi789jkl012mno345pqr678stu901vwx',
|
||||
author: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
committer: {
|
||||
name: 'Developer Name',
|
||||
email: 'developer@example.com',
|
||||
username: 'developer',
|
||||
},
|
||||
added: [],
|
||||
removed: [],
|
||||
modified: ['docs/API.md'],
|
||||
},
|
||||
pusher: {
|
||||
name: 'developer',
|
||||
email: 'developer@example.com',
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjM0NTY=',
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
private: false,
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository for demonstrating push events',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/owner/repo-name',
|
||||
homepage: 'https://example.com',
|
||||
size: 1024,
|
||||
stargazers_count: 42,
|
||||
watchers_count: 42,
|
||||
language: 'TypeScript',
|
||||
forks_count: 5,
|
||||
open_issues_count: 3,
|
||||
default_branch: 'main',
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
node_id: 'MDQ6VXNlcjc4OTA=',
|
||||
avatar_url: 'https://github.com/images/error/owner.gif',
|
||||
html_url: 'https://github.com/owner',
|
||||
owner_type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'developer',
|
||||
id: 5,
|
||||
node_id: 'MDQ6VXNlcjU=',
|
||||
avatar_url: 'https://github.com/images/error/developer.gif',
|
||||
html_url: 'https://github.com/developer',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Git reference that was pushed (e.g., refs/heads/main)',
|
||||
},
|
||||
before: {
|
||||
type: 'string',
|
||||
description: 'SHA of the commit before the push',
|
||||
},
|
||||
after: {
|
||||
type: 'string',
|
||||
description: 'SHA of the commit after the push',
|
||||
},
|
||||
created: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this push created a new branch or tag',
|
||||
},
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this push deleted a branch or tag',
|
||||
},
|
||||
forced: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this was a force push',
|
||||
},
|
||||
base_ref: {
|
||||
type: 'string',
|
||||
description: 'Base reference for the push',
|
||||
},
|
||||
compare: {
|
||||
type: 'string',
|
||||
description: 'URL to compare the changes',
|
||||
},
|
||||
commits: {
|
||||
type: 'array',
|
||||
description: 'Array of commit objects included in this push',
|
||||
items: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA',
|
||||
},
|
||||
tree_id: {
|
||||
type: 'string',
|
||||
description: 'Git tree SHA',
|
||||
},
|
||||
distinct: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this commit is distinct from others in the push',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Commit message',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
description: 'Commit timestamp',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Commit URL',
|
||||
},
|
||||
author: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Author name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Author email',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Author GitHub username',
|
||||
},
|
||||
},
|
||||
committer: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Committer name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Committer email',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Committer GitHub username',
|
||||
},
|
||||
},
|
||||
added: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths added in this commit',
|
||||
},
|
||||
removed: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths removed in this commit',
|
||||
},
|
||||
modified: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths modified in this commit',
|
||||
},
|
||||
},
|
||||
},
|
||||
head_commit: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA of the most recent commit',
|
||||
},
|
||||
tree_id: {
|
||||
type: 'string',
|
||||
description: 'Git tree SHA',
|
||||
},
|
||||
distinct: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this commit is distinct',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Commit message',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
description: 'Commit timestamp',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Commit URL',
|
||||
},
|
||||
author: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Author name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Author email',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Author GitHub username',
|
||||
},
|
||||
},
|
||||
committer: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Committer name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Committer email',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Committer GitHub username',
|
||||
},
|
||||
},
|
||||
added: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths added in this commit',
|
||||
},
|
||||
removed: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths removed in this commit',
|
||||
},
|
||||
modified: {
|
||||
type: 'array',
|
||||
description: 'Array of file paths modified in this commit',
|
||||
},
|
||||
},
|
||||
pusher: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Pusher name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Pusher email',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Owner node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'push',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
969
apps/sim/triggers/github/release_published.ts
Normal file
969
apps/sim/triggers/github/release_published.ts
Normal file
@@ -0,0 +1,969 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubReleasePublishedTrigger: TriggerConfig = {
|
||||
id: 'github_release_published',
|
||||
name: 'GitHub Release Published',
|
||||
provider: 'github',
|
||||
description: 'Trigger workflow when a new release is published in a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>Releases</strong> event.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_release_published',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'published',
|
||||
release: {
|
||||
id: 123456789,
|
||||
node_id: 'RE_kwDOABCDEF4HFGijkl',
|
||||
tag_name: 'v1.0.0',
|
||||
target_commitish: 'main',
|
||||
name: 'v1.0.0 - Initial Release',
|
||||
body: 'This is the first stable release of our project.\n\n## Features\n- Feature A\n- Feature B\n- Feature C\n\n## Bug Fixes\n- Fixed issue #123\n- Fixed issue #456',
|
||||
draft: false,
|
||||
prerelease: false,
|
||||
created_at: '2025-01-15T10:00:00Z',
|
||||
published_at: '2025-01-15T12:00:00Z',
|
||||
url: 'https://api.github.com/repos/owner/repo-name/releases/123456789',
|
||||
html_url: 'https://github.com/owner/repo-name/releases/tag/v1.0.0',
|
||||
assets_url: 'https://api.github.com/repos/owner/repo-name/releases/123456789/assets',
|
||||
upload_url:
|
||||
'https://uploads.github.com/repos/owner/repo-name/releases/123456789/assets{?name,label}',
|
||||
tarball_url: 'https://api.github.com/repos/owner/repo-name/tarball/v1.0.0',
|
||||
zipball_url: 'https://api.github.com/repos/owner/repo-name/zipball/v1.0.0',
|
||||
discussion_url: 'https://github.com/owner/repo-name/discussions/100',
|
||||
author: {
|
||||
login: 'releasemanager',
|
||||
id: 12345,
|
||||
node_id: 'MDQ6VXNlcjEyMzQ1',
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/12345?v=4',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/releasemanager',
|
||||
html_url: 'https://github.com/releasemanager',
|
||||
followers_url: 'https://api.github.com/users/releasemanager/followers',
|
||||
following_url: 'https://api.github.com/users/releasemanager/following{/other_user}',
|
||||
gists_url: 'https://api.github.com/users/releasemanager/gists{/gist_id}',
|
||||
starred_url: 'https://api.github.com/users/releasemanager/starred{/owner}{/repo}',
|
||||
subscriptions_url: 'https://api.github.com/users/releasemanager/subscriptions',
|
||||
organizations_url: 'https://api.github.com/users/releasemanager/orgs',
|
||||
repos_url: 'https://api.github.com/users/releasemanager/repos',
|
||||
events_url: 'https://api.github.com/users/releasemanager/events{/privacy}',
|
||||
received_events_url: 'https://api.github.com/users/releasemanager/received_events',
|
||||
user_type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
assets: [
|
||||
{
|
||||
id: 987654321,
|
||||
node_id: 'RA_kwDOABCDEF4DcXYZ',
|
||||
name: 'release-v1.0.0-linux-amd64.tar.gz',
|
||||
label: 'Linux AMD64 Binary',
|
||||
content_type: 'application/gzip',
|
||||
state: 'uploaded',
|
||||
size: 15728640,
|
||||
download_count: 42,
|
||||
created_at: '2025-01-15T11:30:00Z',
|
||||
updated_at: '2025-01-15T11:30:00Z',
|
||||
browser_download_url:
|
||||
'https://github.com/owner/repo-name/releases/download/v1.0.0/release-v1.0.0-linux-amd64.tar.gz',
|
||||
url: 'https://api.github.com/repos/owner/repo-name/releases/assets/987654321',
|
||||
uploader: {
|
||||
login: 'releasemanager',
|
||||
id: 12345,
|
||||
node_id: 'MDQ6VXNlcjEyMzQ1',
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/12345?v=4',
|
||||
html_url: 'https://github.com/releasemanager',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 987654322,
|
||||
node_id: 'RA_kwDOABCDEF4DcXYa',
|
||||
name: 'release-v1.0.0-darwin-amd64.tar.gz',
|
||||
label: 'macOS AMD64 Binary',
|
||||
content_type: 'application/gzip',
|
||||
state: 'uploaded',
|
||||
size: 14680064,
|
||||
download_count: 28,
|
||||
created_at: '2025-01-15T11:30:00Z',
|
||||
updated_at: '2025-01-15T11:30:00Z',
|
||||
browser_download_url:
|
||||
'https://github.com/owner/repo-name/releases/download/v1.0.0/release-v1.0.0-darwin-amd64.tar.gz',
|
||||
url: 'https://api.github.com/repos/owner/repo-name/releases/assets/987654322',
|
||||
uploader: {
|
||||
login: 'releasemanager',
|
||||
id: 12345,
|
||||
node_id: 'MDQ6VXNlcjEyMzQ1',
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/12345?v=4',
|
||||
html_url: 'https://github.com/releasemanager',
|
||||
user_type: 'User',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
repository: {
|
||||
id: 123456,
|
||||
node_id: 'R_kgDOABCDEF',
|
||||
name: 'repo-name',
|
||||
full_name: 'owner/repo-name',
|
||||
private: false,
|
||||
html_url: 'https://github.com/owner/repo-name',
|
||||
repo_description: 'A sample repository for demonstrating GitHub release webhooks',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/owner/repo-name',
|
||||
archive_url: 'https://api.github.com/repos/owner/repo-name/{archive_format}{/ref}',
|
||||
assignees_url: 'https://api.github.com/repos/owner/repo-name/assignees{/user}',
|
||||
blobs_url: 'https://api.github.com/repos/owner/repo-name/git/blobs{/sha}',
|
||||
branches_url: 'https://api.github.com/repos/owner/repo-name/branches{/branch}',
|
||||
collaborators_url:
|
||||
'https://api.github.com/repos/owner/repo-name/collaborators{/collaborator}',
|
||||
comments_url: 'https://api.github.com/repos/owner/repo-name/comments{/number}',
|
||||
commits_url: 'https://api.github.com/repos/owner/repo-name/commits{/sha}',
|
||||
compare_url: 'https://api.github.com/repos/owner/repo-name/compare/{base}...{head}',
|
||||
contents_url: 'https://api.github.com/repos/owner/repo-name/contents/{+path}',
|
||||
contributors_url: 'https://api.github.com/repos/owner/repo-name/contributors',
|
||||
deployments_url: 'https://api.github.com/repos/owner/repo-name/deployments',
|
||||
downloads_url: 'https://api.github.com/repos/owner/repo-name/downloads',
|
||||
events_url: 'https://api.github.com/repos/owner/repo-name/events',
|
||||
forks_url: 'https://api.github.com/repos/owner/repo-name/forks',
|
||||
git_commits_url: 'https://api.github.com/repos/owner/repo-name/git/commits{/sha}',
|
||||
git_refs_url: 'https://api.github.com/repos/owner/repo-name/git/refs{/sha}',
|
||||
git_tags_url: 'https://api.github.com/repos/owner/repo-name/git/tags{/sha}',
|
||||
hooks_url: 'https://api.github.com/repos/owner/repo-name/hooks',
|
||||
issue_comment_url:
|
||||
'https://api.github.com/repos/owner/repo-name/issues/comments{/number}',
|
||||
issue_events_url: 'https://api.github.com/repos/owner/repo-name/issues/events{/number}',
|
||||
issues_url: 'https://api.github.com/repos/owner/repo-name/issues{/number}',
|
||||
keys_url: 'https://api.github.com/repos/owner/repo-name/keys{/key_id}',
|
||||
labels_url: 'https://api.github.com/repos/owner/repo-name/labels{/name}',
|
||||
languages_url: 'https://api.github.com/repos/owner/repo-name/languages',
|
||||
merges_url: 'https://api.github.com/repos/owner/repo-name/merges',
|
||||
milestones_url: 'https://api.github.com/repos/owner/repo-name/milestones{/number}',
|
||||
notifications_url:
|
||||
'https://api.github.com/repos/owner/repo-name/notifications{?since,all,participating}',
|
||||
pulls_url: 'https://api.github.com/repos/owner/repo-name/pulls{/number}',
|
||||
releases_url: 'https://api.github.com/repos/owner/repo-name/releases{/id}',
|
||||
stargazers_url: 'https://api.github.com/repos/owner/repo-name/stargazers',
|
||||
statuses_url: 'https://api.github.com/repos/owner/repo-name/statuses/{sha}',
|
||||
subscribers_url: 'https://api.github.com/repos/owner/repo-name/subscribers',
|
||||
subscription_url: 'https://api.github.com/repos/owner/repo-name/subscription',
|
||||
tags_url: 'https://api.github.com/repos/owner/repo-name/tags',
|
||||
teams_url: 'https://api.github.com/repos/owner/repo-name/teams',
|
||||
trees_url: 'https://api.github.com/repos/owner/repo-name/git/trees{/sha}',
|
||||
homepage: 'https://example.com',
|
||||
size: 1024,
|
||||
stargazers_count: 350,
|
||||
watchers_count: 350,
|
||||
language: 'TypeScript',
|
||||
has_issues: true,
|
||||
has_projects: true,
|
||||
has_downloads: true,
|
||||
has_wiki: true,
|
||||
has_pages: false,
|
||||
forks_count: 42,
|
||||
mirror_url: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
open_issues_count: 12,
|
||||
license: {
|
||||
key: 'mit',
|
||||
name: 'MIT License',
|
||||
spdx_id: 'MIT',
|
||||
url: 'https://api.github.com/licenses/mit',
|
||||
node_id: 'MDc6TGljZW5zZTEz',
|
||||
},
|
||||
allow_forking: true,
|
||||
is_template: false,
|
||||
topics: ['javascript', 'typescript', 'nodejs'],
|
||||
visibility: 'public',
|
||||
forks: 42,
|
||||
open_issues: 12,
|
||||
watchers: 350,
|
||||
default_branch: 'main',
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
updated_at: '2025-01-15T12:00:00Z',
|
||||
pushed_at: '2025-01-15T11:45:00Z',
|
||||
owner: {
|
||||
login: 'owner',
|
||||
id: 7890,
|
||||
node_id: 'MDQ6VXNlcjc4OTA=',
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/7890?v=4',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/owner',
|
||||
html_url: 'https://github.com/owner',
|
||||
followers_url: 'https://api.github.com/users/owner/followers',
|
||||
following_url: 'https://api.github.com/users/owner/following{/other_user}',
|
||||
gists_url: 'https://api.github.com/users/owner/gists{/gist_id}',
|
||||
starred_url: 'https://api.github.com/users/owner/starred{/owner}{/repo}',
|
||||
subscriptions_url: 'https://api.github.com/users/owner/subscriptions',
|
||||
organizations_url: 'https://api.github.com/users/owner/orgs',
|
||||
repos_url: 'https://api.github.com/users/owner/repos',
|
||||
events_url: 'https://api.github.com/users/owner/events{/privacy}',
|
||||
received_events_url: 'https://api.github.com/users/owner/received_events',
|
||||
owner_type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'releasemanager',
|
||||
id: 12345,
|
||||
node_id: 'MDQ6VXNlcjEyMzQ1',
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/12345?v=4',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/releasemanager',
|
||||
html_url: 'https://github.com/releasemanager',
|
||||
followers_url: 'https://api.github.com/users/releasemanager/followers',
|
||||
following_url: 'https://api.github.com/users/releasemanager/following{/other_user}',
|
||||
gists_url: 'https://api.github.com/users/releasemanager/gists{/gist_id}',
|
||||
starred_url: 'https://api.github.com/users/releasemanager/starred{/owner}{/repo}',
|
||||
subscriptions_url: 'https://api.github.com/users/releasemanager/subscriptions',
|
||||
organizations_url: 'https://api.github.com/users/releasemanager/orgs',
|
||||
repos_url: 'https://api.github.com/users/releasemanager/repos',
|
||||
events_url: 'https://api.github.com/users/releasemanager/events{/privacy}',
|
||||
received_events_url: 'https://api.github.com/users/releasemanager/received_events',
|
||||
user_type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Action performed (published, unpublished, created, edited, deleted, prereleased, released)',
|
||||
},
|
||||
release: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Release ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Release node ID',
|
||||
},
|
||||
tag_name: {
|
||||
type: 'string',
|
||||
description: 'Git tag name for the release',
|
||||
},
|
||||
target_commitish: {
|
||||
type: 'string',
|
||||
description: 'Target branch or commit SHA',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Release name/title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Release description/notes in markdown format',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the release is a draft',
|
||||
},
|
||||
prerelease: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the release is a pre-release',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Release creation timestamp',
|
||||
},
|
||||
published_at: {
|
||||
type: 'string',
|
||||
description: 'Release publication timestamp',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Release API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Release HTML URL',
|
||||
},
|
||||
assets_url: {
|
||||
type: 'string',
|
||||
description: 'Release assets API URL',
|
||||
},
|
||||
upload_url: {
|
||||
type: 'string',
|
||||
description: 'URL for uploading release assets',
|
||||
},
|
||||
tarball_url: {
|
||||
type: 'string',
|
||||
description: 'Source code tarball download URL',
|
||||
},
|
||||
zipball_url: {
|
||||
type: 'string',
|
||||
description: 'Source code zipball download URL',
|
||||
},
|
||||
discussion_url: {
|
||||
type: 'string',
|
||||
description: 'Discussion URL if available',
|
||||
},
|
||||
author: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
gravatar_id: {
|
||||
type: 'string',
|
||||
description: 'Gravatar ID',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'User API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
followers_url: {
|
||||
type: 'string',
|
||||
description: 'Followers API URL',
|
||||
},
|
||||
following_url: {
|
||||
type: 'string',
|
||||
description: 'Following API URL',
|
||||
},
|
||||
gists_url: {
|
||||
type: 'string',
|
||||
description: 'Gists API URL',
|
||||
},
|
||||
starred_url: {
|
||||
type: 'string',
|
||||
description: 'Starred repositories API URL',
|
||||
},
|
||||
subscriptions_url: {
|
||||
type: 'string',
|
||||
description: 'Subscriptions API URL',
|
||||
},
|
||||
organizations_url: {
|
||||
type: 'string',
|
||||
description: 'Organizations API URL',
|
||||
},
|
||||
repos_url: {
|
||||
type: 'string',
|
||||
description: 'Repositories API URL',
|
||||
},
|
||||
events_url: {
|
||||
type: 'string',
|
||||
description: 'Events API URL',
|
||||
},
|
||||
received_events_url: {
|
||||
type: 'string',
|
||||
description: 'Received events API URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
site_admin: {
|
||||
type: 'boolean',
|
||||
description: 'Whether user is a site administrator',
|
||||
},
|
||||
},
|
||||
assets: {
|
||||
type: 'array',
|
||||
description: 'Array of release asset objects with download URLs',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
archive_url: {
|
||||
type: 'string',
|
||||
description: 'Archive API URL',
|
||||
},
|
||||
assignees_url: {
|
||||
type: 'string',
|
||||
description: 'Assignees API URL',
|
||||
},
|
||||
blobs_url: {
|
||||
type: 'string',
|
||||
description: 'Blobs API URL',
|
||||
},
|
||||
branches_url: {
|
||||
type: 'string',
|
||||
description: 'Branches API URL',
|
||||
},
|
||||
collaborators_url: {
|
||||
type: 'string',
|
||||
description: 'Collaborators API URL',
|
||||
},
|
||||
comments_url: {
|
||||
type: 'string',
|
||||
description: 'Comments API URL',
|
||||
},
|
||||
commits_url: {
|
||||
type: 'string',
|
||||
description: 'Commits API URL',
|
||||
},
|
||||
compare_url: {
|
||||
type: 'string',
|
||||
description: 'Compare API URL',
|
||||
},
|
||||
contents_url: {
|
||||
type: 'string',
|
||||
description: 'Contents API URL',
|
||||
},
|
||||
contributors_url: {
|
||||
type: 'string',
|
||||
description: 'Contributors API URL',
|
||||
},
|
||||
deployments_url: {
|
||||
type: 'string',
|
||||
description: 'Deployments API URL',
|
||||
},
|
||||
downloads_url: {
|
||||
type: 'string',
|
||||
description: 'Downloads API URL',
|
||||
},
|
||||
events_url: {
|
||||
type: 'string',
|
||||
description: 'Events API URL',
|
||||
},
|
||||
forks_url: {
|
||||
type: 'string',
|
||||
description: 'Forks API URL',
|
||||
},
|
||||
git_commits_url: {
|
||||
type: 'string',
|
||||
description: 'Git commits API URL',
|
||||
},
|
||||
git_refs_url: {
|
||||
type: 'string',
|
||||
description: 'Git refs API URL',
|
||||
},
|
||||
git_tags_url: {
|
||||
type: 'string',
|
||||
description: 'Git tags API URL',
|
||||
},
|
||||
hooks_url: {
|
||||
type: 'string',
|
||||
description: 'Hooks API URL',
|
||||
},
|
||||
issue_comment_url: {
|
||||
type: 'string',
|
||||
description: 'Issue comment API URL',
|
||||
},
|
||||
issue_events_url: {
|
||||
type: 'string',
|
||||
description: 'Issue events API URL',
|
||||
},
|
||||
issues_url: {
|
||||
type: 'string',
|
||||
description: 'Issues API URL',
|
||||
},
|
||||
keys_url: {
|
||||
type: 'string',
|
||||
description: 'Keys API URL',
|
||||
},
|
||||
labels_url: {
|
||||
type: 'string',
|
||||
description: 'Labels API URL',
|
||||
},
|
||||
languages_url: {
|
||||
type: 'string',
|
||||
description: 'Languages API URL',
|
||||
},
|
||||
merges_url: {
|
||||
type: 'string',
|
||||
description: 'Merges API URL',
|
||||
},
|
||||
milestones_url: {
|
||||
type: 'string',
|
||||
description: 'Milestones API URL',
|
||||
},
|
||||
notifications_url: {
|
||||
type: 'string',
|
||||
description: 'Notifications API URL',
|
||||
},
|
||||
pulls_url: {
|
||||
type: 'string',
|
||||
description: 'Pull requests API URL',
|
||||
},
|
||||
releases_url: {
|
||||
type: 'string',
|
||||
description: 'Releases API URL',
|
||||
},
|
||||
stargazers_url: {
|
||||
type: 'string',
|
||||
description: 'Stargazers API URL',
|
||||
},
|
||||
statuses_url: {
|
||||
type: 'string',
|
||||
description: 'Statuses API URL',
|
||||
},
|
||||
subscribers_url: {
|
||||
type: 'string',
|
||||
description: 'Subscribers API URL',
|
||||
},
|
||||
subscription_url: {
|
||||
type: 'string',
|
||||
description: 'Subscription API URL',
|
||||
},
|
||||
tags_url: {
|
||||
type: 'string',
|
||||
description: 'Tags API URL',
|
||||
},
|
||||
teams_url: {
|
||||
type: 'string',
|
||||
description: 'Teams API URL',
|
||||
},
|
||||
trees_url: {
|
||||
type: 'string',
|
||||
description: 'Trees API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
has_issues: {
|
||||
type: 'boolean',
|
||||
description: 'Whether issues are enabled',
|
||||
},
|
||||
has_projects: {
|
||||
type: 'boolean',
|
||||
description: 'Whether projects are enabled',
|
||||
},
|
||||
has_downloads: {
|
||||
type: 'boolean',
|
||||
description: 'Whether downloads are enabled',
|
||||
},
|
||||
has_wiki: {
|
||||
type: 'boolean',
|
||||
description: 'Whether wiki is enabled',
|
||||
},
|
||||
has_pages: {
|
||||
type: 'boolean',
|
||||
description: 'Whether GitHub Pages is enabled',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
mirror_url: {
|
||||
type: 'string',
|
||||
description: 'Mirror URL if repository is a mirror',
|
||||
},
|
||||
archived: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is archived',
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is disabled',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
license: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'License key',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'License name',
|
||||
},
|
||||
spdx_id: {
|
||||
type: 'string',
|
||||
description: 'SPDX license identifier',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'License API URL',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'License node ID',
|
||||
},
|
||||
},
|
||||
allow_forking: {
|
||||
type: 'boolean',
|
||||
description: 'Whether forking is allowed',
|
||||
},
|
||||
is_template: {
|
||||
type: 'boolean',
|
||||
description: 'Whether repository is a template',
|
||||
},
|
||||
topics: {
|
||||
type: 'array',
|
||||
description: 'Array of repository topics',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
description: 'Repository visibility (public, private, internal)',
|
||||
},
|
||||
forks: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
watchers: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Repository creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Repository last update timestamp',
|
||||
},
|
||||
pushed_at: {
|
||||
type: 'string',
|
||||
description: 'Repository last push timestamp',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Owner node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
gravatar_id: {
|
||||
type: 'string',
|
||||
description: 'Owner gravatar ID',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Owner API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
followers_url: {
|
||||
type: 'string',
|
||||
description: 'Followers API URL',
|
||||
},
|
||||
following_url: {
|
||||
type: 'string',
|
||||
description: 'Following API URL',
|
||||
},
|
||||
gists_url: {
|
||||
type: 'string',
|
||||
description: 'Gists API URL',
|
||||
},
|
||||
starred_url: {
|
||||
type: 'string',
|
||||
description: 'Starred repositories API URL',
|
||||
},
|
||||
subscriptions_url: {
|
||||
type: 'string',
|
||||
description: 'Subscriptions API URL',
|
||||
},
|
||||
organizations_url: {
|
||||
type: 'string',
|
||||
description: 'Organizations API URL',
|
||||
},
|
||||
repos_url: {
|
||||
type: 'string',
|
||||
description: 'Repositories API URL',
|
||||
},
|
||||
events_url: {
|
||||
type: 'string',
|
||||
description: 'Events API URL',
|
||||
},
|
||||
received_events_url: {
|
||||
type: 'string',
|
||||
description: 'Received events API URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
site_admin: {
|
||||
type: 'boolean',
|
||||
description: 'Whether owner is a site administrator',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
gravatar_id: {
|
||||
type: 'string',
|
||||
description: 'Gravatar ID',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'User API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
followers_url: {
|
||||
type: 'string',
|
||||
description: 'Followers API URL',
|
||||
},
|
||||
following_url: {
|
||||
type: 'string',
|
||||
description: 'Following API URL',
|
||||
},
|
||||
gists_url: {
|
||||
type: 'string',
|
||||
description: 'Gists API URL',
|
||||
},
|
||||
starred_url: {
|
||||
type: 'string',
|
||||
description: 'Starred repositories API URL',
|
||||
},
|
||||
subscriptions_url: {
|
||||
type: 'string',
|
||||
description: 'Subscriptions API URL',
|
||||
},
|
||||
organizations_url: {
|
||||
type: 'string',
|
||||
description: 'Organizations API URL',
|
||||
},
|
||||
repos_url: {
|
||||
type: 'string',
|
||||
description: 'Repositories API URL',
|
||||
},
|
||||
events_url: {
|
||||
type: 'string',
|
||||
description: 'Events API URL',
|
||||
},
|
||||
received_events_url: {
|
||||
type: 'string',
|
||||
description: 'Received events API URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
site_admin: {
|
||||
type: 'boolean',
|
||||
description: 'Whether user is a site administrator',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'release',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
893
apps/sim/triggers/github/utils.ts
Normal file
893
apps/sim/triggers/github/utils.ts
Normal file
@@ -0,0 +1,893 @@
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { TriggerOutput } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Shared sub-blocks configuration for all GitHub webhook triggers
|
||||
*/
|
||||
export const githubWebhookSubBlocks: SubBlockConfig[] = [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Generate setup instructions for a specific GitHub event type
|
||||
*/
|
||||
export function githubSetupInstructions(
|
||||
eventType: string,
|
||||
actions?: string[],
|
||||
additionalNotes?: string
|
||||
): string {
|
||||
const actionText = actions
|
||||
? ` (<strong>${actions.join(', ')}</strong> ${actions.length === 1 ? 'action' : 'actions'})`
|
||||
: ''
|
||||
|
||||
const instructions = [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
`Select "Let me select individual events" and check <strong>${eventType}</strong>${actionText}.`,
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
|
||||
if (additionalNotes) {
|
||||
instructions.push(additionalNotes)
|
||||
}
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared repository output schema
|
||||
*/
|
||||
export const repositoryOutputs = {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
fork: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is a fork',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Repository API URL',
|
||||
},
|
||||
homepage: {
|
||||
type: 'string',
|
||||
description: 'Repository homepage URL',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
description: 'Repository size in KB',
|
||||
},
|
||||
stargazers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of stars',
|
||||
},
|
||||
watchers_count: {
|
||||
type: 'number',
|
||||
description: 'Number of watchers',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
description: 'Primary programming language',
|
||||
},
|
||||
forks_count: {
|
||||
type: 'number',
|
||||
description: 'Number of forks',
|
||||
},
|
||||
open_issues_count: {
|
||||
type: 'number',
|
||||
description: 'Number of open issues',
|
||||
},
|
||||
default_branch: {
|
||||
type: 'string',
|
||||
description: 'Default branch name',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Shared sender/user output schema
|
||||
*/
|
||||
export const userOutputs = {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Build output schema for issue events
|
||||
*/
|
||||
export function buildIssueOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
||||
},
|
||||
issue: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Issue ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Issue node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Issue body/description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue state (open, closed)',
|
||||
},
|
||||
state_reason: {
|
||||
type: 'string',
|
||||
description: 'Reason for state (completed, not_planned, reopened)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
milestone: {
|
||||
type: 'object',
|
||||
description: 'Milestone object if assigned',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Issue creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Issue last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Issue closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for issue comment events
|
||||
*/
|
||||
export function buildIssueCommentOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (created, edited, deleted)',
|
||||
},
|
||||
issue: {
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Issue number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Issue title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Issue state (open, closed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Issue HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
},
|
||||
comment: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Comment node ID',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Comment text',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Comment HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Comment creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Comment last update timestamp',
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for pull request events
|
||||
*/
|
||||
export function buildPullRequestOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
pull_request: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Pull request ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Pull request node ID',
|
||||
},
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Pull request description',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
merged: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR was merged',
|
||||
},
|
||||
merged_at: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when PR was merged',
|
||||
},
|
||||
merged_by: userOutputs,
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the PR is a draft',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
patch_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request patch URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Source branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Source repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Source repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Target branch commit SHA',
|
||||
},
|
||||
repo: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Target repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Target repository full name',
|
||||
},
|
||||
},
|
||||
},
|
||||
additions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines added',
|
||||
},
|
||||
deletions: {
|
||||
type: 'number',
|
||||
description: 'Number of lines deleted',
|
||||
},
|
||||
changed_files: {
|
||||
type: 'number',
|
||||
description: 'Number of files changed',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Array of label objects',
|
||||
},
|
||||
assignees: {
|
||||
type: 'array',
|
||||
description: 'Array of assigned users',
|
||||
},
|
||||
requested_reviewers: {
|
||||
type: 'array',
|
||||
description: 'Array of requested reviewers',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request last update timestamp',
|
||||
},
|
||||
closed_at: {
|
||||
type: 'string',
|
||||
description: 'Pull request closed timestamp',
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for PR comment events
|
||||
*/
|
||||
export function buildPRCommentOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (created, edited, deleted)',
|
||||
},
|
||||
issue: {
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
pull_request: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Pull request API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
diff_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request diff URL',
|
||||
},
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Comment node ID',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Comment text',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Comment HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Comment creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Comment last update timestamp',
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for PR review events
|
||||
*/
|
||||
export function buildPRReviewOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (submitted, edited, dismissed)',
|
||||
},
|
||||
review: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Review ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Review node ID',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Review comment body',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Review state (approved, changes_requested, commented, dismissed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Review HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
submitted_at: {
|
||||
type: 'string',
|
||||
description: 'Review submission timestamp',
|
||||
},
|
||||
},
|
||||
pull_request: {
|
||||
number: {
|
||||
type: 'number',
|
||||
description: 'Pull request number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Pull request title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Pull request state (open, closed)',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Pull request HTML URL',
|
||||
},
|
||||
user: userOutputs,
|
||||
head: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
},
|
||||
base: {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for push events
|
||||
*/
|
||||
export function buildPushOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Git reference (e.g., refs/heads/main)',
|
||||
},
|
||||
before: {
|
||||
type: 'string',
|
||||
description: 'SHA of the commit before the push',
|
||||
},
|
||||
after: {
|
||||
type: 'string',
|
||||
description: 'SHA of the commit after the push',
|
||||
},
|
||||
created: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the push created the reference',
|
||||
},
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the push deleted the reference',
|
||||
},
|
||||
forced: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the push was forced',
|
||||
},
|
||||
compare: {
|
||||
type: 'string',
|
||||
description: 'URL to compare the changes',
|
||||
},
|
||||
commits: {
|
||||
type: 'array',
|
||||
description: 'Array of commit objects',
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Commit message',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
description: 'Commit timestamp',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Commit URL',
|
||||
},
|
||||
author: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Author name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Author email',
|
||||
},
|
||||
},
|
||||
added: {
|
||||
type: 'array',
|
||||
description: 'Array of added files',
|
||||
},
|
||||
removed: {
|
||||
type: 'array',
|
||||
description: 'Array of removed files',
|
||||
},
|
||||
modified: {
|
||||
type: 'array',
|
||||
description: 'Array of modified files',
|
||||
},
|
||||
},
|
||||
head_commit: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Commit message',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
description: 'Commit timestamp',
|
||||
},
|
||||
author: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Author name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Author email',
|
||||
},
|
||||
},
|
||||
},
|
||||
pusher: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Pusher name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Pusher email',
|
||||
},
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
description: 'Branch name extracted from ref',
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Build output schema for release events
|
||||
*/
|
||||
export function buildReleaseOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (published, created, edited, deleted, prereleased, released)',
|
||||
},
|
||||
release: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Release ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Release node ID',
|
||||
},
|
||||
tag_name: {
|
||||
type: 'string',
|
||||
description: 'Git tag name',
|
||||
},
|
||||
target_commitish: {
|
||||
type: 'string',
|
||||
description: 'Target branch or commit',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Release name/title',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Release notes/description',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the release is a draft',
|
||||
},
|
||||
prerelease: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the release is a pre-release',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Release HTML URL',
|
||||
},
|
||||
tarball_url: {
|
||||
type: 'string',
|
||||
description: 'Tarball download URL',
|
||||
},
|
||||
zipball_url: {
|
||||
type: 'string',
|
||||
description: 'Zipball download URL',
|
||||
},
|
||||
author: userOutputs,
|
||||
assets: {
|
||||
type: 'array',
|
||||
description: 'Array of release asset objects',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Release creation timestamp',
|
||||
},
|
||||
published_at: {
|
||||
type: 'string',
|
||||
description: 'Release publication timestamp',
|
||||
},
|
||||
},
|
||||
repository: repositoryOutputs,
|
||||
sender: userOutputs,
|
||||
} as any
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a GitHub event matches the expected trigger configuration
|
||||
* This is used for event filtering in the webhook processor
|
||||
*/
|
||||
export function isGitHubEventMatch(
|
||||
triggerId: string,
|
||||
eventType: string,
|
||||
action?: string,
|
||||
payload?: any
|
||||
): boolean {
|
||||
const eventMap: Record<
|
||||
string,
|
||||
{ event: string; actions?: string[]; validator?: (payload: any) => boolean }
|
||||
> = {
|
||||
github_issue_opened: { event: 'issues', actions: ['opened'] },
|
||||
github_issue_closed: { event: 'issues', actions: ['closed'] },
|
||||
github_issue_comment: {
|
||||
event: 'issue_comment',
|
||||
validator: (p) => !p.issue?.pull_request, // Only issues, not PRs
|
||||
},
|
||||
github_pr_opened: { event: 'pull_request', actions: ['opened'] },
|
||||
github_pr_closed: {
|
||||
event: 'pull_request',
|
||||
actions: ['closed'],
|
||||
validator: (p) => p.pull_request?.merged === false, // Not merged
|
||||
},
|
||||
github_pr_merged: {
|
||||
event: 'pull_request',
|
||||
actions: ['closed'],
|
||||
validator: (p) => p.pull_request?.merged === true, // Merged
|
||||
},
|
||||
github_pr_comment: {
|
||||
event: 'issue_comment',
|
||||
validator: (p) => !!p.issue?.pull_request, // Only PRs, not issues
|
||||
},
|
||||
github_pr_reviewed: { event: 'pull_request_review', actions: ['submitted'] },
|
||||
github_push: { event: 'push' },
|
||||
github_release_published: { event: 'release', actions: ['published'] },
|
||||
}
|
||||
|
||||
const config = eventMap[triggerId]
|
||||
if (!config) {
|
||||
return true // Unknown trigger, allow through
|
||||
}
|
||||
|
||||
// Check event type
|
||||
if (config.event !== eventType) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check action if specified
|
||||
if (config.actions && action && !config.actions.includes(action)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Run custom validator if provided
|
||||
if (config.validator && payload) {
|
||||
return config.validator(payload)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -19,6 +19,10 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
@@ -32,16 +36,24 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret (Recommended)',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
@@ -55,6 +67,10 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
@@ -76,6 +92,10 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
@@ -83,6 +103,10 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
@@ -133,6 +157,10 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
609
apps/sim/triggers/github/workflow_run.ts
Normal file
609
apps/sim/triggers/github/workflow_run.ts
Normal file
@@ -0,0 +1,609 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const githubWorkflowRunTrigger: TriggerConfig = {
|
||||
id: 'github_workflow_run',
|
||||
name: 'GitHub Actions Workflow Run',
|
||||
provider: 'github',
|
||||
description:
|
||||
'Trigger workflow when a GitHub Actions workflow run is requested, in progress, or completed',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'application/json', id: 'application/json' },
|
||||
{
|
||||
label: 'application/x-www-form-urlencoded',
|
||||
id: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
],
|
||||
defaultValue: 'application/json',
|
||||
description: 'Format GitHub will use when sending the webhook payload.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Generate or enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from GitHub.',
|
||||
password: true,
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sslVerification',
|
||||
title: 'SSL Verification',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Enabled', id: 'enabled' },
|
||||
{ label: 'Disabled', id: 'disabled' },
|
||||
],
|
||||
defaultValue: 'enabled',
|
||||
description: 'GitHub verifies SSL certificates when delivering webhooks.',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
type: 'text',
|
||||
defaultValue: [
|
||||
'Go to your GitHub Repository > Settings > Webhooks.',
|
||||
'Click "Add webhook".',
|
||||
'Paste the <strong>Webhook URL</strong> above into the "Payload URL" field.',
|
||||
'Select your chosen Content Type from the dropdown.',
|
||||
'Enter the <strong>Webhook Secret</strong> into the "Secret" field if you\'ve configured one.',
|
||||
'Set SSL verification according to your selection.',
|
||||
'Select "Let me select individual events" and check <strong>Workflow runs</strong>.',
|
||||
'Ensure "Active" is checked and click "Add webhook".',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_workflow_run',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: JSON.stringify(
|
||||
{
|
||||
action: 'completed',
|
||||
workflow_run: {
|
||||
id: 30433642,
|
||||
node_id: 'MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ==',
|
||||
name: 'Build',
|
||||
workflow_id: 159038,
|
||||
run_number: 562,
|
||||
run_attempt: 1,
|
||||
event: 'push',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
head_branch: 'master',
|
||||
head_sha: 'acb5820ced9479c074f688cc328bf03f341a511d',
|
||||
path: '.github/workflows/build.yml',
|
||||
display_title: 'Update README',
|
||||
run_started_at: '2020-01-22T19:33:08Z',
|
||||
created_at: '2020-01-22T19:33:08Z',
|
||||
updated_at: '2020-01-22T19:33:08Z',
|
||||
html_url: 'https://github.com/octo-org/octo-repo/actions/runs/30433642',
|
||||
check_suite_id: 42,
|
||||
check_suite_node_id: 'MDEwOkNoZWNrU3VpdGU0Mg==',
|
||||
url: 'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642',
|
||||
actor: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
type: 'User',
|
||||
},
|
||||
triggering_actor: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
type: 'User',
|
||||
},
|
||||
repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'Hello-World',
|
||||
full_name: 'octocat/Hello-World',
|
||||
private: false,
|
||||
},
|
||||
head_repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'Hello-World',
|
||||
full_name: 'octocat/Hello-World',
|
||||
private: false,
|
||||
},
|
||||
head_commit: {
|
||||
id: 'acb5820ced9479c074f688cc328bf03f341a511d',
|
||||
tree_id: 'd23f6eedb1e1b34603681f77168dc1c4',
|
||||
message: 'Update README.md',
|
||||
timestamp: '2020-01-22T19:33:05Z',
|
||||
author: {
|
||||
name: 'Octo Cat',
|
||||
email: 'octocat@github.com',
|
||||
},
|
||||
committer: {
|
||||
name: 'GitHub',
|
||||
email: 'noreply@github.com',
|
||||
},
|
||||
},
|
||||
pull_requests: [],
|
||||
referenced_workflows: [],
|
||||
},
|
||||
workflow: {
|
||||
id: 159038,
|
||||
node_id: 'MDg6V29ya2Zsb3cxNTkwMzg=',
|
||||
name: 'Build',
|
||||
path: '.github/workflows/build.yml',
|
||||
state: 'active',
|
||||
created_at: '2020-01-08T23:48:37.000-08:00',
|
||||
updated_at: '2020-01-08T23:50:21.000-08:00',
|
||||
url: 'https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038',
|
||||
html_url:
|
||||
'https://github.com/octo-org/octo-repo/blob/master/.github/workflows/build.yml',
|
||||
badge_url: 'https://github.com/octo-org/octo-repo/workflows/Build/badge.svg',
|
||||
},
|
||||
repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'Hello-World',
|
||||
full_name: 'octocat/Hello-World',
|
||||
html_url: 'https://github.com/octocat/Hello-World',
|
||||
description: 'This your first repo!',
|
||||
private: false,
|
||||
owner: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
html_url: 'https://github.com/octocat',
|
||||
type: 'User',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (requested, in_progress, completed)',
|
||||
},
|
||||
workflow_run: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Workflow run ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Workflow run node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Workflow name',
|
||||
},
|
||||
workflow_id: {
|
||||
type: 'number',
|
||||
description: 'Workflow ID',
|
||||
},
|
||||
run_number: {
|
||||
type: 'number',
|
||||
description: 'Run number for this workflow',
|
||||
},
|
||||
run_attempt: {
|
||||
type: 'number',
|
||||
description: 'Attempt number for this run',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
description: 'Event that triggered the workflow (push, pull_request, etc.)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Current status (queued, in_progress, completed)',
|
||||
},
|
||||
conclusion: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Conclusion (success, failure, cancelled, skipped, timed_out, action_required)',
|
||||
},
|
||||
head_branch: {
|
||||
type: 'string',
|
||||
description: 'Branch name',
|
||||
},
|
||||
head_sha: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA that triggered the workflow',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path to the workflow file',
|
||||
},
|
||||
display_title: {
|
||||
type: 'string',
|
||||
description: 'Display title for the run',
|
||||
},
|
||||
run_started_at: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when the run started',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Workflow run creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Workflow run last update timestamp',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Workflow run HTML URL',
|
||||
},
|
||||
check_suite_id: {
|
||||
type: 'number',
|
||||
description: 'Associated check suite ID',
|
||||
},
|
||||
check_suite_node_id: {
|
||||
type: 'string',
|
||||
description: 'Associated check suite node ID',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Workflow run API URL',
|
||||
},
|
||||
actor: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
triggering_actor: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether repository is private',
|
||||
},
|
||||
},
|
||||
head_repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Head repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Head repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Head repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Head repository full name',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether repository is private',
|
||||
},
|
||||
},
|
||||
head_commit: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Commit SHA',
|
||||
},
|
||||
tree_id: {
|
||||
type: 'string',
|
||||
description: 'Tree ID',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Commit message',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
description: 'Commit timestamp',
|
||||
},
|
||||
author: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Author name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Author email',
|
||||
},
|
||||
},
|
||||
committer: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Committer name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Committer email',
|
||||
},
|
||||
},
|
||||
},
|
||||
pull_requests: {
|
||||
type: 'array',
|
||||
description: 'Array of associated pull requests',
|
||||
},
|
||||
referenced_workflows: {
|
||||
type: 'array',
|
||||
description: 'Array of referenced workflow runs',
|
||||
},
|
||||
},
|
||||
workflow: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Workflow ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Workflow node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Workflow name',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path to workflow file',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Workflow state (active, deleted, disabled_fork, etc.)',
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
description: 'Workflow creation timestamp',
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
description: 'Workflow last update timestamp',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Workflow API URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Workflow HTML URL',
|
||||
},
|
||||
badge_url: {
|
||||
type: 'string',
|
||||
description: 'Workflow badge URL',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Repository ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Repository node ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Repository name',
|
||||
},
|
||||
full_name: {
|
||||
type: 'string',
|
||||
description: 'Repository full name (owner/repo)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Repository HTML URL',
|
||||
},
|
||||
repo_description: {
|
||||
type: 'string',
|
||||
description: 'Repository description',
|
||||
},
|
||||
owner: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Owner username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Owner ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'Owner node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Owner avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Owner profile URL',
|
||||
},
|
||||
owner_type: {
|
||||
type: 'string',
|
||||
description: 'Owner type (User, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
sender: {
|
||||
login: {
|
||||
type: 'string',
|
||||
description: 'Username',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'User ID',
|
||||
},
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: 'User node ID',
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
description: 'Avatar URL',
|
||||
},
|
||||
html_url: {
|
||||
type: 'string',
|
||||
description: 'Profile URL',
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
description: 'User type (User, Bot, Organization)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Event': 'workflow_run',
|
||||
'X-GitHub-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'X-Hub-Signature-256': 'sha256=...',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,19 @@
|
||||
import { airtableWebhookTrigger } from '@/triggers/airtable'
|
||||
import { genericWebhookTrigger } from '@/triggers/generic'
|
||||
import { githubWebhookTrigger } from '@/triggers/github'
|
||||
import {
|
||||
githubIssueClosedTrigger,
|
||||
githubIssueCommentTrigger,
|
||||
githubIssueOpenedTrigger,
|
||||
githubPRClosedTrigger,
|
||||
githubPRCommentTrigger,
|
||||
githubPRMergedTrigger,
|
||||
githubPROpenedTrigger,
|
||||
githubPRReviewedTrigger,
|
||||
githubPushTrigger,
|
||||
githubReleasePublishedTrigger,
|
||||
githubWebhookTrigger,
|
||||
githubWorkflowRunTrigger,
|
||||
} from '@/triggers/github'
|
||||
import { gmailPollingTrigger } from '@/triggers/gmail'
|
||||
import { googleFormsWebhookTrigger } from '@/triggers/googleforms'
|
||||
import {
|
||||
@@ -27,6 +40,17 @@ export const TRIGGER_REGISTRY: TriggerRegistry = {
|
||||
airtable_webhook: airtableWebhookTrigger,
|
||||
generic_webhook: genericWebhookTrigger,
|
||||
github_webhook: githubWebhookTrigger,
|
||||
github_issue_opened: githubIssueOpenedTrigger,
|
||||
github_issue_closed: githubIssueClosedTrigger,
|
||||
github_issue_comment: githubIssueCommentTrigger,
|
||||
github_pr_opened: githubPROpenedTrigger,
|
||||
github_pr_closed: githubPRClosedTrigger,
|
||||
github_pr_merged: githubPRMergedTrigger,
|
||||
github_pr_comment: githubPRCommentTrigger,
|
||||
github_pr_reviewed: githubPRReviewedTrigger,
|
||||
github_push: githubPushTrigger,
|
||||
github_release_published: githubReleasePublishedTrigger,
|
||||
github_workflow_run: githubWorkflowRunTrigger,
|
||||
gmail_poller: gmailPollingTrigger,
|
||||
microsoftteams_webhook: microsoftTeamsWebhookTrigger,
|
||||
microsoftteams_chat_subscription: microsoftTeamsChatSubscriptionTrigger,
|
||||
|
||||
Reference in New Issue
Block a user