diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a5ccab..3e453a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,8 +13,14 @@ on: 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: @@ -410,6 +416,52 @@ jobs: run: | & "${{ github.workspace }}\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 }}/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 }}\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 @@ -544,6 +596,10 @@ jobs: body: | Binary release only. Source code is not included. + ## Code signing policy + + Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). + - name: Create or update 'latest' tag run: | git config user.name "github-actions[bot]" @@ -560,6 +616,12 @@ jobs: prerelease: false files: release/* generate_release_notes: false + body: | + Binary release only. Source code is not included. + + ## Code signing policy + + Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). - name: Upload artifacts to server uses: burnett01/rsync-deployments@5.2 diff --git a/README.md b/README.md index 0876987..9751709 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,14 @@ [![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]() [![GitHub forks](https://img.shields.io/github/forks/kunkundi/crossdesk.svg?style=social)]() +## Code signing policy + +Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). + +- Committers and reviewers: [kunkundi](https://github.com/kunkundi) +- Approvers: [kunkundi](https://github.com/kunkundi) +- Privacy policy: CrossDesk will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. + [ [English](README_EN.md) / 中文 ] PC 客户端 diff --git a/README_EN.md b/README_EN.md index 7f9a048..07a994e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -10,6 +10,14 @@ [![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]() [![GitHub forks](https://img.shields.io/github/forks/kunkundi/crossdesk.svg?style=social)]() +## Code signing policy + +Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). + +- Committers and reviewers: [kunkundi](https://github.com/kunkundi) +- Approvers: [kunkundi](https://github.com/kunkundi) +- Privacy policy: CrossDesk will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. + [ [中文](README.md) / English ] PC Client diff --git a/scripts/windows/crossdesk.rc b/scripts/windows/crossdesk.rc index 2783240..7f07aee 100644 --- a/scripts/windows/crossdesk.rc +++ b/scripts/windows/crossdesk.rc @@ -9,3 +9,8 @@ CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_debug.manifest" #else CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk.manifest" #endif + +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk" +#define CROSSDESK_ORIGINAL_FILENAME "CrossDesk.exe" + +#include "version_info.rcinc" diff --git a/scripts/windows/crossdesk_portable.rc b/scripts/windows/crossdesk_portable.rc index a391fb5..1fe77f5 100644 --- a/scripts/windows/crossdesk_portable.rc +++ b/scripts/windows/crossdesk_portable.rc @@ -6,3 +6,8 @@ IDI_ICON1 ICON "..\\..\\icons\\windows\\crossdesk.ico" #define RT_MANIFEST 24 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_portable.manifest" + +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Portable" +#define CROSSDESK_ORIGINAL_FILENAME "CrossDesk.exe" + +#include "version_info.rcinc" diff --git a/scripts/windows/crossdesk_service.rc b/scripts/windows/crossdesk_service.rc new file mode 100644 index 0000000..8fd8e20 --- /dev/null +++ b/scripts/windows/crossdesk_service.rc @@ -0,0 +1,4 @@ +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Service" +#define CROSSDESK_ORIGINAL_FILENAME "crossdesk_service.exe" + +#include "version_info.rcinc" diff --git a/scripts/windows/crossdesk_session_helper.rc b/scripts/windows/crossdesk_session_helper.rc new file mode 100644 index 0000000..524798b --- /dev/null +++ b/scripts/windows/crossdesk_session_helper.rc @@ -0,0 +1,18 @@ +// Session helper icon and manifest resources. +IDI_ICON1 ICON "..\\..\\icons\\windows\\crossdesk.ico" + +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#define RT_MANIFEST 24 + +#ifdef CROSSDESK_PORTABLE +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_portable.manifest" +#elif defined(CROSSDESK_DEBUG) +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_debug.manifest" +#else +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk.manifest" +#endif + +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Session Helper" +#define CROSSDESK_ORIGINAL_FILENAME "crossdesk_session_helper.exe" + +#include "version_info.rcinc" diff --git a/scripts/windows/nsis_script.nsi b/scripts/windows/nsis_script.nsi index 335e42c..ad8af8f 100644 --- a/scripts/windows/nsis_script.nsi +++ b/scripts/windows/nsis_script.nsi @@ -4,6 +4,7 @@ ; Installer initial constants !define PRODUCT_NAME "CrossDesk" !define PRODUCT_VERSION "${VERSION}" +!define PRODUCT_VERSION_NUMERIC "${VERSION_NUMERIC}" !define PRODUCT_PUBLISHER "CrossDesk" !define PRODUCT_WEB_SITE "https://www.crossdesk.cn/" !define APP_NAME "CrossDesk" @@ -41,6 +42,14 @@ RequestExecutionLevel admin Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" OutFile "crossdesk-win-x64-${PRODUCT_VERSION}.exe" +VIProductVersion "${PRODUCT_VERSION_NUMERIC}" +VIAddVersionKey /LANG=2052 "CompanyName" "CrossDesk" +VIAddVersionKey /LANG=2052 "FileDescription" "CrossDesk Installer" +VIAddVersionKey /LANG=2052 "FileVersion" "${PRODUCT_VERSION}" +VIAddVersionKey /LANG=2052 "LegalCopyright" "Copyright (C) CrossDesk contributors" +VIAddVersionKey /LANG=2052 "OriginalFilename" "crossdesk-win-x64-${PRODUCT_VERSION}.exe" +VIAddVersionKey /LANG=2052 "ProductName" "${PRODUCT_NAME}" +VIAddVersionKey /LANG=2052 "ProductVersion" "${PRODUCT_VERSION}" InstallDir "$PROGRAMFILES\CrossDesk" InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" "InstallDir" ShowInstDetails show diff --git a/scripts/windows/pkg_x64.ps1 b/scripts/windows/pkg_x64.ps1 index 83fc30f..b700a79 100644 --- a/scripts/windows/pkg_x64.ps1 +++ b/scripts/windows/pkg_x64.ps1 @@ -26,12 +26,40 @@ function Normalize-AppVersion { return $InputVersion } +function Convert-To-WindowsVersion { + param( + [Parameter(Mandatory = $true)] + [string]$InputVersion + ) + + $body = $InputVersion -replace '^v', '' + $baseMatch = [regex]::Match($body, '^[0-9]+(?:\.[0-9]+){0,3}') + if (!$baseMatch.Success) { + return "0.0.0.0" + } + + $parts = [System.Collections.Generic.List[int]]::new() + foreach ($part in $baseMatch.Value.Split('.')) { + $value = [int]$part + if ($value -gt 65535) { + $value = 0 + } + $parts.Add($value) + } + while ($parts.Count -lt 4) { + $parts.Add(0) + } + + return ($parts -join '.') +} + $normalizedVersion = Normalize-AppVersion -InputVersion $Version +$windowsVersion = Convert-To-WindowsVersion -InputVersion $normalizedVersion $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path Push-Location $scriptDir try { - & makensis "/DVERSION=$normalizedVersion" "nsis_script.nsi" + & makensis "/DVERSION=$normalizedVersion" "/DVERSION_NUMERIC=$windowsVersion" "nsis_script.nsi" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/scripts/windows/signpath-installer.xml b/scripts/windows/signpath-installer.xml new file mode 100644 index 0000000..51d62c7 --- /dev/null +++ b/scripts/windows/signpath-installer.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/scripts/windows/version_info.rcinc b/scripts/windows/version_info.rcinc new file mode 100644 index 0000000..ed642ca --- /dev/null +++ b/scripts/windows/version_info.rcinc @@ -0,0 +1,54 @@ +#include + +#ifndef CROSSDESK_VERSION_STRING +#define CROSSDESK_VERSION_STRING "0.0.0" +#endif + +#ifndef CROSSDESK_VERSION_NUMERIC +#define CROSSDESK_VERSION_NUMERIC 0,0,0,0 +#endif + +#ifndef CROSSDESK_FILE_DESCRIPTION +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk" +#endif + +#ifndef CROSSDESK_ORIGINAL_FILENAME +#define CROSSDESK_ORIGINAL_FILENAME "CrossDesk.exe" +#endif + +#ifndef CROSSDESK_FILE_TYPE +#define CROSSDESK_FILE_TYPE VFT_APP +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION CROSSDESK_VERSION_NUMERIC + PRODUCTVERSION CROSSDESK_VERSION_NUMERIC + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef CROSSDESK_DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE CROSSDESK_FILE_TYPE + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "CrossDesk\0" + VALUE "FileDescription", CROSSDESK_FILE_DESCRIPTION "\0" + VALUE "FileVersion", CROSSDESK_VERSION_STRING "\0" + VALUE "InternalName", CROSSDESK_ORIGINAL_FILENAME "\0" + VALUE "LegalCopyright", "Copyright (C) CrossDesk contributors\0" + VALUE "OriginalFilename", CROSSDESK_ORIGINAL_FILENAME "\0" + VALUE "ProductName", "CrossDesk\0" + VALUE "ProductVersion", CROSSDESK_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END +END diff --git a/scripts/windows/wgc_plugin.rc b/scripts/windows/wgc_plugin.rc new file mode 100644 index 0000000..76bb866 --- /dev/null +++ b/scripts/windows/wgc_plugin.rc @@ -0,0 +1,5 @@ +#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Windows Graphics Capture Plugin" +#define CROSSDESK_ORIGINAL_FILENAME "wgc_plugin.dll" +#define CROSSDESK_FILE_TYPE VFT_DLL + +#include "version_info.rcinc" diff --git a/tests/windows_manifest_resource_test.cpp b/tests/windows_manifest_resource_test.cpp index d914db9..8d3ce91 100644 --- a/tests/windows_manifest_resource_test.cpp +++ b/tests/windows_manifest_resource_test.cpp @@ -84,6 +84,14 @@ int main() { const std::string rc = ReadFile(repo_root / "scripts/windows/crossdesk.rc"); const std::string portable_rc = ReadFile(repo_root / "scripts/windows/crossdesk_portable.rc"); + const std::string service_rc = + ReadFile(repo_root / "scripts/windows/crossdesk_service.rc"); + const std::string session_helper_rc = + ReadFile(repo_root / "scripts/windows/crossdesk_session_helper.rc"); + const std::string wgc_plugin_rc = + ReadFile(repo_root / "scripts/windows/wgc_plugin.rc"); + const std::string version_info = + ReadFile(repo_root / "scripts/windows/version_info.rcinc"); const std::string manifest = ReadFile(repo_root / "scripts/windows/crossdesk.manifest"); const std::string debug_manifest = @@ -100,8 +108,31 @@ int main() { ok &= ExpectContains("crossdesk_portable.rc", portable_rc, "crossdesk_portable.manifest"); ok &= ExpectContains("crossdesk_portable.rc", portable_rc, "RT_MANIFEST"); + ok &= ExpectContains("crossdesk.rc", rc, "version_info.rcinc"); + ok &= ExpectContains("crossdesk_portable.rc", portable_rc, + "version_info.rcinc"); + ok &= ExpectContains("crossdesk_service.rc", service_rc, + "crossdesk_service.exe"); + ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc, + "crossdesk_session_helper.exe"); + ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc, + "CROSSDESK_PORTABLE"); + ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc, + "crossdesk_portable.manifest"); + ok &= ExpectContains("wgc_plugin.rc", wgc_plugin_rc, "wgc_plugin.dll"); + ok &= ExpectContains("version_info.rcinc", version_info, "ProductName"); + ok &= ExpectContains("version_info.rcinc", version_info, + "ProductVersion"); + ok &= ExpectContains("version_info.rcinc", version_info, + "OriginalFilename"); ok &= ExpectContains("xmake/targets.lua", targets, "scripts/windows/crossdesk_portable.rc"); + ok &= ExpectContains("xmake/targets.lua", targets, + "scripts/windows/crossdesk_service.rc"); + ok &= ExpectContains("xmake/targets.lua", targets, + "scripts/windows/crossdesk_session_helper.rc"); + ok &= ExpectContains("xmake/targets.lua", targets, + "scripts/windows/wgc_plugin.rc"); ok &= ExpectContains("xmake/targets.lua", targets, "CROSSDESK_PORTABLE"); ok &= ExpectContains("crossdesk.manifest", manifest, "level=\"requireAdministrator\""); diff --git a/xmake/options.lua b/xmake/options.lua index 2618795..b82b0bf 100644 --- a/xmake/options.lua +++ b/xmake/options.lua @@ -35,6 +35,28 @@ function setup_options_and_dependencies() set_languages("c++17") set_encodings("utf-8") + local crossdesk_version = get_config("CROSSDESK_VERSION") or "0.0.0" + local version_base = crossdesk_version:gsub("^v", ""):match("^(%d[%d%.]*)") or "0.0.0" + local version_parts = {} + for part in version_base:gmatch("%d+") do + local value = tonumber(part) or 0 + if value > 65535 then + value = 0 + end + table.insert(version_parts, value) + if #version_parts == 4 then + break + end + end + while #version_parts < 4 do + table.insert(version_parts, 0) + end + + add_defines("CROSSDESK_VERSION_STRING=\"" .. crossdesk_version .. "\"") + if is_os("windows") then + add_defines("CROSSDESK_VERSION_NUMERIC=" .. table.concat(version_parts, ",")) + end + -- set_policy("build.warning", true) -- set_warnings("all", "extra") -- add_cxxflags("/W4", "/WX") diff --git a/xmake/targets.lua b/xmake/targets.lua index bbfd7ac..f79edbb 100644 --- a/xmake/targets.lua +++ b/xmake/targets.lua @@ -297,6 +297,7 @@ function setup_targets() add_files("src/screen_capturer/windows/screen_capturer_wgc.cpp", "src/screen_capturer/windows/wgc_session_impl.cpp", "src/screen_capturer/windows/wgc_plugin_entry.cpp") + add_files("scripts/windows/wgc_plugin.rc") add_includedirs("src/common", "src/screen_capturer", "src/screen_capturer/windows") @@ -306,6 +307,7 @@ function setup_targets() add_links("Advapi32", "Wtsapi32", "Ole32", "Userenv") add_files("src/service/windows/main.cpp", "src/service/windows/service_host.cpp") + add_files("scripts/windows/crossdesk_service.rc") add_includedirs("src/service/windows", {public = true}) target("crossdesk_session_helper") @@ -314,7 +316,7 @@ function setup_targets() add_deps("rd_log", "path_manager") add_links("Advapi32", "User32", "Wtsapi32", "Gdi32") add_files("src/service/windows/session_helper_main.cpp") - add_files(crossdesk_windows_resource) + add_files("scripts/windows/crossdesk_session_helper.rc") add_includedirs("src/common", "src/service/windows", {public = true}) end