From 842412f4ab9d7a167d1c19db35a157968e1c635a Mon Sep 17 00:00:00 2001 From: Giles Westwood Date: Sun, 21 Jun 2026 20:39:31 +0100 Subject: [PATCH 1/5] ci: add tag-triggered release workflow producing prebuilt Linux amd64 tarball The existing CI builds keyfinder-cli but doesn't publish anything, forcing downstream consumers (Ansible roles, package managers, etc.) to rebuild both libkeyfinder and keyfinder-cli from source on every host. This adds a separate workflow that, on tag push, builds keyfinder-cli against a pinned libkeyfinder (v2.2.6) and bundles both binaries into a tarball uploaded as a GitHub release asset, with a SHA-256 sum file. The bundled libkeyfinder.so means consumers only need the standard FFmpeg runtime libs (usually already present via `ffmpeg`). Layout in the tarball matches a standard `/usr/local` prefix so installation is `tar xzf` + copy + ldconfig. Existing per-push build job is untouched. --- .github/workflows/release.yml | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b468520 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +permissions: + contents: write + +jobs: + linux-amd64: + name: Build Linux amd64 tarball + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install --yes \ + cmake pkg-config \ + libfftw3-dev \ + libavformat-dev libavcodec-dev libavutil-dev libswresample-dev + + - name: Build libkeyfinder + run: | + git clone --depth 1 --branch v2.2.6 \ + https://github.com/mixxxdj/libkeyfinder keyfinder + cmake -S keyfinder -B keyfinder/build -DBUILD_TESTING=OFF + cmake --build keyfinder/build + sudo cmake --install keyfinder/build + sudo ldconfig + + - name: Build keyfinder-cli + run: | + cmake -S . -B build + cmake --build build + + # Stage `keyfinder_cli` + the bundled libkeyfinder.so so consumers + # only need FFmpeg runtime libs (the .so symlinks are flattened to + # real files in the tarball so `tar xzf` lands a usable layout). + - name: Stage release tarball + run: | + version="${GITHUB_REF_NAME#v}" + stage="keyfinder-cli-${version}-linux-amd64" + mkdir -p "$stage/bin" "$stage/lib" + cp build/keyfinder-cli "$stage/bin/keyfinder_cli" + # Resolve the .so symlink chain to the real library, then + # recreate the SONAME symlink ldconfig expects. + real=$(readlink -f /usr/local/lib/libkeyfinder.so) + soname=$(basename "$(readlink /usr/local/lib/libkeyfinder.so)") + cp "$real" "$stage/lib/$(basename "$real")" + ln -s "$(basename "$real")" "$stage/lib/$soname" + ln -s "$soname" "$stage/lib/libkeyfinder.so" + cat > "$stage/README.txt" < "$stage.tar.gz.sha256" + echo "ASSET=$stage.tar.gz" >> "$GITHUB_ENV" + echo "SUMS=$stage.tar.gz.sha256" >> "$GITHUB_ENV" + + - name: Upload to GitHub release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ env.ASSET }} + ${{ env.SUMS }} + generate_release_notes: true From ab6a37bd31ee0fcb4d3b0a50bdf8cc4b1fef78a2 Mon Sep 17 00:00:00 2001 From: Giles Westwood Date: Sun, 21 Jun 2026 20:44:22 +0100 Subject: [PATCH 2/5] ci(release): per-distro matrix (ubuntu24, debian13) for FFmpeg ABI compat Ubuntu 24.04 ships FFmpeg 6 (libavformat.so.60); Debian trixie ships FFmpeg 7 (libavformat.so.61). A binary built on one won't load on the other. Build inside per-distro containers so consumers can pick the tarball whose ABI matches their host. Tarballs are now suffixed -ubuntu24 / -debian13 to make the choice explicit; old single-tarball assets stay on the prior release. --- .github/workflows/release.yml | 42 +++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b468520..96ec138 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,27 +11,41 @@ permissions: jobs: linux-amd64: - name: Build Linux amd64 tarball + name: Build ${{ matrix.label }} amd64 tarball runs-on: ubuntu-latest + container: ${{ matrix.container }} + strategy: + fail-fast: false + matrix: + include: + # Ubuntu 24.04 LTS ships FFmpeg 6 (libavformat.so.60). Works on + # Ubuntu 24.04 + downstreams; Debian bookworm also ships FFmpeg 6. + - label: ubuntu24 + container: ubuntu:24.04 + # Debian trixie ships FFmpeg 7 (libavformat.so.61). Separate + # tarball because Ubuntu's .so.60 binary won't load against .61. + - label: debian13 + container: debian:trixie steps: - - uses: actions/checkout@v4 - - name: Install build dependencies run: | - sudo apt-get update - sudo apt-get install --yes \ - cmake pkg-config \ + apt-get update + apt-get install --yes --no-install-recommends \ + ca-certificates git \ + g++ cmake pkg-config \ libfftw3-dev \ libavformat-dev libavcodec-dev libavutil-dev libswresample-dev + - uses: actions/checkout@v4 + - name: Build libkeyfinder run: | git clone --depth 1 --branch v2.2.6 \ https://github.com/mixxxdj/libkeyfinder keyfinder cmake -S keyfinder -B keyfinder/build -DBUILD_TESTING=OFF cmake --build keyfinder/build - sudo cmake --install keyfinder/build - sudo ldconfig + cmake --install keyfinder/build + ldconfig - name: Build keyfinder-cli run: | @@ -39,12 +53,11 @@ jobs: cmake --build build # Stage `keyfinder_cli` + the bundled libkeyfinder.so so consumers - # only need FFmpeg runtime libs (the .so symlinks are flattened to - # real files in the tarball so `tar xzf` lands a usable layout). + # only need FFmpeg runtime libs that match this image's release. - name: Stage release tarball run: | version="${GITHUB_REF_NAME#v}" - stage="keyfinder-cli-${version}-linux-amd64" + stage="keyfinder-cli-${version}-linux-amd64-${{ matrix.label }}" mkdir -p "$stage/bin" "$stage/lib" cp build/keyfinder-cli "$stage/bin/keyfinder_cli" # Resolve the .so symlink chain to the real library, then @@ -55,11 +68,12 @@ jobs: ln -s "$(basename "$real")" "$stage/lib/$soname" ln -s "$soname" "$stage/lib/libkeyfinder.so" cat > "$stage/README.txt" < "$stage.tar.gz.sha256" From b70f7f2356bac63e88153f584a031b28815bcca0 Mon Sep 17 00:00:00 2001 From: Giles Westwood Date: Sun, 21 Jun 2026 20:45:47 +0100 Subject: [PATCH 3/5] ci(release): apt install make (stripped by --no-install-recommends) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96ec138..247fb9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: apt-get update apt-get install --yes --no-install-recommends \ ca-certificates git \ - g++ cmake pkg-config \ + g++ make cmake pkg-config \ libfftw3-dev \ libavformat-dev libavcodec-dev libavutil-dev libswresample-dev From 60f9b40836aea4c000578d5364f93030493291a5 Mon Sep 17 00:00:00 2001 From: Giles Westwood Date: Sun, 21 Jun 2026 21:04:34 +0100 Subject: [PATCH 4/5] ci(release): produce .deb via CPack instead of raw tarball MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMakeLists gains a CPack DEB block — CPACK_DEBIAN_PACKAGE_SHLIBDEPS=ON derives Depends: straight from dpkg-shlibdeps against the built binary, so the right libavformatN / libfftw3-3 / etc. names land per build env. Workflow now: - builds libkeyfinder STATIC (-DBUILD_SHARED_LIBS=OFF -fPIC) so it links into the keyfinder-cli binary, - runs `cpack -G DEB` after the build, - uploads a single self-contained .deb per matrix entry (ubuntu24, debian13) plus its sha256. Consumers install with `apt install ./keyfinder-cli_*_amd64.deb` — no ldconfig handler, no /usr/local layout surgery, FFmpeg deps pulled in through apt's resolver. Tarball output dropped in favour of the .deb (one canonical artifact). --- .github/workflows/release.yml | 55 +++++++++++++++++------------------ CMakeLists.txt | 26 +++++++++++++++++ 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 247fb9b..1005188 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,47 +38,44 @@ jobs: - uses: actions/checkout@v4 - - name: Build libkeyfinder + # libkeyfinder built as a STATIC library so it gets linked into + # the keyfinder-cli binary — the .deb ships one self-contained + # ELF with FFmpeg as the only .so dependency. No /usr/local/lib + # drop, no ldconfig dance, dpkg-shlibdeps derives Depends: + # straight from ldd output. + - name: Build libkeyfinder (static) run: | git clone --depth 1 --branch v2.2.6 \ https://github.com/mixxxdj/libkeyfinder keyfinder - cmake -S keyfinder -B keyfinder/build -DBUILD_TESTING=OFF + cmake -S keyfinder -B keyfinder/build \ + -DBUILD_TESTING=OFF \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build keyfinder/build cmake --install keyfinder/build - ldconfig - name: Build keyfinder-cli run: | - cmake -S . -B build + version="${GITHUB_REF_NAME#v}" + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCPACK_PACKAGE_VERSION="${version}" cmake --build build - # Stage `keyfinder_cli` + the bundled libkeyfinder.so so consumers - # only need FFmpeg runtime libs that match this image's release. - - name: Stage release tarball + - name: Package .deb (cpack) run: | version="${GITHUB_REF_NAME#v}" - stage="keyfinder-cli-${version}-linux-amd64-${{ matrix.label }}" - mkdir -p "$stage/bin" "$stage/lib" - cp build/keyfinder-cli "$stage/bin/keyfinder_cli" - # Resolve the .so symlink chain to the real library, then - # recreate the SONAME symlink ldconfig expects. - real=$(readlink -f /usr/local/lib/libkeyfinder.so) - soname=$(basename "$(readlink /usr/local/lib/libkeyfinder.so)") - cp "$real" "$stage/lib/$(basename "$real")" - ln -s "$(basename "$real")" "$stage/lib/$soname" - ln -s "$soname" "$stage/lib/libkeyfinder.so" - cat > "$stage/README.txt" < "$stage.tar.gz.sha256" - echo "ASSET=$stage.tar.gz" >> "$GITHUB_ENV" - echo "SUMS=$stage.tar.gz.sha256" >> "$GITHUB_ENV" + cd build + # Suffix the package filename with the distro label so a single + # release page can carry both ABI variants without colliding. + cpack -G DEB -D CPACK_PACKAGE_FILE_NAME="keyfinder-cli_${version}-${{ matrix.label }}_amd64" + deb=$(ls keyfinder-cli_*_amd64.deb | head -1) + sha256sum "$deb" > "${deb}.sha256" + # Quick sanity: print Depends + size so the run log shows what + # consumers will pull in. + dpkg-deb -I "$deb" | grep -E "Depends|Installed-Size" + echo "ASSET=build/${deb}" >> "$GITHUB_ENV" + echo "SUMS=build/${deb}.sha256" >> "$GITHUB_ENV" - name: Upload to GitHub release if: startsWith(github.ref, 'refs/tags/') diff --git a/CMakeLists.txt b/CMakeLists.txt index f845b9b..d5e2a43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,3 +31,29 @@ install(TARGETS ${PROJECT_NAME}) if(UNIX) install(FILES ${PROJECT_NAME}.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif() + +# Packaging: `cpack -G DEB` produces a .deb of the installed tree. +# CPACK_DEBIAN_PACKAGE_SHLIBDEPS=ON makes dpkg-shlibdeps derive the +# Depends: line from the binary's dynamic library references, so the +# package gets the correct libavformatN / libfftw3-3 etc. for the +# build environment without us having to hand-curate per-distro deps. +set(CPACK_PACKAGE_NAME "keyfinder-cli") +set(CPACK_PACKAGE_VENDOR "keyfinder-cli upstream") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY + "Small command line tool that detects the musical key of an audio file") +set(CPACK_PACKAGE_HOMEPAGE_URL + "https://github.com/EvanPurkhiser/keyfinder-cli") +set(CPACK_PACKAGE_CONTACT + "keyfinder-cli upstream ") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + +# DEB-specific. +set(CPACK_DEBIAN_PACKAGE_SECTION "sound") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +# Allow CI to override the version (so a tag like v1.2.0-prebuilt-1 +# carries through into the .deb filename + control file). +if(NOT CPACK_PACKAGE_VERSION) + set(CPACK_PACKAGE_VERSION "1.2.0") +endif() + +include(CPack) From 395427c8e9bc350235bce56e72a8e5253f42327a Mon Sep 17 00:00:00 2001 From: Giles Westwood Date: Sun, 21 Jun 2026 21:06:55 +0100 Subject: [PATCH 5/5] ci(release): apt install file/fakeroot/dpkg-dev for CPack DEB --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1005188..eaa9d65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,8 @@ jobs: ca-certificates git \ g++ make cmake pkg-config \ libfftw3-dev \ - libavformat-dev libavcodec-dev libavutil-dev libswresample-dev + libavformat-dev libavcodec-dev libavutil-dev libswresample-dev \ + file fakeroot dpkg-dev - uses: actions/checkout@v4