diff --git a/.github/workflows/deploy_commit.yaml b/.github/workflows/deploy_commit.yaml index 07ece86..145cb06 100644 --- a/.github/workflows/deploy_commit.yaml +++ b/.github/workflows/deploy_commit.yaml @@ -9,8 +9,12 @@ on: jobs: check_and_format: - name: cargo check and clippy - runs-on: ubuntu-24.04 + name: cargo check and clippy (${{ matrix.arch }}) + strategy: + fail-fast: false + matrix: + arch: [x86_64, aarch64] + runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} steps: - run: sudo apt-get update - run: sudo apt-get install -y pkg-config libglib2.0-dev libgtk-4-dev libgtk-3-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libadwaita-1-dev diff --git a/.github/workflows/deploy_release.yaml b/.github/workflows/deploy_release.yaml index e5cd550..1e47176 100644 --- a/.github/workflows/deploy_release.yaml +++ b/.github/workflows/deploy_release.yaml @@ -8,10 +8,14 @@ on: jobs: check_and_format: name: cargo check and clippy - runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + arch: [x86_64, aarch64] + runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} steps: - run: sudo apt-get update - - run: sudo apt-get install -y pkg-config libglib2.0-dev libgtk-4-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libadwaita-1-dev + - run: sudo apt-get install -y pkg-config libglib2.0-dev libgtk-4-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libadwaita-1-dev - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: @@ -20,15 +24,19 @@ jobs: - run: cargo clippy build_and_distropack: - name: build release + distropack upload - runs-on: ubuntu-24.04 + name: build release + distropack upload (${{ matrix.arch }}) + strategy: + fail-fast: false + matrix: + arch: [x86_64, aarch64] + runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} needs: - check_and_format steps: - uses: actions/checkout@v4 - run: sudo apt-get update - - run: sudo apt-get install -y pkg-config libglib2.0-dev libgtk-4-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libadwaita-1-dev + - run: sudo apt-get install -y pkg-config libglib2.0-dev libgtk-4-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libadwaita-1-dev - uses: actions-rust-lang/setup-rust-toolchain@v1 with: @@ -57,7 +65,7 @@ jobs: uses: distropack/distropack-action@v1.0 with: api-token: ${{ secrets.DISTROPACK_API_TOKEN }} - package-id: f6fee267-5e8e-4fe1-b4b1-f724736d598d + package-id: ${{ matrix.arch == 'aarch64' && secrets.DISTROPACK_DAEMON_AARCH64 || 'f6fee267-5e8e-4fe1-b4b1-f724736d598d' }} version: ${{ steps.version.outputs.version }} files: | { @@ -68,7 +76,7 @@ jobs: uses: distropack/distropack-action@v1.0 with: api-token: ${{ secrets.DISTROPACK_API_TOKEN }} - package-id: 9668ac33-5166-4952-a364-24d3dbc5abeb + package-id: ${{ matrix.arch == 'aarch64' && secrets.DISTROPACK_GTK_AARCH64 || '9668ac33-5166-4952-a364-24d3dbc5abeb' }} version: ${{ steps.version.outputs.version }} files: | { @@ -79,7 +87,7 @@ jobs: uses: distropack/distropack-action@v1.0 with: api-token: ${{ secrets.DISTROPACK_API_TOKEN }} - package-id: 019afefc-abbc-7ce1-8466-53d0f351c347 + package-id: ${{ matrix.arch == 'aarch64' && secrets.DISTROPACK_TRAY_AARCH64 || '019afefc-abbc-7ce1-8466-53d0f351c347' }} version: ${{ steps.version.outputs.version }} files: | { diff --git a/ci/gen_daemon_pkgbuild.py b/ci/gen_daemon_pkgbuild.py index a5994b0..54e6394 100644 --- a/ci/gen_daemon_pkgbuild.py +++ b/ci/gen_daemon_pkgbuild.py @@ -15,7 +15,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="The core daemon for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url="{url}" license=('MIT') diff --git a/ci/gen_daemon_pkgbuild_git.py b/ci/gen_daemon_pkgbuild_git.py index a50370b..37ecb3a 100644 --- a/ci/gen_daemon_pkgbuild_git.py +++ b/ci/gen_daemon_pkgbuild_git.py @@ -20,7 +20,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="The core daemon for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url="{url}" license=('MIT') diff --git a/ci/gen_gtk_pkgbuild.py b/ci/gen_gtk_pkgbuild.py index bc1ee0b..2c2142f 100644 --- a/ci/gen_gtk_pkgbuild.py +++ b/ci/gen_gtk_pkgbuild.py @@ -15,7 +15,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A gtk frontend for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/ci/gen_gtk_pkgbuild_git.py b/ci/gen_gtk_pkgbuild_git.py index 0b9d852..72b13f5 100644 --- a/ci/gen_gtk_pkgbuild_git.py +++ b/ci/gen_gtk_pkgbuild_git.py @@ -20,7 +20,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A gtk frontend for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/ci/gen_tray_pkgbuild.py b/ci/gen_tray_pkgbuild.py index 7ec5b2a..fba2d44 100644 --- a/ci/gen_tray_pkgbuild.py +++ b/ci/gen_tray_pkgbuild.py @@ -15,7 +15,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A system tray item for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/ci/gen_tray_pkgbuild_git.py b/ci/gen_tray_pkgbuild_git.py index de42ba4..e82274d 100644 --- a/ci/gen_tray_pkgbuild_git.py +++ b/ci/gen_tray_pkgbuild_git.py @@ -20,7 +20,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A system tray item for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/ci/gen_webview_pkgbuild.py b/ci/gen_webview_pkgbuild.py index 76bff74..10290b0 100644 --- a/ci/gen_webview_pkgbuild.py +++ b/ci/gen_webview_pkgbuild.py @@ -15,7 +15,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A Web Renderer frontend for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/ci/gen_webview_pkgbuild_git.py b/ci/gen_webview_pkgbuild_git.py index 9970279..37cf60f 100644 --- a/ci/gen_webview_pkgbuild_git.py +++ b/ci/gen_webview_pkgbuild_git.py @@ -20,7 +20,7 @@ def create_pkgbuild(pkgname, pkgver, url): pkgver={pkgver} pkgrel=1 pkgdesc="A Web Renderer frontend for Power Options, a blazingly fast power management solution." -arch=('x86_64') +arch=('x86_64' 'aarch64') url={url} license=('MIT') diff --git a/crates/power-daemon/src/lib.rs b/crates/power-daemon/src/lib.rs index 98d8333..6194aa6 100644 --- a/crates/power-daemon/src/lib.rs +++ b/crates/power-daemon/src/lib.rs @@ -233,7 +233,7 @@ impl Instance { fs::remove_file( self.profiles_path - .join(format!("{}.toml", &profile_to_remove_name)), + .join(format!("{}.toml", profile_to_remove_name)), ) .expect("Could not remove profile file"); diff --git a/crates/power-daemon/src/profile.rs b/crates/power-daemon/src/profile.rs index 7d6ef59..9e97149 100644 --- a/crates/power-daemon/src/profile.rs +++ b/crates/power-daemon/src/profile.rs @@ -630,7 +630,7 @@ impl NetworkSettings { if device_type.trim() == "1" { run_command(&format!( "ifconfig {} {}", - &name_str, + name_str, if disable { "down" } else { "up" } )); } diff --git a/docs/adr/001-arm-support.md b/docs/adr/001-arm-support.md new file mode 100644 index 0000000..3facc92 --- /dev/null +++ b/docs/adr/001-arm-support.md @@ -0,0 +1,61 @@ +--- +id: adrs-adr001 +title: "ADR001: Multi-Architecture Support (x86_64 + aarch64)" +# prettier-ignore +description: Architecture Decision Record (ADR) for adding ARM (aarch64) support to Power Options distribution +--- + +## Context + +Power Options is a pure Rust project with zero architecture-specific code — no `#[cfg(target_arch)]`, no arch-dependent dependencies, no inline assembly. The codebase compiles cleanly on any CPU architecture supported by Rust's tier 1 and tier 2 targets. + +Despite this, the project's distribution infrastructure exclusively targets x86_64: + +- **CI pipelines** (`deploy_release.yaml`, `deploy_commit.yaml`) run only on `ubuntu-24.04` (x86_64) runners. +- **AUR PKGBUILD generators** hardcode `arch=('x86_64')` in all 8 generators (daemon, gtk, webview, tray × stable + git variants). +- **DistroPack uploads** provide only x86_64 binaries. +- **OBS Debian control file** correctly uses `Architecture: any`, but CI never builds for non-x86 targets. + +ARM-based devices (e.g., Raspberry Pi, Apple Silicon via Asahi Linux, Snapdragon X Elite laptops, server-grade Ampere Altra) are increasingly common Linux platforms. Users on these systems are forced to build from source manually, which works but defeats the purpose of the packaged distribution. + +A user on an aarch64 system attempting `apt install power-options-gtk` receives: + +``` +power-options-gtk:amd64 : Depends: libadwaita-1-0:amd64 but it is not installable +``` + +This is because only an amd64 (x86_64) binary exists in the DistroPack repository — APT tries to satisfy it via multiarch on a system with no x86_64 userspace. + +## Decision + +We will add aarch64 as a supported architecture across all distribution channels: + +1. **CI/CD**: Convert the release build job to a strategy matrix running on both `ubuntu-24.04` and `ubuntu-24.04-arm` GitHub-hosted runners. The commit check job will also run on both architectures. + +2. **DistroPack**: Upload architecture-specific binaries from each matrix job. We will use distinct package IDs per architecture to keep package metadata clean. + +3. **AUR PKGBUILDs**: Update all 8 PKGBUILD generators to set `arch=('x86_64' 'aarch64')`, allowing Arch Linux ARM (asahi-linux, Arch Linux ARM) users to install from the AUR. + +4. **OBS / Debian packaging**: No changes needed — `Architecture: any` in `debian.control` and the absence of arch-specific requirements in the RPM `.spec` already permit multi-arch builds. The OBS build service will handle cross-architecture builds when configured. + +5. **Install scripts**: No changes needed — they invoke `cargo build --release` natively on whatever architecture the user is on. + +We intentionally do **not** add cross-compilation tooling (e.g., `cross`, `cargo-zigbuild`, QEMU binfmt) at this time. Using native ARM GitHub runners is simpler, faster, and avoids the complications of cross-compiling GTK4/libadwaita/WebKit system dependencies. If native ARM runners prove unavailable or too slow in the future, cross-compilation can be revisited. + +## Consequences + +### Positive +- ARM Linux users can install Power Options via apt, AUR, and OBS without building from source. +- The Rust codebase needs no changes — a testament to Rust's portability. +- GitHub's ARM runners are available at no additional cost for public repositories. +- The build matrix approach is easy to understand and extend (e.g., adding riscv64 in the future). + +### Negative +- CI runtime approximately doubles (two parallel builds instead of one). +- DistroPack package management becomes slightly more complex with per-arch package IDs. +- AUR PKGBUILD `arch` arrays become slightly longer (all 8 files need `('x86_64' 'aarch64')`). + +### Neutral +- The check_and_format job in commit CI will also run on ARM, catching any future architecture-specific regressions. +- The webview frontend (dioxus/webkit2gtk) builds on ARM where webkit2gtk is available — no additional work needed. +- Release tags now produce two sets of binaries. We must verify DistroPack processes both correctly before the first multi-arch release. diff --git a/docs/adr/_adr-XXXX-template.md b/docs/adr/_adr-XXXX-template.md new file mode 100644 index 0000000..d97a5ce --- /dev/null +++ b/docs/adr/_adr-XXXX-template.md @@ -0,0 +1,23 @@ +--- +id: adrs-adr000 +title: "ADR000: [TITLE]" +# prettier-ignore +description: Architecture Decision Record (ADR) for [TITLE] [DESCRIPTION] +--- + + + +## Context + + + +## Decision + + + +## Consequences + + + +