git_deploy: validation improvements
Add a check for shallow repos and move validation logic to the GitRepo class. Additionally report the real number of commits behind. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
51e3568aac
commit
01141fa10e
|
@ -47,6 +47,7 @@ class GitDeploy(AppDeploy):
|
||||||
async def initialize(self) -> Dict[str, Any]:
|
async def initialize(self) -> Dict[str, Any]:
|
||||||
storage = await super().initialize()
|
storage = await super().initialize()
|
||||||
await self.repo.restore_state(storage)
|
await self.repo.restore_state(storage)
|
||||||
|
self._is_valid = self.repo.is_valid()
|
||||||
if not self.needs_refresh():
|
if not self.needs_refresh():
|
||||||
self.repo.log_repo_info()
|
self.repo.log_repo_info()
|
||||||
return storage
|
return storage
|
||||||
|
@ -61,16 +62,10 @@ class GitDeploy(AppDeploy):
|
||||||
self._is_valid = False
|
self._is_valid = False
|
||||||
await self.repo.refresh_repo_state(need_fetch=need_fetch)
|
await self.repo.refresh_repo_state(need_fetch=need_fetch)
|
||||||
self.log_info(f"Channel: {self.channel}")
|
self.log_info(f"Channel: {self.channel}")
|
||||||
if not self.repo.check_is_valid():
|
self._is_valid = self.repo.is_valid()
|
||||||
self.log_info("Repo validation check failed")
|
if not self._is_valid:
|
||||||
if self.server.is_debug_enabled():
|
self.log_info("Repo validation check failed, updates disabled")
|
||||||
self._is_valid = True
|
|
||||||
self.log_info(
|
|
||||||
"Repo debug enabled, overriding validity checks")
|
|
||||||
else:
|
|
||||||
self.log_info("Updates on repo disabled")
|
|
||||||
else:
|
else:
|
||||||
self._is_valid = True
|
|
||||||
self.log_info("Validity check for git repo passed")
|
self.log_info("Validity check for git repo passed")
|
||||||
self._save_state()
|
self._save_state()
|
||||||
|
|
||||||
|
@ -103,6 +98,11 @@ class GitDeploy(AppDeploy):
|
||||||
self.notify_status("Attempting Repo Recovery...")
|
self.notify_status("Attempting Repo Recovery...")
|
||||||
dep_info = await self._collect_dependency_info()
|
dep_info = await self._collect_dependency_info()
|
||||||
if hard:
|
if hard:
|
||||||
|
if self.repo.is_submodule_or_worktree():
|
||||||
|
raise self.server.error(
|
||||||
|
f"Cannot re-clone git repo {self.name}, it is either "
|
||||||
|
f"a submodule or worktree."
|
||||||
|
)
|
||||||
await self.repo.clone()
|
await self.repo.clone()
|
||||||
if self.channel != Channel.DEV:
|
if self.channel != Channel.DEV:
|
||||||
if self.repo.upstream_commit != "?":
|
if self.repo.upstream_commit != "?":
|
||||||
|
@ -287,6 +287,7 @@ class GitRepo:
|
||||||
self.fetch_timeout_handle: Optional[asyncio.Handle] = None
|
self.fetch_timeout_handle: Optional[asyncio.Handle] = None
|
||||||
self.fetch_input_recd: bool = False
|
self.fetch_input_recd: bool = False
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
self.is_shallow = False
|
||||||
|
|
||||||
async def restore_state(self, storage: Dict[str, Any]) -> None:
|
async def restore_state(self, storage: Dict[str, Any]) -> None:
|
||||||
self.valid_git_repo: bool = storage.get('repo_valid', False)
|
self.valid_git_repo: bool = storage.get('repo_valid', False)
|
||||||
|
@ -310,6 +311,7 @@ class GitRepo:
|
||||||
self.head_detached: bool = storage.get('head_detached', False)
|
self.head_detached: bool = storage.get('head_detached', False)
|
||||||
self.git_messages: List[str] = storage.get('git_messages', [])
|
self.git_messages: List[str] = storage.get('git_messages', [])
|
||||||
self.commits_behind: List[Dict[str, Any]] = storage.get('commits_behind', [])
|
self.commits_behind: List[Dict[str, Any]] = storage.get('commits_behind', [])
|
||||||
|
self.commits_behind_count: int = storage.get('cbh_count', 0)
|
||||||
self.diverged: bool = storage.get("diverged", False)
|
self.diverged: bool = storage.get("diverged", False)
|
||||||
self.repo_corrupt: bool = storage.get('corrupt', False)
|
self.repo_corrupt: bool = storage.get('corrupt', False)
|
||||||
def_rbs = self.capture_state_for_rollback()
|
def_rbs = self.capture_state_for_rollback()
|
||||||
|
@ -317,6 +319,8 @@ class GitRepo:
|
||||||
self.rollback_branch: str = storage.get('rollback_branch', def_rbs["branch"])
|
self.rollback_branch: str = storage.get('rollback_branch', def_rbs["branch"])
|
||||||
rbv = storage.get('rollback_version', self.current_version)
|
rbv = storage.get('rollback_version', self.current_version)
|
||||||
self.rollback_version = GitVersion(str(rbv))
|
self.rollback_version = GitVersion(str(rbv))
|
||||||
|
if not await self._detect_git_dir():
|
||||||
|
self.valid_git_repo = False
|
||||||
if self.valid_git_repo:
|
if self.valid_git_repo:
|
||||||
await self.set_current_instance()
|
await self.set_current_instance()
|
||||||
self._check_warnings()
|
self._check_warnings()
|
||||||
|
@ -341,6 +345,7 @@ class GitRepo:
|
||||||
'head_detached': self.head_detached,
|
'head_detached': self.head_detached,
|
||||||
'git_messages': self.git_messages,
|
'git_messages': self.git_messages,
|
||||||
'commits_behind': self.commits_behind,
|
'commits_behind': self.commits_behind,
|
||||||
|
'cbh_count': self.commits_behind_count,
|
||||||
'diverged': self.diverged,
|
'diverged': self.diverged,
|
||||||
'corrupt': self.repo_corrupt
|
'corrupt': self.repo_corrupt
|
||||||
}
|
}
|
||||||
|
@ -402,8 +407,8 @@ class GitRepo:
|
||||||
|
|
||||||
# Get Commits Behind
|
# Get Commits Behind
|
||||||
self.commits_behind = []
|
self.commits_behind = []
|
||||||
cbh = await self.get_commits_behind()
|
if self.commits_behind_count > 0:
|
||||||
if cbh:
|
cbh = await self.get_commits_behind()
|
||||||
tagged_commits = await self.get_tagged_commits()
|
tagged_commits = await self.get_tagged_commits()
|
||||||
debug_msg = '\n'.join([f"{k}: {v}" for k, v in tagged_commits.items()])
|
debug_msg = '\n'.join([f"{k}: {v}" for k, v in tagged_commits.items()])
|
||||||
logging.debug(f"Git Repo {self.alias}: Tagged Commits\n{debug_msg}")
|
logging.debug(f"Git Repo {self.alias}: Tagged Commits\n{debug_msg}")
|
||||||
|
@ -431,16 +436,7 @@ class GitRepo:
|
||||||
async def _check_repo_status(self) -> bool:
|
async def _check_repo_status(self) -> bool:
|
||||||
async with self.git_operation_lock:
|
async with self.git_operation_lock:
|
||||||
self.valid_git_repo = False
|
self.valid_git_repo = False
|
||||||
if self.git_folder_path.is_file():
|
if not await self._detect_git_dir():
|
||||||
# Submodules have a file that contain the path to
|
|
||||||
# the .git folder
|
|
||||||
eventloop = self.server.get_event_loop()
|
|
||||||
data = await eventloop.run_in_thread(self.git_folder_path.read_text)
|
|
||||||
ident, _, gitdir = data.partition(":")
|
|
||||||
if ident.strip() != "gitdir" or not gitdir.strip():
|
|
||||||
return False
|
|
||||||
self.git_folder_path = pathlib.Path(gitdir).expanduser().resolve()
|
|
||||||
if not self.git_folder_path.is_dir():
|
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Git Repo {self.alias}: path '{self.src_path}'"
|
f"Git Repo {self.alias}: path '{self.src_path}'"
|
||||||
" is not a valid git repo")
|
" is not a valid git repo")
|
||||||
|
@ -469,6 +465,21 @@ class GitRepo:
|
||||||
await self.set_current_instance()
|
await self.set_current_instance()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def _detect_git_dir(self) -> bool:
|
||||||
|
if self.git_folder_path.is_file():
|
||||||
|
# Submodules have a file that contain the path to
|
||||||
|
# the .git folder
|
||||||
|
eventloop = self.server.get_event_loop()
|
||||||
|
data = await eventloop.run_in_thread(self.git_folder_path.read_text)
|
||||||
|
ident, _, gitdir = data.partition(":")
|
||||||
|
if ident.strip() != "gitdir" or not gitdir.strip():
|
||||||
|
return False
|
||||||
|
self.git_folder_path = pathlib.Path(gitdir).expanduser().resolve()
|
||||||
|
if self.git_folder_path.is_dir():
|
||||||
|
self.is_shallow = self.git_folder_path.joinpath("shallow").is_file()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
async def _find_current_branch(self) -> None:
|
async def _find_current_branch(self) -> None:
|
||||||
# Populate list of current branches
|
# Populate list of current branches
|
||||||
blist = await self.list_branches()
|
blist = await self.list_branches()
|
||||||
|
@ -559,6 +570,7 @@ class GitRepo:
|
||||||
return moved
|
return moved
|
||||||
|
|
||||||
async def _get_upstream_version(self) -> GitVersion:
|
async def _get_upstream_version(self) -> GitVersion:
|
||||||
|
self.commits_behind_count = 0
|
||||||
if self.channel == Channel.DEV:
|
if self.channel == Channel.DEV:
|
||||||
self.upstream_commit = await self.rev_parse(
|
self.upstream_commit = await self.rev_parse(
|
||||||
f"{self.git_remote}/{self.git_branch}"
|
f"{self.git_remote}/{self.git_branch}"
|
||||||
|
@ -581,6 +593,9 @@ class GitRepo:
|
||||||
upstream_ver_str = tag
|
upstream_ver_str = tag
|
||||||
break
|
break
|
||||||
self.upstream_commit = upstream_commit
|
self.upstream_commit = upstream_commit
|
||||||
|
if self.upstream_commit != "?":
|
||||||
|
rl_args = f"HEAD..{self.upstream_commit} --count"
|
||||||
|
self.commits_behind_count = int(await self.rev_list(rl_args))
|
||||||
return GitVersion(upstream_ver_str)
|
return GitVersion(upstream_ver_str)
|
||||||
|
|
||||||
async def _set_versions(
|
async def _set_versions(
|
||||||
|
@ -612,8 +627,7 @@ class GitRepo:
|
||||||
count = await self.rev_list(f"{self.upstream_commit} --count")
|
count = await self.rev_list(f"{self.upstream_commit} --count")
|
||||||
else:
|
else:
|
||||||
log_msg += "\nRemote has diverged, approximating dev count. "
|
log_msg += "\nRemote has diverged, approximating dev count. "
|
||||||
count = await self.rev_list(f"{self.upstream_commit}..HEAD --count")
|
count = str(self.commits_behind_count + current_version.dev_count)
|
||||||
count = str(int(count) + current_version.dev_count)
|
|
||||||
upstream_version = GitVersion(f"{tag}-{count}-inferred")
|
upstream_version = GitVersion(f"{tag}-{count}-inferred")
|
||||||
log_msg += f"Falling back to inferred version: {upstream_version}"
|
log_msg += f"Falling back to inferred version: {upstream_version}"
|
||||||
logging.info(log_msg)
|
logging.info(log_msg)
|
||||||
|
@ -675,7 +689,8 @@ class GitRepo:
|
||||||
f"Rollback Version: {self.rollback_version}\n"
|
f"Rollback Version: {self.rollback_version}\n"
|
||||||
f"Is Dirty: {self.current_version.dirty}\n"
|
f"Is Dirty: {self.current_version.dirty}\n"
|
||||||
f"Is Detached: {self.head_detached}\n"
|
f"Is Detached: {self.head_detached}\n"
|
||||||
f"Commits Behind: {len(self.commits_behind)}\n"
|
f"Is Shallow: {self.is_shallow}\n"
|
||||||
|
f"Commits Behind Count: {self.commits_behind_count}\n"
|
||||||
f"Diverged: {self.diverged}"
|
f"Diverged: {self.diverged}"
|
||||||
f"{warnings}"
|
f"{warnings}"
|
||||||
)
|
)
|
||||||
|
@ -699,6 +714,8 @@ class GitRepo:
|
||||||
self.repo_warnings.append("Detached HEAD detected")
|
self.repo_warnings.append("Detached HEAD detected")
|
||||||
if self.diverged:
|
if self.diverged:
|
||||||
self.repo_warnings.append("Repo has diverged from remote")
|
self.repo_warnings.append("Repo has diverged from remote")
|
||||||
|
if self.is_dirty():
|
||||||
|
self.repo_warnings.append("Repo has modified files (dirty)")
|
||||||
if len(self.managing_instances) > 1:
|
if len(self.managing_instances) > 1:
|
||||||
instances = "\n".join([f" {ins}" for ins in self.managing_instances])
|
instances = "\n".join([f" {ins}" for ins in self.managing_instances])
|
||||||
self.repo_warnings.append(
|
self.repo_warnings.append(
|
||||||
|
@ -711,9 +728,6 @@ class GitRepo:
|
||||||
ro_msg += "\n".join(self.repo_warnings)
|
ro_msg += "\n".join(self.repo_warnings)
|
||||||
self.server.add_log_rollover_item(f"umgr_{self.alias}_warn", ro_msg, log=False)
|
self.server.add_log_rollover_item(f"umgr_{self.alias}_warn", ro_msg, log=False)
|
||||||
|
|
||||||
def check_is_valid(self):
|
|
||||||
return not self.head_detached and not self.diverged
|
|
||||||
|
|
||||||
def _verify_repo(self, check_remote: bool = False) -> None:
|
def _verify_repo(self, check_remote: bool = False) -> None:
|
||||||
if not self.valid_git_repo:
|
if not self.valid_git_repo:
|
||||||
raise self.server.error(
|
raise self.server.error(
|
||||||
|
@ -1010,6 +1024,7 @@ class GitRepo:
|
||||||
'is_dirty': self.current_version.dirty,
|
'is_dirty': self.current_version.dirty,
|
||||||
'detached': self.head_detached,
|
'detached': self.head_detached,
|
||||||
'commits_behind': self.commits_behind,
|
'commits_behind': self.commits_behind,
|
||||||
|
'commits_behind_count': self.commits_behind_count,
|
||||||
'git_messages': self.git_messages,
|
'git_messages': self.git_messages,
|
||||||
'full_version_string': self.current_version.full_version,
|
'full_version_string': self.current_version.full_version,
|
||||||
'pristine': not self.current_version.dirty,
|
'pristine': not self.current_version.dirty,
|
||||||
|
@ -1029,6 +1044,31 @@ class GitRepo:
|
||||||
def is_current(self) -> bool:
|
def is_current(self) -> bool:
|
||||||
return self.current_commit == self.upstream_commit
|
return self.current_commit == self.upstream_commit
|
||||||
|
|
||||||
|
def is_submodule_or_worktree(self):
|
||||||
|
return (
|
||||||
|
self.src_path.joinpath(".git").is_file() and
|
||||||
|
self.git_folder_path.parent.name in ("modules", "worktrees")
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return (
|
||||||
|
not self.is_damaged() and
|
||||||
|
not self.has_recoverable_errors()
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_damaged(self) -> bool:
|
||||||
|
# A damaged repo requires a clone to recover
|
||||||
|
return not self.valid_git_repo or self.repo_corrupt
|
||||||
|
|
||||||
|
def has_recoverable_errors(self) -> bool:
|
||||||
|
# These errors should be recoverable using a git reset
|
||||||
|
detached_err = False if self.server.is_debug_enabled() else self.head_detached
|
||||||
|
return (
|
||||||
|
self.diverged or
|
||||||
|
self.is_dirty() or
|
||||||
|
detached_err
|
||||||
|
)
|
||||||
|
|
||||||
async def _check_lock_file_exists(self, remove: bool = False) -> bool:
|
async def _check_lock_file_exists(self, remove: bool = False) -> bool:
|
||||||
lock_path = self.git_folder_path.joinpath("index.lock")
|
lock_path = self.git_folder_path.joinpath("index.lock")
|
||||||
if lock_path.is_file():
|
if lock_path.is_file():
|
||||||
|
|
Loading…
Reference in New Issue