1package getter
2
3import (
4	"encoding/json"
5	"fmt"
6	"net/http"
7	"net/url"
8	"strings"
9)
10
11// BitBucketDetector implements Detector to detect BitBucket URLs and turn
12// them into URLs that the Git or Hg Getter can understand.
13type BitBucketDetector struct{}
14
15func (d *BitBucketDetector) Detect(src, _ string) (string, bool, error) {
16	if len(src) == 0 {
17		return "", false, nil
18	}
19
20	if strings.HasPrefix(src, "bitbucket.org/") {
21		return d.detectHTTP(src)
22	}
23
24	return "", false, nil
25}
26
27func (d *BitBucketDetector) detectHTTP(src string) (string, bool, error) {
28	u, err := url.Parse("https://" + src)
29	if err != nil {
30		return "", true, fmt.Errorf("error parsing BitBucket URL: %s", err)
31	}
32
33	// We need to get info on this BitBucket repository to determine whether
34	// it is Git or Hg.
35	var info struct {
36		SCM string `json:"scm"`
37	}
38	infoUrl := "https://api.bitbucket.org/2.0/repositories" + u.Path
39	resp, err := http.Get(infoUrl)
40	if err != nil {
41		return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err)
42	}
43	if resp.StatusCode == 403 {
44		// A private repo
45		return "", true, fmt.Errorf(
46			"shorthand BitBucket URL can't be used for private repos, " +
47				"please use a full URL")
48	}
49	dec := json.NewDecoder(resp.Body)
50	if err := dec.Decode(&info); err != nil {
51		return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err)
52	}
53
54	switch info.SCM {
55	case "git":
56		if !strings.HasSuffix(u.Path, ".git") {
57			u.Path += ".git"
58		}
59
60		return "git::" + u.String(), true, nil
61	case "hg":
62		return "hg::" + u.String(), true, nil
63	default:
64		return "", true, fmt.Errorf("unknown BitBucket SCM type: %s", info.SCM)
65	}
66}
67