Skip to content

Commit

Permalink
Add GitHub Workflow Actions and Bug Fixes (#13)
Browse files Browse the repository at this point in the history
1. Moves the CI-build pipeline from Azure DevOps to GitHub Actions
2. Add SDL compliance (using DevSkim) pipeline
3. Add release pipeline
    - Updates the manifest version
    - Signs the PowerShell scripts
    - Creates a GitHub release
    - Publishes the module to the PowerShell Gallery
4. Fix PSScriptAnalyzer issues
5. Bug fixes
    - Suppress nested confirm prompts

Co-authored-by: TinaMor <[email protected]>
  • Loading branch information
TinaMor and TinaMor authored May 23, 2024
1 parent 66ff9a9 commit 3661edf
Show file tree
Hide file tree
Showing 29 changed files with 1,253 additions and 364 deletions.
64 changes: 64 additions & 0 deletions .github/actions/sign-scripts/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
###########################################################################
# #
# Copyright (c) Microsoft Corporation. All rights reserved. #
# #
# This code is licensed under the MIT License (MIT). #
# #
###########################################################################

name: Sign PowerShell Scripts
description: Sign PowerShell scripts in the Containers-Toolkit repository

inputs:
Directory:
description: The directory containing files to sign
default: "./"
required: false
AzureKeyVaultUrl:
description: "The URL to an Azure Key Vault."
required: true
AzureKeyVaultClientId:
description: "The Client ID (Application ID) to authenticate to the Azure Key Vault."
required: true
AzureKeyVaultClientSecret:
description: "The client secret of your Azure application to authenticate to the Azure Key Vault."
required: true
AzureKeyVaultTenantId:
description: "The Tenant Id to authenticate to the Azure Key Vault."
required: true
AzureKeyVaultCertificate:
description: "The name of the certificate in Azure Key Vault."
required: true

runs:
using: "composite"
steps:
- name: Setup .NET Core # Required to execute ReportGenerator
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.x
dotnet-quality: "ga"

- uses: actions/cache@v3
with:
path: ~/.dotnet/tools
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-nuget-azuresigntool

- name: Install AzureSignTool
shell: pwsh
run: dotnet tool install --global AzureSignTool

# https://learn.microsoft.com/en-us/windows/msix/desktop/cicd-keyvault
# https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653556(v=vs.85)
- name: Sign PowerShell scripts
shell: pwsh
run: |
$akvParams = @{
'AzureKeyVaultClientId' = ${{ inputs.AzureKeyVaultClientId }}
'AzureKeyVaultTenantId' = ${{ inputs.AzureKeyVaultTenantId }}
'AzureKeyVaultClientSecret' = ${{ inputs.AzureKeyVaultClientSecret }}
'AzureKeyVaultUrl' = ${{ inputs.AzureKeyVaultUrl }}
'AzureKeyVaultCertificate' = ${{ inputs.AzureKeyVaultCertificate }}
}
.github/actions/sign-scripts/sign-scripts.ps1 -Directory "${{ inputs.Directory }}" @akvParams
123 changes: 123 additions & 0 deletions .github/actions/sign-scripts/sign-scripts.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
###########################################################################
# #
# Copyright (c) Microsoft Corporation. All rights reserved. #
# #
# This code is licensed under the MIT License (MIT). #
# #
###########################################################################

<#
.SYNOPSIS
Sign PowerShell scripts (ps1, psm1, psd1, ps1xml) using AzureSignTool.
.PARAMETER Directory
Directory containing PowerShell script files.
Defaults to the current working directory (.).
.PARAMETER AzureKeyVaultUrl
The URL to an Azure Key Vault.
.PARAMETER AzureKeyVaultClientId
The Client ID (Application ID) to authenticate to the Azure Key Vault.
.PARAMETER AzureKeyVaultClientSecret
The client secret of your Azure application to authenticate to the Azure Key Vault.
.PARAMETER AzureKeyVaultTenantId
The Tenant Id to authenticate to the Azure Key Vault.
.PARAMETER AzureKeyVaultCertificate
The name of the certificate in Azure Key Vault.
#>


[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[String]$Directory = ".",
[Parameter(Mandatory = $true)]
[String]$AzureKeyVaultUrl,
[Parameter(Mandatory = $true)]
[String]$AzureKeyVaultClientId,
[Parameter(Mandatory = $true)]
[String]$AzureKeyVaultClientSecret,
[Parameter(Mandatory = $true)]
[String]$AzureKeyVaultTenantId,
[Parameter(Mandatory = $true)]
[String]$AzureKeyVaultCertificate
)

$Script:Directory = $Directory
$Script:AzureKeyVaultUrl = $AzureKeyVaultUrl
$Script:AzureKeyVaultClientId = $AzureKeyVaultClientId
$Script:AzureKeyVaultClientSecret = $AzureKeyVaultClientSecret
$Script:AzureKeyVaultTenantId = $AzureKeyVaultTenantId
$Script:AzureKeyVaultCertificate = $AzureKeyVaultCertificate

function Invoke-ExecutableCommand {
param (
[parameter(Mandatory)]
[String] $executable,
[parameter(Mandatory)]
[String] $arguments
)

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $executable
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $arguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()

return $p
}

function Invoke-AzureSignTool {
# Get scripts to sign
$scripts = Get-ChildItem -Path "$Script:Directory" -ErrorAction Stop -Exclude ".github" | `
Get-ChildItem -Recurse -ErrorAction Stop | `
Where-Object { $_.name -match "ps[d|m]?1(xml)?" }

# Sign the scripts
foreach ($script in $scripts) {
Write-Output "Signing file: $($script.Name)"

$params = @{
'kvu' = "`"$Script:AzureKeyVaultUrl`""
'kvi' = "`"$Script:AzureKeyVaultClientId`""
'kvs' = "`"$Script:AzureKeyVaultClientSecret`""
'kvt' = "`"$Script:AzureKeyVaultTenantId`""
'kvc' = "`"$Script:AzureKeyVaultCertificate`""
# timestamp-rfc3161
'tr' = 'http://timestamp.digicert.com '
}

# Convert params to string
$arguments = "sign"
foreach ($kv in $params.GetEnumerator()) {
$arguments += " -$($kv.Name) $($kv.Value)"
}

$arguments += " -v $($script.FullName)"

$output = Invoke-ExecutableCommand -Executable 'AzureSignTool' -Arguments $arguments
if ($output.ExitCode -ne 0) {
$err = $output.StandardError.ReadToEnd()
if ([string]::IsNullOrEmpty($err)) {
$err = $output.StandardOutput.ReadToEnd()
}

Write-Error "Failed to sign script $($script.Name). Error code: ($($output.ExitCode))."
Throw $err
}
}
}

# Execute
Invoke-AzureSignTool
170 changes: 170 additions & 0 deletions .github/workflows/ci-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
###########################################################################
# #
# Copyright (c) Microsoft Corporation. All rights reserved. #
# #
# This code is licensed under the MIT License (MIT). #
# #
###########################################################################

name: CI Build

on:
workflow_dispatch:
inputs:
runner:
description: "Windows runner image"
required: false
default: windows-2022
type: choice
options:
- windows-latest
- windows-2022
- windows-2019

pull_request:
branches: ["main", "releases/**"]
paths-ignore:
- "docs/**"
- "*.md"
- en-US/**"

env:
MODULE_DIR: .\Containers-Toolkit
BUILD_SCRIPTS_DIR: .\build\scripts
PESTER_RESULTS_DIR: .\TestResults
MODULE_ARTIFACT: CTK.Module.Scripts
REPO_ARTIFACT: CTK.Scripts

jobs:
lint:
runs-on: ${{ github.event.inputs.RUNNER || 'windows-2022' }}
steps:
- name: Checkout code
uses: actions/checkout@v4

# FIXME: Fix cache not working
- name: Setup PowerShell module cache
id: cacher
uses: actions/cache@v3
with:
path: "C:\\program files\\powershell\\7\\Modules"
key: ${{ runner.os }}-CTK

- name: Install required PowerShell modules
if: steps.cacher.outputs.cache-hit != 'true'
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
$ModuleName = 'PSScriptAnalyzer'
if (-not (Get-Module -ListAvailable -Name $ModuleName)) {
Write-Output "Modules to install: '$ModuleName'"
Install-Module $ModuleName -ErrorAction Stop -AllowClobber -SkipPublisherCheck -Force
}
- name: Run code analysis with PSScriptAnalyzer
id: code_analysis
shell: pwsh
run: |
${{ env.BUILD_SCRIPTS_DIR }}\script-analyzer.ps1 | Out-File -FilePath lintsummary.md -Encoding utf8 -Force
cat lintsummary.md >> $env:GITHUB_STEP_SUMMARY
$fileExists = Test-Path ./psscriptanalysis.xml
echo "LINTSUMMARY_EXISTS=$fileExists" >> $env:GITHUB_OUTPUT
- name: Publish PSScriptAnalyzer results file
if: steps.code_analysis.outputs.LINTSUMMARY_EXISTS == 'true'
uses: actions/upload-artifact@v4
with:
name: PSScriptAnalyzer.Results
path: psscriptanalysis.xml
if-no-files-found: error
overwrite: true

pester:
# TODO: Add Windows ARM64 support
runs-on: ${{ github.event.inputs.RUNNER || 'windows-2022' }}
steps:
- name: Checkout code
uses: actions/checkout@v4

# FIXME: Fix cache not working
- name: Setup PowerShell module cache
id: cacher
uses: actions/cache@v3
with:
path: "C:\\program files\\powershell\\7\\Modules"
key: ${{ runner.os }}-CTK

- name: Install required PowerShell modules
if: steps.cacher.outputs.cache-hit != 'true'
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
$requiredModules = @('Pester', 'ThreadJob', 'HNS')
$missingModules = $requiredModules | Where-Object { -not (Get-Module -ListAvailable -Name $_) }
if ($missingModules) {
Write-Output "Modules to install: $($missingModules -join ', ')"
Install-Module $missingModules -ErrorAction Stop -AllowClobber -SkipPublisherCheck -Force
}
- name: Run Pester tests
shell: pwsh
run: |
$ErrorActionPreference = 'Continue'
${{ env.BUILD_SCRIPTS_DIR }}\run-tests.ps1
- name: Publish Pester results
uses: actions/upload-artifact@v4
with:
name: CTK.Pester.Results
path: ${{ env.PESTER_RESULTS_DIR }}\Test-Results.xml
if-no-files-found: error
overwrite: true

- name: Publish code coverage results
uses: actions/upload-artifact@v4
with:
name: CTK.Coverage.Summary
path: ${{ env.PESTER_RESULTS_DIR }}\coverage.xml
if-no-files-found: error
overwrite: true

test-coverage:
needs: pester
runs-on: ubuntu-latest
steps:
- name: Download coverage results artifact
uses: actions/download-artifact@v4
with:
name: CTK.Coverage.Summary

- name: Setup .NET Core # Required to execute ReportGenerator
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.x
dotnet-quality: "ga"

- name: ReportGenerator
uses: danielpalme/[email protected]
with:
reports: coverage.xml
targetdir: coveragereport
reporttypes: HtmlInline;MarkdownSummaryGithub;Badges
historydir: coveragehistory

- name: Upload coverage report artifact
uses: actions/upload-artifact@v4
with:
name: coveragereport # Artifact name
path: coveragereport # Directory containing files to upload

- name: Publish coverage summary
uses: marocchino/sticky-pull-request-comment@v2
continue-on-error: true
with:
path: coveragereport/SummaryGithub.md

- name: Post results
run: |
cat coveragereport/SummaryGithub.md >> $GITHUB_STEP_SUMMARY
Loading

0 comments on commit 3661edf

Please sign in to comment.