Update deployment workflow for final HSM solution
This applies the changes from the separate Windows HSM signing prototype development to the official deployment workflow including: 1. Use of Azure Code Signing client 2. Sourcing signtool.exe from runner 3. Moving from batch to PowerShell for Windows signing script 4. Using the same signing process for .exe and .msi
This commit is contained in:
parent
441beb9de3
commit
5ecdf166fb
8 changed files with 38 additions and 376 deletions
122
.github/workflows/deployment-hsm-testing.yml
vendored
122
.github/workflows/deployment-hsm-testing.yml
vendored
|
|
@ -1,122 +0,0 @@
|
|||
name: Deployment HSM Testing
|
||||
run-name: ${{ inputs.tag_name }} / go ${{ inputs.go_version }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag_name:
|
||||
required: true
|
||||
type: string
|
||||
go_version:
|
||||
default: "1.21"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: "~1.17.1"
|
||||
install-only: true
|
||||
- name: Install Azure Code Signing Client
|
||||
shell: pwsh
|
||||
env:
|
||||
ACS_DIR: ${{ runner.temp }}\acs
|
||||
ACS_ZIP: ${{ runner.temp }}\acs.zip
|
||||
CORRELATION_ID: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
run: |
|
||||
# Download Azure Code Signing client containing the DLL needed for signtool in script/sign
|
||||
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Azure.CodeSigning.Client/1.0.38 -OutFile $Env:ACS_ZIP -Verbose
|
||||
Expand-Archive $Env:ACS_ZIP -Destination $Env:ACS_DIR -Force -Verbose
|
||||
|
||||
# Generate metadata file for signtool, used in signing box .exe and .msi
|
||||
@{
|
||||
CertificateProfileName = "GitHubInc"
|
||||
CodeSigningAccountName = "GitHubInc"
|
||||
CorrelationId = $Env:CORRELATION_ID
|
||||
Endpoint = "https://wus.codesigning.azure.net/"
|
||||
} | ConvertTo-Json | Out-File -FilePath $Env:METADATA_PATH
|
||||
|
||||
# Azure Code Signing leverages the environment variables for secrets that complement the metadata.json
|
||||
# file generated above (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
|
||||
# For more information, see https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet
|
||||
- name: Build release binaries
|
||||
shell: bash
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_CLIENT_ID }}
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.SPN_GITHUB_CLI_SIGNING }}
|
||||
AZURE_TENANT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_TENANT_ID }}
|
||||
DLIB_PATH: ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
TAG_NAME: ${{ inputs.tag_name }}
|
||||
run: script/release-hsm --local "$TAG_NAME" --platform windows --config .goreleaser-hsm.yml
|
||||
- name: Set up MSBuild
|
||||
id: setupmsbuild
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
- name: Build MSI
|
||||
shell: bash
|
||||
env:
|
||||
MSBUILD_PATH: ${{ steps.setupmsbuild.outputs.msbuildPath }}
|
||||
run: |
|
||||
for ZIP_FILE in dist/gh_*_windows_*.zip; do
|
||||
MSI_NAME="$(basename "$ZIP_FILE" ".zip")"
|
||||
MSI_VERSION="$(cut -d_ -f2 <<<"$MSI_NAME" | cut -d- -f1)"
|
||||
case "$MSI_NAME" in
|
||||
*_386 )
|
||||
source_dir="$PWD/dist/windows_windows_386"
|
||||
platform="x86"
|
||||
;;
|
||||
*_amd64 )
|
||||
source_dir="$PWD/dist/windows_windows_amd64_v1"
|
||||
platform="x64"
|
||||
;;
|
||||
*_arm64 )
|
||||
echo "skipping building MSI for arm64 because WiX 3.11 doesn't support it: https://github.com/wixtoolset/issues/issues/6141" >&2
|
||||
continue
|
||||
#source_dir="$PWD/dist/windows_windows_arm64"
|
||||
#platform="arm64"
|
||||
;;
|
||||
* )
|
||||
printf "unsupported architecture: %s\n" "$MSI_NAME" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
"${MSBUILD_PATH}\MSBuild.exe" ./build/windows/gh.wixproj -p:SourceDir="$source_dir" -p:OutputPath="$PWD/dist" -p:OutputName="$MSI_NAME" -p:ProductVersion="${MSI_VERSION#v}" -p:Platform="$platform"
|
||||
done
|
||||
- name: Sign .msi release binaries
|
||||
shell: pwsh
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_CLIENT_ID }}
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.SPN_GITHUB_CLI_SIGNING }}
|
||||
AZURE_TENANT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_TENANT_ID }}
|
||||
DLIB_PATH: ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
run: |
|
||||
Get-ChildItem "$Env:GITHUB_WORKSPACE/dist" -Filter *.msi | Foreach-Object {
|
||||
.\script\sign.ps1 $_.FullName
|
||||
}
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
path: |
|
||||
dist/*.zip
|
||||
dist/*.msi
|
||||
50
.github/workflows/deployment.yml
vendored
50
.github/workflows/deployment.yml
vendored
|
|
@ -1,4 +1,5 @@
|
|||
name: Deployment
|
||||
run-name: ${{ inputs.tag_name }} / go ${{ inputs.go_version }} / ${{ inputs.environment }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
|
|
@ -130,26 +131,43 @@ jobs:
|
|||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
- name: Obtain signing certificate
|
||||
id: obtain_cert
|
||||
if: inputs.environment == 'production'
|
||||
shell: bash
|
||||
run: |
|
||||
base64 -d <<<"$CERT_CONTENTS" > ./cert.pfx
|
||||
printf "cert-file=%s\n" ".\\cert.pfx" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
CERT_CONTENTS: ${{ secrets.WINDOWS_CERT_PFX }}
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: "~1.17.1"
|
||||
install-only: true
|
||||
- name: Install Azure Code Signing Client
|
||||
shell: pwsh
|
||||
env:
|
||||
ACS_DIR: ${{ runner.temp }}\acs
|
||||
ACS_ZIP: ${{ runner.temp }}\acs.zip
|
||||
CORRELATION_ID: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
run: |
|
||||
# Download Azure Code Signing client containing the DLL needed for signtool in script/sign
|
||||
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Azure.CodeSigning.Client/1.0.43 -OutFile $Env:ACS_ZIP -Verbose
|
||||
Expand-Archive $Env:ACS_ZIP -Destination $Env:ACS_DIR -Force -Verbose
|
||||
|
||||
# Generate metadata file for signtool, used in signing box .exe and .msi
|
||||
@{
|
||||
CertificateProfileName = "GitHubInc"
|
||||
CodeSigningAccountName = "GitHubInc"
|
||||
CorrelationId = $Env:CORRELATION_ID
|
||||
Endpoint = "https://wus.codesigning.azure.net/"
|
||||
} | ConvertTo-Json | Out-File -FilePath $Env:METADATA_PATH
|
||||
|
||||
# Azure Code Signing leverages the environment variables for secrets that complement the metadata.json
|
||||
# file generated above (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
|
||||
# For more information, see https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet
|
||||
- name: Build release binaries
|
||||
shell: bash
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_CLIENT_ID }}
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.SPN_GITHUB_CLI_SIGNING }}
|
||||
AZURE_TENANT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_TENANT_ID }}
|
||||
DLIB_PATH: ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
TAG_NAME: ${{ inputs.tag_name }}
|
||||
CERT_FILE: ${{ steps.obtain_cert.outputs.cert-file }}
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
|
||||
run: script/release --local "$TAG_NAME" --platform windows
|
||||
- name: Set up MSBuild
|
||||
id: setupmsbuild
|
||||
|
|
@ -184,12 +202,18 @@ jobs:
|
|||
esac
|
||||
"${MSBUILD_PATH}\MSBuild.exe" ./build/windows/gh.wixproj -p:SourceDir="$source_dir" -p:OutputPath="$PWD/dist" -p:OutputName="$MSI_NAME" -p:ProductVersion="${MSI_VERSION#v}" -p:Platform="$platform"
|
||||
done
|
||||
- name: Sign MSI
|
||||
- name: Sign .msi release binaries
|
||||
if: inputs.environment == 'production'
|
||||
shell: pwsh
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_CLIENT_ID }}
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.SPN_GITHUB_CLI_SIGNING }}
|
||||
AZURE_TENANT_ID: ${{ secrets.SPN_GITHUB_CLI_SIGNING_TENANT_ID }}
|
||||
DLIB_PATH: ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
|
||||
METADATA_PATH: ${{ runner.temp }}\acs\metadata.json
|
||||
run: |
|
||||
Get-ChildItem -Path .\dist -Filter *.msi | ForEach-Object {
|
||||
.\script\sign $_.FullName
|
||||
.\script\sign.ps1 $_.FullName
|
||||
}
|
||||
env:
|
||||
CERT_FILE: ${{ steps.obtain_cert.outputs.cert-file }}
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
project_name: gh
|
||||
|
||||
release:
|
||||
prerelease: auto
|
||||
draft: true # we only publish after the Windows MSI gets uploaded
|
||||
name_template: "GitHub CLI {{.Version}}"
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- >-
|
||||
{{ if eq .Runtime.Goos "windows" }}echo{{ end }} make manpages GH_VERSION={{.Version}}
|
||||
- >-
|
||||
{{ if ne .Runtime.Goos "linux" }}echo{{ end }} make completions
|
||||
|
||||
builds:
|
||||
- id: macos #build:macos
|
||||
goos: [darwin]
|
||||
goarch: [amd64, arm64]
|
||||
hooks:
|
||||
post:
|
||||
- cmd: ./script/sign '{{ .Path }}'
|
||||
output: true
|
||||
binary: bin/gh
|
||||
main: ./cmd/gh
|
||||
ldflags:
|
||||
- -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
|
||||
|
||||
- id: linux #build:linux
|
||||
goos: [linux]
|
||||
goarch: [386, arm, amd64, arm64]
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
binary: bin/gh
|
||||
main: ./cmd/gh
|
||||
ldflags:
|
||||
- -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
|
||||
|
||||
- id: windows #build:windows
|
||||
goos: [windows]
|
||||
goarch: [386, amd64, arm64]
|
||||
hooks:
|
||||
post:
|
||||
- cmd: >-
|
||||
{{ if eq .Runtime.Goos "windows" }}pwsh .\script\sign.ps1{{ else }}./script/sign{{ end }} '{{ .Path }}'
|
||||
output: true
|
||||
binary: bin/gh
|
||||
main: ./cmd/gh
|
||||
ldflags:
|
||||
- -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
|
||||
|
||||
archives:
|
||||
- id: linux-archive
|
||||
builds: [linux]
|
||||
name_template: "gh_{{ .Version }}_linux_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
format: tar.gz
|
||||
rlcp: true
|
||||
files:
|
||||
- LICENSE
|
||||
- ./share/man/man1/gh*.1
|
||||
- id: macos-archive
|
||||
builds: [macos]
|
||||
name_template: "gh_{{ .Version }}_macOS_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
format: zip
|
||||
rlcp: true
|
||||
files:
|
||||
- LICENSE
|
||||
- ./share/man/man1/gh*.1
|
||||
- id: windows-archive
|
||||
builds: [windows]
|
||||
name_template: "gh_{{ .Version }}_windows_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: false
|
||||
format: zip
|
||||
rlcp: true
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
nfpms: #build:linux
|
||||
- license: MIT
|
||||
maintainer: GitHub
|
||||
homepage: https://github.com/cli/cli
|
||||
bindir: /usr
|
||||
dependencies:
|
||||
- git
|
||||
description: GitHub’s official command line tool.
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
contents:
|
||||
- src: "./share/man/man1/gh*.1"
|
||||
dst: "/usr/share/man/man1"
|
||||
- src: "./share/bash-completion/completions/gh"
|
||||
dst: "/usr/share/bash-completion/completions/gh"
|
||||
- src: "./share/fish/vendor_completions.d/gh.fish"
|
||||
dst: "/usr/share/fish/vendor_completions.d/gh.fish"
|
||||
- src: "./share/zsh/site-functions/_gh"
|
||||
dst: "/usr/share/zsh/site-functions/_gh"
|
||||
|
|
@ -41,7 +41,7 @@ builds:
|
|||
hooks:
|
||||
post:
|
||||
- cmd: >-
|
||||
{{ if eq .Runtime.Goos "windows" }}.\script\sign{{ else }}./script/sign{{ end }} '{{ .Path }}'
|
||||
{{ if eq .Runtime.Goos "windows" }}pwsh .\script\sign.ps1{{ else }}./script/sign{{ end }} '{{ .Path }}'
|
||||
output: true
|
||||
binary: bin/gh
|
||||
main: ./cmd/gh
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
print_help() {
|
||||
cat <<EOF
|
||||
To tag a new release:
|
||||
script/release [--staging] <tag-name> [--platform {linux|macos|windows}] [--branch <branch>]
|
||||
|
||||
To build staging binaries from the current branch:
|
||||
script/release --current [--platform {linux|macos|windows}]
|
||||
|
||||
To build binaries locally with goreleaser:
|
||||
script/release --local --platform {linux|macos|windows}
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
print_help >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tag_name=""
|
||||
is_local=""
|
||||
do_push=""
|
||||
platform=""
|
||||
branch="trunk"
|
||||
deploy_env="production"
|
||||
goreleaser_config=".goreleaser.yml"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h | --help )
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
-b | --branch )
|
||||
branch="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c | --config )
|
||||
goreleaser_config="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p | --platform )
|
||||
platform="$2"
|
||||
shift 2
|
||||
;;
|
||||
--local )
|
||||
is_local=1
|
||||
shift 1
|
||||
;;
|
||||
--staging )
|
||||
deploy_env="staging"
|
||||
shift 1
|
||||
;;
|
||||
--current )
|
||||
deploy_env="staging"
|
||||
tag_name="$(git describe --tags --abbrev=0)"
|
||||
branch="$(git rev-parse --symbolic-full-name '@{upstream}' 2>/dev/null || git branch --show-current)"
|
||||
branch="${branch#refs/remotes/*/}"
|
||||
do_push=1
|
||||
shift 1
|
||||
;;
|
||||
-* )
|
||||
printf "unrecognized flag: %s\n" "$1" >&2
|
||||
exit 1
|
||||
;;
|
||||
* )
|
||||
tag_name="$1"
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
announce() {
|
||||
local tmpdir="${TMPDIR:-/tmp}"
|
||||
echo "$*" | sed "s:${tmpdir%/}:\$TMPDIR:"
|
||||
"$@"
|
||||
}
|
||||
|
||||
trigger_deployment() {
|
||||
announce gh workflow -R cli/cli run deployment.yml --ref "$branch" -f tag_name="$tag_name" -f environment="$deploy_env"
|
||||
}
|
||||
|
||||
build_local() {
|
||||
local config="$goreleaser_config"
|
||||
case "$platform" in
|
||||
linux )
|
||||
sed '/#build:windows/,/^$/d; /#build:macos/,/^$/d' "$config" >.goreleaser.generated.yml
|
||||
config=".goreleaser.generated.yml"
|
||||
;;
|
||||
macos )
|
||||
sed '/#build:windows/,/^$/d; /#build:linux/,/^$/d' "$config" >.goreleaser.generated.yml
|
||||
config=".goreleaser.generated.yml"
|
||||
;;
|
||||
windows )
|
||||
sed '/#build:linux/,/^$/d; /#build:macos/,/^$/d' "$config" >.goreleaser.generated.yml
|
||||
config=".goreleaser.generated.yml"
|
||||
;;
|
||||
esac
|
||||
[ -z "$tag_name" ] || export GORELEASER_CURRENT_TAG="$tag_name"
|
||||
announce goreleaser release -f "$config" --clean --skip-validate --skip-publish --release-notes="$(mktemp)"
|
||||
}
|
||||
|
||||
if [ -n "$is_local" ]; then
|
||||
build_local
|
||||
else
|
||||
if [ -n "$do_push" ]; then
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "refusing to continue due to uncomitted local changes" >&2
|
||||
exit 1
|
||||
fi
|
||||
announce git push
|
||||
fi
|
||||
trigger_deployment
|
||||
if [ "$deploy_env" = "production" ]; then
|
||||
echo
|
||||
echo "Go to Slack to manually approve this production deployment."
|
||||
fi
|
||||
fi
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
@echo off
|
||||
|
||||
if "%DLIB_PATH%" == "" (
|
||||
echo skipping Windows code-signing; DLIB_PATH not set
|
||||
exit /b
|
||||
)
|
||||
|
||||
if "%METADATA_PATH%" == "" (
|
||||
echo skipping Windows code-signing; METADATA_PATH not set
|
||||
exit /b
|
||||
)
|
||||
|
||||
REM For more information on signtool, see https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool
|
||||
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool" sign /d "GitHub CLI" /fd sha256 /td sha256 /tr http://timestamp.acs.microsoft.com /v /dlib "%DLIB_PATH%" /dmdf "%METADATA_PATH%" "%1"
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@echo off
|
||||
|
||||
if "%CERT_FILE%" == "" (
|
||||
echo skipping Windows code-signing; CERT_FILE not set
|
||||
exit /b
|
||||
)
|
||||
|
||||
.\script\signtool sign /d "GitHub CLI" /f "%CERT_FILE%" /p "%CERT_PASSWORD%" /fd sha256 /tr http://timestamp.digicert.com /v "%1"
|
||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue