param( [Parameter(Mandatory = $true)] [string] $Project, [Parameter(Mandatory = $true)] [string] $Manifest, [Parameter(Mandatory = $true)] [string] $PrivateKey, [string] $OutputDirectory = '.artifacts/plugins' ) $ErrorActionPreference = 'Stop' $repositoryRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) $projectPath = [IO.Path]::GetFullPath($Project, (Get-Location).Path) $manifestPath = [IO.Path]::GetFullPath($Manifest, (Get-Location).Path) $privateKeyPath = [IO.Path]::GetFullPath($PrivateKey, (Get-Location).Path) $outputRoot = [IO.Path]::GetFullPath($OutputDirectory, $repositoryRoot) foreach ($requiredPath in @($projectPath, $manifestPath, $privateKeyPath)) { if (-not (Test-Path -LiteralPath $requiredPath)) { throw "Required path does not exist: $requiredPath" } } $manifestObject = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json if ([string]::IsNullOrWhiteSpace($manifestObject.id) -or [string]::IsNullOrWhiteSpace($manifestObject.version)) { throw 'plugin.json must contain id and version.' } if ($manifestObject.id -notmatch '^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$' -or $manifestObject.version -notmatch '^\d+\.\d+\.\d+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$') { throw 'plugin.json contains an unsafe id or invalid semantic version.' } $temporaryRoot = Join-Path ([IO.Path]::GetTempPath()) ("jiaowu-plugin-" + [Guid]::NewGuid().ToString('N')) $publishRoot = Join-Path $temporaryRoot 'publish' $packageRoot = Join-Path $temporaryRoot 'package' $backendRoot = Join-Path $packageRoot 'backend' try { New-Item -ItemType Directory -Path $publishRoot, $backendRoot -Force | Out-Null $dotnet = (Get-Command dotnet).Source $publishArgs = @('publish', $projectPath, '-c', 'Release', '-o', $publishRoot) & $dotnet @publishArgs if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" } Copy-Item -LiteralPath $manifestPath -Destination (Join-Path $packageRoot 'plugin.json') Copy-Item -Path (Join-Path $publishRoot '*') -Destination $backendRoot -Recurse -Force New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null $baseName = "$($manifestObject.id)-$($manifestObject.version)" $zipPath = Join-Path $outputRoot "$baseName.zip" $signaturePath = Join-Path $outputRoot "$baseName.sig" if (Test-Path -LiteralPath $zipPath) { Remove-Item -LiteralPath $zipPath -Force } Compress-Archive -Path (Join-Path $packageRoot '*') -DestinationPath $zipPath $rsa = [Security.Cryptography.RSA]::Create() try { $rsa.ImportFromPem((Get-Content -Raw -LiteralPath $privateKeyPath)) $content = [IO.File]::ReadAllBytes($zipPath) $signature = $rsa.SignData( $content, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pss) [IO.File]::WriteAllBytes($signaturePath, $signature) } finally { $rsa.Dispose() } Write-Output $zipPath Write-Output $signaturePath } finally { if (Test-Path -LiteralPath $temporaryRoot) { Remove-Item -LiteralPath $temporaryRoot -Recurse -Force } }