Release #95
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| run: | |
| description: The Package workflow run to release | |
| required: true | |
| dry-run: | |
| description: If true, the workflow only indicates which artifacts would be uploaded | |
| required: true | |
| type: boolean | |
| default: true | |
| workflow_call: | |
| inputs: | |
| dispatch: | |
| description: Marker to indicate that the workflow was dispatched via workflow_call | |
| type: string | |
| required: false | |
| default: workflow_call | |
| dry-run: | |
| description: If true, the workflow only indicates which artifacts would be uploaded | |
| required: true | |
| type: boolean | |
| default: true | |
| concurrency: gateway-release | |
| jobs: | |
| preflight: | |
| name: Preflight | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run: ${{ steps.get-run.outputs.run }} | |
| version: ${{ steps.get-version.outputs.version }} | |
| skip-publishing: ${{ steps.check-release.outputs.skip-publishing }} | |
| steps: | |
| ## 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 | |
| run: | | |
| if ('${{ github.event.inputs.run }}') { | |
| echo "run=${{ github.event.inputs.run }}" >> $Env:GITHUB_OUTPUT | |
| } else { | |
| echo "run=${{ github.run_id }}" >> $Env:GITHUB_OUTPUT | |
| } | |
| shell: pwsh | |
| - name: Get dry run | |
| id: get-dry-run | |
| run: | | |
| $DryRun = "false" | |
| if ('${{ inputs.dry-run }}') { | |
| $DryRun = "${{ inputs.dry-run }}" | |
| } | |
| if ([System.Convert]::ToBoolean($DryRun)) { | |
| echo "::notice::This is a dry run; publishing will be skipped" | |
| } else { | |
| echo "::warning::This is not a dry run, release will be published!" | |
| } | |
| shell: pwsh | |
| - name: Download version | |
| run: gh run download ${{ steps.get-run.outputs.run }} -n version --repo $Env:GITHUB_REPOSITORY | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Get version | |
| id: get-version | |
| run: | | |
| $Version = Get-Content VERSION -TotalCount 1 | |
| echo "version=$Version" >> $Env:GITHUB_OUTPUT | |
| echo "::notice::Releasing artifacts for version $Version from run ${{ steps.get-run.outputs.run }}" | |
| shell: pwsh | |
| ## If we already released this version to GitHub, publishing will be skipped | |
| - name: Check GitHub releases | |
| id: check-release | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $Output = (gh release list --repo $Env:GITHUB_REPOSITORY) | Out-String | |
| $Releases = ( $Output -split '\r?\n' ).Trim() | |
| $Versions = ForEach($Release in $Releases) { | |
| $Version = ( $Release -Split '\s+' ).Trim() | |
| $Version = $Version.TrimStart("v") | |
| $Version[0] | |
| } | |
| $SkipPublishing = 'false' | |
| if ($Versions -Contains "${{ steps.get-version.outputs.version }}") { | |
| echo "::warning::GitHub already has a release version ${{ steps.get-version.outputs.version }}; publishing will be skipped" | |
| $SkipPublishing = 'true' | |
| } | |
| echo "skip-publishing=$SkipPublishing" >> $Env:GITHUB_OUTPUT | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| container: | |
| name: Container [${{ matrix.os }} ${{ matrix.base-image }}] | |
| environment: publish-prod | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [linux] | |
| base-image: [bookworm-slim] | |
| include: | |
| - os: linux | |
| runner: ubuntu-latest | |
| steps: | |
| - name: Download artifacts | |
| run: | | |
| gh run download ${{ needs.preflight.outputs.run }} -n webapp-client -n docker -n devolutions-gateway-signed -n native-libs --repo $Env:GITHUB_REPOSITORY | |
| Move-Item -Path devolutions-gateway-signed -Destination devolutions-gateway | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Multi-arch Docker build strategy: | |
| # We prepare separate build contexts for amd64 and arm64 because each architecture | |
| # needs its own pre-compiled binary and architecture-specific native libraries (libxmf.so). | |
| # This approach is more reliable than trying to use COPY --platform in the Dockerfile, | |
| # which would require the binaries to be organized in a specific directory structure. | |
| - name: Prepare artifacts (amd64) | |
| id: prepare-artifacts-amd64 | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $PkgDir = Join-Path docker $Env:RUNNER_OS "amd64" | |
| echo "package-path-amd64=$PkgDir" >> $Env:GITHUB_OUTPUT | |
| Write-Host "PkgDir = $PkgDir" | |
| New-Item -ItemType Directory -Path $PkgDir -Force | |
| $SourceFileName = "DevolutionsGateway_$($Env:RUNNER_OS)_${{ needs.preflight.outputs.version }}_x86_64" | |
| $TargetFileName = "devolutions-gateway" | |
| Write-Host "SourceFileName = $SourceFileName" | |
| Write-Host "TargetFileName = $TargetFileName" | |
| $SourcePath = Get-ChildItem -Recurse -Filter $SourceFileName -File -Path devolutions-gateway | |
| $TargetPath = Join-Path $PkgDir $TargetFileName | |
| Write-Host "SourcePath = $SourcePath" | |
| Write-Host "TargetPath = $TargetPath" | |
| Copy-Item -Path $SourcePath -Destination $TargetPath | |
| chmod +x $TargetPath | |
| $XmfFileName = "libxmf.so" | |
| $XmfSourcePath = Join-Path "native-libs" "linux" "x64" $XmfFileName | Resolve-Path | |
| $XmfTargetPath = Join-Path $PkgDir $XmfFileName | |
| Write-Host "XmfSourcePath = $XmfSourcePath" | |
| Write-Host "XmfTargetPath = $XmfTargetPath" | |
| Copy-Item -Path $XmfSourcePath -Destination $XmfTargetPath | |
| $WebAppArchive = Get-ChildItem -Recurse -Filter "devolutions_gateway_webapp_*.tar.gz" | Select-Object -First 1 | |
| $TargetPath = Join-Path $PkgDir "webapp" "client" | |
| Write-Host "WebAppArchive = $WebAppArchive" | |
| Write-Host "TargetPath = $TargetPath" | |
| New-Item -ItemType Directory -Path $TargetPath | |
| tar -xvzf $WebAppArchive.FullName -C $TargetPath --strip-components=1 | |
| $PowerShellArchive = Get-ChildItem -Recurse -Filter "DevolutionsGateway-ps-*.tar" | Select-Object -First 1 | |
| $psModuleArchiveHash = (Get-FileHash -Path "$PowerShellArchive").Hash | |
| Write-Host "PS module archive hash: $psModuleArchiveHash" | |
| tar -xvf "$PowerShellArchive" -C "$PkgDir" | |
| # Copy Dockerfile and entrypoint | |
| Copy-Item -Path "docker/Linux/Dockerfile" -Destination $PkgDir | |
| Copy-Item -Path "docker/Linux/entrypoint.ps1" -Destination $PkgDir | |
| shell: pwsh | |
| - name: Prepare artifacts (arm64) | |
| id: prepare-artifacts-arm64 | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $PkgDir = Join-Path docker $Env:RUNNER_OS "arm64" | |
| echo "package-path-arm64=$PkgDir" >> $Env:GITHUB_OUTPUT | |
| Write-Host "PkgDir = $PkgDir" | |
| New-Item -ItemType Directory -Path $PkgDir -Force | |
| $SourceFileName = "DevolutionsGateway_$($Env:RUNNER_OS)_${{ needs.preflight.outputs.version }}_arm64" | |
| $TargetFileName = "devolutions-gateway" | |
| Write-Host "SourceFileName = $SourceFileName" | |
| Write-Host "TargetFileName = $TargetFileName" | |
| $SourcePath = Get-ChildItem -Recurse -Filter $SourceFileName -File -Path devolutions-gateway | |
| $TargetPath = Join-Path $PkgDir $TargetFileName | |
| Write-Host "SourcePath = $SourcePath" | |
| Write-Host "TargetPath = $TargetPath" | |
| Copy-Item -Path $SourcePath -Destination $TargetPath | |
| chmod +x $TargetPath | |
| $XmfFileName = "libxmf.so" | |
| $XmfSourcePath = Join-Path "native-libs" "linux" "arm64" $XmfFileName | Resolve-Path | |
| $XmfTargetPath = Join-Path $PkgDir $XmfFileName | |
| Write-Host "XmfSourcePath = $XmfSourcePath" | |
| Write-Host "XmfTargetPath = $XmfTargetPath" | |
| Copy-Item -Path $XmfSourcePath -Destination $XmfTargetPath | |
| $WebAppArchive = Get-ChildItem -Recurse -Filter "devolutions_gateway_webapp_*.tar.gz" | Select-Object -First 1 | |
| $TargetPath = Join-Path $PkgDir "webapp" "client" | |
| Write-Host "WebAppArchive = $WebAppArchive" | |
| Write-Host "TargetPath = $TargetPath" | |
| New-Item -ItemType Directory -Path $TargetPath | |
| tar -xvzf $WebAppArchive.FullName -C $TargetPath --strip-components=1 | |
| $PowerShellArchive = Get-ChildItem -Recurse -Filter "DevolutionsGateway-ps-*.tar" | Select-Object -First 1 | |
| $psModuleArchiveHash = (Get-FileHash -Path "$PowerShellArchive").Hash | |
| Write-Host "PS module archive hash: $psModuleArchiveHash" | |
| tar -xvf "$PowerShellArchive" -C "$PkgDir" | |
| # Copy Dockerfile and entrypoint | |
| Copy-Item -Path "docker/Linux/Dockerfile" -Destination $PkgDir | |
| Copy-Item -Path "docker/Linux/entrypoint.ps1" -Destination $PkgDir | |
| shell: pwsh | |
| # QEMU is required for cross-platform Docker builds on x86_64 runners. | |
| # It enables the runner to emulate ARM64 architecture during the build process. | |
| # Without QEMU, we would need native ARM64 runners (which are more expensive and less available). | |
| # Note: QEMU is only used during the IMAGE BUILD, not at runtime - the final images are native. | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| # Docker Buildx is required for multi-platform builds and creating manifest lists. | |
| # It provides: | |
| # 1. The ability to build for multiple architectures in a single command | |
| # 2. The 'docker buildx imagetools' command for creating multi-arch manifests | |
| # 3. Better caching and build performance compared to legacy docker build | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: devolutionsbot | |
| password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | |
| # Multi-arch build strategy: | |
| # 1. Build separate images for each architecture with arch-specific tags | |
| # 2. Push each image to the registry | |
| # 3. Create a multi-arch manifest that references both images | |
| # | |
| # When users pull the image without specifying architecture, Docker automatically | |
| # selects the correct variant based on their platform. This works because: | |
| # - Each image is tagged with the architecture (e.g., :2025.3.3-amd64) | |
| # - The manifest list (:2025.3.3) contains references to both arch-specific images | |
| # - Docker client inspects the manifest and pulls the matching architecture | |
| # | |
| # Why we use separate contexts instead of --platform linux/amd64,linux/arm64: | |
| # - Each architecture needs different pre-compiled binaries and native libraries | |
| # - Separate contexts allow us to use architecture-specific artifacts directly | |
| # - More explicit and easier to debug than complex Dockerfile conditionals | |
| - name: Build and push multi-arch container | |
| id: build-container | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $Version = "${{ needs.preflight.outputs.version }}" | |
| $ImageName = "devolutions/devolutions-gateway" | |
| $Amd64Context = "${{ steps.prepare-artifacts-amd64.outputs.package-path-amd64 }}" | |
| $Arm64Context = "${{ steps.prepare-artifacts-arm64.outputs.package-path-arm64 }}" | |
| $DryRun = [System.Convert]::ToBoolean('${{ inputs.dry-run }}') | |
| # Build and push amd64 image | |
| Write-Host "Building amd64 image..." | |
| if (-Not $DryRun) { | |
| docker buildx build --platform linux/amd64 ` | |
| --tag "${ImageName}:${Version}-amd64" ` | |
| --push ` | |
| $Amd64Context | |
| } else { | |
| docker buildx build --platform linux/amd64 ` | |
| --tag "${ImageName}:${Version}-amd64" ` | |
| $Amd64Context | |
| } | |
| # Build and push arm64 image (cross-compiled on x86_64 runner using QEMU) | |
| Write-Host "Building arm64 image..." | |
| if (-Not $DryRun) { | |
| docker buildx build --platform linux/arm64 ` | |
| --tag "${ImageName}:${Version}-arm64" ` | |
| --push ` | |
| $Arm64Context | |
| } else { | |
| docker buildx build --platform linux/arm64 ` | |
| --tag "${ImageName}:${Version}-arm64" ` | |
| $Arm64Context | |
| } | |
| if (-Not $DryRun) { | |
| # Create multi-arch manifests that reference both architecture-specific images. | |
| # This enables Docker to automatically select the correct image based on the user's platform. | |
| Write-Host "Creating multi-arch manifest for version ${Version}..." | |
| docker buildx imagetools create ` | |
| --tag "${ImageName}:${Version}" ` | |
| "${ImageName}:${Version}-amd64" ` | |
| "${ImageName}:${Version}-arm64" | |
| Write-Host "Creating multi-arch manifest for latest..." | |
| docker buildx imagetools create ` | |
| --tag "${ImageName}:latest" ` | |
| "${ImageName}:${Version}-amd64" ` | |
| "${ImageName}:${Version}-arm64" | |
| } else { | |
| Write-Host "Dry run: skipping manifest creation and push" | |
| } | |
| echo "image-name=${ImageName}:${Version}" >> $Env:GITHUB_OUTPUT | |
| echo "latest-image-name=${ImageName}:latest" >> $Env:GITHUB_OUTPUT | |
| shell: pwsh | |
| github-release: | |
| name: GitHub release | |
| environment: publish-prod | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Configure runner | |
| run: cargo install parse-changelog | |
| - name: Download artifacts | |
| run: | | |
| gh run download ${{ needs.preflight.outputs.run }} -n jetsocat-signed -n devolutions-gateway-signed -n devolutions-agent-signed -n webapp-client -n changelog --repo $Env:GITHUB_REPOSITORY | |
| Move-Item -Path jetsocat-signed -Destination jetsocat | |
| Move-Item -Path devolutions-gateway-signed -Destination devolutions-gateway | |
| Move-Item -Path devolutions-agent-signed -Destination devolutions-agent | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Manage artifacts | |
| run: | | |
| # Devolutions Agent on Linux does not have any useful feature yet, so we filter out the Linux artifacts. | |
| Remove-Item -Path (Join-Path devolutions-agent linux) -Recurse | |
| # Do not upload tun2socks.exe by itself. | |
| Remove-Item -Path (Join-Path devolutions-agent tun2socks) -Recurse | |
| # For the PowerShell module, only upload the nupkg. | |
| Remove-Item -Path (Join-Path devolutions-gateway PowerShell DevolutionsGateway-ps-*.tar) | |
| shell: pwsh | |
| - name: Create jetsocat ZIP files for winget | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $Version = "${{ needs.preflight.outputs.version }}" | |
| $ZipFolder = "jetsocat-zips" | |
| New-Item -ItemType Directory -Path $ZipFolder -Force | |
| # Define the platforms and architectures. | |
| $Platforms = @( | |
| @{ OS = "windows"; Arch = "x86_64"; Extension = ".exe" }, | |
| @{ OS = "windows"; Arch = "arm64"; Extension = ".exe" }, | |
| @{ OS = "linux"; Arch = "x86_64"; Extension = "" }, | |
| @{ OS = "linux"; Arch = "arm64"; Extension = "" }, | |
| @{ OS = "macos"; Arch = "x86_64"; Extension = "" }, | |
| @{ OS = "macos"; Arch = "arm64"; Extension = "" } | |
| ) | |
| foreach ($Platform in $Platforms) { | |
| $OS = $Platform.OS | |
| $Arch = $Platform.Arch | |
| $Ext = $Platform.Extension | |
| # Find the original binary. | |
| $BinaryPattern = "jetsocat_*_${Version}_${Arch}${Ext}" | |
| $OriginalBinary = Get-ChildItem -Recurse -Filter $BinaryPattern -File | Select-Object -First 1 | |
| if ($OriginalBinary) { | |
| # Create temporary folder for this zip. | |
| $TempFolder = Join-Path $ZipFolder "temp-$OS-$Arch" | |
| New-Item -ItemType Directory -Path $TempFolder -Force | |
| # Copy binary with standard name. | |
| $TargetName = "jetsocat${Ext}" | |
| $TargetPath = Join-Path $TempFolder $TargetName | |
| Copy-Item -Path $OriginalBinary.FullName -Destination $TargetPath | |
| # Create ZIP file. | |
| $ZipName = "jetsocat-$OS-$Arch.zip" | |
| $ZipPath = Join-Path $ZipFolder $ZipName | |
| Compress-Archive -Path $TargetPath -DestinationPath $ZipPath -Force | |
| Write-Host "Created $ZipName" | |
| # Clean up temp folder. | |
| Remove-Item -Path $TempFolder -Recurse -Force | |
| # Remove the original binary to avoid uploading it. | |
| Remove-Item -Path $OriginalBinary.FullName | |
| } else { | |
| Write-Warning "Could not find binary for $OS $Arch" | |
| } | |
| } | |
| shell: pwsh | |
| - name: Create GitHub release | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $Version = "${{ needs.preflight.outputs.version }}" | |
| $HashPath = 'checksums' | |
| $Files = Get-ChildItem -Recurse -File -Exclude 'CHANGELOG.md', '*.dll' | % { Get-FileHash -Algorithm SHA256 $_.FullName } | |
| $Files | % { "$($_.Hash) $(Split-Path $_.Path -leaf)" } | Out-File -FilePath $HashPath -Append -Encoding ASCII | |
| echo "::group::checksums" | |
| Get-Content $HashPath | |
| echo "::endgroup::" | |
| $ChangesPath = 'changes' | |
| parse-changelog $(Join-Path changelog CHANGELOG.md) $Version | Out-File -Encoding UTF8NoBOM $ChangesPath | |
| echo "::group::changes" | |
| Get-Content $ChangesPath | |
| echo "::endgroup::" | |
| $GhCmd = $(@('gh', 'release', 'create', "v$Version", "--repo", $Env:GITHUB_REPOSITORY, "--notes-file", $ChangesPath, $HashPath) + $Files.Path) -Join ' ' | |
| Write-Host $GhCmd | |
| $DryRun = [System.Convert]::ToBoolean('${{ inputs.dry-run }}') | |
| if (-Not $DryRun) { | |
| Invoke-Expression $GhCmd | |
| } | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| psgallery-release: | |
| name: PowerShell release | |
| environment: publish-prod | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download artifacts | |
| run: gh run download ${{ needs.preflight.outputs.run }} -n devolutions-gateway-signed --repo $Env:GITHUB_REPOSITORY | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| ## workflow_call: The same artifacts persist across the entire run, so the PowerShell/DevolutionsGateway directory will still exist from the CI workflow | |
| - name: Manage artifacts | |
| run: Remove-Item -Path (Join-Path PowerShell DevolutionsGateway) -Recurse -ErrorAction Ignore | |
| shell: pwsh | |
| - name: Install PSResourceGet | |
| run: | | |
| Install-PSResource Microsoft.PowerShell.PSResourceGet -Scope CurrentUser -TrustRepository | |
| shell: pwsh | |
| - name: Publish PowerShell module | |
| run: | | |
| $Archive = Get-ChildItem -Recurse -Filter "*-ps-*.tar" -File | |
| Write-Host "Archive = $Archive" | |
| tar -xvf "$Archive" -C './PowerShell' | |
| Get-ChildItem -Path "./PowerShell" -Recurse | |
| $PublishCmd = @('Publish-PSResource', '-Repository', 'PSGallery', '-Path', (Join-Path PowerShell DevolutionsGateway), '-ApiKey', '${{ secrets.PS_GALLERY_NUGET_API_KEY }}') | |
| $DryRun = [System.Convert]::ToBoolean('${{ inputs.dry-run }}') | |
| if ($DryRun) { | |
| $PublishCmd += '-WhatIf' | |
| } | |
| $PublishCmd = $PublishCmd -Join ' ' | |
| Write-Host "PublishCmd = $PublishCmd" | |
| try { | |
| Invoke-Expression $PublishCmd | |
| } catch { | |
| if ($_.Exception.Message -ilike "*cannot be published as the current version*is already available in the repository*") { | |
| echo "::warning::PowerShell module not published; this version is already listed on PSGallery" | |
| } else { | |
| Write-Error $_ | |
| exit 1 | |
| } | |
| } | |
| shell: pwsh | |
| onedrive-gateway: | |
| name: OneDrive (Devolutions Gateway) | |
| environment: onedrive-upload # for OneDrive secrets | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out Devolutions/actions | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: Devolutions/actions | |
| ref: v1 | |
| token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| path: ./.github/workflows | |
| ## Devolutions Toolbox is required for OneDrive uploading | |
| - name: Install Devolutions Toolbox | |
| uses: ./.github/workflows/toolbox-install | |
| with: | |
| github_token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| - name: Download artifacts | |
| run: gh run download ${{ needs.preflight.outputs.run }} -n devolutions-gateway-signed --repo $Env:GITHUB_REPOSITORY | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Prepare upload | |
| id: prepare | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $destinationFolder = "${{ runner.temp }}/artifacts" | |
| $version="${{ needs.preflight.outputs.version }}" | |
| # Note that ".0" is appended here (required by release tooling downstream) | |
| $versionFull="$version.0" | |
| echo "version=${versionFull}" >> $Env:GITHUB_OUTPUT | |
| echo "files-to-upload=$destinationFolder" >> $Env:GITHUB_OUTPUT | |
| New-Item -Path "$destinationFolder" -ItemType "directory" | |
| Move-Item -Path "./windows/x86_64/DevolutionsGateway-x86_64-${version}.msi" -Destination "$destinationFolder/DevolutionsGateway-x86_64-${versionFull}.msi" | |
| Move-Item -Path "./windows/x86_64/DevolutionsGateway_Windows_${version}_x86_64.pdb" -Destination "$destinationFolder/DevolutionsGateway-x86_64-${versionFull}.pdb" | |
| Move-Item -Path "./linux/x86_64/devolutions-gateway_${version}-1_amd64.deb" -Destination "$destinationFolder/devolutions-gateway_${versionFull}_amd64.deb" | |
| Move-Item -Path "./linux/x86_64/devolutions-gateway_${version}-1_x86_64.rpm" -Destination "$destinationFolder/devolutions-gateway_${versionFull}_x86_64.rpm" | |
| shell: pwsh | |
| - name: Upload to OneDrive | |
| uses: ./.github/workflows/onedrive-upload | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' && !inputs.dry-run }} | |
| with: | |
| azure_client_id: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_ID }} | |
| azure_client_secret: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_SECRET }} | |
| conflict_behavior: fail | |
| destination_path: /Gateway/${{ steps.prepare.outputs.version }} | |
| remote: releases | |
| source_path: ${{ steps.prepare.outputs.files-to-upload }} | |
| onedrive-agent: | |
| name: OneDrive (Devolutions Agent) | |
| environment: onedrive-upload # for OneDrive secrets | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out Devolutions/actions | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: Devolutions/actions | |
| ref: v1 | |
| token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| path: ./.github/workflows | |
| ## Devolutions Toolbox is required for OneDrive uploading | |
| - name: Install Devolutions Toolbox | |
| uses: ./.github/workflows/toolbox-install | |
| with: | |
| github_token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| - name: Download artifacts | |
| run: gh run download ${{ needs.preflight.outputs.run }} -n devolutions-agent-signed --repo $Env:GITHUB_REPOSITORY | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Prepare upload | |
| id: prepare | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $destinationFolder = "${{ runner.temp }}/artifacts" | |
| $version="${{ needs.preflight.outputs.version }}" | |
| # Note that ".0" is appended here (required by release tooling downstream) | |
| $versionFull="$version.0" | |
| echo "version=${versionFull}" >> $Env:GITHUB_OUTPUT | |
| echo "files-to-upload=$destinationFolder" >> $Env:GITHUB_OUTPUT | |
| New-Item -Path "$destinationFolder" -ItemType "directory" | |
| Move-Item -Path "./windows/x86_64/DevolutionsAgent-x86_64-${version}.msi" -Destination "$destinationFolder/DevolutionsAgent-x86_64-${versionFull}.msi" | |
| Move-Item -Path "./windows/x86_64/DevolutionsAgent-x86_64-${version}.symbols.zip" -Destination "$destinationFolder/DevolutionsAgent-x86_64-${versionFull}.symbols.zip" | |
| Move-Item -Path "./linux/x86_64/devolutions-agent_${version}-1_amd64.deb" -Destination "$destinationFolder/devolutions-agent_${versionFull}_amd64.deb" | |
| Move-Item -Path "./linux/x86_64/devolutions-agent_${version}-1_x86_64.rpm" -Destination "$destinationFolder/devolutions-agent_${versionFull}_x86_64.rpm" | |
| shell: pwsh | |
| - name: Upload to OneDrive | |
| uses: ./.github/workflows/onedrive-upload | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' && !inputs.dry-run }} | |
| with: | |
| azure_client_id: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_ID }} | |
| azure_client_secret: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_SECRET }} | |
| conflict_behavior: fail | |
| destination_path: /Agent/${{ steps.prepare.outputs.version }} | |
| remote: releases | |
| source_path: ${{ steps.prepare.outputs.files-to-upload }} | |
| jetsocat-publish: | |
| name: Jetsocat NuGet publish | |
| environment: publish-prod | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' || inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download jetsocat-nuget artifact | |
| run: gh run download ${{ needs.preflight.outputs.run }} -n jetsocat-nuget --repo $Env:GITHUB_REPOSITORY | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: NuGet login (OIDC) | |
| id: nuget-login | |
| uses: NuGet/login@v1 | |
| with: | |
| user: ${{ secrets.NUGET_BOT_USERNAME }} | |
| - name: Publish Jetsocat NuGet package | |
| run: | | |
| Set-PSDebug -Trace 1 | |
| $Package = Get-ChildItem -Recurse -Filter "Devolutions.Jetsocat.*.nupkg" -File | Select-Object -First 1 | |
| Write-Host "Package = $Package" | |
| $PushCmd = @( | |
| 'dotnet', | |
| 'nuget', | |
| 'push', | |
| "$($Package.FullName)", | |
| '--api-key', | |
| '${{ steps.nuget-login.outputs.NUGET_API_KEY }}', | |
| '--source', | |
| 'https://api.nuget.org/v3/index.json', | |
| '--skip-duplicate' | |
| ) | |
| $PushCmd = $PushCmd -Join ' ' | |
| Write-Host $PushCmd | |
| $DryRun = [System.Convert]::ToBoolean('${{ inputs.dry-run }}') | |
| if (-Not $DryRun) { | |
| try { | |
| Invoke-Expression $PushCmd | |
| } catch { | |
| if ($_.Exception.Message -ilike "*package already exists*" -Or $_.Exception.Message -ilike "*already has a package*") { | |
| echo "::warning::Jetsocat NuGet package not published; this version is already listed on NuGet.org" | |
| } else { | |
| Write-Error $_ | |
| exit 1 | |
| } | |
| } | |
| } | |
| shell: pwsh | |
| remove-labels: | |
| name: Remove release-required labels | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' && !inputs.dry-run }} | |
| needs: [preflight, container, github-release, psgallery-release, onedrive-gateway, onedrive-agent, jetsocat-publish] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out ${{ github.repository }} | |
| uses: actions/checkout@v4 | |
| - name: Remove labels | |
| run: ./ci/remove-labels.ps1 -Label 'release-required' | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| upload-sbom: | |
| name: Upload SBOM | |
| environment: sbom | |
| if: ${{ needs.preflight.outputs.skip-publishing == 'false' && !inputs.dry-run }} | |
| needs: [preflight] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out Devolutions/actions | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: Devolutions/actions | |
| ref: v1 | |
| token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| path: ./.github/workflows | |
| ## Devolutions Toolbox is required for OneDrive uploading | |
| - name: Install Devolutions Toolbox | |
| uses: ./.github/workflows/toolbox-install | |
| with: | |
| github_token: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} | |
| - name: Download SBOM artifact | |
| run: gh run download ${{ needs.preflight.outputs.run }} -n sbom --repo $GITHUB_REPOSITORY | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload SBOM to OneDrive Releases | |
| uses: ./.github/workflows/onedrive-upload | |
| with: | |
| azure_client_id: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_ID }} | |
| azure_client_secret: ${{ secrets.ONEDRIVE_AUTOMATION_CLIENT_SECRET }} | |
| conflict_behavior: replace | |
| # Append ".0" to match the 4-part version format in OneDrive. | |
| destination_path: /Gateway/${{ needs.preflight.outputs.version }}.0 | |
| remote: releases | |
| source_path: bom.json | |
| - 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.json | |
| project_name: devolutions-gateway | |
| project_version: ${{ needs.preflight.outputs.version }} | |
| server_hostname: 'dtrack-api.devolutions.com' |