[FR] Switch from git to dulwich (#4966)

* [FR] Switch from pure git to dulwich Fixes #4942

* fix lenght

* change length again

* change length again
This commit is contained in:
Matthias Mair 2023-06-05 11:27:46 +02:00 committed by GitHub
parent 2ed7eefa27
commit 58a33c2e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 40 additions and 82 deletions

View File

@ -3,17 +3,27 @@
Provides information on the current InvenTree version Provides information on the current InvenTree version
""" """
import datetime
import os import os
import pathlib
import re import re
import subprocess
import django import django
from dulwich.repo import NotGitRepository, Repo
from .api_version import INVENTREE_API_VERSION from .api_version import INVENTREE_API_VERSION
# InvenTree software version # InvenTree software version
INVENTREE_SW_VERSION = "0.12.0 dev" INVENTREE_SW_VERSION = "0.12.0 dev"
# Discover git
try:
main_repo = Repo(pathlib.Path(__file__).parent.parent.parent)
main_commit = main_repo[main_repo.head()]
except NotGitRepository:
main_commit = None
def inventreeInstanceName(): def inventreeInstanceName():
"""Returns the InstanceName settings for the current database.""" """Returns the InstanceName settings for the current database."""
@ -93,30 +103,6 @@ def inventreeDjangoVersion():
return django.get_version() return django.get_version()
git_available: bool = None # is git available and used by the install?
def check_for_git():
"""Check if git is available on the install."""
global git_available
if git_available is not None:
return git_available
try:
subprocess.check_output('git --version'.split(), stderr=subprocess.DEVNULL)
except Exception:
git_available = False
if git_available is None:
try:
subprocess.check_output('git status'.split(), stderr=subprocess.DEVNULL)
git_available = True
return True
except Exception:
git_available = False
def inventreeCommitHash(): def inventreeCommitHash():
"""Returns the git commit hash for the running codebase.""" """Returns the git commit hash for the running codebase."""
# First look in the environment variables, i.e. if running in docker # First look in the environment variables, i.e. if running in docker
@ -125,13 +111,9 @@ def inventreeCommitHash():
if commit_hash: if commit_hash:
return commit_hash return commit_hash
if not check_for_git(): if main_commit is None:
return None
try:
return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
except Exception: # pragma: no cover
return None return None
return main_commit.sha().hexdigest()[0:7]
def inventreeCommitDate(): def inventreeCommitDate():
@ -142,11 +124,6 @@ def inventreeCommitDate():
if commit_date: if commit_date:
return commit_date.split(' ')[0] return commit_date.split(' ')[0]
if not check_for_git(): if main_commit is None:
return None
try:
d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
return d.split(' ')[0]
except Exception: # pragma: no cover
return None return None
return str(datetime.datetime.fromtimestamp(main_commit.commit_time).date())

View File

@ -7,13 +7,11 @@ The main code for plugin special sauce is in the plugin registry in `InvenTree/p
import logging import logging
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
from maintenance_mode.core import set_maintenance_mode from maintenance_mode.core import set_maintenance_mode
from InvenTree.ready import canAppAccessDatabase from InvenTree.ready import canAppAccessDatabase
from plugin import registry from plugin import registry
from plugin.helpers import check_git_version, log_error
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
@ -47,9 +45,3 @@ class PluginAppConfig(AppConfig):
# drop out of maintenance # drop out of maintenance
# makes sure we did not have an error in reloading and maintenance is still active # makes sure we did not have an error in reloading and maintenance is still active
set_maintenance_mode(False) set_maintenance_mode(False)
# check git version
registry.git_is_modern = check_git_version()
if not registry.git_is_modern: # pragma: no cover # simulating old git seems not worth it for coverage
log_error(_('Your environment has an outdated git version. This prevents InvenTree from loading plugin details.'), 'load')

View File

@ -1,10 +1,10 @@
"""Helpers for plugin app.""" """Helpers for plugin app."""
import datetime
import inspect import inspect
import logging import logging
import pathlib import pathlib
import pkgutil import pkgutil
import subprocess
import sysconfig import sysconfig
import traceback import traceback
from importlib.metadata import entry_points from importlib.metadata import entry_points
@ -14,6 +14,8 @@ from django.conf import settings
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from dulwich.repo import NotGitRepository, Repo
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
@ -109,21 +111,28 @@ def get_entrypoints():
# region git-helpers # region git-helpers
def get_git_log(path): def get_git_log(path):
"""Get dict with info of the last commit to file named in path.""" """Get dict with info of the last commit to file named in path."""
from plugin import registry
output = None output = None
if registry.git_is_modern: path = path.replace(str(settings.BASE_DIR.parent), '')[1:]
path = path.replace(str(settings.BASE_DIR.parent), '')[1:]
command = ['git', 'log', '-n', '1', "--pretty=format:'%H%n%aN%n%aE%n%aI%n%f%n%G?%n%GK'", '--follow', '--', path] try:
walker = Repo.discover(path).get_walker(paths=[path.encode()], max_entries=1)
try: try:
output = str(subprocess.check_output(command, cwd=settings.BASE_DIR.parent), 'utf-8')[1:-1] commit = next(iter(walker)).commit
if output: except StopIteration:
output = output.split('\n')
except subprocess.CalledProcessError: # pragma: no cover
pass
except FileNotFoundError: # pragma: no cover
# Most likely the system does not have 'git' installed
pass pass
else:
output = [
commit.sha().hexdigest(),
commit.author.decode().split('<')[0][:-1],
commit.author.decode().split('<')[1][:-1],
datetime.datetime.fromtimestamp(commit.author_time, ).isoformat(),
commit.message.decode().split('\n')[0],
'E',
None
]
except NotGitRepository:
pass
if not output: if not output:
output = 7 * [''] # pragma: no cover output = 7 * [''] # pragma: no cover
@ -131,29 +140,6 @@ def get_git_log(path):
return {'hash': output[0], 'author': output[1], 'mail': output[2], 'date': output[3], 'message': output[4], 'verified': output[5], 'key': output[6]} return {'hash': output[0], 'author': output[1], 'mail': output[2], 'date': output[3], 'message': output[4], 'verified': output[5], 'key': output[6]}
def check_git_version():
"""Returns if the current git version supports modern features."""
# get version string
try:
output = str(subprocess.check_output(['git', '--version'], cwd=settings.BASE_DIR.parent), 'utf-8')
except subprocess.CalledProcessError: # pragma: no cover
return False
except FileNotFoundError: # pragma: no cover
# Most likely the system does not have 'git' installed
return False
# process version string
try:
version = output[12:-1].split(".")
if len(version) > 1 and version[0] == '2':
if len(version) > 2 and int(version[1]) >= 22:
return True
except ValueError: # pragma: no cover
pass
return False # pragma: no cover
class GitStatus: class GitStatus:
"""Class for resolving git gpg singing state.""" """Class for resolving git gpg singing state."""

View File

@ -60,7 +60,6 @@ class PluginsRegistry:
# flags # flags
self.is_loading = False # Are plugins being loaded right now self.is_loading = False # Are plugins being loaded right now
self.apps_loading = True # Marks if apps were reloaded yet self.apps_loading = True # Marks if apps were reloaded yet
self.git_is_modern = True # Is a modern version of git available
self.installed_apps = [] # Holds all added plugin_paths self.installed_apps = [] # Holds all added plugin_paths

View File

@ -28,6 +28,7 @@ django-user-sessions # user sessions in DB
django-weasyprint # django weasyprint integration django-weasyprint # django weasyprint integration
djangorestframework # DRF framework djangorestframework # DRF framework
django-xforwardedfor-middleware # IP forwarding metadata django-xforwardedfor-middleware # IP forwarding metadata
dulwich # pure Python git integration
drf-spectacular # DRF API documentation drf-spectacular # DRF API documentation
feedparser # RSS newsfeed parser feedparser # RSS newsfeed parser
gunicorn # Gunicorn web server gunicorn # Gunicorn web server

View File

@ -140,6 +140,8 @@ djangorestframework==3.14.0
# drf-spectacular # drf-spectacular
drf-spectacular==0.26.2 drf-spectacular==0.26.2
# via -r requirements.in # via -r requirements.in
dulwich==0.21.5
# via -r requirements.in
et-xmlfile==1.1.0 et-xmlfile==1.1.0
# via openpyxl # via openpyxl
feedparser==6.0.10 feedparser==6.0.10
@ -279,6 +281,7 @@ uritemplate==4.1.1
# drf-spectacular # drf-spectacular
urllib3==2.0.2 urllib3==2.0.2
# via # via
# dulwich
# requests # requests
# sentry-sdk # sentry-sdk
wcwidth==0.2.6 wcwidth==0.2.6