324 lines
14 KiB
PowerShell
324 lines
14 KiB
PowerShell
# ----------------------------------------------------------------
|
||
# Scenario 1a: Webhook Setup — Register on GitLink Platform
|
||
# Role: 在 GitLink 平台上注册 webhook,连接"平台事件"到"本地接收器"
|
||
#
|
||
# 这是整个实时链路的最后一块拼图——把 GitLink 平台的 Issue 创建事件
|
||
# 和本地的 01a-webhook-listener.ps1 接收器连接起来。
|
||
#
|
||
# 完整链路:
|
||
# GitLink Issue 创建
|
||
# → Webhook POST 到 <WebhookUrl>
|
||
# → 01a-webhook-listener.ps1 (接收 HTTP 请求)
|
||
# → 01a-issue-triage.ps1 (AI 分类 + 打标签 + 分配)
|
||
#
|
||
# 前置条件:
|
||
# 方案A (本地开发): 先启动 ngrok → 再启动 01a-webhook-listener.ps1 → 再运行本脚本
|
||
# 方案B (公网服务器): 先启动 01a-webhook-listener.ps1 → 再运行本脚本(直接给公网URL)
|
||
# ----------------------------------------------------------------
|
||
#Requires -Version 5.1
|
||
|
||
param(
|
||
[string]$WebhookUrl = "",
|
||
|
||
[string]$Owner = "",
|
||
[string]$Repo = "",
|
||
[string]$Secret = "",
|
||
[string]$Events = "issue",
|
||
[string]$Description = "Community Ops — Issue Auto-Triage (created by gitlink-cli)",
|
||
[switch]$DryRun,
|
||
[switch]$ListExisting,
|
||
[switch]$DeleteExisting,
|
||
[switch]$Force,
|
||
[switch]$Help
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
Import-Module "$PSScriptRoot/lib/common.psm1" -Force -WarningAction SilentlyContinue
|
||
|
||
if ($Help) {
|
||
Write-Host "Usage: powershell 01a-webhook-setup.ps1 -WebhookUrl URL [-Owner OWNER] [-Repo REPO] [-Secret SECRET] [-Events EVENTS] [-DryRun]"
|
||
Write-Host ""
|
||
Write-Host " 在 GitLink 平台上注册 webhook,将 Issue 创建事件连接到本地接收器。"
|
||
Write-Host ""
|
||
Write-Host " -WebhookUrl URL (必填) Webhook 回调地址,GitLink 会向此 URL POST 事件"
|
||
Write-Host " - 本地开发: https://xxx.ngrok-free.app/webhook"
|
||
Write-Host " - 公网服务器: https://your-server.com:8080/webhook"
|
||
Write-Host " -Owner OWNER 仓库所有者(在git仓库内可自动检测)"
|
||
Write-Host " -Repo REPO 仓库名称(在git仓库内可自动检测)"
|
||
Write-Host " -Secret SECRET HMAC 密钥(需与 01a-webhook-listener.ps1 的 -Secret 一致)"
|
||
Write-Host " -Events EVENTS 触发事件类型(默认: issue)"
|
||
Write-Host " -Description DESC Webhook 描述"
|
||
Write-Host " -ListExisting 列出当前仓库已有的 webhook"
|
||
Write-Host " -DeleteExisting 删除当前仓库所有非 gitlink-cli 创建的 issue 类 webhook(需配合 -Force)"
|
||
Write-Host " -Force 配合 -DeleteExisting 使用"
|
||
Write-Host " -DryRun 预览模式"
|
||
Write-Host ""
|
||
Write-Host " 部署步骤:"
|
||
Write-Host " # 终端 1: 启动 ngrok(本地开发)"
|
||
Write-Host " ngrok http 8080"
|
||
Write-Host ""
|
||
Write-Host " # 终端 2: 启动接收器"
|
||
Write-Host " powershell workflows/01a-webhook-listener.ps1 -Port 8080 -Secret 'your-secret'"
|
||
Write-Host ""
|
||
Write-Host " # 终端 3: 注册 webhook(ngrok 提供的 URL)"
|
||
Write-Host " powershell workflows/01a-webhook-setup.ps1 -WebhookUrl 'https://xxx.ngrok-free.app/webhook' -Secret 'your-secret'"
|
||
exit 0
|
||
}
|
||
|
||
if (-not $WebhookUrl) { Log-Err "WebhookUrl is required (use -Help for usage)"; exit 1 }
|
||
|
||
Check-Auth
|
||
$r = Resolve-OwnerRepo $Owner $Repo
|
||
$Owner = $r.Owner; $Repo = $r.Repo
|
||
|
||
# ================================================================
|
||
# Validation
|
||
# ================================================================
|
||
Log-Title "Webhook Setup: $Owner/$Repo"
|
||
|
||
# Validate URL format
|
||
if ($WebhookUrl -notmatch '^https?://') {
|
||
Log-Err "Webhook URL must start with http:// or https://"
|
||
Log-Info "For GitLink platform, HTTPS is required. Use ngrok for local dev: ngrok http 8080"
|
||
exit 1
|
||
}
|
||
|
||
if ($WebhookUrl -notmatch '^https://') {
|
||
Log-Warn "GitLink requires HTTPS URLs for webhooks. Your URL uses HTTP which may be rejected."
|
||
Log-Info "Consider using ngrok to get an HTTPS URL: ngrok http <port>"
|
||
}
|
||
|
||
Write-Host " Owner: $Owner"
|
||
Write-Host " Repo: $Repo"
|
||
Write-Host " Webhook URL: $WebhookUrl"
|
||
Write-Host " Events: $Events"
|
||
Write-Host " Secret: $(if ($Secret) { '***configured***' } else { '(not set)' })"
|
||
Write-Host " Description: $Description"
|
||
Divider
|
||
|
||
# ================================================================
|
||
# List existing webhooks
|
||
# ================================================================
|
||
if ($ListExisting -or $DeleteExisting) {
|
||
Log-Title "Existing Webhooks"
|
||
|
||
$listResult = Invoke-GL webhook,+list,--owner,$Owner,--repo,$Repo
|
||
if (-not $listResult) {
|
||
Log-Warn "Could not list webhooks (API may not be available or no webhooks)"
|
||
} else {
|
||
try {
|
||
$listData = $listResult | ConvertFrom-Json
|
||
if ($listData.ok -and $listData.data) {
|
||
$webhooks = @()
|
||
if ($listData.data.webhooks) { $webhooks = @($listData.data.webhooks) }
|
||
elseif ($listData.data -is [array]) { $webhooks = $listData.data }
|
||
|
||
if ($webhooks.Count -eq 0) {
|
||
Log-Info "No webhooks configured for this repo"
|
||
} else {
|
||
Write-Host ""
|
||
foreach ($wh in $webhooks) {
|
||
$whId = if ($wh.id) { $wh.id } else { "?" }
|
||
$whUrl = if ($wh.hook_url) { $wh.hook_url } elseif ($wh.url) { $wh.url } else { "?" }
|
||
$whActive = if ($wh.is_active -ne $null) { $wh.is_active } elseif ($wh.active -ne $null) { $wh.active } else { "?" }
|
||
$whEvents = if ($wh.events) { ($wh.events -join ',') } else { "?" }
|
||
$whDesc = if ($wh.description) { $wh.description } else { "(no description)" }
|
||
Write-Host " [#$whId] $whUrl" -ForegroundColor Cyan
|
||
Write-Host " Active: $whActive | Events: $whEvents"
|
||
Write-Host " $whDesc"
|
||
Write-Host ""
|
||
}
|
||
}
|
||
}
|
||
} catch {
|
||
Log-Warn "Could not parse webhook list: $_"
|
||
Log-Info "Raw output: $listResult"
|
||
}
|
||
}
|
||
}
|
||
|
||
# ================================================================
|
||
# Delete existing issue-related webhooks (cleanup before create)
|
||
# ================================================================
|
||
if ($DeleteExisting) {
|
||
if (-not $Force) {
|
||
Log-Warn "-DeleteExisting requires -Force flag for safety. Add -Force to confirm deletion."
|
||
} else {
|
||
Log-Warn "Removing existing webhooks that match issue events..."
|
||
|
||
$listResult = Invoke-GL webhook,+list,--owner,$Owner,--repo,$Repo
|
||
if ($listResult) {
|
||
try {
|
||
$listData = $listResult | ConvertFrom-Json
|
||
$webhooks = @()
|
||
if ($listData.data.webhooks) { $webhooks = @($listData.data.webhooks) }
|
||
elseif ($listData.data -is [array]) { $webhooks = $listData.data }
|
||
|
||
foreach ($wh in $webhooks) {
|
||
$whId = if ($wh.id) { $wh.id } else { $null }
|
||
$whEvents = if ($wh.events) { $wh.events } else { @() }
|
||
$whUrl = if ($wh.hook_url) { $wh.hook_url } else { "" }
|
||
|
||
# Only delete issue-related ones that point to gitlink-cli created URLs
|
||
$isIssueWebhook = ($whEvents -contains "issue") -or ($whEvents -is [string] -and $whEvents -match "issue")
|
||
if ($isIssueWebhook) {
|
||
if ($DryRun) {
|
||
Log-Warn "[DRY RUN] Would delete webhook #$whId ($whUrl)"
|
||
} else {
|
||
Log-Step "Deleting webhook #$whId..."
|
||
$delResult = Invoke-GL webhook,+delete,--owner,$Owner,--repo,$Repo,--id,$whId
|
||
if ($delResult) {
|
||
Log-Ok "Deleted webhook #$whId"
|
||
} else {
|
||
Log-Warn "Failed to delete webhook #$whId"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch { }
|
||
}
|
||
}
|
||
|
||
if (-not $ListExisting) {
|
||
Log-Info "Cleanup complete. Proceeding to create new webhook..."
|
||
} else {
|
||
# User just wanted to list, exit
|
||
exit 0
|
||
}
|
||
}
|
||
|
||
if ($ListExisting) { exit 0 }
|
||
|
||
# ================================================================
|
||
# Check for existing webhook with same URL
|
||
# ================================================================
|
||
Log-Step "Checking for existing webhooks with same URL..."
|
||
$listResult = Invoke-GL webhook,+list,--owner,$Owner,--repo,$Repo
|
||
$existingId = $null
|
||
if ($listResult) {
|
||
try {
|
||
$listData = $listResult | ConvertFrom-Json
|
||
$webhooks = @()
|
||
if ($listData.data.webhooks) { $webhooks = @($listData.data.webhooks) }
|
||
elseif ($listData.data -is [array]) { $webhooks = $listData.data }
|
||
|
||
foreach ($wh in $webhooks) {
|
||
if ($wh.hook_url -eq $WebhookUrl -or $wh.url -eq $WebhookUrl) {
|
||
$existingId = $wh.id
|
||
break
|
||
}
|
||
}
|
||
} catch { }
|
||
}
|
||
|
||
if ($existingId) {
|
||
Log-Warn "A webhook with URL '$WebhookUrl' already exists (ID: #$existingId)"
|
||
Log-Info "To replace it, delete the existing one first with:"
|
||
Log-Info " gitlink-cli webhook +delete --owner $Owner --repo $Repo --id $existingId"
|
||
Log-Info "Or run this script with -DeleteExisting -Force to clean up"
|
||
exit 1
|
||
}
|
||
Log-Ok "No duplicate webhook found"
|
||
|
||
# ================================================================
|
||
# Register Webhook on GitLink
|
||
# ================================================================
|
||
Log-Title "Registering Webhook"
|
||
|
||
# Build command arguments
|
||
$createArgs = @(
|
||
"webhook", "+create",
|
||
"--owner", $Owner,
|
||
"--repo", $Repo,
|
||
"--url", $WebhookUrl,
|
||
"--events", $Events,
|
||
"--description", $Description
|
||
)
|
||
|
||
if ($Secret) {
|
||
$createArgs += @("--secret", $Secret)
|
||
}
|
||
|
||
Log-Step "Creating webhook on GitLink platform..."
|
||
Log-Info "Command: gitlink-cli $($createArgs -join ' ')"
|
||
|
||
if ($DryRun) {
|
||
Log-Warn "[DRY RUN] Would create webhook with above parameters"
|
||
exit 0
|
||
}
|
||
|
||
$createResult = Invoke-GLCheck @createArgs
|
||
if (-not $createResult) {
|
||
Log-Err "Webhook creation failed"
|
||
Log-Info "Common issues:"
|
||
Log-Info " 1. URL must be HTTPS (GitLink requirement)"
|
||
Log-Info " 2. URL must be publicly accessible from GitLink's servers"
|
||
Log-Info " 3. You may need admin permissions on the repo"
|
||
Log-Info " 4. Max 20 webhooks per repo — use -ListExisting to check"
|
||
exit 1
|
||
}
|
||
|
||
$webhookId = ""
|
||
if ($createResult.data.id) { $webhookId = $createResult.data.id }
|
||
elseif ($createResult.data.webhook.id) { $webhookId = $createResult.data.webhook.id }
|
||
Log-Ok "Webhook created! ID: #$webhookId"
|
||
|
||
# ================================================================
|
||
# Test Webhook
|
||
# ================================================================
|
||
Log-Step "Testing webhook connectivity..."
|
||
$testResult = Invoke-GL webhook,+test,--owner,$Owner,--repo,$Repo,--id,$webhookId,--event,issue
|
||
if ($testResult) {
|
||
try {
|
||
$testOk = (($testResult | ConvertFrom-Json).ok -eq $true)
|
||
} catch { $testOk = $false }
|
||
|
||
if ($testOk) {
|
||
Log-Ok "Webhook test ping sent successfully"
|
||
Log-Info "Check the listener console for the test event"
|
||
} else {
|
||
Log-Warn "Webhook test may have failed — check that your listener is running and accessible"
|
||
Log-Info "Verify: curl -X POST $WebhookUrl -H 'Content-Type: application/json' -d '{}'"
|
||
}
|
||
} else {
|
||
Log-Warn "Could not test webhook — check that your listener is running"
|
||
}
|
||
|
||
# ================================================================
|
||
# Complete
|
||
# ================================================================
|
||
Log-Title "Webhook Setup Complete"
|
||
Write-Host ""
|
||
|
||
$checkmark = [char]0x2714
|
||
Write-Host " ${checkmark} GitLink Platform: Webhook registered" -ForegroundColor Green
|
||
Write-Host " → When a new Issue is created in $Owner/$Repo" -ForegroundColor Gray
|
||
Write-Host " → GitLink POSTs to: $WebhookUrl" -ForegroundColor Gray
|
||
Write-Host " → Webhook ID: #$webhookId" -ForegroundColor Gray
|
||
Write-Host ""
|
||
Write-Host " ${checkmark} Local Listener: 01a-webhook-listener.ps1" -ForegroundColor Green
|
||
Write-Host " → Receives HTTP POST from GitLink" -ForegroundColor Gray
|
||
Write-Host " → Validates HMAC signature" -ForegroundColor Gray
|
||
Write-Host " → Calls 01a-issue-triage.ps1" -ForegroundColor Gray
|
||
Write-Host ""
|
||
Write-Host " ${checkmark} Triage Script: 01a-issue-triage.ps1" -ForegroundColor Green
|
||
Write-Host " → AI analyzes issue content" -ForegroundColor Gray
|
||
Write-Host " → Selects matching label from repo's existing labels" -ForegroundColor Gray
|
||
Write-Host " → Assigns to issue creator" -ForegroundColor Gray
|
||
Write-Host ""
|
||
|
||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||
Write-Host " 验证方式:" -ForegroundColor Cyan
|
||
Write-Host " 1. 在 GitLink 网页端 $Owner/$Repo 创建一个新 Issue" -ForegroundColor White
|
||
Write-Host " 2. 观察 01a-webhook-listener.ps1 的控制台输出" -ForegroundColor White
|
||
Write-Host " 3. 检查 Issue 是否自动被打上标签并分配了负责人" -ForegroundColor White
|
||
Write-Host ""
|
||
Write-Host " 手动测试(不走 webhook,直接触发分类):" -ForegroundColor Cyan
|
||
Write-Host " powershell workflows/01a-issue-triage.ps1 -IssueNumber <N>" -ForegroundColor White
|
||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||
Write-Host ""
|
||
Write-Host " Webhook 管理:" -ForegroundColor Cyan
|
||
Write-Host " - 查看: gitlink-cli webhook +list"
|
||
Write-Host " - 详情: gitlink-cli webhook +info --id $webhookId"
|
||
Write-Host " - 删除: gitlink-cli webhook +delete --id $webhookId"
|