Coverage for python/lsst/sconsUtils/vcs/svn.py: 7%

87 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-01 00:45 -0700

1"""A simple python interface to svn, using `os.popen`. 

2 

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 

9 

10 

11def isSvnFile(file): 

12 """Is file under svn control?""" 

13 

14 return re.search(r"is not a working copy", 

15 "".join(os.popen("svn info %s 2>&1" % file).readlines())) is None 

16 

17 

18def getInfo(file="."): 

19 """Return a dictionary of all the information returned by "svn info" for 

20 the specified file""" 

21 

22 if not isSvnFile(file): 

23 raise RuntimeError("%s is not under svn control" % file) 

24 

25 infoList = os.popen("svn info %s" % file).readlines() 

26 

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) 

32 

33 return info 

34 

35 

36def isTrunk(file="."): 

37 """Is file on the trunk?""" 

38 

39 info = getInfo(file) 

40 

41 return re.search(r"/trunk($|/)", info["URL"]) is not None 

42 

43 

44def revision(file=None, lastChanged=False): 

45 """Return file's Revision as a string. 

46 

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 """ 

52 

53 if file: 

54 info = getInfo(file) 

55 

56 if lastChanged: 

57 return info["Last Changed Rev"] 

58 else: 

59 return info["Revision"] 

60 

61 if lastChanged: 

62 raise RuntimeError("lastChanged makes no sense if file is None") 

63 

64 res = os.popen("svnversion . 2>&1").readline() 

65 

66 if res == "exported\n": 

67 raise RuntimeError("No svn revision information is available") 

68 

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"]) 

83 

84 return matches["oldest"], matches["youngest"], tuple(matches["flags"]) 

85 

86 raise RuntimeError("svnversion returned unexpected result \"%s\"" % res[:-1]) 

87 

88 

89def guessVersionName(HeadURL): 

90 """Guess a version name given a HeadURL.""" 

91 

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) 

98 

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+" 

105 

106 try: # Try to lookup the svn versionName 

107 (oldest, youngest, flags) = revision() 

108 

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 

120 

121 if not okVersion: 

122 raise RuntimeError("Problem with determining svn revision: %s" % msg) 

123 

124 versionName += "svn" + youngest 

125 except IOError: 

126 return "unknown" 

127 

128 return versionName 

129 

130 

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. 

135 

136 You may replace "ticket" with "branch" if you wish. 

137 

138 The "versionName" may actually be the directory part of a URL, and 

139 ``.../(branches|tags|tickets)/tagname`` is also supported 

140 """ 

141 

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) 

154 

155 return (type, ticket, revision, pm) 

156 

157 return (None, None, None, None)