|
19 | 19 | include: |
20 | 20 | - os: ubuntu-22.04 |
21 | 21 | label: linux-x86_64 |
22 | | - - os: macos-13 |
23 | | - label: macos-x86_64 |
| 22 | + # macos-13 (Intel) dropped — GitHub runner queue is unreliable. |
24 | 23 | - os: macos-14 |
25 | 24 | label: macos-arm64 |
26 | 25 | - os: windows-2022 |
@@ -128,104 +127,256 @@ jobs: |
128 | 127 | run: cmake --build build --config Release --parallel |
129 | 128 |
|
130 | 129 | # ----------------------------------------------------------------- |
131 | | - # Package: stage the binary plus its RealSense / OpenCV runtime |
132 | | - # libraries into dist/, then tar/zip it. |
| 130 | + # Package: produce a platform-native installer. |
| 131 | + # Linux -> .deb (dpkg-deb, depends on system libgtk-3/libgl/libusb) |
| 132 | + # macOS -> .dmg containing a self-contained .app bundle |
| 133 | + # Windows -> NSIS .exe installer (Start menu shortcut, Add/Remove entry) |
133 | 134 | # ----------------------------------------------------------------- |
134 | | - - name: Package (Linux) |
| 135 | + - name: Derive version from tag |
| 136 | + id: ver |
| 137 | + shell: bash |
| 138 | + run: | |
| 139 | + # ref_name on a tag push is "v1.2.3" — strip leading 'v' to get "1.2.3". |
| 140 | + VERSION="${GITHUB_REF_NAME#v}" |
| 141 | + # Releases triggered via workflow_dispatch / branch push won't have a v-tag; |
| 142 | + # use a 0.0.0+sha fallback so the packaging step still works. |
| 143 | + if [[ "$VERSION" == "$GITHUB_REF_NAME" && ! "$GITHUB_REF_NAME" =~ ^[0-9] ]]; then |
| 144 | + VERSION="0.0.0-${GITHUB_SHA::7}" |
| 145 | + fi |
| 146 | + echo "version=$VERSION" >> "$GITHUB_OUTPUT" |
| 147 | + echo "Resolved version: $VERSION" |
| 148 | +
|
| 149 | + - name: Package (Linux .deb) |
135 | 150 | if: runner.os == 'Linux' |
| 151 | + env: |
| 152 | + VERSION: ${{ steps.ver.outputs.version }} |
136 | 153 | run: | |
137 | 154 | set -euxo pipefail |
138 | | - STAGE="dist/ir-tracking-app-${{ matrix.label }}" |
139 | | - mkdir -p "$STAGE/lib" |
140 | | - cp build/ir-tracking-app "$STAGE/" |
141 | | -
|
142 | | - # Bundle every linked librealsense / libopencv .so. ldd reports a path |
143 | | - # like "libfoo.so => /usr/lib/x86_64-linux-gnu/libfoo.so.1 (0x...)" |
| 155 | + PKG="ir-tracking-app" |
| 156 | + STAGE="$RUNNER_TEMP/deb-staging" |
| 157 | + rm -rf "$STAGE" |
| 158 | + mkdir -p "$STAGE/DEBIAN" "$STAGE/usr/bin" "$STAGE/usr/lib/$PKG" \ |
| 159 | + "$STAGE/usr/share/applications" "$STAGE/usr/share/icons/hicolor/256x256/apps" |
| 160 | +
|
| 161 | + # Binary + bundled libs under /usr/lib/<pkg>/ with $ORIGIN rpath. |
| 162 | + cp build/ir-tracking-app "$STAGE/usr/lib/$PKG/" |
144 | 163 | ldd build/ir-tracking-app \ |
145 | 164 | | awk '/(librealsense|libopencv|libtbb|libusb-1\.0)/ { print $3 }' \ |
146 | | - | grep -v '^$' \ |
147 | | - | sort -u \ |
148 | | - | while read -r lib; do |
149 | | - cp -L "$lib" "$STAGE/lib/" |
150 | | - done |
| 165 | + | grep -v '^$' | sort -u \ |
| 166 | + | while read -r lib; do cp -L "$lib" "$STAGE/usr/lib/$PKG/"; done |
| 167 | + patchelf --set-rpath '$ORIGIN' "$STAGE/usr/lib/$PKG/ir-tracking-app" |
151 | 168 |
|
152 | | - patchelf --set-rpath '$ORIGIN/lib' "$STAGE/ir-tracking-app" |
153 | | -
|
154 | | - # Tiny launcher so users can double-click without worrying about |
155 | | - # LD_LIBRARY_PATH or working directory. |
156 | | - cat > "$STAGE/run.sh" <<'SH' |
| 169 | + # Launcher shim on PATH. |
| 170 | + cat > "$STAGE/usr/bin/$PKG" <<SH |
157 | 171 | #!/usr/bin/env bash |
158 | | - cd "$(dirname "$0")" |
159 | | - exec ./ir-tracking-app "$@" |
| 172 | + exec /usr/lib/$PKG/ir-tracking-app "\$@" |
160 | 173 | SH |
161 | | - chmod +x "$STAGE/run.sh" |
162 | | -
|
163 | | - tar -czf "ir-tracking-app-${{ matrix.label }}.tar.gz" -C dist "ir-tracking-app-${{ matrix.label }}" |
164 | | - ls -lh "ir-tracking-app-${{ matrix.label }}.tar.gz" |
165 | | -
|
166 | | - - name: Package (macOS) |
| 174 | + chmod 0755 "$STAGE/usr/bin/$PKG" |
| 175 | +
|
| 176 | + # Desktop entry so the app shows in app launchers. |
| 177 | + cat > "$STAGE/usr/share/applications/$PKG.desktop" <<EOF |
| 178 | + [Desktop Entry] |
| 179 | + Type=Application |
| 180 | + Name=IR Tracking App |
| 181 | + Comment=Intel RealSense IR retro-reflective marker tracking |
| 182 | + Exec=$PKG |
| 183 | + Icon=$PKG |
| 184 | + Categories=Science;Graphics; |
| 185 | + Terminal=false |
| 186 | + EOF |
| 187 | +
|
| 188 | + # dpkg control metadata. Depends covers the runtime system libs we |
| 189 | + # rely on (GTK for nfd, OpenGL, libusb for RealSense USB). |
| 190 | + INSTALLED_KB=$(du -sk "$STAGE" --exclude=DEBIAN | cut -f1) |
| 191 | + cat > "$STAGE/DEBIAN/control" <<EOF |
| 192 | + Package: $PKG |
| 193 | + Version: $VERSION |
| 194 | + Section: graphics |
| 195 | + Priority: optional |
| 196 | + Architecture: amd64 |
| 197 | + Maintainer: Tianyu Song <tianyu@medivis.com> |
| 198 | + Homepage: https://github.com/stytim/RealSense-ToolTracker |
| 199 | + Depends: libgtk-3-0, libgl1, libusb-1.0-0 |
| 200 | + Installed-Size: $INSTALLED_KB |
| 201 | + Description: Intel RealSense IR retro-reflective marker tool tracker |
| 202 | + Tracks passive IR sphere markers from an Intel RealSense camera and |
| 203 | + publishes the tool pose over UDP. See the GitHub README for usage. |
| 204 | + EOF |
| 205 | +
|
| 206 | + OUT="${PKG}_${VERSION}_amd64.deb" |
| 207 | + dpkg-deb --build --root-owner-group "$STAGE" "$OUT" |
| 208 | + ls -lh "$OUT" |
| 209 | + dpkg-deb -I "$OUT" |
| 210 | +
|
| 211 | + - name: Package (macOS .dmg) |
167 | 212 | if: runner.os == 'macOS' |
| 213 | + env: |
| 214 | + VERSION: ${{ steps.ver.outputs.version }} |
168 | 215 | run: | |
169 | 216 | set -euxo pipefail |
170 | | - STAGE="dist/ir-tracking-app-${{ matrix.label }}" |
171 | | - mkdir -p "$STAGE" |
172 | | - cp build/ir-tracking-app "$STAGE/" |
173 | | -
|
174 | | - # dylibbundler walks the binary's load commands recursively, copies |
175 | | - # every non-system dylib into dist/libs and rewrites install names to |
176 | | - # @executable_path/libs/. Avoids the per-dylib otool dance. |
| 217 | + APP_NAME="IR Tracking App" |
| 218 | + APP_DIR="$RUNNER_TEMP/$APP_NAME.app" |
| 219 | + rm -rf "$APP_DIR" |
| 220 | + mkdir -p "$APP_DIR/Contents/MacOS" \ |
| 221 | + "$APP_DIR/Contents/Resources" \ |
| 222 | + "$APP_DIR/Contents/Frameworks" |
| 223 | +
|
| 224 | + cp build/ir-tracking-app "$APP_DIR/Contents/MacOS/" |
| 225 | + cp resources/app_icon.icns "$APP_DIR/Contents/Resources/app_icon.icns" |
| 226 | +
|
| 227 | + # Bundle dylibs into Contents/Frameworks/, with install names rewritten |
| 228 | + # to @executable_path/../Frameworks/ so the .app is self-contained. |
177 | 229 | dylibbundler -od -b \ |
178 | | - -x "$STAGE/ir-tracking-app" \ |
179 | | - -d "$STAGE/libs" \ |
180 | | - -p "@executable_path/libs/" \ |
| 230 | + -x "$APP_DIR/Contents/MacOS/ir-tracking-app" \ |
| 231 | + -d "$APP_DIR/Contents/Frameworks" \ |
| 232 | + -p "@executable_path/../Frameworks/" \ |
181 | 233 | || true |
182 | 234 |
|
183 | | - # Cosmetic launcher mirrors the Linux one. |
184 | | - cat > "$STAGE/run.sh" <<'SH' |
185 | | - #!/usr/bin/env bash |
186 | | - cd "$(dirname "$0")" |
187 | | - exec ./ir-tracking-app "$@" |
188 | | - SH |
189 | | - chmod +x "$STAGE/run.sh" |
190 | | -
|
191 | | - tar -czf "ir-tracking-app-${{ matrix.label }}.tar.gz" -C dist "ir-tracking-app-${{ matrix.label }}" |
192 | | - ls -lh "ir-tracking-app-${{ matrix.label }}.tar.gz" |
193 | | -
|
194 | | - - name: Package (Windows) |
| 235 | + cat > "$APP_DIR/Contents/Info.plist" <<EOF |
| 236 | + <?xml version="1.0" encoding="UTF-8"?> |
| 237 | + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 238 | + <plist version="1.0"> |
| 239 | + <dict> |
| 240 | + <key>CFBundleExecutable</key><string>ir-tracking-app</string> |
| 241 | + <key>CFBundleIdentifier</key><string>com.medivis.ir-tracking-app</string> |
| 242 | + <key>CFBundleName</key><string>$APP_NAME</string> |
| 243 | + <key>CFBundleDisplayName</key><string>$APP_NAME</string> |
| 244 | + <key>CFBundleVersion</key><string>$VERSION</string> |
| 245 | + <key>CFBundleShortVersionString</key><string>$VERSION</string> |
| 246 | + <key>CFBundlePackageType</key><string>APPL</string> |
| 247 | + <key>CFBundleIconFile</key><string>app_icon.icns</string> |
| 248 | + <key>LSMinimumSystemVersion</key><string>11.0</string> |
| 249 | + <key>NSHighResolutionCapable</key><true/> |
| 250 | + <key>NSCameraUsageDescription</key><string>Used to display the IR camera feed for tool tracking.</string> |
| 251 | + </dict> |
| 252 | + </plist> |
| 253 | + EOF |
| 254 | +
|
| 255 | + # Compose the DMG. UDZO = zlib-compressed read-only. |
| 256 | + DMG_NAME="ir-tracking-app-${VERSION}-${{ matrix.label }}.dmg" |
| 257 | + DMG_STAGING="$RUNNER_TEMP/dmg-staging" |
| 258 | + rm -rf "$DMG_STAGING" |
| 259 | + mkdir -p "$DMG_STAGING" |
| 260 | + cp -R "$APP_DIR" "$DMG_STAGING/" |
| 261 | + # Drag-to-/Applications affordance — a symlink the user can drop on. |
| 262 | + ln -s /Applications "$DMG_STAGING/Applications" |
| 263 | +
|
| 264 | + hdiutil create \ |
| 265 | + -volname "$APP_NAME $VERSION" \ |
| 266 | + -srcfolder "$DMG_STAGING" \ |
| 267 | + -ov \ |
| 268 | + -format UDZO \ |
| 269 | + "$DMG_NAME" |
| 270 | + ls -lh "$DMG_NAME" |
| 271 | +
|
| 272 | + - name: Package (Windows .exe installer) |
195 | 273 | if: runner.os == 'Windows' |
196 | 274 | shell: pwsh |
| 275 | + env: |
| 276 | + VERSION: ${{ steps.ver.outputs.version }} |
197 | 277 | run: | |
198 | 278 | $ErrorActionPreference = 'Stop' |
199 | | - $stage = "dist\ir-tracking-app-${{ matrix.label }}" |
| 279 | +
|
| 280 | + # Stage every file that needs to end up in C:\Program Files\<App>\ |
| 281 | + $stage = "$env:RUNNER_TEMP\nsis-payload" |
| 282 | + if (Test-Path $stage) { Remove-Item -Recurse -Force $stage } |
200 | 283 | New-Item -ItemType Directory -Force -Path $stage | Out-Null |
201 | 284 |
|
202 | | - # The CMake build output landed under build\Release\. |
203 | 285 | $exe = "build\Release\ir-tracking-app.exe" |
204 | | - if (-not (Test-Path $exe)) { $exe = "build\ir-tracking-app.exe" } # if generator differs |
| 286 | + if (-not (Test-Path $exe)) { $exe = "build\ir-tracking-app.exe" } |
205 | 287 | Copy-Item $exe -Destination $stage |
206 | 288 |
|
207 | | - # RealSense runtime DLL. |
208 | 289 | Copy-Item "C:\Program Files (x86)\Intel RealSense SDK 2.0\bin\x64\realsense2.dll" -Destination $stage |
209 | | -
|
210 | | - # OpenCV runtime DLL(s) — chocolatey 4.10 ships the 'world' variant. |
211 | 290 | $opencvBin = Join-Path $env:OpenCV_DIR "x64\vc16\bin" |
212 | 291 | Get-ChildItem -Path $opencvBin -Filter "opencv_world*.dll" ` |
213 | 292 | | Where-Object { $_.Name -notmatch "d\.dll$" } ` |
214 | 293 | | Copy-Item -Destination $stage |
215 | 294 |
|
216 | | - Compress-Archive -Path "$stage\*" -DestinationPath "ir-tracking-app-${{ matrix.label }}.zip" -Force |
217 | | - Get-Item "ir-tracking-app-${{ matrix.label }}.zip" | Format-List Length, Name |
| 295 | + # NSIS comes preinstalled on windows-2022 runners under C:\Program Files (x86)\NSIS. |
| 296 | + $makensis = "C:\Program Files (x86)\NSIS\makensis.exe" |
| 297 | + if (-not (Test-Path $makensis)) { |
| 298 | + choco install nsis -y --no-progress |
| 299 | + } |
| 300 | + if (-not (Test-Path $makensis)) { |
| 301 | + throw "NSIS (makensis.exe) not found after install attempt." |
| 302 | + } |
| 303 | +
|
| 304 | + $version = $env:VERSION |
| 305 | + $outFile = "ir-tracking-app-$version-${{ matrix.label }}-setup.exe" |
| 306 | + $nsiPath = "$env:RUNNER_TEMP\installer.nsi" |
| 307 | + $stageNsi = $stage.Replace('\','\\') |
| 308 | +
|
| 309 | + # NSIS script — install to Program Files\IR Tracking App, Start Menu |
| 310 | + # shortcut + uninstaller + Add/Remove Programs entry. |
| 311 | + $nsi = @" |
| 312 | + !define APPNAME "IR Tracking App" |
| 313 | + !define APPID "ir-tracking-app" |
| 314 | + !define VERSION "$version" |
| 315 | + !define COMPANY "Medivis" |
| 316 | + !define DESCRIPTION "Intel RealSense IR retro-reflective marker tool tracker" |
| 317 | + !define REGKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPID}" |
| 318 | +
|
| 319 | + Name "`${APPNAME} `${VERSION}" |
| 320 | + OutFile "$outFile" |
| 321 | + Unicode true |
| 322 | + InstallDir "`$PROGRAMFILES64\`${APPNAME}" |
| 323 | + InstallDirRegKey HKLM "Software\`${APPNAME}" "InstallDir" |
| 324 | + RequestExecutionLevel admin |
| 325 | + ShowInstDetails show |
| 326 | + ShowUninstDetails show |
| 327 | +
|
| 328 | + Page directory |
| 329 | + Page instfiles |
| 330 | + UninstPage uninstConfirm |
| 331 | + UninstPage instfiles |
| 332 | +
|
| 333 | + Section "Install" |
| 334 | + SetOutPath "`$INSTDIR" |
| 335 | + File /r "$stageNsi\\*" |
| 336 | +
|
| 337 | + CreateDirectory "`$SMPROGRAMS\`${APPNAME}" |
| 338 | + CreateShortcut "`$SMPROGRAMS\`${APPNAME}\`${APPNAME}.lnk" "`$INSTDIR\ir-tracking-app.exe" |
| 339 | + CreateShortcut "`$SMPROGRAMS\`${APPNAME}\Uninstall.lnk" "`$INSTDIR\Uninstall.exe" |
| 340 | +
|
| 341 | + WriteUninstaller "`$INSTDIR\Uninstall.exe" |
| 342 | +
|
| 343 | + WriteRegStr HKLM "Software\`${APPNAME}" "InstallDir" "`$INSTDIR" |
| 344 | + WriteRegStr HKLM "`${REGKEY}" "DisplayName" "`${APPNAME}" |
| 345 | + WriteRegStr HKLM "`${REGKEY}" "DisplayVersion" "`${VERSION}" |
| 346 | + WriteRegStr HKLM "`${REGKEY}" "Publisher" "`${COMPANY}" |
| 347 | + WriteRegStr HKLM "`${REGKEY}" "DisplayIcon" '"`$INSTDIR\ir-tracking-app.exe"' |
| 348 | + WriteRegStr HKLM "`${REGKEY}" "UninstallString" '"`$INSTDIR\Uninstall.exe"' |
| 349 | + WriteRegDWORD HKLM "`${REGKEY}" "NoModify" 1 |
| 350 | + WriteRegDWORD HKLM "`${REGKEY}" "NoRepair" 1 |
| 351 | + SectionEnd |
| 352 | +
|
| 353 | + Section "Uninstall" |
| 354 | + Delete "`$INSTDIR\Uninstall.exe" |
| 355 | + RMDir /r "`$INSTDIR" |
| 356 | + Delete "`$SMPROGRAMS\`${APPNAME}\`${APPNAME}.lnk" |
| 357 | + Delete "`$SMPROGRAMS\`${APPNAME}\Uninstall.lnk" |
| 358 | + RMDir "`$SMPROGRAMS\`${APPNAME}" |
| 359 | + DeleteRegKey HKLM "`${REGKEY}" |
| 360 | + DeleteRegKey HKLM "Software\`${APPNAME}" |
| 361 | + SectionEnd |
| 362 | + "@ |
| 363 | +
|
| 364 | + Set-Content -Path $nsiPath -Value $nsi -Encoding UTF8 |
| 365 | + & $makensis $nsiPath |
| 366 | + if ($LASTEXITCODE -ne 0) { throw "makensis failed (exit $LASTEXITCODE)." } |
| 367 | + Get-Item $outFile | Format-List Length, Name |
218 | 368 |
|
219 | 369 | # ----------------------------------------------------------------- |
220 | | - # Hand the archive off to the release job. |
| 370 | + # Upload the installer for the release job to collect. |
221 | 371 | # ----------------------------------------------------------------- |
222 | 372 | - name: Upload artifact |
223 | 373 | uses: actions/upload-artifact@v4 |
224 | 374 | with: |
225 | 375 | name: ir-tracking-app-${{ matrix.label }} |
226 | 376 | path: | |
227 | | - ir-tracking-app-${{ matrix.label }}.tar.gz |
228 | | - ir-tracking-app-${{ matrix.label }}.zip |
| 377 | + *.deb |
| 378 | + *.dmg |
| 379 | + *-setup.exe |
229 | 380 | if-no-files-found: error |
230 | 381 | retention-days: 7 |
231 | 382 |
|
|
0 commit comments