feat(nix-monitor): add nix-monitor plugin
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Nix Monitor
|
||||
|
||||

|
||||
|
||||
*Nix Monitor* checks Nixpkgs update by comparing local nix hash and remote nixpkgs's hash. It also can run update and clean command.
|
||||
|
||||
No AI is used here but this is my first time writing Luau and plugin in general, so expect some bug. PRs are welcomed!
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
function tr(key)
|
||||
return noctalia.tr(key)
|
||||
end
|
||||
|
||||
function barCfg(key)
|
||||
return barWidget.getConfig(key)
|
||||
end
|
||||
|
||||
function getState(key)
|
||||
return noctalia.state.get(key)
|
||||
end
|
||||
|
||||
function displayWidget(glyph: string, text: string, color: string?)
|
||||
if color == nil then
|
||||
color = "on_surface"
|
||||
end
|
||||
|
||||
if barCfg("colorize_text") then
|
||||
barWidget.setColor(color)
|
||||
end
|
||||
|
||||
if barCfg("colorize_glyph") then
|
||||
barWidget.setGlyphColor(color)
|
||||
end
|
||||
|
||||
if barCfg("show_glyph") then
|
||||
barWidget.setGlyph(glyph)
|
||||
end
|
||||
|
||||
if barCfg("show_text") then
|
||||
barWidget.setText(text)
|
||||
end
|
||||
end
|
||||
|
||||
function update()
|
||||
if getState("isRunning") then
|
||||
displayWidget(barCfg("checking_glyph"), tr("bar.checking"), barCfg("checking_color"))
|
||||
else
|
||||
if getState("isUpdateAvailable") then
|
||||
displayWidget(barCfg("update_available_glyph"), tr("bar.update_available"), barCfg("update_available_color"))
|
||||
else
|
||||
if getState("localHash") == "n/a" or getState("remoteHash") == "n/a" then
|
||||
displayWidget(barCfg("unknown_glyph"), tr("bar.unknown"), barCfg("unknown_color"))
|
||||
else
|
||||
displayWidget(barCfg("up_to_date_glyph"), tr("bar.up_to_date"), barCfg("up_to_date_color"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function onClick()
|
||||
noctalia.togglePanel("avivbintangaringga/nix-monitor:panel")
|
||||
end
|
||||
@@ -0,0 +1,111 @@
|
||||
noctalia.setUpdateInterval(100)
|
||||
|
||||
function tr(key)
|
||||
return noctalia.tr(key)
|
||||
end
|
||||
|
||||
function cfg(key)
|
||||
return noctalia.getConfig(key)
|
||||
end
|
||||
|
||||
function getState(key)
|
||||
return noctalia.state.get(key)
|
||||
end
|
||||
|
||||
function setState(key, val)
|
||||
noctalia.state.set(key, val)
|
||||
end
|
||||
|
||||
function watchState(key, cb)
|
||||
noctalia.state.watch(key, cb)
|
||||
end
|
||||
|
||||
local branch = cfg("branch")
|
||||
local updateCheckInterval = cfg("check_interval")
|
||||
local localHashCmd = "nixos-version --hash 2> /dev/null | cut -c -7"
|
||||
local remoteHashCmd = "git ls-remote https://github.com/NixOS/nixpkgs " .. branch .. " 2>/dev/null | cut -c 1-7"
|
||||
local completedCommands = 0
|
||||
|
||||
setState("lastCheckTime", 0)
|
||||
setState("lastCheckTimeStr", "n/a")
|
||||
setState("localHash", "n/a")
|
||||
setState("remoteHash", "n/a")
|
||||
setState("isRunning", false)
|
||||
setState("isUpdateAvailable", false)
|
||||
|
||||
function update()
|
||||
if (os.time() - getState("lastCheckTime")) >= 60 * updateCheckInterval then
|
||||
checkUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
function checkUpdate()
|
||||
if getState("isRunning") then
|
||||
return
|
||||
end
|
||||
|
||||
setState("lastCheckTime", os.time())
|
||||
noctalia.runAsync("date", function(date) setState("lastCheckTimeStr", date.stdout) end)
|
||||
|
||||
if cfg("show_checking_notification") then
|
||||
noctalia.notify(tr("notification.checking_update"))
|
||||
end
|
||||
|
||||
setState("isRunning", true)
|
||||
|
||||
completedCommands = 0
|
||||
|
||||
noctalia.runAsync(localHashCmd, getLocalHash)
|
||||
noctalia.runStream(remoteHashCmd, getRemoteHash)
|
||||
end
|
||||
|
||||
function getLocalHash(result)
|
||||
if getState("isRunning") ~= true then
|
||||
return
|
||||
end
|
||||
|
||||
setState("localHash", result.stdout)
|
||||
completedCommands += 1
|
||||
onHashCheckCompleted()
|
||||
end
|
||||
|
||||
function getRemoteHash(line)
|
||||
if getState("isRunning") ~= true then
|
||||
return
|
||||
end
|
||||
|
||||
setState("remoteHash", line)
|
||||
completedCommands += 1
|
||||
onHashCheckCompleted()
|
||||
end
|
||||
|
||||
function onHashCheckCompleted()
|
||||
if completedCommands < 2 then
|
||||
return
|
||||
end
|
||||
|
||||
if getState("localHash") ~= getState("remoteHash") then
|
||||
setState("isUpdateAvailable", true)
|
||||
if cfg("show_update_notification") then
|
||||
noctalia.notify(tr("notification.update_is_available"))
|
||||
end
|
||||
end
|
||||
|
||||
if cfg("show_checking_notification") then
|
||||
noctalia.notify(tr("notification.update_check_completed"))
|
||||
end
|
||||
|
||||
setState("isRunning", false)
|
||||
end
|
||||
|
||||
watchState("command", function(command)
|
||||
if command == "check" then
|
||||
checkUpdate()
|
||||
end
|
||||
|
||||
if command == "cancel" then
|
||||
noctalia.runAsync(`kill $(ps -ef | grep "git ls-remote https://github.com/NixOS/nixpkgs" | grep -v grep | awk '\{print $2\}')`)
|
||||
setState("isRunning", false)
|
||||
completedCommands = 0
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,186 @@
|
||||
function tr(key)
|
||||
return noctalia.tr(key)
|
||||
end
|
||||
|
||||
function barCfg(key)
|
||||
return barWidget.getConfig(key)
|
||||
end
|
||||
|
||||
function cfg(key)
|
||||
return noctalia.getConfig(key)
|
||||
end
|
||||
|
||||
function getState(key)
|
||||
return noctalia.state.get(key)
|
||||
end
|
||||
|
||||
function setState(key, val)
|
||||
noctalia.state.set(key, val)
|
||||
end
|
||||
|
||||
function watchState(key, cb)
|
||||
noctalia.state.watch(key, cb)
|
||||
end
|
||||
|
||||
local function labelBox(subtext, text, glyph)
|
||||
return ui.column({
|
||||
fill = "surface/0.7",
|
||||
flexGrow = 1,
|
||||
height = 100,
|
||||
radius = 8,
|
||||
align = "center",
|
||||
padding = 12,
|
||||
justify = "space_between"
|
||||
}, {
|
||||
ui.glyph({
|
||||
name = glyph,
|
||||
size = 30
|
||||
}),
|
||||
ui.label({
|
||||
text = text,
|
||||
fontWeight = "bold"
|
||||
}),
|
||||
ui.label({
|
||||
text = subtext,
|
||||
fontSize = 12,
|
||||
fontWeight = "medium"
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
local function render()
|
||||
local firstBtn = btnCheck
|
||||
|
||||
if getState("isRunning") then
|
||||
firstBtn = ui.button({
|
||||
text = tr("panel.cancel"),
|
||||
glyph = "cancel",
|
||||
flexGrow = 1,
|
||||
variant = "destructive",
|
||||
onClick = "onCancelClicked",
|
||||
})
|
||||
else
|
||||
if getState("isUpdateAvailable") then
|
||||
firstBtn = ui.button({
|
||||
text = tr("panel.update"),
|
||||
glyph = "progress-down",
|
||||
flexGrow = 1,
|
||||
variant = "primary",
|
||||
onClick = "onUpdateClicked",
|
||||
})
|
||||
else
|
||||
firstBtn = ui.button({
|
||||
text = tr("panel.check"),
|
||||
glyph = "search",
|
||||
flexGrow = 1,
|
||||
variant = "outline",
|
||||
onClick = "onCheckClicked",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local lastCheckTimeStr = getState("lastCheckTimeStr")
|
||||
|
||||
panel.render(
|
||||
ui.column({
|
||||
gap = 8
|
||||
}, {
|
||||
ui.row({
|
||||
justify = "space_between",
|
||||
align = "center"
|
||||
}, {
|
||||
ui.label({
|
||||
text = tr("panel.title"),
|
||||
fontSize = 20,
|
||||
}),
|
||||
|
||||
ui.label({
|
||||
text = cfg("branch"),
|
||||
fontSize = 14,
|
||||
}),
|
||||
}),
|
||||
ui.separator({
|
||||
thickness = 2,
|
||||
orientation = "horizontal",
|
||||
spacing = 4
|
||||
}),
|
||||
ui.row({
|
||||
gap = 8,
|
||||
align = "center",
|
||||
justify = "space_between"
|
||||
}, {
|
||||
labelBox(tr("panel.local"), getState("localHash"), "devices-pc"),
|
||||
labelBox(tr("panel.remote"), getState("remoteHash"), "world"),
|
||||
}),
|
||||
ui.separator({
|
||||
thickness = 2,
|
||||
orientation = "horizontal",
|
||||
spacing = 4
|
||||
}),
|
||||
ui.label({
|
||||
text = `{tr("panel.last_checked")}: {lastCheckTimeStr}`
|
||||
}),
|
||||
ui.row({
|
||||
gap = 8,
|
||||
align = "center",
|
||||
justify = "space_between"
|
||||
}, {
|
||||
firstBtn,
|
||||
ui.button({
|
||||
text = tr("panel.clean"),
|
||||
glyph = "trash",
|
||||
flexGrow = 1,
|
||||
variant = "destructive",
|
||||
onClick = "onCleanClicked",
|
||||
}),
|
||||
})
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
watchState("isRunning", render)
|
||||
watchState("lastCheckTimeStr", render)
|
||||
watchState("localHash", render)
|
||||
watchState("remoteHash", render)
|
||||
|
||||
function onOpen(_ctx)
|
||||
render()
|
||||
end
|
||||
|
||||
function onCheckClicked()
|
||||
setState("command", "check")
|
||||
end
|
||||
|
||||
function onCancelClicked()
|
||||
setState("command", "cancel")
|
||||
end
|
||||
|
||||
function onUpdateClicked()
|
||||
command = cfg("update_command")
|
||||
if command == "" then
|
||||
noctalia.notifyError(tr("notification.update_command_is_empty"))
|
||||
return
|
||||
end
|
||||
|
||||
if cfg("close_on_enter") then
|
||||
command = `{command} && echo && read -p "{tr("terminal.press_enter_to_exit")}"`
|
||||
end
|
||||
|
||||
panel.close()
|
||||
noctalia.runInTerminal(command)
|
||||
end
|
||||
|
||||
function onCleanClicked()
|
||||
command = cfg("clean_command")
|
||||
if command == "" then
|
||||
noctalia.notifyError(tr("notification.clean_command_is_empty"))
|
||||
return
|
||||
end
|
||||
|
||||
if cfg("close_on_enter") then
|
||||
command = `{command} && echo && read -p "{tr("terminal.press_enter_to_exit")}"`
|
||||
end
|
||||
|
||||
panel.close()
|
||||
noctalia.runInTerminal(command)
|
||||
end
|
||||
@@ -0,0 +1,164 @@
|
||||
id = "avivbintangaringga/nix-monitor"
|
||||
name = "Nix Monitor"
|
||||
version = "1.0.0"
|
||||
min_noctalia = "5.0.0"
|
||||
author = "avivbintangaringga"
|
||||
description = "Nixpkgs update monitor"
|
||||
tags = [ "nix", "utility" ]
|
||||
dependencies = [ "nixos-version", "git", "pkill" ]
|
||||
|
||||
[[widget]]
|
||||
id = "Nix Monitor"
|
||||
entry = "nix_monitor.luau"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "show_text"
|
||||
type = "boolean"
|
||||
label_key = "setting.widget.show_text.label"
|
||||
default = true
|
||||
|
||||
[[widget.setting]]
|
||||
key = "colorize_text"
|
||||
type = "boolean"
|
||||
label_key = "setting.widget.colorize_text.label"
|
||||
default = false
|
||||
|
||||
[[widget.setting]]
|
||||
key = "show_glyph"
|
||||
type = "boolean"
|
||||
label_key = "setting.widget.show_glyph.label"
|
||||
default = true
|
||||
|
||||
[[widget.setting]]
|
||||
key = "colorize_glyph"
|
||||
type = "boolean"
|
||||
label_key = "setting.widget.colorize_glyph.label"
|
||||
default = true
|
||||
|
||||
[[widget.setting]]
|
||||
key = "up_to_date_glyph"
|
||||
type = "glyph"
|
||||
label_key = "setting.widget.up_to_date_glyph.label"
|
||||
default = "rosette-discount-check"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "up_to_date_color"
|
||||
type = "color"
|
||||
label_key = "setting.widget.up_to_date_color.label"
|
||||
default = "#57ff57"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "checking_glyph"
|
||||
type = "glyph"
|
||||
label_key = "setting.widget.checking_glyph.label"
|
||||
default = "loader-3"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "checking_color"
|
||||
type = "color"
|
||||
label_key = "setting.widget.checking_color.label"
|
||||
default = "#ffeb57"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "update_available_glyph"
|
||||
type = "glyph"
|
||||
label_key = "setting.widget.update_available_glyph.label"
|
||||
default = "cloud-download"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "update_available_color"
|
||||
type = "color"
|
||||
label_key = "setting.widget.update_available_color.label"
|
||||
default = "#ff5757"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "unknown_glyph"
|
||||
type = "glyph"
|
||||
label_key = "setting.widget.unknown_glyph.label"
|
||||
default = "cloud-question"
|
||||
|
||||
[[widget.setting]]
|
||||
key = "unknown_color"
|
||||
type = "color"
|
||||
label_key = "setting.widget.unknown_color.label"
|
||||
default = "on_surface"
|
||||
|
||||
[[setting]]
|
||||
key = "check_interval"
|
||||
type = "int"
|
||||
label_key = "setting.check_interval.label"
|
||||
description_key = "setting.check_interval.description"
|
||||
default = 60
|
||||
min = 10
|
||||
|
||||
[[setting]]
|
||||
key = "show_checking_notification"
|
||||
type = "boolean"
|
||||
label_key = "setting.checking_notification.label"
|
||||
description_key = "setting.checking_notification.description"
|
||||
default = false
|
||||
|
||||
[[setting]]
|
||||
key = "show_update_notification"
|
||||
type = "boolean"
|
||||
label_key = "setting.show_update_notification.label"
|
||||
description_key = "setting.show_update_notification.description"
|
||||
default = true
|
||||
|
||||
[[setting]]
|
||||
key = "branch"
|
||||
type = "select"
|
||||
label_key = "setting.branch.label"
|
||||
description_key = "setting.branch.description"
|
||||
options = [
|
||||
{ value = "master", label = "Master" },
|
||||
{ value = "nixos-unstable", label = "NixOS Unstable" },
|
||||
{ value = "nixos-unstable-small", label = "NixOS Unstable Small" },
|
||||
{ value = "nixos-25.11", label = "NixOS 25.11" },
|
||||
{ value = "nixos-25.05", label = "NixOS 25.05" },
|
||||
{ value = "nixos-24.11", label = "NixOS 24.11" },
|
||||
{ value = "nixos-24.05", label = "NixOS 24.05" },
|
||||
{ value = "nixos-23.11", label = "NixOS 23.11" },
|
||||
{ value = "nixos-23.05", label = "NixOS 23.05" },
|
||||
{ value = "nixos-25.11-small", label = "NixOS 25.11 Small" },
|
||||
{ value = "nixos-25.05-small", label = "NixOS 25.05 Small" },
|
||||
{ value = "nixos-24.11-small", label = "NixOS 24.11 Small" },
|
||||
{ value = "nixos-24.05-small", label = "NixOS 24.05 Small" },
|
||||
{ value = "nixos-23.11-small", label = "NixOS 23.11 Small" },
|
||||
{ value = "nixos-23.05-small", label = "NixOS 23.05 Small" },
|
||||
]
|
||||
default = "nixos-unstable"
|
||||
|
||||
[[setting]]
|
||||
key = "update_command"
|
||||
type = "string"
|
||||
label_key = "setting.update_command.label"
|
||||
description_key = "setting.update_command.description"
|
||||
default = ""
|
||||
|
||||
[[setting]]
|
||||
key = "clean_command"
|
||||
type = "string"
|
||||
label_key = "setting.clean_command.label"
|
||||
description_key = "setting.clean_command.description"
|
||||
default = "nh clean all"
|
||||
|
||||
[[setting]]
|
||||
key = "close_on_enter"
|
||||
type = "boolean"
|
||||
label_key = "setting.close_on_enter.label"
|
||||
description_key = "setting.close_on_enter.description"
|
||||
default = true
|
||||
|
||||
[[panel]]
|
||||
id = "panel"
|
||||
entry = "panel.luau"
|
||||
placement = "attached"
|
||||
position = "auto"
|
||||
open_near_click = true
|
||||
width = 420
|
||||
height = 290
|
||||
|
||||
[[service]]
|
||||
id = "service"
|
||||
entry = "nix_monitor_service.luau"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"bar.checking": "Checking...",
|
||||
"bar.update_available": "Update available!",
|
||||
"bar.up_to_date": "Up to date",
|
||||
"bar.unknown": "Unknown",
|
||||
"notification.checking_update": "Checking update...",
|
||||
"notification.update_is_available": "Update is available!",
|
||||
"notification.update_check_completed": "Update check completed!",
|
||||
"notification.update_command_is_empty": "Update command is empty!",
|
||||
"notification.clean_command_is_empty": "Clean command is empty!",
|
||||
"panel.title": "Nix Monitor",
|
||||
"panel.cancel": "Cancel",
|
||||
"panel.update": "Update",
|
||||
"panel.check": "Check",
|
||||
"panel.clean": "Clean",
|
||||
"panel.local": "Local",
|
||||
"panel.remote": "Remote",
|
||||
"panel.last_checked": "Last checked",
|
||||
"terminal.press_enter_to_exit": "Press enter to exit...",
|
||||
"setting.widget.show_text.label": "Show text",
|
||||
"setting.widget.colorize_text.label": "Colorize text",
|
||||
"setting.widget.show_glyph.label": "Show glyph",
|
||||
"setting.widget.colorize_glyph.label": "Colorize glyph",
|
||||
"setting.widget.up_to_date_glyph.label": "Up to date glyph",
|
||||
"setting.widget.up_to_date_color.label": "Up to date color",
|
||||
"setting.widget.checking_glyph.label": "Checking glyph",
|
||||
"setting.widget.checking_color.label": "Checking color",
|
||||
"setting.widget.update_available_glyph.label": "Update available glyph",
|
||||
"setting.widget.update_available_color.label": "Update available color",
|
||||
"setting.widget.unknown_glyph.label": "Unknown status glyph",
|
||||
"setting.widget.unknown_color.label": "Unknown status color",
|
||||
"setting.check_interval.label": "Update check interval",
|
||||
"setting.check_interval.description": "How often to check for updates (in minutes)",
|
||||
"setting.checking_notification.label": "Show checking update notification",
|
||||
"setting.checking_notification.description": "Wether to show a notification whenever checking is in progress",
|
||||
"setting.show_update_notification.label": "Show update notification",
|
||||
"setting.show_update_notification.description": "Wether to show a notification whenever update is available",
|
||||
"setting.branch.label": "Branch",
|
||||
"setting.branch.description": "Which nixpkgs branch to use",
|
||||
"setting.update_command.label": "Update command",
|
||||
"setting.update_command.description": "Command to run when clicking Update button",
|
||||
"setting.clean_command.label": "Clean command",
|
||||
"setting.clean_command.description": "Command to run when clicking Clean button",
|
||||
"setting.close_on_enter.label": "Close on press Enter",
|
||||
"setting.close_on_enter.description": "Add 'Press enter to exit' on the terminal window"
|
||||
}
|
||||
Reference in New Issue
Block a user