Exemple #1
0
    def report(self, bare=False, inspect_remotes=False):
        """
        :param bool bare: Bare report only
        :param bool inspect_remotes: If True, report on which remote branches are cleanable
        :return GitRunReport: General report on current checkout state
        """
        if not self.is_git_checkout:
            if self.remote_info:
                return GitRunReport(problem="not cloned yet")

            return GitRunReport.not_git()

        result = GitRunReport()

        if not self.config.remotes:
            result.add(problem="no remotes")

        if bare:
            return result

        if self.age is not None and self.age > FRESHNESS_THRESHOLD:
            result.add(note="last fetch %s ago" %
                       runez.represented_duration(self.age))

        orphan_branches = self.orphan_branches
        if self.branches.current in orphan_branches:
            # Current is no more on its remote (should possibly checkout another branch and cleanup, or push)
            orphan_branches = orphan_branches[:]
            orphan_branches.remove(self.branches.current)
            result.add(note="current branch '%s' is orphaned" %
                       self.branches.current)

        if len(orphan_branches) == 1:
            result.add(note="local branch '%s' can be pruned" %
                       orphan_branches[0])

        elif orphan_branches:
            result.add(note="%s can be pruned" %
                       runez.plural(orphan_branches, "local branch"))

        result.add(self.branches.report)

        if inspect_remotes and self.remote_cleanable_branches:
            if len(self.remote_cleanable_branches) == 1:
                cleanable = "'%s'" % next(iter(self.remote_cleanable_branches))

            else:
                cleanable = runez.plural(self.remote_cleanable_branches,
                                         "remote branch")

            result.add(note="%s can be cleaned" % cleanable)

        return result
Exemple #2
0
def replay(existing, tokens, samples):
    """Rerun samples and compare them with their current baseline"""
    kinds = []
    if tokens:
        kinds.append(TestSamples.K_TOKEN)

    skipped = 0
    for sample in samples:
        report = sample.replay(*kinds)
        if report is runez.UNSET:
            skipped += 1
            report = None if existing else " %s" % runez.yellow("skipped")

        elif report:
            report = "\n%s" % "\n".join("  %s" % s
                                        for s in report.splitlines())

        else:
            report = " %s" % runez.green("OK")

        if report is not None:
            print("** %s:%s" % (runez.bold(sample.name), report))

    if skipped:
        print(runez.dim("-- %s skipped" % runez.plural(skipped, "sample")))
Exemple #3
0
    def clean_samples(cls, verbose=False):
        cleanable = []
        for root, dirs, files in os.walk(cls.SAMPLE_FOLDER):
            if not dirs and not files:
                cleanable.append(root)

            if os.path.basename(root).startswith("_xpct-"):
                for fname in files:
                    ypath = os.path.dirname(root)
                    ypath = os.path.join(ypath,
                                         "%s.yml" % runez.basename(fname))
                    if not os.path.isfile(ypath):
                        # Delete _xpct-* files that correspond to moved samples
                        cleanable.append(os.path.join(root, fname))

        if not cleanable:
            if verbose:
                print("No cleanable _xpct- files found")

            return

        for path in cleanable:
            runez.delete(path, logger=logging.info)

        for root, dirs, files in os.walk(cls.SAMPLE_FOLDER):
            if not dirs and not files:
                cleanable.append(root)
                runez.delete(root, logger=logging.info)

        print("%s cleaned" % runez.plural(cleanable, "file"))
Exemple #4
0
def diff(compact, untyped, tokens, implementations, samples):
    """Compare deserialization of 2 implementations"""
    stringify = runez.stringified if untyped else decode
    if compact is None:
        compact = len(samples) > 1

    with runez.TempFolder():
        generated_files = []
        for sample in samples:
            generated_files.append([sample])
            for impl in implementations:
                assert isinstance(impl, Implementation)
                data = impl.get_outcome(sample, tokens=tokens)
                rep = TestSettings.represented(data,
                                               size=None,
                                               stringify=stringify,
                                               dt=simplified_date)
                fname = "%s-%s.txt" % (impl.name, sample.basename)
                generated_files[-1].extend([fname, rep])
                if not compact:
                    with open(fname, "w") as fh:
                        fh.write(rep)
                        if not rep.endswith("\n"):
                            fh.write("\n")

        matches = 0
        failed = 0
        differ = 0
        for sample, n1, r1, n2, r2 in generated_files:
            if isinstance(r1, dict) and isinstance(
                    r2, dict) and r1.get("_error") and r2.get("_error"):
                matches += 1
                failed += 1
                print("%s: both failed" % sample)

            elif r1 == r2:
                matches += 1
                print("%s: OK" % sample)

            else:
                differ += 1
                if compact:
                    print("%s: differ" % sample)

        if not compact:
            for sample, n1, r1, n2, r2 in generated_files:
                if r1 != r2:
                    r = runez.run("diff", "-br", "-U1", n1, n2, fatal=None)
                    print("========  %s  ========" % sample)
                    print(r.full_output)
                    print()

        message = [
            runez.plural(samples, "sample"),
            TestSettings.colored_if_meaningful(matches, "match", runez.green),
            TestSettings.colored_if_meaningful(differ, "differ", runez.orange),
            TestSettings.colored_if_meaningful(failed, "failed", runez.red),
        ]
        print("\n%s" % ", ".join(message))
Exemple #5
0
        def _callback(_ctx, _param, value):
            if not value:
                return None

            impls = ImplementationCollection(value, default=default)
            if impls.unknown:
                raise click.BadParameter("Unknown implementation(s): %s" %
                                         ", ".join(impls.unknown))

            if count and len(impls) != count:
                if count == 1:
                    raise click.BadParameter("Need exactly 1 implementation")

                raise click.BadParameter("Need exactly %s" %
                                         runez.plural(count, "implementation"))

            if count == 1:
                return impls.selected[0]

            return impls
Exemple #6
0
    def option(cls, default="zyaml,ruamel", count=None, **kwargs):
        """
        Args:
            default (str | None): Default implementation(s) to use
            count (int | None): Optional: exact number of implementations that have to specified
            **kwargs: Passed-through to click
        """
        kwargs["default"] = default

        def _callback(_ctx, _param, value):
            if not value:
                return None

            impls = ImplementationCollection(value, default=default)
            if impls.unknown:
                raise click.BadParameter("Unknown implementation(s): %s" %
                                         ", ".join(impls.unknown))

            if count and len(impls) != count:
                if count == 1:
                    raise click.BadParameter("Need exactly 1 implementation")

                raise click.BadParameter("Need exactly %s" %
                                         runez.plural(count, "implementation"))

            if count == 1:
                return impls.selected[0]

            return impls

        metavar = "I1,..."
        hlp = "Implementation(s)"
        if count:
            hlp = runez.plural(count, "implementation")
            metavar = ",".join("I%s" % (i + 1) for i in range(count))

        kwargs.setdefault("help", "%s to use" % hlp)
        kwargs.setdefault("show_default", True)
        kwargs.setdefault("metavar", metavar)
        name = "implementation" if count == 1 else "implementations"
        return click.option(name, "-i", callback=_callback, **kwargs)
Exemple #7
0
    def freshness(self):
        """Short freshness overview"""
        result = []
        if self.report._problem:
            result.append(runez.red(" ".join(self.report._problem)))

        if self.modified:
            result.append(runez.red(runez.plural(self.modified, "diff")))

        if self.untracked:
            result.append(runez.orange("%s untracked" % len(self.untracked)))

        if self.report._note:
            result.append(runez.purple(" ".join(self.report._note)))

        if not self.report._problem and not self.report._note and self._parent.age is not None:
            message = "up to date"
            if self._parent.age > FRESHNESS_THRESHOLD:
                message += "*"

            result.append(runez.teal(message))

        return ", ".join(result)
Exemple #8
0
        def _callback(_ctx, _param, value):
            if not value:
                value = default

            if count == 1 and hasattr(value,
                                      "endswith") and not value.endswith("."):
                value += "."

            if not value:
                raise click.BadParameter("No samples specified")

            s = cls.get_samples(value)
            if not s:
                raise click.BadParameter("No samples match %s" % value)

            if count and count != len(s):
                raise click.BadParameter(
                    "Need exactly %s, filter yielded %s" %
                    (runez.plural(count, "sample"), len(s)))

            if count == 1:
                return s[0]

            return s
Exemple #9
0
def test_plural():
    assert runez.plural(None) == "no values"
    assert runez.plural(None, None) == "no values"
    assert runez.plural("chair") == "chairs"
    assert runez.plural(None, "walker") == "no walkers"

    assert runez.plural(None) == "no values"
    assert runez.plural(1) == "1 value"
    assert runez.plural("foo", "character") == "3 characters"

    assert runez.plural(2, "match") == "2 matches"
    assert runez.plural(0, "dish") == "0 dishes"
    assert runez.plural(5, "goth") == "5 goths"
    assert runez.plural(7, "diff") == "7 diffs"

    assert runez.plural(1, "penny") == "1 penny"
    assert runez.plural(2, "penny") == "2 pennies"

    assert runez.plural([], "record") == "0 records"
    assert runez.plural([""], "record") == "1 record"

    assert runez.plural(1, "person") == "1 person"
    assert runez.plural(2, "person") == "2 people"

    assert runez.plural(2, "man") == "2 men"
    assert runez.plural(2, "woman") == "2 women"
    assert runez.plural(2, "status") == "2 statuses"

    assert runez.plural(0, "carrot") == "0 carrots"
    assert runez.plural(1, "carrot") == "1 carrot"
    assert runez.plural(20, "carrot") == "20 carrots"
    assert runez.plural(20000, "carrot") == "20K carrots"
    assert runez.plural(20000, "carrot", base=None) == "20K carrots"
    assert runez.plural(20000, "carrot", base=0) == "20000 carrots"
Exemple #10
0
def handle_single_clean(target, what):
    """
    :param GitCheckout target: Single checkout to clean
    :param str what: Operation
    """
    report = target.git.fetch()
    if report.has_problems:
        if what != "reset":
            what = "clean"

        print(
            target.header(
                GitRunReport(report).add(problem="<can't %s" % what)))
        runez.abort("")

    if what == "reset":
        return clean_reset(target)

    if what == "show":
        return clean_show(target)

    total_cleaned = 0
    print(target.header())

    if what in "remote all":
        if not target.git.remote_cleanable_branches:
            print("  No remote branches can be cleaned")

        else:
            total = len(target.git.remote_cleanable_branches)
            cleaned = 0
            for branch in target.git.remote_cleanable_branches:
                remote, _, name = branch.partition("/")
                if not remote and name:
                    raise Exception("Unknown branch spec '%s'" % branch)

                if run_git(target, False, "branch", "--delete", "--remotes",
                           branch):
                    cleaned += run_git(target, False, "push", "--delete",
                                       remote, name)

            total_cleaned += cleaned
            if cleaned == total:
                print("%s cleaned" % runez.plural(cleaned, "remote branch"))

            else:
                print("%s/%s remote branches cleaned" % (cleaned, total))

            target.git.reset_cached_properties()
            if what == "all":
                # Fetch to update remote branches (and correctly detect new dangling local)
                target.git.fetch()

    if what in "local all":
        if not target.git.local_cleanable_branches:
            print("  No local branches can be cleaned")

        else:
            total = len(target.git.local_cleanable_branches)
            cleaned = 0
            for branch in target.git.local_cleanable_branches:
                if branch == target.git.branches.current:
                    fallback = target.git.fallback_branch()
                    if not fallback:
                        print(
                            "Skipping branch '%s', can't determine fallback branch"
                            % target.git.branches.current)
                        continue

                    run_git(target, True, "checkout", fallback)
                    run_git(target, True, "pull")

                cleaned += run_git(target, False, "branch", "--delete", branch)

            total_cleaned += cleaned
            if cleaned == total:
                print(
                    runez.bold("%s cleaned" %
                               runez.plural(cleaned, "local branch")))

            else:
                print(
                    runez.orange("%s/%s local branches cleaned" %
                                 (cleaned, total)))

            target.git.reset_cached_properties()

    if total_cleaned:
        print(target.header())