Bug#677792: [NEW] please consider including vcs-lint tool in devscripts

Jon Dowland jmtd at debian.org
Sat Jun 16 19:16:00 UTC 2012


Package: devscripts
Version: 2.11.7
Severity: wishlist
Tags: patch

Hi,

I've written a tool 'vcs-lint' (formerly 'mr-lint'[1]) for which I am looking
for a home.

It's not intended to be a purely Debian tool, but it's certainly what I use
it for most, and it has some Debian-specific functionality.

'mr' and 'moreutils' are places I've considered which are not appropriate.
Is devscripts a good fit?

I don't feel it deserves it's own package, but I'd quite like to get it into
wheezy if possible.

Latest version attached.

Sample usage/output:

    $ vcs-lint
    /home/jon/wd/bup: local branches not present in origin: fix-pythonoptimize,debian-proposed,debian-dump-s390
    /home/jon/wd/bup: commits to local branch debian have not been pushed to origin
    /home/jon/wd/bup: 6 missing upstream tags: 0.14a, 0.17b, 0.20, 0.22a, 0.24b, 0.25~git2011.11.04
    /home/jon/wd/bup: 8 missing debian tags: debian/0.17b-1, debian/0.20-2, debian/0.22a-1, debian/0.25~git2011.11.04-1, debian/0.25~git2011.11.04-2, debian/0.25~git2011.11.04-3, debian/0.25~git2011.11.04-4, debian/0.25~git2011.11.04-5

Please let me know what you think. Thanks!


[1] http://jmtd.net/log/mr-lint/
-------------- next part --------------
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2011 ? Jon Dowland <jmtd at debian.org>
# Licensed under the GNU GPL version 2 or higher.
import sys, os, subprocess

def usage():
  print "usage: vcs-lint [ --verbose ]\n"+\
        "vcs-lint will inspect the current working directory."
  exit(0)

verbose = False
if "--verbose" in sys.argv:
  verbose = True
if "--help" in sys.argv:
  usage()

def debug(str):
  if verbose:
    print str

def run(cmd):
  p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  output, errors = p.communicate()
  return output

# how many debian package versions are there?
def get_package_versions():
  output = run(['dpkg-parsechangelog', '--format', 'rfc822' , '--all'])
  return set([ x[9:] for x in output.split("\n") if "Version: " == x[:9] ])

def get_git_tags():
  tags = set(filter(lambda x: x, run(["git", "tag", "-l"]).split("\n")))
  debug("\ttags: %s" % ", ".join(sorted(tags)))
  return tags

# git tag checking
# there should be a tag 'upstream/$uv' and 'debian/$uv-$dv' for every version $v = "$uv-$dv"
def missing_tags(repo,ttype,missing):
  if missing:
    print "%s: %d missing %s tags: %s" % (repo, len(missing), ttype, ", ".join(sorted(missing)))

def check_package_versions_tagged(repo,git_tags,package_versions):
  debug("\tcheck_package_versions_tagged")
  if not filter(lambda x: x.find("-") >= 0, package_versions): # native package
    missing_tags(repo, "package version", package_versions - git_tags)
  else:
    prefix = ""
    if filter(lambda x: x.find("upstream") == 0, git_tags):
      prefix = "upstream/"
    missing_tags(repo, "upstream",
      set( [ "%s%s"%(prefix,x[:x.find("-")]) for x in package_versions if x.find("-") >= 0 ]) - git_tags)
    missing_tags(repo, "debian", set(["debian/%s"%x for x in package_versions]) - git_tags)

# git branch checking
def get_git_branches(prefix):
  branches = run(["git", "for-each-ref", '--format=%(refname)', prefix]).split("\n")
  return set([ x[len(prefix):] for x in branches if prefix == x[:len(prefix)] ])
def get_git_local_branches():
  return get_git_branches("refs/heads/")
def get_git_origin_branches():
  return get_git_branches("refs/remotes/origin/")

# are all local branches represented at origin?
def check_local_branches_at_origin(repo,origin_branches,local_branches):
  debug("\tcheck_local_branches_at_origin")
  debug("\t\tlocal branches: %s" % ",".join(local_branches))
  debug("\t\torigin branches: %s" % ",".join(origin_branches))
  missing = local_branches - origin_branches
  if missing:
    print "%s: local branches not present in origin: %s" % (repo, ",".join(missing))

# is branch x ahead of origin/x?
def local_branch_ahead_of_origin(branch):
  debug("\tlocal_branch_ahead_of_origin")
  return bool(run(["git", "rev-list", branch, "^remotes/origin/%s" % branch, "--"]))

# do all local branches match origin branches of the same name?
def check_branches_match_origin (repo,origin_branches,local_branches):
  debug("\tcheck_branches_match_origin")
  for b in local_branches & origin_branches:
    debug("\t\t%s" % b)
    l = run(["git", "for-each-ref",'--format="%(objectname)', "refs/remotes/origin/%s" % b])
    r = run(["git", "for-each-ref",'--format="%(objectname)', "refs/heads/%s" % b])
    if r != l:
      if local_branch_ahead_of_origin(b):
        print "%s: commits to local branch %s have not been pushed to origin" % (repo,b)
      else:
        print "%s: local branch %s does not match origin branch %s" % (repo,b,b)

def is_gitrepo():
  return os.path.isdir(".git")

def repo_is_debian_package():
  return os.path.isfile("debian/changelog")

repo = os.getcwd()
if not is_gitrepo:
  sys.stderr.write("%s: not a git repository\n" % repo)
  exit(1)

origin_branches = get_git_origin_branches()
local_branches = get_git_local_branches()
check_local_branches_at_origin(repo, origin_branches, local_branches)
check_branches_match_origin(repo, origin_branches, local_branches)

if repo_is_debian_package:
  package_versions = get_package_versions()
  git_tags = get_git_tags()
  check_package_versions_tagged(repo, git_tags, package_versions)


More information about the devscripts-devel mailing list