mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-08-09 17:26:20 +00:00
Updates tab - Unified OS + Application update card with per-section Apply buttons and a combined "Apply OS + <app>" footer button - Helper-scripts install detection: uses helper_slug from managed_installs cache (hostname fuzzy-match against helpers_cache) so the button surfaces even when /usr/bin/update was removed - Runs the community-scripts helper INSIDE the CT via pct exec so build.func picks the silent update path (PHS_SILENT=1) instead of the install menu — works with and without /usr/bin/update - HELPER_SLUG env passthrough from backend to apply_updates.sh: falls back to constructing the ct/<slug>.sh URL when the CT no longer carries the marker file - Post-apply state refresh via managed_installs.check_for_updates(force) in the /applied hook so the badge updates without a manual reload Options card - Rewrote as view / edit mode split with a single Edit button - Unified apply defaults (snapshot + storage + restart) shared by manual and scheduled runs - Scheduled updates (M5): cron picker + preset dropdown + What-to-update target + Delete schedule button, wired to a background scheduler thread that fires apply_updates.sh headless with the schedule's env vars - External host cron detection with variant + scope reporting (tteck-legacy / community-scripts / custom, OS-only), shown as an informational chip only in edit mode App tab editor - Multi-app registration with per-app upstream tracking method (github / http_json / docker_hub) - Card-contrast pattern in edit mode (bg-card + bg-background inputs) - Auto-heal for missing installed_version via alt_detectors + file_fallbacks Curated tracking hints (M6) - Add http_json upstream for Plex (plex.tv API) - Add binary+github hints for Emby (MediaBrowser/Emby.Releases) and PhotoPrism (photoprism/photoprism) - Extend CI merge whitelist with upstream_type / upstream_url / upstream_json_path / docker_image Tab reorder - LXC modal tabs: Status | App | Updates | Mounts | Backups | Firewall apply_updates.sh - New helper execution path: parse ct/<slug>.sh URL, run inside CT with PHS_SILENT=1, respecting HELPER_SLUG fallback when /usr/bin/update is missing
161 lines
6.9 KiB
YAML
161 lines
6.9 KiB
YAML
name: Update App Tracking Hints
|
|
|
|
on:
|
|
# Manual trigger from the Actions UI
|
|
workflow_dispatch:
|
|
|
|
# Re-merge whenever the generator, workflow, or the maintainer-
|
|
# curated runtime overrides change. `runtime_verified_overrides.json`
|
|
# is the file to edit when a real LXC reveals a canonical path the
|
|
# community-scripts helper doesn't ship (legacy /app/package.json,
|
|
# /opt/vaultwarden/bin/vaultwarden, etc.) — the generator folds it
|
|
# into the operational catalog every run.
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- ".github/scripts/generate_app_tracking_catalog.py"
|
|
- ".github/workflows/update-app-tracking-hints.yml"
|
|
- "json/runtime_verified_overrides.json"
|
|
|
|
# Regen every 6h — picks up new community-scripts LXC apps and
|
|
# detector-relevant script edits without needing a manual trigger.
|
|
schedule:
|
|
- cron: "0 */6 * * *"
|
|
|
|
jobs:
|
|
update-hints:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: ⬇️ Checkout the repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: 🐍 Set up Python
|
|
uses: actions/setup-python@v4
|
|
with:
|
|
python-version: "3.11"
|
|
|
|
- name: ⚙️ Generate app_tracking_hints.generated.json (intermediate)
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
# The generator writes 4 files; only `.generated.json` is
|
|
# consumed downstream by the merge step. The v2 catalog and
|
|
# per-app audit are useful for local review but not kept in
|
|
# the repo — written under /tmp so they never appear as
|
|
# dirty files here.
|
|
#
|
|
# `--runtime-overrides` folds real-CT evidence into the
|
|
# operational hints (canonical paths, cross-method fallbacks
|
|
# per app) so the runtime doesn't get fed helper-marker
|
|
# false-positives.
|
|
run: |
|
|
python .github/scripts/generate_app_tracking_catalog.py \
|
|
--helpers-cache json/helpers_cache.json \
|
|
--existing json/app_tracking_hints.json \
|
|
--runtime-overrides json/runtime_verified_overrides.json \
|
|
--output json/app_tracking_hints.generated.json \
|
|
--v2-output /tmp/app_tracking_catalog.v2.json \
|
|
--audit-output /tmp/app_tracking_hints.audit.json
|
|
|
|
- name: 🧬 Smart-merge generated into app_tracking_hints.json
|
|
# Single source of truth: `app_tracking_hints.json` is the ONE
|
|
# file. It contains 3 kinds of entries:
|
|
# 1. Auto-verified from community-scripts (the generator
|
|
# manages every "generator-owned" field on these).
|
|
# 2. User-edited additions to those entries — extra fields
|
|
# the generator doesn't touch (default_ports,
|
|
# file_fallbacks, custom logo overrides…).
|
|
# 3. User-only entries the generator can't verify (Docker,
|
|
# AdGuard, Pi-hole, WireGuard, …) — left alone.
|
|
# Merge rule: for slugs the generator produces, refresh only
|
|
# the whitelisted fields; preserve everything else. For slugs
|
|
# NOT in the generator's output, keep the existing entry
|
|
# untouched.
|
|
run: |
|
|
python - <<'PY'
|
|
import json
|
|
from pathlib import Path
|
|
|
|
GEN = Path("json/app_tracking_hints.generated.json")
|
|
OUT = Path("json/app_tracking_hints.json")
|
|
|
|
# Fields owned by the generator — refreshed on every run.
|
|
# These are all populated deterministically by the generator
|
|
# (the audit script folds `runtime_verified_overrides.json`
|
|
# in as it runs), so a local hand-edit for a generator-known
|
|
# slug would get overwritten on the next tick. To add a new
|
|
# canonical path or a cross-method fallback for a slug the
|
|
# generator already knows, edit `runtime_verified_overrides
|
|
# .json` — that file IS the maintainer-controlled input.
|
|
#
|
|
# For user-only slugs (Docker, WireGuard, Pi-hole and any
|
|
# other entry not in the generator's output) EVERY field is
|
|
# preserved verbatim by the merge below — the whitelist only
|
|
# governs generator-covered slugs.
|
|
GENERATOR_FIELDS = {
|
|
"installed_via", "package", "file_path", "file_regex",
|
|
"binary_path", "repo", "github_source", "tag_regex",
|
|
"installed_regex",
|
|
# Upstream source discriminator + per-type fields
|
|
# (http_json + docker_hub). Kept in the whitelist so a
|
|
# curated entry in runtime_verified_overrides.json can
|
|
# supply them and the smart merge won't drop them on the
|
|
# next regeneration.
|
|
"upstream_type", "upstream_url", "upstream_json_path",
|
|
"docker_image",
|
|
"logo", "website",
|
|
"default_ports", "file_fallbacks", "alt_detectors",
|
|
}
|
|
|
|
generated = json.loads(GEN.read_text(encoding="utf-8"))
|
|
existing = {}
|
|
if OUT.is_file():
|
|
try:
|
|
existing = json.loads(OUT.read_text(encoding="utf-8"))
|
|
if not isinstance(existing, dict):
|
|
existing = {}
|
|
except json.JSONDecodeError:
|
|
existing = {}
|
|
|
|
merged = {}
|
|
for slug, gen_entry in generated.items():
|
|
base = dict(existing.get(slug) or {})
|
|
# Refresh generator-owned fields (add/update).
|
|
for k, v in gen_entry.items():
|
|
if k in GENERATOR_FIELDS:
|
|
base[k] = v
|
|
# Drop generator-owned fields that the generator no
|
|
# longer emits for this slug (e.g. path renamed away).
|
|
for k in list(base):
|
|
if k in GENERATOR_FIELDS and k not in gen_entry:
|
|
del base[k]
|
|
merged[slug] = base
|
|
# Preserve user-only entries the generator can't verify.
|
|
for slug, entry in existing.items():
|
|
if slug not in generated and isinstance(entry, dict):
|
|
merged[slug] = dict(entry)
|
|
|
|
OUT.write_text(json.dumps(merged, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
|
|
added = sorted(set(generated) - set(existing))
|
|
removed = sorted(set(existing) - set(generated) - {s for s, e in existing.items() if not (
|
|
set(e.keys()) - GENERATOR_FIELDS
|
|
)})
|
|
print(f"merged: {len(merged)} entries "
|
|
f"(generated={len(generated)}, existing={len(existing)})")
|
|
if added:
|
|
print(f" new from generator: {len(added)}")
|
|
# Clean up the intermediate file so it doesn't get committed.
|
|
GEN.unlink()
|
|
PY
|
|
|
|
- name: 📤 Commit + push if changed
|
|
run: |
|
|
git config user.name "ProxMenuxBot"
|
|
git config user.email "bot@proxmenux.local"
|
|
git add json/app_tracking_hints.json
|
|
git diff --cached --quiet || git commit -m "Update app tracking hints"
|
|
git push
|