Coverage for python/lsst/sconsUtils/vcs/svn.py: 7%
87 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-09 02:55 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-09 02:55 -0800
1"""A simple python interface to svn, using `os.popen`.
3If ever we want to do anything clever, we should use one of
4the supported svn/python packages
5"""
6import os
7import re
8import sys
11def isSvnFile(file):
12 """Is file under svn control?"""
14 return re.search(r"is not a working copy",
15 "".join(os.popen("svn info %s 2>&1" % file).readlines())) is None
18def getInfo(file="."):
19 """Return a dictionary of all the information returned by "svn info" for
20 the specified file"""
22 if not isSvnFile(file):
23 raise RuntimeError("%s is not under svn control" % file)
25 infoList = os.popen("svn info %s" % file).readlines()
27 info = {}
28 for line in infoList:
29 mat = re.search(r"^([^:]+)\s*:\s*(.*)", line)
30 if mat:
31 info[mat.group(1)] = mat.group(2)
33 return info
36def isTrunk(file="."):
37 """Is file on the trunk?"""
39 info = getInfo(file)
41 return re.search(r"/trunk($|/)", info["URL"]) is not None
44def revision(file=None, lastChanged=False):
45 """Return file's Revision as a string.
47 If file is None return
48 a tuple (oldestRevision, youngestRevision, flags) as reported
49 by svnversion; e.g. (4123, 4168, ("M", "S")) (oldestRevision
50 and youngestRevision may be equal)
51 """
53 if file:
54 info = getInfo(file)
56 if lastChanged:
57 return info["Last Changed Rev"]
58 else:
59 return info["Revision"]
61 if lastChanged:
62 raise RuntimeError("lastChanged makes no sense if file is None")
64 res = os.popen("svnversion . 2>&1").readline()
66 if res == "exported\n":
67 raise RuntimeError("No svn revision information is available")
69 versionRe = r"^(?P<oldest>\d+)(:(?P<youngest>\d+))?(?P<flags>[MS]*)"
70 mat = re.search(versionRe, res)
71 if mat:
72 matches = mat.groupdict()
73 if not matches["youngest"]:
74 matches["youngest"] = matches["oldest"]
75 # OK, we have only one revision present. Find the newest revision
76 # that actually changed anything in this product and ignore
77 # "oldest" (#522)
78 res = os.popen("svnversion --committed . 2>&1").readline()
79 mat = re.search(versionRe, res)
80 if mat:
81 matches = mat.groupdict()
82 return matches["youngest"], matches["youngest"], tuple(matches["flags"])
84 return matches["oldest"], matches["youngest"], tuple(matches["flags"])
86 raise RuntimeError("svnversion returned unexpected result \"%s\"" % res[:-1])
89def guessVersionName(HeadURL):
90 """Guess a version name given a HeadURL."""
92 if re.search(r"/trunk$", HeadURL):
93 versionName = ""
94 elif re.search(r"/branches/(.+)$", HeadURL):
95 versionName = "branch_%s+" % re.search(r"/branches/(.+)$", HeadURL).group(1)
96 elif re.search(r"/tags/(.+)$", HeadURL):
97 versionName = "%s" % re.search(r"/tags/(.*)$", HeadURL).group(1)
99 return versionName # no need for a "+svnXXXX"
100 elif re.search(r"/tickets/(\d+)$", HeadURL):
101 versionName = "ticket_%s+" % re.search(r"/tickets/(\d+)$", HeadURL).group(1)
102 else:
103 print("Unable to guess versionName name from %s" % HeadURL, file=sys.stderr)
104 versionName = "unknown+"
106 try: # Try to lookup the svn versionName
107 (oldest, youngest, flags) = revision()
109 okVersion = True
110 if "M" in flags:
111 msg = "You are installing, but have unchecked in files"
112 okVersion = False
113 if "S" in flags:
114 msg = "You are installing, but have switched SVN URLs"
115 okVersion = False
116 if oldest != youngest:
117 msg = "You have a range of revisions in your tree (%s:%s); adopting %s" % \
118 (oldest, youngest, youngest)
119 okVersion = False
121 if not okVersion:
122 raise RuntimeError("Problem with determining svn revision: %s" % msg)
124 versionName += "svn" + youngest
125 except IOError:
126 return "unknown"
128 return versionName
131def parseVersionName(versionName):
132 """A callback that knows about the LSST convention that a tagname such as
133 ticket_374 means the top of ticket 374, and ticket_374+svn6021
134 means revision 6021 on ticket 374.
136 You may replace "ticket" with "branch" if you wish.
138 The "versionName" may actually be the directory part of a URL, and
139 ``.../(branches|tags|tickets)/tagname`` is also supported
140 """
142 mat = re.search(r"/(branche|tag|ticket)s/(\d+(?:\.\d+)*)(?:([-+])((svn)?(\d+)))?$", versionName)
143 if not mat:
144 mat = re.search(r"/(branch|ticket)_(\d+)(?:([-+])svn(\d+))?$", versionName)
145 if mat:
146 type = mat.group(1)
147 if type == "branches":
148 type = "branch"
149 ticket = mat.group(2)
150 pm = mat.group(3) # + or -
151 revision = mat.group(4)
152 if revision:
153 revision = re.sub("^svn", "", revision)
155 return (type, ticket, revision, pm)
157 return (None, None, None, None)