26
27 # Quick configuration: Add the following to your ~/.hgrc:
28 #
29 # [extensions]
30 # jcheck = /path/to/jcheck.py
31 #
32 # # Omit these lines if you use Mercurial Queues
33 # [hooks]
34 # pretxnchangegroup.jcheck = python:jcheck.hook
35 # pretxncommit.jcheck = python:jcheck.hook
36 #
37 # # Include this if you use the (deprecated) Mercurial "fetch" extension
38 # [defaults]
39 # fetch = -m Merge
40 #
41 # For more information: http://openjdk.java.net/projects/code-tools/jcheck/
42
43 _version = "@VERSION@"
44 _date = "@DATE@"
45
46 import sys, os, re, urllib, urllib2, json
47 from mercurial.node import *
48 from mercurial import cmdutil, context, error, patch, templater, util, utils
49 try:
50 # Mercurial 4.3 and higher
51 from mercurial import registrar
52 except ImportError:
53 registrar = {}
54 pass
55
56 # Abort() was moved/copied from util to error in hg 1.3 and was removed from
57 # util in 4.6.
58 error_Abort = None
59 if hasattr(error, 'Abort'):
60 error_Abort = error.Abort
61 else:
62 error_Abort = util.Abort
63
64 # date-related utils moved to utils/dateutil in early 2018 (hg 4.7)
65 dateutil_datestr = None
66 if hasattr(utils, 'dateutil'):
661 fx = ctx.filectx(f)
662 if normext_re.match(f) and not self.whitespace_lax:
663 data = fx.data()
664 if "\t" in data or "\r" in data or " \n" in data:
665 m = badwhite_re.search(data)
666 if m:
667 ln = data.count("\n", 0, m.start()) + 1
668 self.error(ctx, "%s:%d: %s" % (f, ln, badwhite_what(m)))
669 ## check_file_header(self, fx, data)
670 flags = fx.manifest().flags(f)
671 if 'x' in flags:
672 self.error(ctx, "%s: Executable files not permitted" % f)
673 if 'l' in flags:
674 self.error(ctx, "%s: Symbolic links not permitted" % f)
675
676 def c_03_hash(self, ctx):
677 hash = hex(ctx.node())
678 if hash in self.blacklist:
679 self.error(ctx, "Blacklisted changeset: " + hash)
680
681 def check(self, node):
682 self.summarized = False
683 self.cs_bugids = [ ]
684 self.cs_author = None
685 self.cs_reviewers = [ ]
686 self.cs_contributor = None
687 ctx = context.changectx(self.repo, node)
688 self.ui.note(oneline(ctx))
689 if hex(node) in changeset_whitelist:
690 self.ui.note("%s in whitelist; skipping\n" % hex(node))
691 return Pass
692 for c in self.checks:
693 cf = checker.__dict__[c]
694 cf(self, ctx)
695 return self.rv
696
697 def check_repo(self):
698
699 if not self.tags_lax:
700 ts = self.repo.tags().keys()
701 ignoredtypes = ['local']
702 for t in ts:
703 if not tag_re.match(t) and not self.tagtype(t) in ignoredtypes:
704 self.error(None,
705 "Illegal tag name: %s" % t)
706
724
725 def hook(ui, repo, hooktype, node=None, source=None, **opts):
726 ui.debug("jcheck: node %s, source %s, args %s\n" % (node, source, opts))
727 repocompat(repo)
728 if not repo.local():
729 raise error_Abort("repository '%s' is not local" % repo.path)
730 if not os.path.exists(os.path.join(repo.root, ".jcheck")):
731 ui.note("jcheck not enabled (no .jcheck in repository root); skipping\n")
732 return Pass
733 strict = opts.has_key("strict") and opts["strict"]
734 lax = opts.has_key("lax") and opts["lax"]
735 if strict:
736 lax = False
737 ch = checker(ui, repo, strict, lax)
738 ch.check_repo()
739 firstnode = bin(node)
740 start = repo.changelog.rev(firstnode)
741 end = (hasattr(repo.changelog, 'count') and repo.changelog.count() or
742 len(repo.changelog))
743 for rev in xrange(start, end):
744 ch.check(repo.changelog.node(rev))
745 if ch.rv == Fail:
746 ui.status("\n")
747 return ch.rv
748
749
750 # Run this hook in repository gates
751
752 def strict_hook(ui, repo, hooktype, node=None, source=None, **opts):
753 opts["strict"] = True
754 return hook(ui, repo, hooktype, node, source, **opts)
755
756 # From Mercurial 1.9, the preferred way to define commands is using the @command
757 # decorator. If this isn't available, fallback on a simple local implementation
758 # that just adds the data to the cmdtable.
759 cmdtable = {}
760 if hasattr(registrar, 'command'):
761 command = registrar.command(cmdtable)
762 elif hasattr(cmdutil, 'command'):
763 command = cmdutil.command(cmdtable)
764 else:
781 repocompat(repo)
782 if not repo.local():
783 raise error_Abort("repository '%s' is not local" % repo.path)
784 if not os.path.exists(os.path.join(repo.root, ".jcheck")):
785 ui.status("jcheck not enabled (no .jcheck in repository root)\n")
786 return Pass
787 if len(opts["rev"]) == 0:
788 opts["rev"] = ["tip"]
789
790 strict = opts.has_key("strict") and opts["strict"]
791 lax = opts.has_key("lax") and opts["lax"]
792 if strict:
793 lax = False
794 ch = checker(ui, repo, strict, lax)
795 ch.check_repo()
796
797 try:
798 nop = lambda c, fns: None
799 iter = cmdutil.walkchangerevs(repo, _matchall(repo), opts, nop)
800 for ctx in iter:
801 ch.check(ctx.node())
802 except (AttributeError, TypeError):
803 # AttributeError: matchall does not exist in hg < 1.1
804 # TypeError: walkchangerevs args differ in hg <= 1.3.1
805 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
806 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, [], get, opts)
807 if ui.debugflag:
808 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
809 for st, rev, fns in changeiter:
810 if st == 'add':
811 node = repo.changelog.node(rev)
812 if ui.debugflag:
813 displayer.show(rev, node, copies=False)
814 ch.check(node)
815 elif st == 'iter':
816 if ui.debugflag:
817 displayer.flush(rev)
818
819 if ch.rv == Fail:
820 ui.status("\n")
821 return ch.rv
822
823 # This is invoked on servers to check pushkeys; it's not needed on clients.
824 def prepushkey(ui, repo, hooktype, namespace, key, old=None, new=None, **opts):
825 if namespace == 'phases':
826 return Pass
827 ui.write_err('ERROR: pushing keys (%s) is disabled\n' % namespace)
828 return Fail
|
26
27 # Quick configuration: Add the following to your ~/.hgrc:
28 #
29 # [extensions]
30 # jcheck = /path/to/jcheck.py
31 #
32 # # Omit these lines if you use Mercurial Queues
33 # [hooks]
34 # pretxnchangegroup.jcheck = python:jcheck.hook
35 # pretxncommit.jcheck = python:jcheck.hook
36 #
37 # # Include this if you use the (deprecated) Mercurial "fetch" extension
38 # [defaults]
39 # fetch = -m Merge
40 #
41 # For more information: http://openjdk.java.net/projects/code-tools/jcheck/
42
43 _version = "@VERSION@"
44 _date = "@DATE@"
45
46 import sys, os, re, urllib, urllib2, json, inspect
47 from mercurial.node import *
48 from mercurial import cmdutil, context, error, patch, templater, util, utils
49 try:
50 # Mercurial 4.3 and higher
51 from mercurial import registrar
52 except ImportError:
53 registrar = {}
54 pass
55
56 # Abort() was moved/copied from util to error in hg 1.3 and was removed from
57 # util in 4.6.
58 error_Abort = None
59 if hasattr(error, 'Abort'):
60 error_Abort = error.Abort
61 else:
62 error_Abort = util.Abort
63
64 # date-related utils moved to utils/dateutil in early 2018 (hg 4.7)
65 dateutil_datestr = None
66 if hasattr(utils, 'dateutil'):
661 fx = ctx.filectx(f)
662 if normext_re.match(f) and not self.whitespace_lax:
663 data = fx.data()
664 if "\t" in data or "\r" in data or " \n" in data:
665 m = badwhite_re.search(data)
666 if m:
667 ln = data.count("\n", 0, m.start()) + 1
668 self.error(ctx, "%s:%d: %s" % (f, ln, badwhite_what(m)))
669 ## check_file_header(self, fx, data)
670 flags = fx.manifest().flags(f)
671 if 'x' in flags:
672 self.error(ctx, "%s: Executable files not permitted" % f)
673 if 'l' in flags:
674 self.error(ctx, "%s: Symbolic links not permitted" % f)
675
676 def c_03_hash(self, ctx):
677 hash = hex(ctx.node())
678 if hash in self.blacklist:
679 self.error(ctx, "Blacklisted changeset: " + hash)
680
681 def check(self, rev, node):
682 self.summarized = False
683 self.cs_bugids = [ ]
684 self.cs_author = None
685 self.cs_reviewers = [ ]
686 self.cs_contributor = None
687 if len(inspect.getargspec(context.changectx.__init__).args) == 4:
688 ctx = context.changectx(self.repo, rev, node)
689 else:
690 ctx = context.changectx(self.repo, node)
691 self.ui.note(oneline(ctx))
692 if hex(node) in changeset_whitelist:
693 self.ui.note("%s in whitelist; skipping\n" % hex(node))
694 return Pass
695 for c in self.checks:
696 cf = checker.__dict__[c]
697 cf(self, ctx)
698 return self.rv
699
700 def check_repo(self):
701
702 if not self.tags_lax:
703 ts = self.repo.tags().keys()
704 ignoredtypes = ['local']
705 for t in ts:
706 if not tag_re.match(t) and not self.tagtype(t) in ignoredtypes:
707 self.error(None,
708 "Illegal tag name: %s" % t)
709
727
728 def hook(ui, repo, hooktype, node=None, source=None, **opts):
729 ui.debug("jcheck: node %s, source %s, args %s\n" % (node, source, opts))
730 repocompat(repo)
731 if not repo.local():
732 raise error_Abort("repository '%s' is not local" % repo.path)
733 if not os.path.exists(os.path.join(repo.root, ".jcheck")):
734 ui.note("jcheck not enabled (no .jcheck in repository root); skipping\n")
735 return Pass
736 strict = opts.has_key("strict") and opts["strict"]
737 lax = opts.has_key("lax") and opts["lax"]
738 if strict:
739 lax = False
740 ch = checker(ui, repo, strict, lax)
741 ch.check_repo()
742 firstnode = bin(node)
743 start = repo.changelog.rev(firstnode)
744 end = (hasattr(repo.changelog, 'count') and repo.changelog.count() or
745 len(repo.changelog))
746 for rev in xrange(start, end):
747 ch.check(rev, repo.changelog.node(rev))
748 if ch.rv == Fail:
749 ui.status("\n")
750 return ch.rv
751
752
753 # Run this hook in repository gates
754
755 def strict_hook(ui, repo, hooktype, node=None, source=None, **opts):
756 opts["strict"] = True
757 return hook(ui, repo, hooktype, node, source, **opts)
758
759 # From Mercurial 1.9, the preferred way to define commands is using the @command
760 # decorator. If this isn't available, fallback on a simple local implementation
761 # that just adds the data to the cmdtable.
762 cmdtable = {}
763 if hasattr(registrar, 'command'):
764 command = registrar.command(cmdtable)
765 elif hasattr(cmdutil, 'command'):
766 command = cmdutil.command(cmdtable)
767 else:
784 repocompat(repo)
785 if not repo.local():
786 raise error_Abort("repository '%s' is not local" % repo.path)
787 if not os.path.exists(os.path.join(repo.root, ".jcheck")):
788 ui.status("jcheck not enabled (no .jcheck in repository root)\n")
789 return Pass
790 if len(opts["rev"]) == 0:
791 opts["rev"] = ["tip"]
792
793 strict = opts.has_key("strict") and opts["strict"]
794 lax = opts.has_key("lax") and opts["lax"]
795 if strict:
796 lax = False
797 ch = checker(ui, repo, strict, lax)
798 ch.check_repo()
799
800 try:
801 nop = lambda c, fns: None
802 iter = cmdutil.walkchangerevs(repo, _matchall(repo), opts, nop)
803 for ctx in iter:
804 ch.check(ctx, ctx.node())
805 except (AttributeError, TypeError):
806 # AttributeError: matchall does not exist in hg < 1.1
807 # TypeError: walkchangerevs args differ in hg <= 1.3.1
808 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
809 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, [], get, opts)
810 if ui.debugflag:
811 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
812 for st, rev, fns in changeiter:
813 if st == 'add':
814 node = repo.changelog.node(rev)
815 if ui.debugflag:
816 displayer.show(rev, node, copies=False)
817 ch.check(rev, node)
818 elif st == 'iter':
819 if ui.debugflag:
820 displayer.flush(rev)
821
822 if ch.rv == Fail:
823 ui.status("\n")
824 return ch.rv
825
826 # This is invoked on servers to check pushkeys; it's not needed on clients.
827 def prepushkey(ui, repo, hooktype, namespace, key, old=None, new=None, **opts):
828 if namespace == 'phases':
829 return Pass
830 ui.write_err('ERROR: pushing keys (%s) is disabled\n' % namespace)
831 return Fail
|