dragonpilot - 基於 openpilot 的開源駕駛輔助系統
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
2.6 KiB

import abc
import os
from pathlib import Path
import subprocess
from typing import List
from markdown_it import MarkdownIt
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock")
STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging")
FINALIZED = os.path.join(STAGING_ROOT, "finalized")
def run(cmd: list[str], cwd: str = None) -> str:
return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8')
class UpdateStrategy(abc.ABC):
def __init__(self):
self.params = Params()
@abc.abstractmethod
def init(self) -> None:
pass
@abc.abstractmethod
def cleanup(self) -> None:
pass
@abc.abstractmethod
def get_available_channels(self) -> List[str]:
"""List of available channels to install, (branches, releases, etc)"""
@abc.abstractmethod
def current_channel(self) -> str:
"""Current channel installed"""
@abc.abstractmethod
def fetched_path(self) -> str:
"""Path to the fetched update"""
@property
def target_channel(self) -> str:
"""Target Channel"""
b: str | None = self.params.get("UpdaterTargetBranch", encoding='utf-8')
if b is None:
b = self.current_channel()
return b
@abc.abstractmethod
def update_ready(self) -> bool:
"""Check if an update is ready to be installed"""
@abc.abstractmethod
def update_available(self) -> bool:
"""Check if an update is available for the current channel"""
@abc.abstractmethod
def describe_current_channel(self) -> tuple[str, str]:
"""Describe the current channel installed, (description, release_notes)"""
@abc.abstractmethod
def describe_ready_channel(self) -> tuple[str, str]:
"""Describe the channel that is ready to be installed, (description, release_notes)"""
@abc.abstractmethod
def fetch_update(self) -> None:
pass
@abc.abstractmethod
def finalize_update(self) -> None:
pass
def set_consistent_flag(consistent: bool) -> None:
os.sync()
consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent"))
if consistent:
consistent_file.touch()
elif not consistent:
consistent_file.unlink(missing_ok=True)
os.sync()
def parse_release_notes(releases_md: str) -> str:
try:
r = releases_md.split('\n\n', 1)[0] # Slice latest release notes
try:
return str(MarkdownIt().render(r))
except Exception:
return r + "\n"
except FileNotFoundError:
pass
except Exception:
cloudlog.exception("failed to parse release notes")
return ""