Add one-command server and client bootstraps
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
#Requires -Version 5.1
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^\d+\.\d+\.\d+([-.][0-9A-Za-z.-]+)?$')]
|
||||
[string]$Version,
|
||||
[string]$ReleaseDirectory = (Join-Path $PSScriptRoot '..\artifacts\releases'),
|
||||
[uri]$GiteaBaseUri = 'https://gitea.lci.ulsa.mx',
|
||||
[string]$Owner = 'alexrg',
|
||||
[string]$Repository = 'SGU-CredentialProvider',
|
||||
[string]$TargetCommitish = 'main',
|
||||
[switch]$Draft
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$tagName = "v$Version"
|
||||
$assetPaths = @(
|
||||
(Join-Path $ReleaseDirectory "sgu-client-bootstrap-$Version.zip"),
|
||||
(Join-Path $ReleaseDirectory "sgu-server-bootstrap-$Version.zip"),
|
||||
(Join-Path $ReleaseDirectory "SHA256SUMS-$Version.txt")
|
||||
)
|
||||
foreach ($assetPath in $assetPaths) {
|
||||
if (-not (Test-Path -LiteralPath $assetPath -PathType Leaf)) {
|
||||
throw "Release asset is missing: $assetPath"
|
||||
}
|
||||
}
|
||||
|
||||
$token = $env:GITEA_TOKEN
|
||||
if (-not $token) {
|
||||
$credentialInput = "protocol=$($GiteaBaseUri.Scheme)`nhost=$($GiteaBaseUri.Host)`n`n"
|
||||
$credentialOutput = $credentialInput | & git credential fill
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Git Credential Manager could not supply Gitea credentials. Set GITEA_TOKEN for this process.'
|
||||
}
|
||||
$credentialValues = @{}
|
||||
foreach ($line in $credentialOutput) {
|
||||
$parts = $line -split '=', 2
|
||||
if ($parts.Count -eq 2) {
|
||||
$credentialValues[$parts[0]] = $parts[1]
|
||||
}
|
||||
}
|
||||
$token = $credentialValues.password
|
||||
}
|
||||
if (-not $token) {
|
||||
throw 'No Gitea token is available. Set GITEA_TOKEN for this process or sign in through Git Credential Manager.'
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Net.Http
|
||||
$handler = [Net.Http.HttpClientHandler]::new()
|
||||
$client = [Net.Http.HttpClient]::new($handler)
|
||||
$client.BaseAddress = [uri]($GiteaBaseUri.AbsoluteUri.TrimEnd('/') + '/')
|
||||
$client.DefaultRequestHeaders.Authorization = [Net.Http.Headers.AuthenticationHeaderValue]::new('token', $token)
|
||||
$client.DefaultRequestHeaders.UserAgent.ParseAdd('SGU-CredentialProvider-Release/1.0')
|
||||
|
||||
function Invoke-GiteaJson {
|
||||
param(
|
||||
[Parameter(Mandatory)][Net.Http.HttpMethod]$Method,
|
||||
[Parameter(Mandatory)][string]$RelativeUri,
|
||||
[object]$Body,
|
||||
[switch]$AllowNotFound
|
||||
)
|
||||
|
||||
$request = [Net.Http.HttpRequestMessage]::new($Method, $RelativeUri)
|
||||
if ($null -ne $Body) {
|
||||
$json = $Body | ConvertTo-Json -Depth 6
|
||||
$request.Content = [Net.Http.StringContent]::new($json, [Text.Encoding]::UTF8, 'application/json')
|
||||
}
|
||||
try {
|
||||
$response = $client.SendAsync($request).GetAwaiter().GetResult()
|
||||
if ($AllowNotFound -and $response.StatusCode -eq [Net.HttpStatusCode]::NotFound) {
|
||||
return $null
|
||||
}
|
||||
$content = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
|
||||
if (-not $response.IsSuccessStatusCode) {
|
||||
throw "Gitea returned HTTP $([int]$response.StatusCode): $content"
|
||||
}
|
||||
if ($content) {
|
||||
return $content | ConvertFrom-Json
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$request.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$encodedOwner = [Uri]::EscapeDataString($Owner)
|
||||
$encodedRepository = [Uri]::EscapeDataString($Repository)
|
||||
$encodedTag = [Uri]::EscapeDataString($tagName)
|
||||
$repositoryPath = "api/v1/repos/$encodedOwner/$encodedRepository"
|
||||
$existingRelease = Invoke-GiteaJson -Method ([Net.Http.HttpMethod]::Get) `
|
||||
-RelativeUri "$repositoryPath/releases/tags/$encodedTag" -AllowNotFound
|
||||
if ($existingRelease) {
|
||||
throw "Release $tagName already exists. Choose a new version."
|
||||
}
|
||||
|
||||
$releaseNotes = @"
|
||||
Bootstrap reproducible para el laboratorio SGU.
|
||||
|
||||
- `sgu-server-bootstrap-$Version.zip`: crea el bosque AD/DNS, OUs, grupo RDP, GPO, recurso `Packages`, broker mTLS y administración remota; se reanuda solo después del reinicio.
|
||||
- `sgu-client-bootstrap-$Version.zip`: registra un certificado mTLS único, instala y valida el Credential Provider antes de unir el equipo al dominio, habilita RDP/WinRM y se repara al arranque.
|
||||
- Windows Home se detecta y se rechaza con una explicación, ya que no admite unión a Active Directory ni RDP host.
|
||||
|
||||
Las contraseñas se solicitan de forma interactiva y no se escriben en archivos ni en la línea de comandos. Verifique los ZIP con `SHA256SUMS-$Version.txt`.
|
||||
"@
|
||||
$releaseBody = [ordered]@{
|
||||
tag_name = $tagName
|
||||
target_commitish = $TargetCommitish
|
||||
name = "SGU Credential Provider $Version"
|
||||
body = $releaseNotes
|
||||
draft = [bool]$Draft
|
||||
prerelease = $Version -match '-'
|
||||
}
|
||||
|
||||
if (-not $PSCmdlet.ShouldProcess("$Owner/$Repository $tagName", 'Create Gitea release and upload bootstrap assets')) {
|
||||
return
|
||||
}
|
||||
|
||||
$release = Invoke-GiteaJson -Method ([Net.Http.HttpMethod]::Post) `
|
||||
-RelativeUri "$repositoryPath/releases" -Body $releaseBody
|
||||
|
||||
foreach ($assetPath in $assetPaths) {
|
||||
$assetName = Split-Path $assetPath -Leaf
|
||||
$uploadUri = "$repositoryPath/releases/$($release.id)/assets?name=$([Uri]::EscapeDataString($assetName))"
|
||||
$stream = [IO.File]::OpenRead((Resolve-Path -LiteralPath $assetPath).Path)
|
||||
$multipart = [Net.Http.MultipartFormDataContent]::new()
|
||||
$fileContent = [Net.Http.StreamContent]::new($stream)
|
||||
$fileContent.Headers.ContentType = [Net.Http.Headers.MediaTypeHeaderValue]::new('application/octet-stream')
|
||||
$multipart.Add($fileContent, 'attachment', $assetName)
|
||||
try {
|
||||
$response = $client.PostAsync($uploadUri, $multipart).GetAwaiter().GetResult()
|
||||
$content = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
|
||||
if (-not $response.IsSuccessStatusCode) {
|
||||
throw "Gitea asset upload returned HTTP $([int]$response.StatusCode): $content"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$multipart.Dispose()
|
||||
$stream.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
Tag = $tagName
|
||||
ReleaseName = $release.name
|
||||
ReleaseUrl = $release.html_url
|
||||
Draft = [bool]$release.draft
|
||||
Assets = $assetPaths | ForEach-Object { Split-Path $_ -Leaf }
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$token = $null
|
||||
$client.Dispose()
|
||||
$handler.Dispose()
|
||||
}
|
||||
Reference in New Issue
Block a user