Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
64ede34
feat: serial number matching and device conflict resolution
marcinpsk Feb 17, 2026
9439ce8
Merge origin/develop into feat/serial-matching-and-conflict-resolution
marcinpsk Feb 24, 2026
603bbf1
feat: devcontainer proxy/codespaces support, auto-create librenms_id …
marcinpsk Feb 15, 2026
7c5f5bc
Update devcontainer.json
marcinpsk Feb 15, 2026
6023a08
feat: serial number matching, conflict resolution, and device sync im…
marcinpsk Feb 16, 2026
b3e0550
feat: devcontainer proxy/codespaces support, auto-create librenms_id …
marcinpsk Feb 15, 2026
6595502
Update devcontainer.json
marcinpsk Feb 15, 2026
2272ef6
fix: address code review findings across views, templates, and forms
marcinpsk Feb 16, 2026
dffd573
fix: cables tab ignoring interface_name_field user preference
marcinpsk Feb 16, 2026
3b3acba
feat: add Modules tab to LibreNMS Sync page
marcinpsk Feb 17, 2026
e198de1
feat: add Install Module button and fix countdown timer
marcinpsk Feb 17, 2026
d07ce9c
feat: show sub-components and fix install redirect
marcinpsk Feb 17, 2026
d090560
Add DeviceTypeMapping model with full CRUD stack
marcinpsk Feb 17, 2026
a181d84
Add ModuleTypeMapping model, fix ordering, add contrib examples
marcinpsk Feb 17, 2026
7c50f2d
Add ModuleBayMapping model with CRUD stack, replace bay matching heur…
marcinpsk Feb 17, 2026
816b9a0
Add chassis inventory fallback for device type matching
marcinpsk Feb 17, 2026
2c7d899
Add {module_path} compatibility guard for module installation
marcinpsk Feb 17, 2026
56b1e4f
Add nested module branch installation and fix URL naming
marcinpsk Feb 17, 2026
4175af7
Add transceiver API integration for modules tab
marcinpsk Feb 17, 2026
8a3e6ec
Add Nokia vendor support and improve module bay matching
marcinpsk Feb 17, 2026
7895ec7
Scope module bay matching to parent module and fix install scroll
marcinpsk Feb 17, 2026
22ee52a
Update Nokia contrib mappings for corrected module types
marcinpsk Feb 17, 2026
3193210
Fix nested module bay matching and expression template evaluation
marcinpsk Feb 21, 2026
0ab6981
Add NormalizationRule: generic regex-based string normalization for m…
marcinpsk Feb 19, 2026
a47e8f8
Add manufacturer scope to NormalizationRule, transceiver mappings
marcinpsk Feb 19, 2026
739ea19
Add UfiSpace device type mappings to contrib examples
marcinpsk Feb 19, 2026
6133452
Fix device import validation: always set 'found' key on device_type r…
marcinpsk Feb 19, 2026
f4c880a
Fix Arcos inventory display and X2 slot resolution
marcinpsk Feb 19, 2026
49224f3
Fix duplicate items and ancestor chain check in module sync
marcinpsk Feb 19, 2026
5ed1e63
Fix Actions column layout: nowrap + shorter Branch button text
marcinpsk Feb 19, 2026
87a532a
Add description-based bay matching and device_type scope for install …
marcinpsk Feb 21, 2026
f64e050
Add Juniper, Arcos, and Finisar mappings to contrib YAML
marcinpsk Feb 19, 2026
1ec2930
Add regex bay mappings, bulk selection, collapsible help, module_path…
marcinpsk Feb 19, 2026
48050d4
Add bulk module install, breakout interface rules, and description ma…
marcinpsk Feb 19, 2026
8237a41
refactor: remove redundant post-install interface renaming logic
marcinpsk Feb 21, 2026
2aa523c
refactor: remove NormalizationRule, add InterfaceNameRule, clean up m…
marcinpsk Feb 21, 2026
16f89e7
refactor: remove InterfaceNameRule (moved to separate plugin), keep i…
marcinpsk Feb 21, 2026
b8ff5bf
Fix import cache staleness and module bay matching
marcinpsk Feb 21, 2026
4bf6346
Fix child bay scoping and branch install error handling
marcinpsk Feb 21, 2026
2f54930
Add e2e Playwright tests for module install workflow
marcinpsk Feb 22, 2026
7b99189
Add name conflict detection for nested modules using {module} token
marcinpsk Feb 22, 2026
5b88922
feat: detect {module_path} support via import instead of version string
marcinpsk Feb 22, 2026
49fb5af
feat: include port class in module sync inventory
marcinpsk Feb 22, 2026
edee517
feat: add Ciena 180-3530-900 to module type mappings
marcinpsk Feb 22, 2026
daa2e4e
fix: walk ancestor chain to filter nested port items
marcinpsk Feb 22, 2026
4d4243b
feat: add container class support and dedup transceiver entries
marcinpsk Feb 22, 2026
ee0e0d9
feat: expand module mappings for Juniper and multi-vendor SFPs
marcinpsk Feb 22, 2026
7a0634b
feat: use ifName for synthetic transceiver naming and add Nokia mappings
marcinpsk Feb 22, 2026
b7dfd97
fix: skip empty-model containers in ENTITY-MIB hierarchy
marcinpsk Feb 22, 2026
72c5d4a
feat: restore NormalizationRule CRUD stack and integrate into module …
marcinpsk Feb 23, 2026
b41d4c5
feat: Juniper MX SFP bay matching + vendor module types
marcinpsk Feb 23, 2026
f9110a6
Fix duplicate interface error and cross-FPC bay mismatch
marcinpsk Feb 23, 2026
e1381e2
refactor: address PR review feedback for user preferences
marcinpsk Feb 24, 2026
75628bf
fix: removed BulkInstallModulesView - unused
marcinpsk Feb 24, 2026
a676d6e
fix: ruff
marcinpsk Feb 24, 2026
b466553
Code review fixes: bugs, cleanup, and hardening
marcinpsk Feb 24, 2026
4b2533e
fix: devcontainer script hardening and cleanup
marcinpsk Feb 26, 2026
1fbc4f9
Code review fixes: safety, scoping, and cleanup (round 2)
marcinpsk Feb 26, 2026
bf3ad64
fix: gate MAC sync on exclude_columns
marcinpsk Feb 26, 2026
f09628c
fix: correct plugin-config.py path in diagnose.sh
marcinpsk Feb 26, 2026
12860a2
refactor: split import_utils.py into a package
marcinpsk Feb 26, 2026
04e4cc3
fix: update mock.patch paths for import_utils package refactor
marcinpsk Feb 26, 2026
27ed0a6
refactor: remove redundant inline Cluster import in device_operations
marcinpsk Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Below are the dev container defaults. The field name to change these defaults is
- Plugin loader: enabled; reads `.devcontainer/config/plugin-config.py` if present
- If `plugin-config.py` is missing: plugin is enabled with empty config (features won’t work until configured)



## 🔧 Configuration

### NetBox Version and Environment (use .devcontainer/.env)
Expand Down
16 changes: 10 additions & 6 deletions .devcontainer/scripts/diagnose.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/bin/bash
# netbox-librenms-plugin devcontainer script

echo "🔍 DevContainer Startup Diagnostics"
echo "=================================="

PLUGIN_WS_DIR="${PLUGIN_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
echo "📍 Current working directory: $(pwd)"
echo "👤 Current user: $(whoami)"
echo "🆔 User ID: $(id)"
Expand All @@ -26,18 +28,20 @@ echo " - Redis: $(timeout 3 bash -c 'cat < /dev/null > /dev/tcp/redis/6379' 2>/
echo ""
echo "🗂️ File System:"
echo " - NetBox venv: $(test -f /opt/netbox/venv/bin/activate && echo 'Exists' || echo 'Missing')"
echo " - Plugin directory: $(test -d /workspaces/netbox-librenms-plugin && echo 'Exists' || echo 'Missing')"
echo " - Setup script: $(test -f /workspaces/netbox-librenms-plugin/.devcontainer/scripts/setup.sh && echo 'Exists' || echo 'Missing')"
echo " - Start script: $(test -f /workspaces/netbox-librenms-plugin/.devcontainer/scripts/start-netbox.sh && echo 'Exists' || echo 'Missing')"
echo " - Start script executable: $(test -x /workspaces/netbox-librenms-plugin/.devcontainer/scripts/start-netbox.sh && echo 'Yes' || echo 'No')"
echo " - Plugin config: $(test -f /workspaces/netbox-librenms-plugin/.devcontainer/plugin-config.py && echo 'Found' || echo 'Missing (using defaults)')"
echo " - Plugin directory: $(test -d "$PLUGIN_WS_DIR" && echo 'Exists' || echo 'Missing')"
echo " - Setup script: $(test -f "$PLUGIN_WS_DIR/.devcontainer/scripts/setup.sh" && echo 'Exists' || echo 'Missing')"
echo " - Start script: $(test -f "$PLUGIN_WS_DIR/.devcontainer/scripts/start-netbox.sh" && echo 'Exists' || echo 'Missing')"
echo " - Start script executable: $(test -x "$PLUGIN_WS_DIR/.devcontainer/scripts/start-netbox.sh" && echo 'Yes' || echo 'No')"
echo " - Plugin config: $(test -f "$PLUGIN_WS_DIR/.devcontainer/config/plugin-config.py" && echo 'Found' || echo 'Missing (using defaults)')"
echo " - NetBox config path: /opt/netbox/netbox/netbox/configuration.py"

echo ""
echo "🚀 Process Status:"
if [ -f /tmp/netbox.pid ]; then
PID=$(cat /tmp/netbox.pid)
if kill -0 $PID 2>/dev/null; then
if [ -z "$PID" ]; then
echo " - NetBox server: PID file exists but is empty"
elif kill -0 "$PID" 2>/dev/null; then
echo " - NetBox server: Running (PID: $PID)"
else
echo " - NetBox server: PID file exists but process not running"
Expand Down
256 changes: 207 additions & 49 deletions .devcontainer/scripts/load-aliases.sh
Original file line number Diff line number Diff line change
@@ -1,67 +1,225 @@
#!/bin/bash
# netbox-librenms-plugin devcontainer script
# Quick alias loader for current session
# Usage: source .devcontainer/scripts/load-aliases.sh

export PATH="/opt/netbox/venv/bin:$PATH"
export DEBUG="${DEBUG:-True}"
PLUGIN_DIR="/workspaces/netbox-librenms-plugin"
PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

alias netbox-run-bg="$PLUGIN_DIR/.devcontainer/scripts/start-netbox.sh --background"
alias netbox-run="$PLUGIN_DIR/.devcontainer/scripts/start-netbox.sh"
# Clean up empty CA bundle vars (Compose/devcontainer inject "" when host var is
# unset, which breaks requests/curl). When setup.sh has installed custom CAs
# into the system trust store, point to it instead.
for _ca_var in REQUESTS_CA_BUNDLE SSL_CERT_FILE CURL_CA_BUNDLE; do
_val="${!_ca_var}"
if [ -z "$_val" ]; then
if [ -f /etc/ssl/certs/ca-certificates.crt ]; then
declare -x "$_ca_var=/etc/ssl/certs/ca-certificates.crt"
else
unset "$_ca_var"
fi
fi
done
unset _ca_var _val

# Load shared process management helpers
if ! source "$PLUGIN_DIR/.devcontainer/scripts/process-helpers.sh"; then
printf '%s\n' "Failed to load process-helpers.sh" >&2
return 1
fi

netbox-run-bg() { "$PLUGIN_DIR/.devcontainer/scripts/start-netbox.sh" --background; }
netbox-run() { "$PLUGIN_DIR/.devcontainer/scripts/start-netbox.sh"; }

# Robust stop command that kills both tracked and orphaned processes
alias netbox-stop='echo "🛑 Stopping NetBox and RQ workers..."; \
if [ -f /tmp/netbox.pid ]; then \
PID=$(cat /tmp/netbox.pid 2>/dev/null); \
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then \
kill "$PID" 2>/dev/null || kill -9 "$PID" 2>/dev/null; \
echo " Stopped NetBox (PID: $PID)"; \
fi; \
rm -f /tmp/netbox.pid; \
fi; \
if [ -f /tmp/rqworker.pid ]; then \
PID=$(cat /tmp/rqworker.pid 2>/dev/null); \
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then \
kill "$PID" 2>/dev/null || kill -9 "$PID" 2>/dev/null; \
echo " Stopped RQ worker (PID: $PID)"; \
fi; \
rm -f /tmp/rqworker.pid; \
fi; \
if pgrep -f "python.*rqworker" >/dev/null 2>&1; then \
ORPHAN_COUNT=$(pgrep -cf "python.*rqworker" 2>/dev/null || echo 0); \
pkill -9 -f "python.*rqworker" 2>/dev/null; \
echo " Killed $ORPHAN_COUNT orphaned RQ worker(s)"; \
fi; \
if pgrep -f "python.*runserver.*8000" >/dev/null 2>&1; then \
pkill -9 -f "python.*runserver.*8000" 2>/dev/null; \
echo " Killed orphaned NetBox server(s)"; \
fi; \
echo "✅ All processes stopped"'

alias netbox-restart="netbox-stop && sleep 1 && netbox-run-bg"
alias netbox-reload="cd $PLUGIN_DIR && (command -v uv >/dev/null 2>&1 && uv pip install -e . || pip install -e .) && netbox-restart"
netbox-stop() {
echo "🛑 Stopping NetBox and RQ workers..."
if [ -f /tmp/netbox.pid ]; then
local PID
PID=$(cat /tmp/netbox.pid 2>/dev/null)
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
if is_expected_pid "$PID" "python.*runserver.*8000"; then
graceful_kill_pid "$PID"
echo " Stopped NetBox (PID: $PID)"
else
echo " Skipping stale /tmp/netbox.pid (PID $PID is not NetBox runserver)"
fi
fi
rm -f /tmp/netbox.pid
fi
if [ -f /tmp/rqworker.pid ]; then
local PID
PID=$(cat /tmp/rqworker.pid 2>/dev/null)
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
if is_expected_pid "$PID" "python.*rqworker"; then
graceful_kill_pid "$PID"
echo " Stopped RQ worker (PID: $PID)"
else
echo " Skipping stale /tmp/rqworker.pid (PID $PID is not rqworker)"
fi
fi
rm -f /tmp/rqworker.pid
fi
if pgrep -f "python.*rqworker" >/dev/null 2>&1; then
local ORPHAN_COUNT
ORPHAN_COUNT=$(pgrep -cf "python.*rqworker" 2>/dev/null || echo 0)
graceful_kill_pattern "python.*rqworker"
echo " Killed $ORPHAN_COUNT orphaned RQ worker(s)"
fi
if pgrep -f "python.*runserver.*8000" >/dev/null 2>&1; then
graceful_kill_pattern "python.*runserver.*8000"
echo " Killed orphaned NetBox server(s)"
fi
echo "✅ All processes stopped"
}

netbox-restart() {
netbox-stop && sleep 1 && netbox-run-bg
}

netbox-reload() {
cd "$PLUGIN_DIR" || return 1
if command -v uv >/dev/null 2>&1; then
uv pip install -e . || return 1
else
pip install -e . || return 1
fi
netbox-restart
}

alias netbox-logs="tail -f /tmp/netbox.log"
alias netbox-status="[ -f /tmp/netbox.pid ] && kill -0 \$(cat /tmp/netbox.pid) 2>/dev/null && echo 'NetBox is running (PID: '\$(cat /tmp/netbox.pid)')' || echo 'NetBox is not running'; [ -f /tmp/rqworker.pid ] && kill -0 \$(cat /tmp/rqworker.pid) 2>/dev/null && echo 'RQ worker is running (PID: '\$(cat /tmp/rqworker.pid)')' || echo 'RQ worker is not running'"
alias rq-logs="tail -f /tmp/rqworker.log"
alias rq-status="[ -f /tmp/rqworker.pid ] && kill -0 \$(cat /tmp/rqworker.pid) 2>/dev/null && echo 'RQ worker is running (PID: '\$(cat /tmp/rqworker.pid)')' || echo 'RQ worker is not running'"
alias netbox-shell="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell"
alias netbox-test="cd $PLUGIN_DIR && source /opt/netbox/venv/bin/activate && python -m pytest"
alias netbox-manage="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py"
alias plugin-install="cd $PLUGIN_DIR && (command -v uv >/dev/null 2>&1 && uv pip install -e . || pip install -e .)"
alias ruff-check="cd $PLUGIN_DIR && ruff check ."
alias ruff-format="cd $PLUGIN_DIR && ruff format ."
alias ruff-fix="cd $PLUGIN_DIR && ruff check --fix ."
alias diagnose="$PLUGIN_DIR/.devcontainer/scripts/diagnose.sh"
alias plugins-install='if [ -f "$PLUGIN_DIR/.devcontainer/extra-requirements.txt" ]; then source /opt/netbox/venv/bin/activate && pip install -r "$PLUGIN_DIR/.devcontainer/extra-requirements.txt"; else echo "No .devcontainer/extra-requirements.txt found"; fi'

netbox-status() {
local PID
if [ -f /tmp/netbox.pid ]; then
PID=$(cat /tmp/netbox.pid 2>/dev/null)
if [ -n "$PID" ] && is_expected_pid "$PID" "python.*runserver.*8000"; then
echo "NetBox is running (PID: $PID)"
else
echo "NetBox is not running"
fi
else
echo "NetBox is not running"
fi
if [ -f /tmp/rqworker.pid ]; then
PID=$(cat /tmp/rqworker.pid 2>/dev/null)
if [ -n "$PID" ] && is_expected_pid "$PID" "python.*rqworker"; then
echo "RQ worker is running (PID: $PID)"
else
echo "RQ worker is not running"
fi
else
echo "RQ worker is not running"
fi
}

rq-status() {
local PID
if [ -f /tmp/rqworker.pid ]; then
PID=$(cat /tmp/rqworker.pid 2>/dev/null)
if [ -n "$PID" ] && is_expected_pid "$PID" "python.*rqworker"; then
echo "RQ worker is running (PID: $PID)"
else
echo "RQ worker is not running"
fi
else
echo "RQ worker is not running"
fi
}

netbox-shell() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell
}

netbox-test() {
cd "$PLUGIN_DIR" && source /opt/netbox/venv/bin/activate && python -m pytest "$@"
}

netbox-manage() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py "$@"
}

plugin-install() {
cd "$PLUGIN_DIR" || return 1
if command -v uv >/dev/null 2>&1; then
uv pip install -e .
else
pip install -e .
fi
}

plugins-install() {
if [ -f "$PLUGIN_DIR/.devcontainer/extra-requirements.txt" ]; then
source /opt/netbox/venv/bin/activate && pip install -r "$PLUGIN_DIR/.devcontainer/extra-requirements.txt"
else
echo "No .devcontainer/extra-requirements.txt found"
fi
}

ruff-check() { cd "$PLUGIN_DIR" && command ruff check .; }
ruff-format() { cd "$PLUGIN_DIR" && command ruff format .; }
ruff-fix() { cd "$PLUGIN_DIR" && command ruff check --fix .; }

diagnose() { "$PLUGIN_DIR/.devcontainer/scripts/diagnose.sh"; }

# RQ job inspection commands
alias rq-stats="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py rqstats"
alias rq-jobs="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \"from django_rq import get_queue; q = get_queue('default'); print(f'Jobs in queue: {len(q)}'); [print(f' {job.id[:8]}: {job.func_name} - {job.get_status()}') for job in q.jobs[:10]]\""
alias rq-failed="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \"from django_rq import get_failed_queue; q = get_failed_queue(); print(f'Failed jobs: {len(q)}'); [print(f' {job.id[:8]}: {job.func_name}') for job in q.jobs[:10]]\""
alias rq-recent="cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \"from core.models import Job; jobs = Job.objects.all().order_by('-created')[:10]; [print(f'{j.id}: {j.name[:50]} - {getattr(j.status, \\\"value\\\", j.status)} ({j.user})') for j in jobs]\""
rq-stats() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py rqstats
}

rq-jobs() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \
"from django_rq import get_queue; q = get_queue('default'); print(f'Jobs in queue: {len(q)}'); [print(f' {job.id[:8]}: {job.func_name} - {job.get_status()}') for job in q.jobs[:10]]"
}

rq-failed() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \
"from django_rq import get_failed_queue; q = get_failed_queue(); print(f'Failed jobs: {len(q)}'); [print(f' {job.id[:8]}: {job.func_name}') for job in q.jobs[:10]]"
}

rq-recent() {
cd /opt/netbox/netbox && source /opt/netbox/venv/bin/activate && python manage.py shell -c \
"from core.models import Job; jobs = Job.objects.all().order_by('-created')[:10]; [print(f'{j.id}: {j.name[:50]} - {getattr(j.status, \"value\", j.status)} ({j.user})') for j in jobs]"
}

# Help
alias dev-help='echo "🎯 NetBox LibreNMS Plugin Development Commands:"; echo ""; echo "📊 NetBox Server Management:"; echo " netbox-run-bg : Start NetBox in background"; echo " netbox-run : Start NetBox in foreground (for debugging)"; echo " netbox-stop : Stop NetBox and RQ worker"; echo " netbox-restart : Restart NetBox and RQ worker"; echo " netbox-reload : Reinstall plugin and restart NetBox"; echo " netbox-status : Check if NetBox and RQ worker are running"; echo " netbox-logs : View NetBox server logs"; echo ""; echo "⚙️ Background Jobs (RQ Worker):"; echo " rq-status : Check if RQ worker is running"; echo " rq-logs : View RQ worker logs"; echo " rq-stats : Show RQ queue statistics"; echo " rq-jobs : List jobs in default queue"; echo " rq-failed : List failed jobs"; echo " rq-recent : Show recent NetBox jobs"; echo ""; echo "🛠️ Development Tools:"; echo " netbox-shell : Open NetBox Django shell"; echo " netbox-test : Run plugin tests"; echo " netbox-manage : Run Django management commands"; echo " plugin-install : Reinstall plugin in development mode"; echo ""; echo "🧹 Code Quality:"; echo " ruff-check : Check code with Ruff"; echo " ruff-format : Format code with Ruff"; echo " ruff-fix : Auto-fix code issues with Ruff"; echo ""; echo "🔎 Diagnostics:"; echo " diagnose : Run startup diagnostics"; echo " dev-help : Show this help message"; echo ""; echo "📖 NetBox available at: http://localhost:8000 (admin/admin)"; echo ""'
dev-help() {
echo "🎯 NetBox LibreNMS Plugin Development Commands:"
echo ""
echo "📊 NetBox Server Management:"
echo " netbox-run-bg : Start NetBox in background"
echo " netbox-run : Start NetBox in foreground (for debugging)"
echo " netbox-stop : Stop NetBox and RQ worker"
echo " netbox-restart : Restart NetBox and RQ worker"
echo " netbox-reload : Reinstall plugin and restart NetBox"
echo " netbox-status : Check if NetBox and RQ worker are running"
echo " netbox-logs : View NetBox server logs"
echo ""
echo "⚙️ Background Jobs (RQ Worker):"
echo " rq-status : Check if RQ worker is running"
echo " rq-logs : View RQ worker logs"
echo " rq-stats : Show RQ queue statistics"
echo " rq-jobs : List jobs in default queue"
echo " rq-failed : List failed jobs"
echo " rq-recent : Show recent NetBox jobs"
echo ""
echo "🛠️ Development Tools:"
echo " netbox-shell : Open NetBox Django shell"
echo " netbox-test : Run plugin tests"
echo " netbox-manage : Run Django management commands"
echo " plugin-install : Reinstall plugin in development mode"
echo ""
echo "🧹 Code Quality:"
echo " ruff-check : Check code with Ruff"
echo " ruff-format : Format code with Ruff"
echo " ruff-fix : Auto-fix code issues with Ruff"
echo ""
echo "🔎 Diagnostics:"
echo " diagnose : Run startup diagnostics"
echo " dev-help : Show this help message"
echo ""
echo "📖 NetBox available at: http://localhost:8000 (admin/admin)"
}

echo "✅ Aliases loaded! Try: rq-status, rq-stats, rq-recent, dev-help"
24 changes: 24 additions & 0 deletions .devcontainer/scripts/process-helpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
# Shared process management helpers.
# Sourced by load-aliases.sh and start-netbox.sh.

# Graceful termination: SIGTERM, wait, then SIGKILL if still alive.
graceful_kill_pid() {
local pid="$1"
kill -15 "$pid" 2>/dev/null
sleep 2
kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null
}
Comment on lines +6 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider returning an exit status for callers that need it.

Currently graceful_kill_pid doesn't indicate success/failure. While not critical for current usage, a return value could be useful for future callers that need to know if the process was successfully terminated.

♻️ Optional: Add return status
 graceful_kill_pid() {
   local pid="$1"
   kill -15 "$pid" 2>/dev/null
   sleep 2
-  kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null
+  if kill -0 "$pid" 2>/dev/null; then
+    kill -9 "$pid" 2>/dev/null
+  fi
+  ! kill -0 "$pid" 2>/dev/null  # return 0 if process is gone
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
graceful_kill_pid() {
local pid="$1"
kill -15 "$pid" 2>/dev/null
sleep 2
kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null
}
graceful_kill_pid() {
local pid="$1"
kill -15 "$pid" 2>/dev/null
sleep 2
if kill -0 "$pid" 2>/dev/null; then
kill -9 "$pid" 2>/dev/null
fi
! kill -0 "$pid" 2>/dev/null # return 0 if process is gone
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.devcontainer/scripts/process-helpers.sh around lines 6 - 11, The helper
function graceful_kill_pid currently performs signals but never returns a
status; modify graceful_kill_pid to return a meaningful exit code indicating
success (process terminated) or failure (still running or error). After sending
SIGTERM and optional SIGKILL, check the process existence (e.g., via kill -0 or
wait) and return 0 when the PID was successfully terminated and a non-zero value
otherwise; preserve existing signaling behavior in graceful_kill_pid while
ensuring callers can inspect the function's exit status.


graceful_kill_pattern() {
local pattern="$1"
pkill -15 -f "$pattern" 2>/dev/null
sleep 2
pgrep -f "$pattern" >/dev/null 2>&1 && pkill -9 -f "$pattern" 2>/dev/null
}

# Verify a PID matches the expected process before killing it
is_expected_pid() {
local pid="$1" pattern="$2"
ps -p "$pid" -o args= 2>/dev/null | grep -Eq "$pattern"
}
Loading
Loading