~rogpeppe/juju-core/387-use-testing-set

« back to all changes in this revision

Viewing changes to bzr/bzr.go

  • Committer: Gustavo Niemeyer
  • Date: 2013-04-03 21:16:56 UTC
  • mto: (1104.3.1 publish-command)
  • mto: This revision was merged to the branch mainline in revision 1116.
  • Revision ID: gustavo@niemeyer.net-20130403211656-30su6znhc9i3255w
bzr: add package to handle Bazaar branches

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Package bzr offers an interface to manage branches of the Bazaar VCS.
 
2
package bzr
 
3
 
 
4
import (
 
5
        "bytes"
 
6
        "fmt"
 
7
        "os"
 
8
        "os/exec"
 
9
        "path"
 
10
        "strings"
 
11
)
 
12
 
 
13
// Branch represents a Bazaar branch.
 
14
type Branch struct {
 
15
        location string
 
16
}
 
17
 
 
18
// New returns a new Branch for the Bazaar branch at location.
 
19
func New(location string) (*Branch, error) {
 
20
        b := &Branch{location}
 
21
        if _, err := os.Stat(location); err == nil {
 
22
                stdout, _, err := b.bzr("root")
 
23
                if err == nil {
 
24
                        b.location = strings.TrimRight(string(stdout), "\n")
 
25
                } else if !strings.Contains(err.Error(), "Not a branch") {
 
26
                        return nil, err
 
27
                }
 
28
        }
 
29
        return b, nil
 
30
}
 
31
 
 
32
// Location returns the location of branch b.
 
33
func (b *Branch) Location() string {
 
34
        return b.location
 
35
}
 
36
 
 
37
// Join returns b's location with parts appended as path components.
 
38
// In other words, if b's location is "lp:foo", and parts is {"bar, baz"},
 
39
// Join returns "lp:foo/bar/baz".
 
40
func (b *Branch) Join(parts ...string) string {
 
41
        return path.Join(append([]string{b.location}, parts...)...)
 
42
}
 
43
 
 
44
func (b *Branch) bzr(args ...string) (stdout, stderr []byte, err error) {
 
45
        if len(args) == 0 {
 
46
                panic("no point in runing bzr without arguments here")
 
47
        }
 
48
        cmd := exec.Command("bzr", args...)
 
49
        if _, err := os.Stat(b.location); err == nil {
 
50
                cmd.Dir = b.location
 
51
        }
 
52
        errbuf := &bytes.Buffer{}
 
53
        cmd.Stderr = errbuf
 
54
        stdout, err = cmd.Output()
 
55
        // Some commands fail with exit status 0 (e.g. bzr root). :-(
 
56
        if err != nil || bytes.Contains(errbuf.Bytes(), []byte("ERROR")) {
 
57
                var errmsg string
 
58
                if err != nil {
 
59
                        errmsg = err.Error()
 
60
                }
 
61
                return nil, nil, fmt.Errorf(`error running "bzr %s": %s%s%s`, args[0], stdout, errbuf.Bytes(), errmsg)
 
62
        }
 
63
        return stdout, errbuf.Bytes(), err
 
64
}
 
65
 
 
66
// Init intializes a new branch at b's location.
 
67
func (b *Branch) Init() error {
 
68
        _, _, err := b.bzr("init", b.location)
 
69
        return err
 
70
}
 
71
 
 
72
// Add adds to b the path resultant from calling b.Join(parts...).
 
73
func (b *Branch) Add(parts ...string) error {
 
74
        _, _, err := b.bzr("add", b.Join(parts...))
 
75
        return err
 
76
}
 
77
 
 
78
// Commit commits pending changes into b.
 
79
func (b *Branch) Commit(message string) error {
 
80
        _, _, err := b.bzr("commit", "-q", "-m", message)
 
81
        return err
 
82
}
 
83
 
 
84
// RevisionId returns the Bazaar revision id for the tip of b.
 
85
func (b *Branch) RevisionId() (string, error) {
 
86
        stdout, stderr, err := b.bzr("revision-info", "-d", b.location)
 
87
        if err != nil {
 
88
                return "", err
 
89
        }
 
90
        pair := bytes.Fields(stdout)
 
91
        if len(pair) != 2 {
 
92
                return "", fmt.Errorf(`invalid output from "bzr revision-info": %s%s`, stdout, stderr)
 
93
        }
 
94
        id := string(pair[1])
 
95
        if id == "null:" {
 
96
                return "", fmt.Errorf("branch has no content")
 
97
        }
 
98
        return id, nil
 
99
}
 
100
 
 
101
// PushLocation returns the default push location for b.
 
102
func (b *Branch) PushLocation() (string, error) {
 
103
        stdout, _, err := b.bzr("info", b.location)
 
104
        if err != nil {
 
105
                return "", err
 
106
        }
 
107
        if i := bytes.Index(stdout, []byte("push branch:")); i >= 0 {
 
108
                return string(stdout[i+13 : i+bytes.IndexAny(stdout[i:], "\r\n")]), nil
 
109
        }
 
110
        return "", fmt.Errorf("no push branch location defined")
 
111
}
 
112
 
 
113
// PushAttr holds options for the Branch.Push method.
 
114
type PushAttr struct {
 
115
        Location string // Location to push to. Use the default push location if empty.
 
116
        Remember bool   // Whether to remember the location being pushed to as the default.
 
117
}
 
118
 
 
119
// Push pushes any new revisions in b to attr.Location if that's
 
120
// provided, or to the default push location otherwise.
 
121
// See PushAttr for other options.
 
122
func (b *Branch) Push(attr *PushAttr) error {
 
123
        args := []string{"push"}
 
124
        if attr != nil {
 
125
                if attr.Remember {
 
126
                        args = append(args, "--remember")
 
127
                }
 
128
                if attr.Location != "" {
 
129
                        args = append(args, attr.Location)
 
130
                }
 
131
        }
 
132
        _, _, err := b.bzr(args...)
 
133
        return err
 
134
}