Files
simstudioai--sim/apps/sim/triggers/azure_devops/utils.ts
T
wehub-resource-sync d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

284 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { TriggerOutput } from '@/triggers/types'
export const azureDevOpsTriggerOptions = [
{ label: 'Build Failed', id: 'azure_devops_build_failed' },
{ label: 'Work Item Created', id: 'azure_devops_work_item_created' },
{ label: 'All Service Hook Events', id: 'azure_devops_webhook' },
]
export const AZURE_DEVOPS_BUILD_FAILED_EVENT = 'build.complete'
export const AZURE_DEVOPS_WORK_ITEM_CREATED_EVENT = 'workitem.created'
function instructions(steps: string[]): string {
return steps.map((s, i) => `<div class="mb-3"><strong>${i + 1}.</strong> ${s}</div>`).join('')
}
export const buildFailedSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'For <strong>Trigger on this type of event</strong>, select <strong>Build completed</strong>.',
'Under <strong>Filters</strong>, set <strong>Build result</strong> to <strong>Failed</strong> (optionally add Canceled / Partially succeeded).',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
])
export const workItemCreatedSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'For <strong>Trigger on this type of event</strong>, select <strong>Work item created</strong>.',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
])
export const webhookSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'Select whichever <strong>event types</strong> you want this URL to receive (build, work item, release, etc.).',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
'Sim does not filter deliveries for this trigger — configure event types in Azure DevOps.',
])
/**
* Returns whether an Azure DevOps service hook payload matches the configured trigger.
*/
export function isAzureDevOpsEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
if (triggerId === 'azure_devops_webhook') {
return true
}
const eventType = body.eventType as string | undefined
if (triggerId === 'azure_devops_build_failed') {
if (eventType !== AZURE_DEVOPS_BUILD_FAILED_EVENT) {
return false
}
const resource = body.resource as Record<string, unknown> | undefined
const result = (resource?.result as string | undefined)?.toLowerCase()
return (
result === 'failed' ||
result === 'canceled' ||
result === 'cancelled' ||
result === 'stopped' ||
result === 'partiallysucceeded'
)
}
if (triggerId === 'azure_devops_work_item_created') {
return eventType === AZURE_DEVOPS_WORK_ITEM_CREATED_EVENT
}
return false
}
export function buildBuildFailedOutputs(): Record<string, TriggerOutput> {
return {
buildId: {
type: 'number',
description: 'Build ID',
},
buildNumber: {
type: 'string',
description: 'Build number string (e.g. 20240101.1)',
},
result: {
type: 'string',
description: 'Build result: failed | canceled | partiallySucceeded',
},
pipelineId: {
type: 'number',
description: 'Pipeline definition ID',
},
pipelineName: {
type: 'string',
description: 'Pipeline definition name',
},
projectName: {
type: 'string',
description: 'Azure DevOps project name',
},
branch: {
type: 'string',
description: 'Source branch name (refs/heads/ prefix stripped)',
},
commitSha: {
type: 'string',
description: 'Source commit SHA',
},
triggeredBy: {
type: 'string',
description: 'Display name of the person who triggered the build',
},
triggeredByEmail: {
type: 'string',
description: 'Email/unique name of the person who triggered the build, or null if not set',
},
startTime: {
type: 'string',
description: 'Build start time (ISO 8601)',
},
finishTime: {
type: 'string',
description: 'Build finish time (ISO 8601)',
},
buildUrl: {
type: 'string',
description: 'API URL for the build resource',
},
}
}
export function buildWorkItemCreatedOutputs(): Record<string, TriggerOutput> {
return {
workItemId: {
type: 'number',
description: 'Work item ID',
},
workItemType: {
type: 'string',
description: 'Work item type for Basic process (e.g. Issue, Task, Epic)',
},
title: {
type: 'string',
description: 'Work item title',
},
state: {
type: 'string',
description: 'Work item state for Basic process (e.g. To Do, Doing, Done)',
},
createdBy: {
type: 'string',
description: 'Display name of the creator, or null if not set',
},
assignedTo: {
type: 'string',
description: 'Assignee display name, or null if unassigned',
},
priority: {
type: 'number',
description: 'Priority (14), or 0 if not set',
},
areaPath: {
type: 'string',
description: 'Area path',
},
iterationPath: {
type: 'string',
description: 'Iteration path',
},
description: {
type: 'string',
description: 'Work item description (HTML), or null if not set',
},
projectName: {
type: 'string',
description: 'Azure DevOps project name',
},
workItemUrl: {
type: 'string',
description: 'API URL for the work item resource',
},
}
}
export function buildWebhookOutputs(): Record<string, TriggerOutput> {
return {
eventType: {
type: 'string',
description: 'Service hook event type (e.g. build.complete, workitem.created)',
},
notificationId: {
type: 'number',
description: 'Notification ID',
},
subscriptionId: {
type: 'string',
description: 'Service hook subscription ID',
},
publisherId: {
type: 'string',
description: 'Publisher ID (e.g. tfs)',
},
createdDate: {
type: 'string',
description: 'Event creation time (ISO 8601)',
},
resource: {
type: 'json',
description: 'Event resource payload',
},
resourceContainers: {
type: 'json',
description: 'Resource container references (project, collection, etc.)',
},
message: {
type: 'json',
description: 'Short message object',
},
detailedMessage: {
type: 'json',
description: 'Detailed message object',
},
}
}
export function formatBuildCompleteInput(body: Record<string, unknown>): Record<string, unknown> {
const resource = (body.resource ?? {}) as Record<string, unknown>
const definition = (resource.definition ?? {}) as Record<string, unknown>
const project = (resource.project ?? {}) as Record<string, unknown>
const requestedFor = (resource.requestedFor ?? {}) as Record<string, unknown>
const sourceBranch = (resource.sourceBranch as string) ?? ''
return {
buildId: Number(resource.id ?? 0),
buildNumber: (resource.buildNumber as string) ?? '',
result: (resource.result as string) ?? '',
pipelineId: Number(definition.id ?? 0),
pipelineName: (definition.name as string) ?? '',
projectName: (project.name as string) ?? '',
branch: sourceBranch.replace(/^refs\/heads\//, ''),
commitSha: (resource.sourceVersion as string) ?? '',
triggeredBy: (requestedFor.displayName as string) ?? null,
triggeredByEmail: (requestedFor.uniqueName as string) ?? null,
startTime: (resource.startTime as string) ?? '',
finishTime: (resource.finishTime as string) ?? '',
buildUrl: (resource.url as string) ?? '',
}
}
export function formatWorkItemCreatedInput(body: Record<string, unknown>): Record<string, unknown> {
const resource = (body.resource ?? {}) as Record<string, unknown>
const fields = (resource.fields ?? {}) as Record<string, unknown>
return {
workItemId: Number(resource.id ?? 0),
workItemType: (fields['System.WorkItemType'] as string) ?? '',
title: (fields['System.Title'] as string) ?? '',
state: (fields['System.State'] as string) ?? '',
createdBy:
(fields['System.CreatedBy'] as { displayName?: string } | undefined)?.displayName ?? null,
assignedTo:
(fields['System.AssignedTo'] as { displayName?: string } | undefined)?.displayName ?? null,
priority: Number(fields['Microsoft.VSTS.Common.Priority'] ?? 0),
areaPath: (fields['System.AreaPath'] as string) ?? '',
iterationPath: (fields['System.IterationPath'] as string) ?? '',
description: (fields['System.Description'] as string) ?? null,
projectName: (fields['System.TeamProject'] as string) ?? '',
workItemUrl: (resource.url as string) ?? '',
}
}
export function formatWebhookEnvelopeInput(body: Record<string, unknown>): Record<string, unknown> {
return {
eventType: (body.eventType as string) ?? '',
notificationId: Number(body.notificationId ?? 0),
subscriptionId: (body.subscriptionId as string) ?? '',
publisherId: (body.publisherId as string) ?? '',
createdDate: (body.createdDate as string) ?? '',
resource: body.resource ?? null,
resourceContainers: body.resourceContainers ?? null,
message: body.message ?? null,
detailedMessage: body.detailedMessage ?? null,
}
}