upgrade wakatime-cli to v6.0.0
This commit is contained in:
parent
a71660899e
commit
4effebf67b
5 changed files with 83 additions and 59 deletions
|
@ -1,7 +1,7 @@
|
||||||
__title__ = 'wakatime'
|
__title__ = 'wakatime'
|
||||||
__description__ = 'Common interface to the WakaTime api.'
|
__description__ = 'Common interface to the WakaTime api.'
|
||||||
__url__ = 'https://github.com/wakatime/wakatime'
|
__url__ = 'https://github.com/wakatime/wakatime'
|
||||||
__version_info__ = ('5', '0', '1')
|
__version_info__ = ('6', '0', '0')
|
||||||
__version__ = '.'.join(__version_info__)
|
__version__ = '.'.join(__version_info__)
|
||||||
__author__ = 'Alan Hamlett'
|
__author__ = 'Alan Hamlett'
|
||||||
__author_email__ = 'alan@wakatime.com'
|
__author_email__ = 'alan@wakatime.com'
|
||||||
|
|
|
@ -15,3 +15,4 @@ API_ERROR = 102
|
||||||
CONFIG_FILE_PARSE_ERROR = 103
|
CONFIG_FILE_PARSE_ERROR = 103
|
||||||
AUTH_ERROR = 104
|
AUTH_ERROR = 104
|
||||||
UNKNOWN_ERROR = 105
|
UNKNOWN_ERROR = 105
|
||||||
|
MALFORMED_HEARTBEAT_ERROR = 106
|
||||||
|
|
|
@ -41,10 +41,10 @@ class CustomEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
class JsonFormatter(logging.Formatter):
|
class JsonFormatter(logging.Formatter):
|
||||||
|
|
||||||
def setup(self, timestamp, isWrite, entity, version, plugin, verbose,
|
def setup(self, timestamp, is_write, entity, version, plugin, verbose,
|
||||||
warnings=False):
|
warnings=False):
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.isWrite = isWrite
|
self.is_write = is_write
|
||||||
self.entity = entity
|
self.entity = entity
|
||||||
self.version = version
|
self.version = version
|
||||||
self.plugin = plugin
|
self.plugin = plugin
|
||||||
|
@ -61,10 +61,10 @@ class JsonFormatter(logging.Formatter):
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
data['caller'] = record.pathname
|
data['caller'] = record.pathname
|
||||||
data['lineno'] = record.lineno
|
data['lineno'] = record.lineno
|
||||||
data['isWrite'] = self.isWrite
|
data['is_write'] = self.is_write
|
||||||
data['file'] = self.entity
|
data['file'] = self.entity
|
||||||
if not self.isWrite:
|
if not self.is_write:
|
||||||
del data['isWrite']
|
del data['is_write']
|
||||||
data['level'] = record.levelname
|
data['level'] = record.levelname
|
||||||
data['message'] = record.getMessage() if self.warnings else record.msg
|
data['message'] = record.getMessage() if self.warnings else record.msg
|
||||||
if not self.plugin:
|
if not self.plugin:
|
||||||
|
@ -103,7 +103,7 @@ def setup_logging(args, version):
|
||||||
formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
||||||
formatter.setup(
|
formatter.setup(
|
||||||
timestamp=args.timestamp,
|
timestamp=args.timestamp,
|
||||||
isWrite=args.isWrite,
|
is_write=args.is_write,
|
||||||
entity=args.entity,
|
entity=args.entity,
|
||||||
version=version,
|
version=version,
|
||||||
plugin=args.plugin,
|
plugin=args.plugin,
|
||||||
|
@ -118,7 +118,7 @@ def setup_logging(args, version):
|
||||||
warnings_formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
warnings_formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
||||||
warnings_formatter.setup(
|
warnings_formatter.setup(
|
||||||
timestamp=args.timestamp,
|
timestamp=args.timestamp,
|
||||||
isWrite=args.isWrite,
|
is_write=args.is_write,
|
||||||
entity=args.entity,
|
entity=args.entity,
|
||||||
version=version,
|
version=version,
|
||||||
plugin=args.plugin,
|
plugin=args.plugin,
|
||||||
|
|
|
@ -37,6 +37,7 @@ from .constants import (
|
||||||
CONFIG_FILE_PARSE_ERROR,
|
CONFIG_FILE_PARSE_ERROR,
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
UNKNOWN_ERROR,
|
UNKNOWN_ERROR,
|
||||||
|
MALFORMED_HEARTBEAT_ERROR,
|
||||||
)
|
)
|
||||||
from .logger import setup_logging
|
from .logger import setup_logging
|
||||||
from .offlinequeue import Queue
|
from .offlinequeue import Queue
|
||||||
|
@ -101,13 +102,13 @@ def parseArguments():
|
||||||
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
||||||
action=FileAction,
|
action=FileAction,
|
||||||
help='absolute path to file for the heartbeat; can also be a '+
|
help='absolute path to file for the heartbeat; can also be a '+
|
||||||
'url, domain, or app when --entitytype is not file')
|
'url, domain, or app when --entity-type is not file')
|
||||||
parser.add_argument('--file', dest='file', action=FileAction,
|
parser.add_argument('--file', dest='file', action=FileAction,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--key', dest='key',
|
parser.add_argument('--key', dest='key',
|
||||||
help='your wakatime api key; uses api_key from '+
|
help='your wakatime api key; uses api_key from '+
|
||||||
'~/.wakatime.conf by default')
|
'~/.wakatime.cfg by default')
|
||||||
parser.add_argument('--write', dest='isWrite',
|
parser.add_argument('--write', dest='is_write',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='when set, tells api this heartbeat was triggered from '+
|
help='when set, tells api this heartbeat was triggered from '+
|
||||||
'writing to a file')
|
'writing to a file')
|
||||||
|
@ -122,9 +123,9 @@ def parseArguments():
|
||||||
help='optional line number; current line being edited')
|
help='optional line number; current line being edited')
|
||||||
parser.add_argument('--cursorpos', dest='cursorpos',
|
parser.add_argument('--cursorpos', dest='cursorpos',
|
||||||
help='optional cursor position in the current file')
|
help='optional cursor position in the current file')
|
||||||
parser.add_argument('--entitytype', dest='entity_type',
|
parser.add_argument('--entity-type', dest='entity_type',
|
||||||
help='entity type for this heartbeat. can be one of "file", '+
|
help='entity type for this heartbeat. can be one of "file", '+
|
||||||
'"url", "domain", or "app"; defaults to file.')
|
'"domain", or "app"; defaults to file.')
|
||||||
parser.add_argument('--proxy', dest='proxy',
|
parser.add_argument('--proxy', dest='proxy',
|
||||||
help='optional https proxy url; for example: '+
|
help='optional https proxy url; for example: '+
|
||||||
'https://user:pass@localhost:8080')
|
'https://user:pass@localhost:8080')
|
||||||
|
@ -153,14 +154,18 @@ def parseArguments():
|
||||||
'POSIX regex syntax; can be used more than once')
|
'POSIX regex syntax; can be used more than once')
|
||||||
parser.add_argument('--ignore', dest='ignore', action='append',
|
parser.add_argument('--ignore', dest='ignore', action='append',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
|
parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
|
||||||
|
action='store_true',
|
||||||
|
help='reads extra heartbeats from STDIN as a JSON array until EOF')
|
||||||
parser.add_argument('--logfile', dest='logfile',
|
parser.add_argument('--logfile', dest='logfile',
|
||||||
help='defaults to ~/.wakatime.log')
|
help='defaults to ~/.wakatime.log')
|
||||||
parser.add_argument('--apiurl', dest='api_url',
|
parser.add_argument('--apiurl', dest='api_url',
|
||||||
help='heartbeats api url; for debugging with a local server')
|
help='heartbeats api url; for debugging with a local server')
|
||||||
parser.add_argument('--timeout', dest='timeout', type=int,
|
parser.add_argument('--timeout', dest='timeout', type=int,
|
||||||
help='number of seconds to wait when sending heartbeats to api')
|
help='number of seconds to wait when sending heartbeats to api; '+
|
||||||
|
'defaults to 60 seconds')
|
||||||
parser.add_argument('--config', dest='config',
|
parser.add_argument('--config', dest='config',
|
||||||
help='defaults to ~/.wakatime.conf')
|
help='defaults to ~/.wakatime.cfg')
|
||||||
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
||||||
help='turns on debug messages in log file')
|
help='turns on debug messages in log file')
|
||||||
parser.add_argument('--version', action='version', version=__version__)
|
parser.add_argument('--version', action='version', version=__version__)
|
||||||
|
@ -188,8 +193,6 @@ def parseArguments():
|
||||||
args.key = default_key
|
args.key = default_key
|
||||||
else:
|
else:
|
||||||
parser.error('Missing api key')
|
parser.error('Missing api key')
|
||||||
if not args.entity_type:
|
|
||||||
args.entity_type = 'file'
|
|
||||||
if not args.entity:
|
if not args.entity:
|
||||||
if args.file:
|
if args.file:
|
||||||
args.entity = args.file
|
args.entity = args.file
|
||||||
|
@ -295,7 +298,7 @@ def get_user_agent(plugin):
|
||||||
|
|
||||||
|
|
||||||
def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
||||||
entity=None, timestamp=None, isWrite=None, plugin=None,
|
entity=None, timestamp=None, is_write=None, plugin=None,
|
||||||
offline=None, entity_type='file', hidefilenames=None,
|
offline=None, entity_type='file', hidefilenames=None,
|
||||||
proxy=None, api_url=None, timeout=None, **kwargs):
|
proxy=None, api_url=None, timeout=None, **kwargs):
|
||||||
"""Sends heartbeat as POST request to WakaTime api server.
|
"""Sends heartbeat as POST request to WakaTime api server.
|
||||||
|
@ -307,7 +310,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
||||||
if not api_url:
|
if not api_url:
|
||||||
api_url = 'https://api.wakatime.com/api/v1/heartbeats'
|
api_url = 'https://api.wakatime.com/api/v1/heartbeats'
|
||||||
if not timeout:
|
if not timeout:
|
||||||
timeout = 30
|
timeout = 60
|
||||||
log.debug('Sending heartbeat to api at %s' % api_url)
|
log.debug('Sending heartbeat to api at %s' % api_url)
|
||||||
data = {
|
data = {
|
||||||
'time': timestamp,
|
'time': timestamp,
|
||||||
|
@ -327,8 +330,8 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
||||||
data['lineno'] = stats['lineno']
|
data['lineno'] = stats['lineno']
|
||||||
if stats.get('cursorpos'):
|
if stats.get('cursorpos'):
|
||||||
data['cursorpos'] = stats['cursorpos']
|
data['cursorpos'] = stats['cursorpos']
|
||||||
if isWrite:
|
if is_write:
|
||||||
data['is_write'] = isWrite
|
data['is_write'] = is_write
|
||||||
if project:
|
if project:
|
||||||
data['project'] = project
|
data['project'] = project
|
||||||
if branch:
|
if branch:
|
||||||
|
@ -435,7 +438,7 @@ def sync_offline_heartbeats(args, hostname):
|
||||||
hostname=hostname,
|
hostname=hostname,
|
||||||
stats=json.loads(heartbeat['stats']),
|
stats=json.loads(heartbeat['stats']),
|
||||||
key=args.key,
|
key=args.key,
|
||||||
isWrite=heartbeat['is_write'],
|
is_write=heartbeat['is_write'],
|
||||||
plugin=heartbeat['plugin'],
|
plugin=heartbeat['plugin'],
|
||||||
offline=args.offline,
|
offline=args.offline,
|
||||||
hidefilenames=args.hidefilenames,
|
hidefilenames=args.hidefilenames,
|
||||||
|
@ -451,6 +454,45 @@ def sync_offline_heartbeats(args, hostname):
|
||||||
return SUCCESS
|
return SUCCESS
|
||||||
|
|
||||||
|
|
||||||
|
def process_heartbeat(args, configs, hostname, heartbeat):
|
||||||
|
exclude = should_exclude(heartbeat['entity'], args.include, args.exclude)
|
||||||
|
if exclude is not False:
|
||||||
|
log.debug(u('Skipping because matches exclude pattern: {pattern}').format(
|
||||||
|
pattern=u(exclude),
|
||||||
|
))
|
||||||
|
return SUCCESS
|
||||||
|
|
||||||
|
if heartbeat.get('entity_type') not in ['file', 'domain', 'app']:
|
||||||
|
heartbeat['entity_type'] = 'file'
|
||||||
|
|
||||||
|
if heartbeat['entity_type'] != 'file' or os.path.isfile(heartbeat['entity']):
|
||||||
|
|
||||||
|
stats = get_file_stats(heartbeat['entity'],
|
||||||
|
entity_type=heartbeat['entity_type'],
|
||||||
|
lineno=heartbeat.get('lineno'),
|
||||||
|
cursorpos=heartbeat.get('cursorpos'),
|
||||||
|
plugin=args.plugin,
|
||||||
|
alternate_language=heartbeat.get('alternate_language'))
|
||||||
|
|
||||||
|
project = heartbeat.get('project') or heartbeat.get('alternate_project')
|
||||||
|
branch = None
|
||||||
|
if heartbeat['entity_type'] == 'file':
|
||||||
|
project, branch = get_project_info(configs, heartbeat)
|
||||||
|
|
||||||
|
heartbeat['project'] = project
|
||||||
|
heartbeat['branch'] = branch
|
||||||
|
heartbeat['stats'] = stats
|
||||||
|
heartbeat['hostname'] = hostname
|
||||||
|
heartbeat['timeout'] = args.timeout
|
||||||
|
heartbeat['key'] = args.key
|
||||||
|
|
||||||
|
return send_heartbeat(**heartbeat)
|
||||||
|
|
||||||
|
else:
|
||||||
|
log.debug('File does not exist; ignoring this heartbeat.')
|
||||||
|
return SUCCESS
|
||||||
|
|
||||||
|
|
||||||
def execute(argv=None):
|
def execute(argv=None):
|
||||||
if argv:
|
if argv:
|
||||||
sys.argv = ['wakatime'] + argv
|
sys.argv = ['wakatime'] + argv
|
||||||
|
@ -462,44 +504,25 @@ def execute(argv=None):
|
||||||
setup_logging(args, __version__)
|
setup_logging(args, __version__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exclude = should_exclude(args.entity, args.include, args.exclude)
|
|
||||||
if exclude is not False:
|
|
||||||
log.debug(u('Skipping because matches exclude pattern: {pattern}').format(
|
|
||||||
pattern=u(exclude),
|
|
||||||
))
|
|
||||||
return SUCCESS
|
|
||||||
|
|
||||||
if args.entity_type != 'file' or os.path.isfile(args.entity):
|
hostname = args.hostname or socket.gethostname()
|
||||||
|
|
||||||
stats = get_file_stats(args.entity,
|
heartbeat = vars(args)
|
||||||
entity_type=args.entity_type,
|
retval = process_heartbeat(args, configs, hostname, heartbeat)
|
||||||
lineno=args.lineno,
|
|
||||||
cursorpos=args.cursorpos,
|
|
||||||
plugin=args.plugin,
|
|
||||||
alternate_language=args.alternate_language)
|
|
||||||
|
|
||||||
project = args.project or args.alternate_project
|
if args.extra_heartbeats:
|
||||||
branch = None
|
try:
|
||||||
if args.entity_type == 'file':
|
for heartbeat in json.loads(sys.stdin.readline()):
|
||||||
project, branch = get_project_info(configs, args)
|
retval = process_heartbeat(args, configs, hostname, heartbeat)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
retval = MALFORMED_HEARTBEAT_ERROR
|
||||||
|
|
||||||
kwargs = vars(args)
|
if retval == SUCCESS:
|
||||||
kwargs['project'] = project
|
retval = sync_offline_heartbeats(args, hostname)
|
||||||
kwargs['branch'] = branch
|
|
||||||
kwargs['stats'] = stats
|
|
||||||
hostname = args.hostname or socket.gethostname()
|
|
||||||
kwargs['hostname'] = hostname
|
|
||||||
kwargs['timeout'] = args.timeout
|
|
||||||
|
|
||||||
status = send_heartbeat(**kwargs)
|
return retval
|
||||||
if status == SUCCESS:
|
|
||||||
return sync_offline_heartbeats(args, hostname)
|
|
||||||
else:
|
|
||||||
return status
|
|
||||||
|
|
||||||
else:
|
|
||||||
log.debug('File does not exist; ignoring this heartbeat.')
|
|
||||||
return SUCCESS
|
|
||||||
except:
|
except:
|
||||||
log.traceback()
|
log.traceback()
|
||||||
|
print(traceback.format_exc())
|
||||||
return UNKNOWN_ERROR
|
return UNKNOWN_ERROR
|
||||||
|
|
|
@ -33,7 +33,7 @@ REV_CONTROL_PLUGINS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_project_info(configs, args):
|
def get_project_info(configs, heartbeat):
|
||||||
"""Find the current project and branch.
|
"""Find the current project and branch.
|
||||||
|
|
||||||
First looks for a .wakatime-project file. Second, uses the --project arg.
|
First looks for a .wakatime-project file. Second, uses the --project arg.
|
||||||
|
@ -50,14 +50,14 @@ def get_project_info(configs, args):
|
||||||
plugin_name = plugin_cls.__name__.lower()
|
plugin_name = plugin_cls.__name__.lower()
|
||||||
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
||||||
|
|
||||||
project = plugin_cls(args.entity, configs=plugin_configs)
|
project = plugin_cls(heartbeat['entity'], configs=plugin_configs)
|
||||||
if project.process():
|
if project.process():
|
||||||
project_name = project_name or project.name()
|
project_name = project_name or project.name()
|
||||||
branch_name = project.branch()
|
branch_name = project.branch()
|
||||||
break
|
break
|
||||||
|
|
||||||
if project_name is None:
|
if project_name is None:
|
||||||
project_name = args.project
|
project_name = heartbeat.get('project')
|
||||||
|
|
||||||
if project_name is None or branch_name is None:
|
if project_name is None or branch_name is None:
|
||||||
|
|
||||||
|
@ -66,14 +66,14 @@ def get_project_info(configs, args):
|
||||||
plugin_name = plugin_cls.__name__.lower()
|
plugin_name = plugin_cls.__name__.lower()
|
||||||
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
||||||
|
|
||||||
project = plugin_cls(args.entity, configs=plugin_configs)
|
project = plugin_cls(heartbeat['entity'], configs=plugin_configs)
|
||||||
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()
|
||||||
break
|
break
|
||||||
|
|
||||||
if project_name is None:
|
if project_name is None:
|
||||||
project_name = args.alternate_project
|
project_name = heartbeat.get('alternate_project')
|
||||||
|
|
||||||
return project_name, branch_name
|
return project_name, branch_name
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue