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

* feat(obs-integration): add OBS integration plugin

* docs(obs-integration): add README.md

* chore(obs-integration): rename obs-integration-thumbnail.webp to thumbnail.webp

* i18n(obs-integration): fix PR check

* fix(obs-integration): honor selected rebuild architecture

* fix(obs-integration): reject dot-prefixed names in validName

* fix(obs-integration): treat empty checkout_dir as unset

* feat(obs-integration): add repo/arch rebuild selection with confirmation

* chore(obs-integration): add opensuse tag to plugin.toml

* chore(obs-integration): new thumbnail

* fix(obs-integration): fix rebuild controls inheriting flexGrow from reused nodes

* feat(obs-integration): overhaul package actions, logging, and navigation

* chore(obs-integration): new thumbnail

* feat(obs-integration): label Service Run All button, move build logs

* fix(obs-integration): split logs based on action source

* fix(obs-integration): correct log display locations and resets

* feat(obs-integration): add confirmation to remove file

* feat(obs-integration): move edit package meta to header

* fix(obs-integration): layout repo/arch label/select alignment

* fix(obs-integration): clean rebuild log duplication

* fix(obs-integration): persist logSource in nav state for panel reopen

* i18n(obs-integration): change 'Repo' to 'Repositories' and 'Arch' to 'Architectures'

* feat(obs-integration): add home nav button, fix header label clipping, rebuild controls layout

* chore(obs-integration): change plugin's panel height

* docs(obs-integration): document source-service operations in README

* fix(obs-integration): correct rebuild log display and persistence, sync UI layout

---------

Co-authored-by: neyfua <ogkhang.05@gmail.com>
This commit is contained in:
Lê Khang
2026-08-03 08:27:34 -04:00
committed by GitHub
co-authored by neyfua
parent f68e308526
commit dd1299e1d6
6 changed files with 1744 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
# OBS Integration
Manage openSUSE Build Service projects and packages from Noctalia. The bar
widget toggles a panel for browsing your projects, checking out packages,
editing metadata and files, and triggering rebuilds on OBS — without leaving
the shell.
## Plugin
| Field | Value |
| --- | --- |
| ID | `neyfua/obs-integration` |
| Entries | Bar widget: `obs-integrate`; panel: `panel` |
## Requirements
Install the openSUSE Build Service `osc` CLI on `PATH` and configure your
credentials in `~/.config/osc/oscrc` (for example with `osc apiservice` setup
or by copying a working `oscrc`). The plugin shells out to `osc` for every
operation, so all authentication stays in your normal OBS configuration.
## Usage
Add the `obs-integrate` widget to a bar. Left-click it to open the panel.
Open the panel directly with:
```sh
noctalia msg panel-toggle neyfua/obs-integration:panel
```
### My Projects
The panel opens on **My Projects**: every project you maintain, plus every
project where you are listed as a package maintainer on OBS. Search filters the
list, and the sort button toggles A-Z / Z-A order. The pen icon edits the
project metadata (`osc meta prj -e`).
### Inside a project
Click a project to list its packages. A package's icon turns **primary** when
that package is checked out in your local directory. Click a package to open
its actions:
- **Checkout package** — `osc co` in the configured checkout directory
(hidden once the package is already checked out).
- **Edit files** — expandable list of editable sources: `_service` and the
package's `.spec` open in your `$EDITOR`; the `.changes` file runs
`osc vc`.
- **Files** — lists every non-dotfile in the checkout. Each file has a remove
button; below the list, **Add/Remove** runs `osc ar` asynchronously in the
panel. **Commit** runs `osc status` first; if pending changes exist it
opens a terminal with `osc ci` so you can edit the commit message, otherwise
the commit runs asynchronously in the panel.
- **Service Local Run** — runs `osc service r` in the checkout, executing the
package's source-service scripts locally (local side effects: the `_service`
scripts can modify files in the checkout).
- **Service Manual Run** — runs `osc service mr` in the checkout, running the
source-service scripts locally in "manual" mode (local side effects, like
Service Local Run).
- **Service Run All** — runs `osc service ra` in the checkout, running all
source-service scripts locally. Requires a confirmation click (turns
**secondary** with a **check** glyph) before it executes.
- **Service Remote Run** — runs `osc service rr` on the OBS server for the
package on the remote project. No local code executes; the request triggers
server-side service runs, and a confirmation is not required.
- **Rebuild package** — pick an architecture (or **All**) and trigger
`osc rebuild`. Confirmation is required before it runs.
- **Edit package meta** — `osc meta pkg -e` in a terminal.
- **Remove package** — deletes the checkout from disk only (never touches the
OBS project); confirm before it runs. If it was the last package in the
project directory, the directory is removed too.
### Sticky navigation
The panel reopens where you left off — inside the same project or package.
## Settings
| Setting | Type | Default | Description |
| --- | --- | --- | --- |
| `checkout_dir` | `string` | `~/OBS` | Base directory where packages are checked out. Packages land in `<checkout_dir>/<project>/<package>`. |
| `show_label` | `bool` | `false` | Show the "OBS" label next to the bar icon. |
## Notes
The plugin runs the `osc` CLI with your existing credentials — it stores
nothing itself beyond a small cache of project/package listings and your last
navigated view. Removal is local-only and never modifies the OBS project.
Most operations (rebuilds, service runs, updates, add/remove) run
asynchronously and display their output directly in the panel. The Commit
operation opens a terminal only when `osc status` reports pending changes;
otherwise, it runs asynchronously in the panel. Confirmation prompts precede
destructive and side-effecting operations: Rebuild package, Remove package,
and Service Run All.
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
id = "neyfua/obs-integration"
name = "OBS Integration"
version = "1.0.0"
plugin_api = 9
author = "neyfua"
license = "MIT"
dependencies = ["osc"]
tags = ["productivity", "service", "utility", "opensuse"]
icon = "buildings"
description = "Manage your openSUSE Build Service projects and packages."
[[setting]]
key = "checkout_dir"
type = "string"
label_key = "settings.checkout_dir.label"
description_key = "settings.checkout_dir.description"
default = "~/OBS"
[[widget]]
id = "obs-integrate"
entry = "widget.luau"
[[widget.setting]]
key = "show_label"
type = "bool"
label_key = "settings.show_label.label"
description_key = "settings.show_label.description"
default = false
[[panel]]
id = "panel"
entry = "panel.luau"
width = 410
height = 450
placement = "floating"
position = "center"
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

+69
View File
@@ -0,0 +1,69 @@
{
"settings": {
"show_label": {
"label": "Show label",
"description": "Show the OBS label next to the icon."
},
"checkout_dir": {
"label": "Package checkout directory",
"description": "Directory where packages will be checked out."
}
},
"panel": {
"my_projects": "My Projects",
"reload_projects": "Reload projects",
"search_projects": "Search projects...",
"no_projects": "No projects found.",
"no_packages": "No packages found.",
"loading": "Loading...",
"error": "OBS command failed",
"retry": "Retry",
"checkout": "Checkout package",
"checking_out": "Checking out...",
"edit_files": "Edit files",
"edit_changes": "Edit {name}.changes",
"update_changes": "Update {name}.changes",
"edit_meta": "Edit package meta",
"rebuild": "Rebuild package",
"repo": "Repositories",
"arch": "Architectures",
"confirm_rebuild": "Confirm rebuild",
"remove": "Remove package",
"confirm_remove": "Click to confirm removal",
"edit_project_meta": "Edit project meta",
"no_editable": "No editable files found.",
"search_packages": "Search...",
"loading_packages": "Loading packages...",
"files": "Files",
"no_files": "No files found.",
"remove_file": "Remove file",
"add_remove": "Add/Remove",
"adding_removing": "Adding/removing files...",
"no_changes": "no changes for package {name}",
"update_dir": "Update directory",
"updating_dir": "Updating directory...",
"running_manual_service": "Running manual service...",
"running_remote_service": "Running remote service...",
"committing": "Committing...",
"rebuilding_pkg": "Rebuilding package...",
"service_run": "Service Manual Run",
"service_remote_run": "Service Remote Run",
"service_local_run": "Service Local Run",
"service_run_all": "Service Run All",
"confirm_run_all": "Confirm Run All",
"confirm": "Confirm",
"running_local_service": "Running local service...",
"running_all_services": "Running all services...",
"commit": "Commit package",
"osc_missing": "'osc' not found",
"osc_missing_hint": "Install the 'osc' package to start using this plugin.",
"osc_not_configured": "OBS account not configured",
"osc_not_configured_hint": "Create ~/.config/osc/oscrc before browsing projects.",
"configure_oscrc": "Configure oscrc"
},
"widget": {
"osc_missing": "'osc' missing",
"configure_obs": "Configure OBS",
"obs": "OBS"
}
}
+46
View File
@@ -0,0 +1,46 @@
--!nonstrict
local OSC_CONFIG = noctalia.expandPath("~/.config/osc/oscrc")
local function render()
local hasOsc = noctalia.commandExists("osc")
local oscConfigured = noctalia.fileExists(OSC_CONFIG)
local showLabel = noctalia.getConfig("show_label") ~= false
barWidget.setGlyph("buildings")
if not hasOsc then
barWidget.setGlyphColor("error")
if showLabel then
barWidget.setText(noctalia.tr("widget.osc_missing"))
barWidget.setColor("error")
end
elseif not oscConfigured then
barWidget.setGlyphColor("on_surface_variant")
if showLabel then
barWidget.setText(noctalia.tr("widget.configure_obs"))
barWidget.setColor("on_surface_variant")
end
else
barWidget.setGlyphColor("on_surface")
if showLabel then
barWidget.setText(noctalia.tr("widget.obs"))
barWidget.setColor("on_surface")
end
end
if not showLabel then
barWidget.setText("")
end
end
function update()
render()
end
function onClick()
noctalia.togglePanel("neyfua/obs-integration:panel")
end
noctalia.setUpdateInterval(5000)
render()