Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(393)

Side by Side Diff: gcl.py

Issue 115615: Add support to cache arbitrary file. Also rename repository_root to REPOSITORY_ROOT. (Closed)
Patch Set: Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/gcl_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 # 5 #
6 # Wrapper script around Rietveld's upload.py that groups files into 6 # Wrapper script around Rietveld's upload.py that groups files into
7 # changelists. 7 # changelists.
8 8
9 import getpass 9 import getpass
10 import os 10 import os
11 import random 11 import random
12 import re 12 import re
13 import shutil
13 import string 14 import string
14 import subprocess 15 import subprocess
15 import sys 16 import sys
16 import tempfile 17 import tempfile
17 import upload 18 import upload
18 import urllib2 19 import urllib2
19 import xml.dom.minidom 20 import xml.dom.minidom
20 21
21 # gcl now depends on gclient. 22 # gcl now depends on gclient.
22 import gclient 23 import gclient
23 24
24 __version__ = '1.0' 25 __version__ = '1.1.1'
25 26
26 27
27 CODEREVIEW_SETTINGS = { 28 CODEREVIEW_SETTINGS = {
28 # Default values. 29 # Default values.
29 "CODE_REVIEW_SERVER": "codereview.chromium.org", 30 "CODE_REVIEW_SERVER": "codereview.chromium.org",
30 "CC_LIST": "chromium-reviews@googlegroups.com", 31 "CC_LIST": "chromium-reviews@googlegroups.com",
31 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", 32 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=",
32 } 33 }
33 34
34 # globals that store the root of the current repository and the directory where 35 # globals that store the root of the current repository and the directory where
35 # we store information about changelists. 36 # we store information about changelists.
36 repository_root = "" 37 REPOSITORY_ROOT = ""
37 gcl_info_dir = ""
38 38
39 # Filename where we store repository specific information for gcl. 39 # Filename where we store repository specific information for gcl.
40 CODEREVIEW_SETTINGS_FILE = "codereview.settings" 40 CODEREVIEW_SETTINGS_FILE = "codereview.settings"
41 41
42 # Warning message when the change appears to be missing tests. 42 # Warning message when the change appears to be missing tests.
43 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" 43 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!"
44 44
45 # Caches whether we read the codereview.settings file yet or not. 45 # Global cache of files cached in GetCacheDir().
46 read_gcl_info = False 46 FILES_CACHE = {}
47 47
48 48
49 ### SVN Functions 49 ### SVN Functions
50 50
51 def IsSVNMoved(filename): 51 def IsSVNMoved(filename):
52 """Determine if a file has been added through svn mv""" 52 """Determine if a file has been added through svn mv"""
53 info = gclient.CaptureSVNInfo(filename) 53 info = gclient.CaptureSVNInfo(filename)
54 return (info.get('Copied From URL') and 54 return (info.get('Copied From URL') and
55 info.get('Copied From Rev') and 55 info.get('Copied From Rev') and
56 info.get('Schedule') == 'add') 56 info.get('Schedule') == 'add')
(...skipping 27 matching lines...) Expand all
84 """ 84 """
85 return [item[1] for item in gclient.CaptureSVNStatus(extra_args) 85 return [item[1] for item in gclient.CaptureSVNStatus(extra_args)
86 if item[0][0] == '?'] 86 if item[0][0] == '?']
87 87
88 88
89 def GetRepositoryRoot(): 89 def GetRepositoryRoot():
90 """Returns the top level directory of the current repository. 90 """Returns the top level directory of the current repository.
91 91
92 The directory is returned as an absolute path. 92 The directory is returned as an absolute path.
93 """ 93 """
94 global repository_root 94 global REPOSITORY_ROOT
95 if not repository_root: 95 if not REPOSITORY_ROOT:
96 infos = gclient.CaptureSVNInfo(os.getcwd(), print_error=False) 96 infos = gclient.CaptureSVNInfo(os.getcwd(), print_error=False)
97 cur_dir_repo_root = infos.get("Repository Root") 97 cur_dir_repo_root = infos.get("Repository Root")
98 if not cur_dir_repo_root: 98 if not cur_dir_repo_root:
99 raise Exception("gcl run outside of repository") 99 raise Exception("gcl run outside of repository")
100 100
101 repository_root = os.getcwd() 101 REPOSITORY_ROOT = os.getcwd()
102 while True: 102 while True:
103 parent = os.path.dirname(repository_root) 103 parent = os.path.dirname(REPOSITORY_ROOT)
104 if (gclient.CaptureSVNInfo(parent, print_error=False).get( 104 if (gclient.CaptureSVNInfo(parent, print_error=False).get(
105 "Repository Root") != cur_dir_repo_root): 105 "Repository Root") != cur_dir_repo_root):
106 break 106 break
107 repository_root = parent 107 REPOSITORY_ROOT = parent
108 return repository_root 108 return REPOSITORY_ROOT
109 109
110 110
111 def GetInfoDir(): 111 def GetInfoDir():
112 """Returns the directory where gcl info files are stored.""" 112 """Returns the directory where gcl info files are stored."""
113 global gcl_info_dir 113 return os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info')
114 if not gcl_info_dir: 114
115 gcl_info_dir = os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info') 115
116 return gcl_info_dir 116 def GetChangesDir():
117 """Returns the directory where gcl change files are stored."""
118 return os.path.join(GetInfoDir(), 'changes')
119
120
121 def GetCacheDir():
122 """Returns the directory where gcl change files are stored."""
123 return os.path.join(GetInfoDir(), 'cache')
124
125
126 def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
127 """Retrieves a file from the repository and caches it in GetCacheDir() for
128 max_age seconds.
129
130 use_root: If False, look up the arborescence for the first match, otherwise go
131 directory to the root repository.
132
133 Note: The cache will be inconsistent if the same file is retrieved with both
134 use_root=True and use_root=False on the same file. Don't be stupid.
135 """
136 global FILES_CACHE
137 if filename not in FILES_CACHE:
138 # Don't try to look up twice.
139 FILES_CACHE[filename] = None
140 # First we check if we have a cached version.
141 cached_file = os.path.join(GetCacheDir(), filename)
142 if (not os.path.exists(cached_file) or
143 os.stat(cached_file).st_mtime > max_age):
144 dir_info = gclient.CaptureSVNInfo(".")
145 repo_root = dir_info["Repository Root"]
146 if use_root:
147 url_path = repo_root
148 else:
149 url_path = dir_info["URL"]
150 content = ""
151 while True:
152 # Look for the codereview.settings file at the current level.
153 svn_path = url_path + "/" + filename
154 content, rc = RunShellWithReturnCode(["svn", "cat", svn_path])
155 if not rc:
156 # Exit the loop if the file was found. Override content.
157 break
158 # Make sure to mark settings as empty if not found.
159 content = ""
160 if url_path == repo_root:
161 # Reached the root. Abandoning search.
162 break
163 # Go up one level to try again.
164 url_path = os.path.dirname(url_path)
165 # Write a cached version even if there isn't a file, so we don't try to
166 # fetch it each time.
167 WriteFile(cached_file, content)
168 else:
169 content = ReadFile(cached_settings_file)
170 # Keep the content cached in memory.
171 FILES_CACHE[filename] = content
172 return FILES_CACHE[filename]
117 173
118 174
119 def GetCodeReviewSetting(key): 175 def GetCodeReviewSetting(key):
120 """Returns a value for the given key for this repository.""" 176 """Returns a value for the given key for this repository."""
121 global read_gcl_info 177 # Use '__just_initialized' as a flag to determine if the settings were
122 if not read_gcl_info: 178 # already initialized.
123 read_gcl_info = True 179 global CODEREVIEW_SETTINGS
124 # First we check if we have a cached version. 180 if '__just_initialized' not in CODEREVIEW_SETTINGS:
125 cached_settings_file = os.path.join(GetInfoDir(), CODEREVIEW_SETTINGS_FILE) 181 for line in GetCachedFile(CODEREVIEW_SETTINGS_FILE).splitlines():
126 if (not os.path.exists(cached_settings_file) or
127 os.stat(cached_settings_file).st_mtime > 60*60*24*3):
128 dir_info = gclient.CaptureSVNInfo(".")
129 repo_root = dir_info["Repository Root"]
130 url_path = dir_info["URL"]
131 settings = ""
132 while True:
133 # Look for the codereview.settings file at the current level.
134 svn_path = url_path + "/" + CODEREVIEW_SETTINGS_FILE
135 settings, rc = RunShellWithReturnCode(["svn", "cat", svn_path])
136 if not rc:
137 # Exit the loop if the file was found.
138 break
139 # Make sure to mark settings as empty if not found.
140 settings = ""
141 if url_path == repo_root:
142 # Reached the root. Abandoning search.
143 break;
144 # Go up one level to try again.
145 url_path = os.path.dirname(url_path)
146
147 # Write a cached version even if there isn't a file, so we don't try to
148 # fetch it each time.
149 WriteFile(cached_settings_file, settings)
150
151 output = ReadFile(cached_settings_file)
152 for line in output.splitlines():
153 if not line or line.startswith("#"): 182 if not line or line.startswith("#"):
154 continue 183 continue
155 k, v = line.split(": ", 1) 184 k, v = line.split(": ", 1)
156 CODEREVIEW_SETTINGS[k] = v 185 CODEREVIEW_SETTINGS[k] = v
186 CODEREVIEW_SETTINGS.setdefault('__just_initialized', None)
157 return CODEREVIEW_SETTINGS.get(key, "") 187 return CODEREVIEW_SETTINGS.get(key, "")
158 188
159 189
160 def IsTreeOpen(): 190 def IsTreeOpen():
161 """Fetches the tree status and returns either True or False.""" 191 """Fetches the tree status and returns either True or False."""
162 url = GetCodeReviewSetting('STATUS') 192 url = GetCodeReviewSetting('STATUS')
163 status = "" 193 status = ""
164 if url: 194 if url:
165 status = urllib2.urlopen(url).read() 195 status = urllib2.urlopen(url).read()
166 return status.find('0') == -1 196 return status.find('0') == -1
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 # . 396 # .
367 # filepathn\n 397 # filepathn\n
368 # SEPARATOR\n 398 # SEPARATOR\n
369 # description 399 # description
370 400
371 401
372 def GetChangelistInfoFile(changename): 402 def GetChangelistInfoFile(changename):
373 """Returns the file that stores information about a changelist.""" 403 """Returns the file that stores information about a changelist."""
374 if not changename or re.search(r'[^\w-]', changename): 404 if not changename or re.search(r'[^\w-]', changename):
375 ErrorExit("Invalid changelist name: " + changename) 405 ErrorExit("Invalid changelist name: " + changename)
376 return os.path.join(GetInfoDir(), changename) 406 return os.path.join(GetChangesDir(), changename)
377 407
378 408
379 def LoadChangelistInfoForMultiple(changenames, fail_on_not_found=True, 409 def LoadChangelistInfoForMultiple(changenames, fail_on_not_found=True,
380 update_status=False): 410 update_status=False):
381 """Loads many changes and merge their files list into one pseudo change. 411 """Loads many changes and merge their files list into one pseudo change.
382 412
383 This is mainly usefull to concatenate many changes into one for a 'gcl try'. 413 This is mainly usefull to concatenate many changes into one for a 'gcl try'.
384 """ 414 """
385 changes = changenames.split(',') 415 changes = changenames.split(',')
386 aggregate_change_info = ChangeInfo(name=changenames) 416 aggregate_change_info = ChangeInfo(name=changenames)
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 save = True 465 save = True
436 files[files.index(file)] = (status, file[1]) 466 files[files.index(file)] = (status, file[1])
437 change_info = ChangeInfo(changename, issue, description, files) 467 change_info = ChangeInfo(changename, issue, description, files)
438 if save: 468 if save:
439 change_info.Save() 469 change_info.Save()
440 return change_info 470 return change_info
441 471
442 472
443 def GetCLs(): 473 def GetCLs():
444 """Returns a list of all the changelists in this repository.""" 474 """Returns a list of all the changelists in this repository."""
445 cls = os.listdir(GetInfoDir()) 475 cls = os.listdir(GetChangesDir())
446 if CODEREVIEW_SETTINGS_FILE in cls: 476 if CODEREVIEW_SETTINGS_FILE in cls:
447 cls.remove(CODEREVIEW_SETTINGS_FILE) 477 cls.remove(CODEREVIEW_SETTINGS_FILE)
448 return cls 478 return cls
449 479
450 480
451 def GenerateChangeName(): 481 def GenerateChangeName():
452 """Generate a random changelist name.""" 482 """Generate a random changelist name."""
453 random.seed() 483 random.seed()
454 current_cl_names = GetCLs() 484 current_cl_names = GetCLs()
455 while True: 485 while True:
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after
1013 1043
1014 1044
1015 def main(argv=None): 1045 def main(argv=None):
1016 if argv is None: 1046 if argv is None:
1017 argv = sys.argv 1047 argv = sys.argv
1018 1048
1019 if len(argv) == 1: 1049 if len(argv) == 1:
1020 Help() 1050 Help()
1021 return 0; 1051 return 0;
1022 1052
1023 # Create the directory where we store information about changelists if it 1053 # Create the directories where we store information about changelists if it
1024 # doesn't exist. 1054 # doesn't exist.
1025 if not os.path.exists(GetInfoDir()): 1055 if not os.path.exists(GetInfoDir()):
1026 os.mkdir(GetInfoDir()) 1056 os.mkdir(GetInfoDir())
1057 if not os.path.exists(GetChangesDir()):
1058 os.mkdir(GetChangesDir())
1059 # For smooth upgrade support, move the files in GetInfoDir() to
1060 # GetChangesDir().
1061 # TODO(maruel): Remove this code in August 2009.
1062 for file in os.listdir(unicode(GetInfoDir())):
1063 file_path = os.path.join(unicode(GetInfoDir()), file)
1064 if os.path.isfile(file_path) and file != CODEREVIEW_SETTINGS_FILE:
1065 shutil.move(file_path, GetChangesDir())
1066 if not os.path.exists(GetCacheDir()):
1067 os.mkdir(GetCacheDir())
1027 1068
1028 # Commands that don't require an argument. 1069 # Commands that don't require an argument.
1029 command = argv[1] 1070 command = argv[1]
1030 if command == "opened": 1071 if command == "opened":
1031 Opened() 1072 Opened()
1032 return 0 1073 return 0
1033 if command == "status": 1074 if command == "status":
1034 Opened() 1075 Opened()
1035 print "\n--- Not in any changelist:" 1076 print "\n--- Not in any changelist:"
1036 UnknownFiles([]) 1077 UnknownFiles([])
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1097 # the files. This allows commands such as 'gcl diff xxx' to work. 1138 # the files. This allows commands such as 'gcl diff xxx' to work.
1098 args =["svn", command] 1139 args =["svn", command]
1099 root = GetRepositoryRoot() 1140 root = GetRepositoryRoot()
1100 args.extend([os.path.join(root, x) for x in change_info.FileList()]) 1141 args.extend([os.path.join(root, x) for x in change_info.FileList()])
1101 RunShell(args, True) 1142 RunShell(args, True)
1102 return 0 1143 return 0
1103 1144
1104 1145
1105 if __name__ == "__main__": 1146 if __name__ == "__main__":
1106 sys.exit(main()) 1147 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tests/gcl_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698