~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/bzr/bzr.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

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
        env      []string
 
17
}
 
18
 
 
19
// New returns a new Branch for the Bazaar branch at location.
 
20
func New(location string) *Branch {
 
21
        b := &Branch{location, cenv()}
 
22
        if _, err := os.Stat(location); err == nil {
 
23
                stdout, _, err := b.bzr("root")
 
24
                if err == nil {
 
25
                        b.location = strings.TrimRight(string(stdout), "\n")
 
26
                }
 
27
        }
 
28
        return b
 
29
}
 
30
 
 
31
// cenv returns a copy of the current process environment with LC_ALL=C.
 
32
func cenv() []string {
 
33
        env := os.Environ()
 
34
        for i, pair := range env {
 
35
                if strings.HasPrefix(pair, "LC_ALL=") {
 
36
                        env[i] = "LC_ALL=C"
 
37
                        return env
 
38
                }
 
39
        }
 
40
        return append(env, "LC_ALL=C")
 
41
}
 
42
 
 
43
// Location returns the location of branch b.
 
44
func (b *Branch) Location() string {
 
45
        return b.location
 
46
}
 
47
 
 
48
// Join returns b's location with parts appended as path components.
 
49
// In other words, if b's location is "lp:foo", and parts is {"bar, baz"},
 
50
// Join returns "lp:foo/bar/baz".
 
51
func (b *Branch) Join(parts ...string) string {
 
52
        return path.Join(append([]string{b.location}, parts...)...)
 
53
}
 
54
 
 
55
func (b *Branch) bzr(subcommand string, args ...string) (stdout, stderr []byte, err error) {
 
56
        cmd := exec.Command("bzr", append([]string{subcommand}, args...)...)
 
57
        if _, err := os.Stat(b.location); err == nil {
 
58
                cmd.Dir = b.location
 
59
        }
 
60
        errbuf := &bytes.Buffer{}
 
61
        cmd.Stderr = errbuf
 
62
        cmd.Env = b.env
 
63
        stdout, err = cmd.Output()
 
64
        // Some commands fail with exit status 0 (e.g. bzr root). :-(
 
65
        if err != nil || bytes.Contains(errbuf.Bytes(), []byte("ERROR")) {
 
66
                var errmsg string
 
67
                if err != nil {
 
68
                        errmsg = err.Error()
 
69
                }
 
70
                return nil, nil, fmt.Errorf(`error running "bzr %s": %s%s%s`, subcommand, stdout, errbuf.Bytes(), errmsg)
 
71
        }
 
72
        return stdout, errbuf.Bytes(), err
 
73
}
 
74
 
 
75
// Init intializes a new branch at b's location.
 
76
func (b *Branch) Init() error {
 
77
        _, _, err := b.bzr("init", b.location)
 
78
        return err
 
79
}
 
80
 
 
81
// Add adds to b the path resultant from calling b.Join(parts...).
 
82
func (b *Branch) Add(parts ...string) error {
 
83
        _, _, err := b.bzr("add", b.Join(parts...))
 
84
        return err
 
85
}
 
86
 
 
87
// Commit commits pending changes into b.
 
88
func (b *Branch) Commit(message string) error {
 
89
        _, _, err := b.bzr("commit", "-q", "-m", message)
 
90
        return err
 
91
}
 
92
 
 
93
// RevisionId returns the Bazaar revision id for the tip of b.
 
94
func (b *Branch) RevisionId() (string, error) {
 
95
        stdout, stderr, err := b.bzr("revision-info", "-d", b.location)
 
96
        if err != nil {
 
97
                return "", err
 
98
        }
 
99
        pair := bytes.Fields(stdout)
 
100
        if len(pair) != 2 {
 
101
                return "", fmt.Errorf(`invalid output from "bzr revision-info": %s%s`, stdout, stderr)
 
102
        }
 
103
        id := string(pair[1])
 
104
        if id == "null:" {
 
105
                return "", fmt.Errorf("branch has no content")
 
106
        }
 
107
        return id, nil
 
108
}
 
109
 
 
110
// PushLocation returns the default push location for b.
 
111
func (b *Branch) PushLocation() (string, error) {
 
112
        stdout, _, err := b.bzr("info", b.location)
 
113
        if err != nil {
 
114
                return "", err
 
115
        }
 
116
        if i := bytes.Index(stdout, []byte("push branch:")); i >= 0 {
 
117
                return string(stdout[i+13 : i+bytes.IndexAny(stdout[i:], "\r\n")]), nil
 
118
        }
 
119
        return "", fmt.Errorf("no push branch location defined")
 
120
}
 
121
 
 
122
// PushAttr holds options for the Branch.Push method.
 
123
type PushAttr struct {
 
124
        Location string // Location to push to. Use the default push location if empty.
 
125
        Remember bool   // Whether to remember the location being pushed to as the default.
 
126
}
 
127
 
 
128
// Push pushes any new revisions in b to attr.Location if that's
 
129
// provided, or to the default push location otherwise.
 
130
// See PushAttr for other options.
 
131
func (b *Branch) Push(attr *PushAttr) error {
 
132
        var args []string
 
133
        if attr != nil {
 
134
                if attr.Remember {
 
135
                        args = append(args, "--remember")
 
136
                }
 
137
                if attr.Location != "" {
 
138
                        args = append(args, attr.Location)
 
139
                }
 
140
        }
 
141
        _, _, err := b.bzr("push", args...)
 
142
        return err
 
143
}
 
144
 
 
145
// CheckClean returns an error if 'bzr status' is not clean.
 
146
func (b *Branch) CheckClean() error {
 
147
        stdout, _, err := b.bzr("status", b.location)
 
148
        if err != nil {
 
149
                return err
 
150
        }
 
151
        if bytes.Count(stdout, []byte{'\n'}) == 1 && bytes.Contains(stdout, []byte(`See "bzr shelve --list" for details.`)) {
 
152
                return nil // Shelves are fine.
 
153
        }
 
154
        if len(stdout) > 0 {
 
155
                return fmt.Errorf("branch is not clean (bzr status)")
 
156
        }
 
157
        return nil
 
158
}