Add support for +archive
Change-Id: I765d492a59592908c2d91729470b0e78b39c564b
diff --git a/gitiles/client.go b/gitiles/client.go
index 9cb6d0f..de60c5c 100644
--- a/gitiles/client.go
+++ b/gitiles/client.go
@@ -21,6 +21,7 @@
"encoding/json"
"flag"
"fmt"
+ "io"
"io/ioutil"
"log"
"net/http"
@@ -124,7 +125,7 @@
return s, nil
}
-func (s *Service) get(u *url.URL) ([]byte, error) {
+func (s *Service) stream(u *url.URL) (*http.Response, error) {
ctx := context.Background()
if err := s.limiter.Wait(ctx); err != nil {
@@ -140,8 +141,9 @@
if err != nil {
return nil, err
}
- defer resp.Body.Close()
+
if resp.StatusCode != 200 {
+ resp.Body.Close()
return nil, fmt.Errorf("%s: %s", u.String(), resp.Status)
}
@@ -149,12 +151,24 @@
log.Printf("%s %s: %d", req.Method, req.URL, resp.StatusCode)
}
if got := resp.Request.URL.String(); got != u.String() {
+ resp.Body.Close()
// We accept redirects, but only for authentication.
// If we get a 200 from a different page than we
// requested, it's probably some sort of login page.
return nil, fmt.Errorf("got URL %s, want %s", got, u.String())
}
+ return resp, nil
+}
+
+func (s *Service) get(u *url.URL) ([]byte, error) {
+ resp, err := s.stream(u)
+ if err != nil {
+ return nil, err
+ }
+
+ defer resp.Body.Close()
+
c, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
@@ -245,6 +259,38 @@
return s.service.get(&blobURL)
}
+// Archive formats for +archive. JGit also supports some shorthands.
+const (
+ ArchiveTbz = "tar.bz2"
+ ArchiveTgz = "tar.gz"
+ ArchiveTar = "tar"
+ ArchiveTxz = "tar.xz"
+
+ // the Gitiles source code claims .tar.xz and .tar are
+ // supported, but googlesource.com doesn't support it,
+ // apparently. In addition, JGit provides ZipFormat, but
+ // gitiles doesn't support it.
+)
+
+// GetArchive downloads an archive of the project. Format is one
+// ArchivXxx formats. dirPrefix, if given, restricts to the given
+// subpath, and strips the path prefix from the files in the resulting
+// tar archive. revision is a git revision, either a branch/tag name
+// ("master") or a hex commit SHA1.
+func (s *RepoService) GetArchive(revision, dirPrefix, format string) (io.ReadCloser, error) {
+ u := s.service.addr
+ u.Path = path.Join(u.Path, s.Name, "+archive", revision)
+ if dirPrefix != "" {
+ u.Path = path.Join(u.Path, dirPrefix)
+ }
+ u.Path += "." + format
+ resp, err := s.service.stream(&u)
+ if err != nil {
+ return nil, err
+ }
+ return resp.Body, err
+}
+
// GetTree fetches a tree. The dir argument may not point to a
// blob. If recursive is given, the server recursively expands the
// tree.
diff --git a/gitiles/prod_test.go b/gitiles/prod_test.go
new file mode 100644
index 0000000..8ee9f87
--- /dev/null
+++ b/gitiles/prod_test.go
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package gitiles
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "io"
+ "testing"
+)
+
+func TestProductionArchive(t *testing.T) {
+ gs, err := NewService(Options{
+ Address: "https://go.googlesource.com",
+ })
+ if err != nil {
+ t.Fatalf("NewService: %v", err)
+ }
+
+ repo := gs.NewRepoService("crypto")
+ if err != nil {
+ t.Fatalf("NewRepoService: %v", err)
+ }
+
+ stream, err := repo.GetArchive("master", "ssh", ArchiveTgz)
+ if err != nil {
+ t.Fatalf("GetArchive: %v", err)
+ }
+ defer stream.Close()
+
+ gz, err := gzip.NewReader(stream)
+ if err != nil {
+ t.Fatalf("gzip.NewReader: %v", err)
+ }
+ r := tar.NewReader(gz)
+
+ names := map[string]bool{}
+ for {
+ hdr, err := r.Next()
+ if err == io.EOF {
+ break
+ }
+ names[hdr.Name] = true
+ }
+
+ if !names["mux.go"] {
+ t.Fatal("did not find 'mux.go', got %v", names)
+ }
+}