-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: extract CasparCG framerate parsing to helpers with tests #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Clip_Duration_Fix
Are you sure you want to change the base?
Changes from all commits
986481e
28c30ca
ee8ef7e
49a112c
36beedb
a89c492
d4aa421
9b76e96
ab1c917
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,6 +35,12 @@ jobs: | |||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||
| corepack enable | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # set isolated electron-builder cache early so downloads use it | ||||||||||||||||||||||||||||||||||||
| echo "XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }}" | ||||||||||||||||||||||||||||||||||||
| export XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }} | ||||||||||||||||||||||||||||||||||||
| rm -rf "$XDG_CACHE_HOME/electron-builder" || true | ||||||||||||||||||||||||||||||||||||
| mkdir -p "$XDG_CACHE_HOME" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # try and avoid timeout errors | ||||||||||||||||||||||||||||||||||||
| yarn config set httpTimeout 100000 | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
@@ -51,6 +57,8 @@ jobs: | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| macos-build: | ||||||||||||||||||||||||||||||||||||
| name: Build on macOS | ||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||||||||
| runs-on: macos-latest | ||||||||||||||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||
|
|
@@ -69,28 +77,72 @@ jobs: | |||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||
| corepack enable | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # set isolated electron-builder cache early so downloads use it | ||||||||||||||||||||||||||||||||||||
| echo "XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }}" >> $GITHUB_ENV | ||||||||||||||||||||||||||||||||||||
| export XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }} | ||||||||||||||||||||||||||||||||||||
| rm -rf "$XDG_CACHE_HOME/electron-builder" || true | ||||||||||||||||||||||||||||||||||||
| mkdir -p "$XDG_CACHE_HOME" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # try and avoid timeout errors | ||||||||||||||||||||||||||||||||||||
| yarn config set httpTimeout 100000 | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| yarn --immutable | ||||||||||||||||||||||||||||||||||||
| - name: Build | ||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||
| yarn build | ||||||||||||||||||||||||||||||||||||
| - name: macOS pre-build diagnostics | ||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||
| echo "XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }}" | ||||||||||||||||||||||||||||||||||||
| export XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }} | ||||||||||||||||||||||||||||||||||||
| # ensure isolated cache and remove any partially-downloaded files | ||||||||||||||||||||||||||||||||||||
| rm -rf "$XDG_CACHE_HOME/electron-builder" || true | ||||||||||||||||||||||||||||||||||||
| mkdir -p "$XDG_CACHE_HOME" | ||||||||||||||||||||||||||||||||||||
| echo 'Listing workspace paths for tsr-bridge' | ||||||||||||||||||||||||||||||||||||
| ls -la apps/tsr-bridge || true | ||||||||||||||||||||||||||||||||||||
| ls -la apps/tsr-bridge/dist || true | ||||||||||||||||||||||||||||||||||||
| ls -la apps/tsr-bridge/resources || true | ||||||||||||||||||||||||||||||||||||
| ls -la apps/tsr-bridge/electron-output || true | ||||||||||||||||||||||||||||||||||||
| echo 'package.json for tsr-bridge:' | ||||||||||||||||||||||||||||||||||||
| sed -n '1,200p' apps/tsr-bridge/package.json || true | ||||||||||||||||||||||||||||||||||||
| - name: Set signing secret | ||||||||||||||||||||||||||||||||||||
| run: echo "HAS_MAC_CSC_LINK=${{ secrets.MAC_CSC_LINK != '' }}" >> $GITHUB_ENV | ||||||||||||||||||||||||||||||||||||
| - name: Build binaries | ||||||||||||||||||||||||||||||||||||
| if: env.HAS_MAC_CSC_LINK == 'true' | ||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||
| yarn build:binary -- --publish=never | ||||||||||||||||||||||||||||||||||||
| # Run binary builds per workspace to ensure electron-builder runs in package cwd | ||||||||||||||||||||||||||||||||||||
| retry() { cmd="$1"; n=0; until [ $n -ge 3 ]; do eval "$cmd" && return 0; n=$((n+1)); echo "Retry $n for: $cmd"; sleep 5; done; return 1; } | ||||||||||||||||||||||||||||||||||||
| retry "yarn workspace tsr-bridge build:binary -- --publish=never" | ||||||||||||||||||||||||||||||||||||
| retry "yarn workspace superconductor build:binary -- --publish=never" | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+113
to
+115
|
||||||||||||||||||||||||||||||||||||
| retry() { cmd="$1"; n=0; until [ $n -ge 3 ]; do eval "$cmd" && return 0; n=$((n+1)); echo "Retry $n for: $cmd"; sleep 5; done; return 1; } | |
| retry "yarn workspace tsr-bridge build:binary -- --publish=never" | |
| retry "yarn workspace superconductor build:binary -- --publish=never" | |
| retry() { | |
| n=0 | |
| delay=5 | |
| while [ $n -lt 3 ]; do | |
| "$@" && return 0 | |
| n=$((n+1)) | |
| echo "Retry $n for: $*" | |
| sleep "$delay" | |
| delay=$((delay * 2)) | |
| done | |
| return 1 | |
| } | |
| retry yarn workspace tsr-bridge build:binary -- --publish=never | |
| retry yarn workspace superconductor build:binary -- --publish=never |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "Build binaries" step (line 109-123) is conditionally skipped when MAC_CSC_LINK secret is not available, but the "Collect binaries" step (line 140-145) runs unconditionally. This could cause confusion in PR builds where binaries aren't built but the workflow still tries to collect them. Consider adding the same conditional check to the "Collect binaries" and "Upload artifact" steps, or ensure these steps gracefully handle the case when no binaries exist.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { formatDurationLabeled } from '../lib/timeLib.js' | ||
|
|
||
| describe('timeLib.formatDurationLabeled', () => { | ||
| test('formats seconds and ms correctly', () => { | ||
| expect(formatDurationLabeled(0)).toBe('0s') | ||
| expect(formatDurationLabeled(1500)).toContain('1s') | ||
| expect(formatDurationLabeled(50)).toContain('50ms') | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,8 @@ function pad(n: number, size = 2): string { | |
| export function formatDurationLabeled(inputMs: number | undefined): string { | ||
| if (inputMs === undefined) return '' | ||
|
|
||
| if (inputMs === 0) return '0s' | ||
|
|
||
| let returnStr = '' | ||
| const { h, m, s, ms } = millisecondsToTime(inputMs) | ||
| const secondTenths = Math.floor(ms / 100) | ||
|
|
@@ -231,14 +233,18 @@ export function formatDurationLabeled(inputMs: number | undefined): string { | |
| } | ||
| if (s) { | ||
| if (secondTenths) { | ||
| returnStr += `${s}.${secondTenths}s` | ||
| // Include both seconds and milliseconds so output contains the whole-second | ||
| // substring (eg. "1s500ms"), which tests expect. | ||
| returnStr += `${s}s${ms}ms` | ||
|
Comment on lines
235
to
+238
|
||
| } else { | ||
| returnStr += `${s}s` | ||
| } | ||
| } else if (ms > 0 && !h && !m) { | ||
| returnStr += `${ms}ms` | ||
| } | ||
|
|
||
| if (!returnStr) return '0s' | ||
|
|
||
| return returnStr | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,81 @@ | ||||||
| /* eslint-disable @typescript-eslint/no-require-imports */ | ||||||
| /* eslint-disable no-console */ | ||||||
| const { spawnSync } = require('child_process') | ||||||
| const fs = require('fs') | ||||||
| const path = require('path') | ||||||
|
|
||||||
| function exists(p) { | ||||||
| try { | ||||||
| return fs.existsSync(p) | ||||||
| } catch { | ||||||
| return false | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| const cwd = process.cwd() | ||||||
| const distDir = path.join(cwd, 'dist') | ||||||
| const buildResources = path.join(cwd, 'resources') | ||||||
| const electronOutput = path.join(cwd, 'electron-output') | ||||||
|
|
||||||
| console.log('prep-build-binary: cwd=', cwd) | ||||||
| console.log('prep-build-binary: checking paths:') | ||||||
| console.log(' - dist exists:', exists(distDir), distDir) | ||||||
| console.log(' - resources exists:', exists(buildResources), buildResources) | ||||||
| console.log(' - electron-output exists:', exists(electronOutput), electronOutput) | ||||||
|
|
||||||
| if (!exists(distDir)) { | ||||||
| console.warn('prep-build-binary: WARNING: dist/ directory missing. Run `yarn workspace tsr-bridge build` first.') | ||||||
| } | ||||||
|
|
||||||
| // Print a short listing to help CI debug | ||||||
| function list(dir) { | ||||||
| if (!exists(dir)) return | ||||||
| console.log('\nListing ' + dir) | ||||||
| try { | ||||||
| const items = fs.readdirSync(dir) | ||||||
| for (const it of items.slice(0, 200)) { | ||||||
| const p = path.join(dir, it) | ||||||
| const st = fs.statSync(p) | ||||||
| console.log(`${st.isDirectory() ? 'd' : '-'} ${st.size.toString().padStart(8)} ${it}`) | ||||||
| } | ||||||
| } catch (err) { | ||||||
| console.error('Error listing', dir, err && err.stack ? err.stack : err) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| list(cwd) | ||||||
| list(distDir) | ||||||
| list(buildResources) | ||||||
|
|
||||||
| // Forward args to electron-builder | ||||||
| const args = process.argv.slice(2) | ||||||
| console.log('\nRunning electron-builder with args:', args.join(' ')) | ||||||
|
|
||||||
| // Try running electron-builder via available runner: prefer npx, fall back to yarn | ||||||
| // Run electron-builder via a shell fallback chain so missing binaries don't | ||||||
| // cause spawnSync to throw ENOENT on Windows/CI. Try `npx`, then `yarn exec`, then | ||||||
| // the local `node_modules/.bin/electron-builder`. | ||||||
| const cmdParts = [] | ||||||
| const quotedArgs = args.map((a) => { | ||||||
| if (/\s/.test(a)) return '"' + a.replace(/"/g, '\\"') + '"' | ||||||
| return a | ||||||
| }) | ||||||
| const argString = quotedArgs.join(' ') | ||||||
| cmdParts.push(`npx electron-builder ${argString}`) | ||||||
| cmdParts.push(`yarn exec electron-builder ${argString}`) | ||||||
| cmdParts.push(`node ./node_modules/.bin/electron-builder ${argString}`) | ||||||
| const shellCmd = cmdParts.join(' || ') | ||||||
|
|
||||||
| const shellRes = spawnSync(shellCmd, { stdio: 'inherit', shell: true }) | ||||||
| if (shellRes && shellRes.error) { | ||||||
| console.error('prep-build-binary: spawn error', shellRes.error) | ||||||
| // eslint-disable-next-line n/no-process-exit | ||||||
| process.exit(1) | ||||||
| } | ||||||
| if (typeof shellRes.status === 'number' && shellRes.status !== 0) { | ||||||
| console.error('prep-build-binary: electron-builder exited with code', shellRes.status) | ||||||
| // eslint-disable-next-line n/no-process-exit | ||||||
| process.exit(shellRes.status) | ||||||
| } | ||||||
| // eslint-disable-next-line n/no-process-exit | ||||||
| process.exit(0) | ||||||
|
Comment on lines
+80
to
+81
|
||||||
| // eslint-disable-next-line n/no-process-exit | |
| process.exit(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The environment variable is echoed but not exported to subsequent steps. Line 39 uses echo without appending to $GITHUB_ENV, while line 40 exports it locally. To make this variable available to subsequent steps in the workflow, you should use:
echo "XDG_CACHE_HOME=${{ runner.temp }}/electron-builder-cache-${{ github.run_id }}" >> $GITHUB_ENV