5
"launchpad.net/gnuflag"
6
"launchpad.net/juju-core/bzr"
7
"launchpad.net/juju-core/charm"
8
"launchpad.net/juju-core/cmd"
9
"launchpad.net/juju-core/log"
15
type PublishCommand struct {
20
// changePushLocation allows translating the branch location
21
// for testing purposes.
22
changePushLocation func(loc string) string
24
pollDelay time.Duration
28
<charm url> can be a charm URL, or an unambiguously condensed form of it;
29
the following forms are accepted:
35
For cs:~user/precise/mysql
36
cs:~user/precise/mysql
38
There is no default series, so one must be provided explicitly when
39
informing a charm URL. If the URL isn't provided, an attempt will be
40
made to infer it from the current branch push URL.
43
func (c *PublishCommand) Info() *cmd.Info {
46
Args: "[<charm url>]",
47
Purpose: "publish charm to the store",
52
func (c *PublishCommand) SetFlags(f *gnuflag.FlagSet) {
53
c.EnvCommandBase.SetFlags(f)
54
f.StringVar(&c.CharmPath, "from", ".", "path for charm to be published")
57
func (c *PublishCommand) Init(args []string) error {
62
return cmd.CheckEmpty(args[1:])
65
func (c *PublishCommand) ChangePushLocation(change func(string) string) {
66
c.changePushLocation = change
69
func (c *PublishCommand) SetPollDelay(delay time.Duration) {
73
// Wording guideline to avoid confusion: charms have *URLs*, branches have *locations*.
75
func (c *PublishCommand) Run(ctx *cmd.Context) (err error) {
76
branch := bzr.New(ctx.AbsPath(c.CharmPath))
77
if _, err := os.Stat(branch.Join(".bzr")); err != nil {
78
return fmt.Errorf("not a charm branch: %s", branch.Location())
80
if err := branch.CheckClean(); err != nil {
87
loc, err := branch.PushLocation()
89
return fmt.Errorf("no charm URL provided and cannot infer from current directory (no push location)")
91
curl, err = charm.Store.CharmURL(loc)
93
return fmt.Errorf("cannot infer charm URL from branch location: %q", loc)
97
curl, err = charm.InferURL(c.URL, "")
103
pushLocation := charm.Store.BranchLocation(curl)
104
if c.changePushLocation != nil {
105
pushLocation = c.changePushLocation(pushLocation)
108
repo, err := charm.InferRepository(curl, "/not/important")
112
if repo != charm.Store {
113
return fmt.Errorf("charm URL must reference the juju charm store")
116
localDigest, err := branch.RevisionId()
118
return fmt.Errorf("cannot obtain local digest: %v", err)
120
log.Infof("local digest is %s", localDigest)
122
ch, err := charm.ReadDir(branch.Location())
126
if ch.Meta().Name != curl.Name {
127
return fmt.Errorf("charm name in metadata must match name in URL: %q != %q", ch.Meta().Name, curl.Name)
130
oldEvent, err := charm.Store.Event(curl, localDigest)
131
if _, ok := err.(*charm.NotFoundError); ok {
132
oldEvent, err = charm.Store.Event(curl, "")
133
if _, ok := err.(*charm.NotFoundError); ok {
134
log.Infof("charm %s is not yet in the store", curl)
139
return fmt.Errorf("cannot obtain event details from the store: %s", err)
142
if oldEvent != nil && oldEvent.Digest == localDigest {
143
return handleEvent(ctx, curl, oldEvent)
146
log.Infof("sending charm to the charm store...")
148
err = branch.Push(&bzr.PushAttr{Location: pushLocation, Remember: true})
152
log.Infof("charm sent; waiting for it to be published...")
154
time.Sleep(c.pollDelay)
155
newEvent, err := charm.Store.Event(curl, "")
156
if _, ok := err.(*charm.NotFoundError); ok {
160
return fmt.Errorf("cannot obtain event details from the store: %s", err)
162
if oldEvent != nil && oldEvent.Digest == newEvent.Digest {
165
if newEvent.Digest != localDigest {
166
// TODO Check if the published digest is in the local history.
167
return fmt.Errorf("charm changed but not to local charm digest; publishing race?")
169
return handleEvent(ctx, curl, newEvent)
174
func handleEvent(ctx *cmd.Context, curl *charm.URL, event *charm.EventResponse) error {
177
curlRev := curl.WithRevision(event.Revision)
178
log.Infof("charm published at %s as %s", event.Time, curlRev)
179
fmt.Fprintln(ctx.Stdout, curlRev)
180
case "publish-error":
181
return fmt.Errorf("charm could not be published: %s", strings.Join(event.Errors, "; "))
183
return fmt.Errorf("unknown event kind %q for charm %s", event.Kind, curl)