diff --git a/config/module/detect.go b/config/module/detect.go index 0021c0a9f..99b2af0a7 100644 --- a/config/module/detect.go +++ b/config/module/detect.go @@ -21,6 +21,7 @@ var Detectors []Detector func init() { Detectors = []Detector{ new(GitHubDetector), + new(BitBucketDetector), new(FileDetector), } } diff --git a/config/module/detect_bitbucket.go b/config/module/detect_bitbucket.go new file mode 100644 index 000000000..657637c09 --- /dev/null +++ b/config/module/detect_bitbucket.go @@ -0,0 +1,66 @@ +package module + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" +) + +// BitBucketDetector implements Detector to detect BitBucket URLs and turn +// them into URLs that the Git or Hg Getter can understand. +type BitBucketDetector struct{} + +func (d *BitBucketDetector) Detect(src, _ string) (string, bool, error) { + if len(src) == 0 { + return "", false, nil + } + + if strings.HasPrefix(src, "bitbucket.org/") { + return d.detectHTTP(src) + } + + return "", false, nil +} + +func (d *BitBucketDetector) detectHTTP(src string) (string, bool, error) { + u, err := url.Parse("https://" + src) + if err != nil { + return "", true, fmt.Errorf("error parsing BitBucket URL: %s", err) + } + + // We need to get info on this BitBucket repository to determine whether + // it is Git or Hg. + var info struct { + SCM string `json:"scm"` + } + infoUrl := "https://api.bitbucket.org/1.0/repositories" + u.Path + resp, err := http.Get(infoUrl) + if err != nil { + return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err) + } + if resp.StatusCode == 403 { + // A private repo + return "", true, fmt.Errorf( + "shorthand BitBucket URL can't be used for private repos, " + + "please use a full URL") + } + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&info); err != nil { + return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err) + } + + switch info.SCM { + case "git": + if !strings.HasSuffix(u.Path, ".git") { + u.Path += ".git" + } + + return "git::" + u.String(), true, nil + case "hg": + return "hg::" + u.String(), true, nil + default: + return "", true, fmt.Errorf("unknown BitBucket SCM type: %s", info.SCM) + } +} diff --git a/config/module/detect_bitbucket_test.go b/config/module/detect_bitbucket_test.go new file mode 100644 index 000000000..91dad2e59 --- /dev/null +++ b/config/module/detect_bitbucket_test.go @@ -0,0 +1,50 @@ +package module + +import ( + "net/http" + "testing" +) + +const testBBUrl = "https://bitbucket.org/hashicorp/tf-test-git" + +func TestBitBucketDetector(t *testing.T) { + if _, err := http.Get(testBBUrl); err != nil { + t.Log("internet may not be working, skipping BB tests") + t.Skip() + } + + cases := []struct { + Input string + Output string + }{ + // HTTP + { + "bitbucket.org/hashicorp/tf-test-git", + "git::https://bitbucket.org/hashicorp/tf-test-git.git", + }, + { + "bitbucket.org/hashicorp/tf-test-git.git", + "git::https://bitbucket.org/hashicorp/tf-test-git.git", + }, + { + "bitbucket.org/hashicorp/tf-test-hg", + "hg::https://bitbucket.org/hashicorp/tf-test-hg", + }, + } + + pwd := "/pwd" + f := new(BitBucketDetector) + for i, tc := range cases { + output, ok, err := f.Detect(tc.Input, pwd) + if err != nil { + t.Fatalf("err: %s", err) + } + if !ok { + t.Fatal("not ok") + } + + if output != tc.Output { + t.Fatalf("%d: bad: %#v", i, output) + } + } +}