extensions.py 5.9 KB
Newer Older
1
import os
2
import threading
3

4
from modules import shared, errors, cache
5
from modules.gitpython_hack import Repo
A
AUTOMATIC 已提交
6
from modules.paths_internal import extensions_dir, extensions_builtin_dir, script_path  # noqa: F401
7 8 9

extensions = []

10
os.makedirs(extensions_dir, exist_ok=True)
11

12 13

def active():
14
    if shared.cmd_opts.disable_all_extensions or shared.opts.disable_all_extensions == "all":
15
        return []
16
    elif shared.cmd_opts.disable_extra_extensions or shared.opts.disable_all_extensions == "extra":
17 18 19
        return [x for x in extensions if x.enabled and x.is_builtin]
    else:
        return [x for x in extensions if x.enabled]
20 21 22


class Extension:
23
    lock = threading.Lock()
24
    cached_fields = ['remote', 'commit_date', 'branch', 'commit_hash', 'version']
25

A
AUTOMATIC 已提交
26
    def __init__(self, name, path, enabled=True, is_builtin=False):
27 28 29 30 31
        self.name = name
        self.path = path
        self.enabled = enabled
        self.status = ''
        self.can_update = False
A
AUTOMATIC 已提交
32
        self.is_builtin = is_builtin
33 34
        self.commit_hash = ''
        self.commit_date = None
35
        self.version = ''
36
        self.branch = None
37 38 39
        self.remote = None
        self.have_info_from_repo = False

40 41 42 43 44 45 46
    def to_dict(self):
        return {x: getattr(self, x) for x in self.cached_fields}

    def from_dict(self, d):
        for field in self.cached_fields:
            setattr(self, field, d[field])

47
    def read_info_from_repo(self):
48
        if self.is_builtin or self.have_info_from_repo:
49 50
            return

51 52 53 54 55 56 57 58
        def read_from_repo():
            with self.lock:
                if self.have_info_from_repo:
                    return

                self.do_read_info_from_repo()

                return self.to_dict()
W
w-e-w 已提交
59 60 61 62 63
        try:
            d = cache.cached_data_for_file('extensions-git', self.name, os.path.join(self.path, ".git"), read_from_repo)
            self.from_dict(d)
        except FileNotFoundError:
            pass
64
        self.status = 'unknown' if self.status == '' else self.status
65

66
    def do_read_info_from_repo(self):
67 68
        repo = None
        try:
69
            if os.path.exists(os.path.join(self.path, ".git")):
70
                repo = Repo(self.path)
71
        except Exception:
72
            errors.report(f"Error reading github repository info from {self.path}", exc_info=True)
73 74 75 76

        if repo is None or repo.bare:
            self.remote = None
        else:
77
            try:
78
                self.remote = next(repo.remote().urls, None)
79 80
                commit = repo.head.commit
                self.commit_date = commit.committed_date
81 82
                if repo.active_branch:
                    self.branch = repo.active_branch.name
83 84
                self.commit_hash = commit.hexsha
                self.version = self.commit_hash[:8]
85

86
            except Exception:
87
                errors.report(f"Failed reading extension data from Git repository ({self.name})", exc_info=True)
88
                self.remote = None
89

90 91
        self.have_info_from_repo = True

92 93 94 95 96 97 98 99 100
    def list_files(self, subdir, extension):
        from modules import scripts

        dirpath = os.path.join(self.path, subdir)
        if not os.path.isdir(dirpath):
            return []

        res = []
        for filename in sorted(os.listdir(dirpath)):
101
            res.append(scripts.ScriptFile(self.path, filename, os.path.join(dirpath, filename)))
102 103 104 105 106 107

        res = [x for x in res if os.path.splitext(x.path)[1].lower() == extension and os.path.isfile(x.path)]

        return res

    def check_updates(self):
108
        repo = Repo(self.path)
A
Adam Huganir 已提交
109
        for fetch in repo.remote().fetch(dry_run=True):
110 111
            if fetch.flags != fetch.HEAD_UPTODATE:
                self.can_update = True
112
                self.status = "new commits"
113 114
                return

115 116 117 118 119 120 121 122 123 124 125
        try:
            origin = repo.rev_parse('origin')
            if repo.head.commit != origin:
                self.can_update = True
                self.status = "behind HEAD"
                return
        except Exception:
            self.can_update = False
            self.status = "unknown (remote error)"
            return

126 127 128
        self.can_update = False
        self.status = "latest"

129
    def fetch_and_reset_hard(self, commit='origin'):
130
        repo = Repo(self.path)
131 132
        # Fix: `error: Your local changes to the following files would be overwritten by merge`,
        # because WSL2 Docker set 755 file permissions instead of 644, this results to the error.
A
Adam Huganir 已提交
133
        repo.git.fetch(all=True)
134
        repo.git.reset(commit, hard=True)
135
        self.have_info_from_repo = False
136 137 138 139 140


def list_extensions():
    extensions.clear()

141
    if not os.path.isdir(extensions_dir):
142 143
        return

144 145 146
    if shared.cmd_opts.disable_all_extensions:
        print("*** \"--disable-all-extensions\" arg was used, will not load any extensions ***")
    elif shared.opts.disable_all_extensions == "all":
147
        print("*** \"Disable all extensions\" option was set, will not load any extensions ***")
148 149
    elif shared.cmd_opts.disable_extra_extensions:
        print("*** \"--disable-extra-extensions\" arg was used, will only load built-in extensions ***")
150 151
    elif shared.opts.disable_all_extensions == "extra":
        print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***")
152

153
    extension_paths = []
154
    for dirname in [extensions_dir, extensions_builtin_dir]:
A
AUTOMATIC 已提交
155 156
        if not os.path.isdir(dirname):
            return
157

A
AUTOMATIC 已提交
158 159 160 161 162
        for extension_dirname in sorted(os.listdir(dirname)):
            path = os.path.join(dirname, extension_dirname)
            if not os.path.isdir(path):
                continue

163
            extension_paths.append((extension_dirname, path, dirname == extensions_builtin_dir))
A
AUTOMATIC 已提交
164

165
    for dirname, path, is_builtin in extension_paths:
A
AUTOMATIC 已提交
166
        extension = Extension(name=dirname, path=path, enabled=dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin)
167
        extensions.append(extension)