diff --git a/.github/workflows/build_sign_release.yaml b/.github/workflows/build_sign_release.yaml index 85adf7b322..885d8326b7 100644 --- a/.github/workflows/build_sign_release.yaml +++ b/.github/workflows/build_sign_release.yaml @@ -10,6 +10,13 @@ on: description: "Release Name" required: true type: string + # Note: This is NOT the ACTUAL release version for ScubaGear. + # That value is found in ScubaGear.psd1. + # This is only used for things like the release file name. + # Yes, this is a disconnect that violates DRY. + # Note: It's possible that this value could be retrieved from ScubaGear.psd1 + # using a function similar to Set-ScubaGearVersionManifest in + # utils/workflow/Set-ScubaGearModuleVersion.psm1. version: description: "Release Version (e.g., 1.2.4)" required: true @@ -27,8 +34,6 @@ jobs: name: Build and Draft Release runs-on: windows-latest environment: Development - env: - RELEASE_VERSION: ${{ inputs.version }} permissions: id-token: write contents: write @@ -44,9 +49,10 @@ jobs: path: repo - name: Install Azure Signing Tool run: | - dotnet --version - dotnet tool install --global AzureSignTool --version 5.0.0 - # OIDC Login to Azure Public Cloud with AzPowershell (enableAzPSSession true) + # Source the function + . repo/utils/workflow/Install-AzureSignTool.ps1 + Install-AzureSignTool + # OpenID Connect (OIDC) login to Azure Public Cloud with AzPowershell - name: Login to Azure uses: azure/login@v2 with: @@ -64,25 +70,13 @@ jobs: echo "KeyVaultCertificateName=$($KeyVaultInfo.KeyVault.CertificateName)" >> $env:GITHUB_OUTPUT - name: Sign Module run: | - # Source the deploy utilities so the functions in it can be called. - . repo/utils/workflow/Publish-ScubaGear.ps1 - # Remove non-release files - Remove-Item -Recurse -Force repo -Include .git* - Write-Output "Creating an array of the files to sign..." - $ArrayOfFilePaths = New-ArrayOfFilePaths ` - -ModuleDestinationPath repo - Write-Output "Creating a file with a list of the files to sign..." - $FileListFileName = New-FileList ` - -ArrayOfFilePaths $ArrayOfFilePaths - Write-Output "Calling AzureSignTool function to sign scripts, manifest, and modules..." - $AzureKeyVaultUrl = '${{ steps.key-vault-info.outputs.KeyVaultUrl }}' - $CertificateName = '${{ steps.key-vault-info.outputs.KeyVaultCertificateName }}' - Use-AzureSignTool ` - -AzureKeyVaultUrl $AzureKeyVaultUrl ` - -CertificateName $CertificateName ` - -FileList $FileListFileName - Move-Item -Path repo -Destination "ScubaGear-${env:RELEASE_VERSION}" -Force - Compress-Archive -Path "ScubaGear-${env:RELEASE_VERSION}" -DestinationPath "ScubaGear-${env:RELEASE_VERSION}.zip" + # Source the function. + . repo/utils/workflow/Build-SignRelease.ps1 + New-ModuleSignature ` + -AzureKeyVaultUrl ${{ steps.key-vault-info.outputs.KeyVaultUrl }} ` + -CertificateName ${{ steps.key-vault-info.outputs.KeyVaultCertificateName }} ` + -ReleaseVersion ${{ inputs.version }} ` + -RootFolderName "repo" - name: Create Release uses: softprops/action-gh-release@v1 id: create-release diff --git a/.github/workflows/publish_private_package.yaml b/.github/workflows/publish_private_package.yaml index 6e92a4e323..e464950b66 100644 --- a/.github/workflows/publish_private_package.yaml +++ b/.github/workflows/publish_private_package.yaml @@ -40,9 +40,10 @@ jobs: path: repo - name: Install Azure Signing Tool run: | - dotnet --version - dotnet tool install --global AzureSignTool --version 5.0.0 - # OIDC Login to Azure Public Cloud with AzPowershell (enableAzPSSession true) + # Source the function + . repo/utils/workflow/Install-AzureSignTool.ps1 + Install-AzureSignTool + # OIDC Login to Azure Public Cloud with AzPowershell - name: Login to Azure uses: azure/login@v2 with: @@ -60,13 +61,13 @@ jobs: echo "KeyVaultCertificateName=$($KeyVaultInfo.KeyVault.CertificateName)" >> $env:GITHUB_OUTPUT - name: Create Private Gallery run: | - # Source the deploy utilities so the functions in it can be called. + # Source the function. . repo/utils/workflow/Publish-ScubaGear.ps1 cd repo New-PrivateGallery -GalleryName $env:GalleryName -Trusted - name: Sign and Publish Module run: | - # Source the deploy utilities so the functions in it can be called. + # Source the function. . repo/utils/workflow/Publish-ScubaGear.ps1 # Remove non-release files Remove-Item -Recurse -Force repo -Include .git* diff --git a/.github/workflows/publish_public_package.yaml b/.github/workflows/publish_public_package.yaml index 49a61f9795..ca4185ef05 100644 --- a/.github/workflows/publish_public_package.yaml +++ b/.github/workflows/publish_public_package.yaml @@ -46,9 +46,10 @@ jobs: path: repo - name: Install Azure Signing Tool run: | - dotnet --version - dotnet tool install --global AzureSignTool --version 5.0.0 - # OIDC Login to Azure Public Cloud with AzPowershell (enableAzPSSession true) + # Source the function + . repo/utils/workflow/Install-AzureSignTool.ps1 + Install-AzureSignTool + # OIDC Login to Azure Public Cloud with AzPowershell - name: Login to Azure uses: azure/login@v2 with: @@ -66,7 +67,7 @@ jobs: echo "KeyVaultCertificateName=$($KeyVaultInfo.KeyVault.CertificateName)" >> $env:GITHUB_OUTPUT - name: Sign and Publish Module run: | - # Source the deploy utilities so the functions in it can be called. + # Source the function. . repo/utils/workflow/Publish-ScubaGear.ps1 # Remove non-release files Remove-Item -Recurse -Force repo -Include .git* diff --git a/Testing/workflow/Build-SignRelease.Tests.ps1 b/Testing/workflow/Build-SignRelease.Tests.ps1 new file mode 100644 index 0000000000..57a2de23d4 --- /dev/null +++ b/Testing/workflow/Build-SignRelease.Tests.ps1 @@ -0,0 +1,18 @@ +# The purpose of this test to ensure that the function fails +# gracefully if the root folder name does not exist. +# Note: Functional testing (not unit testing) should be used +# to verify that AST itself actually works. + +Describe "Bad Inputs Check" { + It "The root folder name should exist" { + $ScriptPath = Join-Path -Path $PSScriptRoot -ChildPath '../../utils/workflow/Build-SignRelease.ps1' -Resolve + # Source the function + . $ScriptPath + # The function should throw an exception if the root folder name does not exist. + { New-ModuleSignature ` + -AzureKeyVaultUrl "https://www.cisa.gov" ` + -CertificateName "certificate name" ` + -ReleaseVersion "0.0.1" ` + -RootFolderName "nonexistantfoldername" } | Should -Throw + } +} diff --git a/Testing/workflow/Install-AzureSignTool.Tests.ps1 b/Testing/workflow/Install-AzureSignTool.Tests.ps1 new file mode 100644 index 0000000000..7fa6a378aa --- /dev/null +++ b/Testing/workflow/Install-AzureSignTool.Tests.ps1 @@ -0,0 +1,21 @@ +# The purpose of this test is to verify that Azure Sign Tool is working. + +BeforeDiscovery { + $ScriptPath = Join-Path -Path $PSScriptRoot -ChildPath '../../utils/workflow/Install-AzureSignTool.ps1' -Resolve + # Source the function + . $ScriptPath + Install-AzureSignTool +} + +Describe "AST Check" { + It "Dotnet should be installed" { + $ToolPath = (Get-Command dotnet).Path + Write-Warning "The path to dotnet is $ToolPath" + Test-Path -Path $ToolPath | Should -Be $true + } + It "AST should be installed" { + $ToolPath = (Get-Command AzureSignTool).Path + Write-Warning "The path to AzureSignTool is $ToolPath" + Test-Path -Path $ToolPath | Should -Be $true + } +} \ No newline at end of file diff --git a/utils/workflow/Build-SignRelease.ps1 b/utils/workflow/Build-SignRelease.ps1 new file mode 100644 index 0000000000..d3f47c99d1 --- /dev/null +++ b/utils/workflow/Build-SignRelease.ps1 @@ -0,0 +1,65 @@ +function New-ModuleSignature { + <# + .SYNOPSIS + Sign the ScubaGear module. + .PARAMETER $AzureKeyVaultUrl + The URL for the KeyVault in Azure. + .PARAMETER $CertificateName + The name of the certificate stored in the KeyVault. + .PARAMETER $ReleaseVersion + The version number of the release (e.g., 1.5.1). + .PARAMETER $RootFolderName + The name of the root folder. + .EXCEPTIONS + System.IO.DirectoryNotFoundException + Thrown if $RootFolderName does not exist. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $AzureKeyVaultUrl, + [Parameter(Mandatory = $true)] + [string] + $CertificateName, + [Parameter(Mandatory = $true)] + [string] + $ReleaseVersion, + [Parameter(Mandatory = $true)] + [string] + $RootFolderName + ) + + Write-Warning "Signing the module with AzureSignTool..." + + # Verify that $RootFolderName exists + Write-Warning "The root folder name is $RootFolderName" + if (Test-Path -Path $RootFolderName) { + Write-Warning "Directory exists" + } else { + Write-Warning "Directory does not exist; throwing an exception..." + throw [System.IO.DirectoryNotFoundException] "Directory not found: $RootFolderName" + } + + # Source the deploy utilities so the functions in it can be called. + $PublishPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\utils\workflow\Publish-ScubaGear.ps1' -Resolve + . $PublishPath + + # Remove non-release files, like the .git dir, required for non-Windows machines + Remove-Item -Recurse -Force $RootFolderName -Include .git* + Write-Warning "Creating an array of the files to sign..." + $ArrayOfFilePaths = New-ArrayOfFilePaths ` + -ModuleDestinationPath $RootFolderName + + Write-Warning "Creating a file with a list of the files to sign..." + $FileListFileName = New-FileList ` + -ArrayOfFilePaths $ArrayOfFilePaths + + Write-Warning "Calling AzureSignTool function to sign scripts, manifest, and modules..." + Use-AzureSignTool ` + -AzureKeyVaultUrl $AzureKeyVaultUrl ` + -CertificateName $CertificateName ` + -FileList $FileListFileName + Move-Item -Path $RootFolderName -Destination "ScubaGear-$ReleaseVersion" -Force + Compress-Archive -Path "ScubaGear-$ReleaseVersion" -DestinationPath "ScubaGear-$ReleaseVersion.zip" +} diff --git a/utils/workflow/Install-AzureSignTool.ps1 b/utils/workflow/Install-AzureSignTool.ps1 new file mode 100644 index 0000000000..a8cb39e77d --- /dev/null +++ b/utils/workflow/Install-AzureSignTool.ps1 @@ -0,0 +1,10 @@ +function Install-AzureSignTool { + <# + .SYNOPSIS + Install Azure Signing Tool + #> + + Write-Warning "Installing AST..." + + dotnet tool install --global AzureSignTool --version 5.0.0 +} \ No newline at end of file diff --git a/utils/workflow/Publish-ScubaGear.ps1 b/utils/workflow/Publish-ScubaGear.ps1 index 9a860b5d7d..70f8b6c46d 100644 --- a/utils/workflow/Publish-ScubaGear.ps1 +++ b/utils/workflow/Publish-ScubaGear.ps1 @@ -381,7 +381,7 @@ function New-FileList { function Use-AzureSignTool { <# - .DESCRIPTION + .SYNOPSIS AzureSignTool is a utility for signing code that is used to secure ScubaGear. https://github.com/vcsjones/AzureSignTool Throws an error if there was an error signing the files. @@ -429,7 +429,6 @@ function Use-AzureSignTool { Write-Warning "The path to AzureSignTool is $ToolPath" # & is the call operator that executes a command, script, or function. $Results = & $ToolPath $SignArguments - # Test the results for failures. # If there are no failures, the $SuccessPattern string will be the last # line in the results.