返回 DeepSeek-Reasonix
complete-signpath-request.ps1
根目录 / scripts / complete-signpath-request.ps1
1 [CmdletBinding()]
2 param(
3 [Parameter(Mandatory = $true)]
4 [ValidatePattern("^[0-9a-fA-F-]{36}$")]
5 [string]$OrganizationId,
6
7 [Parameter(Mandatory = $true)]
8 [ValidatePattern("^[0-9a-fA-F-]{36}$")]
9 [string]$SigningRequestId,
10
11 [Parameter(Mandatory = $true)]
12 [ValidateNotNullOrEmpty()]
13 [string]$ExpectedSigningPolicySlug,
14
15 [Parameter(Mandatory = $true)]
16 [ValidateNotNullOrEmpty()]
17 [string]$OutputArtifactDirectory,
18
19 [ValidateNotNullOrEmpty()]
20 [string]$ApiUrl = "https://app.signpath.io/api",
21
22 [ValidateRange(30, 7200)]
23 [int]$TimeoutSeconds = 1800,
24
25 [ValidateRange(1, 60)]
26 [int]$PollIntervalSeconds = 5,
27
28 [switch]$WaitForExternalApproval
29 )
30
31 Set-StrictMode -Version Latest
32 $ErrorActionPreference = "Stop"
33 $ProgressPreference = "SilentlyContinue"
34
35 $apiToken = $env:SIGNPATH_API_TOKEN
36 if ([string]::IsNullOrWhiteSpace($apiToken)) {
37 throw "SIGNPATH_API_TOKEN is required to approve and download a SignPath request."
38 }
39
40 $parsedApiUrl = $null
41 if (
42 -not [Uri]::TryCreate($ApiUrl, [UriKind]::Absolute, [ref]$parsedApiUrl) -or
43 $parsedApiUrl.Scheme -ne "https"
44 ) {
45 throw "ApiUrl must be an absolute HTTPS URL."
46 }
47
48 $workspace = if ([string]::IsNullOrWhiteSpace($env:GITHUB_WORKSPACE)) {
49 [IO.Path]::GetFullPath((Get-Location).Path)
50 } else {
51 [IO.Path]::GetFullPath($env:GITHUB_WORKSPACE)
52 }
53 $outputDirectory = if ([IO.Path]::IsPathRooted($OutputArtifactDirectory)) {
54 [IO.Path]::GetFullPath($OutputArtifactDirectory)
55 } else {
56 [IO.Path]::GetFullPath((Join-Path $workspace $OutputArtifactDirectory))
57 }
58 $workspacePrefix = $workspace.TrimEnd(
59 [IO.Path]::DirectorySeparatorChar,
60 [IO.Path]::AltDirectorySeparatorChar
61 ) + [IO.Path]::DirectorySeparatorChar
62 if (-not $outputDirectory.StartsWith($workspacePrefix, [StringComparison]::OrdinalIgnoreCase)) {
63 throw "OutputArtifactDirectory must resolve inside GITHUB_WORKSPACE."
64 }
65
66 $headers = @{
67 Authorization = "Bearer $apiToken"
68 }
69 $requestBaseUrl = (
70 $ApiUrl.TrimEnd("/") +
71 "/v1/$OrganizationId/SigningRequests/$SigningRequestId"
72 )
73
74 $request = Invoke-RestMethod `
75 -Method Get `
76 -Uri $requestBaseUrl `
77 -Headers $headers
78 if ($request.signingPolicySlug -ne $ExpectedSigningPolicySlug) {
79 throw (
80 "SignPath request policy mismatch: got '$($request.signingPolicySlug)', " +
81 "expected '$ExpectedSigningPolicySlug'."
82 )
83 }
84
85 $deadline = [DateTimeOffset]::UtcNow.AddSeconds($TimeoutSeconds)
86 $approvalSubmitted = $false
87 $externalApprovalNoticeWritten = $false
88 while ($true) {
89 $status = Invoke-RestMethod `
90 -Method Get `
91 -Uri "$requestBaseUrl/Status" `
92 -Headers $headers
93 Write-Host (
94 "SignPath request $SigningRequestId status: " +
95 "$($status.status) ($($status.workflowStatus))"
96 )
97
98 if ($status.isFinalStatus) {
99 if ($status.status -ne "Completed") {
100 throw (
101 "SignPath request ended with status '$($status.status)' " +
102 "and workflow status '$($status.workflowStatus)'."
103 )
104 }
105 break
106 }
107
108 if ($status.status -eq "WaitingForApproval" -and -not $approvalSubmitted) {
109 if ($WaitForExternalApproval) {
110 if (-not $externalApprovalNoticeWritten) {
111 Write-Host (
112 "Waiting for an authorized SignPath user to approve request " +
113 "$SigningRequestId."
114 )
115 $externalApprovalNoticeWritten = $true
116 }
117 } else {
118 Invoke-RestMethod `
119 -Method Post `
120 -Uri "$requestBaseUrl/Approve" `
121 -Headers $headers | Out-Null
122 $approvalSubmitted = $true
123 Write-Host "Approved SignPath request $SigningRequestId through the release CI identity."
124 }
125 }
126
127 if ([DateTimeOffset]::UtcNow -ge $deadline) {
128 throw "Timed out waiting for SignPath request $SigningRequestId."
129 }
130 Start-Sleep -Seconds $PollIntervalSeconds
131 }
132
133 $temporaryRoot = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) {
134 [IO.Path]::GetTempPath()
135 } else {
136 $env:RUNNER_TEMP
137 }
138 $temporaryArchive = Join-Path $temporaryRoot "signpath-$SigningRequestId.zip"
139 try {
140 Invoke-WebRequest `
141 -Method Get `
142 -Uri "$requestBaseUrl/SignedArtifact" `
143 -Headers $headers `
144 -OutFile $temporaryArchive
145 if (-not (Test-Path -LiteralPath $temporaryArchive -PathType Leaf)) {
146 throw "SignPath did not return a signed artifact."
147 }
148 if ((Get-Item -LiteralPath $temporaryArchive).Length -eq 0) {
149 throw "SignPath returned an empty signed artifact."
150 }
151
152 New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null
153 Expand-Archive `
154 -LiteralPath $temporaryArchive `
155 -DestinationPath $outputDirectory `
156 -Force
157 } finally {
158 Remove-Item -LiteralPath $temporaryArchive -Force -ErrorAction SilentlyContinue
159 }
160
161 Write-Host "Downloaded and extracted signed artifact to $outputDirectory."
162
162 lines Plain Text