Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/aur-bin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: aur-bin

on:
release:
types:
- published
workflow_dispatch:
inputs:
version:
description: Version to publish without the leading v
required: true
source_url:
description: Release archive URL
required: true
sha256:
description: SHA-256 checksum for the release archive
required: true

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Resolve release metadata
id: release
run: |
if [[ "${GITHUB_EVENT_NAME}" == "release" ]]; then
python - <<'PY' >> "$GITHUB_OUTPUT"
import json
import urllib.request
from pathlib import Path

event = json.loads(Path('${{ github.event_path }}').read_text())
tag_name = event['release']['tag_name']
version = tag_name.removeprefix('v')
tarball = None
checksum = None
for asset in event['release']['assets']:
name = asset['name']
if name == f'orators-{tag_name}-x86_64-unknown-linux-gnu.tar.gz':
tarball = asset['browser_download_url']
elif name == f'orators-{tag_name}-x86_64-unknown-linux-gnu.tar.gz.sha256':
checksum = asset['browser_download_url']
if tarball is None or checksum is None:
raise SystemExit('release assets are missing the expected archive or checksum')
with urllib.request.urlopen(checksum) as response:
sha256 = response.read().decode().split()[0]
print(f'version={version}')
print(f'source_url={tarball}')
print(f'sha256={sha256}')
PY
else
echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
echo "source_url=${{ inputs.source_url }}" >> "$GITHUB_OUTPUT"
echo "sha256=${{ inputs.sha256 }}" >> "$GITHUB_OUTPUT"
fi

- name: Prepare AUR SSH key
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
run: |
install -d -m 700 ~/.ssh
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur
chmod 600 ~/.ssh/aur
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
echo 'GIT_SSH_COMMAND=ssh -i ~/.ssh/aur -o IdentitiesOnly=yes' >> "$GITHUB_ENV"

- name: Render orators-bin package
run: |
./scripts/aur/render_orators_bin_pkgbuild.py \
--version "${{ steps.release.outputs.version }}" \
--source-url "${{ steps.release.outputs.source_url }}" \
--sha256 "${{ steps.release.outputs.sha256 }}" \
--output-dir /tmp/orators-bin

- name: Publish to AUR
env:
AUR_PACKAGER_NAME: ${{ secrets.AUR_PACKAGER_NAME }}
AUR_PACKAGER_EMAIL: ${{ secrets.AUR_PACKAGER_EMAIL }}
run: |
./scripts/aur/publish_aur_package.sh \
orators-bin \
/tmp/orators-bin \
"update: orators-bin ${{ steps.release.outputs.version }}"
43 changes: 43 additions & 0 deletions .github/workflows/aur-git.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: aur-git

on:
push:
branches:
- main
paths:
- 'packaging/aur/orators-git/**'
- 'packaging/systemd/user/oratorsd.service'
- '.github/workflows/aur-git.yml'
- 'scripts/aur/generate_srcinfo.sh'
- 'scripts/aur/publish_aur_package.sh'
Comment thread
OneNoted marked this conversation as resolved.
workflow_dispatch:

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Prepare AUR SSH key
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
run: |
install -d -m 700 ~/.ssh
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur
chmod 600 ~/.ssh/aur
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
echo 'GIT_SSH_COMMAND=ssh -i ~/.ssh/aur -o IdentitiesOnly=yes' >> "$GITHUB_ENV"

- name: Stage orators-git package
run: |
rsync -a --delete packaging/aur/orators-git/ /tmp/orators-git/

- name: Publish to AUR
env:
AUR_PACKAGER_NAME: ${{ secrets.AUR_PACKAGER_NAME }}
AUR_PACKAGER_EMAIL: ${{ secrets.AUR_PACKAGER_EMAIL }}
run: |
./scripts/aur/publish_aur_package.sh \
orators-git \
/tmp/orators-git \
"update: refresh orators-git packaging"
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ jobs:
run: cargo test --workspace
- name: Nix flake check
run: nix flake check
- name: Validate release and AUR packaging
run: ./scripts/validate_packaging.sh

57 changes: 57 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
github-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Verify tag matches workspace version
run: |
tag_version="${GITHUB_REF_NAME#v}"
workspace_version=$(python - <<'PY'
import json, subprocess
metadata = json.loads(subprocess.check_output([
'cargo', 'metadata', '--no-deps', '--format-version', '1'
], text=True))
for package in metadata['packages']:
if package['name'] == 'orators':
print(package['version'])
break
else:
raise SystemExit('could not resolve orators package version')
PY
)
if [[ "$tag_version" != "$workspace_version" ]]; then
echo "Tag version $tag_version does not match workspace version $workspace_version" >&2
exit 1
fi

- name: Build release archive
id: bundle
run: |
archive_path=$(./scripts/release/build-release-archive.sh --version "${GITHUB_REF_NAME#v}")
echo "archive_path=$archive_path" >> "$GITHUB_OUTPUT"
echo "checksum_path=${archive_path}.sha256" >> "$GITHUB_OUTPUT"

- name: Publish GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" \
"${{ steps.bundle.outputs.archive_path }}" \
"${{ steps.bundle.outputs.checksum_path }}" \
--title "orators ${GITHUB_REF_NAME#v}" \
--notes "Automated release for orators ${GITHUB_REF_NAME#v}."
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
/result
/.direnv


__pycache__/
*.pyc
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ nix develop
nix flake check
```

## Arch Linux / AUR

- `orators-bin`: prebuilt `x86_64` binaries from GitHub tagged releases
- `orators-git`: latest GitHub `main`, built locally by makepkg
- Both AUR variants currently depend on `bluez-alsa-git` for the BlueALSA runtime

Example install:

```bash
paru -S orators-bin
systemctl --user daemon-reload
systemctl --user enable --now oratorsd.service
oratorsctl install-system-backend
```

`oratorsctl install-user-service` remains useful for manual tarball installs, but packaged installs should prefer the shipped systemd user unit.

## Service Model

- `orators` opens the TUI
Expand Down
25 changes: 21 additions & 4 deletions crates/orators-linux/src/bluealsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use tokio::{process::Child, process::Command, sync::Mutex};

const PLAYER_RESTART_BACKOFF: Duration = Duration::from_secs(3);
const PLAYER_MAX_RESTARTS: u8 = 3;
const PLAYER_VOLUME_MODE: &str = "--volume=software";
pub const SYSTEM_BACKEND_UNIT: &str = "orators-bluealsad.service";
const TRUSTED_BLUEALSA_DIRS: &[&str] = &[
"/usr/libexec/orators/bluealsa",
Expand Down Expand Up @@ -207,15 +208,16 @@ impl BluealsaRuntime {
.context("missing active device address for BlueALSA playback")?;

let child = Command::new(&assets.bluealsa_aplay)
.arg(&address)
.args(player_args(&address))
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.with_context(|| {
format!(
"failed to start `{} {address}`",
assets.bluealsa_aplay.display()
"failed to start `{} {} {address}`",
assets.bluealsa_aplay.display(),
PLAYER_VOLUME_MODE
)
});

Expand Down Expand Up @@ -341,6 +343,10 @@ impl PlayerSupervisor {
}
}

fn player_args(address: &str) -> [&str; 2] {
[PLAYER_VOLUME_MODE, address]
}

#[cfg(test)]
fn find_binary_in_path(name: &str, path: &OsStr) -> Option<PathBuf> {
env::split_paths(path)
Expand Down Expand Up @@ -375,7 +381,10 @@ mod tests {
use orators_core::PlayerState;
use tempfile::tempdir;

use super::{BluealsaAssets, PLAYER_MAX_RESTARTS, PlayerSupervisor, find_binary_in_path};
use super::{
BluealsaAssets, PLAYER_MAX_RESTARTS, PLAYER_VOLUME_MODE, PlayerSupervisor,
find_binary_in_path, player_args,
};

#[test]
fn finds_executable_in_path() {
Expand Down Expand Up @@ -452,4 +461,12 @@ mod tests {
assert_eq!(supervisor.restart_attempts, PLAYER_MAX_RESTARTS);
assert!(supervisor.next_restart_at.is_some());
}

#[test]
fn player_launch_forces_software_volume_mode() {
assert_eq!(
player_args("AA:BB:CC:DD:EE:FF"),
[PLAYER_VOLUME_MODE, "AA:BB:CC:DD:EE:FF"]
);
}
}
2 changes: 1 addition & 1 deletion crates/orators/src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ async fn ensure_daemon_running(connection: &Connection) -> Result<()> {
SystemdUserRuntime
.start_orators_service()
.await
.context("failed to start oratorsd.service; run `oratorsctl install-user-service` first")?;
.context("failed to start oratorsd.service; enable the packaged unit with `systemctl --user enable --now oratorsd.service` or run `oratorsctl install-user-service` first")?;

for _ in 0..12 {
if bus
Expand Down
Loading
Loading