Skip to content

Commit

Permalink
CI: Sign and package Windows builds
Browse files Browse the repository at this point in the history
  • Loading branch information
derrod authored and RytoEX committed Jan 9, 2024
1 parent 07dd32a commit 83e6517
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/actions/bouf/Ensure-Location.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function Ensure-Location {
<#
.SYNOPSIS
Ensures current location to be set to specified directory.
.DESCRIPTION
If specified directory exists, switch to it. Otherwise create it,
then switch.
.EXAMPLE
Ensure-Location "My-Directory"
Ensure-Location -Path "Path-To-My-Directory"
#>

param(
[Parameter(Mandatory)]
[string] $Path
)

if ( ! ( Test-Path $Path ) ) {
$_Params = @{
ItemType = "Directory"
Path = ${Path}
ErrorAction = "SilentlyContinue"
}

New-Item @_Params | Set-Location
} else {
Set-Location -Path ${Path}
}
}
40 changes: 40 additions & 0 deletions .github/actions/bouf/Invoke-External.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function Invoke-External {
<#
.SYNOPSIS
Invokes a non-PowerShell command.
.DESCRIPTION
Runs a non-PowerShell command, and captures its return code.
Throws an exception if the command returns non-zero.
.EXAMPLE
Invoke-External 7z x $MyArchive
#>

if ( $args.Count -eq 0 ) {
throw 'Invoke-External called without arguments.'
}

if ( ! ( Test-Path function:Log-Information ) ) {
. $PSScriptRoot/Logger.ps1
}

$Command = $args[0]
$CommandArgs = @()

if ( $args.Count -gt 1) {
$CommandArgs = $args[1..($args.Count - 1)]
}

$_EAP = $ErrorActionPreference
$ErrorActionPreference = "Continue"

Log-Debug "Invoke-External: ${Command} ${CommandArgs}"

& $command $commandArgs
$Result = $LASTEXITCODE

$ErrorActionPreference = $_EAP

if ( $Result -ne 0 ) {
throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
}
}
149 changes: 149 additions & 0 deletions .github/actions/bouf/Logger.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
function Log-Debug {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
foreach($m in $Message) {
Write-Debug $m
}
}
}

function Log-Verbose {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
foreach($m in $Message) {
Write-Verbose $m
}
}
}

function Log-Warning {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
foreach($m in $Message) {
Write-Warning $m
}
}
}

function Log-Error {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
foreach($m in $Message) {
Write-Error $m
}
}
}

function Log-Information {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
if ( ! ( $script:Quiet ) ) {
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
$Icon = ' =>'

foreach($m in $Message) {
Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) "
Write-Host "${m}"
}
}
}
}

function Log-Group {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
[string[]] $Message
)

Process {
if ( $Env:CI -ne $null ) {
if ( $script:LogGroup ) {
Write-Output '::endgroup::'
$script:LogGroup = $false
}

if ( $Message.count -ge 1 ) {
Write-Output "::group::$($Message -join ' ')"
$script:LogGroup = $true
}
} else {
if ( $Message.count -ge 1 ) {
Log-Information $Message
}
}
}
}

function Log-Status {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
if ( ! ( $script:Quiet ) ) {
$StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
$Icon = ' >'

foreach($m in $Message) {
Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) "
Write-Host "${m}"
}
}
}
}

function Log-Output {
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string[]] $Message
)

Process {
if ( ! ( $script:Quiet ) ) {
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
$Icon = ''

foreach($m in $Message) {
Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}"
}
}
}
}

$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5
133 changes: 133 additions & 0 deletions .github/actions/bouf/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Run bouf
description: Generates signed OBS install files and updater files
inputs:
gcpWorkloadIdentityProvider:
description: GCP Identity Provider Pool ID
required: true
gcpServiceAccountName:
description: Google service account name
required: true
gcpManifestSigningKeyName:
description: Name of the manifest signing key in GCP KMS
required: false
version:
description: Version string (e.g., 30.0.0-rc1)
required: true
channel:
description: Update channel
required: false
default: 'stable'

runs:
using: composite
steps:
- name: Extract Artifact
shell: pwsh
run: |
Expand-Archive -Path build\*.zip -DestinationPath build
Remove-Item build\*.zip
- name: Setup bouf
shell: pwsh
env:
BOUF_TAG: 'v0.6.1'
BOUF_HASH: '7292e43186ecc6210079fa5702254455797c7652dc6b08b5b61ac2d721766d86'
BOUF_NSIS_HASH: '2f5ecff05a002913c10aafa838febc1b0ae6e779f5ca67efa545ed787ae485a0'
GH_TOKEN: ${{ github.token }}
run: |
# Download bouf release
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
Ensure-Location bouf
$windows_zip = "bouf-windows-${env:BOUF_TAG}.zip"
$nsis_zip = "bouf-nsis-${env:BOUF_TAG}.zip"
Invoke-External gh release download "${env:BOUF_TAG}" -R "obsproject/bouf" -p $windows_zip -p $nsis_zip
if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne "${env:BOUF_HASH}") {
throw "bouf hash does not match."
}
if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne "${env:BOUF_NSIS_HASH}") {
throw "NSIS package hash does not match."
}
Expand-Archive -Path $windows_zip -DestinationPath bin
Expand-Archive -Path $nsis_zip -DestinationPath nsis
- name: Download Google CNG Provider
shell: pwsh
env:
CNG_TAG: 'cng-v1.0'
GH_TOKEN: ${{ github.token }}
run: |
# Download Google CNG provider release from github
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
Ensure-Location gcng
Invoke-External gh release download "${env:CNG_TAG}" -R "GoogleCloudPlatform/kms-integrations" -p "*amd64.zip"
Expand-Archive -Path *.zip
$sigPath = Get-ChildItem *.sig -Recurse
$msiPath = Get-ChildItem *.msi -Recurse
# Verify digital signature against Google's public key
Invoke-External openssl dgst -sha384 -verify "${env:GITHUB_ACTION_PATH}/cng-release-signing-key.pem" -signature $sigPath $msiPath
# Finally, install the CNG provider
Invoke-External msiexec /i $msiPath /qn /norestart
- name: Install pandoc and rclone
shell: pwsh
run: |
choco install rclone --version 1.64.2 -y --no-progress
choco install pandoc --version 3.1.9 -y --no-progress
- name: Prepare Release Notes
shell: pwsh
run: |
# Release notes are just the tag body on Windows
Set-Location repo
git tag -l --format='%(contents:body)' ${{ inputs.version }} > "${{ github.workspace }}/notes.rst"
- name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033'
with:
workload_identity_provider: ${{ inputs.gcpWorkloadIdentityProvider }}
service_account: ${{ inputs.gcpServiceAccountName }}

- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@e30db14379863a8c79331b04a9969f4c1e225e0b'

- name: Download Old Builds
shell: pwsh
env:
RCLONE_GCS_ENV_AUTH: 'true'
run: |
rclone copy --transfers 100 :gcs:obs-builds "${{ github.workspace }}/old_builds"
- name: Run bouf
shell: pwsh
run: |
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
$boufArgs = @(
"--config", "${env:GITHUB_ACTION_PATH}/config.toml",
"--version", "${{ inputs.version }}"
"--branch", "${{ inputs.channel }}"
"--notes-file", "${{ github.workspace }}/notes.rst"
"-i", "${{ github.workspace }}/build"
"-p", "${{ github.workspace }}/old_builds"
"-o", "${{ github.workspace }}/output"
)
Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs
- name: Sign Updater Manifest
shell: pwsh
if: inputs.gcpManifestSigningKeyName != ''
run: |
$gcloudArgs = @(
"--input-file", "${{ github.workspace }}/output/manifest.json"
"--signature-file", "${{ github.workspace }}/output/manifest.json.sig"
"--digest-algorithm", "sha512"
"--location", "global"
"--keyring", "production"
"--key", "${{ inputs.gcpManifestSigningKeyName }}"
"--version", "1"
)
gcloud kms asymmetric-sign @gcloudArgs
5 changes: 5 additions & 0 deletions .github/actions/bouf/cng-release-signing-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtfLbXkHUVc9oUPTNyaEK3hIwmuGRoTtd
6zDhwqjJuYaMwNd1aaFQLMawTwZgR0Xn27ymVWtqJHBe0FU9BPIQ+SFmKw+9jSwu
/FuqbJnLmTnWMJ1jRCtyHNZawvv2wbiB
-----END PUBLIC KEY-----
Loading

0 comments on commit 83e6517

Please sign in to comment.