mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 03:55:05 +00:00
feat: show git commit hash alongside the version
This commit is contained in:
@@ -43,6 +43,7 @@ Health check. **This is the only route exempt from authentication**: you can cal
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "5.3.0",
|
||||
"commit": "a24c9b9",
|
||||
"update_check": {
|
||||
"update_available": false,
|
||||
"current_version": "5.3.0",
|
||||
@@ -51,6 +52,8 @@ Health check. **This is the only route exempt from authentication**: you can cal
|
||||
}
|
||||
```
|
||||
|
||||
`commit` is the short git commit of the running checkout. It is `null` when the server cannot read one (for example, an installed package with no `.git` directory, or a host without git).
|
||||
|
||||
If the update check fails (for example, no network), `update_available` and `latest_version` are `null` while `current_version` still reports the installed version.
|
||||
|
||||
| Status | Meaning |
|
||||
|
||||
@@ -74,7 +74,7 @@ For example, `unshackle_debug_EXAMPLE_20260703-142530.jsonl`. The `<service>` ta
|
||||
Every line is a self-contained JSON object. The first line of a session records the environment:
|
||||
|
||||
```json
|
||||
{"timestamp":"2026-07-03T14:25:30.123456+00:00","session_id":"a1b2c3d4","level":"INFO","operation":"session_start","message":"Debug logging session started","context":{"unshackle_version":"5.3.0","python_version":"3.12.4 ...","platform":"Linux-6.18.5-x86_64","platform_system":"Linux","platform_release":"6.18.5"}}
|
||||
{"timestamp":"2026-07-03T14:25:30.123456+00:00","session_id":"a1b2c3d4","level":"INFO","operation":"session_start","message":"Debug logging session started","context":{"unshackle_version":"5.3.0","unshackle_commit":"a24c9b9","python_version":"3.12.4 ...","platform":"Linux-6.18.5-x86_64","platform_system":"Linux","platform_release":"6.18.5"}}
|
||||
```
|
||||
|
||||
Subsequent lines describe operations: service calls, DRM licence requests, vault lookups, downloader progress, and errors. Common fields include:
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Unit tests for the git commit probe behind the banner and the /api/health `commit` field.
|
||||
The probe runs at import time, so it must always return a string and must never raise, whatever
|
||||
state git and the checkout are in."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import unshackle.core
|
||||
from unshackle.core import _git_commit
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
# Without a .git the probe returns before it runs git, so the subprocess stubs below prove nothing.
|
||||
needs_checkout = pytest.mark.skipif(
|
||||
not (Path(unshackle.core.__file__).parents[2] / ".git").exists(), reason="not running from a git checkout"
|
||||
)
|
||||
|
||||
|
||||
def test_returns_str_in_this_checkout() -> None:
|
||||
assert isinstance(_git_commit(), str)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error",
|
||||
[
|
||||
FileNotFoundError("git"),
|
||||
PermissionError("git"),
|
||||
subprocess.TimeoutExpired(cmd="git", timeout=2),
|
||||
subprocess.SubprocessError("boom"),
|
||||
],
|
||||
)
|
||||
@needs_checkout
|
||||
def test_swallows_subprocess_errors(monkeypatch: pytest.MonkeyPatch, error: Exception) -> None:
|
||||
def raise_it(*args: object, **kwargs: object) -> None:
|
||||
raise error
|
||||
|
||||
monkeypatch.setattr(subprocess, "run", raise_it)
|
||||
assert _git_commit() == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("returncode", "stdout", "expected"),
|
||||
[
|
||||
(0, "a24c9b9\n", "a24c9b9"),
|
||||
(128, "", ""), # empty repo, corrupt repo, or a stale worktree pointer
|
||||
(0, "", ""), # no output despite success
|
||||
],
|
||||
)
|
||||
@needs_checkout
|
||||
def test_maps_git_result(monkeypatch: pytest.MonkeyPatch, returncode: int, stdout: str, expected: str) -> None:
|
||||
completed = subprocess.CompletedProcess(args=["git"], returncode=returncode, stdout=stdout, stderr="noise")
|
||||
monkeypatch.setattr(subprocess, "run", lambda *a, **k: completed)
|
||||
assert _git_commit() == expected
|
||||
@@ -1 +1,24 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "5.3.0"
|
||||
|
||||
|
||||
def _git_commit() -> str:
|
||||
root = Path(__file__).parents[2]
|
||||
if not (root / ".git").exists():
|
||||
return ""
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
["git", "rev-parse", "--short", "HEAD"],
|
||||
cwd=root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=2,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
return ""
|
||||
return proc.stdout.strip() if proc.returncode == 0 else ""
|
||||
|
||||
|
||||
__commit__ = _git_commit()
|
||||
|
||||
@@ -10,7 +10,7 @@ from rich.padding import Padding
|
||||
from rich.text import Text
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
from unshackle.core import __version__
|
||||
from unshackle.core import __commit__, __version__
|
||||
from unshackle.core.commands import Commands
|
||||
from unshackle.core.config import config
|
||||
from unshackle.core.console import ComfyRichHandler, console
|
||||
@@ -63,7 +63,8 @@ def main(version: bool, debug: bool) -> None:
|
||||
r" ▀▀▀ ▀▀ █▪ ▀▀▀▀ ▀▀▀ · ▀ ▀ ·▀▀▀ ·▀ ▀.▀▀▀ ▀▀▀ ",
|
||||
style="ascii.art",
|
||||
),
|
||||
f"v [repr.number]{__version__}[/] - © 2025-{datetime.now().year} - github.com/unshackle-dl/unshackle",
|
||||
f"v [repr.number]{__version__}[/]{f' ({__commit__})' if __commit__ else ''}"
|
||||
f" - © 2025-{datetime.now().year} - github.com/unshackle-dl/unshackle",
|
||||
),
|
||||
(1, 11, 1, 10),
|
||||
expand=True,
|
||||
|
||||
@@ -8,7 +8,7 @@ import click
|
||||
from aiohttp import web
|
||||
from aiohttp_swagger3 import SwaggerDocs, SwaggerInfo, SwaggerUiSettings
|
||||
|
||||
from unshackle.core import __version__
|
||||
from unshackle.core import __commit__, __version__
|
||||
from unshackle.core.api.errors import APIError, APIErrorCode, build_error_response, handle_api_exception
|
||||
from unshackle.core.api.handlers import (
|
||||
cancel_download_job_handler,
|
||||
@@ -105,6 +105,10 @@ async def health(request: web.Request) -> web.Response:
|
||||
version:
|
||||
type: string
|
||||
example: "2.0.0"
|
||||
commit:
|
||||
type: string
|
||||
nullable: true
|
||||
example: "a24c9b9"
|
||||
update_check:
|
||||
type: object
|
||||
properties:
|
||||
@@ -128,7 +132,9 @@ async def health(request: web.Request) -> web.Response:
|
||||
log.warning(f"Failed to check for updates: {e}")
|
||||
update_info = {"update_available": None, "current_version": __version__, "latest_version": None}
|
||||
|
||||
return web.json_response({"status": "ok", "version": __version__, "update_check": update_info})
|
||||
return web.json_response(
|
||||
{"status": "ok", "version": __version__, "commit": __commit__ or None, "update_check": update_info}
|
||||
)
|
||||
|
||||
|
||||
@api_handler
|
||||
|
||||
@@ -787,7 +787,7 @@ class DebugLogger:
|
||||
"""Log the start of a new session with environment information."""
|
||||
import platform
|
||||
|
||||
from unshackle.core import __version__
|
||||
from unshackle.core import __commit__, __version__
|
||||
|
||||
self.log(
|
||||
level="INFO",
|
||||
@@ -795,6 +795,7 @@ class DebugLogger:
|
||||
message="Debug logging session started",
|
||||
context={
|
||||
"unshackle_version": __version__,
|
||||
"unshackle_commit": __commit__ or None,
|
||||
"python_version": sys.version,
|
||||
"platform": platform.platform(),
|
||||
"platform_system": platform.system(),
|
||||
|
||||
Reference in New Issue
Block a user