Example #1
0
 def run_command(self, host, command, username=None, port=None):
     assert isinstance(command, str)
     command = command.encode(SSHGitClient.DEFAULT_ENCODING)
     sshcmd = ui.config(b"ui", b"ssh", b"ssh")
     args = procutil.sshargs(
         sshcmd, pycompat.bytesurl(host), username, port
     )
     cmd = b'%s %s %s' % (sshcmd, args, procutil.shellquote(command))
     # consistent with mercurial
     ui.debug(b'running %s\n' % cmd)
     # we cannot use Mercurial's procutil.popen4() since it
     # always redirects stderr into a pipe
     proc = subprocess.Popen(
         procutil.tonativestr(cmd),
         shell=True,
         bufsize=0,
         stdin=subprocess.PIPE,
         stdout=subprocess.PIPE,
     )
     return SubprocessWrapper(proc)
Example #2
0
    def _basictransfer(self, obj, action, localstore):
        """Download or upload a single object using basic transfer protocol

        obj: dict, an object description returned by batch API
        action: string, one of ['upload', 'download']
        localstore: blobstore.local

        See https://github.com/git-lfs/git-lfs/blob/master/docs/api/\
        basic-transfers.md
        """
        oid = obj[b'oid']
        href = obj[b'actions'][action].get(b'href')
        headers = obj[b'actions'][action].get(b'header', {}).items()

        request = util.urlreq.request(pycompat.strurl(href))
        if action == b'upload':
            # If uploading blobs, read data from local blobstore.
            if not localstore.verify(oid):
                raise error.Abort(
                    _(b'detected corrupt lfs object: %s') % oid,
                    hint=_(b'run hg verify'),
                )
            request.data = filewithprogress(localstore.open(oid), None)
            request.get_method = lambda: r'PUT'
            request.add_header(r'Content-Type', r'application/octet-stream')
            request.add_header(r'Content-Length', len(request.data))

        for k, v in headers:
            request.add_header(pycompat.strurl(k), pycompat.strurl(v))

        response = b''
        try:
            with contextlib.closing(self.urlopener.open(request)) as req:
                ui = self.ui  # Shorten debug lines
                if self.ui.debugflag:
                    ui.debug(b'Status: %d\n' % req.status)
                    # lfs-test-server and hg serve return headers in different
                    # order
                    headers = pycompat.bytestr(req.info()).strip()
                    ui.debug(b'%s\n' % b'\n'.join(sorted(headers.splitlines())))

                if action == b'download':
                    # If downloading blobs, store downloaded data to local
                    # blobstore
                    localstore.download(oid, req)
                else:
                    while True:
                        data = req.read(1048576)
                        if not data:
                            break
                        response += data
                    if response:
                        ui.debug(b'lfs %s response: %s' % (action, response))
        except util.urlerr.httperror as ex:
            if self.ui.debugflag:
                self.ui.debug(
                    b'%s: %s\n' % (oid, ex.read())
                )  # XXX: also bytes?
            raise LfsRemoteError(
                _(b'LFS HTTP error: %s (oid=%s, action=%s)')
                % (stringutil.forcebytestr(ex), oid, action)
            )
        except util.urlerr.urlerror as ex:
            hint = _(b'attempted connection to %s') % pycompat.bytesurl(
                util.urllibcompat.getfullurl(request)
            )
            raise LfsRemoteError(
                _(b'LFS error: %s') % _urlerrorreason(ex), hint=hint
            )
Example #3
0
    def _batchrequest(self, pointers, action):
        """Get metadata about objects pointed by pointers for given action

        Return decoded JSON object like {'objects': [{'oid': '', 'size': 1}]}
        See https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
        """
        objects = [
            {r'oid': pycompat.strurl(p.oid()), r'size': p.size()}
            for p in pointers
        ]
        requestdata = pycompat.bytesurl(
            json.dumps(
                {r'objects': objects, r'operation': pycompat.strurl(action),}
            )
        )
        url = b'%s/objects/batch' % self.baseurl
        batchreq = util.urlreq.request(pycompat.strurl(url), data=requestdata)
        batchreq.add_header(r'Accept', r'application/vnd.git-lfs+json')
        batchreq.add_header(r'Content-Type', r'application/vnd.git-lfs+json')
        try:
            with contextlib.closing(self.urlopener.open(batchreq)) as rsp:
                rawjson = rsp.read()
        except util.urlerr.httperror as ex:
            hints = {
                400: _(
                    b'check that lfs serving is enabled on %s and "%s" is '
                    b'supported'
                )
                % (self.baseurl, action),
                404: _(b'the "lfs.url" config may be used to override %s')
                % self.baseurl,
            }
            hint = hints.get(ex.code, _(b'api=%s, action=%s') % (url, action))
            raise LfsRemoteError(
                _(b'LFS HTTP error: %s') % stringutil.forcebytestr(ex),
                hint=hint,
            )
        except util.urlerr.urlerror as ex:
            hint = (
                _(b'the "lfs.url" config may be used to override %s')
                % self.baseurl
            )
            raise LfsRemoteError(
                _(b'LFS error: %s') % _urlerrorreason(ex), hint=hint
            )
        try:
            response = pycompat.json_loads(rawjson)
        except ValueError:
            raise LfsRemoteError(
                _(b'LFS server returns invalid JSON: %s')
                % rawjson.encode("utf-8")
            )

        if self.ui.debugflag:
            self.ui.debug(b'Status: %d\n' % rsp.status)
            # lfs-test-server and hg serve return headers in different order
            headers = pycompat.bytestr(rsp.info()).strip()
            self.ui.debug(b'%s\n' % b'\n'.join(sorted(headers.splitlines())))

            if r'objects' in response:
                response[r'objects'] = sorted(
                    response[r'objects'], key=lambda p: p[r'oid']
                )
            self.ui.debug(
                b'%s\n'
                % pycompat.bytesurl(
                    json.dumps(
                        response,
                        indent=2,
                        separators=(r'', r': '),
                        sort_keys=True,
                    )
                )
            )

        def encodestr(x):
            if isinstance(x, pycompat.unicode):
                return x.encode('utf-8')
            return x

        return pycompat.rapply(encodestr, response)