Exemple #1
0
  def merge(self, src, op_cb=None):
    """Merges the divergent changes of the src branch onto this one."""
    self._check_is_current()
    self._check_op_not_in_progress()

    result, unused_ff_conf = self.gl_repo.git_repo.merge_analysis(src.target)
    if result & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE:
      raise GlError('No commits to merge')
    try:
      git.merge(src, '--no-ff')
    except ErrorReturnCode as e:
      err = stderr(e)
      if not 'stash' in err:
        raise GlError(stdout(e) + err)
      if op_cb and op_cb.save:
        op_cb.save()
      git.stash.save('--', _stash_msg_merge(self))
      try:
        git.merge(src, '--no-ff')
      except ErrorReturnCode as e:
        raise GlError(stdout(e) + stderr(e))

    restore_fn = op_cb.restore_ok if op_cb else None
    self._safe_restore(_stash_msg_merge, restore_fn=restore_fn)
    self._state_cleanup()
Exemple #2
0
    def commit(self,
               page,
               message='',
               author=None,
               parent=None,
               extra_path=None):
        path = page.path
        paths_to_commit = [path]
        if extra_path:
            paths_to_commit.append(extra_path)
        kwargs = {}
        User = get_user_model()
        if isinstance(author, User) and author.is_authenticated():
            kwargs['author'] = u"%s <%s>" % (author.get_full_name()
                                             or author.username, author.email)
        elif isinstance(author, six.string_types):
            kwargs['author'] = author

        try:
            there_were_changes = parent and parent != self.last_version(page)
            status = git.status('--porcelain', path).stdout.decode('utf8')[:2]
            if parent and status != "UU":
                git.stash()
                git.checkout('--detach', parent)
                try:
                    git.stash('pop')
                except:
                    git.checkout('--theirs', path)

            if status == 'UU':
                # See http://stackoverflow.com/a/8062976/811740
                kwargs['i'] = True

            git.add(path)
            git_commit_cmd = git.commit.bake(allow_empty=True,
                                             allow_empty_message=True,
                                             m=message,
                                             **kwargs)
            git_commit_cmd('--', *paths_to_commit)
            last = self.last_version(page)
            if parent and status != "UU":
                git.checkout('master')
                git.merge(last)
        except ErrorReturnCode as e:
            # TODO: make this more robust!
            error = e.stdout.decode('utf8')
            if 'CONFLICT' in error:
                # For '-i' attribute see http://stackoverflow.com/q/5827944/811740
                git_commit_cmd = git.commit.bake(allow_empty=True,
                                                 allow_empty_message=True,
                                                 m=_('Merged with conflict'),
                                                 i=True,
                                                 **kwargs)
                git_commit_cmd('--', *paths_to_commit)
                raise Page.EditionConflict(
                    _('Automatic merge failed. Please, fix the conflict and save the page.'
                      ))
            else:
                raise
        return there_were_changes
Exemple #3
0
    def merge(self, src, op_cb=None):
        """Merges the divergent changes of the src branch onto this one."""
        self._check_is_current()
        self._check_op_not_in_progress()

        result, unused_ff_conf = self.gl_repo.git_repo.merge_analysis(
            src.target)
        if result & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE:
            raise GlError('No commits to merge')
        try:
            git.merge(src, '--no-ff')
        except ErrorReturnCode as e:
            err = stderr(e)
            if not 'stash' in err:
                raise GlError(stdout(e) + err)
            if op_cb and op_cb.save:
                op_cb.save()
            git.stash.save('--', _stash_msg_merge(self))
            try:
                git.merge(src, '--no-ff')
            except ErrorReturnCode as e:
                raise GlError(stdout(e) + stderr(e))

        self._state_cleanup()
        restore_fn = op_cb.restore_ok if op_cb else None
        self._safe_restore(_stash_msg_merge, restore_fn=restore_fn)
Exemple #4
0
    def add_to_blacklist(self, items_to_blacklist, username, code_permissions):
        # Check if we're on master
        if git("rev-parse", "--abbrev-ref", "HEAD").strip() != "master":
            return (False, "Not currently on master.")

        # Check that we're up-to-date with origin (GitHub)
        git.remote.update()
        if git("rev-parse", "refs/remotes/origin/master").strip() != git("rev-parse", "master").strip():
            return (False, "HEAD isn't at tip of origin's master branch")

        # Check that blacklisted_websites.txt isn't modified locally. That could get ugly fast
        if "blacklisted_websites.txt" in git.status():  # Also ugly
            return (False, "blacklisted_websites.txt modified locally. This is probably bad.")

        # Store current commit hash
        current_commit = git("rev-parse", "HEAD").strip()

        # Add items to file
        with open("blacklisted_websites.txt", "a+") as blacklisted_websites:
            last_character = blacklisted_websites.read()[-1:]
            if last_character != "\n":
                blacklisted_websites.write("\n")
            blacklisted_websites.write("\n".join(items_to_blacklist) + "\n")

        # Checkout a new branch (mostly unnecessary, but may help if we create PRs in the future
        branch = "auto-blacklist-{0}".format(str(time.time()))
        git.checkout("-b", branch)

        # Clear HEAD just in case
        git.reset("HEAD")

        git.add("blacklisted_websites.txt")
        git.commit("-m", "Auto blacklist of {0} by {1} --autopull".format(", ".join(items_to_blacklist), username))

        if code_permissions:
            git.checkout("master")
            git.merge(branch)
            git.push()
        else:
            git.push("origin", branch)
            git.checkout("master")

            if GlobalVars.github_username is None or GlobalVars.github_password is None:
                return (False, "tell someone to set a GH password")

            payload = {"title": "{0}: Blacklist {1}".format(username, ", ".join(items_to_blacklist)),
                       "body": "{0} requests blacklist of domains: \n\n - {1}".format(username, "\n - ".join(items_to_blacklist)),
                       "head": branch,
                       "base": "master"}
            response = requests.post("https://api.github.com/repos/Charcoal-SE/SmokeDetector/pulls", auth=HTTPBasicAuth(GlobalVars.github_username, GlobalVars.github_password), data=json.dumps(payload))
            print(response.json())
            return (True, "You don't have code privileges, but I've [created a pull request for you]({0}).".format(response.json()["html_url"]))

        git.checkout(current_commit)  # Return to old commit to await CI. This will make Smokey think it's in reverted mode if it restarts

        if not code_permissions:
            return (False, "Unable to perform action due to lack of code-level permissions. [Branch pushed](https://github.com/Charcoal-SE/SmokeDetector/tree/{0}), PR at your leisure.".format(branch))

        return (True, "Blacklisted {0} - the entry will be applied via autopull if CI succeeds.".format(", ".join(items_to_blacklist)))
Exemple #5
0
def update(conf, args):
  '''Apply updates from the upstream repository.'''

  print('Checking for updates...')

  # fetch changes from the canonical repo
  git.fetch(constants.GIT_REMOTE, no_tags=True, quiet=True)

  # get a list of the commit messages for the incoming changes
  updates = git('--no-pager', 'log', '..FETCH_HEAD', oneline=True)
  updates = [tuple(m.split(None, 1)) for m in updates.splitlines()]

  # print out a list of the incoming updates
  if len(updates) > 0:
    print('Available updates:')

    max_updates = 10
    for commit, msg in updates[:max_updates]:
      print(color.yellow('*'), msg)

    # print a special message if too many updates are available
    if len(updates) > max_updates:
      print('...and', color.green(len(updates) - max_updates), 'more!')
      print('Run `git log ..FETCH_HEAD` to see the full list')

    # bail if we have uncommitted changes (git exits non-0 in this case)
    if git.diff(exit_code=True, quiet=True, _ok_code=(0, 1)).exit_code != 0:
      raise ValueError('The repository has uncommitted changes. Handle them, '
        'then try updating again.')

    print('Applying the update...')

    # stash _all_ changes to the repo
    git.stash(include_untracked=True, all=True, quiet=True)

    # squash all the fetched commits together and merge them into master
    git.merge('@{u}', squash=True, quiet=True)

    # add a nice update commit that includes the latest upstream commit hash
    commit_message = 'Update dotparty to %s' % updates[0][0]
    git.commit(m=commit_message, quiet=True)

    # TODO: if squash merge failed, roll back to the pre-update state and
    # complain with instructions for the user to do their own update.

    # un-stash all our old changes
    git.stash('pop', quiet=True)

    # push our changes back up to the remote
    git.push(quiet=True)

    print('Update successful!')
  else:
    print('Already up-to-date!')
Exemple #6
0
    def next(self, merge=False):
        current = self.current()

        try:
            switch_to = self.points()[self.points().index(current) + 1]
        except IndexError:
            # we've reached the end of the list; switch to master
            switch_to = 'master'

        git.checkout(switch_to)

        if merge:
            git.merge(current)
Exemple #7
0
def reply_comment_email(from_email, subject, message):
    try:
        m = re.search('\[(\d+)\-(\w+)\]', subject)
        branch_name = m.group(1)
        article = m.group(2)

        message = decode_best_effort(message)

        # safe logic: no answer or unknown answer is a go for publishing
        if message[:2].upper() == 'NO':
            logger.info('discard comment: %s' % branch_name)
            email_body = get_template('drop_comment').render(original=message)
            mail(pecosys.get_config('post', 'from_email'),
                 pecosys.get_config('post', 'to_email'),
                 'Re: ' + subject, email_body)
        else:
            if pecosys.get_config("git", "disabled"):
                logger.debug("GIT usage disabled (debug mode)")
            else:
                git.merge(branch_name)
                if pecosys.get_config("git", "remote"):
                    git.push()
                logger.info('commit comment: %s' % branch_name)

                # execute post-commit command asynchronously
                postcommand = pecosys.get_config('post', 'postcommand')
                if postcommand:
                    executor.execute(postcommand)

            # send approval confirmation email to admin
            email_body = get_template('approve_comment').render(original=message)
            mail(pecosys.get_config('post', 'from_email'),
                 pecosys.get_config('post', 'to_email'),
                 'Re: ' + subject, email_body)

            # notify reader once comment is published
            reader_email, article_url = get_email_metadata(message)
            if reader_email:
                notify_reader(reader_email, article_url)

            # notify subscribers every time a new comment is published
            notify_subscribers(article)

        if pecosys.get_config("git", "disabled"):
            logger.debug("GIT usage disabled (debug mode)")
        else:
            git.branch("-D", branch_name)
    except:
        logger.exception("new email failure")
Exemple #8
0
    def next(self, merge=False):
        current = self.current()

        try:
            switch_to = self.points()[
                self.points().index(current) + 1
            ]
        except IndexError:
            # we've reached the end of the list; switch to master
            switch_to = 'master'

        git.checkout(switch_to)

        if merge:
            git.merge(current)
Exemple #9
0
    def setUp(self):
        super(TestFileResolve, self).setUp()

        # Generate a conflict
        git.checkout(b='branch')
        utils_lib.write_file(FP_IN_CONFLICT, contents='branch')
        utils_lib.write_file(DIR_FP_IN_CONFLICT, contents='branch')
        git.add(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT)
        git.commit(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT, m='branch')
        git.checkout('master')
        utils_lib.write_file(FP_IN_CONFLICT, contents='master')
        utils_lib.write_file(DIR_FP_IN_CONFLICT, contents='master')
        git.add(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT)
        git.commit(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT, m='master')
        git.merge('branch', _ok_code=[1])
Exemple #10
0
  def setUp(self):
    super(TestFileResolve, self).setUp()

    # Generate a conflict
    git.checkout(b='branch')
    utils_lib.write_file(FP_IN_CONFLICT, contents='branch')
    utils_lib.write_file(DIR_FP_IN_CONFLICT, contents='branch')
    git.add(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT)
    git.commit(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT, m='branch')
    git.checkout('master')
    utils_lib.write_file(FP_IN_CONFLICT, contents='master')
    utils_lib.write_file(DIR_FP_IN_CONFLICT, contents='master')
    git.add(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT)
    git.commit(FP_IN_CONFLICT, DIR_FP_IN_CONFLICT, m='master')
    git.merge('branch', _ok_code=[1])
Exemple #11
0
    def commit(self, page, message="", author=None, parent=None, extra_path=None):
        path = page.path
        paths_to_commit = [path]
        if extra_path:
            paths_to_commit.append(extra_path)
        kwargs = {}
        if isinstance(author, User) and author.is_authenticated():
            kwargs["author"] = u"%s <%s>" % (author.get_full_name() or author.username, author.email)
        elif isinstance(author, six.string_types):
            kwargs["author"] = author

        try:
            there_were_changes = parent and parent != self.last_version(page)
            status = git.status("--porcelain", path).stdout.decode("utf8")[:2]
            if parent and status != "UU":
                git.stash()
                git.checkout("--detach", parent)
                try:
                    git.stash("pop")
                except:
                    git.checkout("--theirs", path)

            if status == "UU":
                # See http://stackoverflow.com/a/8062976/811740
                kwargs["i"] = True

            git.add(path)
            git_commit_cmd = git.commit.bake(allow_empty=True, allow_empty_message=True, m=message, **kwargs)
            git_commit_cmd("--", *paths_to_commit)
            last = self.last_version(page)
            if parent and status != "UU":
                git.checkout("master")
                git.merge(last)
        except ErrorReturnCode as e:
            # TODO: make this more robust!
            error = e.stdout.decode("utf8")
            if "CONFLICT" in error:
                # For '-i' attribute see http://stackoverflow.com/q/5827944/811740
                git_commit_cmd = git.commit.bake(
                    allow_empty=True, allow_empty_message=True, m=_("Merged with conflict"), i=True, **kwargs
                )
                git_commit_cmd("--", *paths_to_commit)
                raise Page.EditionConflict(_("Automatic merge failed. Please, fix the conflict and save the page."))
            else:
                raise
        return there_were_changes
Exemple #12
0
    def commit(self, page, message='', author=None, parent=None, extra_path=None):
        path = page.path
        paths_to_commit = [path]
        if extra_path:
            paths_to_commit.append(extra_path)
        kwargs = {}
        User = get_user_model()
        if isinstance(author, User) and is_authenticated(author):
            kwargs['author'] = u"%s <%s>" % (author.get_full_name() or author.username, author.email)
        elif isinstance(author, six.string_types):
            kwargs['author'] = author

        try:
            there_were_changes = parent and parent != self.last_version(page)
            status = git.status('--porcelain', path).stdout.decode('utf8')[:2]
            if parent and status != "UU":
                git.stash()
                git.checkout('--detach', parent)
                try:
                    git.stash('pop')
                except:
                    git.checkout('--theirs', path)

            if status == 'UU':
                # See http://stackoverflow.com/a/8062976/811740
                kwargs['i'] = True

            git.add(path)
            git_commit_cmd = git.commit.bake(allow_empty=True, allow_empty_message=True, m=message, **kwargs)
            git_commit_cmd('--', *paths_to_commit)
            last = self.last_version(page)
            if parent and status != "UU":
                git.checkout('master')
                git.merge(last)
        except ErrorReturnCode as e:
            # TODO: make this more robust!
            error = e.stdout.decode('utf8')
            if 'CONFLICT' in error:
                # For '-i' attribute see http://stackoverflow.com/q/5827944/811740
                git_commit_cmd = git.commit.bake(allow_empty=True, allow_empty_message=True, m=_('Merged with conflict'), i=True, **kwargs)
                git_commit_cmd('--', *paths_to_commit)
                raise Page.EditionConflict(_('Automatic merge failed. Please, fix the conflict and save the page.'))
            else:
                raise
        return there_were_changes
Exemple #13
0
def merge_and_check(base, head):
    """Merge <head> into <base>, then run some tests.

    Only modifies the working tree---doesn't actually create a merge
    commit. Resets and cleans the repo and leaves it in headless mode.

    Raises sh.ErrorReturnCode if the merge or the tests fail.
    """
    # Make sure we're up to date
    git.fetch()
    # Make sure we can do a clean checkout
    git.reset(hard=True)
    git.clean('-dfx')
    git.checkout('origin/' + base)
    # Merge the working tree, but don't modify the index
    git.merge('origin/' + head, no_commit=True)
    # Check the PR!
    check()
Exemple #14
0
def merge_and_check(base, head):
    """Merge <head> into <base>, then run some tests.

    Only modifies the working tree---doesn't actually create a merge
    commit. Resets and cleans the repo and leaves it in headless mode.

    Raises sh.ErrorReturnCode if the merge or the tests fail.
    """
    # Make sure we're up to date
    git.fetch()
    # Make sure we can do a clean checkout
    git.reset(hard=True)
    git.clean('-dfx')
    git.checkout('origin/' + base)
    # Merge the working tree, but don't modify the index
    git.merge('origin/' + head, no_commit=True)
    # Check the PR!
    check()
Exemple #15
0
    def merge(self, branch, base_branch="master"):
        """
        Merge the the given WIP branch to master (or base_branch, if specified)

        If the merge fails, the merge will be aborted
        and then a MergeException will be thrown. The
        message of the MergeException will be the
        "git status" output, so details about merge
        conflicts can be determined.

        """

        os.chdir(self.repo)

        current_branch = self.current_branch()
        if current_branch != base_branch:
            git.checkout(base_branch)

        # Always create a merge commit, even if we could fast forward, so we know
        # when merges occured
        try:
            merge_output = git.merge("--no-ff", branch)
        except sh.ErrorReturnCode:
            # attempt to reset things so other operations can
            # continue
            output = git.status()
            git.merge("--abort")

            # re-raise the exception so other code can decide
            # what to do with it
            raise MergeException(output)

        # the merge succeeded, so remove the local WIP branch
        git.branch("-d", branch)

        new_sha = git("rev-parse", "HEAD")
        return new_sha.strip()
Exemple #16
0
    def commit(self, page, message='', author=None, parent=None):
        path = page.path
        kwargs = {}
        if isinstance(author, User) and author.is_authenticated():
            kwargs['author'] = u"%s <%s>" % (author.get_full_name() or author.username, author.email)
        elif isinstance(author, six.string_types):
            kwargs['author'] = author

        try:
            there_were_changes = parent and parent != self.last_version(page)
            status = git.status('--porcelain', path).stdout.decode('utf8')[:2]
            if parent and status != "UU":
                git.stash()
                git.checkout('--detach', parent)
                try:
                    git.stash('pop')
                except:
                    git.checkout('--theirs', path)

            if status == 'UU':
                # See http://stackoverflow.com/a/8062976/811740
                kwargs['i'] = True

            git.add(path)
            git.commit(path, allow_empty_message=True, m=message, **kwargs)
            last = self.last_version(page)
            if parent and status != "UU":
                git.checkout('master')
                git.merge(last)
        except ErrorReturnCode as e:
            # TODO: make this more robust!
            error = e.stdout.decode('utf8')
            if 'CONFLICT' in error:
                raise Page.EditionConflict(_('Automatic merge failed. Please, fix the conflict and save the page.'))
            else:
                raise
        return there_were_changes
    def merge(self, branch, base_branch="master"):
        """
        Merge the the given WIP branch to master (or base_branch, if specified)

        If the merge fails, the merge will be aborted
        and then a MergeException will be thrown. The
        message of the MergeException will be the
        "git status" output, so details about merge
        conflicts can be determined.

        """

        os.chdir(self.repo)

        current_branch = self.current_branch()
        if current_branch != base_branch:
            git.checkout(base_branch)

        # Always create a merge commit, even if we could fast forward, so we know
        # when merges occured
        try:
            merge_output = git.merge("--no-ff", branch)
        except sh.ErrorReturnCode:
            # attempt to reset things so other operations can
            # continue
            output = git.status()
            git.merge("--abort")

            # re-raise the exception so other code can decide
            # what to do with it
            raise MergeException(output)

        # the merge succeeded, so remove the local WIP branch
        git.branch("-d", branch)

        new_sha      = git("rev-parse","HEAD")
        return new_sha.strip()
Exemple #18
0
 def abort_merge(self):
     if not self.merge_in_progress:
         raise GlError('No merge in progress, nothing to abort')
     git.merge(abort=True)
Exemple #19
0
    def add_to_blacklist(self, **kwargs):
        blacklist = kwargs.get("blacklist", "website")
        items_to_blacklist = kwargs.get("items_to_blacklist", [])
        username = kwargs.get("username", "")
        chat_profile_link = kwargs.get("chat_profile_link",
                                       "http://chat.stackexchange.com/users")
        code_permissions = kwargs.get("code_permissions", False)

        if blacklist == "website":
            blacklist_file_name = "blacklisted_websites.txt"
        elif blacklist == "keyword":
            blacklist_file_name = "bad_keywords.txt"
        elif blacklist == "username":
            blacklist_file_name = "blacklisted_usernames.txt"

        # Check if we're on master
        if git("rev-parse", "--abbrev-ref", "HEAD").strip() != "master":
            return (False, "Not currently on master.")

        # Check that we're up-to-date with origin (GitHub)
        git.remote.update()
        if git("rev-parse", "refs/remotes/origin/master").strip() != git(
                "rev-parse", "master").strip():
            return (False, "HEAD isn't at tip of origin's master branch")

        # Check that blacklisted_websites.txt isn't modified locally. That could get ugly fast
        if blacklist_file_name in git.status():  # Also ugly
            return (False,
                    "{0} modified locally. This is probably bad.".format(
                        blacklist_file_name))

        # Store current commit hash
        current_commit = git("rev-parse", "HEAD").strip()

        # Add items to file
        with open(blacklist_file_name, "a+") as blacklist_file:
            last_character = blacklist_file.read()[-1:]
            if last_character != "\n":
                blacklist_file.write("\n")
            blacklist_file.write("\n".join(items_to_blacklist) + "\n")

        # Checkout a new branch (for PRs for non-code-privileged people)
        branch = "auto-blacklist-{0}".format(str(time.time()))
        git.checkout("-b", branch)

        # Clear HEAD just in case
        git.reset("HEAD")

        git.add(blacklist_file_name)
        git.commit(
            "-m", u"Auto blacklist of {0} by {1} --autopull".format(
                ", ".join(items_to_blacklist), username))

        if code_permissions:
            git.checkout("master")
            git.merge(branch)
            git.push()
        else:
            git.push("origin", branch)
            git.checkout("master")

            if GlobalVars.github_username is None or GlobalVars.github_password is None:
                return (False, "Tell someone to set a GH password")

            list_of_domains = ""

            for domain in range(len(items_to_blacklist)):
                list_of_domains += "\n - {0} - [MS search](https://metasmoke.erwaysoftware.com/search?utf8=%E2%9C%93&body_is_regex=1&body={0})".format(
                    items_to_blacklist[domain])

            payload = {
                "title":
                "{0}: Blacklist {1}".format(username,
                                            ", ".join(items_to_blacklist)),
                "body":
                "[{0}]({1}) requests the blacklist of the following {2}(s): \n{3}\n<!-- METASMOKE-BLACKLIST {4} -->"
                .format(username, chat_profile_link, blacklist,
                        list_of_domains, "|".join(items_to_blacklist)),
                "head":
                branch,
                "base":
                "master"
            }
            response = requests.post(
                "https://api.github.com/repos/Charcoal-SE/SmokeDetector/pulls",
                auth=HTTPBasicAuth(GlobalVars.github_username,
                                   GlobalVars.github_password),
                data=json.dumps(payload))
            print(response.json())
            return (
                True,
                "You don't have code privileges, but I've [created a pull request for you]({0})."
                .format(response.json()["html_url"]))

        git.checkout(
            current_commit
        )  # Return to old commit to await CI. This will make Smokey think it's in reverted mode if it restarts

        return (
            True,
            "Blacklisted {0} - the entry will be applied via autopull if CI succeeds."
            .format(", ".join(items_to_blacklist)))
Exemple #20
0
 def abort_merge(self):
   if not self.merge_in_progress:
     raise GlError('No merge in progress, nothing to abort')
   git.merge(abort=True)
Exemple #21
0
    def add_to_blacklist(**kwargs):
        blacklist = kwargs.get("blacklist", "")
        item_to_blacklist = kwargs.get("item_to_blacklist", "")
        username = kwargs.get("username", "")
        chat_profile_link = kwargs.get("chat_profile_link", "http://chat.stackexchange.com/users")
        code_permissions = kwargs.get("code_permissions", False)

        # Make sure git credentials are set up
        if git.config("--global", "--get", "user.name") == "":
            return (False, "Tell someone to run `git config --global user.name \"SmokeDetector\"`")

        if git.config("--global", "--get", "user.name") == "":
            return (False, "Tell someone to run `git config --global user.email \"[email protected]\"`")

        if blacklist == "":
            # If we broke the code, and this isn't assigned, error out before doing anything, but do
            # so gracefully with a nice error message.
            return (False, "Programming Error - Critical information missing for GitManager: blacklist")

        if item_to_blacklist == "":
            # If we broke the code, and this isn't assigned, error out before doing anything, but do
            # so gracefully with a nice error message.
            return (False, "Programming Error - Critical information missing for GitManager: item_to_blacklist")

        item_to_blacklist = item_to_blacklist.replace("\s", " ")

        if blacklist == "website":
            blacklist_file_name = "blacklisted_websites.txt"
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "keyword":
            blacklist_file_name = "bad_keywords.txt"
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "username":
            blacklist_file_name = "blacklisted_usernames.txt"
            ms_search_option = "&username_is_regex=1&username="******"Invalid blacklist type specified, something has broken badly!")

        git.checkout("master")
        try:
            git.pull()
        except:
            pass

        # Check that we're up-to-date with origin (GitHub)
        git.remote.update()
        if git("rev-parse", "refs/remotes/origin/master").strip() != git("rev-parse", "master").strip():
            return (False, "HEAD isn't at tip of origin's master branch")

        # Check that blacklisted_websites.txt isn't modified locally. That could get ugly fast
        if blacklist_file_name in git.status():  # Also ugly
            return (False, "{0} is modified locally. This is probably bad.".format(blacklist_file_name))

        # Add item to file
        with open(blacklist_file_name, "a+") as blacklist_file:
            last_character = blacklist_file.read()[-1:]
            if last_character != "\n":
                blacklist_file.write("\n")
            blacklist_file.write(item_to_blacklist + "\n")

        # Checkout a new branch (for PRs for non-code-privileged people)
        branch = "auto-blacklist-{0}".format(str(time.time()))
        git.checkout("-b", branch)

        # Clear HEAD just in case
        git.reset("HEAD")

        git.add(blacklist_file_name)
        git.commit("-m", u"Auto blacklist of {0} by {1} --autopull".format(item_to_blacklist, username))

        if code_permissions:
            git.checkout("master")
            git.merge(branch)
            git.push()
        else:
            git.push("origin", branch)
            git.checkout("master")

            if GlobalVars.github_username is None or GlobalVars.github_password is None:
                return (False, "Tell someone to set a GH password")

            payload = {"title": u"{0}: Blacklist {1}".format(username, item_to_blacklist),
                       "body": u"[{0}]({1}) requests the blacklist of the {2} {3}. See the Metasmoke search [here]"
                               "(https://metasmoke.erwaysoftware.com/search?utf8=%E2%9C%93{4}{5})\n"
                               u"<!-- METASMOKE-BLACKLIST-{6} {3} -->".format(username, chat_profile_link, blacklist,
                                                                              item_to_blacklist, ms_search_option,
                                                                              item_to_blacklist.replace(" ", "+"),
                                                                              blacklist.upper()),
                       "head": branch,
                       "base": "master"}
            response = requests.post("https://api.github.com/repos/Charcoal-SE/SmokeDetector/pulls",
                                     auth=HTTPBasicAuth(GlobalVars.github_username, GlobalVars.github_password),
                                     data=json.dumps(payload))
            print(response.json())
            try:
                git.checkout("deploy")  # Return to deploy, pending the accept of the PR in Master.
                return (True, "You don't have code privileges, but I've [created a pull request for you]({0}).".format(
                    response.json()["html_url"]))
            except KeyError:
                # Error capture/checking for any "invalid" GH reply without an 'html_url' item,
                # which will throw a KeyError.
                if "bad credentials" in str(response.json()['message']).lower():
                    # Capture the case when GH credentials are bad or invalid
                    return (False, "Something is wrong with the GH credentials, tell someone to check them.")
                else:
                    # Capture any other invalid response cases.
                    return (False, "A bad or invalid reply was received from GH, the message was: %s" %
                            response.json()['message'])

        git.checkout("deploy")  # Return to deploy to await CI.

        return (True, "Blacklisted {0}".format(item_to_blacklist))
Exemple #22
0
    def add_to_blacklist(**kwargs):
        if 'windows' in str(platform.platform()).lower():
            log('warning', "Git support not available in Windows.")
            return (False, "Git support not available in Windows.")

        blacklist = kwargs.get("blacklist", "")
        item_to_blacklist = kwargs.get("item_to_blacklist", "")
        username = kwargs.get("username", "")
        chat_profile_link = kwargs.get("chat_profile_link", "http://chat.stackexchange.com/users")
        code_permissions = kwargs.get("code_permissions", False)

        # Make sure git credentials are set up
        if git.config("--global", "--get", "user.name", _ok_code=[0, 1]) == "":
            return (False, "Tell someone to run `git config --global user.name \"SmokeDetector\"`")

        if git.config("--global", "--get", "user.email", _ok_code=[0, 1]) == "":
            return (False, "Tell someone to run `git config --global user.email \"[email protected]\"`")

        if blacklist == "":
            # If we broke the code, and this isn't assigned, error out before doing anything, but do
            # so gracefully with a nice error message.
            return (False, "Programming Error - Critical information missing for GitManager: blacklist")

        if item_to_blacklist == "":
            # If we broke the code, and this isn't assigned, error out before doing anything, but do
            # so gracefully with a nice error message.
            return (False, "Programming Error - Critical information missing for GitManager: item_to_blacklist")

        item_to_blacklist = item_to_blacklist.replace("\s", " ")

        if blacklist == "website":
            blacklist_file_name = "blacklisted_websites.txt"
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "keyword":
            blacklist_file_name = "bad_keywords.txt"
            ms_search_option = "&body_is_regex=1&body="
        elif blacklist == "username":
            blacklist_file_name = "blacklisted_usernames.txt"
            ms_search_option = "&username_is_regex=1&username="******"Invalid blacklist type specified, something has broken badly!")

        git.checkout("master")
        try:
            git.pull()
        except:
            pass

        # Check that we're up-to-date with origin (GitHub)
        git.remote.update()
        if git("rev-parse", "refs/remotes/origin/master").strip() != git("rev-parse", "master").strip():
            return (False, "HEAD isn't at tip of origin's master branch")

        # Check that blacklisted_websites.txt isn't modified locally. That could get ugly fast
        if blacklist_file_name in git.status():  # Also ugly
            return (False, "{0} is modified locally. This is probably bad.".format(blacklist_file_name))

        # Add item to file
        with open(blacklist_file_name, "a+") as blacklist_file:
            last_character = blacklist_file.read()[-1:]
            if last_character != "\n":
                blacklist_file.write("\n")
            blacklist_file.write(item_to_blacklist + "\n")

        # Checkout a new branch (for PRs for non-code-privileged people)
        branch = "auto-blacklist-{0}".format(str(time.time()))
        git.checkout("-b", branch)

        # Clear HEAD just in case
        git.reset("HEAD")

        git.add(blacklist_file_name)
        git.commit("--author='SmokeDetector <*****@*****.**>'",
                   "-m", u"Auto blacklist of {0} by {1} --autopull".format(item_to_blacklist, username))

        if code_permissions:
            git.checkout("master")
            git.merge(branch)
            git.push("origin", "master")
            git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
        else:
            git.push("origin", branch)
            git.checkout("master")

            if GlobalVars.github_username is None or GlobalVars.github_password is None:
                return (False, "Tell someone to set a GH password")

            payload = {"title": u"{0}: Blacklist {1}".format(username, item_to_blacklist),
                       "body": u"[{0}]({1}) requests the blacklist of the {2} {3}. See the Metasmoke search [here]"
                               "(https://metasmoke.erwaysoftware.com/search?utf8=%E2%9C%93{4}{5})\n"
                               u"<!-- METASMOKE-BLACKLIST-{6} {3} -->".format(username, chat_profile_link, blacklist,
                                                                              item_to_blacklist, ms_search_option,
                                                                              item_to_blacklist.replace(" ", "+"),
                                                                              blacklist.upper()),
                       "head": branch,
                       "base": "master"}
            response = requests.post("https://api.github.com/repos/Charcoal-SE/SmokeDetector/pulls",
                                     auth=HTTPBasicAuth(GlobalVars.github_username, GlobalVars.github_password),
                                     data=json.dumps(payload))
            log('debug', response.json())
            try:
                git.checkout("deploy")  # Return to deploy, pending the accept of the PR in Master.
                git.branch('-D', branch)  # Delete the branch in the local git tree since we're done with it.
                return (True, "You don't have code privileges, but I've [created a pull request for you]({0}).".format(
                    response.json()["html_url"]))
            except KeyError:
                git.checkout("deploy")  # Return to deploy

                # Delete the branch in the local git tree, we'll create it again if the
                # command is run again. This way, we keep things a little more clean in
                # the local git tree
                git.branch('-D', branch)

                # Error capture/checking for any "invalid" GH reply without an 'html_url' item,
                # which will throw a KeyError.
                if "bad credentials" in str(response.json()['message']).lower():
                    # Capture the case when GH credentials are bad or invalid
                    return (False, "Something is wrong with the GH credentials, tell someone to check them.")
                else:
                    # Capture any other invalid response cases.
                    return (False, "A bad or invalid reply was received from GH, the message was: %s" %
                            response.json()['message'])

        git.checkout("deploy")  # Return to deploy to await CI.

        return (True, "Blacklisted {0}".format(item_to_blacklist))