New config hide_project_names
This commit is contained in:
parent
3373ef39e4
commit
71d5eef12a
15 changed files with 165 additions and 17 deletions
|
@ -3,6 +3,13 @@ History
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
||||||
|
10.2.2 (Unreleased)
|
||||||
|
+++++++++++++++++++
|
||||||
|
|
||||||
|
- New config hide_project_name and argument --hide-project-names for
|
||||||
|
obfuscating project names when sending coding activity to api.
|
||||||
|
|
||||||
|
|
||||||
10.2.1 (2018-04-26)
|
10.2.1 (2018-04-26)
|
||||||
+++++++++++++++++++
|
+++++++++++++++++++
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ format. An example config file with all available options::
|
||||||
debug = false
|
debug = false
|
||||||
api_key = your-api-key
|
api_key = your-api-key
|
||||||
hide_filenames = false
|
hide_filenames = false
|
||||||
|
hide_project_names = false
|
||||||
exclude =
|
exclude =
|
||||||
^COMMIT_EDITMSG$
|
^COMMIT_EDITMSG$
|
||||||
^TAG_EDITMSG$
|
^TAG_EDITMSG$
|
||||||
|
|
5
tests/samples/configs/paranoid_projects.cfg
Normal file
5
tests/samples/configs/paranoid_projects.cfg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[settings]
|
||||||
|
debug = false
|
||||||
|
api_key = c21f8ebd-6a6a-48a0-900b-0870db3d7afe
|
||||||
|
api_url = https://api.wakatime.com/api/v1/heartbeats
|
||||||
|
hide_project_names = true
|
|
@ -4,8 +4,9 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--exclude EXCLUDE] [--exclude-unknown-project]
|
[--hide-project-names] [--exclude EXCLUDE]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude-unknown-project] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
||||||
|
|
|
@ -4,8 +4,9 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--exclude EXCLUDE] [--exclude-unknown-project]
|
[--hide-project-names] [--exclude EXCLUDE]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude-unknown-project] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
||||||
|
|
|
@ -4,8 +4,9 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--exclude EXCLUDE] [--exclude-unknown-project]
|
[--hide-project-names] [--exclude EXCLUDE]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude-unknown-project] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
wakatime: error: argument --timeout: invalid int value: 'abc'
|
wakatime: error: argument --timeout: invalid int value: 'abc'
|
||||||
|
|
|
@ -4,10 +4,11 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--exclude EXCLUDE] [--exclude-unknown-project]
|
[--hide-project-names] [--exclude EXCLUDE]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude-unknown-project] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
|
|
||||||
Common interface for the WakaTime api.
|
Common interface for the WakaTime api.
|
||||||
|
|
||||||
|
@ -49,6 +50,10 @@ optional arguments:
|
||||||
--disable-offline Disables offline time logging instead of queuing
|
--disable-offline Disables offline time logging instead of queuing
|
||||||
logged time.
|
logged time.
|
||||||
--hide-filenames Obfuscate filenames. Will not send file names to api.
|
--hide-filenames Obfuscate filenames. Will not send file names to api.
|
||||||
|
--hide-project-names Obfuscate project names. When a project folder is
|
||||||
|
detected instead of using the folder name as the
|
||||||
|
project, a .wakatime-project file is created with a
|
||||||
|
random project name.
|
||||||
--exclude EXCLUDE Filename patterns to exclude from logging. POSIX regex
|
--exclude EXCLUDE Filename patterns to exclude from logging. POSIX regex
|
||||||
syntax. Can be used more than once.
|
syntax. Can be used more than once.
|
||||||
--exclude-unknown-project
|
--exclude-unknown-project
|
||||||
|
|
|
@ -459,6 +459,46 @@ class ConfigsTestCase(TestCase):
|
||||||
self.assertOfflineHeartbeatsSynced()
|
self.assertOfflineHeartbeatsSynced()
|
||||||
self.assertSessionCacheSaved()
|
self.assertSessionCacheSaved()
|
||||||
|
|
||||||
|
def test_obfuscte_project_names(self):
|
||||||
|
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = CustomResponse()
|
||||||
|
|
||||||
|
with TemporaryDirectory() as tempdir:
|
||||||
|
shutil.copytree('tests/samples/projects/git', os.path.join(tempdir, 'git'))
|
||||||
|
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
|
||||||
|
entity = os.path.join(tempdir, 'git', 'emptyfile.txt')
|
||||||
|
now = u(int(time.time()))
|
||||||
|
config = 'tests/samples/configs/paranoid_projects.cfg'
|
||||||
|
key = u(uuid.uuid4())
|
||||||
|
dependencies = []
|
||||||
|
generated_proj = 'Icy Bridge 42'
|
||||||
|
|
||||||
|
args = ['--file', entity, '--key', key, '--config', config, '--time', now, '--log-file', '~/.wakatime.log']
|
||||||
|
|
||||||
|
with mock.patch('wakatime.project.generate_project_name') as mock_proj:
|
||||||
|
mock_proj.return_value = generated_proj
|
||||||
|
|
||||||
|
retval = execute(args)
|
||||||
|
|
||||||
|
self.assertEquals(retval, SUCCESS)
|
||||||
|
self.assertNothingPrinted()
|
||||||
|
|
||||||
|
heartbeat = {
|
||||||
|
'language': 'Text only',
|
||||||
|
'lines': 0,
|
||||||
|
'entity': os.path.realpath(entity),
|
||||||
|
'project': generated_proj,
|
||||||
|
'time': float(now),
|
||||||
|
'is_write': False,
|
||||||
|
'type': 'file',
|
||||||
|
'dependencies': dependencies,
|
||||||
|
'user_agent': ANY,
|
||||||
|
}
|
||||||
|
self.assertHeartbeatSent(heartbeat)
|
||||||
|
|
||||||
|
self.assertHeartbeatNotSavedOffline()
|
||||||
|
self.assertOfflineHeartbeatsSynced()
|
||||||
|
self.assertSessionCacheSaved()
|
||||||
|
|
||||||
@log_capture()
|
@log_capture()
|
||||||
def test_exclude_file(self, logs):
|
def test_exclude_file(self, logs):
|
||||||
logging.disable(logging.NOTSET)
|
logging.disable(logging.NOTSET)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from testfixtures import log_capture
|
||||||
from wakatime.compat import u, open
|
from wakatime.compat import u, open
|
||||||
from wakatime.constants import API_ERROR, SUCCESS
|
from wakatime.constants import API_ERROR, SUCCESS
|
||||||
from wakatime.exceptions import NotYetImplemented
|
from wakatime.exceptions import NotYetImplemented
|
||||||
|
from wakatime.project import generate_project_name
|
||||||
from wakatime.projects.base import BaseProject
|
from wakatime.projects.base import BaseProject
|
||||||
from wakatime.projects.git import Git
|
from wakatime.projects.git import Git
|
||||||
from .utils import ANY, DynamicIterable, TestCase, TemporaryDirectory, CustomResponse, mock, json
|
from .utils import ANY, DynamicIterable, TestCase, TemporaryDirectory, CustomResponse, mock, json
|
||||||
|
@ -723,3 +724,7 @@ class ProjectTestCase(TestCase):
|
||||||
self.assertNothingPrinted()
|
self.assertNothingPrinted()
|
||||||
self.assertNothingLogged(logs)
|
self.assertNothingLogged(logs)
|
||||||
self.assertEquals('proj-arg', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
self.assertEquals('proj-arg', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||||
|
|
||||||
|
def test_generate_project_name(self):
|
||||||
|
self.assertGreater(len(generate_project_name()), 1)
|
||||||
|
self.assertNotEqual(generate_project_name(), generate_project_name())
|
||||||
|
|
|
@ -133,6 +133,13 @@ def parse_arguments():
|
||||||
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('--hide-project-names', dest='hide_project_names',
|
||||||
|
action='store_true',
|
||||||
|
help='Obfuscate project names. When a project ' +
|
||||||
|
'folder is detected instead of using the ' +
|
||||||
|
'folder name as the project, a ' +
|
||||||
|
'.wakatime-project file is created with a ' +
|
||||||
|
'random project name.')
|
||||||
parser.add_argument('--exclude', dest='exclude', action='append',
|
parser.add_argument('--exclude', dest='exclude', action='append',
|
||||||
help='Filename patterns to exclude from logging. ' +
|
help='Filename patterns to exclude from logging. ' +
|
||||||
'POSIX regex syntax. Can be used more than once.')
|
'POSIX regex syntax. Can be used more than once.')
|
||||||
|
@ -267,6 +274,8 @@ def parse_arguments():
|
||||||
for pattern in option.split("\n"):
|
for pattern in option.split("\n"):
|
||||||
if pattern.strip() != '':
|
if pattern.strip() != '':
|
||||||
args.hide_filenames.append(pattern)
|
args.hide_filenames.append(pattern)
|
||||||
|
if not args.hide_project_names and configs.has_option('settings', 'hide_project_names'):
|
||||||
|
args.hide_project_names = configs.getboolean('settings', 'hide_project_names')
|
||||||
if args.offline_deprecated:
|
if args.offline_deprecated:
|
||||||
args.offline = False
|
args.offline = False
|
||||||
if args.offline and configs.has_option('settings', 'offline'):
|
if args.offline and configs.has_option('settings', 'offline'):
|
||||||
|
|
|
@ -9,8 +9,11 @@
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import random
|
||||||
|
|
||||||
|
from .compat import open
|
||||||
from .projects.git import Git
|
from .projects.git import Git
|
||||||
from .projects.mercurial import Mercurial
|
from .projects.mercurial import Mercurial
|
||||||
from .projects.projectfile import ProjectFile
|
from .projects.projectfile import ProjectFile
|
||||||
|
@ -62,7 +65,7 @@ def get_project_info(configs, heartbeat, data):
|
||||||
branch_name = project.branch()
|
branch_name = project.branch()
|
||||||
break
|
break
|
||||||
|
|
||||||
if project_name is None:
|
if project_name is None and not data.get('hide_project_names'):
|
||||||
project_name = data.get('project') or heartbeat.args.project
|
project_name = data.get('project') or heartbeat.args.project
|
||||||
|
|
||||||
if project_name is None or branch_name is None:
|
if project_name is None or branch_name is None:
|
||||||
|
@ -76,6 +79,15 @@ def get_project_info(configs, heartbeat, data):
|
||||||
if project.process():
|
if project.process():
|
||||||
project_name = project_name or project.name()
|
project_name = project_name or project.name()
|
||||||
branch_name = branch_name or project.branch()
|
branch_name = branch_name or project.branch()
|
||||||
|
if data.get('hide_project_names'):
|
||||||
|
branch_name = None
|
||||||
|
project_name = generate_project_name()
|
||||||
|
project_file = os.path.join(project.folder(), '.wakatime-project')
|
||||||
|
try:
|
||||||
|
with open(project_file, 'w') as fh:
|
||||||
|
fh.write(project_name)
|
||||||
|
except IOError:
|
||||||
|
project_name = None
|
||||||
break
|
break
|
||||||
|
|
||||||
if project_name is None:
|
if project_name is None:
|
||||||
|
@ -88,3 +100,42 @@ def get_configs_for_plugin(plugin_name, configs):
|
||||||
if configs and configs.has_section(plugin_name):
|
if configs and configs.has_section(plugin_name):
|
||||||
return dict(configs.items(plugin_name))
|
return dict(configs.items(plugin_name))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def generate_project_name():
|
||||||
|
"""Generates a random project name."""
|
||||||
|
|
||||||
|
adjectives = [
|
||||||
|
'aged', 'ancient', 'autumn', 'billowing', 'bitter', 'black', 'blue', 'bold',
|
||||||
|
'broad', 'broken', 'calm', 'cold', 'cool', 'crimson', 'curly', 'damp',
|
||||||
|
'dark', 'dawn', 'delicate', 'divine', 'dry', 'empty', 'falling', 'fancy',
|
||||||
|
'flat', 'floral', 'fragrant', 'frosty', 'gentle', 'green', 'hidden', 'holy',
|
||||||
|
'icy', 'jolly', 'late', 'lingering', 'little', 'lively', 'long', 'lucky',
|
||||||
|
'misty', 'morning', 'muddy', 'mute', 'nameless', 'noisy', 'odd', 'old',
|
||||||
|
'orange', 'patient', 'plain', 'polished', 'proud', 'purple', 'quiet', 'rapid',
|
||||||
|
'raspy', 'red', 'restless', 'rough', 'round', 'royal', 'shiny', 'shrill',
|
||||||
|
'shy', 'silent', 'small', 'snowy', 'soft', 'solitary', 'sparkling', 'spring',
|
||||||
|
'square', 'steep', 'still', 'summer', 'super', 'sweet', 'throbbing', 'tight',
|
||||||
|
'tiny', 'twilight', 'wandering', 'weathered', 'white', 'wild', 'winter', 'wispy',
|
||||||
|
'withered', 'yellow', 'young'
|
||||||
|
]
|
||||||
|
nouns = [
|
||||||
|
'art', 'band', 'bar', 'base', 'bird', 'block', 'boat', 'bonus',
|
||||||
|
'bread', 'breeze', 'brook', 'bush', 'butterfly', 'cake', 'cell', 'cherry',
|
||||||
|
'cloud', 'credit', 'darkness', 'dawn', 'dew', 'disk', 'dream', 'dust',
|
||||||
|
'feather', 'field', 'fire', 'firefly', 'flower', 'fog', 'forest', 'frog',
|
||||||
|
'frost', 'glade', 'glitter', 'grass', 'hall', 'hat', 'haze', 'heart',
|
||||||
|
'hill', 'king', 'lab', 'lake', 'leaf', 'limit', 'math', 'meadow',
|
||||||
|
'mode', 'moon', 'morning', 'mountain', 'mouse', 'mud', 'night', 'paper',
|
||||||
|
'pine', 'poetry', 'pond', 'queen', 'rain', 'recipe', 'resonance', 'rice',
|
||||||
|
'river', 'salad', 'scene', 'sea', 'shadow', 'shape', 'silence', 'sky',
|
||||||
|
'smoke', 'snow', 'snowflake', 'sound', 'star', 'sun', 'sun', 'sunset',
|
||||||
|
'surf', 'term', 'thunder', 'tooth', 'tree', 'truth', 'union', 'unit',
|
||||||
|
'violet', 'voice', 'water', 'waterfall', 'wave', 'wildflower', 'wind', 'wood'
|
||||||
|
]
|
||||||
|
numbers = [str(x) for x in range(10)]
|
||||||
|
return ' '.join([
|
||||||
|
random.choice(adjectives).capitalize(),
|
||||||
|
random.choice(nouns).capitalize(),
|
||||||
|
random.choice(numbers) + random.choice(numbers),
|
||||||
|
])
|
||||||
|
|
|
@ -43,3 +43,8 @@ class BaseProject(object):
|
||||||
""" Returns the current branch.
|
""" Returns the current branch.
|
||||||
"""
|
"""
|
||||||
raise NotYetImplemented()
|
raise NotYetImplemented()
|
||||||
|
|
||||||
|
def folder(self):
|
||||||
|
""" Returns the project's top folder path.
|
||||||
|
"""
|
||||||
|
raise NotYetImplemented()
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Git(BaseProject):
|
||||||
_submodule = None
|
_submodule = None
|
||||||
_project_name = None
|
_project_name = None
|
||||||
_head_file = None
|
_head_file = None
|
||||||
|
_project_folder = None
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
return self._find_git_config_file(self.path)
|
return self._find_git_config_file(self.path)
|
||||||
|
@ -40,6 +41,9 @@ class Git(BaseProject):
|
||||||
return self._get_branch_from_head_file(line)
|
return self._get_branch_from_head_file(line)
|
||||||
return u('master')
|
return u('master')
|
||||||
|
|
||||||
|
def folder(self):
|
||||||
|
return self._project_folder
|
||||||
|
|
||||||
def _find_git_config_file(self, path):
|
def _find_git_config_file(self, path):
|
||||||
path = os.path.realpath(path)
|
path = os.path.realpath(path)
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
|
@ -47,6 +51,7 @@ class Git(BaseProject):
|
||||||
if os.path.isfile(os.path.join(path, '.git', 'config')):
|
if os.path.isfile(os.path.join(path, '.git', 'config')):
|
||||||
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')
|
||||||
|
self._project_folder = path
|
||||||
return True
|
return True
|
||||||
|
|
||||||
link_path = self._path_from_gitdir_link_file(path)
|
link_path = self._path_from_gitdir_link_file(path)
|
||||||
|
@ -56,12 +61,14 @@ class Git(BaseProject):
|
||||||
if self._is_worktree(link_path):
|
if self._is_worktree(link_path):
|
||||||
self._project_name = self._project_from_worktree(link_path)
|
self._project_name = self._project_from_worktree(link_path)
|
||||||
self._head_file = os.path.join(link_path, 'HEAD')
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
|
self._project_folder = path
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# next check if this is a submodule
|
# next check if this is a submodule
|
||||||
if self._submodules_supported_for_path(path):
|
if self._submodules_supported_for_path(path):
|
||||||
self._project_name = os.path.basename(path)
|
self._project_name = os.path.basename(path)
|
||||||
self._head_file = os.path.join(link_path, 'HEAD')
|
self._head_file = os.path.join(link_path, 'HEAD')
|
||||||
|
self._project_folder = path
|
||||||
return True
|
return True
|
||||||
|
|
||||||
split_path = os.path.split(path)
|
split_path = os.path.split(path)
|
||||||
|
|
|
@ -47,6 +47,11 @@ class Mercurial(BaseProject):
|
||||||
log.traceback(logging.WARNING)
|
log.traceback(logging.WARNING)
|
||||||
return u('default')
|
return u('default')
|
||||||
|
|
||||||
|
def folder(self):
|
||||||
|
if self.configDir:
|
||||||
|
return os.path.dirname(self.configDir)
|
||||||
|
return None
|
||||||
|
|
||||||
def _find_hg_config_dir(self, path):
|
def _find_hg_config_dir(self, path):
|
||||||
path = os.path.realpath(path)
|
path = os.path.realpath(path)
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
|
|
|
@ -41,6 +41,11 @@ class Subversion(BaseProject):
|
||||||
return None # pragma: nocover
|
return None # pragma: nocover
|
||||||
return u(self.info['URL'].split('/')[-1].split('\\')[-1])
|
return u(self.info['URL'].split('/')[-1].split('\\')[-1])
|
||||||
|
|
||||||
|
def folder(self):
|
||||||
|
if 'Repository Root' not in self.info:
|
||||||
|
return None
|
||||||
|
return self.info['Repository Root']
|
||||||
|
|
||||||
def _find_binary(self):
|
def _find_binary(self):
|
||||||
if self.binary_location:
|
if self.binary_location:
|
||||||
return self.binary_location
|
return self.binary_location
|
||||||
|
|
Loading…
Reference in a new issue