Esempio n. 1
0
def _get_repo(remote):
    if not changegroup or experiment('wire'):
        if not changegroup and not check_enabled('no-mercurial'):
            logging.warning('Mercurial libraries not found. Falling back to '
                            'experimental native access.')

        stream = HgRepoHelper.connect(remote.url)
        if stream:
            return bundlerepo(remote.url, stream)
        return HelperRepo(remote.url)

    if remote.parsed_url.scheme == b'file':
        # Make file://c:/... paths work by taking the netloc
        path = remote.parsed_url.netloc + remote.parsed_url.path
        if sys.platform == 'win32':
            # TODO: This probably needs more thought.
            path = path.lstrip(b'/')
        if not os.path.isdir(path):
            return bundlerepo(path)
    ui = get_ui()
    if changegroup and remote.parsed_url.scheme == b'file':
        repo = localpeer(ui, path)
    else:
        try:
            repo = hg.peer(ui, {}, remote.url)
        except (error.RepoError, HTTPError, IOError):
            if remote.parsed_url.scheme in ('http', 'https'):
                return bundlerepo(remote.url, HTTPReader(remote.url))
            raise

    assert repo.capable(b'getbundle')

    return repo
Esempio n. 2
0
def get_repo(remote):
    if not changegroup or experiment('wire'):
        if not changegroup and not check_enabled('no-mercurial'):
            logging.warning('Mercurial libraries not found. Falling back to '
                            'native access.')
        logging.warning(
            'Native access to mercurial repositories is experimental!')

        stream = HgRepoHelper.connect(remote.url)
        if stream:
            return bundlerepo(remote.url, stream)
        return HelperRepo(remote.url)

    if remote.parsed_url.scheme == 'file':
        path = remote.parsed_url.path
        if sys.platform == 'win32':
            # TODO: This probably needs more thought.
            path = path.lstrip('/')
        if not os.path.isdir(path):
            return bundlerepo(path)
    ui = get_ui()
    if changegroup and remote.parsed_url.scheme == 'file':
        repo = localpeer(ui, path)
    else:
        try:
            repo = hg.peer(ui, {}, remote.url)
        except (error.RepoError, urllib2.HTTPError, IOError):
            return bundlerepo(remote.url, HTTPReader(remote.url))

    assert repo.capable('getbundle')

    return repo
Esempio n. 3
0
def get_bundle(url):
    reader = None
    if not changegroup:
        reader = BundleHelper.connect(url)
        if not reader:
            BundleHelper.close()
    if not reader:
        reader = HTTPReader(url)
    return unbundle_fh(reader, url)
Esempio n. 4
0
def get_bundle(url):
    reader = None
    if not changegroup:
        reader = BundleHelper.connect(url)
        if not reader:
            BundleHelper.close()
    if not reader:
        disable_ssl = False

        if sys.platform == 'win32':
            disable_ssl = True

        reader = HTTPReader(url, disable_ssl=disable_ssl)
    return unbundle_fh(reader, url)
Esempio n. 5
0
def get_clonebundle(repo):
    url = Git.config('cinnabar.clonebundle')
    if not url:
        try:
            if check_enabled('no-mercurial'):
                raise ImportError('Do not use mercurial')
            from mercurial.exchange import (
                parseclonebundlesmanifest,
                filterclonebundleentries,
            )
        except ImportError:
            return None

        bundles = repo._call('clonebundles')

        class dummy(object):
            pass

        fakerepo = dummy()
        fakerepo.requirements = set()
        fakerepo.supportedformats = set()
        fakerepo.ui = repo.ui

        entries = parseclonebundlesmanifest(fakerepo, bundles)
        if not entries:
            return None

        entries = filterclonebundleentries(fakerepo, entries)
        if not entries:
            return None

        url = entries[0].get('URL')

    if not url:
        return None

    sys.stderr.write('Getting clone bundle from %s\n' % url)

    return unbundle_fh(HTTPReader(url), url)
Esempio n. 6
0
    def test_recovery(self):
        sizes = {}
        length = 0
        for s in [162000, 64000, 57932]:
            sizes[length] = s
            length += s
        the_test = self

        # This HTTP server handler cuts responses before the full content is
        # returned, according to the partial sizes defined above.
        # It assumes the client will retry with a Range request starting from
        # where it left, up to the end of the file.
        class Handler(BaseHTTPRequestHandler):
            def do_GET(self):
                range_def = self.headers.getheader('Range')
                if range_def:
                    start, end = range_def.partition('bytes=')[2].split('-')
                    start = int(start) if start else 0
                    end = int(end) if end else length - 1
                    the_test.assertIn(start, sizes)
                    the_test.assertEqual(end, length - 1)
                    self.send_response(206)
                    self.send_header('Content-Range',
                                     'bytes %d-%d/%d' % (start, end, length))
                else:
                    start = 0
                    self.send_response(200)
                self.send_header('Content-Type', 'text/plain')
                self.send_header('Content-Length', str(length))
                self.send_header('Accept-Ranges', 'bytes')
                self.end_headers()

                buf = '-' * 4096
                left = sizes[start]
                while left:
                    if left < len(buf):
                        buf = buf[:left]
                    self.wfile.write(buf)
                    left -= len(buf)

            def log_request(self, *args, **kwargs):
                pass

        server = HTTPServer(('', 0), Handler)
        port = server.socket.getsockname()[1]
        thread = Thread(target=server.serve_forever)
        thread.start()
        try:
            reader = HTTPReader('http://localhost:%d/foo' % port)
            read = 0
            while True:
                buf = reader.read(1250)
                # If the read above is interrupted and the HTTPReader can
                # recover with a range request, we still expect the right
                # size.
                self.assertEqual(len(buf), min(1250, length - read))
                read += len(buf)
                if not buf:
                    break

            self.assertEqual(read, length)

        finally:
            server.shutdown()
            thread.join()
Esempio n. 7
0
def download(args):
    '''download a prebuilt helper'''

    helper = 'git-cinnabar-helper'
    system = args.system
    machine = args.machine

    if system.startswith('MSYS_NT'):
        system = 'Windows'

    if system == 'Darwin':
        system = 'macOS'
    elif system == 'Windows':
        helper += '.exe'
        if machine == 'AMD64':
            machine = 'x86_64'

    available = (
        ('Linux', 'x86_64'),
        ('macOS', 'x86_64'),
        ('macOS', 'arm64'),
        ('Windows', 'x86_64'),
        ('Windows', 'x86'),
    )

    if args.list:
        for system, machine in available:
            print("%s/%s" % (system, machine))
        return 0

    if (system, machine) not in available:
        print('No download available for %s/%s' % (system, machine),
              file=sys.stderr)
        return 1

    if args.dev is False:
        version = VERSION
        if version.endswith('a'):
            # For version x.y.za, download a development helper
            args.dev = ''

    script_path = os.path.dirname(os.path.abspath(sys.argv[0]))

    if args.dev is not False:
        sha1 = helper_hash()
        if sha1 is None:
            print('Cannot find the right development helper for this '
                  'version of git cinnabar.',
                  file=sys.stderr)
            return 1
        url = 'https://community-tc.services.mozilla.com/api/index/v1/task/'
        url += 'project.git-cinnabar.helper.'
        url += '{}.{}.{}.{}'.format(
            sha1.decode('ascii'), system.lower(), machine,
            args.dev.lower() if args.dev else '').rstrip('.')
        url += '/artifacts/public/{}'.format(helper)

    else:
        if system in ('Windows', 'macOS'):
            ext = 'zip'
        else:
            ext = 'tar.xz'
        REPO_BASE = 'https://github.com/glandium'
        url = '%s/git-cinnabar/releases/download/%s/git-cinnabar.%s.%s.%s' % (
            REPO_BASE, version, system.lower(), machine.lower(), ext)

    if args.url:
        print(url)
        return 0

    if args.output:
        d = os.path.dirname(args.output)
    else:
        d = script_path
        if not os.access(d, os.W_OK):
            d = os.path.join(os.path.expanduser('~'), '.git-cinnabar')
            try:
                os.makedirs(d)
            except Exception:
                pass
            if not os.path.isdir(d):
                print('Cannot write to either %s or %s.' % (d, script_path),
                      file=sys.stderr)
                return 1

    print('Downloading from %s...' % url)
    try:
        reader = HTTPReader(url, disable_ssl=args.disable_ssl)
    except HTTPError:
        # Try again, just in case
        try:
            reader = HTTPReader(url, disable_ssl=args.disable_ssl)
        except HTTPError as e:
            print('Download failed with status code %d\n' % e.code,
                  file=sys.stderr)
            print(
                'Error body was:\n\n%s' % e.read().decode('utf-8', 'replace'),
                file=sys.stderr)
            return 1

    class ReaderProgress(object):
        def __init__(self, reader, length=None):
            self._reader = reader
            self._length = length
            self._read = 0
            self._pos = 0
            self._buf = ''
            self._progress = Progress(' {}%' if self._length else ' {} bytes')

        def read(self, length):
            # See comment above tell
            if self._pos < self._read:
                assert self._read - self._pos <= 8
                assert length <= len(self._buf)
                data = self._buf[:length]
                self._buf = self._buf[length:]
                self._pos += length
            else:
                assert self._read == self._pos
                data = self._reader.read(length)
                self._read += len(data)
                self._pos = self._read
                # Keep the last 8 bytes we read for GzipFile
                self._buf = data[-8:]
            self.progress()
            return data

        def progress(self):
            if self._length:
                count = self._read * 100 // self._length
            else:
                count = self._read
            self._progress.progress(count)

        def finish(self):
            self._progress.finish()

        # GzipFile wants to seek to the end of the file and back, so we add
        # enough tell/seek support to make it happy. It also rewinds 8 bytes
        # for the CRC, so we also handle that.
        def tell(self):
            return self._pos

        def seek(self, pos, how=os.SEEK_SET):
            if how == os.SEEK_END:
                self._pos = self._length + pos
            elif how == os.SEEK_SET:
                self._pos = pos
            elif how == os.SEEK_CUR:
                self._pos += pos
            else:
                raise NotImplementedError()
            return self._pos

    encoding = reader.fh.headers.get('Content-Encoding', 'identity')
    helper_content = ReaderProgress(reader, reader.length)
    if encoding == 'gzip':
        class WrapGzipFile(GzipFile):
            def finish(self):
                self.fileobj.finish()
        helper_content = WrapGzipFile(mode='rb', fileobj=helper_content)

    if args.dev is False:
        content = BytesIO()
        copyfileobj(helper_content, content)
        if hasattr(helper_content, 'finish'):
            helper_content.finish()
        content.seek(0)

        print('Extracting %s...' % helper)
        if ext == 'zip':
            zip = zipfile.ZipFile(content, 'r')
            info = zip.getinfo('git-cinnabar/%s' % helper)
            helper_content = ReaderProgress(zip.open(info), info.file_size)
        elif ext == 'tar.xz':
            class UntarProgress(ReaderProgress):
                def __init__(self, content, helper):
                    self._proc = subprocess.Popen(
                        ['tar', '-JxO', 'git-cinnabar/%s' % helper],
                        stdin=subprocess.PIPE, stdout=subprocess.PIPE)

                    super(UntarProgress, self).__init__(self._proc.stdout)

                    def send(stdin, content):
                        copyfileobj(content, stdin)
                        stdin.close()

                    self._thread = threading.Thread(
                        target=send, args=(self._proc.stdin, content))
                    self._thread.start()

                def finish(self):
                    self._proc.wait()
                    self._thread.join()
                    super(UntarProgress, self).finish()

            helper_content = UntarProgress(content, helper)

        else:
            assert False

    fd, path = tempfile.mkstemp(prefix=helper, dir=d)
    fh = os.fdopen(fd, 'wb')

    success = False
    try:
        copyfileobj(helper_content, fh)
        success = True
    finally:
        if hasattr(helper_content, 'finish'):
            helper_content.finish()
        fh.close()
        if success:
            mode = os.stat(path).st_mode
            if args.output:
                helper_path = args.output
            else:
                helper_path = os.path.join(d, helper)
            try:
                # on Windows it's necessary to remove the file first.
                os.remove(helper_path)
            except OSError as exc:
                if exc.errno != errno.ENOENT:
                    raise
                pass
            os.rename(path, helper_path)
            # Add executable bits wherever read bits are set
            mode = mode | ((mode & 0o0444) >> 2)
            os.chmod(helper_path, mode)

            if not args.no_config:
                Git.run('config', '--global', 'cinnabar.helper',
                        os.path.abspath(helper_path))
        else:
            os.unlink(path)

    return 0
Esempio n. 8
0
    def test_recovery(self):
        sizes = {}
        length = 0
        for s in [162000, 64000, 57932]:
            sizes[length] = s
            length += s
        the_test = self

        # This HTTP server handler cuts responses before the full content is
        # returned, according to the partial sizes defined above.
        # It assumes the client will retry with a Range request starting from
        # where it left, up to the end of the file.
        class Handler(BaseHTTPRequestHandler):
            redirected_once = False
            errored_once = False

            def do_GET(self):
                if self.path == '/foo':
                    the_test.assertFalse(Handler.redirected_once)
                    Handler.redirected_once = True
                    self.send_response(301)
                    self.send_header('Location', '/bar')
                    self.end_headers()
                    return
                elif self.path != '/bar':
                    self.send_response(404)
                    self.end_headers()
                    return
                range_def = self.headers.get('Range')
                if range_def:
                    if not Handler.errored_once:
                        Handler.errored_once = True
                        self.send_response(500)
                        self.end_headers()
                        return
                    start, end = range_def.partition('bytes=')[2].split('-')
                    start = int(start) if start else 0
                    end = int(end) if end else length - 1
                    the_test.assertIn(start, sizes)
                    the_test.assertEqual(end, length - 1)
                    self.send_response(206)
                    self.send_header('Content-Range',
                                     'bytes %d-%d/%d' % (start, end, length))
                else:
                    start = 0
                    self.send_response(200)
                self.send_header('Content-Type', 'text/plain')
                self.send_header('Content-Length', str(length))
                self.send_header('Accept-Ranges', 'bytes')
                self.end_headers()

                buf = b'-' * 4096
                left = sizes[start]
                while left:
                    if left < len(buf):
                        buf = buf[:left]
                    self.wfile.write(buf)
                    left -= len(buf)

            def log_request(self, *args, **kwargs):
                pass

        server = HTTPServer(('', 0), Handler)
        port = server.socket.getsockname()[1]
        thread = Thread(target=server.serve_forever)
        thread.start()
        try:
            reader = HTTPReader('http://localhost:%d/foo' % port)
            read = 0
            while True:
                buf = reader.read(1250)
                # If the read above is interrupted and the HTTPReader can
                # recover with a range request, we still expect the right
                # size.
                self.assertEqual(len(buf), min(1250, length - read))
                read += len(buf)
                if not buf:
                    break

            self.assertEqual(read, length)

        finally:
            server.shutdown()
            thread.join()