-
Notifications
You must be signed in to change notification settings - Fork 0
670 lines (602 loc) · 28.4 KB
/
Copy pathci.yml
File metadata and controls
670 lines (602 loc) · 28.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
# =============================================================================
# CI/CD Pipeline — damp
# =============================================================================
#
# Stages: build → test → release (per platform)
#
# Runners:
# Windows: self-hosted [self-hosted, Windows] (machine must be x86-64)
# Linux: self-hosted [self-hosted, Linux] (machine must be x86-64)
# macOS: GitHub-hosted macos-15-intel (the codebase requires x86-64 SIMD
# (AVX via libsimdpp), so Apple-Silicon images cannot build it)
#
# SECURITY — self-hosted runners on a public repository:
# Jobs on self-hosted runners are gated so they NEVER run for pull requests
# from forks (a fork PR would otherwise execute arbitrary code on our
# hardware). Same-repo PRs and pushes run normally. If a fork PR needs CI,
# maintainers can re-push the branch to this repo.
#
# Self-hosted Windows runner prerequisites:
# cmake, git, git-lfs, nuget, ffmpeg, conda, MSVC build tools
#
# Self-hosted Linux runner prerequisites (Ubuntu 22.04+):
# build-essential (gcc/g++/make), cmake, python3, ffmpeg, and dev packages:
# libsndfile1-dev libtbb-dev libboost-{system,filesystem,serialization,
# math,iostreams}-dev libpng-dev zlib1g-dev libfftw3-dev libarmadillo-dev
# See docs/RUNNERS.md for the full self-hosted runner setup.
#
# sound_quality2_cache is a gitignored generated artifact. Every build job
# regenerates it from the in-repo resource/analysis_data (no external
# datasets) and every release packaging step hard-fails if it is missing —
# shipping without it breaks the mastering5 mode and its presets.
#
# Release: triggered on version tags (v*)
#
# =============================================================================
name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
run_mastering_tests:
description: "Run the full (slow) mastering mode test matrix"
type: boolean
default: false
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ===========================================================================
# WINDOWS (self-hosted)
# ===========================================================================
build-windows:
# Self-hosted: never run fork-PR code on our hardware.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Windows]
timeout-minutes: 40
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
persist-credentials: false
# actions/checkout fetches LFS objects but, on self-hosted runners without
# a global `git lfs install`, may leave the working tree holding pointer
# files. Smudge them from the already-fetched local object store (no network
# or credentials needed). Idempotent where checkout already smudged.
- name: Materialize Git LFS files
run: git lfs checkout
- name: Ensure Git Bash precedes the WSL bash stub on PATH
shell: powershell
run: |
$gitBin = Join-Path (Split-Path (Split-Path (Get-Command git).Source)) 'bin'
Add-Content -Path $env:GITHUB_PATH -Value $gitBin
- name: Verify prerequisites
shell: bash
run: |
echo "=== Self-hosted runner prerequisite check ==="
fail=0
for cmd in cmake git nuget ffmpeg conda; do
if command -v "$cmd" &>/dev/null; then
echo "OK: $cmd ($(command -v "$cmd"))"
else
echo "MISSING: $cmd"; fail=1
fi
done
if command -v cl &>/dev/null || [ -n "$VSINSTALLDIR" ]; then
echo "OK: MSVC (Visual Studio)"
else
echo "WARN: cl.exe not on PATH — cmake will attempt to find MSVC automatically"
fi
if [ "$fail" -eq 1 ]; then
echo ""; echo "FATAL: Missing prerequisites. See runner setup instructions in ci.yml."
exit 1
fi
echo "=== All prerequisites OK ==="
- name: Install Intel IPP + TBB (NuGet)
shell: bash
run: |
nuget install intelipp.static.win-x64 -Version 2019.4.245 -OutputDirectory intel_deps
nuget install inteltbb.devel.win -Version 2021.1.1.133 -OutputDirectory intel_deps
nuget install inteltbb.redist.win -Version 2021.1.1.133 -OutputDirectory intel_deps
echo "IPPROOT=$GITHUB_WORKSPACE/intel_deps/intelipp.static.win-x64.2019.4.245/lib/native" >> $GITHUB_ENV
echo "TBBROOT=$GITHUB_WORKSPACE/intel_deps/inteltbb.devel.win.2021.1.1.133/lib/native" >> $GITHUB_ENV
- name: Install build libraries (conda)
shell: bash
run: |
# An all-users Miniforge base env (e.g. C:\Miniforge3) is owned by
# Administrators and is NOT writable by the runner's service account,
# so `conda install` into base fails with EnvironmentNotWritableError.
# Create a dedicated env inside the workspace instead: the runner owns
# it, and `git clean` wipes it fresh on each checkout.
eval "$(conda shell.bash hook)"
CONDA_ENV="$GITHUB_WORKSPACE/condaenv"
rm -rf "$CONDA_ENV"
# `zlib` (not just the libpng-pulled `libzlib` runtime DLL) is needed
# for the zlib.lib import library + headers used to link on Windows.
# libpng/zlib are pinned to current fixed releases — these DLLs ship
# in the Windows release zip, so keep them scanner-clean.
conda create -y -p "$CONDA_ENV" -c conda-forge \
boost==1.82.0 armadillo==12.6.2 libpng==1.6.58 fftw==3.3.10 zlib==1.3.2
echo "CONDA_ROOT=$CONDA_ENV" >> $GITHUB_ENV
- name: Build
shell: bash
run: |
cmake -DCMAKE_BUILD_TYPE=Release \
-DSANDYBRIDGE_SUPPORT=ON \
-DDISABLE_TARGET_BENCH=ON \
-DDISABLE_TARGET_TEST=ON \
-DBUILD_MASTERING_GUI=ON \
-DENABLE_FFTW=ON \
.
cmake --build . --config Release
- name: Package binaries and DLLs
shell: bash
run: |
mkdir -p staging/bin
cp bin/Release/*.exe staging/bin/
cp prebuilt/win64/libsndfile-1.2.2-win64/bin/sndfile.dll staging/bin/
TBB_REDIST="${GITHUB_WORKSPACE}/intel_deps/inteltbb.redist.win.2021.1.1.133/runtimes/win-x64/native"
cp "$TBB_REDIST"/tbb12.dll "$TBB_REDIST"/tbbmalloc.dll staging/bin/
# Boost + compression DLLs from conda
CONDA_DLL="$CONDA_ROOT/Library/bin"
(
cd "$CONDA_DLL"
cp boost_system.dll boost_filesystem.dll boost_serialization.dll \
boost_math_tr1.dll boost_iostreams.dll \
libbz2.dll zlib.dll zstd.dll \
"$OLDPWD/staging/bin/"
# liblzma is a transitive dependency (boost_iostreams)
cp liblzma.dll "$OLDPWD/staging/bin/" 2>/dev/null || true
)
# FFTW DLLs (mastering2/mastering3 modes) — hard fail if missing,
# shipping without them silently breaks 6 factory presets
cp "$CONDA_DLL"/fftw3.dll "$CONDA_DLL"/fftw3f.dll staging/bin/
# Visual C++ runtime. MSVCP140.dll and VCRUNTIME140*.dll are NOT part of
# a clean Windows install, so without them every binary fails to launch
# with "MSVCP140.dll was not found" until the user installs the VC++
# redistributable. Microsoft explicitly permits redistributing these, so
# ship them next to the exes (app-local deployment, ~1.8 MB total).
VSWHERE="/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
VSPATH=$("$VSWHERE" -latest -products '*' -property installationPath | tr -d '\r')
CRT_DIR=$(ls -d "$VSPATH/VC/Redist/MSVC/"*/x64/Microsoft.VC*.CRT 2>/dev/null | tail -1)
if [ -z "$CRT_DIR" ]; then
echo "FATAL: could not locate the VC++ redistributable CRT directory"
echo " (looked under $VSPATH/VC/Redist/MSVC/*/x64/Microsoft.VC*.CRT)"
exit 1
fi
echo "Bundling VC++ runtime from: $CRT_DIR"
cp "$CRT_DIR"/*.dll staging/bin/
# Fail loudly rather than ship an archive that cannot start.
for dll in msvcp140.dll vcruntime140.dll vcruntime140_1.dll; do
test -f "staging/bin/$dll" || { echo "FATAL: missing $dll"; exit 1; }
done
- name: Generate sound_quality2_cache
shell: bash
run: |
staging/bin/damp_analyzer \
--mode=sound_quality2_preparation \
--analysis_data_dir=resource/analysis_data \
--sound_quality2_cache=staging/sound_quality2_cache
test -s staging/sound_quality2_cache
- uses: actions/upload-artifact@v4
with:
name: build-windows
path: staging
test-windows:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
needs: build-windows
runs-on: [self-hosted, Windows]
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Ensure Git Bash precedes the WSL bash stub on PATH
shell: powershell
run: |
$gitBin = Join-Path (Split-Path (Split-Path (Get-Command git).Source)) 'bin'
Add-Content -Path $env:GITHUB_PATH -Value $gitBin
- uses: actions/download-artifact@v4
with:
name: build-windows
path: staging
- name: Smoke test — binaries start and report version
shell: bash
run: |
staging/bin/damp.exe --version
staging/bin/damp_analyzer.exe --version
echo "Binaries launch OK"
# Preset JSON validation runs on the Linux job (presets are
# platform-independent), so it is not duplicated here — that keeps Python
# off the Windows runner's requirements.
# Fast modes always; mastering5 also validates the generated cache.
- name: Mastering smoke — mastering6 + mastering5
shell: bash
run: |
mkdir -p test_out
cd resource
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_m6.wav \
--mastering=true --mastering_mode=mastering6
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_m5.wav \
--mastering=true --mastering_mode=mastering5 \
--sound_quality2_cache=../staging/sound_quality2_cache
cd ..
test -s test_out/out_m6.wav && test -s test_out/out_m5.wav
- name: Mastering smoke — full matrix (slow)
if: inputs.run_mastering_tests == true
shell: bash
run: |
mkdir -p test_out
cd resource
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_classic.wav \
--mastering=true --mastering_mode=classic
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_m2.wav \
--mastering=true --mastering_mode=mastering2 --mastering2_config_file=mastering2_config.json
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_m3.wav \
--mastering=true --mastering_mode=mastering3
../staging/bin/damp.exe --input=../test_data/test2.wav --output=../test_out/out_ma.wav \
--mastering=true --mastering_mode=mastering_a
# ===========================================================================
# RELEASE (single job)
# GitHub "immutable releases" lock a release's asset set once it is published,
# so three parallel release jobs racing to publish the same tag fail after the
# first one wins. Instead, one job gathers every platform's build artifact and
# publishes the release atomically with all assets attached in a single call.
# ===========================================================================
release:
needs: [build-windows, test-windows, build-linux, test-linux, build-macos]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: [self-hosted, Linux]
timeout-minutes: 20
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
lfs: true
# Ship real resource/ data, not LFS pointer files (self-hosted runners may
# lack a global git-lfs install; see the build jobs).
- name: Materialize Git LFS files
run: git lfs checkout
# No `name:` downloads every artifact into artifacts/<artifact-name>/:
# artifacts/build-{windows,linux,macos}/{bin,sound_quality2_cache}
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Package all platform archives
shell: bash
run: |
set -euo pipefail
stage() { # $1 = os tag, $2 = artifact dir under artifacts/
local os="$1" art="artifacts/$2"
local root="release/$os/damp"
mkdir -p "$root"
cp -r "$art/bin" "$root/bin"
# Linux ships its runtime libraries next to bin/ (DT_RPATH=$ORIGIN/../lib);
# Windows keeps its DLLs inside bin/ and macOS has no bundle yet.
if [ -d "$art/lib" ]; then cp -r "$art/lib" "$root/lib"; fi
cp -r ci/licenses "$root/"
cp -r resource "$root/"
cp -r test_data "$root/"
cp LICENSE README.md "$root/"
# Gitignored generated cache from the build artifact — required for
# mastering5; hard-fail if absent (upload-artifact drops it otherwise).
cp "$art/sound_quality2_cache" "$root/resource/"
cp "$art/sound_quality2_cache" "$root/"
}
stage windows build-windows
stage linux build-linux
stage macos build-macos
# upload-artifact does not preserve the executable bit; restore it.
chmod +x release/linux/damp/bin/* release/macos/damp/bin/*
mkdir -p dist
# Windows .zip via python3 (already a Linux-runner prerequisite) so the
# release does not depend on `zip` being installed on the runner.
( cd release/windows && python3 -m zipfile -c "$GITHUB_WORKSPACE/dist/damp-windows-x64.zip" damp )
tar -C release/linux -Jcf dist/damp-linux-x64.tar.xz damp
tar -C release/macos -czf dist/damp-macos-x64.tar.gz damp
ls -l dist
- name: Publish GitHub Release
# Third-party action on a self-hosted runner with contents:write —
# pin to a full commit SHA so a moved/hijacked tag can't run
# arbitrary code here or misuse the token. (SHA = v2)
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65
with:
files: |
dist/damp-windows-x64.zip
dist/damp-linux-x64.tar.xz
dist/damp-macos-x64.tar.gz
generate_release_notes: true
# ===========================================================================
# LINUX (self-hosted)
# CLI tools only — the GUI needs X11/Wayland dev headers for GLFW; enable
# BUILD_MASTERING_GUI here once runners are provisioned for it.
# ===========================================================================
build-linux:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux]
timeout-minutes: 45
env:
LANG: C.UTF-8
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
persist-credentials: false
# See build-windows: smudge LFS files from the local object store in case
# the runner lacks a global `git lfs install`. Idempotent.
- name: Materialize Git LFS files
run: git lfs checkout
- name: Verify prerequisites
run: |
echo "=== Self-hosted runner prerequisite check ==="
fail=0
for cmd in cmake make gcc g++ python3 ffmpeg; do
if command -v "$cmd" &>/dev/null; then
echo "OK: $cmd ($(command -v "$cmd"))"
else
echo "MISSING: $cmd"; fail=1
fi
done
if [ "$fail" -eq 1 ]; then
echo ""; echo "FATAL: Missing prerequisites. Required dev packages:"
echo " libsndfile1-dev libtbb-dev libboost-system-dev libboost-filesystem-dev"
echo " libboost-serialization-dev libboost-math-dev libboost-iostreams-dev"
echo " libpng-dev zlib1g-dev libfftw3-dev"
exit 1
fi
echo "=== All prerequisites OK ==="
- name: Build
# Unix Makefiles (not Ninja): make ships with build-essential, which is
# already required for gcc/g++, so the build needs no extra runner tool.
run: |
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release \
-DSANDYBRIDGE_SUPPORT=ON \
-DDISABLE_IPP=ON \
-DENABLE_FFTW=ON \
-DDISABLE_TARGET_BENCH=ON \
.
make -j "$(nproc)"
find bin -maxdepth 1 -type f -executable | xargs -r -n 1 -P "$(nproc)" strip
- name: Generate sound_quality2_cache
run: |
mkdir -p staging
bin/damp_analyzer \
--mode=sound_quality2_preparation \
--analysis_data_dir=resource/analysis_data \
--sound_quality2_cache=staging/sound_quality2_cache
test -s staging/sound_quality2_cache
- name: Bundle runtime libraries
shell: bash
run: |
set -euo pipefail
mkdir -p staging/bin staging/lib
cp -R bin/. staging/bin/
# Copy every non-system shared object the binaries need into lib/, so the
# tarball runs without apt-installing anything. Boost, libpng and zlib are
# already linked statically (see CMakeLists), so in practice this is
# libsndfile + its codecs, TBB, FFTW, libstdc++ and libgcc.
#
# glibc is deliberately NOT bundled: pairing a bundled libc with the host's
# dynamic loader breaks in subtle ways. The tarball's floor is therefore the
# glibc of the build host (Ubuntu 22.04 -> runs on 22.04 and newer).
EXCLUDE='^(linux-vdso|ld-linux-x86-64|libc|libm|libdl|libpthread|librt|libresolv)\.so'
deps=$(mktemp)
for b in staging/bin/*; do
[ -f "$b" ] && [ -x "$b" ] || continue
ldd "$b" 2>/dev/null | awk '$3 ~ /^\// { print $1 "\t" $3 }' >> "$deps" || true
done
sort -u "$deps" -o "$deps"
while IFS=$'\t' read -r soname path; do
[ -n "$soname" ] || continue
printf '%s' "$soname" | grep -Eq "$EXCLUDE" && continue
# -L: resolve the symlink, but keep the SONAME as the filename.
cp -L "$path" "staging/lib/$soname"
done < "$deps"
rm -f "$deps"
echo "--- bundled into lib/ ---"
ls -1 staging/lib
echo "lib/ total: $(du -sh staging/lib | cut -f1)"
- name: Verify the Linux bundle is self-contained
shell: bash
run: |
set -euo pipefail
# 1) The binaries must carry an inheritable DT_RPATH pointing at ../lib.
if ! readelf -d staging/bin/damp | grep -E 'RPATH' | grep -q 'ORIGIN/../lib'; then
echo "FATAL: damp has no DT_RPATH=\$ORIGIN/../lib (RUNPATH is not enough:"
echo " it is not used to resolve a dependency's own dependencies)."
readelf -d staging/bin/damp | head -20
exit 1
fi
# 2) Nothing may be left unresolved.
for b in staging/bin/*; do
[ -x "$b" ] || continue
if ldd "$b" | grep -q 'not found'; then
echo "FATAL: unresolved libraries in $b"; ldd "$b" | grep 'not found'; exit 1
fi
done
# 3) Report the real glibc floor. We bundle everything except glibc, so the
# floor is whatever the *build host* was compiled against — not a value
# we get to choose here. Derive it rather than assume it.
req=$(objdump -T staging/bin/* staging/lib/*.so* 2>/dev/null \
| grep -oE 'GLIBC_[0-9]+\.[0-9]+' | sort -u -V | tail -1)
echo "Minimum glibc required by this build: ${req:-unknown}"
echo "Build host glibc: $(ldd --version | head -1)"
# 4) The only check that actually proves portability: run the packaged
# binaries in a clean container with none of our build dependencies.
# The image must be new enough to satisfy the floor above; override with
# PORTABILITY_IMAGE when the build host moves to a newer glibc.
IMAGE="${PORTABILITY_IMAGE:-ubuntu:24.04}"
if command -v docker >/dev/null 2>&1; then
echo "--- clean-container check against $IMAGE ---"
docker run --rm "$IMAGE" ldd --version | head -1 || true
if ! docker run --rm -v "$PWD/staging:/damp:ro" "$IMAGE" /damp/bin/damp --version; then
echo ""
echo "FATAL: the packaged binaries do not run on $IMAGE."
echo " They need ${req:-a newer glibc}, which that image does not provide."
echo " Either bump PORTABILITY_IMAGE, or build on an older distro"
echo " (in a container) to lower the floor."
exit 1
fi
docker run --rm -v "$PWD/staging:/damp:ro" "$IMAGE" /damp/bin/damp_analyzer --version
echo "clean-container check PASSED on $IMAGE"
else
echo "NOTE: docker is not available on this runner, so the clean-container"
echo " check was skipped. The bundle was verified via DT_RPATH + ldd only."
fi
- uses: actions/upload-artifact@v4
with:
name: build-linux
path: |
staging/bin
staging/lib
staging/sound_quality2_cache
test-linux:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
needs: build-linux
runs-on: [self-hosted, Linux]
timeout-minutes: 30
env:
LANG: C.UTF-8
steps:
- uses: actions/checkout@v4
with:
lfs: true
persist-credentials: false
- uses: actions/download-artifact@v4
with:
name: build-linux
path: staging
- name: Setup binaries
run: |
cp -R staging/bin ./test_bin
chmod +x test_bin/*
# Mirror the release layout (bin/ + ../lib) so the binaries resolve their
# libraries through the bundle's DT_RPATH rather than the runner's system
# packages — otherwise these tests would pass even with a broken bundle.
if [ -d staging/lib ]; then cp -R staging/lib ./lib; fi
- name: Smoke test — binaries start and report version
run: |
test_bin/damp --version
test_bin/damp_analyzer --version
test_bin/damp_test --gtest_list_tests > /dev/null
echo "Binaries launch OK"
- name: Unit tests (gtest)
run: |
# SoundQuality2Calculator.Validation is a "heavy" regression test whose
# pass/fail thresholds (AUC, stddev) are hand-calibrated against the
# proprietary bakuage_dataset1/analysis training set, which is not part
# of the open-source release. Run against any other corpus (e.g. the
# in-repo resource/analysis_data) it produces a different statistical
# distribution and fails by design. Exclude it from the public CI gate;
# developers with the calibration dataset can run it locally via
# damp_test --sound_quality2_analysis_data=/path/to/bakuage_dataset1/analysis
test_bin/damp_test --gtest_filter=-SoundQuality2Calculator.Validation
- name: Validate preset JSON files
run: |
python3 - <<'EOF'
import json, glob, sys
presets = sorted(glob.glob('resource/presets/*.json'))
errors = 0
for f in presets:
try:
data = json.load(open(f, encoding='utf-8'))
mode = data.get('mastering_mode')
if mode is None:
print(f'ERROR: {f} missing mastering_mode'); errors += 1
elif not (0 <= int(mode) <= 5):
print(f'ERROR: {f} invalid mastering_mode {mode}'); errors += 1
except (json.JSONDecodeError, ValueError) as e:
print(f'ERROR: {f}: {e}'); errors += 1
if errors:
print(f'{errors} preset errors found'); sys.exit(1)
print(f'All {len(presets)} presets valid')
EOF
- name: Mastering smoke — default + mastering2 + mastering6
run: |
mkdir -p /tmp/damp_test_out
cd resource
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_default.wav
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_m2.wav \
--mastering=true --mastering_mode=mastering2 --mastering2_config_file=mastering2_config.json
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_m6.wav \
--mastering=true --mastering_mode=mastering6
- name: Mastering smoke — full matrix (slow)
if: inputs.run_mastering_tests == true
run: |
mkdir -p /tmp/damp_test_out
cd resource
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_classic.wav \
--mastering=true --mastering_mode=classic
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_m3.wav \
--mastering=true --mastering_mode=mastering3
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_m5.wav \
--mastering=true --mastering_mode=mastering5 \
--sound_quality2_cache=../staging/sound_quality2_cache
../test_bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_ma.wav \
--mastering=true --mastering_mode=mastering_a
# ===========================================================================
# MACOS (GitHub-hosted, Intel image — x86-64 SIMD is required)
# Build + test in one job to avoid paying brew install twice.
# ===========================================================================
build-macos:
runs-on: macos-15-intel
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
# GitHub-hosted runners ship a global git-lfs, so checkout already smudges;
# this is a harmless safety net mirroring the self-hosted jobs.
- name: Materialize Git LFS files
run: git lfs checkout
- name: Install dependencies
run: brew install cmake ninja libsndfile tbb boost libpng fftw ffmpeg armadillo
- name: Build
run: |
cmake -GNinja -DCMAKE_BUILD_TYPE=Release \
-DSANDYBRIDGE_SUPPORT=ON \
-DDISABLE_IPP=ON \
-DENABLE_FFTW=ON \
-DDISABLE_TARGET_BENCH=ON \
-DDISABLE_TARGET_TEST=ON \
.
ninja -j "$(sysctl -n hw.ncpu)"
- name: Generate sound_quality2_cache
run: |
mkdir -p staging
bin/damp_analyzer \
--mode=sound_quality2_preparation \
--analysis_data_dir=resource/analysis_data \
--sound_quality2_cache=staging/sound_quality2_cache
test -s staging/sound_quality2_cache
- name: Smoke test — binaries start and report version
run: |
bin/damp --version
bin/damp_analyzer --version
- name: Mastering smoke — mastering6
run: |
mkdir -p /tmp/damp_test_out
cd resource
../bin/damp --input=../test_data/test2.wav --output=/tmp/damp_test_out/out_m6.wav \
--mastering=true --mastering_mode=mastering6
- name: Package
run: |
cp -R bin staging/
- uses: actions/upload-artifact@v4
with:
name: build-macos
path: |
staging/bin
staging/sound_quality2_cache
# Release packaging/publishing for all platforms is handled by the single
# `release` job above (see its header comment on immutable releases).