mirror of
https://github.com/wakatime/sublime-wakatime.git
synced 2023-08-10 21:13:02 +03:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
935ddbd5f6 | |||
b57b1eb696 | |||
6ec097b9d1 | |||
b3ed36d3b2 | |||
3669e4df6a | |||
3504096082 | |||
5990947706 | |||
2246e31244 | |||
b55fe702d3 | |||
e0fbbb50bb | |||
32c0cb5a97 | |||
67d8b0d24f | |||
b8b2f4944b | |||
a20161164c | |||
405211bb07 | |||
ffc879c4eb | |||
1e23919694 | |||
b2086a3cd2 | |||
005b07520c | |||
60608bd322 | |||
cde8f8f1de | |||
4adfca154c | |||
f7b3924a30 | |||
db00024455 | |||
9a6be7ca4e | |||
1ea9b2a761 | |||
bd5e87e030 | |||
0256ff4a6a | |||
9d170b3276 | |||
c54e575210 | |||
07513d8f10 | |||
30902cc050 | |||
aa7962d49a | |||
d8c662f3db | |||
10d88ebf2d | |||
2f28c561b1 | |||
24968507df | |||
641cd539ed | |||
0c65d7e5b2 | |||
f0532f5b8e | |||
8094db9680 | |||
bf20551849 | |||
2b6e32b578 | |||
363c3d38e2 | |||
88466d7db2 | |||
122fcbbee5 | |||
c41fcec5d8 | |||
be09b34d44 | |||
e1ee1c1216 | |||
a37061924b | |||
da01fa268b | |||
c279418651 | |||
5cf2c8f7ac | |||
d1455e77a8 | |||
8499e7bafe | |||
abc26a0864 |
149
HISTORY.rst
149
HISTORY.rst
@ -3,6 +3,151 @@ History
|
||||
-------
|
||||
|
||||
|
||||
9.0.2 (2019-12-04)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v13.0.3.
|
||||
- Support slashes in Mercurial and Git branch names.
|
||||
`wakatime#199 <https://github.com/wakatime/wakatime/issues/199>`_
|
||||
|
||||
|
||||
9.0.1 (2019-11-24)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v13.0.2.
|
||||
- Filter dependencies longer than 200 characters.
|
||||
- Close sqlite connection even when error raised.
|
||||
`wakatime#196 <https://github.com/wakatime/wakatime/issues/196>`_
|
||||
- Detect ColdFusion as root language instead of HTML.
|
||||
- New arguments for reading and writing ini config file.
|
||||
- Today argument shows categories when available.
|
||||
- Prevent unnecessarily debug log when syncing offline heartbeats.
|
||||
- Support for Python 3.7.
|
||||
|
||||
|
||||
9.0.0 (2019-06-23)
|
||||
++++++++++++++++++
|
||||
|
||||
- New optional config option hide_branch_names.
|
||||
`wakatime#183 <https://github.com/wakatime/wakatime/issues/183>`_
|
||||
|
||||
|
||||
8.7.0 (2019-05-29)
|
||||
++++++++++++++++++
|
||||
|
||||
- Prevent creating user sublime-settings file when api key already exists in
|
||||
common wakatime.cfg file.
|
||||
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
|
||||
|
||||
|
||||
8.6.1 (2019-05-28)
|
||||
++++++++++++++++++
|
||||
|
||||
- Fix parsing common wakatime.cfg file.
|
||||
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
|
||||
|
||||
|
||||
8.6.0 (2019-05-27)
|
||||
++++++++++++++++++
|
||||
|
||||
- Prevent prompting for api key when found from config file.
|
||||
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
|
||||
|
||||
|
||||
8.5.0 (2019-05-10)
|
||||
++++++++++++++++++
|
||||
|
||||
- Remove clock icon from status bar.
|
||||
- Use wakatime-cli to fetch status bar coding time.
|
||||
|
||||
|
||||
8.4.2 (2019-05-07)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v11.0.0.
|
||||
- Rename argument --show-time-today to --today.
|
||||
- New argument --show-time-today for printing Today's coding time.
|
||||
|
||||
|
||||
8.4.1 (2019-05-01)
|
||||
++++++++++++++++++
|
||||
|
||||
- Use api subdomain for fetching today's coding activity.
|
||||
|
||||
|
||||
8.4.0 (2019-05-01)
|
||||
++++++++++++++++++
|
||||
|
||||
- Show today's coding time in status bar.
|
||||
|
||||
|
||||
8.3.6 (2019-04-30)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.8.4.
|
||||
- Use wakatime fork of certifi package.
|
||||
`#95 <https://github.com/wakatime/sublime-wakatime/issues/95>`_
|
||||
|
||||
|
||||
8.3.5 (2019-04-30)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.8.3.
|
||||
- Upgrade certifi to version 2019.03.09.
|
||||
|
||||
|
||||
8.3.4 (2019-03-30)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.8.2.
|
||||
- Detect go.mod files as Go language.
|
||||
`jetbrains-wakatime#119 <https://github.com/wakatime/jetbrains-wakatime/issues/119>`_
|
||||
- Detect C++ language from all C++ file extensions.
|
||||
`vscode-wakatime#87 <https://github.com/wakatime/vscode-wakatime/issues/87>`_
|
||||
- Add ssl_certs_file arg and config for custom ca bundles.
|
||||
`wakatime#164 <https://github.com/wakatime/wakatime/issues/164>`_
|
||||
- Fix bug causing random project names when hide project names enabled.
|
||||
`vscode-wakatime#162 <https://github.com/wakatime/vscode-wakatime/issues/61>`_
|
||||
- Add support for UNC network shares without drive letter mapped on Winows.
|
||||
`wakatime#162 <https://github.com/wakatime/wakatime/issues/162>`_
|
||||
|
||||
|
||||
8.3.3 (2018-12-19)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.6.1.
|
||||
- Correctly parse include_only_with_project_file when set to false.
|
||||
`wakatime#161 <https://github.com/wakatime/wakatime/issues/161>`_
|
||||
- Support language argument for non-file entity types.
|
||||
- Send 25 heartbeats per API request.
|
||||
- New category "Writing Tests".
|
||||
`wakatime#156 <https://github.com/wakatime/wakatime/issues/156>`_
|
||||
- Fix bug caused by git config section without any submodule option defined.
|
||||
`wakatime#152 <https://github.com/wakatime/wakatime/issues/152>`_
|
||||
|
||||
|
||||
8.3.2 (2018-10-06)
|
||||
++++++++++++++++++
|
||||
|
||||
- Send buffered heartbeats to API every 30 seconds.
|
||||
|
||||
|
||||
8.3.1 (2018-10-05)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.4.1.
|
||||
- Send 50 offline heartbeats to API per request with 1 second delay in between.
|
||||
|
||||
|
||||
8.3.0 (2018-10-03)
|
||||
++++++++++++++++++
|
||||
|
||||
- Upgrade wakatime-cli to v10.4.0.
|
||||
- Support logging coding activity to remote network drive files on Windows
|
||||
platform by detecting UNC path from drive letter.
|
||||
`wakatime#72 <https://github.com/wakatime/wakatime/issues/72>`_
|
||||
|
||||
|
||||
8.2.0 (2018-09-30)
|
||||
++++++++++++++++++
|
||||
|
||||
@ -61,10 +206,10 @@ History
|
||||
- Upgrade wakatime-cli to v10.1.2.
|
||||
- Detect dependencies from Swift, Objective-C, TypeScript and JavaScript files.
|
||||
- Categorize .mjs files as JavaScript.
|
||||
`#wakatime121 <https://github.com/wakatime/wakatime/issues/121>`_
|
||||
`wakatime#121 <https://github.com/wakatime/wakatime/issues/121>`_
|
||||
- Detect dependencies from Elm, Haskell, Haxe, Kotlin, Rust, and Scala files.
|
||||
- Improved Matlab vs Objective-C language detection.
|
||||
`#wakatime129 <https://github.com/wakatime/wakatime/issues/129>`_
|
||||
`wakatime#129 <https://github.com/wakatime/wakatime/issues/129>`_
|
||||
|
||||
|
||||
8.0.6 (2018-01-04)
|
||||
|
3
LICENSE
3
LICENSE
@ -1,7 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2014 by the respective authors (see AUTHORS file).
|
||||
All rights reserved.
|
||||
Copyright (c) 2014 Alan Hamlett.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
17
README.md
17
README.md
@ -1,11 +1,11 @@
|
||||
sublime-wakatime
|
||||
================
|
||||
# sublime-wakatime
|
||||
|
||||
[](https://wakatime.com/badge/github/wakatime/sublime-wakatime)
|
||||
|
||||
Metrics, insights, and time tracking automatically generated from your programming activity.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
## Installation
|
||||
|
||||
1. Install [Package Control](https://packagecontrol.io/installation).
|
||||
|
||||
@ -20,14 +20,12 @@ Installation
|
||||
6. Use Sublime and your coding activity will be displayed on your [WakaTime dashboard](https://wakatime.com).
|
||||
|
||||
|
||||
Screen Shots
|
||||
------------
|
||||
## Screen Shots
|
||||
|
||||

|
||||
|
||||
|
||||
Unresponsive Plugin Warning
|
||||
---------------------------
|
||||
## Unresponsive Plugin Warning
|
||||
|
||||
In Sublime Text 2, if you get a warning message:
|
||||
|
||||
@ -38,8 +36,7 @@ To fix this, go to `Preferences → Settings - User` then add the following sett
|
||||
`"detect_slow_plugins": false`
|
||||
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
## Troubleshooting
|
||||
|
||||
First, turn on debug mode in your `WakaTime.sublime-settings` file.
|
||||
|
||||
|
167
WakaTime.py
167
WakaTime.py
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" ==========================================================
|
||||
File: WakaTime.py
|
||||
Description: Automatic time tracking for Sublime Text 2 and 3.
|
||||
@ -7,7 +8,7 @@ Website: https://wakatime.com/
|
||||
==========================================================="""
|
||||
|
||||
|
||||
__version__ = '8.2.0'
|
||||
__version__ = '9.0.2'
|
||||
|
||||
|
||||
import sublime
|
||||
@ -25,7 +26,6 @@ import threading
|
||||
import traceback
|
||||
import urllib
|
||||
import webbrowser
|
||||
from datetime import datetime
|
||||
from subprocess import STDOUT, PIPE
|
||||
from zipfile import ZipFile
|
||||
try:
|
||||
@ -110,7 +110,6 @@ class Popen(subprocess.Popen):
|
||||
|
||||
|
||||
# globals
|
||||
HEARTBEAT_FREQUENCY = 2
|
||||
ST_VERSION = int(sublime.version())
|
||||
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
API_CLIENT = os.path.join(PLUGIN_DIR, 'packages', 'wakatime', 'cli.py')
|
||||
@ -121,8 +120,14 @@ LAST_HEARTBEAT = {
|
||||
'file': None,
|
||||
'is_write': False,
|
||||
}
|
||||
LAST_HEARTBEAT_SENT_AT = 0
|
||||
LAST_FETCH_TODAY_CODING_TIME = 0
|
||||
FETCH_TODAY_DEBOUNCE_COUNTER = 0
|
||||
FETCH_TODAY_DEBOUNCE_SECONDS = 60
|
||||
PYTHON_LOCATION = None
|
||||
HEARTBEATS = queue.Queue()
|
||||
HEARTBEAT_FREQUENCY = 2 # minutes between logging heartbeat when editing same file
|
||||
SEND_BUFFER_SECONDS = 30 # seconds between sending buffered heartbeats to API
|
||||
|
||||
|
||||
# Log Levels
|
||||
@ -135,9 +140,45 @@ ERROR = 'ERROR'
|
||||
# add wakatime package to path
|
||||
sys.path.insert(0, os.path.join(PLUGIN_DIR, 'packages'))
|
||||
try:
|
||||
from wakatime.main import parseConfigFile
|
||||
from wakatime.configs import parseConfigFile
|
||||
except ImportError:
|
||||
pass
|
||||
def parseConfigFile():
|
||||
return None
|
||||
|
||||
|
||||
class ApiKey(object):
|
||||
_key = None
|
||||
|
||||
def read(self):
|
||||
if self._key:
|
||||
return self._key
|
||||
|
||||
key = SETTINGS.get('api_key')
|
||||
if key:
|
||||
self._key = key
|
||||
return self._key
|
||||
|
||||
try:
|
||||
configs = parseConfigFile()
|
||||
if configs:
|
||||
if configs.has_option('settings', 'api_key'):
|
||||
key = configs.get('settings', 'api_key')
|
||||
if key:
|
||||
self._key = key
|
||||
return self._key
|
||||
except:
|
||||
pass
|
||||
|
||||
return self._key
|
||||
|
||||
def write(self, key):
|
||||
global SETTINGS
|
||||
self._key = key
|
||||
SETTINGS.set('api_key', str(key))
|
||||
sublime.save_settings(SETTINGS_FILE)
|
||||
|
||||
|
||||
APIKEY = ApiKey()
|
||||
|
||||
|
||||
def set_timeout(callback, seconds):
|
||||
@ -178,45 +219,101 @@ def resources_folder():
|
||||
return os.path.join(os.path.expanduser('~'), '.wakatime')
|
||||
|
||||
|
||||
def update_status_bar(status):
|
||||
def update_status_bar(status=None, debounced=False, msg=None):
|
||||
"""Updates the status bar."""
|
||||
global LAST_FETCH_TODAY_CODING_TIME, FETCH_TODAY_DEBOUNCE_COUNTER
|
||||
|
||||
try:
|
||||
if SETTINGS.get('status_bar_message'):
|
||||
msg = datetime.now().strftime(SETTINGS.get('status_bar_message_fmt'))
|
||||
if '{status}' in msg:
|
||||
msg = msg.format(status=status)
|
||||
if not msg and SETTINGS.get('status_bar_message') is not False and SETTINGS.get('status_bar_enabled'):
|
||||
if SETTINGS.get('status_bar_coding_activity') and status == 'OK':
|
||||
if debounced:
|
||||
FETCH_TODAY_DEBOUNCE_COUNTER -= 1
|
||||
if debounced or not LAST_FETCH_TODAY_CODING_TIME:
|
||||
now = int(time.time())
|
||||
if LAST_FETCH_TODAY_CODING_TIME and (FETCH_TODAY_DEBOUNCE_COUNTER > 0 or LAST_FETCH_TODAY_CODING_TIME > now - FETCH_TODAY_DEBOUNCE_SECONDS):
|
||||
return
|
||||
LAST_FETCH_TODAY_CODING_TIME = now
|
||||
FetchStatusBarCodingTime().start()
|
||||
return
|
||||
else:
|
||||
FETCH_TODAY_DEBOUNCE_COUNTER += 1
|
||||
set_timeout(lambda: update_status_bar(status, debounced=True), FETCH_TODAY_DEBOUNCE_SECONDS)
|
||||
return
|
||||
else:
|
||||
msg = 'WakaTime: {status}'.format(status=status)
|
||||
|
||||
if msg:
|
||||
active_window = sublime.active_window()
|
||||
if active_window:
|
||||
for view in active_window.views():
|
||||
view.set_status('wakatime', msg)
|
||||
|
||||
except RuntimeError:
|
||||
set_timeout(lambda: update_status_bar(status), 0)
|
||||
set_timeout(lambda: update_status_bar(status=status, debounced=debounced, msg=msg), 0)
|
||||
|
||||
|
||||
class FetchStatusBarCodingTime(threading.Thread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.debug = SETTINGS.get('debug')
|
||||
self.api_key = APIKEY.read() or ''
|
||||
self.proxy = SETTINGS.get('proxy')
|
||||
self.python_binary = SETTINGS.get('python_binary')
|
||||
|
||||
def run(self):
|
||||
if not self.api_key:
|
||||
log(DEBUG, 'Missing WakaTime API key.')
|
||||
return
|
||||
|
||||
python = self.python_binary
|
||||
if not python or not python.strip():
|
||||
python = python_binary()
|
||||
if not python:
|
||||
log(DEBUG, 'Missing Python.')
|
||||
return
|
||||
|
||||
ua = 'sublime/%d sublime-wakatime/%s' % (ST_VERSION, __version__)
|
||||
cmd = [
|
||||
python,
|
||||
API_CLIENT,
|
||||
'--today',
|
||||
'--key', str(bytes.decode(self.api_key.encode('utf8'))),
|
||||
'--plugin', ua,
|
||||
]
|
||||
if self.debug:
|
||||
cmd.append('--verbose')
|
||||
if self.proxy:
|
||||
cmd.extend(['--proxy', self.proxy])
|
||||
|
||||
log(DEBUG, ' '.join(obfuscate_apikey(cmd)))
|
||||
try:
|
||||
process = Popen(cmd, stdout=PIPE, stderr=STDOUT)
|
||||
output, err = process.communicate()
|
||||
output = u(output)
|
||||
retcode = process.poll()
|
||||
if not retcode and output:
|
||||
msg = 'Today: {output}'.format(output=output)
|
||||
update_status_bar(msg=msg)
|
||||
else:
|
||||
log(DEBUG, 'wakatime-core today exited with status: {0}'.format(retcode))
|
||||
if output:
|
||||
log(DEBUG, u('wakatime-core today output: {0}').format(output))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def prompt_api_key():
|
||||
global SETTINGS
|
||||
|
||||
if SETTINGS.get('api_key'):
|
||||
if APIKEY.read():
|
||||
return True
|
||||
|
||||
default_key = ''
|
||||
try:
|
||||
configs = parseConfigFile()
|
||||
if configs is not None:
|
||||
if configs.has_option('settings', 'api_key'):
|
||||
default_key = configs.get('settings', 'api_key')
|
||||
except:
|
||||
pass
|
||||
|
||||
window = sublime.active_window()
|
||||
if window:
|
||||
def got_key(text):
|
||||
if text:
|
||||
SETTINGS.set('api_key', str(text))
|
||||
sublime.save_settings(SETTINGS_FILE)
|
||||
window.show_input_panel('[WakaTime] Enter your wakatime.com api key:', default_key, got_key, None, None)
|
||||
APIKEY.write(text)
|
||||
window.show_input_panel('[WakaTime] Enter your wakatime.com api key:', '', got_key, None, None)
|
||||
return True
|
||||
else:
|
||||
log(ERROR, 'Could not prompt for api key because no window found.')
|
||||
@ -451,11 +548,18 @@ def append_heartbeat(entity, timestamp, is_write, view, project, folders):
|
||||
}
|
||||
|
||||
# process the queue of heartbeats in the future
|
||||
seconds = 4
|
||||
set_timeout(process_queue, seconds)
|
||||
set_timeout(lambda: process_queue(timestamp), SEND_BUFFER_SECONDS)
|
||||
|
||||
|
||||
def process_queue():
|
||||
def process_queue(timestamp):
|
||||
global LAST_HEARTBEAT_SENT_AT
|
||||
|
||||
# Prevent sending heartbeats more often than SEND_BUFFER_SECONDS
|
||||
now = int(time.time())
|
||||
if timestamp != LAST_HEARTBEAT['time'] and LAST_HEARTBEAT_SENT_AT > now - SEND_BUFFER_SECONDS:
|
||||
return
|
||||
LAST_HEARTBEAT_SENT_AT = now
|
||||
|
||||
try:
|
||||
heartbeat = HEARTBEATS.get_nowait()
|
||||
except queue.Empty:
|
||||
@ -484,7 +588,7 @@ class SendHeartbeatsThread(threading.Thread):
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.debug = SETTINGS.get('debug')
|
||||
self.api_key = SETTINGS.get('api_key', '')
|
||||
self.api_key = APIKEY.read() or ''
|
||||
self.ignore = SETTINGS.get('ignore', [])
|
||||
self.include = SETTINGS.get('include', [])
|
||||
self.hidefilenames = SETTINGS.get('hidefilenames')
|
||||
@ -639,7 +743,7 @@ def plugin_loaded():
|
||||
SETTINGS = sublime.load_settings(SETTINGS_FILE)
|
||||
|
||||
log(INFO, 'Initializing WakaTime plugin v%s' % __version__)
|
||||
update_status_bar('Initializing')
|
||||
update_status_bar('Initializing...')
|
||||
|
||||
if not python_binary():
|
||||
log(WARNING, 'Python binary not found.')
|
||||
@ -655,6 +759,7 @@ def plugin_loaded():
|
||||
def after_loaded():
|
||||
if not prompt_api_key():
|
||||
set_timeout(after_loaded, 0.5)
|
||||
update_status_bar('OK')
|
||||
|
||||
|
||||
# need to call plugin_loaded because only ST3 will auto-call it
|
||||
|
@ -21,12 +21,13 @@
|
||||
// POSIX regular expressions will bypass your ignore setting.
|
||||
"include": [".*"],
|
||||
|
||||
// Status bar message. Set to false to hide status bar message.
|
||||
// Defaults to true.
|
||||
"status_bar_message": true,
|
||||
// Status bar for surfacing errors and displaying today's coding time. Set
|
||||
// to false to hide. Defaults to true.
|
||||
"status_bar_enabled": true,
|
||||
|
||||
// Status bar message format.
|
||||
"status_bar_message_fmt": "WakaTime {status} %I:%M %p",
|
||||
// Show today's coding activity in WakaTime status bar item.
|
||||
// Defaults to true.
|
||||
"status_bar_coding_activity": true,
|
||||
|
||||
// Obfuscate file paths when sending to API. Your dashboard will no longer display coding activity per file.
|
||||
"hidefilenames": false,
|
||||
|
@ -1,7 +1,7 @@
|
||||
__title__ = 'wakatime'
|
||||
__description__ = 'Common interface to the WakaTime api.'
|
||||
__url__ = 'https://github.com/wakatime/wakatime'
|
||||
__version_info__ = ('10', '3', '0')
|
||||
__version_info__ = ('13', '0', '3')
|
||||
__version__ = '.'.join(__version_info__)
|
||||
__author__ = 'Alan Hamlett'
|
||||
__author_email__ = 'alan@wakatime.com'
|
||||
|
@ -99,12 +99,16 @@ def send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=False):
|
||||
should_try_ntlm = '\\' in args.proxy
|
||||
proxies['https'] = args.proxy
|
||||
|
||||
ssl_verify = not args.nosslverify
|
||||
if args.ssl_certs_file and ssl_verify:
|
||||
ssl_verify = args.ssl_certs_file
|
||||
|
||||
# send request to api
|
||||
response, code = None, None
|
||||
try:
|
||||
response = session.post(api_url, data=request_body, headers=headers,
|
||||
proxies=proxies, timeout=timeout,
|
||||
verify=not args.nosslverify)
|
||||
verify=ssl_verify)
|
||||
except RequestException:
|
||||
if should_try_ntlm:
|
||||
return send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=True)
|
||||
@ -159,6 +163,123 @@ def send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=False):
|
||||
return AUTH_ERROR if code == 401 else API_ERROR
|
||||
|
||||
|
||||
def get_time_today(args, use_ntlm_proxy=False):
|
||||
"""Get coding time from WakaTime API for given time range.
|
||||
|
||||
Returns total time as string or `None` when unable to fetch summary from
|
||||
the API. When verbose output enabled, returns error message when unable to
|
||||
fetch summary.
|
||||
"""
|
||||
|
||||
url = 'https://api.wakatime.com/api/v1/users/current/summaries'
|
||||
timeout = args.timeout
|
||||
if not timeout:
|
||||
timeout = 60
|
||||
|
||||
api_key = u(base64.b64encode(str.encode(args.key) if is_py3 else args.key))
|
||||
auth = u('Basic {api_key}').format(api_key=api_key)
|
||||
headers = {
|
||||
'User-Agent': get_user_agent(args.plugin),
|
||||
'Accept': 'application/json',
|
||||
'Authorization': auth,
|
||||
}
|
||||
|
||||
session_cache = SessionCache()
|
||||
session = session_cache.get()
|
||||
|
||||
should_try_ntlm = False
|
||||
proxies = {}
|
||||
if args.proxy:
|
||||
if use_ntlm_proxy:
|
||||
from .packages.requests_ntlm import HttpNtlmAuth
|
||||
username = args.proxy.rsplit(':', 1)
|
||||
password = ''
|
||||
if len(username) == 2:
|
||||
password = username[1]
|
||||
username = username[0]
|
||||
session.auth = HttpNtlmAuth(username, password, session)
|
||||
else:
|
||||
should_try_ntlm = '\\' in args.proxy
|
||||
proxies['https'] = args.proxy
|
||||
|
||||
ssl_verify = not args.nosslverify
|
||||
if args.ssl_certs_file and ssl_verify:
|
||||
ssl_verify = args.ssl_certs_file
|
||||
|
||||
params = {
|
||||
'start': 'today',
|
||||
'end': 'today',
|
||||
}
|
||||
|
||||
# send request to api
|
||||
response, code = None, None
|
||||
try:
|
||||
response = session.get(url, params=params, headers=headers,
|
||||
proxies=proxies, timeout=timeout,
|
||||
verify=ssl_verify)
|
||||
except RequestException:
|
||||
if should_try_ntlm:
|
||||
return get_time_today(args, use_ntlm_proxy=True)
|
||||
|
||||
session_cache.delete()
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
exception_data = {
|
||||
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
|
||||
'traceback': traceback.format_exc(),
|
||||
}
|
||||
log.error(exception_data)
|
||||
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
|
||||
return None, API_ERROR
|
||||
|
||||
except: # delete cached session when requests raises unknown exception
|
||||
if should_try_ntlm:
|
||||
return get_time_today(args, use_ntlm_proxy=True)
|
||||
|
||||
session_cache.delete()
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
exception_data = {
|
||||
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
|
||||
'traceback': traceback.format_exc(),
|
||||
}
|
||||
log.error(exception_data)
|
||||
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
|
||||
return None, API_ERROR
|
||||
|
||||
code = response.status_code if response is not None else None
|
||||
content = response.text if response is not None else None
|
||||
|
||||
if code == requests.codes.ok:
|
||||
try:
|
||||
summary = response.json()['data'][0]
|
||||
if len(summary['categories']) > 1:
|
||||
text = ', '.join(['{0} {1}'.format(x['text'], x['name'].lower()) for x in summary['categories']])
|
||||
else:
|
||||
text = summary['grand_total']['text']
|
||||
session_cache.save(session)
|
||||
return text, SUCCESS
|
||||
except:
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
exception_data = {
|
||||
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
|
||||
'traceback': traceback.format_exc(),
|
||||
}
|
||||
log.error(exception_data)
|
||||
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
|
||||
return None, API_ERROR
|
||||
else:
|
||||
if should_try_ntlm:
|
||||
return get_time_today(args, use_ntlm_proxy=True)
|
||||
|
||||
session_cache.delete()
|
||||
log.debug({
|
||||
'response_code': code,
|
||||
'response_text': content,
|
||||
})
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
return 'Error: {0}'.format(code), API_ERROR
|
||||
return None, API_ERROR
|
||||
|
||||
|
||||
def _process_server_results(heartbeats, code, content, results, args, configs):
|
||||
log.debug({
|
||||
'response_code': code,
|
||||
|
@ -19,8 +19,8 @@ import time
|
||||
import traceback
|
||||
from .__about__ import __version__
|
||||
from .compat import basestring
|
||||
from .configs import parseConfigFile
|
||||
from .constants import AUTH_ERROR, DEFAULT_SYNC_OFFLINE_ACTIVITY
|
||||
from .configs import getConfigFile, parseConfigFile
|
||||
from .constants import AUTH_ERROR, DEFAULT_SYNC_OFFLINE_ACTIVITY, SUCCESS
|
||||
from .packages import argparse
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ def parse_arguments():
|
||||
parser.add_argument('--file', dest='file', action=FileAction,
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
|
||||
metavar='API_KEY',
|
||||
help='Your wakatime api key; uses api_key from ' +
|
||||
'~/.wakatime.cfg by default.')
|
||||
parser.add_argument('--write', dest='is_write', action='store_true',
|
||||
@ -75,10 +76,11 @@ def parse_arguments():
|
||||
help='Optional floating-point unix epoch timestamp. ' +
|
||||
'Uses current time by default.')
|
||||
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
|
||||
metavar='INT',
|
||||
help='Optional line number. This is the current ' +
|
||||
'line being edited.')
|
||||
parser.add_argument('--cursorpos', dest='cursorpos',
|
||||
action=StoreWithoutQuotes,
|
||||
metavar='INT', action=StoreWithoutQuotes,
|
||||
help='Optional cursor position in the current file.')
|
||||
parser.add_argument('--entity-type', dest='entity_type',
|
||||
action=StoreWithoutQuotes,
|
||||
@ -89,8 +91,8 @@ def parse_arguments():
|
||||
help='Category of this heartbeat activity. Can be ' +
|
||||
'"coding", "building", "indexing", ' +
|
||||
'"debugging", "running tests", ' +
|
||||
'"manual testing", "browsing", ' +
|
||||
'"code reviewing" or "designing". ' +
|
||||
'"writing tests", "manual testing", ' +
|
||||
'"code reviewing", "browsing", or "designing". ' +
|
||||
'Defaults to "coding".')
|
||||
parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
|
||||
help='Optional proxy configuration. Supports HTTPS '+
|
||||
@ -103,10 +105,14 @@ def parse_arguments():
|
||||
help='Disables SSL certificate verification for HTTPS '+
|
||||
'requests. By default, SSL certificates are ' +
|
||||
'verified.')
|
||||
parser.add_argument('--ssl-certs-file', dest='ssl_certs_file',
|
||||
metavar='FILE', action=StoreWithoutQuotes,
|
||||
help='Override the bundled Python Requests CA certs ' +
|
||||
'file. By default, uses certifi for ca certs.')
|
||||
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
|
||||
help='Optional project name.')
|
||||
parser.add_argument('--alternate-project', dest='alternate_project',
|
||||
action=StoreWithoutQuotes,
|
||||
metavar='PROJECT', action=StoreWithoutQuotes,
|
||||
help='Optional alternate project name. ' +
|
||||
'Auto-discovered project takes priority.')
|
||||
parser.add_argument('--alternate-language', dest='alternate_language',
|
||||
@ -149,7 +155,12 @@ def parse_arguments():
|
||||
'folder name as the project, a ' +
|
||||
'.wakatime-project file is created with a ' +
|
||||
'random project name.')
|
||||
parser.add_argument('--hide-branch-names', dest='hide_branch_names',
|
||||
action='store_true',
|
||||
help='Obfuscate branch names. Will not send revision ' +
|
||||
'control branch names to api.')
|
||||
parser.add_argument('--exclude', dest='exclude', action='append',
|
||||
metavar='PATH',
|
||||
help='Filename patterns to exclude from logging. ' +
|
||||
'POSIX regex syntax. Can be used more than once.')
|
||||
parser.add_argument('--exclude-unknown-project',
|
||||
@ -157,6 +168,7 @@ def parse_arguments():
|
||||
help='When set, any activity where the project ' +
|
||||
'cannot be detected will be ignored.')
|
||||
parser.add_argument('--include', dest='include', action='append',
|
||||
metavar='PATH',
|
||||
help='Filename patterns to log. When used in ' +
|
||||
'combination with --exclude, files matching ' +
|
||||
'include will still be logged. POSIX regex ' +
|
||||
@ -173,32 +185,50 @@ def parse_arguments():
|
||||
help='Reads extra heartbeats from STDIN as a JSON ' +
|
||||
'array until EOF.')
|
||||
parser.add_argument('--log-file', dest='log_file',
|
||||
action=StoreWithoutQuotes,
|
||||
metavar='FILE', action=StoreWithoutQuotes,
|
||||
help='Defaults to ~/.wakatime.log.')
|
||||
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
|
||||
metavar='URL',
|
||||
help='Heartbeats api url. For debugging with a ' +
|
||||
'local server.')
|
||||
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument('--timeout', dest='timeout', type=int,
|
||||
action=StoreWithoutQuotes,
|
||||
metavar='SECONDS', action=StoreWithoutQuotes,
|
||||
help='Number of seconds to wait when sending ' +
|
||||
'heartbeats to api. Defaults to 60 seconds.')
|
||||
parser.add_argument('--sync-offline-activity',
|
||||
parser.add_argument('--sync-offline-activity', metavar='AMOUNT',
|
||||
dest='sync_offline_activity',
|
||||
action=StoreWithoutQuotes,
|
||||
help='Amount of offline activity to sync from your ' +
|
||||
'local ~/.wakatime.db sqlite3 file to your ' +
|
||||
'WakaTime Dashboard before exiting. Can be ' +
|
||||
'"none" or a positive integer number. Defaults ' +
|
||||
'to 5, meaning for every heartbeat sent while ' +
|
||||
'online 5 offline heartbeats are synced. Can ' +
|
||||
'be used without --entity to only sync offline ' +
|
||||
'activity without generating new heartbeats.')
|
||||
'to 100, meaning for every heartbeat sent ' +
|
||||
'while online, 100 offline heartbeats are ' +
|
||||
'synced. Can be used without --entity to only ' +
|
||||
'sync offline activity without generating new ' +
|
||||
'heartbeats.')
|
||||
parser.add_argument('--today', dest='today',
|
||||
action='store_true',
|
||||
help='Prints dashboard time for Today, then exits.')
|
||||
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
|
||||
help='Defaults to ~/.wakatime.cfg.')
|
||||
metavar='FILE', help='Defaults to ~/.wakatime.cfg.')
|
||||
parser.add_argument('--config-section', dest='config_section',
|
||||
metavar='SECTION', action=StoreWithoutQuotes,
|
||||
help='Optional config section when reading or ' +
|
||||
'writing a config key. Defaults to [settings].')
|
||||
parser.add_argument('--config-read', dest='config_read',
|
||||
metavar='KEY', action=StoreWithoutQuotes,
|
||||
help='Prints value for the given config key, then ' +
|
||||
'exits.')
|
||||
parser.add_argument('--config-write', dest='config_write',
|
||||
nargs=2, metavar=('KEY', 'VALUE'),
|
||||
action=StoreWithoutQuotes,
|
||||
help='Writes value to a config key, then exits. ' +
|
||||
'Expects two arguments, key and value.')
|
||||
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
||||
help='Turns on debug messages in log file.')
|
||||
parser.add_argument('--version', action='version', version=__version__)
|
||||
@ -206,13 +236,30 @@ def parse_arguments():
|
||||
# parse command line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# parse ~/.wakatime.cfg file
|
||||
configs = parseConfigFile(args.config)
|
||||
|
||||
if args.config_read:
|
||||
section = args.config_section or 'settings'
|
||||
key = args.config_read
|
||||
print(configs.get(section, key))
|
||||
raise SystemExit(SUCCESS)
|
||||
|
||||
if args.config_write:
|
||||
section = args.config_section or 'settings'
|
||||
key = args.config_write[0]
|
||||
val = args.config_write[1]
|
||||
if not configs.has_section(section):
|
||||
configs.add_section(section)
|
||||
configs.set(section, key, val)
|
||||
with open(args.config or getConfigFile(), 'w', encoding='utf-8') as fh:
|
||||
configs.write(fh)
|
||||
raise SystemExit(SUCCESS)
|
||||
|
||||
# use current unix epoch timestamp by default
|
||||
if not args.timestamp:
|
||||
args.timestamp = time.time()
|
||||
|
||||
# parse ~/.wakatime.cfg file
|
||||
configs = parseConfigFile(args.config)
|
||||
|
||||
# update args from configs
|
||||
if not args.hostname:
|
||||
if configs.has_option('settings', 'hostname'):
|
||||
@ -241,7 +288,7 @@ def parse_arguments():
|
||||
if not args.entity:
|
||||
if args.file:
|
||||
args.entity = args.file
|
||||
elif not args.sync_offline_activity or args.sync_offline_activity == 'none':
|
||||
elif (not args.sync_offline_activity or args.sync_offline_activity == 'none') and not args.today:
|
||||
parser.error('argument --entity is required')
|
||||
|
||||
if not args.sync_offline_activity:
|
||||
@ -275,7 +322,7 @@ def parse_arguments():
|
||||
except TypeError: # pragma: nocover
|
||||
pass
|
||||
if not args.include_only_with_project_file and configs.has_option('settings', 'include_only_with_project_file'):
|
||||
args.include_only_with_project_file = configs.get('settings', 'include_only_with_project_file')
|
||||
args.include_only_with_project_file = configs.get('settings', 'include_only_with_project_file') == 'true'
|
||||
if not args.include:
|
||||
args.include = []
|
||||
if configs.has_option('settings', 'include'):
|
||||
@ -287,8 +334,9 @@ def parse_arguments():
|
||||
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')
|
||||
boolean_or_list('hide_file_names', args, configs, alternative_names=['hide_filenames', 'hidefilenames'])
|
||||
boolean_or_list('hide_project_names', args, configs, alternative_names=['hide_projectnames', 'hideprojectnames'])
|
||||
_boolean_or_list('hide_file_names', args, configs, alternative_names=['hide_filenames', 'hidefilenames'])
|
||||
_boolean_or_list('hide_project_names', args, configs, alternative_names=['hide_projectnames', 'hideprojectnames'])
|
||||
_boolean_or_list('hide_branch_names', args, configs, alternative_names=['hide_branchnames', 'hidebranchnames'], default=None)
|
||||
if args.offline_deprecated:
|
||||
args.offline = False
|
||||
if args.offline and configs.has_option('settings', 'offline'):
|
||||
@ -307,6 +355,8 @@ def parse_arguments():
|
||||
'domain\\user:pass.')
|
||||
if configs.has_option('settings', 'no_ssl_verify'):
|
||||
args.nosslverify = configs.getboolean('settings', 'no_ssl_verify')
|
||||
if configs.has_option('settings', 'ssl_certs_file'):
|
||||
args.ssl_certs_file = configs.get('settings', 'ssl_certs_file')
|
||||
if not args.verbose and configs.has_option('settings', 'verbose'):
|
||||
args.verbose = configs.getboolean('settings', 'verbose')
|
||||
if not args.verbose and configs.has_option('settings', 'debug'):
|
||||
@ -331,16 +381,16 @@ def parse_arguments():
|
||||
return args, configs
|
||||
|
||||
|
||||
def boolean_or_list(config_name, args, configs, alternative_names=[]):
|
||||
def _boolean_or_list(config_name, args, configs, alternative_names=[], default=[]):
|
||||
"""Get a boolean or list of regexes from args and configs."""
|
||||
|
||||
# when argument flag present, set to wildcard regex
|
||||
for key in alternative_names:
|
||||
for key in alternative_names + [config_name]:
|
||||
if hasattr(args, key) and getattr(args, key):
|
||||
setattr(args, config_name, ['.*'])
|
||||
return
|
||||
|
||||
setattr(args, config_name, [])
|
||||
setattr(args, config_name, default)
|
||||
|
||||
option = None
|
||||
alternative_names.insert(0, config_name)
|
||||
@ -352,7 +402,11 @@ def boolean_or_list(config_name, args, configs, alternative_names=[]):
|
||||
if option is not None:
|
||||
if option.strip().lower() == 'true':
|
||||
setattr(args, config_name, ['.*'])
|
||||
elif option.strip().lower() != 'false':
|
||||
elif option.strip().lower() == 'false':
|
||||
setattr(args, config_name, [])
|
||||
else:
|
||||
for pattern in option.split("\n"):
|
||||
if pattern.strip() != '':
|
||||
if not getattr(args, config_name):
|
||||
setattr(args, config_name, [])
|
||||
getattr(args, config_name).append(pattern)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
@ -115,4 +116,7 @@ class Popen(subprocess.Popen):
|
||||
except AttributeError:
|
||||
pass
|
||||
kwargs['startupinfo'] = startupinfo
|
||||
super(Popen, self).__init__(*args, **kwargs)
|
||||
if 'env' not in kwargs:
|
||||
kwargs['env'] = os.environ.copy()
|
||||
kwargs['env']['LANG'] = 'en-US' if is_win else 'en_US.UTF-8'
|
||||
subprocess.Popen.__init__(self, *args, **kwargs)
|
||||
|
@ -46,11 +46,11 @@ Default is 2MB.
|
||||
"""
|
||||
MAX_FILE_SIZE_SUPPORTED = 2000000
|
||||
|
||||
""" Default number of offline heartbeats to sync before exiting."""
|
||||
""" Default limit of number of offline heartbeats to sync before exiting."""
|
||||
DEFAULT_SYNC_OFFLINE_ACTIVITY = 100
|
||||
|
||||
""" Number of heartbeats per api request.
|
||||
Even when sending more heartbeats, this is the number of heartbeats sent per
|
||||
individual https request to the WakaTime API.
|
||||
"""
|
||||
HEARTBEATS_PER_REQUEST = 10
|
||||
HEARTBEATS_PER_REQUEST = 25
|
||||
|
@ -131,5 +131,9 @@ class DependencyParser(object):
|
||||
if self.parser:
|
||||
plugin = self.parser(self.source_file, lexer=self.lexer)
|
||||
dependencies = plugin.parse()
|
||||
return list(set(dependencies))
|
||||
|
||||
def filter_dependencies(dep):
|
||||
return dep and len(dep) <= 200
|
||||
|
||||
return list(filter(filter_dependencies, set(dependencies)))
|
||||
return []
|
||||
|
@ -10,8 +10,9 @@
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
from subprocess import PIPE
|
||||
|
||||
from .compat import u, json
|
||||
from .compat import u, json, is_win, Popen
|
||||
from .exceptions import SkipHeartbeat
|
||||
from .project import get_project_info
|
||||
from .stats import get_file_stats
|
||||
@ -42,7 +43,15 @@ class Heartbeat(object):
|
||||
cursorpos = None
|
||||
user_agent = None
|
||||
|
||||
_sensitive = ('dependencies', 'lines', 'lineno', 'cursorpos', 'branch')
|
||||
_sensitive_when_hiding_filename = (
|
||||
'dependencies',
|
||||
'lines',
|
||||
'lineno',
|
||||
'cursorpos',
|
||||
)
|
||||
_sensitive_when_hiding_branch = (
|
||||
'branch',
|
||||
)
|
||||
|
||||
def __init__(self, data, args, configs, _clone=None):
|
||||
if not data:
|
||||
@ -69,6 +78,7 @@ class Heartbeat(object):
|
||||
'debugging',
|
||||
'running tests',
|
||||
'manual testing',
|
||||
'writing tests',
|
||||
'browsing',
|
||||
'code reviewing',
|
||||
'designing',
|
||||
@ -85,6 +95,7 @@ class Heartbeat(object):
|
||||
return
|
||||
if self.type == 'file':
|
||||
self.entity = format_file_path(self.entity)
|
||||
self._format_local_file()
|
||||
if not self._file_exists():
|
||||
self.skip = u('File does not exist; ignoring this heartbeat.')
|
||||
return
|
||||
@ -138,21 +149,24 @@ class Heartbeat(object):
|
||||
Returns a Heartbeat.
|
||||
"""
|
||||
|
||||
if not self.args.hide_file_names:
|
||||
return self
|
||||
|
||||
if self.entity is None:
|
||||
return self
|
||||
|
||||
if self.type != 'file':
|
||||
return self
|
||||
|
||||
if self.should_obfuscate_filename():
|
||||
self._sanitize_metadata()
|
||||
if self._should_obfuscate_filename():
|
||||
self._sanitize_metadata(keys=self._sensitive_when_hiding_filename)
|
||||
if self._should_obfuscate_branch(default=True):
|
||||
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
|
||||
extension = u(os.path.splitext(self.entity)[1])
|
||||
self.entity = u('HIDDEN{0}').format(extension)
|
||||
elif self.should_obfuscate_project():
|
||||
self._sanitize_metadata()
|
||||
self._sanitize_metadata(keys=self._sensitive_when_hiding_filename)
|
||||
if self._should_obfuscate_branch(default=True):
|
||||
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
|
||||
elif self._should_obfuscate_branch():
|
||||
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
|
||||
|
||||
return self
|
||||
|
||||
@ -190,22 +204,6 @@ class Heartbeat(object):
|
||||
is_write=self.is_write,
|
||||
)
|
||||
|
||||
def should_obfuscate_filename(self):
|
||||
"""Returns True if hide_file_names is true or the entity file path
|
||||
matches one in the list of obfuscated file paths."""
|
||||
|
||||
for pattern in self.args.hide_file_names:
|
||||
try:
|
||||
compiled = re.compile(pattern, re.IGNORECASE)
|
||||
if compiled.search(self.entity):
|
||||
return True
|
||||
except re.error as ex:
|
||||
log.warning(u('Regex error ({msg}) for hide_file_names pattern: {pattern}').format(
|
||||
msg=u(ex),
|
||||
pattern=u(pattern),
|
||||
))
|
||||
return False
|
||||
|
||||
def should_obfuscate_project(self):
|
||||
"""Returns True if hide_project_names is true or the entity file path
|
||||
matches one in the list of obfuscated project paths."""
|
||||
@ -220,6 +218,49 @@ class Heartbeat(object):
|
||||
msg=u(ex),
|
||||
pattern=u(pattern),
|
||||
))
|
||||
|
||||
return False
|
||||
|
||||
def _should_obfuscate_filename(self):
|
||||
"""Returns True if hide_file_names is true or the entity file path
|
||||
matches one in the list of obfuscated file paths."""
|
||||
|
||||
for pattern in self.args.hide_file_names:
|
||||
try:
|
||||
compiled = re.compile(pattern, re.IGNORECASE)
|
||||
if compiled.search(self.entity):
|
||||
return True
|
||||
except re.error as ex:
|
||||
log.warning(u('Regex error ({msg}) for hide_file_names pattern: {pattern}').format(
|
||||
msg=u(ex),
|
||||
pattern=u(pattern),
|
||||
))
|
||||
|
||||
return False
|
||||
|
||||
def _should_obfuscate_branch(self, default=False):
|
||||
"""Returns True if hide_file_names is true or the entity file path
|
||||
matches one in the list of obfuscated file paths."""
|
||||
|
||||
# when project names or file names are hidden and hide_branch_names is
|
||||
# not set, we default to hiding branch names along with file/project.
|
||||
if default and self.args.hide_branch_names is None:
|
||||
return True
|
||||
|
||||
if not self.branch or not self.args.hide_branch_names:
|
||||
return False
|
||||
|
||||
for pattern in self.args.hide_branch_names:
|
||||
try:
|
||||
compiled = re.compile(pattern, re.IGNORECASE)
|
||||
if compiled.search(self.entity) or compiled.search(self.branch):
|
||||
return True
|
||||
except re.error as ex:
|
||||
log.warning(u('Regex error ({msg}) for hide_branch_names pattern: {pattern}').format(
|
||||
msg=u(ex),
|
||||
pattern=u(pattern),
|
||||
))
|
||||
|
||||
return False
|
||||
|
||||
def _unicode(self, value):
|
||||
@ -236,6 +277,87 @@ class Heartbeat(object):
|
||||
return (self.entity and os.path.isfile(self.entity) or
|
||||
self.args.local_file and os.path.isfile(self.args.local_file))
|
||||
|
||||
def _format_local_file(self):
|
||||
"""When args.local_file empty on Windows, tries to map args.entity to a
|
||||
unc path.
|
||||
|
||||
Updates args.local_file in-place without returning anything.
|
||||
"""
|
||||
|
||||
if self.type != 'file':
|
||||
return
|
||||
|
||||
if not self.entity:
|
||||
return
|
||||
|
||||
if not is_win:
|
||||
return
|
||||
|
||||
if self._file_exists():
|
||||
return
|
||||
|
||||
self.args.local_file = self._to_unc_path(self.entity)
|
||||
|
||||
def _to_unc_path(self, filepath):
|
||||
drive, rest = self._splitdrive(filepath)
|
||||
if not drive:
|
||||
return filepath
|
||||
|
||||
stdout = None
|
||||
try:
|
||||
stdout, stderr = Popen(['net', 'use'], stdout=PIPE, stderr=PIPE).communicate()
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
if stdout:
|
||||
cols = None
|
||||
for line in stdout.strip().splitlines()[1:]:
|
||||
line = u(line)
|
||||
if not line.strip():
|
||||
continue
|
||||
if not cols:
|
||||
cols = self._unc_columns(line)
|
||||
continue
|
||||
start, end = cols.get('local', (0, 0))
|
||||
if not start and not end:
|
||||
break
|
||||
local = line[start:end].strip().split(':')[0].upper()
|
||||
if not local.isalpha():
|
||||
continue
|
||||
if local == drive:
|
||||
start, end = cols.get('remote', (0, 0))
|
||||
if not start and not end:
|
||||
break
|
||||
remote = line[start:end].strip()
|
||||
return remote + rest
|
||||
|
||||
return filepath
|
||||
|
||||
def _unc_columns(self, line):
|
||||
cols = {}
|
||||
current_col = u('')
|
||||
newcol = False
|
||||
start, end = 0, 0
|
||||
for char in line:
|
||||
if char.isalpha():
|
||||
if newcol:
|
||||
cols[current_col.strip().lower()] = (start, end)
|
||||
current_col = u('')
|
||||
start = end
|
||||
newcol = False
|
||||
current_col += u(char)
|
||||
else:
|
||||
newcol = True
|
||||
end += 1
|
||||
if start != end and current_col:
|
||||
cols[current_col.strip().lower()] = (start, -1)
|
||||
return cols
|
||||
|
||||
def _splitdrive(self, filepath):
|
||||
if filepath[1:2] != ':' or not filepath[0].isalpha():
|
||||
return None, filepath
|
||||
return filepath[0].upper(), filepath[2:]
|
||||
|
||||
def _excluded_by_pattern(self):
|
||||
return should_exclude(self.entity, self.args.include, self.args.exclude)
|
||||
|
||||
@ -249,8 +371,8 @@ class Heartbeat(object):
|
||||
return False
|
||||
return find_project_file(self.entity) is None
|
||||
|
||||
def _sanitize_metadata(self):
|
||||
for key in self._sensitive:
|
||||
def _sanitize_metadata(self, keys=[]):
|
||||
for key in keys:
|
||||
setattr(self, key, None)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -14,6 +14,7 @@ from __future__ import print_function
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
pwd = os.path.dirname(os.path.abspath(__file__))
|
||||
@ -21,7 +22,7 @@ sys.path.insert(0, os.path.dirname(pwd))
|
||||
sys.path.insert(0, os.path.join(pwd, 'packages'))
|
||||
|
||||
from .__about__ import __version__
|
||||
from .api import send_heartbeats
|
||||
from .api import send_heartbeats, get_time_today
|
||||
from .arguments import parse_arguments
|
||||
from .compat import u, json
|
||||
from .constants import SUCCESS, UNKNOWN_ERROR, HEARTBEATS_PER_REQUEST
|
||||
@ -37,17 +38,26 @@ def execute(argv=None):
|
||||
if argv:
|
||||
sys.argv = ['wakatime'] + argv
|
||||
|
||||
args, configs = parse_arguments()
|
||||
try:
|
||||
args, configs = parse_arguments()
|
||||
except SystemExit as ex:
|
||||
return ex.code
|
||||
|
||||
setup_logging(args, __version__)
|
||||
|
||||
if args.today:
|
||||
text, retval = get_time_today(args)
|
||||
if text:
|
||||
print(text)
|
||||
return retval
|
||||
|
||||
try:
|
||||
heartbeats = []
|
||||
|
||||
hb = Heartbeat(vars(args), args, configs)
|
||||
if hb:
|
||||
heartbeats.append(hb)
|
||||
else:
|
||||
elif args.entity:
|
||||
log.debug(hb.skip)
|
||||
|
||||
if args.extra_heartbeats:
|
||||
@ -76,6 +86,7 @@ def execute(argv=None):
|
||||
if retval == SUCCESS:
|
||||
queue = Queue(args, configs)
|
||||
for offline_heartbeats in queue.pop_many(args.sync_offline_activity):
|
||||
time.sleep(1)
|
||||
retval = send_heartbeats(offline_heartbeats, args, configs)
|
||||
if retval != SUCCESS:
|
||||
break
|
||||
|
@ -60,9 +60,12 @@ class Queue(object):
|
||||
}
|
||||
c.execute('INSERT INTO {0} VALUES (:id,:heartbeat)'.format(self.table_name), data)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except sqlite3.Error:
|
||||
log.traceback()
|
||||
try:
|
||||
conn.close()
|
||||
except: # pragma: nocover
|
||||
pass
|
||||
|
||||
def pop(self):
|
||||
if not HAS_SQL:
|
||||
|
@ -1,3 +1,3 @@
|
||||
from .core import where, old_where
|
||||
from .core import where
|
||||
|
||||
__version__ = "2017.07.27.1"
|
||||
__version__ = "2019.03.09"
|
||||
|
@ -1,2 +1,2 @@
|
||||
from certifi import where
|
||||
from . import where
|
||||
print(where())
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
@ -8,29 +7,9 @@ certifi.py
|
||||
This module returns the installation location of cacert.pem.
|
||||
"""
|
||||
import os
|
||||
import warnings
|
||||
|
||||
|
||||
class DeprecatedBundleWarning(DeprecationWarning):
|
||||
"""
|
||||
The weak security bundle is being deprecated. Please bother your service
|
||||
provider to get them to stop using cross-signed roots.
|
||||
"""
|
||||
|
||||
|
||||
def where():
|
||||
f = os.path.dirname(__file__)
|
||||
|
||||
return os.path.join(f, 'cacert.pem')
|
||||
|
||||
|
||||
def old_where():
|
||||
warnings.warn(
|
||||
"The weak security bundle is being deprecated.",
|
||||
DeprecatedBundleWarning
|
||||
)
|
||||
f = os.path.dirname(__file__)
|
||||
return os.path.join(f, 'weak.pem')
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(where())
|
||||
|
@ -1,414 +0,0 @@
|
||||
# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
|
||||
# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
|
||||
# Label: "Entrust.net Secure Server CA"
|
||||
# Serial: 927650371
|
||||
# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee
|
||||
# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39
|
||||
# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
|
||||
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
|
||||
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
|
||||
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
|
||||
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
|
||||
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
|
||||
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
|
||||
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
|
||||
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
|
||||
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
|
||||
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
|
||||
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
|
||||
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
|
||||
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
|
||||
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
|
||||
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
|
||||
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
|
||||
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
|
||||
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
|
||||
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
|
||||
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
|
||||
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
|
||||
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
|
||||
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
|
||||
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
|
||||
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
|
||||
# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
|
||||
# Label: "ValiCert Class 2 VA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87
|
||||
# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6
|
||||
# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
|
||||
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
|
||||
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
|
||||
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
|
||||
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
|
||||
NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
|
||||
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
|
||||
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
|
||||
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
|
||||
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
|
||||
dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
|
||||
WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
|
||||
v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
|
||||
UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
|
||||
IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
|
||||
W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
|
||||
# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
|
||||
# Label: "NetLock Express (Class C) Root"
|
||||
# Serial: 104
|
||||
# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4
|
||||
# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b
|
||||
# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx
|
||||
ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
|
||||
b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD
|
||||
EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X
|
||||
DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw
|
||||
DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u
|
||||
c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr
|
||||
TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN
|
||||
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA
|
||||
OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC
|
||||
2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW
|
||||
RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P
|
||||
AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW
|
||||
ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0
|
||||
YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz
|
||||
b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO
|
||||
ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB
|
||||
IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs
|
||||
b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
|
||||
ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s
|
||||
YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg
|
||||
a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g
|
||||
SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0
|
||||
aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg
|
||||
YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg
|
||||
Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY
|
||||
ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g
|
||||
pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4
|
||||
Fp1hBWeAyNDYpQcCNJgEjTME1A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
|
||||
# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok
|
||||
# Label: "NetLock Business (Class B) Root"
|
||||
# Serial: 105
|
||||
# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6
|
||||
# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af
|
||||
# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx
|
||||
ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
|
||||
b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD
|
||||
EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05
|
||||
OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G
|
||||
A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
|
||||
Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l
|
||||
dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG
|
||||
SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK
|
||||
gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX
|
||||
iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc
|
||||
Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E
|
||||
BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G
|
||||
SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu
|
||||
b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh
|
||||
bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv
|
||||
Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln
|
||||
aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0
|
||||
IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
|
||||
c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph
|
||||
biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo
|
||||
ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP
|
||||
UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj
|
||||
YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo
|
||||
dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA
|
||||
bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06
|
||||
sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa
|
||||
n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS
|
||||
NitjrFgBazMpUIaD8QFI
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
|
||||
# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
|
||||
# Label: "RSA Root Certificate 1"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72
|
||||
# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb
|
||||
# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
|
||||
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
|
||||
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
|
||||
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
|
||||
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
|
||||
NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
|
||||
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
|
||||
YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
|
||||
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
|
||||
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
|
||||
cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
|
||||
2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
|
||||
JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
|
||||
Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
|
||||
n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
|
||||
PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
|
||||
# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
|
||||
# Label: "ValiCert Class 1 VA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb
|
||||
# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e
|
||||
# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
|
||||
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
|
||||
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
|
||||
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
|
||||
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy
|
||||
NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
|
||||
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
|
||||
YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
|
||||
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
|
||||
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y
|
||||
LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+
|
||||
TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y
|
||||
TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0
|
||||
LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW
|
||||
I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
|
||||
nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
|
||||
# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
|
||||
# Label: "Equifax Secure eBusiness CA 1"
|
||||
# Serial: 4
|
||||
# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d
|
||||
# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41
|
||||
# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
|
||||
MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
|
||||
ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
|
||||
MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
|
||||
LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
|
||||
KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
|
||||
RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
|
||||
WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
|
||||
Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
|
||||
AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
|
||||
eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
|
||||
zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
|
||||
WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
|
||||
/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
|
||||
# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
|
||||
# Label: "Equifax Secure Global eBusiness CA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc
|
||||
# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45
|
||||
# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
|
||||
MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
|
||||
ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
|
||||
MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
|
||||
dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
|
||||
c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
|
||||
UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
|
||||
58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
|
||||
o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
|
||||
aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
|
||||
A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
|
||||
Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
|
||||
8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
|
||||
# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
|
||||
# Label: "Thawte Premium Server CA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a
|
||||
# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a
|
||||
# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
|
||||
FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
|
||||
VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
|
||||
biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
|
||||
dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
|
||||
MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
|
||||
MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
|
||||
A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
|
||||
b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
|
||||
cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
|
||||
bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
|
||||
VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
|
||||
ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
|
||||
uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
|
||||
9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
|
||||
hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
|
||||
pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
|
||||
# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
|
||||
# Label: "Thawte Server CA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d
|
||||
# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c
|
||||
# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
|
||||
FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
|
||||
VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
|
||||
biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
|
||||
MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
|
||||
MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
|
||||
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
|
||||
dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
|
||||
cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
|
||||
DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
|
||||
gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
|
||||
yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
|
||||
L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
|
||||
EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
|
||||
7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
|
||||
QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
|
||||
qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
|
||||
# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
|
||||
# Label: "Verisign Class 3 Public Primary Certification Authority"
|
||||
# Serial: 149843929435818692848040365716851702463
|
||||
# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67
|
||||
# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2
|
||||
# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
|
||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
||||
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
||||
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
|
||||
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
|
||||
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
|
||||
# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority
|
||||
# Label: "Verisign Class 3 Public Primary Certification Authority"
|
||||
# Serial: 80507572722862485515306429940691309246
|
||||
# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4
|
||||
# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b
|
||||
# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
|
||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
||||
MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
||||
CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
|
||||
2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
|
||||
2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
|
||||
# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
|
||||
# Label: "Verisign Class 3 Public Primary Certification Authority - G2"
|
||||
# Serial: 167285380242319648451154478808036881606
|
||||
# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9
|
||||
# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f
|
||||
# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
|
||||
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
|
||||
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
|
||||
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
|
||||
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
|
||||
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
|
||||
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
|
||||
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
|
||||
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
|
||||
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
|
||||
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
|
||||
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
|
||||
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
|
||||
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
|
||||
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
|
||||
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
|
||||
oJ2daZH9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
|
||||
# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
|
||||
# Label: "GTE CyberTrust Global Root"
|
||||
# Serial: 421
|
||||
# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db
|
||||
# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74
|
||||
# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
|
||||
VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
|
||||
bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
|
||||
b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
|
||||
UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
|
||||
cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
|
||||
b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
|
||||
iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
|
||||
r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
|
||||
04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
|
||||
GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
|
||||
3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
|
||||
lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
|
||||
# Subject: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
|
||||
# Label: "Equifax Secure Certificate Authority"
|
||||
# Serial: 903804111
|
||||
# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4
|
||||
# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a
|
||||
# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
|
||||
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
|
||||
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
|
||||
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
|
||||
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
|
||||
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
|
||||
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
|
||||
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
|
||||
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
|
||||
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
|
||||
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
|
||||
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
|
||||
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
|
||||
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
|
||||
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
|
||||
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
|
||||
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
|
||||
-----END CERTIFICATE-----
|
File diff suppressed because it is too large
Load Diff
@ -76,24 +76,25 @@ def get_project_info(configs, heartbeat, data):
|
||||
|
||||
plugin_name = plugin_cls.__name__.lower()
|
||||
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
||||
|
||||
project = plugin_cls(heartbeat.entity, configs=plugin_configs)
|
||||
|
||||
if project.process():
|
||||
project_name = project_name or project.name()
|
||||
if not hide_project:
|
||||
project_name = project_name or project.name()
|
||||
branch_name = branch_name or project.branch()
|
||||
if hide_project:
|
||||
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
|
||||
|
||||
if project_name is None and not hide_project:
|
||||
project_name = data.get('alternate_project') or heartbeat.args.alternate_project
|
||||
if project_name is None:
|
||||
if not hide_project:
|
||||
project_name = data.get('alternate_project') or heartbeat.args.alternate_project
|
||||
else:
|
||||
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
|
||||
|
||||
return project_name, branch_name
|
||||
|
||||
|
@ -78,7 +78,7 @@ class Git(BaseProject):
|
||||
|
||||
def _get_branch_from_head_file(self, line):
|
||||
if u(line.strip()).startswith('ref: '):
|
||||
return u(line.strip().rsplit('/', 1)[-1])
|
||||
return u(line.strip().split('/', 2)[-1])
|
||||
return None
|
||||
|
||||
def _submodules_supported_for_path(self, path):
|
||||
@ -87,10 +87,10 @@ class Git(BaseProject):
|
||||
|
||||
disabled = self._configs.get('submodules_disabled')
|
||||
|
||||
if not disabled or disabled.strip().lower() == 'false':
|
||||
return True
|
||||
if disabled.strip().lower() == 'true':
|
||||
return False
|
||||
if disabled.strip().lower() == 'false':
|
||||
return True
|
||||
|
||||
for pattern in disabled.split("\n"):
|
||||
if pattern.strip():
|
||||
|
@ -36,11 +36,11 @@ class Mercurial(BaseProject):
|
||||
branch_file = os.path.join(self.configDir, 'branch')
|
||||
try:
|
||||
with open(branch_file, 'r', encoding='utf-8') as fh:
|
||||
return u(fh.readline().strip().rsplit('/', 1)[-1])
|
||||
return u(fh.readline().strip())
|
||||
except UnicodeDecodeError: # pragma: nocover
|
||||
try:
|
||||
with open(branch_file, 'r', encoding=sys.getfilesystemencoding()) as fh:
|
||||
return u(fh.readline().strip().rsplit('/', 1)[-1])
|
||||
return u(fh.readline().strip())
|
||||
except:
|
||||
log.traceback(logging.WARNING)
|
||||
except IOError: # pragma: nocover
|
||||
|
@ -70,17 +70,17 @@ class Subversion(BaseProject):
|
||||
if not self._is_mac() or self._has_xcode_tools():
|
||||
stdout = None
|
||||
try:
|
||||
os.environ['LANG'] = 'en_US'
|
||||
stdout, stderr = Popen([
|
||||
self._find_binary(), 'info', os.path.realpath(path)
|
||||
], stdout=PIPE, stderr=PIPE).communicate()
|
||||
stdout, stderr = Popen(
|
||||
[self._find_binary(), 'info', os.path.realpath(path)],
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
).communicate()
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
if stdout:
|
||||
for line in stdout.splitlines():
|
||||
line = u(line)
|
||||
line = line.split(': ', 1)
|
||||
line = u(line).split(': ', 1)
|
||||
if len(line) == 2:
|
||||
info[line[0]] = line[1]
|
||||
return info
|
||||
|
@ -55,9 +55,12 @@ class SessionCache(object):
|
||||
}
|
||||
c.execute('INSERT INTO {0} VALUES (:value)'.format(self.table_name), values)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except: # pragma: nocover
|
||||
log.traceback(logging.DEBUG)
|
||||
try:
|
||||
conn.close()
|
||||
except: # pragma: nocover
|
||||
pass
|
||||
|
||||
def get(self):
|
||||
"""Returns a requests.Session object.
|
||||
@ -87,7 +90,7 @@ class SessionCache(object):
|
||||
try:
|
||||
conn.close()
|
||||
except: # pragma: nocover
|
||||
log.traceback(logging.DEBUG)
|
||||
pass
|
||||
|
||||
return session if session is not None else requests.session()
|
||||
|
||||
@ -101,9 +104,12 @@ class SessionCache(object):
|
||||
conn, c = self.connect()
|
||||
c.execute('DELETE FROM {0}'.format(self.table_name))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except:
|
||||
log.traceback(logging.DEBUG)
|
||||
try:
|
||||
conn.close()
|
||||
except: # pragma: nocover
|
||||
pass
|
||||
|
||||
def _get_db_file(self):
|
||||
home = '~'
|
||||
|
@ -25,6 +25,7 @@ from .packages.pygments.lexers import (
|
||||
_fn_matches,
|
||||
basename,
|
||||
ClassNotFound,
|
||||
CppLexer,
|
||||
find_lexer_class,
|
||||
get_lexer_by_name,
|
||||
)
|
||||
@ -41,31 +42,28 @@ log = logging.getLogger('WakaTime')
|
||||
|
||||
def get_file_stats(file_name, entity_type='file', lineno=None, cursorpos=None,
|
||||
plugin=None, language=None, local_file=None):
|
||||
if entity_type != 'file':
|
||||
stats = {
|
||||
'language': None,
|
||||
'dependencies': [],
|
||||
'lines': None,
|
||||
'lineno': lineno,
|
||||
'cursorpos': cursorpos,
|
||||
}
|
||||
else:
|
||||
language, lexer = standardize_language(language, plugin)
|
||||
"""Returns a hash of information about the entity."""
|
||||
|
||||
language = standardize_language(language, plugin)
|
||||
stats = {
|
||||
'language': language,
|
||||
'dependencies': [],
|
||||
'lines': None,
|
||||
'lineno': lineno,
|
||||
'cursorpos': cursorpos,
|
||||
}
|
||||
|
||||
if entity_type == 'file':
|
||||
lexer = get_lexer(language)
|
||||
if not language:
|
||||
language, lexer = guess_language(file_name, local_file)
|
||||
|
||||
language = use_root_language(language, lexer)
|
||||
|
||||
parser = DependencyParser(local_file or file_name, lexer)
|
||||
dependencies = parser.parse()
|
||||
|
||||
stats = {
|
||||
'language': language,
|
||||
'dependencies': dependencies,
|
||||
stats.update({
|
||||
'language': use_root_language(language, lexer),
|
||||
'dependencies': parser.parse(),
|
||||
'lines': number_lines_in_file(local_file or file_name),
|
||||
'lineno': lineno,
|
||||
'cursorpos': cursorpos,
|
||||
}
|
||||
})
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
@ -171,6 +169,10 @@ def get_language_from_extension(file_name):
|
||||
"""
|
||||
|
||||
filepart, extension = os.path.splitext(file_name)
|
||||
pathpart, filename = os.path.split(file_name)
|
||||
|
||||
if filename == 'go.mod':
|
||||
return 'Go'
|
||||
|
||||
if re.match(r'\.h.*$', extension, re.IGNORECASE) or re.match(r'\.c.*$', extension, re.IGNORECASE):
|
||||
|
||||
@ -184,8 +186,12 @@ def get_language_from_extension(file_name):
|
||||
return 'Objective-C++'
|
||||
|
||||
available_extensions = extensions_in_same_folder(file_name)
|
||||
if '.cpp' in available_extensions:
|
||||
return 'C++'
|
||||
|
||||
for ext in CppLexer.filenames:
|
||||
ext = ext.lstrip('*')
|
||||
if ext in available_extensions:
|
||||
return 'C++'
|
||||
|
||||
if '.c' in available_extensions:
|
||||
return 'C'
|
||||
|
||||
@ -222,22 +228,21 @@ def number_lines_in_file(file_name):
|
||||
def standardize_language(language, plugin):
|
||||
"""Maps a string to the equivalent Pygments language.
|
||||
|
||||
Returns a tuple of (language_str, lexer_obj).
|
||||
Returns the standardized language string.
|
||||
"""
|
||||
|
||||
if not language:
|
||||
return None, None
|
||||
return None
|
||||
|
||||
# standardize language for this plugin
|
||||
if plugin:
|
||||
plugin = plugin.split(' ')[-1].split('/')[0].split('-')[0]
|
||||
standardized = get_language_from_json(language, plugin)
|
||||
if standardized is not None:
|
||||
return standardized, get_lexer(standardized)
|
||||
return standardized
|
||||
|
||||
# standardize language against default languages
|
||||
standardized = get_language_from_json(language, 'default')
|
||||
return standardized, get_lexer(standardized)
|
||||
return get_language_from_json(language, 'default')
|
||||
|
||||
|
||||
def get_lexer(language):
|
||||
@ -254,6 +259,12 @@ def get_lexer(language):
|
||||
|
||||
|
||||
def use_root_language(language, lexer):
|
||||
override = {
|
||||
'Coldfusion HTML': 'ColdFusion',
|
||||
}
|
||||
if language in override:
|
||||
return override[language]
|
||||
|
||||
if lexer and hasattr(lexer, 'root_lexer'):
|
||||
return u(lexer.root_lexer.name)
|
||||
|
||||
|
@ -26,6 +26,7 @@ log = logging.getLogger('WakaTime')
|
||||
|
||||
BACKSLASH_REPLACE_PATTERN = re.compile(r'[\\/]+')
|
||||
WINDOWS_DRIVE_PATTERN = re.compile(r'^[a-z]:/')
|
||||
WINDOWS_NETWORK_MOUNT_PATTERN = re.compile(r'^\\{2}[a-z]+', re.IGNORECASE)
|
||||
|
||||
|
||||
def should_exclude(entity, include, exclude):
|
||||
@ -77,10 +78,16 @@ def format_file_path(filepath):
|
||||
"""Formats a path as absolute and with the correct platform separator."""
|
||||
|
||||
try:
|
||||
is_windows_network_mount = WINDOWS_NETWORK_MOUNT_PATTERN.match(filepath)
|
||||
filepath = os.path.realpath(os.path.abspath(filepath))
|
||||
filepath = re.sub(BACKSLASH_REPLACE_PATTERN, '/', filepath)
|
||||
if WINDOWS_DRIVE_PATTERN.match(filepath):
|
||||
is_windows_drive = WINDOWS_DRIVE_PATTERN.match(filepath)
|
||||
if is_windows_drive:
|
||||
filepath = filepath.capitalize()
|
||||
if is_windows_network_mount:
|
||||
# Add back a / to the front, since the previous modifications
|
||||
# will have replaced any double slashes with single
|
||||
filepath = '/' + filepath
|
||||
except:
|
||||
pass
|
||||
return filepath
|
||||
|
Reference in New Issue
Block a user