feat(obs-integration): add package's build status view (#284)
* feat(obs-integration): add package's build status view * i18n(obs-integration): new translation for build status * chore(obs-integration): update plugin to v1.1.0 * docs(obs-integration): update README * i18n(obs-integration): fix duplicates and missing comma in translation --------- Co-authored-by: neyfua <ogkhang.05@gmail.com>
This commit is contained in:
@@ -64,8 +64,8 @@ its actions:
|
||||
- **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.
|
||||
- **Rebuild package** — pick a repository and architecture (or **All**) and trigger `osc rebuild`. Confirmation is required before it runs.
|
||||
- **Build Status** — Shows real-time build results for the package across all repositories and architectures. Click **Refresh status** to pull latest results.
|
||||
- **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
|
||||
@@ -93,3 +93,7 @@ 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.
|
||||
|
||||
Build status data is fetched from `osc results` and cached for the session. The
|
||||
status display shares the same underlying `osc results` call used for rebuild
|
||||
architecture selection, so enabling a package view pulls both in one query.
|
||||
|
||||
+110
-2
@@ -21,6 +21,7 @@ local rebuildRepoIndex = 0
|
||||
local rebuildConfirm = false
|
||||
local runAllConfirm = false
|
||||
local repoMap = {}
|
||||
local statusMap = {}
|
||||
local savedRebuildRepo = nil
|
||||
local savedRebuildArch = nil
|
||||
local rebuildLoaded = false
|
||||
@@ -275,17 +276,25 @@ loadPackageFiles = function(pkgDir, pkg)
|
||||
end, 2000)
|
||||
end
|
||||
|
||||
local function loadRebuildArches(project, pkg)
|
||||
local function loadRebuildArches(project, pkg)
|
||||
rebuildLoaded = false
|
||||
rebuildRepos = {}
|
||||
repoMap = {}
|
||||
statusMap = {}
|
||||
noctalia.runAsync("osc results " .. shellQuote(project) .. " " .. shellQuote(pkg), function(result)
|
||||
local map = {}
|
||||
local statuses = {}
|
||||
if result.exitCode == 0 then
|
||||
for line in tostring(result.stdout):gmatch("[^\n]+") do
|
||||
line = noctalia.string.trim(line)
|
||||
local repo, arch, _, status = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)")
|
||||
if status then
|
||||
status = status:gsub("%*$", "")
|
||||
end
|
||||
if repo ~= nil and arch ~= nil and status ~= "disabled" then
|
||||
if not map[repo] then
|
||||
map[repo] = {}
|
||||
statuses[repo] = {}
|
||||
end
|
||||
local found = false
|
||||
for _, a in ipairs(map[repo]) do
|
||||
@@ -297,6 +306,7 @@ local function loadRebuildArches(project, pkg)
|
||||
if not found then
|
||||
table.insert(map[repo], arch)
|
||||
end
|
||||
statuses[repo][arch] = status
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -309,6 +319,7 @@ local function loadRebuildArches(project, pkg)
|
||||
table.sort(map[repo])
|
||||
end
|
||||
repoMap = map
|
||||
statusMap = statuses
|
||||
rebuildRepos = repos
|
||||
local repoIndex = 0
|
||||
if savedRebuildRepo then
|
||||
@@ -336,6 +347,50 @@ local function loadRebuildArches(project, pkg)
|
||||
end, 15000)
|
||||
end
|
||||
|
||||
local STATUS_STYLE = {
|
||||
succeeded = { glyph = "check", color = "secondary", labelKey = "status_succeeded" },
|
||||
failure = { glyph = "x", color = "error", labelKey = "status_failed" },
|
||||
broken = { glyph = "alert-circle", color = "error", labelKey = "status_failed" },
|
||||
unresolvable = { glyph = "alert-circle", color = "error", labelKey = "status_unresolvable" },
|
||||
building = { glyph = "loader", color = "primary", labelKey = "status_building" },
|
||||
dispatching = { glyph = "loader", color = "primary", labelKey = "status_building" },
|
||||
scheduled = { glyph = "clock", color = "on_surface_variant", labelKey = "status_scheduled" },
|
||||
signing = { glyph = "lock", color = "warning", labelKey = "status_signing" },
|
||||
blocked = { glyph = "clock", color = "on_surface_variant", labelKey = "status_blocked" },
|
||||
excluded = { glyph = "ban", color = "on_surface_variant", labelKey = "status_excluded" },
|
||||
unbuildable = { glyph = "ban", color = "on_surface_variant", labelKey = "status_unbuildable" },
|
||||
finished = { glyph = "check", color = "secondary", labelKey = "status_succeeded" },
|
||||
unknown = { glyph = "help-circle", color = "on_surface_variant", labelKey = "status_unknown" },
|
||||
}
|
||||
|
||||
local STATUS_GROUPS = {
|
||||
["succeeded"] = "succeeded",
|
||||
["success"] = "succeeded",
|
||||
["finished"] = "succeeded",
|
||||
["failed"] = "failure",
|
||||
["broken"] = "failure",
|
||||
["unresolvable"] = "unresolvable",
|
||||
["building"] = "building",
|
||||
["dispatching"] = "building",
|
||||
["scheduled"] = "scheduled",
|
||||
["signing"] = "signing",
|
||||
["blocked"] = "blocked",
|
||||
["excluded"] = "excluded",
|
||||
["unbuildable"] = "unbuildable",
|
||||
["unknown"] = "unknown",
|
||||
["disabled"] = "disabled",
|
||||
}
|
||||
|
||||
local function statusPresentation(status)
|
||||
status = status or "unknown"
|
||||
local group = STATUS_GROUPS[status] or status
|
||||
local base = STATUS_STYLE[group]
|
||||
if base then
|
||||
return base.glyph, base.color, noctalia.tr("panel." .. base.labelKey), group
|
||||
end
|
||||
return "help-circle", "on_surface_variant", status, "unknown"
|
||||
end
|
||||
|
||||
local function removeFileLocal(path, pkgDir, pkg)
|
||||
noctalia.runAsync("rm " .. shellQuote(path), function(result)
|
||||
if result.exitCode == 0 then
|
||||
@@ -831,6 +886,7 @@ function render()
|
||||
rebuildRepos = {}
|
||||
rebuildRepoIndex = 0
|
||||
repoMap = {}
|
||||
statusMap = {}
|
||||
savedRebuildRepo = nil
|
||||
savedRebuildArch = nil
|
||||
rebuildLoaded = false
|
||||
@@ -1262,7 +1318,7 @@ function render()
|
||||
}),
|
||||
ui.button({
|
||||
key = "rebuild-btn",
|
||||
glyph = rebuildConfirm and "check" or "refresh",
|
||||
glyph = rebuildConfirm and "check" or "analyze-filled",
|
||||
tooltip = rebuildConfirm and noctalia.tr("panel.confirm_rebuild") or noctalia.tr("panel.rebuild"),
|
||||
variant = rebuildConfirm and "secondary" or "default",
|
||||
onClick = function()
|
||||
@@ -1297,6 +1353,58 @@ function render()
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(body, ui.separator({ thickness = 1, spacing = 6 }))
|
||||
|
||||
local statusChildren = {
|
||||
ui.row({ align = "center", gap = 8 }, {
|
||||
ui.glyph({ name = "activity", color = "primary", size = 16 }),
|
||||
ui.label({ text = noctalia.tr("panel.build_status"), fontWeight = "bold", fontSize = 15, flexGrow = 1 }),
|
||||
ui.button({
|
||||
glyph = "refresh",
|
||||
tooltip = noctalia.tr("panel.status_refresh"),
|
||||
variant = "ghost",
|
||||
controlSize = "sm",
|
||||
onClick = function()
|
||||
loadRebuildArches(selectedProject, selectedPackage)
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
if not rebuildLoaded then
|
||||
table.insert(statusChildren, ui.row({ align = "center", gap = 8, paddingV = 2 }, {
|
||||
ui.glyph({ name = "loader", color = "primary", size = 14 }),
|
||||
ui.label({ text = noctalia.tr("panel.status_loading"), color = "on_surface_variant" }),
|
||||
}))
|
||||
else
|
||||
local hadResults = false
|
||||
for _, repo in ipairs(rebuildRepos) do
|
||||
local archs = repoMap[repo] or {}
|
||||
if #archs > 0 then
|
||||
hadResults = true
|
||||
table.insert(
|
||||
statusChildren,
|
||||
ui.label({ text = repo, fontWeight = "bold", fontSize = 14, color = "on_surface" })
|
||||
)
|
||||
for _, arch in ipairs(archs) do
|
||||
local status = statusMap[repo] and statusMap[repo][arch] or nil
|
||||
local glyph, color, label = statusPresentation(status)
|
||||
table.insert(
|
||||
statusChildren,
|
||||
ui.row({ align = "center", gap = 8, paddingL = 12 }, {
|
||||
ui.glyph({ name = glyph, color = color, size = 14 }),
|
||||
ui.label({ text = arch, fontSize = 13, color = "on_surface", flexGrow = 1 }),
|
||||
ui.label({ text = label, fontSize = 13, color = color }),
|
||||
})
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not hadResults then
|
||||
table.insert(statusChildren, ui.label({ text = noctalia.tr("panel.no_builds"), color = "on_surface_variant" }))
|
||||
end
|
||||
end
|
||||
table.insert(body, ui.column({ gap = 4 }, statusChildren))
|
||||
|
||||
elseif selectedProject then
|
||||
table.insert(
|
||||
body,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
id = "neyfua/obs-integration"
|
||||
name = "OBS Integration"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
plugin_api = 9
|
||||
author = "neyfua"
|
||||
license = "MIT"
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
"no_files": "No files found.",
|
||||
"no_packages": "No packages found.",
|
||||
"no_projects": "No projects found.",
|
||||
"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.",
|
||||
"rebuild": "Rebuild package",
|
||||
"rebuilding_pkg": "Rebuilding package...",
|
||||
"reload_projects": "Reload projects",
|
||||
@@ -47,6 +43,24 @@
|
||||
"service_remote_run": "Service Remote Run",
|
||||
"service_run": "Service Manual Run",
|
||||
"service_run_all": "Service Run All",
|
||||
"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.",
|
||||
"build_status": "Build Status",
|
||||
"status_refresh": "Refresh status",
|
||||
"status_loading": "Loading status...",
|
||||
"no_builds": "No build results.",
|
||||
"status_succeeded": "Succeeded",
|
||||
"status_failed": "Failed",
|
||||
"status_unresolvable": "Unresolvable",
|
||||
"status_building": "Building",
|
||||
"status_scheduled": "Scheduled",
|
||||
"status_signing": "Signing",
|
||||
"status_blocked": "Blocked",
|
||||
"status_excluded": "Excluded",
|
||||
"status_unbuildable": "Unbuildable",
|
||||
"status_unknown": "Unknown",
|
||||
"update_changes": "Update {name}.changes",
|
||||
"update_dir": "Update directory",
|
||||
"updating_dir": "Updating directory..."
|
||||
|
||||
Reference in New Issue
Block a user