Skip to content

feat(obs-integration): add OBS integration plugin - #210

Merged
ItsLemmy merged 26 commits into
noctalia-dev:mainfrom
neyfua:obs-integration
Aug 3, 2026
Merged

feat(obs-integration): add OBS integration plugin#210
ItsLemmy merged 26 commits into
noctalia-dev:mainfrom
neyfua:obs-integration

Conversation

@neyfua

@neyfua neyfua commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Plugin

  • Id: neyfua/obs-integration
  • New plugin
  • Update to an existing plugin (version bumped in plugin.toml)

What it does

Manage openSUSE Build Service (OBS) projects and packages directly from the Noctalia bar, without leaving the shell for common tasks.

  • Bar widget — OBS icon toggles the plugin panel; status colors reflect OSC/account state.
  • My Projects — lists every project you own and projects where you maintain packages (verified against OBS roles, so scmsync git-pool false positives are excluded). Search and A-Z/Z-A sort included.
  • Browse packages — opens a project to its packages, showing which are checked out locally.
  • Checkout / remove — one-click osc co for new packages; remove a package locally (with a confirm step), auto-cleaning the project dir when the last package goes.
  • Edit — osc meta for project/package metadata, $EDITOR for _service and .spec, osc vc for .changes, plus a full file listing with per-file remove.
  • Rebuild — trigger osc rebuild for a specific architecture or all.
  • Add/Remove & Commit — run osc ar and osc ci from the checkout with the terminal visible for progress or commit-message editing.
  • Source services (local) — osc service r and osc service mr execute the package's _service scripts locally and can modify files in the checkout. osc service ra runs all local services after a confirmation click.
  • Source services (remote) — osc service rr requests a server-side service run and does not execute code locally. It does not require confirmation.
  • Sticky navigation — the panel reopens where you left off (project or package level).
    It's a GUI convenience layer over the osc CLI. Operations such as metadata edits, commits, rebuilds, and remote service runs can change OBS-side state. Credentials remain in your normal ~/.config/osc/oscrc.

External dependencies

The plugin shells out to the osc command (listed in plugin.toml) for project/package discovery and metadata, checkouts and updates, result lookup, changelog editing, add/remove, commits, rebuilds, and source-service operations. osc service r, osc service mr, and osc service ra execute package source services locally; Run All (ra) requires confirmation. osc service rr requests execution on the OBS server, does not run code locally, and does not require confirmation. Authentication is read from the user's existing ~/.config/osc/oscrc; the plugin keeps no separate credentials.

It also spawns $EDITOR (or nano as a fallback) to open _service, .spec, and .changes files, and uses standard find, sleep, cut, ls, grep, and rm commands for local operations inside the checkout directory.

Testing

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Noctalia version tested against: v5.0.0-beta.7
  • Plugin API level: 9

Screenshots / Videos

image image image image image

Checklist

  • The directory name matches the part of id after the / in plugin.toml exactly.
  • It ships plugin.toml, README.md, thumbnail.webp, and translations/en.json.
  • README.md follows the
    README template, documents
    every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
  • I created thumbnail.webp with the thumbnail generator.
  • version follows semver and is bumped in this PR; plugin_api is the oldest API level this plugin requires.
  • Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and
    understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
  • I did not edit catalog.toml; CI generates it.
  • This PR touches exactly one plugin directory.

Code review attestation

Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:

  • The code is readable and not obfuscated, minified, or generated.
  • It does not download and execute remote code.
  • Every network call, filesystem write, and spawned process is something the description above accounts for.
  • I have the right to publish this code under the license declared in plugin.toml.

@ItsLemmy

ItsLemmy commented Aug 2, 2026

Copy link
Copy Markdown
Contributor
  1. blocking - obs-integration/panel.luau:746

    The rebuild architecture selector never takes effect; every rebuild is
    issued for all architectures.

    ui.select delivers its onChange index to Luau as a string, not a number
    (noctalia-shell src/ui/ui_tree_reconciler.cpp:1522 formats the index into
    a string, and ScriptRuntime::enqueueCallStrings pushes it with
    lua_pushlstring). obs-integration/panel.luau:737 stores that string
    verbatim into rebuildArchIndex, and obs-integration/panel.luau:746 then
    does rebuildOptions[rebuildArchIndex]. A Lua table lookup with the string
    key "1" is not the same key as the number 1, so the lookup always yields
    nil, and rebuildPackage (obs-integration/panel.luau:120) drops the -a flag
    for every selection.

    The same string value is fed back as selectedIndex at
    obs-integration/panel.luau:733. numProp uses std::get_if
    (src/ui/ui_tree_reconciler.cpp:45), so the prop is silently ignored on
    later renders while the widget keeps showing the architecture the user
    picked. The UI therefore reports a targeted rebuild while osc rebuild runs
    against every architecture, which is a real OBS-side side effect.

    Other plugins in this repo already handle the convention, for example
    hypr-screen-mirror/panel.luau:48 uses tonumber(selected) and
    gslapper/panel.luau:912 offsets a 0-based index. Converting with tonumber
    and indexing rebuildOptions[index + 1] fixes it.

  2. non-blocking - obs-integration/panel.luau:41

    validName does not implement the guarantee its own comment at
    obs-integration/panel.luau:39 states ("reject anything that could escape
    the checkout dir ... dots-as-prefix"). The pattern ^[%w:.+%-_]+$ accepts
    "." and "..", so validName("..") returns true.

    Project names reach this guard from OBS server output (osc search at
    obs-integration/panel.luau:442 and the osc api XML parse at
    obs-integration/panel.luau:488) and from the on-disk cache
    (obs-integration/panel.luau:355). A project value of ".." would make
    removePackageLocal (obs-integration/panel.luau:197) build
    <checkout_dir>/../ and rm -rf outside the checkout tree. Exploitation
    requires attacker-influenced OBS data or a tampered cache file, so this is
    not a practical attack, but the guard in front of rm -rf should reject
    names consisting only of dots.

  3. non-blocking - obs-integration/panel.luau:49

    resolveCheckoutDir falls back to "~/OBS" only when checkout_dir is nil. An
    empty string is truthy in Lua, so setting the setting to "" yields an empty
    base path, and removePackageLocal (obs-integration/panel.luau:202) then
    computes projectDir = "/" and runs rm -rf against a filesystem
    root path. Treating an empty or whitespace-only setting as unset would
    close this.

@ItsLemmy
ItsLemmy marked this pull request as draft August 2, 2026 01:46
@neyfua
neyfua marked this pull request as ready for review August 2, 2026 11:45
@neyfua
neyfua marked this pull request as draft August 2, 2026 13:56
@neyfua
neyfua marked this pull request as ready for review August 2, 2026 19:09
@neyfua

neyfua commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@ItsLemmy I think it's ready for another review if u have some free time :D

@ItsLemmy

ItsLemmy commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
  1. blocking - obs-integration/panel.luau:404

The panel invokes four undocumented source-service operations: osc service mr, osc service rr, osc service r, and osc service ra. See also obs-integration/panel.luau:413, obs-integration/panel.luau:421, obs-integration/panel.luau:430, and the buttons at obs-integration/panel.luau:897 and obs-integration/panel.luau:1084.

obs-integration/README.md:39 lists the package actions but omits all source-service execution. The PR description and external-dependency section omit them as well, despite attesting that every spawned process is described. Local source services execute scripts selected by package _service definitions, while the remote operation requests server-side execution. Users therefore cannot assess these security-relevant side effects from the required metadata. Document each operation, whether it runs locally or remotely, and which operations require confirmation.

  1. non-blocking - obs-integration/panel.luau:741

When osc who exits non-zero, its callback leaves oscUser as nil and calls maybeVerify() again. The nil branch immediately starts another identical osc who, whose failure repeats the cycle. See also the initial call at obs-integration/panel.luau:726.

A transient network, authentication, or CLI failure leaves project loading incomplete and repeatedly spawns osc who without termination or backoff. Record the failure, finish loading with an error state, and let the existing retry action initiate another attempt.

  1. non-blocking - obs-integration/README.md:77

The README says rebuilds and commits open a terminal. Rebuilds are actually executed through runActionAsync at obs-integration/panel.luau:233 and display their output inside the panel. Commits open a terminal only when osc status reports changes; otherwise obs-integration/panel.luau:390 runs the commit asynchronously.

Update the README so users receive accurate information about where command output appears and when a terminal opens.

@ItsLemmy
ItsLemmy marked this pull request as draft August 3, 2026 02:44
@neyfua
neyfua marked this pull request as ready for review August 3, 2026 03:00
@ItsLemmy
ItsLemmy merged commit dd1299e into noctalia-dev:main Aug 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants