a Jellyfin & Subsonic client for the terminal — powered by mpv, Chromecast and UPnP MediaRenderer
mpv chromecast mpris navidrome jellyfin upnp tui
0

Configure Feed

Select the types of activity you want to include in your feed.

fin / .github / workflows / release.yml
11 kB 312 lines
1name: Release 2 3on: 4 push: 5 tags: 6 - "v*" 7 workflow_dispatch: 8 inputs: 9 tag: 10 description: "Tag to release (e.g. v0.1.0)" 11 required: true 12 type: string 13 14permissions: 15 contents: write 16 17jobs: 18 build: 19 name: ${{ matrix.label }} 20 runs-on: ${{ matrix.os }} 21 strategy: 22 fail-fast: false 23 matrix: 24 include: 25 - label: macos-amd64 26 os: macos-latest 27 target: x86_64-apple-darwin 28 - label: macos-aarch64 29 os: macos-latest 30 target: aarch64-apple-darwin 31 - label: linux-amd64 32 os: ubuntu-latest 33 target: x86_64-unknown-linux-gnu 34 # Native ARM Linux runner — no `cross`, no QEMU. Faster and 35 # matches production glibc more closely. 36 - label: linux-aarch64 37 os: ubuntu-24.04-arm 38 target: aarch64-unknown-linux-gnu 39 40 steps: 41 - name: Checkout 42 uses: actions/checkout@v4 43 with: 44 # On manual dispatch, build the tag's code rather than the branch 45 # the workflow was dispatched from. 46 ref: ${{ inputs.tag || github.ref }} 47 48 - name: Install Rust toolchain 49 uses: dtolnay/rust-toolchain@stable 50 with: 51 targets: ${{ matrix.target }} 52 53 - name: Cache cargo 54 uses: Swatinem/rust-cache@v2 55 with: 56 key: ${{ matrix.target }} 57 58 # cpal links against ALSA on Linux (macOS uses CoreAudio, no dev pkg needed). 59 - name: Install ALSA dev headers 60 if: startsWith(matrix.os, 'ubuntu') 61 run: | 62 sudo apt-get update 63 sudo apt-get install -y libasound2-dev pkg-config 64 65 - name: Build 66 run: cargo build --release --locked --target ${{ matrix.target }} --bin fin 67 68 - name: Package tarball 69 shell: bash 70 run: | 71 set -euo pipefail 72 if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 73 version="${{ inputs.tag }}" 74 else 75 version="${GITHUB_REF_NAME}" 76 fi 77 name="fin-${version}-${{ matrix.label }}" 78 src="target/${{ matrix.target }}/release/fin" 79 mkdir -p dist/staging 80 cp "$src" dist/staging/fin 81 cp README.md LICENSE dist/staging/ 82 tar -C dist/staging -czf "dist/${name}.tar.gz" . 83 (cd dist && shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256") 84 ls -lh dist 85 86 - name: Install dpkg-deb and rpmbuild 87 if: startsWith(matrix.os, 'ubuntu') 88 run: | 89 sudo apt-get update 90 sudo apt-get install -y dpkg-dev rpm 91 92 - name: Build .deb 93 if: startsWith(matrix.os, 'ubuntu') 94 shell: bash 95 run: | 96 set -euo pipefail 97 if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 98 version="${{ inputs.tag }}" 99 else 100 version="${GITHUB_REF_NAME}" 101 fi 102 version_no_v="${version#v}" 103 case "${{ matrix.label }}" in 104 linux-amd64) deb_arch=amd64 ;; 105 linux-aarch64) deb_arch=arm64 ;; 106 esac 107 pkg_root="dist/debian/${deb_arch}" 108 mkdir -p "$pkg_root/usr/local/bin" 109 install -m 0755 "target/${{ matrix.target }}/release/fin" "$pkg_root/usr/local/bin/fin" 110 sed -i "s/^Version: .*/Version: ${version_no_v}/" "$pkg_root/DEBIAN/control" 111 dpkg-deb --build --root-owner-group "$pkg_root" "dist/fin_${version_no_v}_${deb_arch}.deb" 112 113 - name: Build .rpm 114 if: matrix.label == 'linux-amd64' 115 shell: bash 116 run: | 117 set -euo pipefail 118 if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 119 version="${{ inputs.tag }}" 120 else 121 version="${GITHUB_REF_NAME}" 122 fi 123 version_no_v="${version#v}" 124 rpm_arch=amd64 125 rpm_target=x86_64 126 src_root="dist/rpm/${rpm_arch}" 127 mkdir -p "${src_root}/usr/local/bin" 128 install -m 0755 "target/${{ matrix.target }}/release/fin" "${src_root}/usr/local/bin/fin" 129 sed -i "s/^Version: .*/Version: ${version_no_v}/" "${src_root}/fin.spec" 130 131 rpm_top="$(pwd)/dist/rpmbuild" 132 mkdir -p "${rpm_top}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} 133 cp -r "${src_root}" "${rpm_top}/SOURCES/${rpm_arch}" 134 rpmbuild --define "_topdir ${rpm_top}" \ 135 --define "_sourcedir ${rpm_top}/SOURCES" \ 136 --target "${rpm_target}" \ 137 -bb "${src_root}/fin.spec" 138 find "${rpm_top}/RPMS" -name '*.rpm' -exec mv {} dist/ \; 139 140 - name: Checksum packages 141 if: startsWith(matrix.os, 'ubuntu') 142 shell: bash 143 run: | 144 set -euo pipefail 145 cd dist 146 for f in *.deb *.rpm; do 147 [ -e "$f" ] || continue 148 shasum -a 256 "$f" > "$f.sha256" 149 done 150 ls -lh 151 152 - name: Upload artifact 153 uses: actions/upload-artifact@v4 154 with: 155 name: ${{ matrix.label }} 156 path: | 157 dist/*.tar.gz* 158 dist/*.deb* 159 dist/*.rpm* 160 if-no-files-found: error 161 162 build-bsd: 163 name: ${{ matrix.label }} 164 runs-on: ubuntu-latest 165 strategy: 166 fail-fast: false 167 matrix: 168 include: 169 # cpal uses its ALSA backend on FreeBSD/NetBSD, so those need 170 # alsa-lib + pkgconf. On OpenBSD cpal falls back to the Null host 171 # (no sound-card output at runtime; Chromecast/DLNA still work), 172 # so no audio packages there. cmake is for aws-lc-sys (rustls 173 # crypto provider), which uses its CMake builder and the bundled 174 # "universal" bindings on the BSDs — no bindgen needed. mpv is a 175 # runtime-only external process, nothing to install at build time. 176 # 177 # aarch64 BSD builds run under emulation and take hours, so they 178 # live in release-bsd-aarch64.yml and attach their tarballs to the 179 # release after it's published — they never block this workflow. 180 - label: freebsd-amd64 181 os: freebsd 182 os_version: "14.3" 183 arch: x86-64 184 install_deps: sudo env ASSUME_ALWAYS_YES=YES pkg install -y rust alsa-lib pkgconf cmake 185 - label: netbsd-amd64 186 os: netbsd 187 os_version: "10.1" 188 arch: x86-64 189 install_deps: sudo pkgin -y update && sudo pkgin -y install rust alsa-lib pkgconf cmake 190 - label: openbsd-amd64 191 os: openbsd 192 os_version: "7.9" 193 arch: x86-64 194 install_deps: sudo pkg_add rust cmake 195 196 steps: 197 - name: Checkout 198 uses: actions/checkout@v4 199 with: 200 # On manual dispatch, build the tag's code rather than the branch 201 # the workflow was dispatched from. 202 ref: ${{ inputs.tag || github.ref }} 203 204 - name: Start ${{ matrix.os }} ${{ matrix.arch }} VM 205 uses: cross-platform-actions/action@v1.3.0 206 with: 207 operating_system: ${{ matrix.os }} 208 version: ${{ matrix.os_version }} 209 architecture: ${{ matrix.arch }} 210 shell: bash 211 memory: 8G 212 cpu_count: 4 213 214 - name: Build in VM 215 shell: cpa.sh {0} 216 run: | 217 set -euo pipefail 218 ${{ matrix.install_deps }} 219 case "$(uname -s)" in 220 NetBSD) 221 # pkg-config crate looks for `pkg-config`; pkgsrc ships `pkgconf`. 222 export PKG_CONFIG=/usr/pkg/bin/pkgconf 223 # NetBSD's runtime linker doesn't search /usr/pkg/lib by 224 # default, so embed an rpath for libasound. 225 export RUSTFLAGS="-C link-arg=-Wl,-rpath,/usr/pkg/lib" 226 ;; 227 OpenBSD) 228 # OpenBSD's default datasize ulimit is too low for rustc 229 ulimit -d "$(ulimit -H -d)" 2>/dev/null || true 230 ;; 231 esac 232 # Keep target/ outside the workspace so it isn't synced back 233 export CARGO_TARGET_DIR="$HOME/build" 234 cargo build --release --locked --bin fin 235 mkdir -p dist 236 cp "$CARGO_TARGET_DIR/release/fin" dist/fin 237 238 - name: Package tarball 239 shell: bash 240 run: | 241 set -euo pipefail 242 if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 243 version="${{ inputs.tag }}" 244 else 245 version="${GITHUB_REF_NAME}" 246 fi 247 name="fin-${version}-${{ matrix.label }}" 248 mkdir -p dist/staging 249 install -m 0755 dist/fin dist/staging/fin 250 cp README.md LICENSE dist/staging/ 251 tar -C dist/staging -czf "dist/${name}.tar.gz" . 252 (cd dist && shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256") 253 ls -lh dist 254 255 - name: Upload artifact 256 uses: actions/upload-artifact@v4 257 with: 258 name: ${{ matrix.label }} 259 path: dist/*.tar.gz* 260 if-no-files-found: error 261 262 release: 263 name: Publish GitHub release 264 needs: [build, build-bsd] 265 runs-on: ubuntu-latest 266 steps: 267 - name: Download all artifacts 268 uses: actions/download-artifact@v4 269 with: 270 path: dist 271 merge-multiple: true 272 273 - name: Show artifacts 274 run: ls -lh dist 275 276 - name: Create release 277 uses: softprops/action-gh-release@v2 278 with: 279 tag_name: ${{ github.event.inputs.tag || github.ref_name }} 280 files: dist/* 281 generate_release_notes: true 282 draft: false 283 prerelease: false 284 285 - name: Check Gemfury secrets 286 id: gemfury 287 env: 288 FURY_TOKEN: ${{ secrets.FURY_TOKEN }} 289 FURY_ACCOUNT: ${{ secrets.FURY_ACCOUNT }} 290 run: | 291 if [ -n "$FURY_TOKEN" ] && [ -n "$FURY_ACCOUNT" ]; then 292 echo "available=true" >> "$GITHUB_OUTPUT" 293 else 294 echo "available=false" >> "$GITHUB_OUTPUT" 295 echo "Gemfury secrets not set; skipping push" 296 fi 297 298 - name: Push .deb and .rpm to Gemfury 299 if: steps.gemfury.outputs.available == 'true' 300 env: 301 FURY_TOKEN: ${{ secrets.FURY_TOKEN }} 302 FURY_ACCOUNT: ${{ secrets.FURY_ACCOUNT }} 303 shell: bash 304 run: | 305 set -euo pipefail 306 shopt -s nullglob 307 for pkg in dist/*.deb dist/*.rpm; do 308 echo "Pushing $pkg to Gemfury (${FURY_ACCOUNT})" 309 curl -fsS -F package=@"${pkg}" \ 310 "https://${FURY_TOKEN}@push.fury.io/${FURY_ACCOUNT}/" 311 echo 312 done