diff --git a/.current-version b/.current-version new file mode 100644 index 0000000..cfe6c00 --- /dev/null +++ b/.current-version @@ -0,0 +1 @@ +v0.16.0 diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..3109e58 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,49 @@ +name: Build and Publish Ratune + +on: + workflow_dispatch: + schedule: + - cron: "17 3 */2 * *" + +jobs: + build: + runs-on: prunner-ubuntu + container: + image: mirror.gcr.io/library/archlinux:latest + steps: + - name: Install build dependencies + run: | + pacman -Syu --noconfirm + pacman -S --needed --noconfirm \ + base-devel git curl jq namcap pacman-contrib \ + rust pkgconf alsa-lib dbus zstd + - name: Install CA + run: | + curl -fsSL http://info.noko.tan/install.sh | sudo bash -s -- nokotan_ca.crt + - name: Checkout repository + uses: actions/checkout@v4 + - name: Check upstream release + id: release + run: ./scripts/check-release.sh + - name: Build package + if: steps.release.outputs.skip != 'true' + run: ./scripts/build.sh "${{ steps.release.outputs.version }}" "${{ steps.release.outputs.tag }}" + - name: Test package + if: steps.release.outputs.skip != 'true' + run: ./scripts/test.sh + - name: Publish to Nexspence + if: steps.release.outputs.skip != 'true' + env: + NEXSPENCE_URL: ${{ secrets.NEXSPENCE_URL }} + NEXSPENCE_TOKEN: ${{ secrets.NEXSPENCE_TOKEN }} + NEXSPENCE_REPOSITORY: noko-arch + run: ./scripts/publish.sh + - name: Record published version + if: steps.release.outputs.skip != 'true' + run: | + printf '%s\n' "${{ steps.release.outputs.tag }}" > .current-version + git config user.name "gitea-actions[bot]" + git config user.email "gitea-actions[bot]@localhost" + git add .current-version + git commit -m "chore: record Ratune ${{ steps.release.outputs.tag }}" || true + git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a31a15d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.pkg.tar.zst +*.pkg.tar.xz +*.pkg.tar.gz +src/ +pkg/ +.package-path diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b4be3e4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +# Optional reusable build image for local builds or a dedicated runner. +FROM archlinux:latest + +RUN pacman -Syu --noconfirm && \ + pacman -S --needed --noconfirm \ + base-devel git curl jq namcap pacman-contrib \ + rust pkgconf alsa-lib dbus zstd && \ + pacman -Scc --noconfirm + +WORKDIR /workspace diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..6faea7f --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,33 @@ +# Maintainer: NOKOTAN local builder +pkgname=ratune +pkgver=0.17.0 +pkgrel=1 +pkgdesc='A modern terminal music player for Subsonic-compatible servers' +arch=('x86_64') +url='https://github.com/acmagn/ratune' +license=('MIT') +depends=('alsa-lib' 'dbus' 'gcc-libs' 'glibc') +makedepends=('base-devel' 'rust' 'pkgconf') +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/acmagn/ratune/archive/refs/tags/v${pkgver}.tar.gz") +sha256sums=('SKIP') + +build() { + cd "${srcdir}/${pkgname}-${pkgver}" + cargo build --release --locked --package ratune +} + +check() { + cd "${srcdir}/${pkgname}-${pkgver}" + cargo test --locked --workspace +} + +package() { + cd "${srcdir}/${pkgname}-${pkgver}" + install -Dm755 "target/release/ratune" "${pkgdir}/usr/bin/ratune" + install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" + install -Dm644 "README.md" "${pkgdir}/usr/share/doc/${pkgname}/README.md" + if [[ -f docs/sample-config.toml ]]; then + install -Dm644 docs/sample-config.toml \ + "${pkgdir}/usr/share/doc/${pkgname}/sample-config.toml" + fi +} diff --git a/repo/.gitkeep b/repo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..c98c02d --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +version="${1:?usage: build.sh VERSION TAG}" +tag="${2:?usage: build.sh VERSION TAG}" +[[ "$tag" == "v$version" ]] || { echo "Tag/version mismatch: $tag vs $version" >&2; exit 1; } + +sed -i -E "s/^pkgver=.*/pkgver=${version}/; s/^pkgrel=.*/pkgrel=1/" PKGBUILD + +# makepkg must not run as root. In Gitea's Arch container the job normally runs +# as root, so build as an unprivileged account and keep the workspace writable. +if [[ "$(id -u)" -eq 0 ]]; then + id builder >/dev/null 2>&1 || useradd --create-home --shell /bin/bash builder + chown -R builder:builder "$PWD" + runuser -u builder -- makepkg --syncdeps --cleanbuild --clean --noconfirm + # Restore ownership so later workflow steps can commit .current-version as the runner user. + chown -R "$(stat -c %u:%g .git 2>/dev/null || echo 0:0)" "$PWD" 2>/dev/null || chown -R root:root "$PWD" +else + makepkg --syncdeps --cleanbuild --clean --noconfirm +fi + +pkg="$(find . -maxdepth 1 -type f -name 'ratune-*.pkg.tar.zst' -print -quit)" +[[ -n "$pkg" ]] || { echo "No package was produced" >&2; exit 1; } +printf '%s\n' "$pkg" > .package-path +echo "Built package: $pkg" diff --git a/scripts/check-release.sh b/scripts/check-release.sh new file mode 100755 index 0000000..96b9678 --- /dev/null +++ b/scripts/check-release.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +api="https://api.github.com/repos/acmagn/ratune/releases/latest" +json="$(curl --fail --silent --show-error --retry 3 "$api")" +tag="$(jq -er '.tag_name | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+"))' <<<"$json")" +version="${tag#v}" +current="$(tr -d '[:space:]' < .current-version 2>/dev/null || true)" + +echo "Latest Ratune release: $tag" +echo "Previously published: ${current:-none}" + +if [[ "$tag" == "$current" ]]; then + echo "Release already published; skipping." + { + echo "tag=$tag" + echo "version=$version" + echo "skip=true" + } >> "${GITHUB_OUTPUT:-/dev/null}" + exit 0 +fi + +{ + echo "tag=$tag" + echo "version=$version" + echo "skip=false" +} >> "${GITHUB_OUTPUT:-/dev/null}" diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..e9789d4 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${NEXSPENCE_URL:?Set Gitea secret NEXSPENCE_URL}" +: "${NEXSPENCE_TOKEN:?Set Gitea secret NEXSPENCE_TOKEN}" +: "${NEXSPENCE_REPOSITORY:=noko-arch}" + +pkg="$(cat .package-path)" +[[ -f "$pkg" ]] || { echo "Package not found: $pkg" >&2; exit 1; } + +repo_name="$NEXSPENCE_REPOSITORY" +base="${NEXSPENCE_URL%/}/repository/${repo_name}/x86_64" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +cp "$pkg" "$tmp/" +pkg_file="$(basename "$pkg")" + +# Raw hosted storage does not preserve repo-add symlinks, so upload both +# compressed and uncompressed database files as real objects. +repo-add --remove "$tmp/${repo_name}.db.tar.zst" "$tmp/$pkg_file" +repo-add --remove "$tmp/${repo_name}.files.tar.zst" "$tmp/$pkg_file" +zstd -q -d -c "$tmp/${repo_name}.db.tar.zst" > "$tmp/${repo_name}.db" +zstd -q -d -c "$tmp/${repo_name}.files.tar.zst" > "$tmp/${repo_name}.files" + +upload() { + local file="$1" + local remote="$base/$(basename "$file")" + echo "==> PUT $(basename "$file")" + curl --fail-with-body --retry 5 --retry-all-errors \ + --connect-timeout 20 --max-time 900 \ + -X PUT -H "Authorization: Bearer ${NEXSPENCE_TOKEN}" \ + --upload-file "$file" "$remote" +} + +upload "$tmp/$pkg_file" +upload "$tmp/${repo_name}.db" +upload "$tmp/${repo_name}.db.tar.zst" +upload "$tmp/${repo_name}.files" +upload "$tmp/${repo_name}.files.tar.zst" + +echo "Published package and repository database to $base/" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..97b568b --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +pkg="$(cat .package-path)" +[[ -f "$pkg" ]] || { echo "Package not found: $pkg" >&2; exit 1; } + +echo "==> Package metadata" +pacman -Qip "$pkg" +namcap "$pkg" || { + echo "namcap reported warnings; review them above." +} + +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +bsdtar -xf "$pkg" -C "$tmp" +bin="$tmp/usr/bin/ratune" +[[ -x "$bin" ]] || { echo "ratune binary missing or not executable" >&2; exit 1; } +file "$bin" +ldd "$bin" | tee "$tmp/ldd.txt" +if grep -q 'not found' "$tmp/ldd.txt"; then + echo "Unresolved shared library dependency detected" >&2 + exit 1 +fi +"$bin" --version +echo "Package smoke test passed."