diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53f8242..7f30638 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,29 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "cargo" directory: "/" schedule: interval: "weekly" + groups: + egui-stack: + patterns: + - "egui*" + - "eframe" + + serde-stack: + patterns: + - "serde*" + + tokio-stack: + patterns: + - "tokio*" + - "reqwest" + + clap-stack: + patterns: + - "clap*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc68b83..314880d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: jobs: - linux-build: + build-and-package: strategy: matrix: include: @@ -43,22 +43,19 @@ jobs: - name: Setup Rust toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: 1.96.0 + toolchain: stable + cache: false - name: Rust Cache uses: swatinem/rust-cache@v2 + with: + prefix-key: "v3-${{ matrix.runner }}" - name: Run tests run: cargo test --locked - - name: Build all binaries (debug-speed compilation into target/release) - env: - CARGO_PROFILE_RELEASE_OPT_LEVEL: 0 - CARGO_PROFILE_RELEASE_DEBUG: "true" - CARGO_PROFILE_RELEASE_STRIP: "false" - CARGO_PROFILE_RELEASE_LTO: "false" - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 256 - run: cargo build --release --locked + - name: Build all binaries + run: cargo build --locked - name: Extract all binary names id: cargo-meta @@ -70,7 +67,7 @@ jobs: echo "$BIN_NAMES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - - name: Package all binaries into one archive + - name: Package ZIP archive shell: bash run: | set -euo pipefail @@ -81,7 +78,7 @@ jobs: FILES=() while IFS= read -r BIN; do [ -z "$BIN" ] && continue - FILES+=("target/release/$BIN") + FILES+=("target/debug/$BIN") done <<< "${{ steps.cargo-meta.outputs.bin_names }}" if [ "${#FILES[@]}" -eq 0 ]; then @@ -89,67 +86,15 @@ jobs: exit 1 fi - for f in "${FILES[@]}"; do - if [ ! -f "$f" ]; then - echo "Error: expected binary not found: $f" >&2 - exit 1 - fi - echo "Will add: $f" - done - zip -j "$ARCHIVE_NAME" "${FILES[@]}" - - name: Upload archive as artifact + - name: Upload ZIP artifact uses: actions/upload-artifact@v4 with: name: archive-${{ matrix.arch }} path: pwsp-*.zip retention-days: 7 - deb-build: - strategy: - matrix: - include: - - arch: x64 - runner: ubuntu-latest - - arch: arm64 - runner: ubuntu-24.04-arm - fail-fast: false - runs-on: ${{ matrix.runner }} - - steps: - - name: Install apt deps (dev-libs) - run: | - sudo apt-get update - sudo apt-get install -y \ - libpipewire-0.3-dev \ - libclang-dev \ - libasound2-dev \ - libdbus-1-dev \ - pkg-config - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: 1.96.0 - - - name: Rust Cache - uses: swatinem/rust-cache@v2 - - - name: Build all binaries (debug-speed compilation into target/release) - env: - CARGO_PROFILE_RELEASE_OPT_LEVEL: 0 - CARGO_PROFILE_RELEASE_DEBUG: "true" - CARGO_PROFILE_RELEASE_STRIP: "false" - CARGO_PROFILE_RELEASE_LTO: "false" - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 256 - run: cargo build --release --locked - - name: Cache cargo-deb id: cache-cargo-deb uses: actions/cache@v4 @@ -161,14 +106,14 @@ jobs: if: steps.cache-cargo-deb.outputs.cache-hit != 'true' run: cargo install --locked cargo-deb - - name: Create .deb package (debug binaries from target/release) + - name: Create .deb package shell: bash run: | set -euo pipefail export PATH="$HOME/.cargo/bin:$PATH" - cargo-deb -p pwsp-gui --no-build --no-strip + cargo-deb -p pwsp-gui --no-build --no-strip --profile dev - - name: Upload .deb(s) as artifacts + - name: Upload .deb artifact uses: actions/upload-artifact@v4 with: name: deb-packages-${{ matrix.arch }} diff --git a/.github/workflows/dependabot-flatpak.yml b/.github/workflows/dependabot-flatpak.yml new file mode 100644 index 0000000..2e63141 --- /dev/null +++ b/.github/workflows/dependabot-flatpak.yml @@ -0,0 +1,36 @@ +name: Dependabot Flatpak Cargo Sources + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - "Cargo.lock" + +jobs: + update-cargo-sources: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + contents: write # Needed to push back to the branch + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install dependencies + run: pip install aiohttp toml + + - name: Generate cargo-sources.json + run: bash ./scripts/generate-sources.sh + + - name: Commit and push changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "chore(flatpak): automatically update cargo-sources.json" + file_pattern: "packages/flatpak/cargo-sources.json" diff --git a/.github/workflows/flatter.yml b/.github/workflows/flatter.yml index 2ab39b6..19c3852 100644 --- a/.github/workflows/flatter.yml +++ b/.github/workflows/flatter.yml @@ -21,9 +21,17 @@ on: default: "stable" jobs: - flatter-x64: - name: Flatter (x86_64) - runs-on: ubuntu-latest + build-flatpak: + name: Build Flatpak (${{ matrix.arch }}) + strategy: + matrix: + include: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: ubuntu-24.04-arm + fail-fast: false + runs-on: ${{ matrix.runner }} permissions: contents: read container: @@ -56,6 +64,8 @@ jobs: - name: Modify Manifest run: | + sed -i '/^branch:/d' packages/flatpak/ru.arabianq.pwsp.yaml + sed -i '/^default-branch:/d' packages/flatpak/ru.arabianq.pwsp.yaml echo "branch: ${{ steps.set_branch.outputs.branch }}" >> packages/flatpak/ru.arabianq.pwsp.yaml echo "default-branch: ${{ steps.set_branch.outputs.branch }}" >> packages/flatpak/ru.arabianq.pwsp.yaml @@ -69,26 +79,28 @@ jobs: with: files: packages/flatpak/ru.arabianq.pwsp.yaml gpg-sign: ${{ steps.gpg.outputs.fingerprint }} - upload-bundles: false + upload-bundles: true upload-pages-artifact: false - arch: x86_64 + arch: ${{ matrix.arch }} cache: true - flatter-arm64: - name: Flatter (aarch64) - needs: flatter-x64 - runs-on: ubuntu-24.04-arm - permissions: - contents: read + prepare-repo: + name: Prepare OSTree Repo + needs: build-flatpak + runs-on: ubuntu-latest + concurrency: gh-pages-deploy container: image: ghcr.io/andyholmes/flatter/freedesktop:25.08 options: --privileged + permissions: + contents: write # Needed to upload files to GitHub Release steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Download Flatpak Bundles + uses: actions/download-artifact@v4 with: - ref: ${{ inputs.tag_name || github.ref }} + path: bundles + pattern: flatpak-bundle-* - name: Setup GPG id: gpg @@ -97,49 +109,40 @@ jobs: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - - name: Set Default Branch - id: set_branch + - name: Create OSTree Repo and Prepare Release Bundles run: | - if [ "${{ github.event_name }}" == "release" ]; then - echo "branch=stable" >> $GITHUB_OUTPUT - elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - echo "branch=${{ inputs.build_branch }}" >> $GITHUB_OUTPUT - else - echo "branch=nightly" >> $GITHUB_OUTPUT - fi + mkdir -p pages-repo release-bundles + ostree init --mode=archive-z2 --repo=pages-repo - - name: Modify Manifest - run: | - echo "branch: ${{ steps.set_branch.outputs.branch }}" >> packages/flatpak/ru.arabianq.pwsp.yaml - echo "default-branch: ${{ steps.set_branch.outputs.branch }}" >> packages/flatpak/ru.arabianq.pwsp.yaml + for dir in bundles/flatpak-bundle-*; do + arch=$(basename "$dir" | sed 's/flatpak-bundle-//') - - name: Install SDK Extensions - run: | - flatpak install -y flathub org.freedesktop.Sdk.Extension.rust-stable//25.08 - flatpak install -y flathub org.freedesktop.Sdk.Extension.llvm20//25.08 + # Find the flatpak file inside the dir + bundle=$(find "$dir" -name "*.flatpak" | head -n 1) - - name: Build Flatpak - uses: andyholmes/flatter@main + if [ -n "$bundle" ]; then + echo "Importing $bundle for architecture $arch..." + flatpak build-import-bundle pages-repo "$bundle" + + # Copy with a unique name for GitHub Release + cp "$bundle" "release-bundles/ru.arabianq.pwsp-${arch}.flatpak" + fi + done + + # Update summary and sign the repository + flatpak build-update-repo --generate-static-deltas --gpg-sign=${{ steps.gpg.outputs.fingerprint }} pages-repo + + - name: Deploy Flatpak Repo to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 with: - files: packages/flatpak/ru.arabianq.pwsp.yaml - gpg-sign: ${{ steps.gpg.outputs.fingerprint }} - upload-bundles: false - upload-pages-artifact: true - arch: aarch64 - cache: true + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./pages-repo + destination_dir: . + keep_files: true - deploy: - name: Deploy to GitHub Pages - runs-on: ubuntu-latest - needs: flatter-arm64 - permissions: - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + - name: Upload bundles to GitHub Release + if: github.event_name == 'release' + uses: softprops/action-gh-release@v2 + with: + files: release-bundles/*.flatpak + tag_name: ${{ github.event.release.tag_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 970c785..e0bfdf5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,46 +23,32 @@ jobs: id: tag run: | set -euo pipefail - INPUT_TAG="${{ github.event.inputs.tag || '' }}" - if [ -n "$INPUT_TAG" ]; then - echo "Using input tag: $INPUT_TAG" - echo "tag=$INPUT_TAG" >> $GITHUB_OUTPUT - exit 0 - fi - EVENT_TAG="${{ github.event.release.tag_name || '' }}" - if [ -n "$EVENT_TAG" ]; then - echo "Using event tag: $EVENT_TAG" + + if [ -n "$INPUT_TAG" ]; then + echo "tag=$INPUT_TAG" >> $GITHUB_OUTPUT + elif [ -n "$EVENT_TAG" ]; then echo "tag=$EVENT_TAG" >> $GITHUB_OUTPUT - exit 0 - fi - - if [[ "${GITHUB_REF:-}" =~ ^refs/tags/(.+)$ ]]; then - echo "Using GITHUB_REF tag: ${BASH_REMATCH[1]}" + elif [[ "${GITHUB_REF:-}" =~ ^refs/tags/(.+)$ ]]; then echo "tag=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT - exit 0 + else + LATEST_JSON=$(curl -sSf -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${{ github.repository }}/releases/latest" || true) + TAG_NAME=$(echo "$LATEST_JSON" | jq -r '.tag_name // empty') + if [ -n "$TAG_NAME" ]; then + echo "tag=$TAG_NAME" >> $GITHUB_OUTPUT + else + echo "tag=" >> $GITHUB_OUTPUT + fi fi - echo "No tag in input/event/GITHUB_REF — querying latest release via API..." - LATEST_JSON=$(curl -sSf -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${{ github.repository }}/releases/latest" || true) - TAG_NAME=$(echo "$LATEST_JSON" | jq -r '.tag_name // empty') - if [ -n "$TAG_NAME" ]; then - echo "Found latest release tag: $TAG_NAME" - echo "tag=$TAG_NAME" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "No tag found" - echo "tag=" >> $GITHUB_OUTPUT - - name: Fail if no tag determined if: ${{ steps.tag.outputs.tag == '' }} run: | - echo "ERROR: No tag determined. Provide a tag when running manually or ensure a release exists." + echo "ERROR: No tag determined." exit 1 - linux-release: + build-release: needs: prepare strategy: matrix: @@ -75,7 +61,7 @@ jobs: runs-on: ${{ matrix.runner }} steps: - - name: Install apt deps (jq/zip + dev-libs) + - name: Install apt deps run: | sudo apt-get update sudo apt-get install -y \ @@ -95,10 +81,13 @@ jobs: - name: Setup Rust toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: 1.96.0 + toolchain: stable + cache: false - name: Rust Cache uses: swatinem/rust-cache@v2 + with: + prefix-key: "v3-${{ matrix.runner }}" - name: Extract all binary names id: cargo-meta @@ -113,13 +102,12 @@ jobs: - name: Build all release binaries run: cargo build --release --locked - - name: Package all binaries into one archive + - name: Package ZIP archive shell: bash run: | set -euo pipefail TAG="${{ needs.prepare.outputs.tag }}" ARCHIVE_NAME="pwsp-${TAG}-linux-${{ matrix.arch }}.zip" - echo "Creating archive: $ARCHIVE_NAME" FILES=() while IFS= read -r BIN; do @@ -127,68 +115,15 @@ jobs: FILES+=("target/release/$BIN") done <<< "${{ steps.cargo-meta.outputs.bin_names }}" - if [ "${#FILES[@]}" -eq 0 ]; then - echo "Error: no binaries were discovered via cargo metadata." >&2 - exit 1 - fi - - for f in "${FILES[@]}"; do - if [ ! -f "$f" ]; then - echo "Error: expected binary not found: $f" >&2 - exit 1 - fi - echo "Will add: $f" - done - zip -j "$ARCHIVE_NAME" "${FILES[@]}" - - name: Upload zip archive + - name: Upload ZIP artifact uses: actions/upload-artifact@v4 with: name: zip-archive-${{ matrix.arch }} path: pwsp-*.zip retention-days: 1 - deb-release: - needs: prepare - strategy: - matrix: - include: - - arch: x64 - runner: ubuntu-latest - - arch: arm64 - runner: ubuntu-24.04-arm - fail-fast: false - runs-on: ${{ matrix.runner }} - - steps: - - name: Install apt deps (dev-libs) - run: | - sudo apt-get update - sudo apt-get install -y \ - libpipewire-0.3-dev \ - libclang-dev \ - libasound2-dev \ - libdbus-1-dev \ - pkg-config - - - name: Checkout code at tag - uses: actions/checkout@v4 - with: - ref: ${{ needs.prepare.outputs.tag }} - fetch-depth: 0 - - - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: 1.96.0 - - - name: Rust Cache - uses: swatinem/rust-cache@v2 - - - name: Build all release binaries - run: cargo build --release --locked - - name: Cache cargo-deb id: cache-cargo-deb uses: actions/cache@v4 @@ -200,14 +135,14 @@ jobs: if: steps.cache-cargo-deb.outputs.cache-hit != 'true' run: cargo install --locked cargo-deb - - name: Create .deb package (release binaries) + - name: Create .deb package shell: bash run: | set -euo pipefail export PATH="$HOME/.cargo/bin:$PATH" cargo-deb -p pwsp-gui --no-build - - name: Upload deb package + - name: Upload DEB artifact uses: actions/upload-artifact@v4 with: name: deb-package-${{ matrix.arch }} @@ -215,32 +150,14 @@ jobs: retention-days: 1 publish-release: - needs: [prepare, linux-release, deb-release] + needs: [prepare, build-release] runs-on: ubuntu-latest steps: - - name: Download zip archive (x64) + - name: Download all artifacts uses: actions/download-artifact@v4 with: - name: zip-archive-x64 - path: ./dist - - - name: Download zip archive (arm64) - uses: actions/download-artifact@v4 - with: - name: zip-archive-arm64 - path: ./dist - - - name: Download deb package (x64) - uses: actions/download-artifact@v4 - with: - name: deb-package-x64 - path: ./dist - - - name: Download deb package (arm64) - uses: actions/download-artifact@v4 - with: - name: deb-package-arm64 path: ./dist + merge-multiple: true - name: Upload artifacts to Release uses: softprops/action-gh-release@v2 @@ -251,6 +168,10 @@ jobs: ./dist/pwsp-*.zip ./dist/*.deb + trigger-copr: + needs: prepare + runs-on: ubuntu-latest + steps: - name: Install copr-cli run: pip install copr-cli @@ -265,3 +186,75 @@ jobs: --commit ${{ needs.prepare.outputs.tag }} \ --spec packages/rpm/pwsp.spec \ arabianq/pwsp + + build-apt-repo: + needs: [prepare, build-release] + runs-on: ubuntu-latest + concurrency: gh-pages-deploy + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup GPG + id: gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + + - name: Download DEB artifacts + uses: actions/download-artifact@v4 + with: + pattern: deb-package-* + path: debs + merge-multiple: true + + - name: Checkout existing gh-pages + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages-branch + continue-on-error: true + + - name: Generate APT Repository + run: | + sudo apt-get update + sudo apt-get install -y reprepro + + mkdir -p apt-repo/conf + + cat < apt-repo/conf/distributions + Origin: arabianq + Label: PipeWire Soundpad + Codename: stable + Architectures: amd64 arm64 + Components: main + Description: APT Repository for PWSP + SignWith: ${{ steps.gpg.outputs.fingerprint }} + EOF + + # Export public key for users to download + gpg --armor --export ${{ steps.gpg.outputs.fingerprint }} > apt-repo/pubkey.gpg + + if [ -d "gh-pages-branch/apt" ]; then + cp -r gh-pages-branch/apt/* apt-repo/ || true + fi + + cd apt-repo + + for deb in ../debs/*.deb; do + if [ -f "$deb" ]; then + echo "Adding $deb to APT repository..." + reprepro includedeb stable "$deb" + fi + done + + - name: Deploy APT Repo to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./apt-repo + destination_dir: apt + keep_files: true diff --git a/Cargo.toml b/Cargo.toml index e0c63ea..e70607c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,10 +76,15 @@ egui_dnd = "0.16.0" reqwest = "0.13.4" percent-encoding = "2.3.2" +[profile.dev] + +[profile.dev.package."*"] +opt-level = 3 +debug = false + [profile.release] strip = true lto = true codegen-units = 1 opt-level = "z" panic = "abort" - diff --git a/README.md b/README.md index ced8d32..7fb61fc 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,19 @@ paru -S pwsp-bin # or 'pwsp' to build from source ``` **Debian / Ubuntu:** -Download pre-built `.deb` packages or standalone binaries from the [Releases page](https://github.com/arabianq/pipewire-soundpad/releases). +We provide an official APT repository for automatic updates: +```bash +# 1. Download the public key +wget -O- https://arabianq.github.io/pipewire-soundpad/apt/pubkey.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/pwsp.gpg + +# 2. Add the repository to your sources +echo "deb [signed-by=/etc/apt/keyrings/pwsp.gpg] https://arabianq.github.io/pipewire-soundpad/apt/ stable main" | sudo tee /etc/apt/sources.list.d/pwsp.list + +# 3. Update and install +sudo apt update +sudo apt install pwsp-gui +``` +*(Alternatively, you can manually download `.deb` packages from the [Releases page](https://github.com/arabianq/pipewire-soundpad/releases)).* ### 🦀 Cargo / Source Build ```bash