Skip to content

Commit e6bbe21

Browse files
committed
fix(ci): use GitHub API + curl for private repo release asset downloads
CMake file(DOWNLOAD) doesn't forward Authorization headers on HTTP 302 redirects, which breaks private repo release asset downloads (GitHub redirects to S3). Changed vmouse download to use GitHub REST API with curl to properly authenticate. - Rewrite _fetch_vmouse() to query release API and download via asset endpoint with Accept: application/octet-stream - Use curl (with -L) for all authenticated downloads - Add curl to MSYS2 install list for CI reliability
1 parent 0e1db0a commit e6bbe21

File tree

2 files changed

+119
-20
lines changed

2 files changed

+119
-20
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ jobs:
107107
update: true
108108
install: >-
109109
wget
110+
curl
110111
111112
- name: Update Windows dependencies
112113
env:

cmake/packaging/FetchDriverDeps.cmake

Lines changed: 118 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ endif()
5151

5252
# ---------------------------------------------------------------------------
5353
# Helper: download a single file (skip if already cached)
54+
# Uses curl for authenticated requests to handle GitHub's 302 redirects
55+
# properly (CMake file(DOWNLOAD) doesn't forward auth headers on redirect).
5456
# ---------------------------------------------------------------------------
5557
function(_driver_download url output_path)
5658
if(EXISTS "${output_path}")
@@ -63,41 +65,137 @@ function(_driver_download url output_path)
6365
message(STATUS " Downloading: ${url}")
6466

6567
if(GITHUB_TOKEN)
66-
file(DOWNLOAD "${url}" "${output_path}"
67-
HTTPHEADER "Authorization: token ${GITHUB_TOKEN}"
68-
STATUS _status
69-
TLS_VERIFY ON)
68+
# Use curl to handle GitHub's 302 redirects for private repo assets.
69+
# CMake's file(DOWNLOAD) won't send auth headers after redirect to S3.
70+
find_program(_CURL curl REQUIRED)
71+
execute_process(
72+
COMMAND "${_CURL}" -fsSL
73+
-H "Authorization: token ${GITHUB_TOKEN}"
74+
-H "Accept: application/octet-stream"
75+
-o "${output_path}"
76+
"${url}"
77+
RESULT_VARIABLE _code
78+
ERROR_VARIABLE _err)
79+
if(NOT _code EQUAL 0)
80+
message(WARNING " curl download failed (${_code}): ${_err}")
81+
file(REMOVE "${output_path}")
82+
return()
83+
endif()
7084
else()
7185
file(DOWNLOAD "${url}" "${output_path}"
7286
STATUS _status
7387
TLS_VERIFY ON)
88+
list(GET _status 0 _code)
89+
if(NOT _code EQUAL 0)
90+
list(GET _status 1 _msg)
91+
message(WARNING " Download failed (${_code}): ${_msg}")
92+
file(REMOVE "${output_path}")
93+
return()
94+
endif()
7495
endif()
7596

76-
list(GET _status 0 _code)
77-
if(NOT _code EQUAL 0)
78-
list(GET _status 1 _msg)
79-
message(WARNING " Download failed (${_code}): ${_msg}")
80-
file(REMOVE "${output_path}")
81-
return()
82-
endif()
83-
84-
file(SIZE "${output_path}" _size)
85-
if(_size EQUAL 0)
86-
message(WARNING " Downloaded file is empty: ${output_path}")
87-
file(REMOVE "${output_path}")
97+
if(EXISTS "${output_path}")
98+
file(SIZE "${output_path}" _size)
99+
if(_size EQUAL 0)
100+
message(WARNING " Downloaded file is empty: ${output_path}")
101+
file(REMOVE "${output_path}")
102+
endif()
88103
endif()
89104
endfunction()
90105

91106
# ---------------------------------------------------------------------------
92-
# ZakoVirtualMouse (individual release assets)
107+
# ZakoVirtualMouse (private repo — use GitHub API for authenticated downloads)
108+
# For private repos, browser_download_url returns 302→S3 which rejects
109+
# forwarded auth headers. We must use the GitHub REST API asset endpoint
110+
# with Accept: application/octet-stream.
93111
# ---------------------------------------------------------------------------
94112
function(_fetch_vmouse)
95113
message(STATUS "Fetching ZakoVirtualMouse ${VMOUSE_DRIVER_VERSION} ...")
96-
set(_base "https://github.com/${_VMOUSE_REPO}/releases/download/${VMOUSE_DRIVER_VERSION}")
97114

98-
foreach(_f ZakoVirtualMouse.dll ZakoVirtualMouse.inf ZakoVirtualMouse.cat)
99-
_driver_download("${_base}/${_f}" "${VMOUSE_DRIVER_DIR}/${_f}")
115+
set(_files ZakoVirtualMouse.dll ZakoVirtualMouse.inf ZakoVirtualMouse.cat)
116+
117+
# Check if all files already cached
118+
set(_all_cached TRUE)
119+
foreach(_f ${_files})
120+
if(NOT EXISTS "${VMOUSE_DRIVER_DIR}/${_f}")
121+
set(_all_cached FALSE)
122+
break()
123+
endif()
100124
endforeach()
125+
if(_all_cached)
126+
message(STATUS " All vmouse files already cached")
127+
return()
128+
endif()
129+
130+
if(NOT GITHUB_TOKEN)
131+
message(WARNING " GITHUB_TOKEN required for private repo ${_VMOUSE_REPO}")
132+
return()
133+
endif()
134+
135+
find_program(_CURL curl REQUIRED)
136+
file(MAKE_DIRECTORY "${VMOUSE_DRIVER_DIR}")
137+
138+
# Query release assets via GitHub API
139+
set(_api_url "https://api.github.com/repos/${_VMOUSE_REPO}/releases/tags/${VMOUSE_DRIVER_VERSION}")
140+
set(_json "${DRIVER_DEPS_CACHE}/_vmouse_release.json")
141+
execute_process(
142+
COMMAND "${_CURL}" -fsSL
143+
-H "Authorization: token ${GITHUB_TOKEN}"
144+
-H "Accept: application/vnd.github+json"
145+
-o "${_json}"
146+
"${_api_url}"
147+
RESULT_VARIABLE _rc
148+
ERROR_VARIABLE _err)
149+
if(NOT _rc EQUAL 0)
150+
message(WARNING " Failed to query vmouse release API (${_rc}): ${_err}")
151+
return()
152+
endif()
153+
154+
# For each required file, find its asset id and download via API
155+
foreach(_f ${_files})
156+
if(EXISTS "${VMOUSE_DRIVER_DIR}/${_f}")
157+
continue()
158+
endif()
159+
160+
# Extract asset download URL from JSON using regex
161+
# The API JSON contains entries like:
162+
# "name": "ZakoVirtualMouse.dll", ... "url": "https://api.github.com/repos/.../assets/12345"
163+
file(READ "${_json}" _json_content)
164+
165+
# Find block for this asset: locate "name": "<filename>" then extract nearest "url"
166+
# We use string(REGEX) to find the asset API url
167+
string(REGEX MATCH "\"url\"[^}]*\"name\":[ ]*\"${_f}\"" _match_after "${_json_content}")
168+
string(REGEX MATCH "\"name\":[ ]*\"${_f}\"[^}]*\"url\"" _match_before "${_json_content}")
169+
170+
set(_asset_api_url "")
171+
# Try to extract the url from the assets array
172+
# GitHub API returns assets like: { "url": "https://api.github.com/repos/.../assets/ID", ... "name": "file" }
173+
string(REGEX MATCH "\"url\":[ ]*\"(https://api\\.github\\.com/repos/[^\"]+/assets/[0-9]+)\"[^}]*\"name\":[ ]*\"${_f}\"" _m "${_json_content}")
174+
if(_m)
175+
set(_asset_api_url "${CMAKE_MATCH_1}")
176+
endif()
177+
178+
if(NOT _asset_api_url)
179+
message(WARNING " Could not find asset URL for ${_f} in release JSON")
180+
continue()
181+
endif()
182+
183+
message(STATUS " Downloading ${_f} via API: ${_asset_api_url}")
184+
execute_process(
185+
COMMAND "${_CURL}" -fsSL
186+
-H "Authorization: token ${GITHUB_TOKEN}"
187+
-H "Accept: application/octet-stream"
188+
-o "${VMOUSE_DRIVER_DIR}/${_f}"
189+
"${_asset_api_url}"
190+
RESULT_VARIABLE _rc
191+
ERROR_VARIABLE _err)
192+
if(NOT _rc EQUAL 0)
193+
message(WARNING " Download failed for ${_f} (${_rc}): ${_err}")
194+
file(REMOVE "${VMOUSE_DRIVER_DIR}/${_f}")
195+
endif()
196+
endforeach()
197+
198+
file(REMOVE "${_json}")
101199
endfunction()
102200

103201
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)