Ejemplo n.º 1
0
    def diff(self, revisions, include_files, exclude_patterns):
        """Return the generated diff.

        Args:
            revisions (dict):
                A dictionary containing ``base`` and ``tip`` keys.

            include_files (list):
                A list of file paths to include in the diff.

            exclude_patterns (list):
                A list of file paths to exclude from the diff.

        Returns:
            dict:
            A dictionary containing ``diff``, ``parent_diff``, and
            ``base_commit_id`` keys. In the case of TFS, the parent diff key
            will always be ``None``.
        """
        base = str(revisions['base'])
        tip = str(revisions['tip'])

        if tip == self.REVISION_WORKING_COPY:
            return self._diff_working_copy(base, include_files,
                                           exclude_patterns)
        else:
            die('Posting committed changes is not yet supported for TFS.')
Ejemplo n.º 2
0
    def get_repository_info(self):
        if not self.p4.is_supported():
            return None

        p4_info = self.p4.info()

        # For the repository path, we first prefer p4 brokers, then the
        # upstream p4 server. If neither of those are found, just return None.
	repository_path = p4_info.get('Broker address', None)

        if repository_path is None:
            repository_path = p4_info.get('Server address', None)

        if repository_path is None:
            return None

        try:
            parts = repository_path.split(':')
            hostname = None

            if len(parts) == 3 and parts[0] == 'ssl':
                hostname = parts[1]
                port = parts[2]
            elif len(parts) == 2:
                hostname, port = parts

            if not hostname:
                die('Path %s is not a valid Perforce P4PORT' % repository_path)

            info = socket.gethostbyaddr(hostname)

            # If aliases exist for hostname, create a list of alias:port
            # strings for repository_path.
            if info[1]:
                servers = [info[0]] + info[1]
                repository_path = ["%s:%s" % (server, port)
                                   for server in servers]
            else:
                repository_path = "%s:%s" % (info[0], port)
        except (socket.gaierror, socket.herror):
            pass

        server_version = p4_info.get('Server version', None)

        if not server_version:
            return None

        m = re.search(r'[^ ]*/([0-9]+)\.([0-9]+)/[0-9]+ .*$',
                      server_version, re.M)
        if m:
            self.p4d_version = int(m.group(1)), int(m.group(2))
        else:
            # Gracefully bail if we don't get a match
            return None

        # Now that we know it's Perforce, make sure we have GNU diff
        # installed, and error out if we don't.
        check_gnu_diff()

        return RepositoryInfo(path=repository_path, supports_changesets=True)
Ejemplo n.º 3
0
    def credentials_prompt(self,
                           realm,
                           uri,
                           username=None,
                           password=None,
                           *args,
                           **kwargs):
        """Prompt the user for credentials using the command line.

        This will prompt the user, and then return the provided
        username and password. This is used as a callback in the
        API when the user requires authorization.
        """
        if username is None or password is None:
            if getattr(self.options, 'diff_filename', None) == '-':
                die('HTTP authentication is required, but cannot be '
                    'used with --diff-filename=-')

            print()
            print('Please log in to the Review Board server at %s.' %
                  urlparse(uri)[1])

            # getpass will write its prompt to stderr but input
            # writes to stdout. See bug 2831.
            if username is None:
                sys.stderr.write('Username: '******'Password: ')

        return username, password
Ejemplo n.º 4
0
    def _run_p4(self, command):
        """Execute a perforce command using the python marshal API.

        - command: A list of strings of the command to execute.

        The return type depends on the command being run.
        """
        command = ['p4', '-G'] + command
        p = subprocess.Popen(command, stdout=subprocess.PIPE)
        result = []
        has_error = False

        while 1:
            try:
                data = marshal.load(p.stdout)
            except EOFError:
                break
            else:
                result.append(data)
                if data.get('code', None) == 'error':
                    has_error = True

        rc = p.wait()

        if rc or has_error:
            for record in result:
                if 'data' in record:
                    print record['data']
            die('Failed to execute command: %s\n' % (command,))

        return result
Ejemplo n.º 5
0
    def get_repository_info(self):
        if not check_install('cm version'):
            return None

        # Get the repository that the current directory is from.  If there
        # is more than one repository mounted in the current directory,
        # bail out for now (in future, should probably enter a review
        # request per each repository.)
        split = execute(["cm", "ls", "--format={8}"],
                        split_lines=True,
                        ignore_errors=True)
        m = re.search(r'^rep:(.+)$', split[0], re.M)

        if not m:
            return None

        # Make sure the repository list contains only one unique entry
        if len(split) != split.count(split[0]):
            # Not unique!
            die('Directory contains more than one mounted repository')

        path = m.group(1)

        # Get the workspace directory, so we can strip it from the diff output
        self.workspacedir = execute(["cm", "gwp", ".", "--format={1}"],
                                    split_lines=False,
                                    ignore_errors=True).strip()

        logging.debug("Workspace is %s" % self.workspacedir)

        return RepositoryInfo(path,
                              supports_changesets=True,
                              supports_parent_diffs=False)
Ejemplo n.º 6
0
    def diff(self, revisions, include_files, exclude_patterns):
        """Return the generated diff.

        Args:
            revisions (dict):
                A dictionary containing ``base`` and ``tip`` keys.

            include_files (list):
                A list of file paths to include in the diff.

            exclude_patterns (list):
                A list of file paths to exclude from the diff.

        Returns:
            dict:
            A dictionary containing ``diff``, ``parent_diff``, and
            ``base_commit_id`` keys. In the case of TFS, the parent diff key
            will always be ``None``.
        """
        base = str(revisions['base'])
        tip = str(revisions['tip'])

        if tip == self.REVISION_WORKING_COPY:
            return self._diff_working_copy(base, include_files,
                                           exclude_patterns)
        else:
            die('Posting committed changes is not yet supported for TFS.')
Ejemplo n.º 7
0
    def main(self, *args):
        """Create and update review requests."""
        self.post_process_options()
        origcwd = os.path.abspath(os.getcwd())
        repository_info, tool = self.initialize_scm_tool()
        server_url = self.get_server_url(repository_info, tool)
        api_root = self.get_root(server_url)
        self.setup_tool(tool, api_root=api_root)

        if self.options.revision_range:
            diff, parent_diff = tool.diff_between_revisions(
                self.options.revision_range,
                args,
                repository_info)
        elif self.options.svn_changelist:
            diff, parent_diff = tool.diff_changelist(
                self.options.svn_changelist)
        elif self.options.diff_filename:
            parent_diff = None

            if self.options.diff_filename == '-':
                diff = sys.stdin.read()
            else:
                try:
                    diff_path = os.path.join(origcwd,
                                             self.options.diff_filename)
                    fp = open(diff_path, 'r')
                    diff = fp.read()
                    fp.close()
                except IOError, e:
                    die("Unable to open diff filename: %s" % e)
Ejemplo n.º 8
0
    def otp_token_prompt(self, uri, token_method, *args, **kwargs):
        """Prompt the user for a one-time password token.

        Their account is configured with two-factor authentication. The
        server will have sent a token to their configured mobile device
        or application. The user will be prompted for this token.
        """
        if getattr(self.options, "diff_filename", None) == "-":
            die("A two-factor authentication token is required, but cannot " "be used with --diff-filename=-")

        print
        print "==> Two-factor authentication token required"

        if token_method == "sms":
            print ("You should be getting a text message with " "an authentication token.")
            print "Enter the token below."
        elif token_method == "call":
            print ("You should be getting an automated phone call with " "an authentication token.")
            print "Enter the token below."
        elif token_method == "generator":
            print "Enter the token shown on your token generator app below."

        print

        return getpass.getpass("Token: ")
Ejemplo n.º 9
0
    def post_request(self,
                     tool,
                     repository_info,
                     server_url,
                     api_root,
                     changenum=None,
                     diff_content=None,
                     parent_diff_content=None,
                     submit_as=None,
                     retries=3):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        if self.options.rid:
            # Retrieve the review request coresponding to the user
            # provided id.
            try:
                review_request = api_root.get_review_request(
                    review_request_id=self.options.rid)
            except APIError, e:
                die("Error getting review request %s: %s" %
                    (self.options.rid, e))

            if review_request.status == 'submitted':
                die("Review request %s is marked as %s. In order to "
                    "update it, please reopen the request and try again." %
                    (self.options.rid, review_request.status))
Ejemplo n.º 10
0
    def main(self, *args):
        """Create and update review requests."""
        self.post_process_options()
        origcwd = os.path.abspath(os.getcwd())
        repository_info, tool = self.initialize_scm_tool()
        server_url = self.get_server_url(repository_info, tool)
        api_root = self.get_root(server_url)
        self.setup_tool(tool, api_root=api_root)

        if self.options.revision_range:
            diff, parent_diff = tool.diff_between_revisions(
                self.options.revision_range, args, repository_info)
        elif self.options.svn_changelist:
            diff, parent_diff = tool.diff_changelist(
                self.options.svn_changelist)
        elif self.options.diff_filename:
            parent_diff = None

            if self.options.diff_filename == '-':
                diff = sys.stdin.read()
            else:
                try:
                    diff_path = os.path.join(origcwd,
                                             self.options.diff_filename)
                    fp = open(diff_path, 'r')
                    diff = fp.read()
                    fp.close()
                except IOError, e:
                    die("Unable to open diff filename: %s" % e)
Ejemplo n.º 11
0
    def credentials_prompt(self, realm, uri, username=None, password=None,
                           *args, **kwargs):
        """Prompt the user for credentials using the command line.

        This will prompt the user, and then return the provided
        username and password. This is used as a callback in the
        API when the user requires authorization.
        """
        if username is None or password is None:
            if getattr(self.options, 'diff_filename', None) == '-':
                die('HTTP authentication is required, but cannot be '
                    'used with --diff-filename=-')

            print()
            print('Please log in to the Review Board server at %s.' %
                  urlparse(uri)[1])

            # getpass will write its prompt to stderr but input
            # writes to stdout. See bug 2831.
            if username is None:
                sys.stderr.write('Username: '******'Password: ')

        return username, password
Ejemplo n.º 12
0
    def otp_token_prompt(self, uri, token_method, *args, **kwargs):
        """Prompt the user for a one-time password token.

        Their account is configured with two-factor authentication. The
        server will have sent a token to their configured mobile device
        or application. The user will be prompted for this token.
        """
        if getattr(self.options, 'diff_filename', None) == '-':
            die('A two-factor authentication token is required, but cannot '
                'be used with --diff-filename=-')

        print()
        print('Please enter your two-factor authentication token for Review '
              'Board.')

        if token_method == 'sms':
            print('You should be getting a text message with '
                  'an authentication token.')
            print('Enter the token below.')
        elif token_method == 'call':
            print('You should be getting an automated phone call with '
                  'an authentication token.')
            print('Enter the token below.')
        elif token_method == 'generator':
            print('Enter the token shown on your token generator app below.')

        print()

        return getpass.getpass(b'Token: ')
Ejemplo n.º 13
0
 def _die(self, msg, e):
     """Remove the connection to the database and print an error message."""
     self.db = None  # So that _write_db doesn't cause an exception
     logging.error('%s: %s. Try running "rbt clear-cache" to manually '
                   'clear the HTTP cache for the API.',
                   msg, e)
     die()
Ejemplo n.º 14
0
def check_gnu_diff():
    """Checks if GNU diff is installed, and informs the user if it's not."""
    has_gnu_diff = False

    try:
        if hasattr(os, 'uname') and os.uname()[0] == 'SunOS':
            diff_cmd = 'gdiff'
        else:
            diff_cmd = 'diff'

        result = execute([diff_cmd, '--version'], ignore_errors=True)
        has_gnu_diff = 'GNU diffutils' in result
    except OSError:
        pass

    if not has_gnu_diff:
        sys.stderr.write('\n')
        sys.stderr.write('GNU diff is required in order to generate diffs. '
                         'Make sure it is installed\n')
        sys.stderr.write('and in the path.\n')
        sys.stderr.write('\n')

        if os.name == 'nt':
            sys.stderr.write('On Windows, you can install this from:\n')
            sys.stderr.write(GNU_DIFF_WIN32_URL)
            sys.stderr.write('\n')

        die()
Ejemplo n.º 15
0
    def otp_token_prompt(self, uri, token_method, *args, **kwargs):
        """Prompt the user for a one-time password token.

        Their account is configured with two-factor authentication. The
        server will have sent a token to their configured mobile device
        or application. The user will be prompted for this token.
        """
        if getattr(self.options, 'diff_filename', None) == '-':
            die('A two-factor authentication token is required, but cannot '
                'be used with --diff-filename=-')

        print()
        print('Please enter your two-factor authentication token for Review '
              'Board.')

        if token_method == 'sms':
            print('You should be getting a text message with '
                  'an authentication token.')
            print('Enter the token below.')
        elif token_method == 'call':
            print('You should be getting an automated phone call with '
                  'an authentication token.')
            print('Enter the token below.')
        elif token_method == 'generator':
            print('Enter the token shown on your token generator app below.')

        print()

        return getpass.getpass(b'Token: ')
Ejemplo n.º 16
0
    def _run_p4(self, command):
        """Execute a perforce command using the python marshal API.

        - command: A list of strings of the command to execute.

        The return type depends on the command being run.
        """
        command = ['p4', '-G'] + command
        p = subprocess.Popen(command, stdout=subprocess.PIPE)
        result = []
        has_error = False

        while 1:
            try:
                data = marshal.load(p.stdout)
            except EOFError:
                break
            else:
                result.append(data)
                if data.get('code', None) == 'error':
                    has_error = True

        rc = p.wait()

        if rc or has_error:
            for record in result:
                if 'data' in record:
                    print record['data']
            die('Failed to execute command: %s\n' % (command, ))

        return result
Ejemplo n.º 17
0
def main():
    origcwd = os.path.abspath(os.getcwd())

    if 'APPDATA' in os.environ:
        homepath = os.environ['APPDATA']
    elif 'HOME' in os.environ:
        homepath = os.environ["HOME"]
    else:
        homepath = ''

    # If we end up creating a cookie file, make sure it's only readable by the
    # user.
    os.umask(0077)

    # Load the config and cookie files
    cookie_file = os.path.join(homepath, ".post-review-cookies.txt")
    user_config, globals()['configs'] = load_config_files(homepath)

    args = parse_options(sys.argv[1:])

    debug('RBTools %s' % get_version_string())
    debug('Python %s' % sys.version)
    debug('Running on %s' % (platform.platform()))
    debug('Home = %s' % homepath)
    debug('Current Directory = %s' % os.getcwd())

    debug(
        'Checking the repository type. Errors shown below are mostly harmless.'
    )
    repository_info, tool = scan_usable_client(options)
    debug('Finished checking the repository type.')

    tool.user_config = user_config
    tool.configs = configs

    # Verify that options specific to an SCM Client have not been mis-used.
    tool.check_options()

    if repository_info.supports_changesets:
        changenum = tool.get_changenum(args)
    else:
        changenum = None

    if options.revision_range:
        diff, parent_diff = tool.diff_between_revisions(
            options.revision_range, args, repository_info)
    elif options.svn_changelist:
        diff, parent_diff = tool.diff_changelist(options.svn_changelist)
    elif options.diff_filename:
        parent_diff = None

        if options.diff_filename == '-':
            diff = sys.stdin.read()
        else:
            try:
                fp = open(os.path.join(origcwd, options.diff_filename), 'r')
                diff = fp.read()
                fp.close()
            except IOError, e:
                die("Unable to open diff filename: %s" % e)
Ejemplo n.º 18
0
def tempt_fate(server, submit_as=None, retries=3):
    """
    Attempts to create a review request on a Review Board server and upload
    a diff. On success, the review request path is displayed.
    """
    try:
        if options.rid:
            # review_request = server.get_ship_it_reviewers(options.rid)
            review_request = server.get_comment_user(options.rid)
        else:
            die("Please give a review id")

    except APIError, e:
        if e.error_code == 103:  # Not logged in
            retries = retries - 1

            # We had an odd issue where the server ended up a couple of
            # years in the future. Login succeeds but the cookie date was
            # "odd" so use of the cookie appeared to fail and eventually
            # ended up at max recursion depth :-(. Check for a maximum
            # number of retries.
            if retries >= 0:
                server.login(force=True)
                return tempt_fate(server, submit_as, retries=retries)

        if options.rid:
            die("Error getting review request %s: %s" % (options.rid, e))
Ejemplo n.º 19
0
 def get_review_request(self, request_id):
     """Return the review request resource for the given ID."""
     try:
         request = \
             self.root_resource.get_review_requests().get_item(request_id)
     except APIError, e:
         die("Error getting review request: %s" % e)
Ejemplo n.º 20
0
    def check_api_version(self):
        """Checks the API version on the server to determine which to use."""
        try:
            root_resource = self.api_get('api/')
            rsp = self.api_get(root_resource['links']['info']['href'])

            self.rb_version = rsp['info']['product']['package_version']

            if parse_version(self.rb_version) >= parse_version('1.5.2'):
                self.deprecated_api = False
                self.root_resource = root_resource
                debug('Using the new web API')
                return True
        except APIError, e:
            if e.http_status not in (401, 404):
                # We shouldn't reach this. If there's a permission denied
                # from lack of logging in, then the basic auth handler
                # should have hit it.
                #
                # However in some versions it wants you to be logged in
                # and returns a 401 from the application after you've
                # done your http basic auth
                die("Unable to access the root /api/ URL on the server.")

                return False
Ejemplo n.º 21
0
 def diff_between_revisions(self, revision_range, args, repository_info):
     """
     This doesn't make much sense in Plastic SCM 4.
     We'll only implement revisions for changests and branches
     """
     die("This option is not supported. Only reviews of a changeset or "
         "branch are supported")
Ejemplo n.º 22
0
def tempt_fate(server, submit_as=None, retries=3):
    """
    Attempts to create a review request on a Review Board server and upload
    a diff. On success, the review request path is displayed.
    """
    try:
        if options.rid:
            # review_request = server.get_ship_it_reviewers(options.rid)
            review_request = server.get_comment_user(options.rid)
        else:
            die("Please give a review id")

    except APIError, e:
        if e.error_code == 103:  # Not logged in
            retries = retries - 1

            # We had an odd issue where the server ended up a couple of
            # years in the future. Login succeeds but the cookie date was
            # "odd" so use of the cookie appeared to fail and eventually
            # ended up at max recursion depth :-(. Check for a maximum
            # number of retries.
            if retries >= 0:
                server.login(force=True)
                return tempt_fate(server, submit_as, retries=retries)

        if options.rid:
            die("Error getting review request %s: %s" % (options.rid, e))
Ejemplo n.º 23
0
    def get_repository_info(self):
        if not check_install('cm version'):
            return None

        # Get the repository that the current directory is from.  If there
        # is more than one repository mounted in the current directory,
        # bail out for now (in future, should probably enter a review
        # request per each repository.)
        split = execute(["cm", "ls", "--format={8}"], split_lines=True,
                        ignore_errors=True)
        m = re.search(r'^rep:(.+)$', split[0], re.M)

        if not m:
            return None

        # Make sure the repository list contains only one unique entry
        if len(split) != split.count(split[0]):
            # Not unique!
            die('Directory contains more than one mounted repository')

        path = m.group(1)

        # Get the workspace directory, so we can strip it from the diff output
        self.workspacedir = execute(["cm", "gwp", ".", "--format={1}"],
                                    split_lines=False,
                                    ignore_errors=True).strip()

        logging.debug("Workspace is %s" % self.workspacedir)

        return RepositoryInfo(path,
                              supports_changesets=True,
                              supports_parent_diffs=False)
Ejemplo n.º 24
0
def check_gnu_diff():
    """Checks if GNU diff is installed, and informs the user if it's not."""
    has_gnu_diff = False

    try:
        if hasattr(os, 'uname') and os.uname()[0] == 'SunOS':
            diff_cmd = 'gdiff'
        else:
            diff_cmd = 'diff'

        result = execute([diff_cmd, '--version'], ignore_errors=True)
        has_gnu_diff = 'GNU diffutils' in result
    except OSError:
        pass

    if not has_gnu_diff:
        sys.stderr.write('\n')
        sys.stderr.write('GNU diff is required in order to generate diffs. '
                         'Make sure it is installed\n')
        sys.stderr.write('and in the path.\n')
        sys.stderr.write('\n')

        if os.name == 'nt':
            sys.stderr.write('On Windows, you can install this from:\n')
            sys.stderr.write(GNU_DIFF_WIN32_URL)
            sys.stderr.write('\n')

        die()
Ejemplo n.º 25
0
 def diff_between_revisions(self, revision_range, args, repository_info):
     """
     This doesn't make much sense in Plastic SCM 4.
     We'll only implement revisions for changests and branches
     """
     die("This option is not supported. Only reviews of a changeset or "
         "branch are supported")
Ejemplo n.º 26
0
    def credentials_prompt(self,
                           realm,
                           uri,
                           username=None,
                           password=None,
                           *args,
                           **kwargs):
        """Prompt the user for credentials using the command line.

        This will prompt the user, and then return the provided
        username and password. This is used as a callback in the
        API when the user requires authorization.
        """
        if getattr(self.options, 'diff_filename', None) == '-':
            die('HTTP authentication is required, but cannot be '
                'used with --diff-filename=-')

        if username is None or password is None:
            print
            print "==> HTTP Authentication Required"
            print 'Enter authorization information for "%s" at %s' % \
                (realm, urlparse(uri)[1])

            # getpass will write its prompt to stderr but raw_input
            # writes to stdout. See bug 2831.
            if username is None:
                sys.stderr.write('Username: '******'Password: ')

        return username, password
Ejemplo n.º 27
0
 def _die(self, msg, e):
     """Remove the connection to the database and print an error message."""
     self.db = None  # So that _write_db doesn't cause an exception
     logging.error(
         "%s: %s. Try running 'rbt clear-cache' to manually "
         "clear the HTTP cache for the API.", msg, e)
     die()
Ejemplo n.º 28
0
    def credentials_prompt(self, realm, uri, username=None, password=None, *args, **kwargs):
        """Prompt the user for credentials using the command line.

        This will prompt the user, and then return the provided
        username and password. This is used as a callback in the
        API when the user requires authorization.
        """
        if getattr(self.options, "diff_filename", None) == "-":
            die("HTTP authentication is required, but cannot be " "used with --diff-filename=-")

        if username is None or password is None:
            print
            print "==> HTTP Authentication Required"
            print 'Enter authorization information for "%s" at %s' % (realm, urlparse(uri)[1])

            # getpass will write its prompt to stderr but raw_input
            # writes to stdout. See bug 2831.
            if username is None:
                sys.stderr.write("Username: "******"Password: ")

        return username, password
Ejemplo n.º 29
0
def check_gnu_patch():
    """Checks if GNU patch is installed, and informs the user if it's not."""
    has_gnu_patch = False

    try:
        result = execute(['patch', '--version'], ignore_errors=True)
        has_gnu_patch = 'Free Software Foundation' in result
    except OSError:
        pass

    try:
        # SunOS has gpatch
        result = execute(['gpatch', '--version'], ignore_errors=True)
        has_gnu_patch = 'Free Software Foundation' in result
    except OSError:
        pass

    if not has_gnu_patch:
        sys.stderr.write('\n')
        sys.stderr.write('GNU patch is required to post this review.'
                         'Make sure it is installed and in the path.\n')
        sys.stderr.write('\n')

        if os.name == 'nt':
            sys.stderr.write('On Windows, you can install this from:\n')
            sys.stderr.write(GNU_PATCH_WIN32_URL)
            sys.stderr.write('\n')

        die()
Ejemplo n.º 30
0
def main():
    origcwd = os.path.abspath(os.getcwd())

    if 'APPDATA' in os.environ:
        homepath = os.environ['APPDATA']
    elif 'HOME' in os.environ:
        #homepath = os.environ["HOME"]
        homepath = '/usr/local/bin/perforce'
    else:
        homepath = ''

    # If we end up creating a cookie file, make sure it's only readable by the
    # user.
    os.umask(0077)

    # Load the config and cookie files
    cookie_file = os.path.join(homepath, ".post-review-cookies.txt")
    user_config, globals()['configs'] = load_config_files(homepath)

    args = parse_options(sys.argv[1:])

    debug('RBTools %s' % get_version_string())
    debug('Python %s' % sys.version)
    debug('Running on %s' % (platform.platform()))
    debug('Home = %s' % homepath)
    debug('Current Directory = %s' % os.getcwd())

    debug('Checking the repository type. Errors shown below are mostly harmless.')
    repository_info, tool = scan_usable_client(options)
    debug('Finished checking the repository type.')

    tool.user_config = user_config
    tool.configs = configs

    # Verify that options specific to an SCM Client have not been mis-used.
    tool.check_options()

    if repository_info.supports_changesets:
        changenum = tool.get_changenum(args)
    else:
        changenum = None

    if options.revision_range:
        diff, parent_diff = tool.diff_between_revisions(options.revision_range, args,
                                                        repository_info)
    elif options.svn_changelist:
        diff, parent_diff = tool.diff_changelist(options.svn_changelist)
    elif options.diff_filename:
        parent_diff = None

        if options.diff_filename == '-':
            diff = sys.stdin.read()
        else:
            try:
                fp = open(os.path.join(origcwd, options.diff_filename), 'r')
                diff = fp.read()
                fp.close()
            except IOError, e:
                die("Unable to open diff filename: %s" % e)
Ejemplo n.º 31
0
def diff_list_resource(server):

    if options.rid:
        url = 'api/review-requests/%s/diffs/' % (options.rid)

        return server.api_get(url)
    else:
        die('You must provide a review id (-r #id) to get review comments')
Ejemplo n.º 32
0
def diff_list_resource(server):

    if options.rid:
        url = 'api/review-requests/%s/diffs/' % (options.rid)

        return server.api_get(url)
    else:
        die('You must provide a review id (-r #id) to get review comments')
Ejemplo n.º 33
0
    def main(self, *args):
        """Print the diff to terminal."""
        diff = self.get_diff(*args)

        if not diff:
            die("There don't seem to be any diffs!")

        print diff
Ejemplo n.º 34
0
    def main(self, *args):
        """Print the diff to terminal."""
        diff = self.get_diff(*args)

        if not diff:
            die("There don't seem to be any diffs!")

        print diff
Ejemplo n.º 35
0
    def check_valid_type(self, close_type):
        """Check if the user specificed a proper type.

        Type must either be 'discarded' or 'submitted'. If the type
        is wrong, the command will stop and alert the user.
        """
        if close_type not in (SUBMITTED, DISCARDED):
            die("%s is not valid type. Try '%s' or '%s'" %
                (self.options.close_type, SUBMITTED, DISCARDED))
Ejemplo n.º 36
0
    def check_valid_type(self, close_type):
        """Check if the user specificed a proper type.

        Type must either be 'discarded' or 'submitted'. If the type
        is wrong, the command will stop and alert the user.
        """
        if close_type not in (SUBMITTED, DISCARDED):
            die("%s is not valid type. Try '%s' or '%s'" %
                (self.options.close_type, SUBMITTED, DISCARDED))
Ejemplo n.º 37
0
    def get_review_request(self, request_id):
        """Returns the review request resource for the given ID."""
        try:
            request = \
                self.root_resource.get_review_requests().get_item(request_id)
        except APIError:
            die('The specified review request does not exist.')

        return request
Ejemplo n.º 38
0
    def get_review_request(self, request_id):
        """Returns the review request resource for the given ID."""
        try:
            request = \
                self.root_resource.get_review_requests().get_item(request_id)
        except APIError:
            die('The specified review request does not exist.')

        return request
Ejemplo n.º 39
0
    def apply_patch(self,
                    patch_file,
                    base_path,
                    base_dir,
                    p=None,
                    revert=False):
        """Apply the patch and return a PatchResult indicating its success."""
        # Figure out the -p argument for patch. We override the calculated
        # value if it is supplied via a commandline option.
        p_num = p or self._get_p_number(base_path, base_dir)

        cmd = ['patch']

        if revert:
            cmd.append('-R')

        if p_num >= 0:
            cmd.append('-p%d' % p_num)

        cmd.extend(['-i', six.text_type(patch_file)])

        # Ignore return code 2 in case the patch file consists of only empty
        # files, which 'patch' can't handle. Other 'patch' errors also give
        # return code 2, so we must check the command output.
        rc, patch_output = execute(cmd,
                                   extra_ignore_errors=(2, ),
                                   return_error_code=True)
        only_garbage_in_patch = ('patch: **** Only garbage was found in the '
                                 'patch input.\n')

        if (patch_output and patch_output.startswith('patch: **** ')
                and patch_output != only_garbage_in_patch):
            die('Failed to execute command: %s\n%s' % (cmd, patch_output))

        # Check the patch for any added/deleted empty files to handle.
        if self.supports_empty_files():
            try:
                with open(patch_file, 'rb') as f:
                    patch = f.read()
            except IOError as e:
                logging.error('Unable to read file %s: %s', patch_file, e)
                return

            patched_empty_files = self.apply_patch_for_empty_files(
                patch, p_num, revert=revert)

            # If there are no empty files in a "garbage-only" patch, the patch
            # is probably malformed.
            if (patch_output == only_garbage_in_patch
                    and not patched_empty_files):
                die('Failed to execute command: %s\n%s' % (cmd, patch_output))

        # TODO: Should this take into account apply_patch_for_empty_files ?
        #       The return value of that function is False both when it fails
        #       and when there are no empty files.
        return PatchResult(applied=(rc == 0), patch_output=patch_output)
Ejemplo n.º 40
0
    def get_repository_info(self):
        if not self.p4.is_supported():
            return None

        p4_info = self.p4.info()

        repository_path = p4_info.get('Server address', None)

        if repository_path is None:
            return None

        try:
            parts = repository_path.split(':')
            hostname = None

            if len(parts) == 3 and parts[0] == 'ssl':
                hostname = parts[1]
                port = parts[2]
            elif len(parts) == 2:
                hostname, port = parts

            if not hostname:
                die('Path %s is not a valid Perforce P4PORT' % repository_path)

            info = socket.gethostbyaddr(hostname)

            # If aliases exist for hostname, create a list of alias:port
            # strings for repository_path.
            if info[1]:
                servers = [info[0]] + info[1]
                repository_path = [
                    "%s:%s" % (server, port) for server in servers
                ]
            else:
                repository_path = "%s:%s" % (info[0], port)
        except (socket.gaierror, socket.herror):
            pass

        server_version = p4_info.get('Server version', None)

        if not server_version:
            return None

        m = re.search(r'[^ ]*/([0-9]+)\.([0-9]+)/[0-9]+ .*$', server_version,
                      re.M)
        if m:
            self.p4d_version = int(m.group(1)), int(m.group(2))
        else:
            # Gracefully bail if we don't get a match
            return None

        # Now that we know it's Perforce, make sure we have GNU diff
        # installed, and error out if we don't.
        check_gnu_diff()

        return RepositoryInfo(path=repository_path, supports_changesets=True)
Ejemplo n.º 41
0
    def get_repository_info(self):
        """Returns information on the Clear Case repository.

        This will first check if the cleartool command is
        installed and in the path, and post-review was run
        from inside of the view.
        """
        if not check_install('cleartool help'):
            return None

        viewname = execute(["cleartool", "pwv", "-short"]).strip()
        if viewname.startswith('** NONE'):
            return None

        # Now that we know it's ClearCase, make sure we have GNU diff installed,
        # and error out if we don't.
        check_gnu_diff()

        property_lines = execute(["cleartool", "lsview", "-full", "-properties",
                                  "-cview"], split_lines=True)
        for line in property_lines:
            properties = line.split(' ')
            if properties[0] == 'Properties:':
                # Determine the view type and check if it's supported.
                #
                # Specifically check if webview was listed in properties
                # because webview types also list the 'snapshot'
                # entry in properties.
                if 'webview' in properties:
                    die("Webviews are not supported. You can use post-review"
                        " only in dynamic or snapshot view.")
                if 'dynamic' in properties:
                    self.viewtype = 'dynamic'
                else:
                    self.viewtype = 'snapshot'

                break

        # Find current VOB's tag
        vobstag = execute(["cleartool", "describe", "-short", "vob:."],
                            ignore_errors=True).strip()
        if "Error: " in vobstag:
            die("To generate diff run post-review inside vob.")

        # From current working directory cut path to VOB.
        # VOB's tag contain backslash character before VOB's name.
        # I hope that first character of VOB's tag like '\new_proj'
        # won't be treat as new line character but two separate:
        # backslash and letter 'n'
        cwd = os.getcwd()
        base_path = cwd[:cwd.find(vobstag) + len(vobstag)]

        return ClearCaseRepositoryInfo(path=base_path,
                              base_path=base_path,
                              vobstag=vobstag,
                              supports_parent_diffs=False)
Ejemplo n.º 42
0
def get_raw_diffs(server):
    if options.rid:

        url = '%sr/%s/diff/raw/' % (server.url, options.rid)

        print url
        print server.http_get(url)

    else:
        die('You must provide a review id (-r #id) to get review comments')
Ejemplo n.º 43
0
def get_raw_diffs(server):
    if options.rid:

        url = '%sr/%s/diff/raw/' % (server.url, options.rid)

        print url
        print server.http_get(url)

    else:
        die('You must provide a review id (-r #id) to get review comments')
Ejemplo n.º 44
0
    def run_p4(self,
               p4_args,
               marshalled=False,
               password=None,
               ignore_errors=False,
               *args,
               **kwargs):
        cmd = ['p4']

        if marshalled:
            cmd += ['-G']

        if getattr(self.options, 'p4_client', None):
            cmd += ['-c', self.options.p4_client]

        if getattr(self.options, 'p4_port', None):
            cmd += ['-p', self.options.p4_port]

        if getattr(self.options, 'p4_passwd', None):
            cmd += ['-P', self.options.p4_passwd]

        cmd += p4_args

        if password is not None:
            cmd += ['-P', password]

        if marshalled:
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
            result = []
            has_error = False

            while 1:
                try:
                    data = marshal.load(p.stdout)
                except EOFError:
                    break
                else:
                    result.append(data)
                    if data.get('code', None) == 'error':
                        has_error = True

            rc = p.wait()

            if not ignore_errors and (rc or has_error):
                for record in result:
                    if 'data' in record:
                        print record['data']
                die('Failed to execute command: %s\n' % (cmd, ))

            return result
        else:
            result = execute(cmd, ignore_errors=ignore_errors, *args, **kwargs)

        return result
Ejemplo n.º 45
0
 def _write_db(self):
     """Flush the contents of the DB to the disk."""
     if self.db:
         try:
             self.db.commit()
         except sqlite3.Error as e:
             logging.error('Could not write database to disk: %s. Try '
                           'running "rbt clear-cache" to manually clear '
                           'the HTTP cache for the API.',
                           e)
             die()
Ejemplo n.º 46
0
    def change_status(self, review_request, status):
        """
        Change the status of the review request
        """

        if status not in ['discarded', 'pending', 'submitted']:
            die("Can't change status of review in %s" % status)

        self.api_put(review_request['links']['self']['href'], {
            'status': status,
        })
Ejemplo n.º 47
0
 def _write_db(self):
     """Flush the contents of the DB to the disk."""
     if self.db:
         try:
             self.db.commit()
         except sqlite3.Error as e:
             logging.error(
                 "Could not write database to disk: %s. Try "
                 "running 'rbt clear-cache' to manually clear "
                 "the HTTP cache for the API.", e)
             die()
Ejemplo n.º 48
0
    def change_status(self, review_request, status):
        """
        Change the status of the review request
        """

        if status not in ['discarded', 'pending', 'submitted']:
            die("Can't change status of review in %s" % status)

        self.api_put(review_request['links']['self']['href'], {
            'status': status,
        })
Ejemplo n.º 49
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool()
        server_url = self.get_server_url(repository_info, tool)
        self.root_resource = self.get_root(server_url)

        request = self.get_review_request(request_id)
        try:
            draft = request.get_draft()
            draft = draft.update(public=True)
        except APIError, e:
            die("Error publishing review request (it may already be"
                "publish): %s" % e)
Ejemplo n.º 50
0
    def apply_patch(self, patch_file, base_path, base_dir, p=None,
                    revert=False):
        """Apply the patch and return a PatchResult indicating its success."""
        # Figure out the -p argument for patch. We override the calculated
        # value if it is supplied via a commandline option.
        p_num = p or self._get_p_number(base_path, base_dir)

        cmd = ['patch']

        if revert:
            cmd.append('-R')

        if p_num >= 0:
            cmd.append('-p%d' % p_num)

        cmd.extend(['-i', six.text_type(patch_file)])

        # Ignore return code 2 in case the patch file consists of only empty
        # files, which 'patch' can't handle. Other 'patch' errors also give
        # return code 2, so we must check the command output.
        rc, patch_output = execute(cmd, extra_ignore_errors=(2,),
                                   return_error_code=True)
        only_garbage_in_patch = ('patch: **** Only garbage was found in the '
                                 'patch input.\n')

        if (patch_output and patch_output.startswith('patch: **** ') and
            patch_output != only_garbage_in_patch):
            die('Failed to execute command: %s\n%s' % (cmd, patch_output))

        # Check the patch for any added/deleted empty files to handle.
        if self.supports_empty_files():
            try:
                with open(patch_file, 'rb') as f:
                    patch = f.read()
            except IOError as e:
                logging.error('Unable to read file %s: %s', patch_file, e)
                return

            patched_empty_files = self.apply_patch_for_empty_files(
                patch, p_num, revert=revert)

            # If there are no empty files in a "garbage-only" patch, the patch
            # is probably malformed.
            if (patch_output == only_garbage_in_patch and
                not patched_empty_files):
                die('Failed to execute command: %s\n%s' % (cmd, patch_output))

        # TODO: Should this take into account apply_patch_for_empty_files ?
        #       The return value of that function is False both when it fails
        #       and when there are no empty files.
        return PatchResult(applied=(rc == 0), patch_output=patch_output)
Ejemplo n.º 51
0
def parse_config_file(filename):
    """Parse a .reviewboardrc file.

    Returns a dictionary containing the configuration from the file.

    The ``filename`` argument should contain a full path to a
    .reviewboardrc file.
    """
    config = {}
    try:
        execfile(filename, config)
    except SyntaxError, e:
        die('Syntax error in config file: %s\n'
            'Line %i offset %i\n' % (filename, e.lineno, e.offset))
Ejemplo n.º 52
0
def main():
    origcwd = os.path.abspath(os.getcwd())

    if 'APPDATA' in os.environ:
        homepath = os.environ['APPDATA']
    elif 'HOME' in os.environ:
        homepath = os.environ["HOME"]
    else:
        homepath = ''

    # If we end up creating a cookie file, make sure it's only readable by the
    # user.
    os.umask(0077)

    # Load the config and cookie files
    cookie_file = os.path.join(homepath, ".post-review-cookies.txt")
    user_config, globals()['configs'] = load_config_files(homepath)

    args = parse_options(sys.argv[1:])

    debug('RBTools %s' % get_version_string())
    debug('Home = %s' % homepath)

    repository_info, tool = scan_usable_client(options)
    tool.user_config = user_config
    tool.configs = configs

    # Verify that options specific to an SCM Client have not been mis-used.
    tool.check_options()

    # Try to find a valid Review Board server to use.
    if options.server:
        server_url = options.server
    else:
        server_url = tool.scan_for_server(repository_info)

    if not server_url:
        print "Unable to find a Review Board server for this source code tree."
        sys.exit(1)

    server = ReviewBoardServer(server_url, repository_info, cookie_file)

    # Handle the case where /api/ requires authorization (RBCommons).
    if not server.check_api_version():
        die("Unable to log in with the supplied username and password.")

    # Let's begin.
    server.login()

    tempt_fate(server)
Ejemplo n.º 53
0
    def get_root(self, server_url):
        """Returns the root resource of an RBClient."""
        cookie_file = self.get_cookie()

        self.rb_api = RBClient(server_url,
                               cookie_file=cookie_file,
                               username=self.options.username,
                               password=self.options.password,
                               auth_callback=self.credentials_prompt)
        root = None
        try:
            root = self.rb_api.get_root()
        except ServerInterfaceError, e:
            die("Could not reach the review board server at %s" % server_url)