code: func (m *Mercury) WatchDeployStatus(ctx context.Context, appname, entrypoint, nodename string) chan *types.DeployStatus {
  	if appname == "" {
  		entrypoint = ""
  	}
  	if entrypoint == "" {
  		nodename = ""
  	}
  	// 显式加个 / 保证 prefix 唯一
  	key := filepath.Join(containerDeployPrefix, appname, entrypoint, nodename) + "/"
  	ch := make(chan *types.DeployStatus)
  	go func() {
  		defer close(ch)
  		for resp := range m.Watch(ctx, key, clientv3.WithPrefix()) {
  			msg := &types.DeployStatus{}
  			if resp.Err() != nil {
  				if !resp.Canceled {
  					msg.Err = resp.Err()
  					ch <- msg
  				}
  				return
  			}
  			for _, ev := range resp.Events {
  				appname, entrypoint, nodename, id := parseStatusKey(string(ev.Kv.Key))
  				msg.Data = string(ev.Kv.Value)
  				msg.Action = ev.Type.String()
  				msg.Appname = appname
  				msg.Entrypoint = entrypoint
  				msg.Nodename = nodename
  				msg.ID = id
  				log.Debugf("[WatchDeployStatus] app %s_%s event, id %s, action %s", appname, entrypoint, utils.ShortID(msg.ID), msg.Action)
  				if msg.Data != "" {
  					log.Debugf("[WatchDeployStatus] data %s", msg.Data)
  				}
  				ch <- msg
  			}
  		}
  	}()
  	return ch
  }
docs: // WatchDeployStatus watch deployed status