Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,18 @@ jobs:

- name: Build self-contained DualSense sidecar
shell: pwsh
run: .\scripts\build-ds5-sidecar.ps1
env:
DS5_COMPONENT_VERSION: 1.0.0+${{ needs.setup_release.outputs.release_commit }}
run: .\scripts\build-ds5-sidecar.ps1 -ComponentVersion $env:DS5_COMPONENT_VERSION

- name: Package DualSense component release asset
shell: pwsh
env:
DS5_RELEASE_TAG: ${{ needs.setup_release.outputs.release_tag }}.杂鱼
run: |
.\scripts\package-ds5-sidecar.ps1 `
-RuntimeDirectory build/ds5-sidecar-runtime `
-ReleaseTag $env:DS5_RELEASE_TAG

- name: Build Web UI
shell: pwsh
Expand Down Expand Up @@ -272,25 +283,24 @@ jobs:
# Package the Inno Setup installer and portable ZIP.
# Install into the staging directory first.
cmake --install . --prefix ./inno_staging
test -f ./inno_staging/tools/sunshine-ds5-sidecar/Sunshine.Ds5Sidecar.exe
test -f ./inno_staging/assets/components/dualsense.json
test ! -e ./inno_staging/tools/sunshine-ds5-sidecar

# Run the Inno Setup compiler and prove that it consumed the sidecar.
# The main package contains only the trusted component manifest.
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" sunshine_installer.iss | tee inno-build.log
grep -F "Sunshine.Ds5Sidecar.exe" inno-build.log
! grep -F "sunshine-ds5-sidecar" inno-build.log

# Generate the portable ZIP.
cpack -G ZIP --config ./CPackConfig.cmake --verbose
cmake -E tar tf ./cpack_artifacts/Sunshine.zip | tr '\\' '/' | grep -F "Sunshine/tools/sunshine-ds5-sidecar/Sunshine.Ds5Sidecar.exe"
cmake -E tar tf ./cpack_artifacts/Sunshine.zip | tr '\\' '/' | grep -F "Sunshine/assets/components/dualsense.json"
! cmake -E tar tf ./cpack_artifacts/Sunshine.zip | tr '\\' '/' | grep -F "Sunshine/tools/sunshine-ds5-sidecar/"

# move
mv ./cpack_artifacts/Sunshine.exe ../artifacts/sunshine-windows-installer.exe
mv ./cpack_artifacts/Sunshine.zip ../artifacts/sunshine-windows-portable.zip

- name: Generate Checksums
shell: pwsh
run: |
# Generate SHA256 checksums.
.\scripts\generate-checksums.ps1 -Path .\artifacts -Output "SHA256SUMS.txt"
sidecar_assets=(./ds5-component/Sunshine.Ds5Sidecar.*.win-x64.zip)
test "${#sidecar_assets[@]}" -eq 1
cp "${sidecar_assets[0]}" ../artifacts/

- name: Rename release assets
shell: msys2 {0}
Expand All @@ -310,25 +320,36 @@ jobs:
mv artifacts/sunshine-windows-installer.exe "artifacts/Sunshine.${NEWTAG}.WindowsInstaller.exe"
mv artifacts/sunshine-windows-portable.zip "artifacts/Sunshine.${NEWTAG}.WindowsPortable.zip"

- name: Generate Checksums
shell: pwsh
run: .\scripts\generate-checksums.ps1 -Path .\artifacts -Output "SHA256SUMS.txt"

- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: sunshine-windows-r${{ github.run_number }}
path: |
artifacts/Sunshine.*.WindowsInstaller.exe
artifacts/Sunshine.*.WindowsPortable.zip
artifacts/Sunshine.Ds5Sidecar.*.win-x64.zip
artifacts/SHA256SUMS.txt
artifacts/checksums.json
if-no-files-found: error

- name: Create/Update GitHub Release
if: needs.setup_release.outputs.publish_release == 'true'
uses: LizardByte/create-release-action@v2025.426.1549
uses: softprops/action-gh-release@v3
Comment thread
Yundi339 marked this conversation as resolved.
Outdated
with:
allowUpdates: true
body: ${{ needs.setup_release.outputs.release_body }}
generateReleaseNotes: ${{ needs.setup_release.outputs.release_generate_release_notes }}
generate_release_notes: ${{ needs.setup_release.outputs.release_generate_release_notes }}
name: ${{ needs.setup_release.outputs.release_tag }}.杂鱼
prerelease: true
tag: ${{ needs.setup_release.outputs.release_tag }}.杂鱼
token: ${{ secrets.GH_BOT_TOKEN }}
tag_name: ${{ needs.setup_release.outputs.release_tag }}.杂鱼
Comment thread
Yundi339 marked this conversation as resolved.
Outdated
files: |
artifacts/Sunshine.*.WindowsInstaller.exe
artifacts/Sunshine.*.WindowsPortable.zip
artifacts/Sunshine.Ds5Sidecar.*.win-x64.zip
artifacts/SHA256SUMS.txt
artifacts/checksums.json
env:
GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
65 changes: 56 additions & 9 deletions .github/workflows/sign-and-repackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,18 @@ jobs:

- name: Build self-contained DualSense sidecar
shell: pwsh
run: .\scripts\build-ds5-sidecar.ps1
env:
DS5_COMPONENT_VERSION: 1.0.0+${{ github.sha }}
run: .\scripts\build-ds5-sidecar.ps1 -ComponentVersion $env:DS5_COMPONENT_VERSION
Comment thread
Yundi339 marked this conversation as resolved.
Outdated

- name: Package unsigned DualSense component manifest
shell: pwsh
env:
DS5_RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.release-tag }}
run: |
.\scripts\package-ds5-sidecar.ps1 `
-RuntimeDirectory build/ds5-sidecar-runtime `
-ReleaseTag $env:DS5_RELEASE_TAG

- name: Build Windows
shell: msys2 {0}
Expand Down Expand Up @@ -208,6 +219,8 @@ jobs:
# 生成 Inno Setup staging 目录
echo "Generating Inno Setup staging directory..."
cmake --install . --prefix ./inno_staging
test -f ./inno_staging/assets/components/dualsense.json
test ! -e ./inno_staging/tools/sunshine-ds5-sidecar

cd ..

Expand All @@ -224,6 +237,8 @@ jobs:
echo "Extracting: $PORTABLE"
mkdir -p unsigned-files
7z x "$PORTABLE" -o"unsigned-files" -y -aoa
mkdir -p unsigned-files/Sunshine/tools/sunshine-ds5-sidecar
cp -a build/ds5-sidecar-runtime/. unsigned-files/Sunshine/tools/sunshine-ds5-sidecar/
echo "Extracted files for signing:"
ls -laR unsigned-files/
else
Expand Down Expand Up @@ -274,6 +289,22 @@ jobs:
Write-Host "`n✓ All signatures are VALID!" -ForegroundColor Green

# 使用签名后的文件重新打包 ZIP 便携版
# Sidecar executables are included in the SignPath input only to receive
# the same production signature. They are packaged separately afterwards.
- name: Package signed DualSense component release asset
shell: pwsh
env:
DS5_RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.release-tag }}
run: |
.\scripts\package-ds5-sidecar.ps1 `
-RuntimeDirectory signed-files/Sunshine/tools/sunshine-ds5-sidecar `
-ReleaseTag $env:DS5_RELEASE_TAG
Copy-Item -LiteralPath build/ds5-component/dualsense.json `
-Destination signed-files/Sunshine/assets/components/dualsense.json -Force
Copy-Item -LiteralPath build/ds5-component/dualsense.json `
-Destination build/inno_staging/assets/components/dualsense.json -Force
Remove-Item -LiteralPath signed-files/Sunshine/tools/sunshine-ds5-sidecar -Recurse -Force

- name: Repackage signed Portable ZIP
shell: bash
run: |
Expand Down Expand Up @@ -326,9 +357,10 @@ jobs:

# 运行 Inno Setup 编译器重新打包
echo "Running ISCC to repackage installer..."
test -f "$STAGING_DIR/tools/sunshine-ds5-sidecar/Sunshine.Ds5Sidecar.exe"
test -f "$STAGING_DIR/assets/components/dualsense.json"
test ! -e "$STAGING_DIR/tools/sunshine-ds5-sidecar"
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" "build/sunshine_installer.iss" | tee build/inno-signed-build.log
grep -F "Sunshine.Ds5Sidecar.exe" build/inno-signed-build.log
! grep -F "sunshine-ds5-sidecar" build/inno-signed-build.log
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

# 使用与 Portable ZIP 一致的版本号
if [ -n "${{ github.event.inputs.release-tag }}" ]; then
Expand Down Expand Up @@ -379,25 +411,40 @@ jobs:
}
}

# 上传最终的签名文件
# Upload the final signed files
- name: Generate signed package checksums
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path release-artifacts | Out-Null
Copy-Item -Path Sunshine-*-Portable-Signed.zip -Destination release-artifacts -Force
Copy-Item -Path final-signed/*.exe -Destination release-artifacts -Force
Copy-Item -Path build/ds5-component/Sunshine.Ds5Sidecar.*.win-x64.zip -Destination release-artifacts -Force
.\scripts\generate-checksums.ps1 -Path .\release-artifacts -Output "SHA256SUMS.txt"

- name: Upload final signed packages
uses: actions/upload-artifact@v4
with:
name: sunshine-windows-fully-signed
path: |
Sunshine-*-Portable-Signed.zip
final-signed/*.exe
release-artifacts/Sunshine-*-Portable-Signed.zip
release-artifacts/*.exe
release-artifacts/Sunshine.Ds5Sidecar.*.win-x64.zip
release-artifacts/SHA256SUMS.txt
release-artifacts/checksums.json
if-no-files-found: error

# 发布到 GitHub Release
- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.release-tag }}
name: ${{ github.event_name == 'release' && github.event.release.name || github.event.inputs.release-tag }} (Signed)
files: |
Sunshine-*-Portable-Signed.zip
final-signed/*.exe
release-artifacts/Sunshine-*-Portable-Signed.zip
release-artifacts/*.exe
release-artifacts/Sunshine.Ds5Sidecar.*.win-x64.zip
release-artifacts/SHA256SUMS.txt
release-artifacts/checksums.json
draft: true
prerelease: true
allowUpdates: true
Expand Down
4 changes: 0 additions & 4 deletions cmake/packaging/sunshine.iss.in
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,6 @@ Source: "{#MySourceDir}\tools\qiin-tabtip.exe"; DestDir: "{app}\tools"; Flags: i
Source: "{#MySourceDir}\tools\nefconw.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\stylus-input-probe.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: application

; First-party self-contained DualSense sidecar runtime. Keep this mandatory so
; packaging fails instead of producing a GUI that cannot install the component.
Source: "{#MySourceDir}\tools\sunshine-ds5-sidecar\*"; DestDir: "{app}\tools\sunshine-ds5-sidecar"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: application

; Portable scripts (will be deleted during install, but needed for layout)
Source: "{#MySourceDir}\install_portable.bat"; DestDir: "{app}"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\uninstall_portable.bat"; DestDir: "{app}"; Flags: ignoreversion; Components: application
Expand Down
22 changes: 12 additions & 10 deletions cmake/packaging/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT application)
install(TARGETS qiin-tabtip RUNTIME DESTINATION "tools" COMPONENT application)
install(TARGETS stylus-input-probe RUNTIME DESTINATION "tools" COMPONENT application)

# The optional DualSense runtime is first-party and self-contained. HIDMaestro
# itself is downloaded and digest-verified by the Control Panel when the user
# opts in, so it is intentionally absent from this directory.
set(DS5_SIDECAR_RUNTIME_DIR "${CMAKE_BINARY_DIR}/ds5-sidecar-runtime" CACHE PATH
"Path to the published self-contained Sunshine DualSense sidecar runtime")
if(EXISTS "${DS5_SIDECAR_RUNTIME_DIR}/Sunshine.Ds5Sidecar.exe")
install(DIRECTORY "${DS5_SIDECAR_RUNTIME_DIR}/"
DESTINATION "tools/sunshine-ds5-sidecar"
COMPONENT application)
# The optional DualSense runtime is released separately. The main package only
# contains the manifest that binds the Control Panel to this release's asset.
set(DS5_COMPONENT_MANIFEST_FILE "${CMAKE_BINARY_DIR}/ds5-component/dualsense.json" CACHE FILEPATH
"Path to the generated Sunshine DualSense component manifest")
if(EXISTS "${DS5_COMPONENT_MANIFEST_FILE}")
install(FILES "${DS5_COMPONENT_MANIFEST_FILE}"
DESTINATION "${SUNSHINE_ASSETS_DIR}/components"
COMPONENT assets)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/assets/components")
file(COPY "${DS5_COMPONENT_MANIFEST_FILE}"
DESTINATION "${CMAKE_BINARY_DIR}/assets/components")
else()
message(STATUS "Self-contained DualSense sidecar was not published; optional component install will be unavailable")
message(STATUS "DualSense component manifest was not generated; optional component install will be unavailable")
endif()

# Shared tool: nefconw.exe (used by VDD and vmouse install scripts)
Expand Down
4 changes: 2 additions & 2 deletions docs/windows_dualsense_component_lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ UX 验收:

1. Sidecar 调用 HIDMaestro v1.6.1 的公共 `HIDMaestro.Core.dll` API,不复制其实现,也不调用 HIDMaestroTest UI。
2. HIDMaestro 使用官方发布物 `HIDMaestro-v1.6.1.zip`,固定下载地址和 SHA-256 `00145c23d9838be6089389ce58b3fd2b6766fa9bc0f1f3c60a3c885361b53c34`。发布物大小为 118,879,222 bytes。
3. Sunshine 安装包仅携带自研 Sidecar 的 `win-x64` 自包含 .NET 运行时,不携带 HIDMaestro DLL。GUI 在用户明确选择安装时下载第三方发布物,校验后只提取 Core、许可证、README 和第三方通知。
3. Sunshine 安装包仅携带与本地版本绑定的 `assets/components/dualsense.json`,不再携带自研 Sidecar 运行时或 HIDMaestro DLL。用户明确选择安装后,GUI 只接受 manifest 固定的同版本 Sunshine Release Sidecar 资产和固定的 HIDMaestro 发布物;下载、解压和逐文件校验完成后才会激活组件。
4. 首期每个 Sunshine 进程只允许一个虚拟 DualSense。Xbox 360、DualShock 4 和既有自动模式继续走 ViGEm,不改变成熟驱动支持范围。
5. 客户端只选择 `physical` 或 `emulated`。前者预检 USB DualSense 四声道端点后声明 `ML_FF_DS5_HAPTICS_PCM`;后者声明 `ML_FF_DS5_HAPTICS_IR_V2`。两位互斥且不在运行中自动切换。
6. `0x550A` v1 固定承载 48 kHz、双声道、S16LE 原始 PCM;`0x550B` v2 固定承载 72-byte 双 lane IR。两者均按 5 ms 节拍使用不可靠有序传输,断序/`DISCONTINUITY` 重置客户端状态。
Expand All @@ -662,7 +662,7 @@ UX 验收:
- Sidecar `win-x64` 自包含发布通过,产物约 107 MB 且确认不包含 `HIDMaestro.Core.dll`;把官方校验 DLL 放入 staging 后,probe 返回协议 1、standard/composite profile、驱动及 USB/IP 均可用。
- Control Panel 已同步 Sunshine master 使用的 VDD/HDR 基线;3 项 DualSense Rust 单测、Vue production build 和 12 项 renderer 测试通过。完整配置读取失败会中止保存,USB/IP 不可用时后端拒绝 composite profile,页面仍允许用户切回 HID-only。
- Core 的 DS5 命名管道改用 overlapped I/O;独立 fake-sidecar 回归测试覆盖 `alloc -> reader blocked -> free`,本机在 93 ms 内完成,避免同步 `ReadFile` 与 owner EOF 相互等待。
- Windows `application` 组件的隔离安装烟测通过:自包含 Sidecar 被安装到 `tools/sunshine-ds5-sidecar`,且安装目录中不含 `HIDMaestro.Core.dll`,保持由 GUI 下载并校验第三方运行时的边界。
- Windows 主包的隔离安装烟测必须确认:Installer 与 Portable 仅含 `assets/components/dualsense.json`,不含 `tools/sunshine-ds5-sidecar`。Sidecar ZIP 与主包一起作为同一 Release 资产发布;签名流水线在 Sidecar 文件签名后重新生成 ZIP 和 manifest,确保 GUI 校验的是最终字节。
- `moonlight-audio-haptics` authored/ABI 测试、common-c IR v2 golden parser 和 Moonlight IR-to-rumble renderer 测试通过。
- Sunshine 合成 PCM -> SDK 双 lane IR -> 72-byte 小端序列化 -> common-c 解析的跨仓库测试通过;空流结束会产生静音 `STREAM_END`。

Expand Down
10 changes: 8 additions & 2 deletions scripts/build-ds5-sidecar.ps1
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
param(
[string]$OutputDirectory = "build/ds5-sidecar-runtime",
[string]$PackageCache = "build/ds5-sidecar-cache"
[string]$PackageCache = "build/ds5-sidecar-cache",
[string]$ComponentVersion = "1.0.0-dev"
)

$ErrorActionPreference = 'Stop'
$version = 'v1.6.1'
$expectedSha256 = '00145c23D9838BE6089389CE58B3FD2B6766FA9BC0F1F3C60A3C885361B53C34'
$url = "https://github.com/hifihedgehog/HIDMaestro/releases/download/$version/HIDMaestro-$version.zip"
$root = Split-Path -Parent $PSScriptRoot
$ComponentVersion = $ComponentVersion.Trim()
if ([string]::IsNullOrWhiteSpace($ComponentVersion) -or
$ComponentVersion.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -ge 0) {
throw 'ComponentVersion must be a non-empty file-name-safe value'
}
$buildRoot = [System.IO.Path]::GetFullPath((Join-Path $root 'build'))
$cache = [System.IO.Path]::GetFullPath((Join-Path $root $PackageCache))
$output = [System.IO.Path]::GetFullPath((Join-Path $root $OutputDirectory))
Expand Down Expand Up @@ -61,7 +67,7 @@ if ($LASTEXITCODE -ne 0) {
# package. Excluding it here keeps the Sunshine installer first-party-only.
Remove-Item -LiteralPath (Join-Path $output 'HIDMaestro.Core.dll') -Force
@{
component_version = '1.0.0-build-runtime'
component_version = $ComponentVersion
protocol = 1
target = 'win-x64-self-contained'
hidmaestro_build_version = $version
Expand Down
Loading
Loading