 Issue 29508667:
  Issue 4354, 4355 - handle dirty/outdated repos on release  (Closed)
    
  
    Issue 29508667:
  Issue 4354, 4355 - handle dirty/outdated repos on release  (Closed) 
  | Left: | ||
| Right: | 
| OLD | NEW | 
|---|---|
| 1 # This Source Code Form is subject to the terms of the Mozilla Public | 1 # This Source Code Form is subject to the terms of the Mozilla Public | 
| 2 # License, v. 2.0. If a copy of the MPL was not distributed with this | 2 # License, v. 2.0. If a copy of the MPL was not distributed with this | 
| 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. | 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
| 4 | 4 | 
| 5 import os | 5 import os | 
| 6 import re | 6 import re | 
| 7 import codecs | 7 import codecs | 
| 8 import subprocess | 8 import subprocess | 
| 9 import tarfile | 9 import tarfile | 
| 10 import json | 10 import json | 
| 11 | 11 | 
| 
Vasily Kuznetsov
2017/08/08 18:10:12
There's no need for this empty line. The imports f
 
tlucas
2017/08/09 08:12:35
Acknowledged.
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 12 from itertools import groupby | |
| 13 | |
| 12 from packager import readMetadata, getDefaultFileName | 14 from packager import readMetadata, getDefaultFileName | 
| 13 | 15 | 
| 14 | 16 | 
| 15 def get_dependencies(prefix, repos): | 17 def get_dependencies(prefix, repos): | 
| 16 from ensure_dependencies import read_deps, safe_join | 18 from ensure_dependencies import read_deps, safe_join | 
| 17 repo = repos[prefix] | 19 repo = repos[prefix] | 
| 18 deps = read_deps(repo) | 20 deps = read_deps(repo) | 
| 19 if deps: | 21 if deps: | 
| 20 for subpath in deps: | 22 for subpath in deps: | 
| 21 if subpath.startswith('_'): | 23 if subpath.startswith('_'): | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 38 if os.path.basename(fileinfo.name) in ('.hgtags', '.hgig nore'): | 40 if os.path.basename(fileinfo.name) in ('.hgtags', '.hgig nore'): | 
| 39 continue | 41 continue | 
| 40 filedata = repoarchive.extractfile(fileinfo) | 42 filedata = repoarchive.extractfile(fileinfo) | 
| 41 fileinfo.name = re.sub(r'^[^/]+/', prefix, fileinfo.name ) | 43 fileinfo.name = re.sub(r'^[^/]+/', prefix, fileinfo.name ) | 
| 42 archive.addfile(fileinfo, filedata) | 44 archive.addfile(fileinfo, filedata) | 
| 43 finally: | 45 finally: | 
| 44 process.stdout.close() | 46 process.stdout.close() | 
| 45 process.wait() | 47 process.wait() | 
| 46 | 48 | 
| 47 | 49 | 
| 50 def repo_has_uncommitted(aborts_process=True): | |
| 51 """Checks if the given repository is clean""" | |
| 52 uncommitted = False | |
| 
Vasily Kuznetsov
2017/08/08 18:10:12
You're spending quite some effort reformatting the
 
tlucas
2017/08/09 08:12:35
Acknowledged.
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 53 status_mapping = { | |
| 54 'M': 'modified', | |
| 55 'A': 'added', | |
| 56 'R': 'removed', | |
| 57 '!': 'missing', | |
| 58 '?': 'untracked', | |
| 59 } | |
| 60 | |
| 61 # Check for any uncommitted changes | |
| 62 buff = subprocess.check_output(['hg', 'status']) | |
| 63 if len(buff): | |
| 64 uncommitted = True | |
| 65 dirty_files = buff.strip().split(os.linesep) | |
| 66 print('Dirty / uncommitted changes in repository!') | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
Using print as a function only seems to work under
 
tlucas
2017/08/09 08:12:35
Acknowledged.
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 67 grouped_dirty = groupby(dirty_files, key=lambda x: x.split(' ')[0]) | |
| 68 for dirty_grp in grouped_dirty: | |
| 69 print('{}:'.format(status_mapping[dirty_grp[0]])) | |
| 70 for dirty in dirty_grp[1]: | |
| 71 print(' {}'.format(dirty.split(' ')[1])) | |
| 72 | |
| 73 return uncommitted, aborts_process | |
| 74 | |
| 75 | |
| 76 def repo_has_outgoing(aborts_process=False): | |
| 77 """Checks whether there would be outgoing changesets to the given path""" | |
| 78 try: | |
| 79 buff = subprocess.check_output(['hg', 'outgoing']) | |
| 80 print('Detected outgoing changesets:') | |
| 81 print(buff) | |
| 82 return True, aborts_process | |
| 83 except subprocess.CalledProcessError: | |
| 84 return False, aborts_process | |
| 85 | |
| 86 | |
| 87 def repo_has_incoming(repo_paths, aborts_process=True): | |
| 88 """Checks whether the local repositories are up-to-date""" | |
| 89 incoming = False | |
| 90 | |
| 91 for repo_path in repo_paths: | |
| 92 try: | |
| 93 buff = subprocess.check_output(['hg', 'incoming', '-R', repo_path]) | |
| 94 print('Detected incoming changesets:') | |
| 
Vasily Kuznetsov
2017/08/08 18:10:12
Here we're printing the incoming changesets but ac
 
tlucas
2017/08/09 08:12:35
Acknowledged.
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 95 print(buff) | |
| 96 incoming = True | |
| 97 except subprocess.CalledProcessError: | |
| 98 pass | |
| 99 | |
| 100 return incoming, aborts_process | |
| 101 | |
| 102 | |
| 103 def ask_to_continue(): | |
| 104 """Asks the user if he wants to continue despite facing warnings""" | |
| 105 try: | |
| 106 input = raw_input | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
AFAIK, this compatibility trick doesn't work (unde
 
tlucas
2017/08/09 08:12:34
I tested a snippet of this, but not in function-sc
 
Vasily Kuznetsov
2017/08/09 17:09:09
Yes, I think it would be the best to ignore Python
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 107 except NameError: | |
| 108 pass | |
| 109 | |
| 110 print('The above error/s has/have been detected within the repositories.') | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
I know that you took this from the ticket, but I'm
 
tlucas
2017/08/09 08:12:35
I like it (and it was actually me, who added this
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 111 print('You might want to check whether this is ok or not.') | |
| 112 print('Are you sure about continuing the release-process?') | |
| 113 while True: | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
What do you think about rewriting this loop, and t
 
tlucas
2017/08/09 08:12:35
Acknowledged.
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 114 choice = input('Please choose (yes / no): ') | |
| 115 if choice.lower() not in ('yes', 'no'): | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
('yes', 'no') should be a set (this is error A102
 
tlucas
2017/08/09 08:12:35
This is removed while following your proposed appr
 
tlucas
2017/08/15 14:40:12
Done. Kept the reminder on what to specifically en
 | |
| 116 print('Please answer "yes" or "no"!') | |
| 117 else: | |
| 118 break | |
| 119 | |
| 120 return choice.lower() == 'yes' | |
| 121 | |
| 122 | |
| 48 def run(baseDir, type, version, keyFile, downloadsRepo): | 123 def run(baseDir, type, version, keyFile, downloadsRepo): | 
| 124 # run repository-checks and bail out early, in case the user does not | |
| 125 # explicitly want to continue OR if the check would cause the process to | |
| 126 # abort anyway | |
| 127 repo_checks = ( | |
| 
Vasily Kuznetsov
2017/08/08 18:10:11
I think the new code in this function is unnecessa
 
tlucas
2017/08/09 08:12:35
Acknowledged. I'll also rename the function accord
 
tlucas
2017/08/15 14:40:12
Done.
 | |
| 128 (repo_has_uncommitted, ()), | |
| 129 (repo_has_outgoing, ()), | |
| 130 (repo_has_incoming, ((baseDir, downloadsRepo),)), | |
| 131 ) | |
| 132 | |
| 133 check_results = [func(*args) for func, args in repo_checks] | |
| 134 | |
| 135 if ( | |
| 136 any(check and aborts for check, aborts in check_results) | |
| 137 or ( | |
| 138 any(check and not aborts for check, aborts in check_results) | |
| 139 and not ask_to_continue() | |
| 140 )): | |
| 141 print('Aborting release.') | |
| 142 return 1 | |
| 143 | |
| 49 if type == 'gecko': | 144 if type == 'gecko': | 
| 50 import buildtools.packagerGecko as packager | 145 import buildtools.packagerGecko as packager | 
| 51 elif type == 'safari': | 146 elif type == 'safari': | 
| 52 import buildtools.packagerSafari as packager | 147 import buildtools.packagerSafari as packager | 
| 53 elif type == 'edge': | 148 elif type == 'edge': | 
| 54 import buildtools.packagerEdge as packager | 149 import buildtools.packagerEdge as packager | 
| 55 elif type == 'chrome': | 150 elif type == 'chrome': | 
| 56 import buildtools.packagerChrome as packager | 151 import buildtools.packagerChrome as packager | 
| 57 | 152 | 
| 58 # Replace version number in metadata file "manually", ConfigParser will mess | 153 # Replace version number in metadata file "manually", ConfigParser will mess | 
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 116 create_sourcearchive(baseDir, archivePath) | 211 create_sourcearchive(baseDir, archivePath) | 
| 117 downloads.append(archivePath) | 212 downloads.append(archivePath) | 
| 118 | 213 | 
| 119 # Now add the downloads and commit | 214 # Now add the downloads and commit | 
| 120 subprocess.check_call(['hg', 'add', '-R', downloadsRepo] + downloads) | 215 subprocess.check_call(['hg', 'add', '-R', downloadsRepo] + downloads) | 
| 121 subprocess.check_call(['hg', 'commit', '-R', downloadsRepo, '-m', 'Releasing %s %s' % (extensionName, version)]) | 216 subprocess.check_call(['hg', 'commit', '-R', downloadsRepo, '-m', 'Releasing %s %s' % (extensionName, version)]) | 
| 122 | 217 | 
| 123 # Push all changes | 218 # Push all changes | 
| 124 subprocess.check_call(['hg', 'push', '-R', baseDir]) | 219 subprocess.check_call(['hg', 'push', '-R', baseDir]) | 
| 125 subprocess.check_call(['hg', 'push', '-R', downloadsRepo]) | 220 subprocess.check_call(['hg', 'push', '-R', downloadsRepo]) | 
| OLD | NEW |