upgrade wakatime-cli to v10.2.1
This commit is contained in:
parent
5e34f3f6a7
commit
202df81e04
5 changed files with 191 additions and 107 deletions
|
@ -1,7 +1,7 @@
|
||||||
__title__ = 'wakatime'
|
__title__ = 'wakatime'
|
||||||
__description__ = 'Common interface to the WakaTime api.'
|
__description__ = 'Common interface to the WakaTime api.'
|
||||||
__url__ = 'https://github.com/wakatime/wakatime'
|
__url__ = 'https://github.com/wakatime/wakatime'
|
||||||
__version_info__ = ('10', '1', '3')
|
__version_info__ = ('10', '2', '1')
|
||||||
__version__ = '.'.join(__version_info__)
|
__version__ = '.'.join(__version_info__)
|
||||||
__author__ = 'Alan Hamlett'
|
__author__ = 'Alan Hamlett'
|
||||||
__author_email__ = 'alan@wakatime.com'
|
__author_email__ = 'alan@wakatime.com'
|
||||||
|
|
|
@ -52,100 +52,128 @@ def parse_arguments():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# define supported command line arguments
|
# define supported command line arguments
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description='Common interface for the ' +
|
||||||
description='Common interface for the WakaTime api.')
|
'WakaTime api.')
|
||||||
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
||||||
action=FileAction,
|
action=FileAction,
|
||||||
help='absolute path to file for the heartbeat; can also be a '+
|
help='Absolute path to file for the heartbeat. Can ' +
|
||||||
'url, domain, or app when --entity-type is not file')
|
'also be a url, domain or app when ' +
|
||||||
|
'--entity-type is not file.')
|
||||||
parser.add_argument('--file', dest='file', action=FileAction,
|
parser.add_argument('--file', dest='file', action=FileAction,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
|
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
|
||||||
help='your wakatime api key; uses api_key from '+
|
help='Your wakatime api key; uses api_key from ' +
|
||||||
'~/.wakatime.cfg by default')
|
'~/.wakatime.cfg by default.')
|
||||||
parser.add_argument('--write', dest='is_write',
|
parser.add_argument('--write', dest='is_write', action='store_true',
|
||||||
action='store_true',
|
help='When set, tells api this heartbeat was ' +
|
||||||
help='when set, tells api this heartbeat was triggered from '+
|
'triggered from writing to a file.')
|
||||||
'writing to a file')
|
|
||||||
parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes,
|
parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes,
|
||||||
help='optional text editor plugin name and version '+
|
help='Optional text editor plugin name and version ' +
|
||||||
'for User-Agent header')
|
'for User-Agent header.')
|
||||||
parser.add_argument('--time', dest='timestamp', metavar='time',
|
parser.add_argument('--time', dest='timestamp', metavar='time',
|
||||||
type=float, action=StoreWithoutQuotes,
|
type=float, action=StoreWithoutQuotes,
|
||||||
help='optional floating-point unix epoch timestamp; '+
|
help='Optional floating-point unix epoch timestamp. ' +
|
||||||
'uses current time by default')
|
'Uses current time by default.')
|
||||||
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
|
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
|
||||||
help='optional line number; current line being edited')
|
help='Optional line number. This is the current ' +
|
||||||
parser.add_argument('--cursorpos', dest='cursorpos', action=StoreWithoutQuotes,
|
'line being edited.')
|
||||||
help='optional cursor position in the current file')
|
parser.add_argument('--cursorpos', dest='cursorpos',
|
||||||
parser.add_argument('--entity-type', dest='entity_type', action=StoreWithoutQuotes,
|
action=StoreWithoutQuotes,
|
||||||
help='entity type for this heartbeat. can be one of "file", '+
|
help='Optional cursor position in the current file.')
|
||||||
'"domain", or "app"; defaults to file.')
|
parser.add_argument('--entity-type', dest='entity_type',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Entity type for this heartbeat. Can be ' +
|
||||||
|
'"file", "domain" or "app". Defaults to "file".')
|
||||||
|
parser.add_argument('--category', dest='category',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Category of this heartbeat activity. Can be ' +
|
||||||
|
'"coding", "building", "indexing", ' +
|
||||||
|
'"debugging", "running tests", ' +
|
||||||
|
'"manual testing", "browsing", ' +
|
||||||
|
'"code reviewing" or "designing". ' +
|
||||||
|
'Defaults to "coding".')
|
||||||
parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
|
parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
|
||||||
help='optional proxy configuration. Supports HTTPS '+
|
help='Optional proxy configuration. Supports HTTPS '+
|
||||||
'and SOCKS proxies. For example: '+
|
'and SOCKS proxies. For example: '+
|
||||||
'https://user:pass@host:port or '+
|
'https://user:pass@host:port or '+
|
||||||
'socks5://user:pass@host:port or ' +
|
'socks5://user:pass@host:port or ' +
|
||||||
'domain\\user:pass')
|
'domain\\user:pass')
|
||||||
parser.add_argument('--no-ssl-verify', dest='nosslverify',
|
parser.add_argument('--no-ssl-verify', dest='nosslverify',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='disables SSL certificate verification for HTTPS '+
|
help='Disables SSL certificate verification for HTTPS '+
|
||||||
'requests. By default, SSL certificates are verified.')
|
'requests. By default, SSL certificates are ' +
|
||||||
|
'verified.')
|
||||||
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
|
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
|
||||||
help='optional project name')
|
help='Optional project name.')
|
||||||
parser.add_argument('--alternate-project', dest='alternate_project', action=StoreWithoutQuotes,
|
parser.add_argument('--alternate-project', dest='alternate_project',
|
||||||
help='optional alternate project name; auto-discovered project '+
|
action=StoreWithoutQuotes,
|
||||||
'takes priority')
|
help='Optional alternate project name. ' +
|
||||||
parser.add_argument('--alternate-language', dest='alternate_language', action=StoreWithoutQuotes,
|
'Auto-discovered project takes priority.')
|
||||||
|
parser.add_argument('--alternate-language', dest='alternate_language',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--language', dest='language', action=StoreWithoutQuotes,
|
parser.add_argument('--language', dest='language',
|
||||||
help='optional language name; if valid, takes priority over '+
|
action=StoreWithoutQuotes,
|
||||||
'auto-detected language')
|
help='Optional language name. If valid, takes ' +
|
||||||
parser.add_argument('--hostname', dest='hostname', action=StoreWithoutQuotes, help='hostname of '+
|
'priority over auto-detected language.')
|
||||||
'current machine.')
|
parser.add_argument('--hostname', dest='hostname',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Hostname of current machine.')
|
||||||
parser.add_argument('--disable-offline', dest='offline',
|
parser.add_argument('--disable-offline', dest='offline',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
help='disables offline time logging instead of queuing logged time')
|
help='Disables offline time logging instead of ' +
|
||||||
|
'queuing logged time.')
|
||||||
parser.add_argument('--disableoffline', dest='offline_deprecated',
|
parser.add_argument('--disableoffline', dest='offline_deprecated',
|
||||||
action='store_true', help=argparse.SUPPRESS)
|
action='store_true',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--hide-filenames', dest='hide_filenames',
|
parser.add_argument('--hide-filenames', dest='hide_filenames',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='obfuscate filenames; will not send file names to api')
|
help='Obfuscate filenames. Will not send file names ' +
|
||||||
|
'to api.')
|
||||||
parser.add_argument('--hidefilenames', dest='hidefilenames',
|
parser.add_argument('--hidefilenames', dest='hidefilenames',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--exclude', dest='exclude', action='append',
|
parser.add_argument('--exclude', dest='exclude', action='append',
|
||||||
help='filename patterns to exclude from logging; POSIX regex '+
|
help='Filename patterns to exclude from logging. ' +
|
||||||
'syntax; can be used more than once')
|
'POSIX regex syntax. Can be used more than once.')
|
||||||
|
parser.add_argument('--exclude-unknown-project',
|
||||||
|
dest='exclude_unknown_project', action='store_true',
|
||||||
|
help='When set, any activity where the project ' +
|
||||||
|
'cannot be detected will be ignored.')
|
||||||
parser.add_argument('--include', dest='include', action='append',
|
parser.add_argument('--include', dest='include', action='append',
|
||||||
help='filename patterns to log; when used in combination with '+
|
help='Filename patterns to log. When used in ' +
|
||||||
'--exclude, files matching include will still be logged; '+
|
'combination with --exclude, files matching ' +
|
||||||
'POSIX regex syntax; can be used more than once')
|
'include will still be logged. POSIX regex ' +
|
||||||
|
'syntax. Can be used more than once.')
|
||||||
parser.add_argument('--include-only-with-project-file',
|
parser.add_argument('--include-only-with-project-file',
|
||||||
dest='include_only_with_project_file',
|
dest='include_only_with_project_file',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='disables tracking folders unless they contain '+
|
help='Disables tracking folders unless they contain ' +
|
||||||
'a .wakatime-project file; defaults to false')
|
'a .wakatime-project file. Defaults to false.')
|
||||||
parser.add_argument('--ignore', dest='ignore', action='append',
|
parser.add_argument('--ignore', dest='ignore', action='append',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
|
parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='reads extra heartbeats from STDIN as a JSON array until EOF')
|
help='Reads extra heartbeats from STDIN as a JSON ' +
|
||||||
parser.add_argument('--log-file', dest='log_file', action=StoreWithoutQuotes,
|
'array until EOF.')
|
||||||
help='defaults to ~/.wakatime.log')
|
parser.add_argument('--log-file', dest='log_file',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Defaults to ~/.wakatime.log.')
|
||||||
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
|
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
|
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
|
||||||
help='heartbeats api url; for debugging with a local server')
|
help='Heartbeats api url. For debugging with a ' +
|
||||||
|
'local server.')
|
||||||
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
|
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--timeout', dest='timeout', type=int, action=StoreWithoutQuotes,
|
parser.add_argument('--timeout', dest='timeout', type=int,
|
||||||
help='number of seconds to wait when sending heartbeats to api; '+
|
action=StoreWithoutQuotes,
|
||||||
'defaults to 60 seconds')
|
help='Number of seconds to wait when sending ' +
|
||||||
|
'heartbeats to api. Defaults to 60 seconds.')
|
||||||
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
|
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
|
||||||
help='defaults to ~/.wakatime.cfg')
|
help='Defaults to ~/.wakatime.cfg.')
|
||||||
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
||||||
help='turns on debug messages in log file')
|
help='Turns on debug messages in log file.')
|
||||||
parser.add_argument('--version', action='version', version=__version__)
|
parser.add_argument('--version', action='version', version=__version__)
|
||||||
|
|
||||||
# parse command line arguments
|
# parse command line arguments
|
||||||
|
@ -172,14 +200,14 @@ def parse_arguments():
|
||||||
args.key = default_key
|
args.key = default_key
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
parser.error('Missing api key. Find your api key from wakatime.com/settings.')
|
parser.error('Missing api key. Find your api key from wakatime.com/settings/api-key.')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise SystemExit(AUTH_ERROR)
|
raise SystemExit(AUTH_ERROR)
|
||||||
|
|
||||||
is_valid = not not re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$', args.key, re.I)
|
is_valid = not not re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$', args.key, re.I)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
try:
|
try:
|
||||||
parser.error('Invalid api key. Find your api key from wakatime.com/settings.')
|
parser.error('Invalid api key. Find your api key from wakatime.com/settings/api-key.')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise SystemExit(AUTH_ERROR)
|
raise SystemExit(AUTH_ERROR)
|
||||||
|
|
||||||
|
@ -219,6 +247,8 @@ def parse_arguments():
|
||||||
args.include.append(pattern)
|
args.include.append(pattern)
|
||||||
except TypeError: # pragma: nocover
|
except TypeError: # pragma: nocover
|
||||||
pass
|
pass
|
||||||
|
if not args.exclude_unknown_project and configs.has_option('settings', 'exclude_unknown_project'):
|
||||||
|
args.exclude_unknown_project = configs.getboolean('settings', 'exclude_unknown_project')
|
||||||
if not args.hide_filenames and args.hidefilenames:
|
if not args.hide_filenames and args.hidefilenames:
|
||||||
args.hide_filenames = args.hidefilenames
|
args.hide_filenames = args.hidefilenames
|
||||||
if args.hide_filenames:
|
if args.hide_filenames:
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Heartbeat(object):
|
||||||
time = None
|
time = None
|
||||||
entity = None
|
entity = None
|
||||||
type = None
|
type = None
|
||||||
|
category = None
|
||||||
is_write = None
|
is_write = None
|
||||||
project = None
|
project = None
|
||||||
branch = None
|
branch = None
|
||||||
|
@ -58,6 +59,21 @@ class Heartbeat(object):
|
||||||
if self.type not in ['file', 'domain', 'app']:
|
if self.type not in ['file', 'domain', 'app']:
|
||||||
self.type = 'file'
|
self.type = 'file'
|
||||||
|
|
||||||
|
self.category = data.get('category')
|
||||||
|
allowed_categories = [
|
||||||
|
'coding',
|
||||||
|
'building',
|
||||||
|
'indexing',
|
||||||
|
'debugging',
|
||||||
|
'running tests',
|
||||||
|
'manual testing',
|
||||||
|
'browsing',
|
||||||
|
'code reviewing',
|
||||||
|
'designing',
|
||||||
|
]
|
||||||
|
if self.category not in allowed_categories:
|
||||||
|
self.category = None
|
||||||
|
|
||||||
if not _clone:
|
if not _clone:
|
||||||
exclude = self._excluded_by_pattern()
|
exclude = self._excluded_by_pattern()
|
||||||
if exclude:
|
if exclude:
|
||||||
|
@ -78,6 +94,10 @@ class Heartbeat(object):
|
||||||
self.project = project
|
self.project = project
|
||||||
self.branch = branch
|
self.branch = branch
|
||||||
|
|
||||||
|
if self._excluded_by_unknown_project():
|
||||||
|
self.skip = u('Skipping because project unknown.')
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stats = get_file_stats(self.entity,
|
stats = get_file_stats(self.entity,
|
||||||
entity_type=self.type,
|
entity_type=self.type,
|
||||||
|
@ -155,6 +175,7 @@ class Heartbeat(object):
|
||||||
'time': self.time,
|
'time': self.time,
|
||||||
'entity': self._unicode(self.entity),
|
'entity': self._unicode(self.entity),
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
|
'category': self.category,
|
||||||
'is_write': self.is_write,
|
'is_write': self.is_write,
|
||||||
'project': self._unicode(self.project),
|
'project': self._unicode(self.project),
|
||||||
'branch': self._unicode(self.branch),
|
'branch': self._unicode(self.branch),
|
||||||
|
@ -170,9 +191,10 @@ class Heartbeat(object):
|
||||||
return self.dict().items()
|
return self.dict().items()
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return u('{time}-{type}-{project}-{branch}-{entity}-{is_write}').format(
|
return u('{time}-{type}-{category}-{project}-{branch}-{entity}-{is_write}').format(
|
||||||
time=self.time,
|
time=self.time,
|
||||||
type=self.type,
|
type=self.type,
|
||||||
|
category=self.category,
|
||||||
project=self._unicode(self.project),
|
project=self._unicode(self.project),
|
||||||
branch=self._unicode(self.branch),
|
branch=self._unicode(self.branch),
|
||||||
entity=self._unicode(self.entity),
|
entity=self._unicode(self.entity),
|
||||||
|
@ -192,6 +214,11 @@ class Heartbeat(object):
|
||||||
def _excluded_by_pattern(self):
|
def _excluded_by_pattern(self):
|
||||||
return should_exclude(self.entity, self.args.include, self.args.exclude)
|
return should_exclude(self.entity, self.args.include, self.args.exclude)
|
||||||
|
|
||||||
|
def _excluded_by_unknown_project(self):
|
||||||
|
if self.project:
|
||||||
|
return False
|
||||||
|
return self.args.exclude_unknown_project
|
||||||
|
|
||||||
def _excluded_by_missing_project_file(self):
|
def _excluded_by_missing_project_file(self):
|
||||||
if not self.args.include_only_with_project_file:
|
if not self.args.include_only_with_project_file:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -35,17 +35,9 @@ class Git(BaseProject):
|
||||||
def branch(self):
|
def branch(self):
|
||||||
head = self._head_file
|
head = self._head_file
|
||||||
if head:
|
if head:
|
||||||
try:
|
line = self._first_line_of_file(head)
|
||||||
with open(head, 'r', encoding='utf-8') as fh:
|
if line is not None:
|
||||||
return self._get_branch_from_head_file(fh.readline())
|
return self._get_branch_from_head_file(line)
|
||||||
except UnicodeDecodeError: # pragma: nocover
|
|
||||||
try:
|
|
||||||
with open(head, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
|
||||||
return self._get_branch_from_head_file(fh.readline())
|
|
||||||
except:
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
except IOError: # pragma: nocover
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
return u('master')
|
return u('master')
|
||||||
|
|
||||||
def _find_git_config_file(self, path):
|
def _find_git_config_file(self, path):
|
||||||
|
@ -56,12 +48,22 @@ class Git(BaseProject):
|
||||||
self._project_name = os.path.basename(path)
|
self._project_name = os.path.basename(path)
|
||||||
self._head_file = os.path.join(path, '.git', 'HEAD')
|
self._head_file = os.path.join(path, '.git', 'HEAD')
|
||||||
return True
|
return True
|
||||||
if self._submodules_supported_for_path(path):
|
|
||||||
submodule_path = self._find_path_from_submodule(path)
|
link_path = self._path_from_gitdir_link_file(path)
|
||||||
if submodule_path:
|
if link_path:
|
||||||
self._project_name = os.path.basename(path)
|
|
||||||
self._head_file = os.path.join(submodule_path, 'HEAD')
|
# first check if this is a worktree
|
||||||
|
if self._is_worktree(link_path):
|
||||||
|
self._project_name = self._project_from_worktree(link_path)
|
||||||
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# next check if this is a submodule
|
||||||
|
if self._submodules_supported_for_path(path):
|
||||||
|
self._project_name = os.path.basename(path)
|
||||||
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
|
return True
|
||||||
|
|
||||||
split_path = os.path.split(path)
|
split_path = os.path.split(path)
|
||||||
if split_path[1] == '':
|
if split_path[1] == '':
|
||||||
return False
|
return False
|
||||||
|
@ -77,8 +79,6 @@ class Git(BaseProject):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
disabled = self._configs.get('submodules_disabled')
|
disabled = self._configs.get('submodules_disabled')
|
||||||
if not disabled:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if disabled.strip().lower() == 'true':
|
if disabled.strip().lower() == 'true':
|
||||||
return False
|
return False
|
||||||
|
@ -99,30 +99,51 @@ class Git(BaseProject):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _find_path_from_submodule(self, path):
|
def _is_worktree(self, link_path):
|
||||||
|
return os.path.basename(os.path.dirname(link_path)) == 'worktrees'
|
||||||
|
|
||||||
|
def _path_from_gitdir_link_file(self, path):
|
||||||
link = os.path.join(path, '.git')
|
link = os.path.join(path, '.git')
|
||||||
if not os.path.isfile(link):
|
if not os.path.isfile(link):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
line = self._first_line_of_file(link)
|
||||||
with open(link, 'r', encoding='utf-8') as fh:
|
if line is not None:
|
||||||
return self._get_path_from_submodule_link(path, fh.readline())
|
return self._path_from_gitdir_string(path, line)
|
||||||
except UnicodeDecodeError:
|
|
||||||
try:
|
|
||||||
with open(link, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
|
||||||
return self._get_path_from_submodule_link(path, fh.readline())
|
|
||||||
except:
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
except IOError:
|
|
||||||
log.traceback(logging.WARNING)
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_path_from_submodule_link(self, path, line):
|
def _path_from_gitdir_string(self, path, line):
|
||||||
if line.startswith('gitdir: '):
|
if line.startswith('gitdir: '):
|
||||||
subpath = line[len('gitdir: '):].strip()
|
subpath = line[len('gitdir: '):].strip()
|
||||||
if os.path.isfile(os.path.join(path, subpath, 'config')) and \
|
if os.path.isfile(os.path.join(path, subpath, 'HEAD')):
|
||||||
os.path.isfile(os.path.join(path, subpath, 'HEAD')):
|
|
||||||
return os.path.realpath(os.path.join(path, subpath))
|
return os.path.realpath(os.path.join(path, subpath))
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _project_from_worktree(self, link_path):
|
||||||
|
commondir = os.path.join(link_path, 'commondir')
|
||||||
|
if os.path.isfile(commondir):
|
||||||
|
line = self._first_line_of_file(commondir)
|
||||||
|
if line:
|
||||||
|
gitdir = os.path.abspath(os.path.join(link_path, line))
|
||||||
|
if os.path.basename(gitdir) == '.git':
|
||||||
|
return os.path.basename(os.path.dirname(gitdir))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _first_line_of_file(self, filepath):
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as fh:
|
||||||
|
return fh.readline().strip()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
||||||
|
return fh.readline().strip()
|
||||||
|
except:
|
||||||
|
log.traceback(logging.WARNING)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
@ -24,6 +24,10 @@ from .compat import u
|
||||||
log = logging.getLogger('WakaTime')
|
log = logging.getLogger('WakaTime')
|
||||||
|
|
||||||
|
|
||||||
|
BACKSLASH_REPLACE_PATTERN = re.compile(r'[\\/]+')
|
||||||
|
WINDOWS_DRIVE_PATTERN = re.compile(r'^[a-z]:/')
|
||||||
|
|
||||||
|
|
||||||
def should_exclude(entity, include, exclude):
|
def should_exclude(entity, include, exclude):
|
||||||
if entity is not None and entity.strip() != '':
|
if entity is not None and entity.strip() != '':
|
||||||
for pattern in include:
|
for pattern in include:
|
||||||
|
@ -74,8 +78,10 @@ def format_file_path(filepath):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
filepath = os.path.realpath(os.path.abspath(filepath))
|
filepath = os.path.realpath(os.path.abspath(filepath))
|
||||||
filepath = re.sub(r'[/\\]', os.path.sep, filepath)
|
filepath = re.sub(BACKSLASH_REPLACE_PATTERN, '/', filepath)
|
||||||
except: # pragma: nocover
|
if WINDOWS_DRIVE_PATTERN.match(filepath):
|
||||||
|
filepath = filepath.capitalize()
|
||||||
|
except:
|
||||||
pass
|
pass
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue