Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Beginnings of support for backing up repositories from kiln

  • Loading branch information...
commit 05136de69bc396848982089c891f91a436f51ee5 1 parent c711b90
Craig Silverstein csilvers authored
11 .hgrc
View
@@ -0,0 +1,11 @@
+[ui]
+username = Kiln Mirrorbot <csilvers@khanacademy.org>
+
+[extensions]
+kilnauth=~/hg_mirrors/kilnauth.py
+
+[auth]
+kiln.prefix = https://khanacademy.kilnhg.com
+
+[hostfingerprints]
+khanacademy.kilnhg.com = fe:ab:65:89:7c:6f:1a:21:a8:39:54:6c:2a:cb:ca:ae:e9:e5:f0:01
12 README
View
@@ -8,10 +8,20 @@ Install instructions:
2) Files in etc/ should be installed under /
3) Other files should be installed under the root account, e.g. /home/ubuntu
+ (Don't forget to copy the dot-files like .hgrc!)
4) After installing the /etc files, run:
sudo update-rc.d git-daemon defaults
service git-daemon start
-5) To install reviewboard and/or gerrit, follow the instructions in
+5) Also run, so you can enter the appropriate password/etc:
+ cd hg_mirrors && hg clone --noupdate https://khanacademy.kilnhg.com/Code/Mobile-Apps/Group/android /tmp/test_repo && python kiln_local_backup.py .
+
+6) To install reviewboard and/or gerrit, follow the instructions in
reviewboard.install and gerrit.install
+
+Will also want to install the following packages:
+ python-pip
+
+And pip install the following packages:
+ mercurial
110 hg_mirrors/README.kiln-local-backup
View
@@ -0,0 +1,110 @@
+Taken from http://code.google.com/p/kiln-local-backup
+
+LICENSE
+
+ Copyright (c) 2010 Nate Silva
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+OVERVIEW
+
+ This is a backup script to maintain a copy of all of your Kiln Mercurial
+ repositories on your local system.
+
+GETTING STARTED
+
+ For Windows you need:
+
+ * A Kiln account
+ * A FogBugz API token
+ * Python 2.4 or higher
+ * simplejson (not needed with Python 2.6 or higher)
+ * The Kiln Tools installed
+
+ On other platforms, you’ll also need to install:
+
+ * Mecurial 1.3 or higher
+ * The KilnAuth Mercurial extension
+
+ If you are missing any of these, see REFERENCES at the end of this document.
+
+BACKING UP
+
+ All repositories are backed up to the directory you specify. For example, if
+ you want to back up to C:\KilnBackups, run:
+
+ python kiln-local-backup.py C:\KilnBackups
+
+ You will be prompted for your API token and Kiln server name. If your
+ credentials aren’t stored in KilnAuth, you’ll also be prompted for your
+ password.
+
+ Your API token and server name are saved to a file in the backup directory,
+ so you won’t have to enter them next time.
+
+ Once KilnAuth has your credentials, you will not be prompted for your
+ password again.
+
+ To see the full syntax, including options for passing your API token and
+ server name on the command line, type:
+
+ python kiln-local-backup.py --help
+
+FAQS
+
+ Q: My backup is empty!
+
+ A: No, it’s not. With Mercurial, the entire repository is stored in the .hg
+ subdirectory, which may be hidden. You can clone the repository to see
+ that the files are really there, if it makes you feel better.
+
+ Alternately, you can specify the --update command-line option. Then you’ll
+ see your working files. The working files that you see are just the latest
+ version of your project, but rest assured the script is backing up your
+ whole repository, including all history. That’s located in the .hg
+ subdirectory.
+
+
+ Q: Scheduled backups don’t work.
+
+ A: Make sure you are running the backup under THE SAME USER ACCOUNT you were
+ using when you entered your password for KilnAuth.
+
+
+ Q: What platforms does this run on?
+
+ A: I have tested it on Mac OS X 10.6, Windows Server 2003 R2, and Ubuntu
+ Linux 9.10.
+
+
+ Q: Can I back up repositories with non-ASCII names?
+
+ A: It should work. On Windows, non-ASCII characters in the repository name
+ will be replaced with equivalent XML character references. On Unix/Mac,
+ non-ASCII names will be used as-is.
+
+REFERENCES
+
+ To get a FogBugz API token, see: http://goo.gl/rGDQ9
+
+ To get the KilnAuth Mercurial extension, install the Kiln Client, or see your
+ Kiln Tools page. The URL is: http://[your kiln site]/Tools.
+
+ simplejson is only needed for Python versions earlier than 2.6. It can be
+ found at http://goo.gl/nv3tu.
BIN  hg_mirrors/kiln-local-backup-0.1.6.zip
View
Binary file not shown
382 hg_mirrors/kiln_local_backup.py
View
@@ -0,0 +1,382 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+kiln_local_backup.py
+
+Copyright (c) 2010 Nate Silva
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+"""
+__version__ = "0.1.6"
+
+import sys
+import os
+import urllib2
+import time
+import urllib
+import urlparse
+from subprocess import Popen, PIPE
+from optparse import OptionParser, IndentedHelpFormatter
+
+try:
+ import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ sys.exit('For versions of Python earlier than 2.6, you must ' +
+ 'install simplejson (http://pypi.python.org/pypi/simplejson/).')
+
+CONFIG_FILE = 'backup.config'
+
+
+def parse_command_line(args):
+ """
+ Parse the command line arguments.
+
+ Returns a tuple of (options, destination_dir).
+
+ Calls sys.exit() if the command line could not be parsed.
+ """
+
+ usage = 'usage: %prog [options] DESTINATION-DIR'
+ description = 'Backs up all available Mercurial repositories on Kiln ' + \
+ 'by cloning them (if they have not been backed up before), or by ' + \
+ 'pulling changes. In order to run this without user interaction, ' + \
+ 'you must install the FogBugz "KilnAuth" Mercurial extension and ' + \
+ 'clone at least one repository so that your credentials are saved.'
+ version = "%prog, v" + __version__
+
+ parser = OptionParser(usage=usage, description=description, version=version)
+ parser.formatter = IndentedHelpFormatter(max_help_position=30)
+
+ parser.add_option('-t', '--token', dest='token', help='FogBugz API token')
+ parser.add_option('-s', '--server', dest='server', help='Kiln server name')
+ parser.add_option('--scheme', dest='scheme', type='choice',
+ choices=('https', 'http'), help='scheme used to connect to server')
+ parser.add_option('-q', '--quiet', dest='verbose', action='store_false',
+ default=True, help='non-verbose output')
+ parser.add_option('-l', '--limit', dest='limit', metavar='PATH',
+ help='only backup repos in the specified project/group (ex.: ' + \
+ 'MyProject) (or: MyProject/MyGroup)')
+ parser.add_option('-u', '--update', dest='update', action='store_true',
+ default=False, help='update working copy when cloning or pulling')
+
+ (options, args) = parser.parse_args(args)
+
+ # Get the destination directory, which should be the one and
+ # only non-option argument.
+ if len(args) == 0:
+ parser.error('Must specify the destination directory for the backups.')
+ if len(args) > 1:
+ parser.error('Unknown arguments passed after destination directory')
+ destination_dir = args[0]
+
+ # Now get any saved options from the configuration file and use
+ # them to fill in any missing options.
+ configfile_path = os.path.join(destination_dir, CONFIG_FILE)
+ if os.path.exists(configfile_path):
+ configfile = open(configfile_path, 'r')
+ config_data = json.load(configfile)
+ configfile.close()
+
+ if not options.token and 'token' in config_data:
+ options.token = config_data['token']
+
+ if not options.server and 'server' in config_data:
+ options.server = config_data['server']
+
+ if not options.scheme and 'scheme' in config_data:
+ options.scheme = config_data['scheme']
+
+ # default to https if still no scheme specified
+ if not options.scheme:
+ options.scheme = 'https'
+
+ return (options, destination_dir)
+
+
+def get_repos(scheme, server, token, verbose):
+ """
+ Query Kiln to get the list of available repositories. Return the
+ list, sorted by the Kiln “full name” of each repository.
+ """
+
+ if verbose:
+ print console_encode('Getting the list of repositories from %s' %
+ server)
+
+ urlp = urlparse.urlparse('%s://%s/' % (scheme, server))
+
+ if urlp.netloc[-10:] == 'kilnhg.com':
+ url = '%s://%s/Api/Repos/?token=%s' % (scheme, server, token)
+ else:
+ url = '%s://%s/kiln/Api/Repos/?token=%s' % (scheme, server, token)
+
+ data = json.load(urllib2.urlopen(url))
+ if 'error' in data:
+ sys.exit(data['error'])
+
+ if verbose:
+ print console_encode('Found %d repositories' % len(data))
+
+ return sorted(data, lambda x, y: cmp(x['fullName'], y['fullName']))
+
+
+def backup_repo(clone_url, target_dir, verbose, update):
+ """
+ Backup the specified repository. Returns True if successful. If
+ the backup fails, prints an error message and returns False.
+ """
+ backup_method = 'clone'
+
+ # If the filesystem does not use Unicode (from Python’s
+ # perspective), convert target_dir to plain ASCII.
+ if not sys.getfilesystemencoding().upper().startswith('UTF'):
+ target_dir = target_dir.encode('ascii', 'xmlcharrefreplace')
+
+ # Does the target directory already exist?
+ if os.path.isdir(target_dir):
+ # Yes, it exists. Does it refer to the same repository?
+ default = Popen(['hg', 'paths', '-R', target_dir, 'default'],
+ stdout=PIPE, stderr=PIPE).communicate()[0].strip()
+ default = urllib2.unquote(default)
+
+ if default == clone_url:
+ # It exists and is identical. We will pull.
+ backup_method = 'pull'
+ else:
+ # It exists but refers to a different repo or is not a
+ # repo. Move it to an archive directory.
+ (parent_dir, repo_name) = os.path.split(target_dir)
+ if verbose:
+ print console_encode('exists but is not the same repo'),
+ sys.stdout.flush()
+ new_name = 'archive/%f-%s' % (time.time(), repo_name)
+ new_dir = os.path.join(parent_dir, new_name)
+ os.renames(target_dir, new_dir)
+ if verbose:
+ print console_encode('(archived); continuing backup...'),
+ sys.stdout.flush()
+ else:
+ # Path doesn’t exist: create it
+ os.makedirs(target_dir)
+
+ # Back it up
+ if backup_method == 'clone':
+ if update:
+ args = ['hg', 'clone', clone_url, target_dir]
+ else:
+ args = ['hg', 'clone', '--noupdate', clone_url, target_dir]
+ else:
+ if update:
+ args = ['hg', 'pull', '-u', '-R', target_dir]
+ else:
+ args = ['hg', 'pull', '-R', target_dir]
+
+ # KilnAuth uses os.path.expanduser which should use
+ # %USERPROFILE%. When run from Scheduled Tasks, it does not seem
+ # to work unless you also set %HOMEDRIVE% and %HOMEPATH%.
+ child_env = os.environ
+ if os.name == 'nt':
+ (drive, path) = os.path.splitdrive(os.environ['USERPROFILE'])
+ child_env['HOMEDRIVE'] = drive
+ child_env['HOMEPATH'] = path
+
+ proc = Popen(args, stdout=PIPE, stderr=PIPE, env=child_env)
+ (_, stderrdata) = proc.communicate()
+ if proc.returncode:
+ print console_encode('**** FAILED ****')
+ print console_encode('*' * 60)
+ print console_encode(u'Error backing up repository %s\nError was: %s' %
+ (clone_url, stderrdata))
+ print console_encode('*' * 60)
+ return False
+
+ if verbose:
+ print console_encode('backed up using method %s' %
+ backup_method.upper())
+
+ return True
+
+
+def console_encode(message):
+ """
+ Encodes the message as appropriate for output to sys.stdout.
+ This is needed especially for Windows, where stdout is often a
+ non-Unicode encoding.
+ """
+ if sys.stdout.encoding == None:
+ # Encoding not available. Force ASCII.
+ return unicode(message).encode('ASCII', 'xmlcharrefreplace')
+ if sys.stdout.encoding.upper().startswith('UTF'):
+ # Unicode console. No need to convert.
+ return message
+ else:
+ return unicode(message).encode(sys.stdout.encoding,
+ 'xmlcharrefreplace')
+
+
+def encode_url(url):
+ """
+ URLs returned by Kiln may have unencoded Unicode characters in
+ them. Encode them.
+ """
+ url_parts = list(urlparse.urlsplit(url.encode('utf-8')))
+ url_parts[2] = urllib.quote(url_parts[2])
+ return urlparse.urlunsplit(url_parts)
+
+
+def main():
+ """
+ Main entry point for Kiln backup utility.
+ """
+
+ # Parse the command line
+ (options, destination_dir) = parse_command_line(sys.argv[1:])
+
+ # If token or server were not specified, prompt the user.
+ if not options.token:
+ options.token = raw_input('Your FogBugz API token: ')
+ if not options.token:
+ sys.exit('FogBugz API token is required.')
+
+ if not options.server:
+ options.server = \
+ raw_input('Kiln server name (e.g. company.kilnhg.com): ')
+ if not options.server:
+ sys.exit('Kiln server name is required.')
+
+ # If the destination directory doesn’t exist, try to create it.
+ if not os.path.isdir(destination_dir):
+ os.makedirs(destination_dir)
+ if not os.path.isdir(destination_dir):
+ sys.exit('destination directory', destination_dir, "doesn't exist",
+ "and couldn't be created")
+
+ # Save configuration
+ configfile = open(os.path.join(destination_dir, CONFIG_FILE), 'w+')
+ config = {'server': options.server, 'token': options.token,
+ 'scheme': options.scheme}
+ json.dump(config, configfile, indent=4)
+ configfile.write('\n')
+ configfile.close()
+
+ # Keep track of state for printing status messages
+ if options.verbose:
+ count = 0
+ last_subdirectory = ''
+ current_group = None
+
+ # Keep track of overall success status. We continue backing up
+ # even if there’s an error.
+ overall_success = True
+
+ # Back up the repositories
+ repos = get_repos(options.scheme, options.server, options.token, options.verbose)
+
+ # If using --limit, filter repos we don’t want to backup.
+ if options.limit:
+ # Normalize the limit. Convert backslashes. Remove any
+ # leading or trailing slash.
+ limit = '/Kiln/Repo/%s' % options.limit.replace('\\', '/').strip('/')
+
+ # Replace spaces with dashes, as Kiln does (in case the
+ # user typed a human-readable repo name that has spaces)
+ limit = limit.replace(' ', '-')
+
+ # Filter. Case-insensitive. (Kiln won’t let you create two
+ # groups or projects with the same name but different case.)
+ repos = [_ for _ in repos if
+ _['url'].lower().endswith(limit.lower())]
+
+ if options.verbose:
+ if len(repos) == 0:
+ message = 'No repositories match the specified limit. '
+ message += 'Nothing to back up!'
+ else:
+ if len(repos) == 1:
+ message = '1 repository matches '
+ else:
+ message = '%d repositories match ' % len(repos)
+ message += 'the specified limit and will be backed up'
+ print console_encode(message)
+
+ # Return an error code if there are no repos to back up. This
+ # probably indicates a typo or similar mistake.
+ if len(repos) == 0:
+ overall_success = False
+
+ for repo in repos:
+ # The "full name" from Kiln is the project name, plus any
+ # group name and the repo name. Components are separated by
+ # a right angle quote (»). Turn this into a path by
+ # converting angle quotes into path separators.
+ parts = repo['fullName'].split(u'»')
+ parts = [_.strip() for _ in parts] # trim whitespace
+ subdirectory = os.path.join(*parts)
+
+ if options.verbose:
+ # For the progress message, show the project and group
+ # name as a header, and under that, list the repo names
+ # being backed up. Also show a counter.
+
+ group = os.path.commonprefix([last_subdirectory,
+ os.path.dirname(subdirectory)])
+
+ if group != current_group:
+ current_group = os.path.dirname(subdirectory)
+ print console_encode('\n%s' % current_group)
+
+ last_subdirectory = subdirectory
+ count += 1
+ print console_encode(' >> [%d/%d] %s ' % (count, len(repos),
+ os.path.basename(subdirectory))),
+
+ # The following line fixes Issue 1. Python conveniently
+ # inserts a space when a print statement ends with a
+ # comma. Unfortunately if Mercurial is going to prompt
+ # for a password, it does not know about the space that
+ # is “supposed” to be there and the result looks like:
+ # "reponamepassword:". The following line prevents
+ # Python from inserting the space. Instead we manually
+ # forced a space to the end of the print statement above.
+ sys.stdout.write('')
+
+ sys.stdout.flush()
+
+ clone_url = encode_url(repo['cloneUrl'].strip('"'))
+ target_dir = unicode(os.path.join(destination_dir, subdirectory))
+
+ success = backup_repo(clone_url, target_dir, options.verbose,
+ options.update)
+ overall_success = overall_success and success
+
+ if overall_success:
+ print 'All repositories backed up successfully.'
+ return 0
+ else:
+ print 'Completed with errors.'
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
229 hg_mirrors/kilnauth.py
View
@@ -0,0 +1,229 @@
+# Copyright (C) 2009-2011 Fog Creek Software. All rights reserved.
+#
+# To enable the "kilnauth" extension put these lines in your ~/.hgrc:
+# [extensions]
+# kilnauth = /path/to/kilnauth.py
+#
+# For help on the usage of kilnauth use:
+# hg help kilnauth
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+'''stores authentication cookies for HTTP repositories
+
+This extension knows how to capture Kiln authentication tokens when pushing
+over HTTP. This means you only need to enter your login and password once;
+after that, the FogBugz token will be stored in your home directory, allowing
+pushing without a password.
+
+If you ever need to logout of Kiln, simply run ``hg logout''
+'''
+
+from cookielib import MozillaCookieJar, Cookie
+from urllib2 import Request
+import os
+import re
+import shutil
+import stat
+import sys
+import tempfile
+
+try:
+ from hashlib import md5
+except:
+ # Python 2.4
+ import md5
+
+try:
+ WindowsError
+except NameError:
+ WindowsError = None
+
+from mercurial.i18n import _
+import mercurial.url
+from mercurial import commands
+
+current_user = None
+
+class CookieJar(MozillaCookieJar, object):
+ def __init__(self, filename, *args, **kwargs):
+ self.__original_path = filename
+ tf = tempfile.NamedTemporaryFile(delete=False)
+ self.__temporary_path = tf.name
+ tf.close()
+ if os.path.exists(filename):
+ shutil.copyfile(filename, self.__temporary_path)
+ return super(CookieJar, self).__init__(self.__temporary_path, *args, **kwargs)
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ os.unlink(self.__temporary_path)
+ self.__temporary_path = None
+
+ def __del__(self):
+ try:
+ if self.__temporary_path:
+ os.unlink(self.__temporary_path)
+ except (OSError, IOError):
+ pass
+
+ def save(self, *args, **kwargs):
+ with open(self.__temporary_path, 'rb') as f:
+ before = md5(f.read()).digest()
+ super(CookieJar, self).save(*args, **kwargs)
+ with open(self.__temporary_path, 'rb') as f:
+ after = md5(f.read()).digest()
+ if before != after:
+ try:
+ os.rename(self.__temporary_path, self.__original_path)
+ except WindowsError:
+ shutil.copyfile(self.__temporary_path, self.__original_path)
+ except (IOError, OSError):
+ pass
+
+def get_cookiejar(ui):
+ global current_user
+ if os.name == 'nt':
+ cookie_path = os.path.expanduser('~\\_hgcookies')
+ else:
+ cookie_path = os.path.expanduser('~/.hgcookies')
+
+ if not os.path.isdir(cookie_path):
+ if os.path.exists(cookie_path):
+ os.remove(cookie_path)
+ os.mkdir(cookie_path)
+ if os.name == 'posix':
+ os.chmod(cookie_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
+
+ cookie_path = os.path.join(cookie_path, md5(current_user).hexdigest())
+ # Cygwin's Python does not always expanduser() properly...
+ if re.match(r'^[A-Za-z]:', cookie_path) is not None and re.match(r'[A-Za-z]:\\', cookie_path) is None:
+ cookie_path = re.sub(r'([A-Za-z]):', r'\1:\\', cookie_path)
+
+ try:
+ cj = CookieJar(cookie_path)
+ if not os.path.exists(cookie_path):
+ cj.save()
+ if os.name == 'posix':
+ os.chmod(cookie_path, stat.S_IREAD | stat.S_IWRITE)
+ cj.load(ignore_discard=True, ignore_expires=True)
+ return cj
+ except IOError:
+ ui.warn(_('Cookie file %s exists, but could not be opened.\nContinuing without cookie authentication.\n') % cookie_path)
+ return MozillaCookieJar(tempfile.NamedTemporaryFile().name)
+
+def make_cookie(request, name, value):
+ domain = request.get_host()
+ port = None
+ if ':' in domain:
+ domain, port = domain.split(':', 1)
+ if '.' not in domain:
+ domain += ".local"
+ return Cookie(version=0,
+ name=name, value=value,
+ port=port, port_specified=False,
+ domain=domain, domain_specified=False, domain_initial_dot=False,
+ path='/', path_specified=False, secure=False,
+ expires=None, discard=False,
+ comment=None, comment_url=None,
+ rest={})
+
+def get_username(url):
+ url = re.sub(r'https?://', '', url)
+ url = re.sub(r'/.*', '', url)
+ if '@' in url:
+ # There should be some login info
+ # rfind in case it's an email address
+ username = url[:url.rfind('@')]
+ if ':' in username:
+ username = url[:url.find(':')]
+ return username
+ # Didn't find anything...
+ return ''
+
+def get_dest(ui):
+ from mercurial.dispatch import _parse
+ try:
+ cmd_info = _parse(ui, sys.argv[1:])
+ cmd = cmd_info[0]
+ dest = cmd_info[2]
+ if dest:
+ dest = dest[0]
+ elif cmd in ['outgoing', 'push']:
+ dest = 'default-push'
+ else:
+ dest = 'default'
+ except:
+ dest = 'default'
+ return ui.expandpath(dest)
+
+def reposetup(ui, repo):
+ global current_user
+ if repo.local():
+ try:
+ current_user = get_username(get_dest(ui))
+ except:
+ current_user = ''
+
+def extsetup():
+ global current_user
+ ui = mercurial.ui.ui()
+ current_user = get_username(get_dest(ui))
+
+ def open_wrapper(func):
+ def open(*args, **kwargs):
+ if isinstance(args[0], Request):
+ request = args[0]
+ cj = get_cookiejar(ui)
+ cj.set_cookie(make_cookie(args[0], 'fSetNewFogBugzAuthCookie', '1'))
+ cj.add_cookie_header(request)
+ response = func(*args, **kwargs)
+ cj.extract_cookies(response, args[0])
+ cj.save(ignore_discard=True, ignore_expires=True)
+ else:
+ response = func(*args, **kwargs)
+ return response
+ return open
+
+ old_opener = mercurial.url.opener
+ def opener(*args, **kwargs):
+ urlopener = old_opener(*args, **kwargs)
+ urlopener.open = open_wrapper(urlopener.open)
+ return urlopener
+ mercurial.url.opener = opener
+
+def logout(ui, domain=None):
+ """log out of http repositories
+
+ Clears the cookies stored for HTTP repositories. If [domain] is
+ specified, only that domain will be logged out. Otherwise,
+ all domains will be logged out.
+ """
+
+ cj = get_cookiejar(ui)
+ try:
+ cj.clear(domain=domain)
+ cj.save()
+ except KeyError:
+ ui.write("Not logged in to '%s'\n" % (domain,))
+
+commands.norepo += ' logout'
+
+cmdtable = {
+ 'logout': (logout, [], '[domain]')
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.