11 "github.com/gin-gonic/gin"
12 "github.com/iotbzh/xds-server/lib/common"
15 // MakeArgs is the parameters (json format) of /make command
16 type MakeArgs struct {
18 SdkID string `json:"sdkid"` // sdk ID to use for setting env
19 Args []string `json:"args"` // args to pass to make command
20 Env []string `json:"env"`
21 RPath string `json:"rpath"` // relative path into project
22 CmdTimeout int `json:"timeout"` // command completion timeout in Second
25 // MakeOutMsg Message send on each output (stdout+stderr) of make command
26 type MakeOutMsg struct {
27 CmdID string `json:"cmdID"`
28 Timestamp string `json:"timestamp"`
29 Stdout string `json:"stdout"`
30 Stderr string `json:"stderr"`
33 // MakeExitMsg Message send on make command exit
34 type MakeExitMsg struct {
35 CmdID string `json:"cmdID"`
36 Timestamp string `json:"timestamp"`
37 Code int `json:"code"`
38 Error error `json:"error"`
41 // MakeOutEvent Event send in WS when characters are received on stdout/stderr
42 const MakeOutEvent = "make:output"
44 // MakeExitEvent Event send in WS when command exited
45 const MakeExitEvent = "make:exit"
49 func (s *APIService) buildMake(c *gin.Context) {
52 if c.BindJSON(&args) != nil {
53 common.APIError(c, "Invalid arguments")
57 sess := s.sessions.Get(c)
59 common.APIError(c, "Unknown sessions")
64 common.APIError(c, "Websocket not established")
68 // Allow to pass id in url (/make/:id) or as JSON argument
74 common.APIError(c, "Invalid id")
78 prj := s.mfolder.GetFolderFromID(id)
80 common.APIError(c, "Unknown id")
84 execTmo := args.CmdTimeout
86 // TODO get default timeout from config.json file
87 execTmo = 24 * 60 * 60 // 1 day
90 // Define callback for output
91 var oCB common.EmitOutputCB
92 oCB = func(sid string, id int, stdout, stderr string, data *map[string]interface{}) {
93 // IO socket can be nil when disconnected
94 so := s.sessions.IOSocketGet(sid)
96 s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%d", MakeOutEvent, sid, id)
100 // Retrieve project ID and RootPath
101 prjID := (*data)["ID"].(string)
102 prjRootPath := (*data)["RootPath"].(string)
104 // Cleanup any references to internal rootpath in stdout & stderr
105 stdout = strings.Replace(stdout, prjRootPath, "", -1)
106 stderr = strings.Replace(stderr, prjRootPath, "", -1)
108 s.log.Debugf("%s emitted - WS sid %s - id:%d - prjID:%s", MakeOutEvent, sid, id, prjID)
110 // FIXME replace by .BroadcastTo a room
111 err := (*so).Emit(MakeOutEvent, MakeOutMsg{
112 CmdID: strconv.Itoa(id),
113 Timestamp: time.Now().String(),
118 s.log.Errorf("WS Emit : %v", err)
122 // Define callback for output
123 eCB := func(sid string, id int, code int, err error, data *map[string]interface{}) {
124 s.log.Debugf("Command [Cmd ID %d] exited: code %d, error: %v", id, code, err)
126 // IO socket can be nil when disconnected
127 so := s.sessions.IOSocketGet(sid)
129 s.log.Infof("%s not emitted - WS closed (id:%d", MakeExitEvent, id)
133 // Retrieve project ID and RootPath
134 prjID := (*data)["ID"].(string)
136 // XXX - workaround to be sure that Syncthing detected all changes
137 if err := s.mfolder.ForceSync(prjID); err != nil {
138 s.log.Errorf("Error while syncing folder %s: %v", prjID, err)
141 // FIXME replace by .BroadcastTo a room
142 e := (*so).Emit(MakeExitEvent, MakeExitMsg{
143 CmdID: strconv.Itoa(id),
144 Timestamp: time.Now().String(),
149 s.log.Errorf("WS Emit : %v", e)
153 cmdID := makeCommandID
157 // Retrieve env command regarding Sdk ID
158 if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 {
159 cmd = append(cmd, envCmd...)
160 cmd = append(cmd, "&&")
163 cmd = append(cmd, "cd", prj.GetFullPath(args.RPath), "&&", "make")
164 if len(args.Args) > 0 {
165 cmd = append(cmd, args.Args...)
168 s.log.Debugf("Execute [Cmd ID %d]: %v", cmdID, cmd)
170 data := make(map[string]interface{})
172 data["RootPath"] = prj.RootPath
174 err := common.ExecPipeWs(cmd, args.Env, sop, sess.ID, cmdID, execTmo, s.log, oCB, eCB, nil)
176 common.APIError(c, err.Error())
180 c.JSON(http.StatusOK,