Exemplo n.º 1
0
    def run(self):
        """
        Future Functionality:
            Given a filename or a reference, determine what actually needs to be resolved
            and then attempt to automatically resolve the issue. The current items that
            will be attempted to address include (in order of how they would be addressed):
                - filename standards     => attempt to rename file to meet standards
                - divergent chains       => append to end of alter chain and rename file
                - abandoned alters       => append to end of alter chain and rename file
                - missing up/down        => create the missing pair

        Current Functionality:
            Given a filename, resolve a conflict that is happening by moving the file
            to the end of the build chain. First however, verify that this alter hasn't
            been run in the DB. If so, require the force option to continue.

            If the alter is at the end of the db history then we can also warn and, if
            given the force option, can undo the commit before resolving (should issue
            message about what has happened).
        """
        (options, args) = self.parser.parse_args()

        if len(args) == 0:
            raise ArgsError("You must provide a filename or reference",
                            self.parser.format_help())

        # search for file/ref
        self.file = self.ref = args[0]
        if self._file_exists():
            self.type = 'file'
        elif self._ref_exists():
            self.type = 'ref'
        else:
            raise MissingRefError("Filename or reference not found for '%s'" %
                                  self.file)

        # We can now assume that self.ref and self.file are set properly

        # TODO: implement ability to determine issue and resolve (if possible) automatically
        # determine what the issue is:
        # check = CheckCommand()
        # if not check.check_filename(self.file):
        #     # rename file
        #     pass

        sys.stdout.write("Resolving\n")
        self._relocate_sub_chain()

        sys.stdout.write("\nRe-Checking...\n")
        sys.argv = [sys.argv[0]]
        CheckCommand(self.context).run()
Exemplo n.º 2
0
    def run(self):
        (options, args) = self.parser.parse_args()

        # validate static_alter_dir set if flag used
        if options.write_to_file:
            options.include_rev_query = True
            if self.config.get('static_alter_dir') is None:
                raise Exception(
                    'static_alter_dir must be set in config.json to'
                    '\nuse -w/--write-to-file flag')
            self._setup_static_alter_dir()

        refs = args
        nodes = ChainUtil.build_chain()
        ref_nodes = []

        if len(refs) == 0:
            # entire chain
            refs = self._get_node_ids(nodes)
            refs.reverse()

        # validate valid refs
        for ref in refs:
            node = self._find_ref(ref, nodes)
            if node is False:
                raise MissingRefError("Ref '%s' could not be found" % ref,
                                      self.parser.format_help())
            else:
                ref_nodes.append(node)

        # gen SQL for each ref
        if options.write_to_file:
            # gen SQL for each ref, and save to individual files.
            for node in ref_nodes:
                sql = self.gen_sql_for_reflist([node], options)
                if options.down_alter:
                    filename = node.down_filename()
                else:
                    filename = node.filename
                fobj = open(
                    os.path.join(self.config['static_alter_dir'], filename),
                    'w')
                fobj.write(sql)
                fobj.close()
                print os.path.join(self.config['static_alter_dir'], filename)
        else:
            # gen SQL for refs in one call
            sql = self.gen_sql_for_reflist(ref_nodes, options)
            sys.stdout.write(sql)
Exemplo n.º 3
0
    def check_abandoned_alters(self, chain):
        """
        Check for files that do not exist within the current alter chain.
        """
        tail = chain
        chain_files = []
        while tail is not None:
            chain_files.append(tail.filename)
            tail = tail.backref

        up_alter = re.compile('-up.sql')
        for file in self.files:
            if up_alter.search(file) is not None:
                if file not in chain_files:
                    # @jmurray - how can scenario be encountered?
                    raise MissingRefError(
                        "File not found within build-chain '%s'" % file)
Exemplo n.º 4
0
    def run(self):
        """
        Update the DB by checking the current state (stored in the DB itself)
        and and bringing the DB up to date from the current list of alters.

        Returns nothing, but updated DB (via alters) and updated revision number
        in DB table

        Now that we have the history of what has _been_ run and the alter chain
        of all alters, we can determine what _needs_ to be run. First we will
        go up the history until it diverges with the chain. Then we'll run all
        of the "undos" for any history items that still exist and then run the
        list from where we left off.
        """
        (options, args) = self.parser.parse_args()

        CheckCommand(self.context).run(inline=True)

        # get history
        history = self.db.get_commit_history()
        history = sorted(history, key=lambda h: h[0])
        history_alters = [h[1] for h in history]

        # get current alter chain
        tail = ChainUtil.build_chain()
        alter_list = [tail]
        if None in alter_list:
            alter_list.remove(None)
        while tail is not None and tail.backref is not None:
            tail = tail.backref
            alter_list.append(tail)

        # find (and remove) synced alters from alter_list (so they are not run again)
        common_history = 0
        for (_id, alter_id, datetime) in history:
            if len(alter_list) == 0:
                break
            # don't count alters for other env's in common-history
            alter = alter_list.pop()
            while not self.should_run(alter):
                alter = alter_list.pop()

            if alter.id == alter_id:
                common_history += 1
            else:
                alter_list.append(alter)
                break

        # undo alters that are not in sync with alter chain if options.undo is true.
        if options.undo:
            uncommon_history = history[common_history:]
            if len(uncommon_history) > 0:
                # clean up alters from here
                uncommon_history.reverse()
                for (_id, alter_id, datetime) in uncommon_history:
                    alters = [a for a in alter_list if a.id == alter_id]
                    if len(alters) > 1:
                        msg = "Multiple alters found for a single id (%s)" % a.id
                        if not options.force:
                            raise MultipleDownAltersError(msg)
                        else:
                            sys.stderr.write(msg + "\n")
                    elif len(alters) == 0:
                        raise MissingDownAlterError("Missing down alter %s" % alter_id)
                    alter = alters[0]
                    self.db.run_down(alter)
                    if alter.id in history_alters:
                        history_alters.remove(alter.id)

        # Ensure that if a target ref was specified that one was found in
        # in the list of alters to run (up)
        if len(alter_list) and len(args) and args[0] not in [a.id for a in alter_list]:
            raise MissingRefError('revision (%s) not found in alters that would be run' % args[0])

        # Do alters that are in the alter chain and have not
        # been run yet
        max_ = int(options.N or len(alter_list))
        i = 0
        while not len(alter_list) == 0:
            if i == max_:
                break

            alter = alter_list.pop()

            if len(args) > 0:
                target_rev = args[0]
                if target_rev == alter.id:
                    i = (max_ - 1)

            i += 1
            if alter.id not in history_alters and self.should_run(alter):
                self.db.run_up(alter=alter,
                          force=options.force,
                          verbose=options.verbose)
            elif not self.should_run(alter):
                # possible to get a skipped alter in the event that it wasn't removed
                # in the common-history code (aka, running new alters)
                pass
            else:
                sys.stderr.write("Warning: alter " + str(alter.id) + " has already been " \
                        "run. Skipping\n")

        sys.stdout.write("Updated\n")
Exemplo n.º 5
0
    def __build_and_validate_linked_list(cls, nodes):
        """
        Build a linked list and validate it's correctness given an array of
        SimpleNodes contain soft/weak references to each other

        Returns tail of list (since it's processed backwards)
        :rtype : SimpleNode
        """
        # check if we're working with no nodes, return None if so
        # don't error/exit because and empty list is not necessarily invalid
        if len(nodes) == 0:
            return None

        heads = []
        backrefs = {}
        for node in nodes:
            if node.backref is not None:
                # Check for duplicate refs
                backnodes = [n for n in nodes if n.id == node.backref]
                if len(backnodes) == 1:
                    node.backref = backnodes[0]
                elif len(backnodes) > 1:
                    msg = "\n".join([
                        "Duplicate refs found in %s\n" % b.filename
                        for b in backnodes
                    ])
                    raise DuplicateRefsError(msg)
                else:
                    raise MissingRefError(
                        "Backref points to non-existent alter: %s\n" %
                        node.filename)

                # catalog backrefs (for divergence checking)
                if node.backref.id not in backrefs:
                    backrefs[node.backref.id] = []
                backrefs[node.backref.id].append(node)
            else:
                heads.append(node)

        # check backref catalog for duplicates
        for (_, _nodes) in backrefs.iteritems():
            if len(_nodes) > 1:
                msg = []
                msg.append(
                    "Divergent Branch:"
                    "\nThis means that we have found alters that share a common parent. To fix"
                    "\nthis you can run the 'resolve' command. When merging changes from your"
                    "\nfeature-branch, ensure that you resolve your files that are in conflict"
                    "\n(not existing files that were previously in a good state)."
                    "\n")
                msg += [
                    "\tDuplicate backref found (divergent branch): %s\n" %
                    node.filename for node in _nodes
                ]
                msg = "\n".join(msg)
                raise DuplicateRefsError(msg)

        # check head(s)
        if len(heads) > 1:
            msg = ["More than one head found:"
                   ] + ["  %s" % head.filename for head in heads]
            msg = "\n".join(msg)
            raise HeadError(msg)
        elif len(heads) == 0:
            raise HeadError("No head found")

        # check tail(s)
        tails = []
        for node in nodes:
            if node.backref is None:
                continue
            children = [n for n in nodes if n.backref == node]
            if len(children) == 0:
                tails.append(node)

        if len(tails) > 1:
            msg = "\n".join([
                "Duplicate backref found in %s\n" % tail.filename
                for tail in tails
            ])
            raise DuplicateRefsError(msg)

        elif len(tails) == 0 and (not len(nodes) == 1):
            raise CircularRefError(
                "something strange is happening... no last alter found (circular references!!)"
            )

        if len(nodes) == 1:
            return heads[0]
        else:
            return tails[0]
Exemplo n.º 6
0
    def run(self):
        """
        Analogous to what the up_command definition does, but in reverse.
        """
        (options, args) = self.parser.parse_args()

        CheckCommand(self.context).run(inline=True)

        # check validity of options (can't really do this in OptionParser AFAIK)
        if len(args) == 0 and options.N is None:
            raise OptionsError("must specify either argument or number of down-alters to run", self.parser.format_help())

        # get current history
        history = self.db.get_commit_history()
        history = sorted(history, key=lambda h: h[0], reverse=True)

        # get current alter chain
        tail = ChainUtil.build_chain()
        alter_list = [tail]
        if None in alter_list:
            alter_list.remove(None)
        while tail is not None and tail.backref is not None:
            tail = tail.backref
            alter_list.append(tail)

        # parse the args given
        run_type, target_rev = self.parse_args(args)

        # collect the down-alters that we need to run depending on the command line
        # options and arguments that were given
        down_alters_to_run = []
        max_history_len = int(options.N or len(history))
        i = 0
        for (id, alter_id, alter_time) in history:
            if i == max_history_len:
                break
            if run_type == 'base':
                if i == (max_history_len - 1):
                    break
            elif run_type == 'all':
                pass
            else:
                if target_rev == alter_id:
                    i = (max_history_len - 1)

            i += 1
            alters = [a for a in alter_list if a.id == alter_id]
            if len(alters) > 0:
                alter = alters[0]
                down_alters_to_run.append(alter)
            else:
                # error depending on the force and verbose flags (missing alter to run)
                if options.force:
                    sys.stderr.write("Warning: missing alter: %s\n" % alter_id)
                    self.db.remove_commit(ref=alter_id)
                else:
                    raise MissingDownAlterError("missing alter: %s\n" % alter_id)

        # ensure that if a target_revision was specified that one was found in
        # in the list of alters to run (down)
        if (run_type == 'revision' and
              target_rev not in [a.id for a in down_alters_to_run]):
            raise MissingRefError('revision (%s) not found in alters that would be run' % target_rev)

        # run all the down-alters that we have collected
        for alter_to_run in down_alters_to_run:
            self.db.run_down(alter=alter_to_run,
                        force=options.force,
                        verbose=options.verbose)

        sys.stdout.write("Downgraded\n")