diff --git a/Makefile b/Makefile index 6aee967..1c7839d 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,11 @@ docker: run: docker ps -a | grep $(NAME) && ([ $$? -eq 0 ] && (docker stop $(NAME) && docker rm -f $(NAME))) || echo "no running container." docker run -itd -v $(shell pwd)/data:/app/public/buildbox -p 6789:6789 --name $(NAME) ssaplayground:latest -tidy-docker: +tidy: docker ps -a | grep $(NAME) [ $$? -eq 0 ] && docker stop $(NAME) && docker rm -f $(NAME) docker images -f "dangling=true" -q | xargs docker rmi -f docker image prune -f clean: rm -rf $(BUILD) -.PHONY: all start docker update clean \ No newline at end of file +.PHONY: all docker run tidy clean \ No newline at end of file diff --git a/TODO.md b/TODO.md index 371afea..8351162 100644 --- a/TODO.md +++ b/TODO.md @@ -9,4 +9,5 @@ - [x] ~~Use short link instead of UUID?~~ seems no need - [x] share with /gossa?id=uuid -> access /gossa/uuid/main.go or /gossa/uuid/main_test.go, access /gossa/uuid/ssa.html - [x] ~~Better Editor: Monaco Editor~~ maybe not? -- [ ] Containerized deployment \ No newline at end of file +- [x] Containerized deployment +- [x] Support Go Modules \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 3ae8c91..bc8a607 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,9 @@ FROM golang:1.14-alpine WORKDIR /app ADD . /app +# required for runtime/cgo +RUN apk add g++ RUN go get -u golang.org/x/tools/cmd/goimports && go mod tidy -RUN go build -o ssaplayground.service -mod vendor +RUN go build -o ssaplayground.service -mod=vendor EXPOSE 6789 ENTRYPOINT [ "/app/ssaplayground.service", "-conf=/app/configs/docker.yaml"] diff --git a/src/route/api.go b/src/route/api.go index f06038e..62d46bd 100644 --- a/src/route/api.go +++ b/src/route/api.go @@ -46,6 +46,8 @@ type BuildSSAOutput struct { Msg string `json:"msg"` } +// BuildSSA serves the code send by user and builds its SSA IR into html. +// TODO: speedup for request response, e.g. as async rest api. func BuildSSA(c *gin.Context) { // 1. create a folder in config.Get().Static/buildbox out := BuildSSAOutput{ @@ -65,7 +67,7 @@ func BuildSSA(c *gin.Context) { err = c.BindJSON(&in) if err != nil { os.Remove(path) - out.Msg = fmt.Sprintf("cannot bind input params, err: %v", err) + out.Msg = fmt.Sprintf("cannot bind input params, err: \n%v", err) c.JSON(http.StatusInternalServerError, out) return } @@ -86,24 +88,35 @@ func BuildSSA(c *gin.Context) { err = ioutil.WriteFile(buildFile, []byte(in.Code), os.ModePerm) if err != nil { os.Remove(path) - out.Msg = err.Error() + out.Msg = fmt.Sprintf("cannot save your code, err: \n%v", err) c.JSON(http.StatusInternalServerError, out) return } - // 3. goimports && GOSSAFUNC=foo go build + // 3.1 goimports err = autoimports(buildFile) if err != nil { os.Remove(path) - out.Msg = err.Error() + out.Msg = fmt.Sprintf("cannot run autoimports for your code, err: \n%v", err) c.JSON(http.StatusBadRequest, out) return } + + // 3.2 go mod init gossa && go mod tidy + err = initModules(path) + if err != nil { + os.Remove(path) + out.Msg = fmt.Sprintf("cannot use go modules for your code, err: \n%v", err) + c.JSON(http.StatusBadRequest, out) + return + } + + // 3.3 GOSSAFUNC=foo go build outFile := filepath.Join(path, "/main.out") err = buildSSA(in.FuncName, in.GcFlags, outFile, buildFile, isTest) if err != nil { os.Remove(path) - out.Msg = err.Error() + out.Msg = fmt.Sprintf("cannot build ssa for your code, err: \n%v", err) c.JSON(http.StatusBadRequest, out) return } @@ -138,6 +151,32 @@ func autoimports(outf string) error { return nil } +func initModules(path string) error { + // 1. go mod init + cmd := exec.Command("go", "mod", "init", "gossa") + cmd.Dir = path + cmd.Stderr = &bytes.Buffer{} + err := cmd.Run() + if err != nil { + msg := cmd.Stderr.(*bytes.Buffer).String() + msg = strings.ReplaceAll(msg, path, "$GOSSAPATH") + return errors.New(msg) + } + + // 2. go mod tidy + cmd = exec.Command("go", "mod", "tidy") + cmd.Dir = path + cmd.Stderr = &bytes.Buffer{} + err = cmd.Run() + if err != nil { + msg := cmd.Stderr.(*bytes.Buffer).String() + msg = strings.ReplaceAll(msg, path, "$GOSSAPATH") + return errors.New(msg) + } + + return nil +} + func buildSSA(funcname, gcflags, outf, buildf string, isTest bool) error { var cmd *exec.Cmd if !isTest {