name: Build and Release on: push: branches: - "**" - "!ci/linux-build-image" tags: - "*" - "!ios-v*" - "!android-v*" workflow_dispatch: inputs: patch: description: "Hotfix patch number, for example 1 or 2. Use 0 for a normal build." required: false default: "0" sign_windows_test: description: "Submit the Windows installer to SignPath using test-signing." required: false type: boolean default: false permissions: actions: read contents: write env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: build-linux: name: Build Linux (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} strategy: matrix: include: - arch: amd64 runner: ubuntu-24.04 image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.1.1-rust1.92.0 package_script: ./apps/desktop/scripts/linux/pkg_amd64.sh - arch: arm64 runner: ubuntu-24.04-arm image: crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake3.1.1-rust1.92.0 package_script: ./apps/desktop/scripts/linux/pkg_arm64.sh container: image: ${{ matrix.image }} options: --user root steps: - name: Extract version number shell: bash run: | VERSION_REF="${GITHUB_REF##*/}" VERSION_BASE="${VERSION_REF#v}" PATCH_NUMBER="${{ github.event.inputs.patch }}" BUILD_DATE_OVERRIDE="" if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then PATCH_NUMBER=0 fi if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" BUILD_DATE_OVERRIDE="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE_OVERRIDE="${BASH_REMATCH[3]}" PATCH_NUMBER="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE_OVERRIDE="${BASH_REMATCH[3]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" fi echo "VERSION_BASE=${VERSION_BASE}" >> $GITHUB_ENV echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_ENV echo "BUILD_DATE_OVERRIDE=${BUILD_DATE_OVERRIDE}" >> $GITHUB_ENV - name: Set legal Debian version shell: bash run: | BUILD_DATE="${BUILD_DATE_OVERRIDE}" if [[ -z "${BUILD_DATE}" ]]; then BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) fi if [[ ! "${VERSION_BASE}" =~ ^[0-9] ]]; then VERSION_BASE="0.0.0-${VERSION_BASE}" fi if [[ "${PATCH_NUMBER}" != "0" ]]; then LEGAL_VERSION="v${VERSION_BASE}-${PATCH_NUMBER}-${BUILD_DATE}" else LEGAL_VERSION="v${VERSION_BASE}-${BUILD_DATE}" fi echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV - name: Checkout code uses: actions/checkout@v5 with: submodules: recursive - name: Verify Linux build image run: verify-crossdesk-build-image - name: Ensure Wayland build dependencies shell: bash run: | if pkg-config --exists dbus-1 libdrm && \ test -f /opt/crossdesk-pipewire-sdk/include/pipewire-0.3/pipewire/pipewire.h && \ test -f /opt/crossdesk-pipewire-sdk/include/spa-0.2/spa/param/video/format-utils.h; then exit 0 fi apt-get update apt-get install -y --no-install-recommends \ libdbus-1-dev \ libdrm-dev rm -rf /var/lib/apt/lists/* install-crossdesk-pipewire-sdk pkg-config --exists dbus-1 libdrm test -f /opt/crossdesk-pipewire-sdk/include/pipewire-0.3/pipewire/pipewire.h test -f /opt/crossdesk-pipewire-sdk/include/spa-0.2/spa/param/video/format-utils.h - name: Build CrossDesk env: CUDA_PATH: /usr/local/cuda XMAKE_GLOBALDIR: /data run: | xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --USE_CUDA=true --USE_WAYLAND=true \ --cc=gcc-10 --cxx=g++-10 --root -y xmake b -vy --root crossdesk - name: Verify repository and wire boundaries env: XMAKE_GLOBALDIR: /data run: | xmake b -vy --root repository_structure_test xmake b -vy --root crossdesk_wire_test xmake b -vy --root keyboard_state_wire_test xmake run --root repository_structure_test xmake run --root crossdesk_wire_test xmake run --root keyboard_state_wire_test - name: Install Linux headless runtime dependencies run: | apt-get update apt-get install -y --no-install-recommends xvfb xfce4 dbus - name: Package run: | chmod +x ${{ matrix.package_script }} ${{ matrix.package_script }} ${LEGAL_VERSION} - name: Verify Ubuntu 20.04 package dependency resolution run: | PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" apt-get update apt-get install --simulate "${PACKAGE_FILE}" - name: Verify packaged Ubuntu 20.04 glibc baseline run: | PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" VERIFY_DIR="${RUNNER_TEMP}/crossdesk-glibc-${{ matrix.arch }}" install -d "${VERIFY_DIR}" dpkg-deb --extract "${PACKAGE_FILE}" "${VERIFY_DIR}" verify-crossdesk-glibc-baseline "${VERIFY_DIR}" 2.31 - name: Upload artifact uses: actions/upload-artifact@v6 with: name: crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }} path: ${{ github.workspace }}/crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}.deb # macOS build-macos: name: Build on macOS runs-on: ${{ matrix.runner }} env: MACOSX_DEPLOYMENT_TARGET: "14.0" EXPECTED_MACOS_SDK: "26.5" strategy: matrix: include: - arch: x64 binary_arch: x86_64 runner: macos-26-intel cache-key: intel-macos26-xcode26.6-min14 out-dir: ./build/macosx/x86_64/release/crossdesk package_script: ./apps/desktop/scripts/macos/pkg_x64.sh - arch: arm64 binary_arch: arm64 runner: macos-26 cache-key: arm-macos26-xcode26.6-min14 out-dir: ./build/macosx/arm64/release/crossdesk package_script: ./apps/desktop/scripts/macos/pkg_arm64.sh steps: - name: Extract version number id: version shell: bash run: | VERSION_REF="${GITHUB_REF##*/}" VERSION_BASE="${VERSION_REF#v}" BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) PATCH_NUMBER="${{ github.event.inputs.patch }}" if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then PATCH_NUMBER=0 fi if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" BUILD_DATE="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE="${BASH_REMATCH[3]}" PATCH_NUMBER="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE="${BASH_REMATCH[3]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" fi if [[ "${PATCH_NUMBER}" != "0" ]]; then VERSION_NUM="v${VERSION_BASE}-${PATCH_NUMBER}-${BUILD_DATE}" else VERSION_NUM="v${VERSION_BASE}-${BUILD_DATE}" fi echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV echo "VERSION_NUM=${VERSION_NUM}" echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_ENV - name: Select Xcode 26.6 shell: bash run: | sudo xcode-select -s /Applications/Xcode_26.6.app/Contents/Developer xcodebuild -version SDK_VERSION="$(xcrun --sdk macosx --show-sdk-version)" if [[ "${SDK_VERSION}" != "${EXPECTED_MACOS_SDK}" ]]; then echo "Unexpected macOS SDK: expected ${EXPECTED_MACOS_SDK}, got ${SDK_VERSION}" >&2 exit 1 fi - name: Cache xmake dependencies uses: actions/cache@v5 with: path: ~/.xmake/packages key: "${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-${{ github.run_id }}" restore-keys: | ${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}- - name: Install xmake run: brew install xmake - name: Update xmake repositories run: xmake repo -u - name: Checkout code uses: actions/checkout@v5 - name: Initialize submodules run: git submodule update --init --recursive - name: Build CrossDesk run: | xmake f --target_minver=${MACOSX_DEPLOYMENT_TARGET} --CROSSDESK_VERSION=${VERSION_NUM} --USE_CUDA=false -vyD xmake b -vy crossdesk - name: Package CrossDesk app run: | chmod +x ${{ matrix.package_script }} ${{ matrix.package_script }} ${VERSION_NUM} - name: Verify packaged app shell: bash run: | PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-macos-${{ matrix.arch }}-${VERSION_NUM}.pkg" VERIFY_DIR="$(mktemp -d "${RUNNER_TEMP}/crossdesk-macos-pkg.XXXXXX")" trap 'rm -rf "${VERIFY_DIR}"' EXIT pkgutil --expand-full "${PACKAGE_FILE}" "${VERIFY_DIR}/expanded" APP_PATH="$(find "${VERIFY_DIR}/expanded" -type d -name 'CrossDesk.app' -print -quit)" if [[ -z "${APP_PATH}" ]]; then echo "CrossDesk.app is missing from ${PACKAGE_FILE}" >&2 exit 1 fi ./apps/desktop/scripts/macos/verify_app.sh "${APP_PATH}" "${{ matrix.binary_arch }}" "${EXPECTED_MACOS_SDK}" - name: Upload build artifacts uses: actions/upload-artifact@v6 with: name: crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }} path: crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg - name: Move files to release dir run: | mkdir -p release cp crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg release/ # iOS (also available as a standalone workflow) build-ios: name: Build iOS uses: ./.github/workflows/build-ios.yml with: patch: ${{ inputs.patch || '0' }} permissions: contents: read # Android (also available as a standalone workflow) build-android: name: Build Android uses: ./.github/workflows/build-android.yml permissions: contents: read # Windows build-windows-x64: name: Build on Windows x64 runs-on: windows-2022 env: XMAKE_GLOBALDIR: D:\xmake_global steps: - name: Extract version number shell: pwsh run: | $ref = $env:GITHUB_REF $version = $ref -replace '^refs/(tags|heads)/', '' $version = $version -replace '^v', '' $version = $version -replace '/', '-' $BUILD_DATE = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), "China Standard Time")).ToString("yyyyMMdd") $PATCH_NUMBER = "${{ github.event.inputs.patch }}" if ($PATCH_NUMBER -notmatch '^[0-9]+$') { $PATCH_NUMBER = "0" } if ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$') { $version = $Matches[1] $PATCH_NUMBER = $Matches[3] $BUILD_DATE = $Matches[4] } elseif ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$') { $version = $Matches[1] $BUILD_DATE = $Matches[3] $PATCH_NUMBER = $Matches[4] } elseif ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$') { $version = $Matches[1] $BUILD_DATE = $Matches[3] } elseif ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$' -and $PATCH_NUMBER -eq "0") { $version = $Matches[1] $PATCH_NUMBER = $Matches[3] } if ($PATCH_NUMBER -ne "0") { $VERSION_NUM = "v$version-$PATCH_NUMBER-$BUILD_DATE" } else { $VERSION_NUM = "v$version-$BUILD_DATE" } echo "VERSION_NUM=$VERSION_NUM" >> $env:GITHUB_ENV echo "BUILD_DATE=$BUILD_DATE" >> $env:GITHUB_ENV echo "PATCH_NUMBER=$PATCH_NUMBER" >> $env:GITHUB_ENV - name: Cache xmake dependencies uses: actions/cache@v5 with: path: D:\xmake_global\.xmake\packages key: "${{ runner.os }}-xmake-deps-intel-${{ github.run_id }}" restore-keys: | ${{ runner.os }}-xmake-deps-intel- - name: Install xmake run: | Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content echo "C:\Users\runneradmin\xmake" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append xmake repo -u xmake create cuda Set-Location cuda xmake g --theme=plain $cudaPath = "" $packagesPath = "D:\xmake_global\.xmake\packages" if (Test-Path $packagesPath) { Write-Host "Packages directory exists: $packagesPath" try { $info = xmake require --info "cuda 12.6.3" 2>$null if ($null -ne $info -and $info -ne "") { $cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim() } } catch {} } else { Write-Host "Packages directory not found: $packagesPath" Write-Host "Installing CUDA package..." xmake require -vy "cuda 12.6.3" $info = xmake require --info "cuda 12.6.3" $cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim() } echo "CUDA_PATH=$cudaPath" >> $env:GITHUB_ENV Write-Host "Resolved CUDA_PATH = $cudaPath" Pop-Location - name: Download INetC plugin shell: powershell run: | $url = "https://github.com/DigitalMediaServer/NSIS-INetC-plugin/releases/download/v1.0.5.7/InetC.zip" $out = "$env:RUNNER_TEMP\InetC.zip" Invoke-WebRequest -Uri $url -OutFile $out Expand-Archive $out -DestinationPath "$env:RUNNER_TEMP\InetC" -Force $source = "$env:RUNNER_TEMP\InetC\x86-unicode\INetC.dll" $target = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\INetC.dll" Write-Host "Copying $source to $target" Copy-Item $source $target -Force - name: Checkout code uses: actions/checkout@v5 - name: Initialize submodules run: git submodule update --init --recursive - name: Copy nsProcess plugin to NSIS folder run: | $nsisPluginDir = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode" copy "${{ github.workspace }}\apps\desktop\scripts\windows\nsProcess.dll" $nsisPluginDir - name: Build CrossDesk run: | xmake f --CROSSDESK_VERSION=${{ env.VERSION_NUM }} --USE_CUDA=true -y xmake b -vy crossdesk - name: Test installed Windows autostart and unattended host run: | xmake b -vy windows_autostart_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake run windows_autostart_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake b -vy windows_unattended_policy_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake b -vy windows_unattended_security_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake run windows_unattended_policy_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake run windows_unattended_security_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake b -vy windows_service_lifetime_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake run windows_service_lifetime_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - name: Package shell: pwsh run: | & "${{ github.workspace }}\apps\desktop\scripts\windows\pkg_x64.ps1" $env:VERSION_NUM - name: Upload unsigned Windows installer for test signing if: github.event_name == 'workflow_dispatch' && inputs.sign_windows_test id: upload-unsigned-windows-installer uses: actions/upload-artifact@v7 with: path: ${{ github.workspace }}/apps/desktop/scripts/windows/crossdesk-win-x64-${{ env.VERSION_NUM }}.exe archive: false if-no-files-found: error retention-days: 1 - name: Sign Windows installer with SignPath test certificate if: github.event_name == 'workflow_dispatch' && inputs.sign_windows_test uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: crossdesk signing-policy-slug: test-signing github-artifact-id: ${{ steps.upload-unsigned-windows-installer.outputs.artifact-id }} wait-for-completion: true output-artifact-directory: ${{ github.workspace }}\signpath\signed-installer skip-decompress: true parameters: | version: ${{ toJSON(env.VERSION_NUM) }} - name: Verify and use test-signed Windows installer if: github.event_name == 'workflow_dispatch' && inputs.sign_windows_test shell: pwsh run: | $installerName = "crossdesk-win-x64-${{ env.VERSION_NUM }}.exe" $signedInstaller = Join-Path "${{ github.workspace }}\signpath\signed-installer" $installerName $destination = Join-Path "${{ github.workspace }}\apps\desktop\scripts\windows" $installerName if (!(Test-Path $signedInstaller)) { throw "SignPath did not return the signed installer: $signedInstaller" } $signature = Get-AuthenticodeSignature $signedInstaller if (!$signature.SignerCertificate -or $signature.Status -eq "NotSigned") { throw "SignPath returned an installer without an Authenticode signature." } Write-Host "Signer: $($signature.SignerCertificate.Subject)" Write-Host "Signature status: $($signature.Status)" Copy-Item $signedInstaller $destination -Force - name: Build Portable CrossDesk run: | xmake f --CROSSDESK_VERSION=${{ env.VERSION_NUM }} --USE_CUDA=true --CROSSDESK_PORTABLE=true -y xmake b -vy crossdesk - name: Test portable Windows autostart run: | xmake b -vy windows_autostart_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } xmake run windows_autostart_test if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - name: Package Portable shell: pwsh run: | $buildDir = "${{ github.workspace }}\build\windows\x64\release" $portableDir = "${{ github.workspace }}\portable" New-Item -ItemType Directory -Force -Path $portableDir $portableFiles = @( @("crossdesk.exe", "CrossDesk.exe"), @("crossdesk_service.exe", "crossdesk_service.exe"), @("crossdesk_session_helper.exe", "crossdesk_session_helper.exe"), @("crossdesk_privacy_cursor_helper.exe", "crossdesk_privacy_cursor_helper.exe"), @("crossdesk_privacy_window.dll", "crossdesk_privacy_window.dll"), @("slint_cpp.dll", "slint_cpp.dll") ) foreach ($file in $portableFiles) { $source = Join-Path $buildDir $file[0] $destination = Join-Path $portableDir $file[1] if (!(Test-Path $source)) { throw "Missing portable package file: $source" } Copy-Item $source $destination -Force } Copy-Item (Join-Path $buildDir "*.dll") $portableDir -Force # Bundled Amyuni usbmmidd_v2 virtual display driver (headless hosts) $driverDir = Join-Path $buildDir "usbmmidd_v2" if (!(Test-Path (Join-Path $driverDir "deviceinstaller64.exe"))) { throw "Missing bundled usbmmidd_v2 driver package: $driverDir" } Copy-Item $driverDir (Join-Path $portableDir "usbmmidd_v2") -Recurse -Force foreach ($file in $portableFiles) { $packagedFile = Join-Path $portableDir $file[1] if (!(Test-Path $packagedFile)) { throw "Portable package is missing: $packagedFile" } } Compress-Archive -Path "$portableDir\*" -DestinationPath "${{ github.workspace }}\crossdesk-win-x64-portable-${{ env.VERSION_NUM }}.zip" - name: Upload artifact uses: actions/upload-artifact@v6 with: name: crossdesk-win-x64-${{ env.VERSION_NUM }} path: ${{ github.workspace }}/apps/desktop/scripts/windows/crossdesk-win-x64-${{ env.VERSION_NUM }}.exe - name: Upload portable artifact uses: actions/upload-artifact@v6 with: name: crossdesk-win-x64-portable-${{ env.VERSION_NUM }} path: ${{ github.workspace }}/crossdesk-win-x64-portable-${{ env.VERSION_NUM }}.zip sign-android: name: Sign Android release needs: build-android if: startsWith(github.ref, 'refs/tags/v') uses: ./.github/workflows/sign-android.yml with: version: ${{ needs.build-android.outputs.version }} secrets: ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} release: name: Publish Release if: startsWith(github.ref, 'refs/tags/v') needs: [build-linux, build-macos, build-ios, build-android, sign-android, build-windows-x64] runs-on: ubuntu-latest concurrency: group: publish-release-metadata cancel-in-progress: false steps: - name: Checkout repository uses: actions/checkout@v5 - name: Download all artifacts uses: actions/download-artifact@v8 with: path: artifacts - name: Verify release manifest updates run: python3 -B -m unittest discover -s .github/scripts -p 'test_*.py' -v - name: Extract version number id: version shell: bash run: | VERSION_REF="${GITHUB_REF##*/}" VERSION_BASE="${VERSION_REF#v}" BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) PATCH_NUMBER="${{ github.event.inputs.patch }}" if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then PATCH_NUMBER=0 fi if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" BUILD_DATE="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE="${BASH_REMATCH[3]}" PATCH_NUMBER="${BASH_REMATCH[4]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then VERSION_BASE="${BASH_REMATCH[1]}" BUILD_DATE="${BASH_REMATCH[3]}" elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then VERSION_BASE="${BASH_REMATCH[1]}" PATCH_NUMBER="${BASH_REMATCH[3]}" fi BUILD_DATE_ISO="${BUILD_DATE:0:4}-${BUILD_DATE:4:2}-${BUILD_DATE:6:2}" if [[ "${PATCH_NUMBER}" != "0" ]]; then VERSION_NUM="${VERSION_BASE}-${PATCH_NUMBER}-${BUILD_DATE}" else VERSION_NUM="${VERSION_BASE}-${BUILD_DATE}" fi VERSION_WITH_V="v${VERSION_NUM}" echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT echo "VERSION_WITH_V=${VERSION_WITH_V}" >> $GITHUB_OUTPUT echo "VERSION_BASE=${VERSION_BASE}" >> $GITHUB_OUTPUT echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_OUTPUT echo "BUILD_DATE_ISO=${BUILD_DATE_ISO}" >> $GITHUB_OUTPUT echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_OUTPUT - name: Prepare release notes run: | cat > "$RUNNER_TEMP/release-notes.md" << EOF ## Corresponding source Source for these binaries: [${GITHUB_REF_NAME}](https://github.com/${GITHUB_REPOSITORY}/tree/${GITHUB_SHA}). Clone that version with recursive submodules. The repository includes the build scripts and dependency patches. [iOS source, modification and rebuild instructions](https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_SHA}/apps/ios/README.md). The iOS archive also contains the exact source revision and third-party source catalog with archive checksums. [Android source and rebuild instructions](https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_SHA}/apps/android/README.md). The Android arm64 Release APK is signed with the CrossDesk publishing key and can be installed directly. It includes the exact application and MiniRTC revisions, license documents and native build recipes. ## Code signing policy Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). EOF - name: Rename artifacts run: | mkdir -p release cp artifacts/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg cp artifacts/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg cp artifacts/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}.deb cp artifacts/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.deb cp artifacts/crossdesk-ios-arm64-unsigned-${{ needs.build-ios.outputs.version }}/* release/crossdesk-ios-arm64-unsigned-${{ needs.build-ios.outputs.version }}.zip cp artifacts/crossdesk-android-arm64-${{ needs.build-android.outputs.version }}/*.apk release/crossdesk-android-arm64-${{ needs.build-android.outputs.version }}.apk cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe cp artifacts/crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}.zip - name: List release files run: ls -lh release/ - name: Upload to Versioned GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.version.outputs.VERSION_WITH_V }} # The pushed tag already determines the release source commit. target_commitish: ${{ github.event.repository.default_branch }} name: Release ${{ steps.version.outputs.VERSION_WITH_V }} draft: false prerelease: false files: release/* generate_release_notes: false body_path: ${{ runner.temp }}/release-notes.md - name: Create or update 'latest' tag run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -f latest git push origin latest --force - name: Upload to GitHub Release (latest) uses: softprops/action-gh-release@v2 with: tag_name: latest name: Latest Release draft: false prerelease: false files: release/* generate_release_notes: false body_path: ${{ runner.temp }}/release-notes.md - name: Upload artifacts to server uses: burnett01/rsync-deployments@5.2 with: switches: -avzr --progress path: release/* remote_path: /var/www/html/downloads/ remote_host: ${{ secrets.SERVER_HOST }} remote_user: ${{ secrets.SERVER_USER }} remote_key: ${{ secrets.SERVER_KEY }} - name: Download current version.json run: | curl --fail --silent --show-error --retry 3 --max-time 30 \ -H 'Cache-Control: no-cache' \ https://version.crossdesk.cn/version.json -o current-version.json - name: Generate version.json for all platforms env: RELEASE_TAG: ${{ steps.version.outputs.VERSION_WITH_V }} run: | python3 .github/scripts/update_version_manifest.py \ --current current-version.json --output version.json --artifacts release \ --tag "${RELEASE_TAG}" --name "Release ${RELEASE_TAG}" \ --notes "${RUNNER_TEMP}/release-notes.md" - name: Upload version.json to server uses: burnett01/rsync-deployments@5.2 with: switches: -avzr path: version.json remote_path: /var/www/html/version/ remote_host: ${{ secrets.SERVER_HOST }} remote_user: ${{ secrets.SERVER_USER }} remote_key: ${{ secrets.SERVER_KEY }}