Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portage Store

Latest release License: GPL-2.0

A GTK4/libadwaita application store for Gentoo's Portage package manager, plus a terminal companion that shares the same core.

Search and install packages, watch builds stream live in a job queue, and get told about the things Gentoo otherwise expects you to remember to check yourself — security advisories, unread news, pending config updates, a stale tree, orphaned packages — without running five different commands after every sync.

portage-store          # the GUI
portage-store-cli      # the same core, no window

Status: works, used daily on the author's own system, but young. The privilege model (see below) is deliberately documented rather than hidden — read it before installing.

The Explore landing page: category tiles above auto-advancing themed carousels


Contents

Screenshots · Features · Requirements · Installing · Privilege model · The CLI · Architecture · Development · Packaging


Screenshots

The Explore landing page: category tiles above auto-advancing themed carousels Search results, with Portage hits first and Flatpak matches collapsed underneath
Explore
Category tiles above auto-advancing themed carousels.
Search
Name and description matches, ranked, with Flatpak hits kept separate.
A package detail page with upstream screenshots and fact tiles Versions and package details
Package detail
Upstream screenshots, plus download size, install method, build time, and version.
Versions and details
What the tree offers, plus license, slot, and upstream links.
The USE flag editor A package screenshot opened in the full-size viewer
Build options
Every USE flag with its description, and how many other installed packages share it.
Screenshot viewer
Upstream screenshots open full-size in place.
A sync running in the bottom job bar with the queue popover open The log drawer streaming live sync output
Job queue
What's running, and everything waiting behind it.
Live output
The full build log, with an errors-only filter.
The Updates tab showing an up-to-date system The Flatpak section expanded below the Portage results
Updates
Pending @world updates, security advisories, and health banners.
Flatpak
Flatpak-only matches, collapsed beneath the Portage results.
Portage Settings showing make.conf variables The Env Files page
Portage settings
make.conf as fields, with overlays, profile, and kernel alongside.
Env files
Per-package package.env overrides, edited inline.
The Package Presets dialog The Free Up Space dialog
Presets
Shareable USE flags plus a starter package list.
Free up space
What each Portage cache would reclaim, before clearing it.

Features

Finding things

  • Whole-tree in-memory index. The first search parses one full eix --xml dump (~22k packages) and keeps it. Everything after that filters memory: ~600 ms once, ~2 ms per search thereafter. Dropped and rebuilt only when a sync completes.
  • Name and description search, so "password" finds keepass and bitwarden-desktop-bin, not just packages with "password" in the name.
  • Typo tolerance. When nothing matches well, a Levenshtein pass over the index suggests close names instead of returning an empty page.
  • Search operators — cat:games-*, use:wayland, installed:true, @world — applied in memory, so changing one re-renders instantly.
  • Curated category tiles — each showing a real app icon (Steam for Games, GIMP or Blender for Create, ...) picked at random from that category's pool on every launch, with the tile's own gradient drawn from that icon's color — above a landing page of themed carousels.

Installing and managing

  • A single job queue. Portage takes a global lock, so one emerge runs at a time and everything else waits its turn — reorderable and cancellable from a popover without touching what's already running. The whole queue can be exported as a POSIX shell script (one emerge/helper invocation per queued job, in run order), so what's queued can be handed to a different machine, cron, or just kept as a record instead of only ever running through this app.
  • Automatic conflict resolution. A failed build that needs a USE flag, a keyword, a license acceptance, or a circular-dependency break is detected, explained, and offered as apply-and-retry.
  • Toolchain blast-radius warning. Before running an update that touches gcc, glibc, or another toolchain component, a confirmation warns that this commonly cascades into rebuilding everything linked against it, if free RAM also looks tight for that at the current job count — one of several preflight checks (alongside a stale tree and unreviewed config updates) shown before a big @world update, none of which block it; they're every one dismissed together by choosing Continue.
  • Live build output, with a log drawer, an errors-only filter, and a searchable history of past builds.
  • Build-time ETAs from qlop's own recorded merge history, counting down live.
  • Resource throttling (on by default): builds run under nice/ionice with a RAM-aware MAKEOPTS cap instead of whatever's in make.conf. Added after an unthrottled build genuinely OOM-killed unrelated processes during development.
  • Night-only builds (opt-in): a mutating job queued outside the 23:00–07:00 window waits instead of starting immediately, with a "Run Now" override always available if it turns out you don't want to wait after all.
  • Sandbox builds. For a package the live system's resolver won't touch (a hard mask, a keyword mask, a conflict) — builds it instead in a disposable, throttled chroot: a shared stage3 base (fetched and checksum-verified once, so a second sandbox build never re-downloads it) with the live portage tree bind-mounted read-only and up to three pooled overlayfs instances, so a package that conflicts with whatever was already built in one instance just overflows to a fresh one instead of failing outright. The result lands in the same binary-package cache the host uses, so it's installable afterward like any other cached binpkg.
  • Downgrades, installed straight from a locally cached binary package (--usepkgonly, so it can never silently fall back to rebuilding from source) — no network round-trip, no recompile.

Knowing what's wrong

  • GLSA security advisories via glsa-check — the one category no default Gentoo install surfaces anywhere unless you already know the tool exists. Shown in its own section above ordinary updates, with a one-click fix.
  • Unread Gentoo news (GLEP 42), pending CONFIG_PROTECT updates with an inline diff reviewer and bulk accept, sync staleness, preserved-rebuild, and orphaned packages.
  • A health dashboard combining all of it, with optional periodic background checks and trend history ("this has been pending 6 days").

Understanding a package

  • Dependency preview, download size, and compile-vs-prebuilt — cached by atom+version, disk-backed with a 1-hour TTL so it survives a restart.
  • Reverse dependencies — "why is this installed?" walks up to an @world ancestor, bounded on depth, fan-out, and total subprocess calls so one click on glib can't spawn thousands.
  • USE flag editor showing where each flag's current value actually comes from.
  • Man page, upstream AppStream metadata, GitHub social card, and Gentoo wiki/Bugzilla mentions, when they exist.

Sharing a setup

  • Presets — a named bundle of USE flags plus a starter package list ("Gaming Desktop", "Minimal Server") that layers onto what's already installed. Export to JSON, import someone else's.
  • Profile bundles — the whole picture: @world plus every file under /etc/portage, tarred up. Importing shows a diff of what would actually change before touching anything.

System configuration

Overlays (enable GURU and friends), make.conf variables, package.env overrides, kernel selection, profile switching, and binary-package repositories — each a focused page over its own module.

Every write this app makes under /etc/portage is committed to a git repo there, browsable and revertible from the GUI.

Flatpak

A second, fully independent job lane. Flatpak has no global lock, so a Flatpak update never waits behind an hours-long @world rebuild. Flatpak-only search hits appear as a collapsed strip beneath the Portage results, never mixed into them.


Requirements

Build: Rust 1.85+ (2024 edition; developed against 1.96), gui-libs/gtk 4.12+, gui-libs/libadwaita 1.5+.

Runtime:

Tool Package Used for
emerge, portageq, glsa-check sys-apps/portage installs, updates, advisories
eix app-portage/eix the search index (run eix-update first)
equery app-portage/gentoolkit reverse dependencies
qlop app-portage/portage-utils build-time history / ETAs
eclean-dist, eclean-pkg app-portage/gentoolkit cache cleanup
eselect app-admin/eselect unread news, kernel symlink selection
doas app-admin/doas the privilege helper (see below)
curl, git, tar, df, man base system artwork, config history, bundles
flatpak sys-apps/flatpak optional — the Flatpak lane auto-disables without it

Installing

Build

git clone https://github.com/tarilka0gg/portage-store.git
cd portage-store
cargo build --release

Produces target/release/portage-store (GUI) and target/release/portage-store-cli.

The in-app web preview is behind an off-by-default feature flag, because WebKitGTK is one of the slowest packages on Gentoo to build and this app does not need it — links open in your browser instead. Enable it with --features webview if you want it.

Set up the privilege helper

This is required — without it, anything that writes to the system will fail.

# 1. Install the helper scripts, root-owned and not writable by your user
sudo mkdir -p /usr/local/libexec/portage-store
sudo install -m 0755 -o root -g root resources/priv-helper.sh \
    /usr/local/libexec/portage-store/priv-helper
sudo install -m 0755 -o root -g root resources/sandbox-build.sh \
    /usr/local/libexec/portage-store/sandbox-build.sh

# 2. Create its audit log
sudo mkdir -p /var/log/portage-store
sudo chown root:portage /var/log/portage-store
sudo chmod 0750 /var/log/portage-store
sudo touch /var/log/portage-store/priv-helper.log
sudo chown root:portage /var/log/portage-store/priv-helper.log
sudo chmod 0640 /var/log/portage-store/priv-helper.log

# 3. Allow it to run without a password, for your user only
echo "permit nopass $USER cmd /usr/local/libexec/portage-store/priv-helper" \
    | sudo tee -a /etc/doas.conf

Then install the binaries wherever you like (~/.local/bin, /usr/local/bin, …).


The privilege model — read this

Everything privileged goes through one root-owned script, reachable by a passwordless doas rule scoped to exactly that one path.

This is a real, deliberate tradeoff, so it's stated plainly:

  • There is no password prompt. A passwordless rule means nothing stands between "the app asked for this" and "it happened as root."
  • In exchange, the attack surface is made as narrow as possible. No privileged call ever carries script text. The helper is invoked with a subcommand name and arguments — never bash -c "$SOMETHING" — and the script itself lives root-owned on disk, referenced only by name.
  • The helper does not trust its caller. Every path is re-validated on the root side: it must resolve (after canonicalization, closing symlink-swap races) under /etc/portage or /etc as appropriate, and the runnable-binary list is an allowlist, not a filter.
  • Every privileged call is logged, by root, to /var/log/portage-store/priv-helper.log — the compensating control for having no prompt. The GUI has a page to read it.

Read resources/priv-helper.sh before installing. It is deliberately written to be read.


The CLI

Same core, no window. Every subcommand is a thin wrapper over the same functions the GUI calls — not a reimplementation.

portage-store-cli search firefox
portage-store-cli show www-client/firefox
portage-store-cli list-installed
portage-store-cli updates

portage-store-cli install app-misc/tmux --pretend
portage-store-cli install app-misc/tmux
portage-store-cli uninstall app-misc/tmux
portage-store-cli update-world --pretend
portage-store-cli sync

portage-store-cli preset list
portage-store-cli preset apply "Minimal Server"
portage-store-cli preset apply-file ./my-preset.json

Mutating commands stream emerge output live and exit non-zero on failure, so they compose fine in scripts.


Architecture

Three pieces, one direction of dependency:

  portage-store  (GTK4 GUI)      portage-store-cli  (clap)
              \                 /
               \               /
            portage_store  (lib)          ← no GTK, no UI state
                     |
     eix · emerge · qlop · glsa-check · flatpak
     doas+helper · /etc/portage · /var/db/pkg
  • src/portage/, src/backend.rs, src/flatpak.rs — the domain layer. Everything that knows how Gentoo works: parsing eix --xml, building emerge argv, reading @world, writing package.use. Plain functions returning plain structs. Zero gtk/adw references anywhere in it, which is exactly what made the CLI cheap to add rather than a rewrite.
  • src/ui/ — all the GTK. Split by area (queue.rs, checks.rs, browse.rs, flatpak_lane.rs, log_drawer.rs, one file per dialog), each taking &Rc<App>.
  • src/bin/portage-store-cli.rs — the terminal front end.

Where to look, by feature:

Module Owns
eix.rs tree index, search, category browse, lookup
emerge.rs job builders, streaming runner, pretend cache, output parsers
priv_write.rs, command.rs the one privileged-write seam, and its test harness
glsa.rs, news.rs, config_protect.rs, sync.rs the four health banners
reverse_deps.rs "why is this installed", with its own recursion budget
qlop.rs, resource_limits.rs build-time history and throttling math
preset.rs, profile_bundle.rs the two "share my setup" shapes
media.rs, icons.rs the two on-disk artwork caches

Development

cargo build
cargo test           # 255 tests, no live system required
cargo run

Tests never touch a real subprocess: parsers are fed captured fixtures, and the two places that genuinely interleave subprocess calls with logic (priv_write.rs, reverse_deps.rs) go through a CommandRunner trait with a fake implementation. So cargo test passes on a non-Gentoo machine.


Packaging

packaging/ holds a work-in-progress ebuild, metadata.xml, .desktop entry, AppStream metainfo, and a placeholder icon, aimed at eventual submission to GURU. Not submitted, and not yet correct — packaging/README.md lists exactly what needs fixing first, including a real helper-path mismatch between the ebuild and the compiled-in constant.


License

GPL-2.0, matching Portage and Gentoo's own licensing.

About

A GTK4/libadwaita app store for Gentoo Portage, plus a CLI companion sharing the same core

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages