diff --git a/api/handler.go b/api/handler.go index 910ddb3..5f533b8 100644 --- a/api/handler.go +++ b/api/handler.go @@ -333,3 +333,25 @@ func GetTree(w http.ResponseWriter, r *http.Request) { } w.Write(b) } + +func GetBranch(w http.ResponseWriter, r *http.Request) { + repo := r.URL.Query().Get(":name") + if repo == "" { + err := fmt.Errorf("Error when trying to obtain the branches of repository %s (repository is required).", repo) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + branches, err := repository.GetBranch(repo) + if err != nil { + err := fmt.Errorf("Error when trying to obtain the branches of repository %s (%s).", repo, err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + b, err := json.Marshal(branches) + if err != nil { + err := fmt.Errorf("Error when trying to obtain the branches of repository %s (%s).", repo, err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.Write(b) +} diff --git a/api/handler_test.go b/api/handler_test.go index 2c89d08..b0a8e85 100644 --- a/api/handler_test.go +++ b/api/handler_test.go @@ -971,3 +971,78 @@ func (s *S) TestGetTreeWhenCommandFails(c *gocheck.C) { c.Assert(recorder.Code, gocheck.Equals, http.StatusBadRequest) c.Assert(recorder.Body.String(), gocheck.Equals, "Error when trying to obtain tree for path /test on ref master of repository repo (output error).\n") } + +func (s *S) TestGetBranch(c *gocheck.C) { + url := "/repository/repo/branch?:name=repo" + refs := make([]map[string]string, 1) + refs[0] = make(map[string]string) + refs[0]["ref"] = "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9" + refs[0]["name"] = "doge_barks" + refs[0]["commiterName"] = "doge" + refs[0]["commiterEmail"] = "" + refs[0]["authorName"] = "doge" + refs[0]["authorEmail"] = "" + refs[0]["subject"] = "will bark" + mockRetriever := repository.MockContentRetriever{ + Refs: refs, + } + repository.Retriever = &mockRetriever + defer func() { + repository.Retriever = nil + }() + request, err := http.NewRequest("GET", url, nil) + c.Assert(err, gocheck.IsNil) + recorder := httptest.NewRecorder() + GetBranch(recorder, request) + c.Assert(recorder.Code, gocheck.Equals, http.StatusOK) + var obj []map[string]string + json.Unmarshal(recorder.Body.Bytes(), &obj) + c.Assert(len(obj), gocheck.Equals, 1) + c.Assert(obj[0]["ref"], gocheck.Equals, refs[0]["ref"]) + c.Assert(obj[0]["name"], gocheck.Equals, refs[0]["name"]) + c.Assert(obj[0]["commiterName"], gocheck.Equals, refs[0]["commiterName"]) + c.Assert(obj[0]["commiterEmail"], gocheck.Equals, refs[0]["commiterEmail"]) + c.Assert(obj[0]["authorName"], gocheck.Equals, refs[0]["authorName"]) + c.Assert(obj[0]["authorEmail"], gocheck.Equals, refs[0]["authorEmail"]) + c.Assert(obj[0]["subject"], gocheck.Equals, refs[0]["subject"]) +} + +func (s *S) TestGetBranchWhenRepoNotSupplied(c *gocheck.C) { + url := "/repository//branch?:name=" + request, err := http.NewRequest("GET", url, nil) + c.Assert(err, gocheck.IsNil) + recorder := httptest.NewRecorder() + GetBranch(recorder, request) + c.Assert(recorder.Code, gocheck.Equals, http.StatusBadRequest) + expected := "Error when trying to obtain the branches of repository (repository is required).\n" + c.Assert(recorder.Body.String(), gocheck.Equals, expected) +} + +func (s *S) TestGetBranchWhenRepoNonExistent(c *gocheck.C) { + url := "/repository/repo/branch?:name=repo" + request, err := http.NewRequest("GET", url, nil) + c.Assert(err, gocheck.IsNil) + recorder := httptest.NewRecorder() + GetBranch(recorder, request) + c.Assert(recorder.Code, gocheck.Equals, http.StatusBadRequest) + expected := "Error when trying to obtain the branches of repository repo (Error when trying to obtain the refs of repository repo (Repository does not exist).).\n" + c.Assert(recorder.Body.String(), gocheck.Equals, expected) +} + +func (s *S) TestGetBranchWhenCommandFails(c *gocheck.C) { + url := "/repository/repo/branch/?:name=repo" + expected := fmt.Errorf("output error") + mockRetriever := repository.MockContentRetriever{ + OutputError: expected, + } + repository.Retriever = &mockRetriever + defer func() { + repository.Retriever = nil + }() + request, err := http.NewRequest("GET", url, nil) + c.Assert(err, gocheck.IsNil) + recorder := httptest.NewRecorder() + GetBranch(recorder, request) + c.Assert(recorder.Code, gocheck.Equals, http.StatusBadRequest) + c.Assert(recorder.Body.String(), gocheck.Equals, "Error when trying to obtain the branches of repository repo (output error).\n") +} diff --git a/docs/source/api.rst b/docs/source/api.rst index 7914c30..4243ecd 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -129,3 +129,20 @@ Example URLs (http://gandalf-server omitted for clarity):: $ curl /repository/myrepository/archive/master.zip # gets master and zip format $ curl /repository/myrepository/archive/master.tar.gz # gets master and tar.gz format $ curl /repository/myrepository/archive/0.1.0.zip # gets 0.1.0 tag and zip format + +Get branch +----------- + +Returns a list of all the branches of the specified `repository`. + +* Method: GET +* URI: /repository/`:name`/branch +* Format: JSON + +Where: + +* `:name` is the name of the repository. + +Example URL (http://gandalf-server omitted for clarity):: + + $ curl /repository/myrepository/branch # gets list of branches diff --git a/repository/mocks.go b/repository/mocks.go index 17afd92..e64874b 100644 --- a/repository/mocks.go +++ b/repository/mocks.go @@ -103,7 +103,7 @@ func CreateTestRepository(tmp_path string, repo string, file string, content str return cleanup, err } -func CreateCommitOnTestRepository(tmpPath string, repo string, file string, content string) ([]byte, error) { +func CreateCommitOnTestRepository(tmpPath, repo, file, content string) ([]byte, error) { testPath := path.Join(tmpPath, repo+".git") gitPath, err := exec.LookPath("git") if err != nil { @@ -134,7 +134,7 @@ func CreateCommitOnTestRepository(tmpPath string, repo string, file string, cont return out, nil } -func CreateBranchesOnTestRepository(tmp_path string, repo string, file string, content string, branches ...string) error { +func CreateBranchesOnTestRepository(tmp_path string, repo string, branches ...string) error { testPath := path.Join(tmp_path, repo+".git") gitPath, err := exec.LookPath("git") if err != nil { @@ -147,37 +147,12 @@ func CreateBranchesOnTestRepository(tmp_path string, repo string, file string, c return err } for _, branch := range branches { - fp, err := os.OpenFile(path.Join(testPath, file), os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer fp.Close() - _, err = fp.WriteString("such string") - if err != nil { - return err - } cmd = exec.Command(gitPath, "checkout", "-b", branch) cmd.Dir = testPath err = cmd.Run() if err != nil { return err } - cmd = exec.Command(gitPath, "add", ".") - cmd.Dir = testPath - err = cmd.Run() - if err != nil { - return err - } - if len(content) > 0 { - cmd = exec.Command(gitPath, "commit", "-m", content+" on "+branch) - } else { - cmd = exec.Command(gitPath, "commit", "-m", "", "--allow-empty-message") - } - cmd.Dir = testPath - err = cmd.Run() - if err != nil { - return err - } } return err } diff --git a/repository/repository.go b/repository/repository.go index eaaa657..3b43464 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -356,18 +356,22 @@ func (*GitContentRetriever) GetForEachRef(repo, pattern string) ([]map[string]st var ref, name, commiterName, commiterEmail, commiterDate, authorName, authorEmail, authorDate, subject string gitPath, err := exec.LookPath("git") if err != nil { - return nil, fmt.Errorf("Error when trying to obtain the branches of repository %s (%s).", repo, err) + return nil, fmt.Errorf("Error when trying to obtain the refs of repository %s (%s).", repo, err) } cwd := barePath(repo) repoExists, err := exists(cwd) if err != nil || !repoExists { - return nil, fmt.Errorf("Error when trying to obtain the branches of repository %s (Repository does not exist).", repo) + return nil, fmt.Errorf("Error when trying to obtain the refs of repository %s (Repository does not exist).", repo) + } + format := "%(objectname)%09%(refname:short)%09%(committername)%09%(committeremail)%09%(committerdate)%09%(authorname)%09%(authoremail)%09%(authordate)%09%(contents:subject)" + cmd := exec.Command(gitPath, "for-each-ref", "--sort=-committerdate", "--format", format) + if len(pattern) > 0 { + cmd.Args = append(cmd.Args, pattern) } - cmd := exec.Command(gitPath, "for-each-ref", "--sort=-committerdate", "--format", "%(objectname)%09%(refname)%09%(committername)%09%(committeremail)%09%(committerdate)%09%(authorname)%09%(authoremail)%09%(authordate)%09%(contents:subject)", pattern) cmd.Dir = cwd out, err := cmd.Output() if err != nil { - return nil, fmt.Errorf("Error when trying to obtain the branches of repository %s (%s).", repo, err) + return nil, fmt.Errorf("Error when trying to obtain the refs of repository %s (%s).", repo, err) } lines := strings.Split(strings.TrimSpace(string(out)), "\n") objectCount := len(lines) @@ -378,9 +382,9 @@ func (*GitContentRetriever) GetForEachRef(repo, pattern string) ([]map[string]st continue } fields := strings.Split(line, "\t") - if len(fields) > 4 { // let there be commits with empty subject + if len(fields) > 7 { // let there be commits with empty subject ref = fields[0] - name = strings.Replace(fields[1], pattern, "", 1) + name = fields[1] commiterName = fields[2] commiterEmail = fields[3] commiterDate = fields[4] @@ -389,7 +393,7 @@ func (*GitContentRetriever) GetForEachRef(repo, pattern string) ([]map[string]st authorDate = fields[7] subject = strings.Join(fields[8:], "\t") // let there be subjects with \t } else { - return nil, fmt.Errorf("Error when trying to obtain the branches of repository %s (Invalid git for-each-ref output [%s]).", repo, out) + return nil, fmt.Errorf("Error when trying to obtain the refs of repository %s (Invalid git for-each-ref output [%s]).", repo, out) } object := make(map[string]string) object["ref"] = ref diff --git a/repository/repository_test.go b/repository/repository_test.go index 1364289..cc88910 100644 --- a/repository/repository_test.go +++ b/repository/repository_test.go @@ -816,25 +816,25 @@ func (s *S) TestGetBranchIntegration(c *gocheck.C) { bare = oldBare }() c.Assert(errCreate, gocheck.IsNil) - errCreateBranches := CreateBranchesOnTestRepository(bare, repo, file, content, "doge_bites", "doge_barks") + errCreateBranches := CreateBranchesOnTestRepository(bare, repo, "doge_bites", "doge_barks") c.Assert(errCreateBranches, gocheck.IsNil) branches, err := GetBranch(repo) c.Assert(err, gocheck.IsNil) - c.Assert(len(branches), gocheck.Equals, 3) + c.Assert(branches, gocheck.HasLen, 3) c.Assert(branches[0]["ref"], gocheck.Matches, "[a-f0-9]{40}") c.Assert(branches[0]["name"], gocheck.Equals, "doge_barks") c.Assert(branches[0]["commiterName"], gocheck.Equals, "doge") c.Assert(branches[0]["commiterEmail"], gocheck.Equals, "") c.Assert(branches[0]["authorName"], gocheck.Equals, "doge") c.Assert(branches[0]["authorEmail"], gocheck.Equals, "") - c.Assert(branches[0]["subject"], gocheck.Equals, "will bark on doge_barks") + c.Assert(branches[0]["subject"], gocheck.Equals, "will bark") c.Assert(branches[1]["ref"], gocheck.Matches, "[a-f0-9]{40}") c.Assert(branches[1]["name"], gocheck.Equals, "doge_bites") c.Assert(branches[1]["commiterName"], gocheck.Equals, "doge") c.Assert(branches[1]["commiterEmail"], gocheck.Equals, "") c.Assert(branches[1]["authorName"], gocheck.Equals, "doge") c.Assert(branches[1]["authorEmail"], gocheck.Equals, "") - c.Assert(branches[1]["subject"], gocheck.Equals, "will bark on doge_bites") + c.Assert(branches[1]["subject"], gocheck.Equals, "will bark") c.Assert(branches[2]["ref"], gocheck.Matches, "[a-f0-9]{40}") c.Assert(branches[2]["name"], gocheck.Equals, "master") c.Assert(branches[2]["commiterName"], gocheck.Equals, "doge") @@ -844,7 +844,7 @@ func (s *S) TestGetBranchIntegration(c *gocheck.C) { c.Assert(branches[2]["subject"], gocheck.Equals, "will bark") } -func (s *S) TestGetBranchIntegrationEmptySubject(c *gocheck.C) { +func (s *S) TestGetForEachRefIntegrationWithSubjectEmpty(c *gocheck.C) { oldBare := bare bare = "/tmp" repo := "gandalf-test-repo" @@ -856,25 +856,135 @@ func (s *S) TestGetBranchIntegrationEmptySubject(c *gocheck.C) { bare = oldBare }() c.Assert(errCreate, gocheck.IsNil) - errCreateBranches := CreateBranchesOnTestRepository(bare, repo, file, content, "doge_howls") + errCreateBranches := CreateBranchesOnTestRepository(bare, repo, "doge_howls") c.Assert(errCreateBranches, gocheck.IsNil) - branches, err := GetBranch(repo) + refs, err := GetForEachRef(repo, "refs/") + c.Assert(err, gocheck.IsNil) + c.Assert(refs, gocheck.HasLen, 2) + c.Assert(refs[0]["ref"], gocheck.Matches, "[a-f0-9]{40}") + c.Assert(refs[0]["name"], gocheck.Equals, "doge_howls") + c.Assert(refs[0]["commiterName"], gocheck.Equals, "doge") + c.Assert(refs[0]["commiterEmail"], gocheck.Equals, "") + c.Assert(refs[0]["authorName"], gocheck.Equals, "doge") + c.Assert(refs[0]["authorEmail"], gocheck.Equals, "") + c.Assert(refs[0]["subject"], gocheck.Equals, "") + c.Assert(refs[1]["ref"], gocheck.Matches, "[a-f0-9]{40}") + c.Assert(refs[1]["name"], gocheck.Equals, "master") + c.Assert(refs[1]["commiterName"], gocheck.Equals, "doge") + c.Assert(refs[1]["commiterEmail"], gocheck.Equals, "") + c.Assert(refs[1]["authorName"], gocheck.Equals, "doge") + c.Assert(refs[1]["authorEmail"], gocheck.Equals, "") + c.Assert(refs[1]["subject"], gocheck.Equals, "") +} + +func (s *S) TestGetForEachRefIntegrationWithSubjectTabbed(c *gocheck.C) { + oldBare := bare + bare = "/tmp" + repo := "gandalf-test-repo" + file := "README" + content := "will\tbark" + cleanUp, errCreate := CreateTestRepository(bare, repo, file, content) + defer func() { + cleanUp() + bare = oldBare + }() + c.Assert(errCreate, gocheck.IsNil) + errCreateBranches := CreateBranchesOnTestRepository(bare, repo, "doge_howls") + c.Assert(errCreateBranches, gocheck.IsNil) + refs, err := GetForEachRef(repo, "refs/") + c.Assert(err, gocheck.IsNil) + c.Assert(refs, gocheck.HasLen, 2) + c.Assert(refs[0]["ref"], gocheck.Matches, "[a-f0-9]{40}") + c.Assert(refs[0]["name"], gocheck.Equals, "doge_howls") + c.Assert(refs[0]["commiterName"], gocheck.Equals, "doge") + c.Assert(refs[0]["commiterEmail"], gocheck.Equals, "") + c.Assert(refs[0]["authorName"], gocheck.Equals, "doge") + c.Assert(refs[0]["authorEmail"], gocheck.Equals, "") + c.Assert(refs[0]["subject"], gocheck.Equals, "will\tbark") + c.Assert(refs[1]["ref"], gocheck.Matches, "[a-f0-9]{40}") + c.Assert(refs[1]["name"], gocheck.Equals, "master") + c.Assert(refs[1]["commiterName"], gocheck.Equals, "doge") + c.Assert(refs[1]["commiterEmail"], gocheck.Equals, "") + c.Assert(refs[1]["authorName"], gocheck.Equals, "doge") + c.Assert(refs[1]["authorEmail"], gocheck.Equals, "") + c.Assert(refs[1]["subject"], gocheck.Equals, "will\tbark") +} + +func (s *S) TestGetForEachRefIntegrationWhenPatternEmpty(c *gocheck.C) { + oldBare := bare + bare = "/tmp" + repo := "gandalf-test-repo" + file := "README" + content := "much WOW" + cleanUp, errCreate := CreateTestRepository(bare, repo, file, content) + defer func() { + cleanUp() + bare = oldBare + }() + c.Assert(errCreate, gocheck.IsNil) + refs, err := GetForEachRef("gandalf-test-repo", "") c.Assert(err, gocheck.IsNil) - c.Assert(len(branches), gocheck.Equals, 2) - c.Assert(branches[0]["ref"], gocheck.Matches, "[a-f0-9]{40}") - c.Assert(branches[0]["name"], gocheck.Equals, "doge_howls") - c.Assert(branches[0]["commiterName"], gocheck.Equals, "doge") - c.Assert(branches[0]["commiterEmail"], gocheck.Equals, "") - c.Assert(branches[0]["authorName"], gocheck.Equals, "doge") - c.Assert(branches[0]["authorEmail"], gocheck.Equals, "") - c.Assert(branches[0]["subject"], gocheck.Equals, "") - c.Assert(branches[1]["ref"], gocheck.Matches, "[a-f0-9]{40}") - c.Assert(branches[1]["name"], gocheck.Equals, "master") - c.Assert(branches[1]["commiterName"], gocheck.Equals, "doge") - c.Assert(branches[1]["commiterEmail"], gocheck.Equals, "") - c.Assert(branches[1]["authorName"], gocheck.Equals, "doge") - c.Assert(branches[1]["authorEmail"], gocheck.Equals, "") - c.Assert(branches[1]["subject"], gocheck.Equals, "") + c.Assert(refs, gocheck.HasLen, 1) + c.Assert(refs[0], gocheck.HasLen, 9) +} + +func (s *S) TestGetForEachRefIntegrationWhenPatternNonExistent(c *gocheck.C) { + oldBare := bare + bare = "/tmp" + repo := "gandalf-test-repo" + file := "README" + content := "much WOW" + cleanUp, errCreate := CreateTestRepository(bare, repo, file, content) + defer func() { + cleanUp() + bare = oldBare + }() + c.Assert(errCreate, gocheck.IsNil) + refs, err := GetForEachRef("gandalf-test-repo", "non_existent_pattern") + c.Assert(err, gocheck.IsNil) + c.Assert(refs, gocheck.HasLen, 1) + c.Assert(refs[0], gocheck.HasLen, 0) +} + +func (s *S) TestGetForEachRefIntegrationWhenInvalidRepo(c *gocheck.C) { + _, err := GetForEachRef("invalid-repo", "refs/") + c.Assert(err.Error(), gocheck.Equals, "Error when trying to obtain the refs of repository invalid-repo (Repository does not exist).") +} + +func (s *S) TestGetForEachRefIntegrationWhenPatternSpaced(c *gocheck.C) { + oldBare := bare + bare = "/tmp" + repo := "gandalf-test-repo" + file := "README" + content := "much WOW" + cleanUp, errCreate := CreateTestRepository(bare, repo, file, content) + defer func() { + cleanUp() + bare = oldBare + }() + c.Assert(errCreate, gocheck.IsNil) + errCreateBranches := CreateBranchesOnTestRepository(bare, repo, "doge_howls") + c.Assert(errCreateBranches, gocheck.IsNil) + refs, err := GetForEachRef("gandalf-test-repo", "much bark") + c.Assert(err, gocheck.IsNil) + c.Assert(refs, gocheck.HasLen, 1) + c.Assert(refs[0], gocheck.HasLen, 0) +} + +func (s *S) TestGetForEachRefIntegrationWhenPatternInvalid(c *gocheck.C) { + oldBare := bare + bare = "/tmp" + repo := "gandalf-test-repo" + file := "README" + content := "much WOW" + cleanUp, errCreate := CreateTestRepository(bare, repo, file, content) + defer func() { + cleanUp() + bare = oldBare + }() + c.Assert(errCreate, gocheck.IsNil) + _, err := GetForEachRef("gandalf-test-repo", "--format") + c.Assert(err.Error(), gocheck.Equals, "Error when trying to obtain the refs of repository gandalf-test-repo (exit status 129).") } func (s *S) TestGetDiffIntegration(c *gocheck.C) { diff --git a/webserver/main.go b/webserver/main.go index d280cc5..5b82bca 100644 --- a/webserver/main.go +++ b/webserver/main.go @@ -49,6 +49,7 @@ For an example conf check gandalf/etc/gandalf.conf file.\n %s` router.Get("/repository/:name/contents", http.HandlerFunc(api.GetFileContents)) router.Get("/repository/:name/tree/:path", http.HandlerFunc(api.GetTree)) router.Get("/repository/:name/tree", http.HandlerFunc(api.GetTree)) + router.Get("/repository/:name/branch", http.HandlerFunc(api.GetTree)) router.Get("/healthcheck/", http.HandlerFunc(api.HealthCheck)) router.Post("/hook/:name", http.HandlerFunc(api.AddHook))