Skip to content

Package

Package #195

Workflow file for this run

name: Package
on:
workflow_dispatch:
inputs:
run:
description: 'The CI workflow run to package'
required: true
validate-dependency:
description: 'Validate that the CI workflow ran to completion'
type: boolean
required: false
default: true
jetsocat-nuget-version:
description: 'Jetsocat nuget package version'
required: false
workflow_call:
inputs:
dispatch:
description: "Marker to indicate that the workflow was dispatched via workflow_call"
type: string
required: false
default: "workflow_call"
ref:
description: "The commit SHA to build"
required: false
type: string
jobs:
preflight:
name: Preflight
runs-on: ubuntu-20.04
outputs:
run: ${{ steps.get-run.outputs.run }}
commit: ${{ steps.get-commit.outputs.commit }}
version: ${{ steps.get-version.outputs.version }}
package-env: ${{ steps.info.outputs.package-env }}
steps:
- name: Package information
id: info
shell: pwsh
run: |
$ref = '${{ github.ref_name }}'
$IsMasterBranch = ('${{ github.ref_name }}' -eq 'master')
$IsScheduledJob = ('${{ github.event_name }}' -eq 'schedule')
$PackageEnv = if ($IsMasterBranch -And -Not $IsScheduledJob) {
"publish-prod"
} else {
"publish-prod" # "publish-test"
}
echo "package-env=$PackageEnv" >> $Env:GITHUB_OUTPUT
## workflow_dispatch: The run_id is read from the inputs
## workflow_call: The run_id is the current run_id
- name: Get run
id: get-run
shell: pwsh
run: |
if ('${{ github.event.inputs.run }}') {
echo "run=${{ github.event.inputs.run }}" >> $Env:GITHUB_OUTPUT
} else {
echo "run=${{ github.run_id }}" >> $Env:GITHUB_OUTPUT
}
## To consistently repackage the CI artifacts, we must use the same commit that produced the artifacts
##
## workflow_dispatch: Lookup the SHA from the given run_id
## workflow_call: Use the input SHA; otherwise lookup the SHA from the current run_id
- name: Get commit
id: get-commit
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CI_RUN: ${{ steps.get-run.outputs.run }}
run: |
$Ref = '${{ inputs.ref }}'
if (-Not $Ref) {
$Run = gh api /repos/$Env:GITHUB_REPOSITORY/actions/runs/$Env:CI_RUN | ConvertFrom-Json
if ($($Run.head_branch) -Ne "master") {
echo "::notice::specified run is not on master"
}
$ValidateDependency = $false
if ('${{ github.event.inputs.validate-dependency }}') {
$ValidateDependency = [System.Convert]::ToBoolean('${{ github.event.inputs.validate-dependency }}')
}
if ($ValidateDependency) {
if (($($Run.status) -Ne "completed") -Or ($($Run.conclusion) -Ne "success")) {
echo "::error::specified run is either not complete or not successful"
exit 1
}
}
$Ref = $($Run.head_sha)
}
echo "commit=$Ref" >> $Env:GITHUB_OUTPUT
echo "::notice::Packaging artifacts built from commit $Ref in run ${{ steps.get-run.outputs.run }}"
- name: Checkout ${{ github.repository }}
uses: actions/checkout@v4
with:
ref: ${{ steps.get-commit.outputs.commit }}
- name: Upload version artifact
uses: actions/upload-artifact@v4
with:
name: version
path: VERSION
overwrite: true
- name: Upload docker file artifacts
uses: actions/upload-artifact@v4
with:
name: docker
path: package/**/Dockerfile
- name: Upload changelog artifacts
uses: actions/upload-artifact@v4
with:
name: changelog
path: CHANGELOG.md
- name: Get version
id: get-version
shell: pwsh
run: |
$Version = Get-Content VERSION -TotalCount 1
echo "version=$Version" >> $Env:GITHUB_OUTPUT
- name: Download Cadeau
shell: pwsh
run: |
./ci/download-cadeau.ps1 -Platform 'win' -Architecture 'x64'
./ci/download-cadeau.ps1 -Platform 'linux' -Architecture 'x64'
- name: Upload native libs
uses: actions/upload-artifact@v4
with:
name: native-libs
path: native-libs/
codesign:
name: Codesign
runs-on: ${{ matrix.runner }}
needs: preflight
environment: ${{ needs.preflight.outputs.package-env }}
strategy:
matrix:
project: [ jetsocat, devolutions-gateway, devolutions-agent ]
os: [ windows, macos, linux ]
include:
- os: windows
runner: windows-2022
- os: macos
runner: macos-latest
- os: linux
runner: ubuntu-20.04
exclude:
- project: devolutions-gateway
os: macos
- project: devolutions-agent
os: macos
steps:
- name: Checkout ${{ github.repository }}
uses: actions/checkout@v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- name: Download artifacts
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$Destination = Join-Path ${{ runner.temp }} ${{ matrix.project }}
gh run download ${{ needs.preflight.outputs.run }} -n ${{ matrix.project }} -D "$Destination"
## Delete the files that we won't operate on to prevent them being re-uploaded
## This ensures consistency of the artifact since we are operating in a matrix
- name: Manage artifacts
shell: pwsh
run: |
$Destination = Join-Path ${{ runner.temp }} ${{ matrix.project }}
$Exclusions = @('${{ matrix.os }}', 'Powershell')
Get-ChildItem "$Destination" -Exclude $Exclusions | Remove-Item -Recurse
- name: Install AzureSignTool
if: matrix.os == 'windows'
run: |
dotnet tool install --global AzureSignTool
- name: Configure certificates (macOS)
if: matrix.os == 'macos'
env:
DEVELOPER_ID_CERTIFICATE: ${{ secrets.APPLE_APP_DEV_ID_APP_CERTIFICATE }}
DEVELOPER_ID_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_DEV_ID_APP_CERTIFICATE_PASSWORD }}
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=Price2011
DEVELOPER_ID_CERTIFICATE_PATH=$RUNNER_TEMP/dev_id_cert.p12
echo -n "$DEVELOPER_ID_CERTIFICATE" | base64 --decode --output $DEVELOPER_ID_CERTIFICATE_PATH
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security import $DEVELOPER_ID_CERTIFICATE_PATH -P "$DEVELOPER_ID_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Configure runner (Windows)
if: matrix.os == 'windows'
run: |
echo "C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
# https://github.com/actions/runner-images/issues/9667
choco uninstall wixtoolset
choco install wixtoolset --version 3.14.0 --allow-downgrade --force
echo "C:\Program Files (x86)\WiX Toolset v3.14\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Sign executables
if: matrix.os == 'windows' || matrix.os == 'macos'
shell: pwsh
run: |
$IncludePattern = switch ('${{ matrix.project }}') {
'devolutions-gateway' { 'DevolutionsGateway_*.exe' }
'devolutions-agent' { 'DevolutionsAgent_*.exe', 'DevolutionsPedmShellExt.dll',
'DevolutionsPedmShellExt.msix', 'DevolutionsDesktopAgent.exe' }
'jetsocat' { 'jetsocat_*' }
}
$ExcludePattern = "*.pdb"
Get-ChildItem -Path ${{ runner.temp }} -Recurse -Include $IncludePattern -Exclude $ExcludePattern | % {
if ('${{ matrix.os }}' -Eq 'windows') {
if ($_.Name -Eq 'DevolutionsPedmShellExt.msix') {
$PackagePublisher = '${{ secrets.CODE_SIGNING_APPX_PUBLISHER }}'
$UnpackedMsix = Join-Path ${{ runner.temp }} "unpacked-context-menu-msix"
$AppxManifest = Join-Path $UnpackedMsix "AppxManifest.xml"
$PackedMsix = $_.FullName
& 'MakeAppx.exe' unpack /p $PackedMsix /d $UnpackedMsix /nv
Remove-Item $PackedMsix -Force | Out-Null
$appx = [xml](Get-Content -Path $AppxManifest)
$appx.Package.Identity.Publisher = $PackagePublisher
$xmlWriterSettings = New-Object System.Xml.XmlWriterSettings
$xmlWriterSettings.Indent = $true
$xmlWriterSettings.Encoding = [System.Text.Encoding]::UTF8
$xmlTextWriter = [System.Xml.XmlTextWriter]::Create($AppxManifest, $xmlWriterSettings)
$appx.Save($xmlTextWriter)
$xmlTextWriter.Close()
& 'MakeAppx.exe' pack /d $UnpackedMsix /p $PackedMsix /nv
Remove-Item $UnpackedMsix -Recurse -Force | Out-Null
}
$Params = @('sign',
'-kvt', '${{ secrets.AZURE_TENANT_ID }}',
'-kvu', '${{ secrets.CODE_SIGNING_KEYVAULT_URL }}',
'-kvi', '${{ secrets.CODE_SIGNING_CLIENT_ID }}',
'-kvs', '${{ secrets.CODE_SIGNING_CLIENT_SECRET }}',
'-kvc', '${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }}',
'-tr', '${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }}',
'-v')
AzureSignTool @Params $_.FullName
} elseif ('${{ matrix.os }}' -Eq 'macos') {
$SignCmd = $(@(
'codesign',
'--timestamp',
'--options=runtime',
'-s', '"Developer ID Application: Devolutions inc. (N592S9ASDB)"',
'-v',
$_.FullName
)) -Join ' '
Write-Host $SignCmd
Invoke-Expression $SignCmd
} else {
echo "::debug::nothing to do for ${{ matrix.os }}"
}
}
- name: Download web client artifacts
if: matrix.os == 'windows' && matrix.project == 'devolutions-gateway'
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$Destination = Join-Path "webapp" "client"
gh run download ${{ needs.preflight.outputs.run }} -n webapp-client -D "$Destination"
- name: Add msbuild to PATH
if: matrix.os == 'windows' && (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent')
uses: microsoft/setup-msbuild@v2
- name: Download native-libs
uses: actions/download-artifact@v4
if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows'
with:
name: native-libs
path: native-libs
- name: Zip debug symbol files
if: matrix.project == 'devolutions-agent' && matrix.os == 'windows'
shell: pwsh
run: |
$PackageRoot = Join-Path ${{ runner.temp }} ${{ matrix.project}}
Get-ChildItem "$PackageRoot\windows\x86_64\*.pdb" -Recurse | Compress-Archive -DestinationPath "$PackageRoot\windows\x86_64\DevolutionsAgent-x86_64-${{ needs.preflight.outputs.version }}.symbols.zip" -CompressionLevel Optimal
Get-ChildItem "$PackageRoot\windows\x86_64\*.pdb" -Recurse | Remove-Item | Out-Null
- name: Regenerate Gateway MSI
if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows'
shell: pwsh
run: |
$PackageRoot = Join-Path ${{ runner.temp }} ${{ matrix.project}}
$Env:DGATEWAY_EXECUTABLE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsGateway*.exe' | Select -First 1
$Env:DGATEWAY_PSMODULE_PATH = Join-Path $PackageRoot PowerShell DevolutionsGateway
$Env:DGATEWAY_WEBCLIENT_PATH = Join-Path "webapp" "client" | Resolve-Path
$Env:DGATEWAY_LIB_XMF_PATH = Join-Path "native-libs" "xmf.dll" | Resolve-Path
Get-ChildItem -Path (Join-Path $PackageRoot PowerShell "*.zip") -Recurse | % {
Remove-Item $_.FullName -Force
}
./ci/tlk.ps1 package -Product gateway -PackageOption generate
- name: Regenerate Agent MSI
if: matrix.project == 'devolutions-agent' && matrix.os == 'windows'
shell: pwsh
run: |
$PackageRoot = Join-Path ${{ runner.temp }} ${{ matrix.project}}
$Env:DAGENT_EXECUTABLE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsAgent*.exe' | Select -First 1
$Env:DAGENT_DESKTOP_AGENT_OUTPUT_PATH = Join-Path $PackageRoot ${{ matrix.os }} x86_64 DesktopAgent
$Env:DAGENT_PEDM_SHELL_EXT_DLL = Get-ChildItem -Path $PackageRoot -Recurse -Include 'DevolutionsPedmShellExt.dll' | Select -First 1
$Env:DAGENT_PEDM_SHELL_EXT_MSIX = Get-ChildItem -Path $PackageRoot -Recurse -Include 'DevolutionsPedmShellExt.msix' | Select -First 1
$Env:DAGENT_SESSION_EXECUTABLE = Get-ChildItem -Path $PackageRoot -Recurse -Include 'DevolutionsSession.exe' | Select -First 1
./ci/tlk.ps1 package -Product agent -PackageOption generate
- name: Sign Gateway MSI runtime
if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows'
shell: pwsh
working-directory: package/WindowsManaged/Release
run: |
Get-ChildItem -Path .\* -Include "*.exe" | % {
$Params = @('sign',
'-kvt', '${{ secrets.AZURE_TENANT_ID }}',
'-kvu', '${{ secrets.CODE_SIGNING_KEYVAULT_URL }}',
'-kvi', '${{ secrets.CODE_SIGNING_CLIENT_ID }}',
'-kvs', '${{ secrets.CODE_SIGNING_CLIENT_SECRET }}',
'-kvc', '${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }}',
'-tr', '${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }}',
'-v')
AzureSignTool @Params $_.FullName
}
- name: Sign Agent MSI runtime
if: matrix.project == 'devolutions-agent' && matrix.os == 'windows'
shell: pwsh
working-directory: package/AgentWindowsManaged/Release
run: |
Get-ChildItem -Path .\* -Include "*.exe" | % {
$Params = @('sign',
'-kvt', '${{ secrets.AZURE_TENANT_ID }}',
'-kvu', '${{ secrets.CODE_SIGNING_KEYVAULT_URL }}',
'-kvi', '${{ secrets.CODE_SIGNING_CLIENT_ID }}',
'-kvs', '${{ secrets.CODE_SIGNING_CLIENT_SECRET }}',
'-kvc', '${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }}',
'-tr', '${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }}',
'-v')
AzureSignTool @Params $_.FullName
}
- name: Repackage Gateway
if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows'
shell: pwsh
run: |
$PackageRoot = Join-Path ${{ runner.temp }} devolutions-gateway
$Env:DGATEWAY_PACKAGE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsGateway*.msi' | Where-Object { $_.Name -NotLike "*legacy*"} | Select -First 1
./ci/tlk.ps1 package -Product gateway -PackageOption assemble
- name: Repackage Agent
if: matrix.project == 'devolutions-agent' && matrix.os == 'windows'
shell: pwsh
run: |
$PackageRoot = Join-Path ${{ runner.temp }} devolutions-agent
$Env:DAGENT_PACKAGE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsAgent*.msi' | Where-Object { $_.Name -NotLike "*legacy*"} | Select -First 1
./ci/tlk.ps1 package -Product agent -PackageOption assemble
- name: Sign packages
if: (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent') && matrix.os == 'windows'
shell: pwsh
run: |
$ContentDescription = switch ('${{ matrix.project }}') {
'devolutions-gateway' { 'Devolutions Gateway' }
'devolutions-agent' { 'Devolutions Agent' }
}
Get-ChildItem -Path ${{ runner.temp }} -Recurse -Include '*.msi' | % {
$Params = @('sign',
'-kvt', '${{ secrets.AZURE_TENANT_ID }}',
'-kvu', '${{ secrets.CODE_SIGNING_KEYVAULT_URL }}',
'-kvi', '${{ secrets.CODE_SIGNING_CLIENT_ID }}',
'-kvs', '${{ secrets.CODE_SIGNING_CLIENT_SECRET }}',
'-kvc', '${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }}',
'-tr', '${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }}',
'-d', $ContentDescription,
'-v')
AzureSignTool @Params $_.FullName
}
- name: Verification
if: (matrix.os == 'windows' || matrix.os == 'macos') && env.package-env == 'publish-prod'
shell: pwsh
run: |
$RootPath = Join-Path ${{ runner.temp }} ${{ matrix.project }} ${{ matrix.os }}
if ('${{ matrix.os }}' -Eq 'windows') {
Get-ChildItem -Path $RootPath -Recurse -Include ('*.exe', '*.msi') | % {
signtool verify /pa "$($_.FullName)"
if ($LastExitCode -Ne 0) {
echo "::error::failed to verify the signature of $($_.FullName)"
exit 1
}
}
} elseif ('${{ matrix.os }}' -Eq 'macos') {
Get-ChildItem -Path $RootPath -Recurse -Include 'jetsocat_*' | % {
codesign -dvvv "$($_.FullName)"
if ($LastExitCode -Ne 0) {
echo "::error::failed to verify the signature of $($_.FullName)"
exit 1
}
}
}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}-${{ matrix.os }}
path: ${{ runner.temp }}/${{ matrix.project }}
if-no-files-found: error
retention-days: 1
devolutions-gateway-merge:
name: Merge gateway artifacts
runs-on: ubuntu-latest
needs: [preflight, codesign]
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
pattern: devolutions-gateway-*
merge-multiple: true
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/**/*
name: devolutions-gateway
overwrite: true
devolutions-agent-merge:
name: Merge agent artifacts
runs-on: ubuntu-latest
needs: [preflight, codesign]
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
pattern: devolutions-agent-*
merge-multiple: true
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/**/*
name: devolutions-agent
overwrite: true
jetsocat-merge:
name: Merge jetsocat artifacts
runs-on: ubuntu-latest
needs: [preflight, codesign]
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
pattern: jetsocat-*
merge-multiple: true
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/**/*
name: jetsocat
overwrite: true
web-app:
name: Web App
runs-on: ubuntu-latest
needs: [preflight, codesign]
steps:
- name: Download artifacts
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh run download ${{ needs.preflight.outputs.run }} -n webapp-client -R $Env:GITHUB_REPOSITORY -D webapp-client
- name: Create tarball
run: tar -czvf devolutions_gateway_webapp_${{ needs.preflight.outputs.version }}.tar.gz webapp-client
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: webapp-client
path: devolutions_gateway_webapp_${{ needs.preflight.outputs.version }}.tar.gz
if-no-files-found: error
overwrite: true
nuget:
name: Nuget
runs-on: ubuntu-latest
needs: [preflight, codesign, jetsocat-merge]
steps:
- name: Checkout ${{ github.repository }}
uses: actions/checkout@v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: jetsocat
path: jetsocat/nuget/bin
- name: Rename artifacts
shell: pwsh
working-directory: jetsocat/nuget/bin
run: |
# Backward compatibility with prior nuspec versions
Get-ChildItem -Directory -Recurse "x86_64" | Rename-Item -NewName "x64"
# Remove version number and architecture from binary name
Get-ChildItem -File -Recurse -Exclude "*.pdb" | Rename-Item -NewName "jetsocat"
cd windows
Get-ChildItem -File -Recurse -Exclude "*.pdb" | Rename-Item -NewName "jetsocat.exe"
- name: Set package metadata
shell: pwsh
working-directory: jetsocat/nuget
run: |
$Version = '${{ github.event.inputs.jetsocat-nuget-version }}'
if ([string]::IsNullOrWhitespace($Version)) {
$Version = Get-Date -Format "yyyy.M.d"
}
$Nuspec = (Resolve-Path "Devolutions.Jetsocat.nuspec")
$Xml = [xml] (Get-Content $Nuspec)
Select-Xml -xml $Xml -XPath //package/metadata/version | % { $_.Node.'#text' = "$Version" }
Select-Xml -xml $Xml -XPath //package/metadata/description | % { $_.Node.'#text' = "Websocket toolkit for jet protocol related operations" }
$Xml.Save($Nuspec)
- name: Build package
shell: pwsh
working-directory: jetsocat/nuget
run: |
Install-Module -Name ZipIt -Force
& 'nuget' 'pack' 'Devolutions.Jetsocat.nuspec'
$NugetPackage = (Get-Item ".\*.nupkg" | Select-Object -First 1) | Resolve-Path -Relative
Set-ZipItUnixFilePermissions $NugetPackage -FilePattern "native/jetsocat$" -FilePermissions "r-xr-xr-x"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: jetsocat-nuget
path: jetsocat/nuget/*.nupkg
if-no-files-found: error
generate-sbom:
name: Upload SBOM
runs-on: ubuntu-latest
needs: preflight
steps:
- name: Checkout ${{ github.repository }}
uses: actions/checkout@v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- name: Check out Devolutions/actions
uses: actions/checkout@v4
with:
repository: Devolutions/actions
ref: v1
token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }}
path: ./.github/workflows
- name: Install Devolutions Toolbox
uses: ./.github/workflows/toolbox-install
with:
github_token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }}
- name: Generate SBOM
uses: ./.github/workflows/cdxgen
- name: Save SBOM
uses: actions/upload-artifact@v4
with:
name: bom.xml
path: bom.xml
- name: Upload SBOM to Dependency-Track
uses: ./.github/workflows/dtrack-upload-sbom
with:
api_key: ${{ secrets.DTRACK_AUTOMATION_API_KEY }}
autocreate: 'true'
bom_filename: bom.xml
project_name: devolutions-gateway
project_version: ${{ needs.preflight.outputs.version }}
server_hostname: 'dtrack-api.devolutions.com'