Hello community,

here is the log from the commit of package azure-cli-feedback for 
openSUSE:Factory checked in at 2019-10-31 18:16:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-feedback (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-feedback.new.2990 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-feedback"

Thu Oct 31 18:16:10 2019 rev:7 rq:742692 version:2.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-feedback/azure-cli-feedback.changes    
2019-06-05 11:43:33.635035040 +0200
+++ 
/work/SRC/openSUSE:Factory/.azure-cli-feedback.new.2990/azure-cli-feedback.changes
  2019-10-31 18:16:12.170069368 +0100
@@ -1,0 +2,8 @@
+Thu Oct 24 12:10:24 UTC 2019 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- New upstream release
+  + Version 2.2.1
+  + For detailed information about changes see the
+    HISTORY.txt file provided with this package
+
+-------------------------------------------------------------------

Old:
----
  azure-cli-feedback-2.2.0.tar.gz

New:
----
  azure-cli-feedback-2.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-feedback.spec ++++++
--- /var/tmp/diff_new_pack.jszJb2/_old  2019-10-31 18:16:13.014070242 +0100
+++ /var/tmp/diff_new_pack.jszJb2/_new  2019-10-31 18:16:13.022070250 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           azure-cli-feedback
-Version:        2.2.0
+Version:        2.2.1
 Release:        0
 Summary:        Microsoft Azure CLI 'feedback' Command Module
 License:        MIT

++++++ azure-cli-feedback-2.2.0.tar.gz -> azure-cli-feedback-2.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-feedback-2.2.0/HISTORY.rst 
new/azure-cli-feedback-2.2.1/HISTORY.rst
--- old/azure-cli-feedback-2.2.0/HISTORY.rst    2019-04-05 00:46:52.000000000 
+0200
+++ new/azure-cli-feedback-2.2.1/HISTORY.rst    2019-04-19 00:15:19.000000000 
+0200
@@ -3,6 +3,10 @@
 Release History
 ===============
 
+2.2.1
++++++
+* Minor fixes
+
 2.2.0
 +++++
 * `az feedback` now shows metadata on recently run commands
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-feedback-2.2.0/PKG-INFO 
new/azure-cli-feedback-2.2.1/PKG-INFO
--- old/azure-cli-feedback-2.2.0/PKG-INFO       2019-04-05 00:47:37.000000000 
+0200
+++ new/azure-cli-feedback-2.2.1/PKG-INFO       2019-04-19 00:16:08.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: azure-cli-feedback
-Version: 2.2.0
+Version: 2.2.1
 Summary: Microsoft Azure Command-Line Tools Feedback Command Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -20,6 +20,10 @@
         Release History
         ===============
         
+        2.2.1
+        +++++
+        * Minor fixes
+        
         2.2.0
         +++++
         * `az feedback` now shows metadata on recently run commands
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-feedback-2.2.0/azure/cli/command_modules/feedback/custom.py 
new/azure-cli-feedback-2.2.1/azure/cli/command_modules/feedback/custom.py
--- old/azure-cli-feedback-2.2.0/azure/cli/command_modules/feedback/custom.py   
2019-04-05 00:46:52.000000000 +0200
+++ new/azure-cli-feedback-2.2.1/azure/cli/command_modules/feedback/custom.py   
2019-04-19 00:15:19.000000000 +0200
@@ -5,6 +5,7 @@
 
 from __future__ import print_function
 import os
+import re
 import math
 import platform
 import datetime
@@ -23,12 +24,14 @@
 from azure.cli.core.util import get_az_version_string
 from azure.cli.core.azlogging import _UNKNOWN_COMMAND, _CMD_LOG_LINE_PREFIX
 from azure.cli.core.util import open_page_in_browser
-import pyperclip
 
 _ONE_MIN_IN_SECS = 60
 
 _ONE_HR_IN_SECS = 3600
 
+# see: 
https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
+_MAX_URL_LENGTH = 2035
+
 
 logger = get_logger(__name__)
 
@@ -36,8 +39,12 @@
 
 _GET_STARTED_URL = "aka.ms/azcli/get-started"
 _QUESTIONS_URL = "aka.ms/azcli/questions"
+
 _CLI_ISSUES_URL = "aka.ms/azcli/issues"
+_RAW_CLI_ISSUES_URL = "https://github.com/Azure/azure-cli/issues/new";
+
 _EXTENSIONS_ISSUES_URL = "aka.ms/azcli/ext/issues"
+_RAW_EXTENSIONS_ISSUES_URL = 
"https://github.com/Azure/azure-cli-extensions/issues/new";
 
 _MSG_INTR = \
     '\nWe appreciate your feedback!\n\n' \
@@ -54,12 +61,12 @@
 BEGIN TEMPLATE
 ===============
 **A browser has been opened to {} to create an issue.**
-**The issue template has been copied to your clipboard. You can also run `az 
feedback --verbose` to emit the output to stdout.**
+**You can also run `az feedback --verbose` to emit the full output to stderr.**
 """
 
 _ISSUES_TEMPLATE = """
 
-### **This is an autogenerated template. Please review and update as needed.**
+### **This is autogenerated. Please review and update as needed.**
 
 ## Describe the bug
 
@@ -70,15 +77,13 @@
 {errors_string}
 
 ## To Reproduce:
-Steps to reproduce the behavior. Note: Command arguments have been redacted.
+Steps to reproduce the behavior. Note that argument values have been redacted, 
as they may contain sensitive information.
 
-- `Fill in additional info here`
-- `Run: {executed_command}`
+- _Put any pre-requisite steps here..._
+- `{executed_command}`
 
 ## Expected Behavior
 
-A clear and concise description of what you expected to happen.
-
 ## Environment Summary
 ```
 {platform}
@@ -88,9 +93,8 @@
 {cli_version}
 ```
 ## Additional Context
-Add any other context about the problem here.
 
-<!-- Please do not remove these markdown comments -->
+<!--Please don't remove this:-->
 {auto_gen_comment}
 
 """
@@ -167,6 +171,12 @@
             success_msg = "SUCCESS" if was_successful else "FAILURE"
         return success_msg
 
+    def failed(self):
+        if not self.command_data_dict:
+            return False
+
+        return not self.command_data_dict.get("success", True)
+
     def get_command_time_str(self):
         if not self.metadata_tup:
             return ""
@@ -268,7 +278,7 @@
             if status_msg.startswith("exit code"):
                 idx = status_msg.index(":")  # raises ValueError
                 exit_code = int(log_record_list[-1].log_msg[idx + 1:].strip())
-                log_data["success"] = True if not exit_code else False
+                log_data["success"] = bool(not exit_code)
         except (IndexError, ValueError):
             logger.debug("Couldn't extract exit code from command log %s.", 
file_name)
 
@@ -328,9 +338,147 @@
                 if parts[0] != p_id:  # ensure that this is indeed a valid log.
                     return None
 
+        # add newline at end of log
+        if not parts[-1].endswith("\n"):
+            parts[-1] += "\n"
+
         return CommandLogFile._LogRecordType(*parts)
 
 
+class ErrorMinifier(object):
+
+    _FILE_RE = re.compile(r'File "(.*)"')
+    _CONTINUATION_STR = "...\n"
+
+    def __init__(self, errors_list):
+        self._errors_list = errors_list
+        self._capacity = None
+        self._minified_error = "\n".join(self._errors_list)
+
+    def set_capacity(self, capacity):
+        logger.debug("Capacity for error string: %s", capacity)
+
+        self._capacity = int(capacity)
+        self._minified_error = self._get_minified_errors()
+
+    def _get_minified_errors(self):  # pylint: 
disable=too-many-return-statements
+        errors_list = self._errors_list
+        errors_string = "\n".join(errors_list)
+        if self._capacity is None:
+            return errors_string
+
+        if not errors_list:
+            return ""
+
+        # if within capacity return string
+        if len(errors_string) <= self._capacity:
+            return errors_string
+
+        # shorten file names and try again
+        for i, error in enumerate(errors_list):
+            errors_list[i] = self._minify_by_shortening_file_names(error, 
levels=5)
+        errors_string = "\n".join(errors_list)
+        if len(errors_string) <= self._capacity:
+            return errors_string
+
+        # shorten file names and try again
+        for i, error in enumerate(errors_list):
+            errors_list[i] = self._minify_by_shortening_file_names(error, 
levels=4)
+        errors_string = "\n".join(errors_list)
+        if len(errors_string) <= self._capacity:
+            return errors_string
+
+        # return first exception if multiple exceptions occurs
+        for i, error in enumerate(errors_list):
+            errors_list[i] = self._minify_by_removing_nested_exceptions(error)
+        errors_string = "\n".join(errors_list)
+        if len(errors_string) <= self._capacity:
+            return errors_string
+
+        # last resort keep removing middle lines
+        while len(errors_string) > self._capacity:
+            errors_string = self._minify_by_removing_lines(errors_string)
+
+        return errors_string
+
+    @staticmethod
+    def _minify_by_shortening_file_names(error_string, levels=5):
+        new_lines = []
+        for line in error_string.splitlines():
+            # if original exception
+            if line.strip().startswith("File") and ", line" in line:
+                parts = line.split(",")
+                match = ErrorMinifier._FILE_RE.search(parts[0])
+                if match:
+                    parts[0] = 
ErrorMinifier._shorten_file_name(match.group(1), levels)
+                    parts[1] = parts[1].replace("line", "ln")
+                line = ",".join(parts)
+            # if cleaned exceptions
+            elif ".py" in line and ", ln" in line:
+                parts = line.split(",")
+                parts[0] = ErrorMinifier._shorten_file_name(parts[0], levels)
+                line = ",".join(parts)
+            # remove this line
+            elif "here is the traceback" in line.lower():
+                continue
+
+            new_lines.append(line)
+
+        return "\n".join(new_lines)
+
+    @staticmethod
+    def _shorten_file_name(file_name, levels=5):
+        if levels > 0:
+            new_name = os.path.basename(file_name)
+            file_name = os.path.dirname(file_name)
+            for _ in range(levels - 1):
+                new_name = os.path.join(os.path.basename(file_name), new_name)
+                file_name = os.path.dirname(file_name)
+            return new_name
+        return file_name
+
+    @staticmethod
+    def _minify_by_removing_nested_exceptions(error_string):
+        lines = error_string.splitlines()
+
+        idx = len(lines) - 1
+        for i, line in enumerate(lines):
+            if "During handling of the above exception" in line:
+                idx = i
+                break
+
+        # if unchanged return error_string
+        if idx == len(lines) - 1:
+            return error_string
+
+        lines = lines[:idx] + [ErrorMinifier._CONTINUATION_STR] + lines[-3:]
+        return "\n".join(lines)
+
+    @staticmethod
+    def _minify_by_removing_lines(error_string):
+        error_string = error_string.replace(ErrorMinifier._CONTINUATION_STR, 
"")
+        lines = error_string.splitlines()
+
+        mid = int(len(lines) / 2) + 1
+        if not (".py" in lines[mid] and ", ln" in lines[mid]):
+            mid -= 1
+
+        new_lines = []
+        for i, line in enumerate(lines):
+            if i == mid:
+                new_lines.append(ErrorMinifier._CONTINUATION_STR.strip())
+            if i in range(mid, mid + 4):
+                continue
+            new_lines.append(line)
+
+        return "\n".join(new_lines)
+
+    def __str__(self):
+        if self._minified_error:
+            return "```\n{}\n```".format(self._minified_error.strip())
+        return ""
+
+
 def _build_issue_info_tup(command_log_file=None):
     def _get_parent_proc_name():
         import psutil
@@ -354,7 +502,7 @@
         format_dict["command_name"] = command_name
 
         if command_log_file.command_data_dict:
-            errors = 
"\n".join(command_log_file.command_data_dict.get("errors", []))
+            errors_list = command_log_file.command_data_dict.get("errors", [])
             executed_command = 
command_log_file.command_data_dict.get("command_args", "")
             extension_name = 
command_log_file.command_data_dict.get("extension_name", "")
             extension_version = 
command_log_file.command_data_dict.get("extension_version", "")
@@ -364,16 +512,7 @@
                 extension_info = "\nExtension Name: {}. Version: 
{}.".format(extension_name, extension_version)
                 is_ext = True
 
-            if errors:
-                num_lines = errors.count("\n")
-                reaction = ":confused:"
-                if num_lines >= 100:
-                    reaction = ":expressionless:"
-                elif num_lines >= 15:
-                    reaction = ":open_mouth:"
-                errors = "\n{}\n\n```{}```".format(reaction, errors)
-
-            format_dict["errors_string"] = errors
+            format_dict["errors_string"] = ErrorMinifier(errors_list)
             format_dict["executed_command"] = "az " + executed_command if 
executed_command else executed_command
             format_dict["command_name"] += extension_info
 
@@ -384,17 +523,64 @@
     format_dict["shell"] = "Shell: {}".format(_get_parent_proc_name())
     format_dict["auto_gen_comment"] = _AUTO_GEN_COMMENT
 
-    issues_url = _EXTENSIONS_ISSUES_URL if is_ext else _CLI_ISSUES_URL
+    pretty_url_name = _EXTENSIONS_ISSUES_URL if is_ext else _CLI_ISSUES_URL
+    # get issue body without minification
+    original_issue_body = _ISSUES_TEMPLATE.format(**format_dict)
+
+    # First try
+    capacity = _MAX_URL_LENGTH  # some browsers support a max of roughly 2000 
characters
+    res = _get_minified_issue_url(command_log_file, format_dict.copy(), 
is_ext, capacity)
+    formatted_issues_url, minified_issue_body = res
+    capacity = capacity - (len(formatted_issues_url) - _MAX_URL_LENGTH)
+
+    # while formatted issue to long, minify to new capacity
+    tries = 0
+    while len(formatted_issues_url) > _MAX_URL_LENGTH and tries < 25:
+        # reduce capacity by difference if formatted_issues_url is too long 
because of url escaping
+        res = _get_minified_issue_url(command_log_file, format_dict.copy(), 
is_ext, capacity)
+        formatted_issues_url, minified_issue_body = res
+        capacity = capacity - (len(formatted_issues_url) - _MAX_URL_LENGTH)
+        tries += 1
+
+    # if something went wrong with minification (i.e. another part of the 
issue is unexpectedly too long)
+    # then truncate the whole issue body and warn the user.
+    if len(formatted_issues_url) > _MAX_URL_LENGTH:
+        formatted_issues_url = formatted_issues_url[:_MAX_URL_LENGTH]
+        logger.warning("Failed to properly minify issue url. "
+                       "Please use 'az feedback --verbose' to get the full 
issue output.")
+
+    logger.debug("Total minified issue length is %s", len(minified_issue_body))
+    logger.debug("Total formatted url length is %s", len(formatted_issues_url))
+
+    return _ISSUES_TEMPLATE_PREFIX.format(pretty_url_name), 
formatted_issues_url, original_issue_body
+
+
+def _get_minified_issue_url(command_log_file, format_dict, is_ext, capacity):
+    # get issue body without errors
+    minified_errors = format_dict["errors_string"]
+    format_dict["errors_string"] = ""
+    no_errors_issue_body = _ISSUES_TEMPLATE.format(**format_dict)
+
+    # get minified issue body
+    format_dict["errors_string"] = minified_errors
+    if hasattr(minified_errors, "set_capacity"):
+        logger.debug("Length of issue body before errors added: %s", 
len(no_errors_issue_body))
+        minified_errors.set_capacity(
+            capacity - len(no_errors_issue_body))  # factor in length of url 
and expansion of url escaped characters
+    minified_issue_body = _ISSUES_TEMPLATE.format(**format_dict)
 
     # prefix formatted url with 'https://' if necessary and supply empty body 
to remove any existing issue template
-    formatted_issues_url = issues_url
+    # aka.ms doesn't work well for long urls / query params
+    formatted_issues_url = _RAW_EXTENSIONS_ISSUES_URL if is_ext else 
_RAW_CLI_ISSUES_URL
     if not formatted_issues_url.startswith("http"):
         formatted_issues_url = "https://"; + formatted_issues_url
-    new_placeholder = urlencode({'body': "The issue has been copied to your 
clipboard. Paste it here!"
-                                         "\nTo print out the issue body 
locally, run `az feedback --verbose`"})
+    query_dict = {'body': minified_issue_body}
+    if command_log_file and command_log_file.failed():
+        query_dict['template'] = 'Bug_report.md'
+    new_placeholder = urlencode(query_dict)
     formatted_issues_url = "{}?{}".format(formatted_issues_url, 
new_placeholder)
 
-    return _ISSUES_TEMPLATE_PREFIX.format(issues_url), 
_ISSUES_TEMPLATE.format(**format_dict), formatted_issues_url
+    return formatted_issues_url, minified_issue_body
 
 
 def _get_az_version_summary():
@@ -411,6 +597,7 @@
     legal_line = -1
     for i, line in enumerate(lines):
         if line.startswith("azure-cli"):
+            line = " ".join(line.split())
             new_lines.append(line)
         if line.lower().startswith("extensions:"):
             ext_line = i
@@ -424,7 +611,12 @@
 
     if 0 < ext_line < legal_line:
         for i in range(ext_line, legal_line):
-            new_lines.append(lines[i])
+            l_lower = lines[i].lower()
+            if "python location" in l_lower or "extensions directory" in 
l_lower:
+                break
+
+            line = " ".join(lines[i].split())
+            new_lines.append(line)
 
     return "\n".join(new_lines)
 
@@ -515,26 +707,27 @@
 
     if ans in ["y", "n"]:
         if ans == "y":
-            prefix, body, url = _build_issue_info_tup()
+            prefix, url, original_issue = _build_issue_info_tup()
         else:
             return False
     else:
         if ans in ["q", "quit"]:
             return False
         if ans == 0:
-            prefix, body, url = _build_issue_info_tup()
+            prefix, url, original_issue = _build_issue_info_tup()
         else:
-            prefix, body, url = _build_issue_info_tup(recent_command_list[ans])
+            prefix, url, original_issue = 
_build_issue_info_tup(recent_command_list[ans])
 
     print(prefix)
 
     # open issues page in browser and copy issue body to clipboard
-    try:
-        pyperclip.copy(body)
-    except pyperclip.PyperclipException as ex:
-        logger.debug(ex)
+    # import pyperclip
+    # try:  # todo: if no longer using clipboard, remove dependency
+    #     pyperclip.copy(original_issue)
+    # except pyperclip.PyperclipException as ex:
+    #     logger.debug(ex)
 
-    logger.info(body)
+    logger.info(original_issue)
     open_page_in_browser(url)
 
     return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-feedback-2.2.0/azure_cli_feedback.egg-info/PKG-INFO 
new/azure-cli-feedback-2.2.1/azure_cli_feedback.egg-info/PKG-INFO
--- old/azure-cli-feedback-2.2.0/azure_cli_feedback.egg-info/PKG-INFO   
2019-04-05 00:47:37.000000000 +0200
+++ new/azure-cli-feedback-2.2.1/azure_cli_feedback.egg-info/PKG-INFO   
2019-04-19 00:16:08.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: azure-cli-feedback
-Version: 2.2.0
+Version: 2.2.1
 Summary: Microsoft Azure Command-Line Tools Feedback Command Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -20,6 +20,10 @@
         Release History
         ===============
         
+        2.2.1
+        +++++
+        * Minor fixes
+        
         2.2.0
         +++++
         * `az feedback` now shows metadata on recently run commands
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-feedback-2.2.0/setup.py 
new/azure-cli-feedback-2.2.1/setup.py
--- old/azure-cli-feedback-2.2.0/setup.py       2019-04-05 00:46:52.000000000 
+0200
+++ new/azure-cli-feedback-2.2.1/setup.py       2019-04-19 00:15:19.000000000 
+0200
@@ -15,7 +15,7 @@
     cmdclass = {}
 
 
-VERSION = "2.2.0"
+VERSION = "2.2.1"
 CLASSIFIERS = [
     'Development Status :: 5 - Production/Stable',
     'Intended Audience :: Developers',


Reply via email to