Tue, 16 Dec 2025 20:12:54 +0100
download result from buildenv and parse result.json
package main import ( "bytes" "encoding/json" "encoding/xml" "fmt" "log" "os" "os/exec" "path" "strings" "text/template" ) type Config struct { XMLName xml.Name `xml:"config"` BuildEnv []BuildEnv `xml:"buildenv"` BuildEnvMap map[string]BuildEnv Repository []Repository `xml:"repository"` } type BuildEnv struct { Name string `xml:"name,attr"` Host string `xml:"host"` User string `xml:"user"` } type Repository struct { Path string `xml:"path"` BuildEnvs string `xml:"buildenvs"` Build []struct { Name string `xml:"name,attr"` IsPlatform string `xml:"isplatform,attr"` NotPlatform string `xml:"not,attr"` NotList []string Test []string `xml:"test"` Var []struct { Name string `xml:"name,attr"` Value string `xml:",chardata"` } Configure string `xml:"configure"` Compile string `xml:"compile"` Check string `xml:"check"` } `xml:"build"` } type Result struct { Build string `json:"build"` Name string `json:"name"` Configure string `json:"configure"` Compile string `json:"compile"` Check string `json:"check"` } type Results struct { Results []Result } func main() { data, err := os.ReadFile("testconfig.xml") if err != nil { log.Fatal(err) } config := &Config{} if err := xml.Unmarshal(data, &config); err != nil { log.Fatal(err) } config.BuildEnvMap = make(map[string]BuildEnv) for _, env := range config.BuildEnv { config.BuildEnvMap[env.Name] = env } for ri := range config.Repository { for bi := range config.Repository[ri].Build { build := &config.Repository[ri].Build[bi] build.NotList = strings.Split(build.NotPlatform, ",") } } templateStr, err := os.ReadFile("build.template") if err != nil { log.Fatal(err) } funcs := template.FuncMap{ "add": func(i int, n int) int { return i + n }, "sub": func(i int, n int) int { return i - n }, } tpl, err := template.New("").Funcs(funcs).Parse(string(templateStr)) if err != nil { log.Fatal(err) } err = os.Mkdir("tmp", 0755) if err != nil && !os.IsExist(err) { log.Fatal(err) } tmp := "tmp" for _, repo := range config.Repository { if err = create_repo_archive(&repo, tpl, tmp); err != nil { log.Fatal(err) } exec_buildenvs(config, &repo, tmp) } } func create_repo_archive(repo *Repository, tpl *template.Template, tmp_dir string) error { // create a build directory, that contains the repository source // and build scripts buildPath := path.Join(tmp_dir, "build") if err := os.RemoveAll(buildPath); err != nil { log.Print("remove build dir failed: ", err) return err } if err := os.RemoveAll(path.Join(tmp_dir, "build.tar")); err != nil { log.Print("remove build.tar failed") return err } if err := os.RemoveAll(path.Join(tmp_dir, "build.tar.gz")); err != nil { log.Print("remove build.tar.gz failed") return err } if err := os.RemoveAll(path.Join(tmp_dir, "result")); err != nil { log.Print("remove result failed") return err } err := os.Mkdir(buildPath, 0755) if err != nil { log.Print("mkdir build dir failed: ", err) return err } // update repository and copy it to the build directory cmd := exec.Command("hg", "pull") var outb, errb bytes.Buffer cmd.Dir = repo.Path cmd.Stdout = &outb cmd.Stderr = &errb err = cmd.Run() if err != nil { log.Print("hg: ", errb.String()) return err } outb.Reset() errb.Reset() cmd = exec.Command("hg", "update") cmd.Dir = repo.Path cmd.Stdout = &outb cmd.Stderr = &errb err = cmd.Run() if err != nil { log.Print("hg: ", errb.String()) return err } // copy repository to the tmp_dir build directory cmd = exec.Command("cp", "-r", repo.Path, path.Join(buildPath, "src")) err = cmd.Run() if err != nil { log.Print("cannot copy the repository to the build dir") return err } // create build script file, err := os.Create(path.Join(buildPath, "build.sh")) if err != nil { log.Print("cannot create build.sh") return err } defer file.Close() err = tpl.Execute(file, repo) if err != nil { log.Print("cannot execute build.sh template") return err } file.Chmod(0755) // create tarball cmd = exec.Command("tar", "cvf", "../build.tar", "src", "build.sh") cmd.Dir = buildPath if err := cmd.Run(); err != nil { log.Print("tar error") return err } cmd = exec.Command("gzip", path.Join(tmp_dir, "build.tar")) if err := cmd.Run(); err != nil { log.Print("gzip error") return err } return nil } func host_is_available(host string) bool { cmd := exec.Command("ping", "-c", "1", host) if err := cmd.Run(); err != nil { return false } return true } func exec_buildenvs(config *Config, repo *Repository, tmp_dir string) int { if len(repo.BuildEnvs) == 0 { log.Print("repo %s has no buildenvs", repo.Path) return 0 } success := 0 buildenvs := strings.Split(repo.BuildEnvs, ",") for _, buildenv := range buildenvs { env, ok := config.BuildEnvMap[buildenv] if !ok { log.Print("unknown build env ", buildenv) continue } if !host_is_available(env.Host) { log.Print("skip unavailable host ", env.Host) continue } // steps: // upload build.tar.gz // extract build.tar.gz on the buildenv host and run build.sh // upload using scp cmd := exec.Command("scp", path.Join(tmp_dir, "build.tar.gz"), env.Host+":") log.Print("scp: ", cmd.Args) if err := cmd.Run(); err != nil { log.Print("cannot copy build env to ", env.Host) continue } // run build.sh build_dir := "build" remote_command := fmt.Sprintf("sh -c 'rm -Rf %s; mkdir -p %s; gzip -dc build.tar.gz | tar xf - -C %s ; (cd %s; ./build.sh)'", build_dir, build_dir, build_dir, build_dir) log.Printf("host: %s: command: %s", env.Host, remote_command) var outb, errb bytes.Buffer cmd = exec.Command("ssh", "-t", env.Host, remote_command) cmd.Stdout = &outb cmd.Stderr = &errb err := cmd.Run() log.Printf("host: %s: stdout: %s", env.Host, outb.String()) if err != nil { log.Print("cannot execute build.sh on buildenv host ", env.Host) log.Print("stderr: ", errb.String()) continue } // download result cmd.Stdout = nil cmd.Stderr = nil cmd = exec.Command("scp", "-r", env.Host+":"+build_dir+"/result", tmp_dir) log.Print("scp: ", cmd.Args) if err := cmd.Run(); err != nil { log.Print("cannot get result from buildenv") continue } // parse result resultData, err := os.ReadFile(path.Join(tmp_dir, "result/result.json")) if err != nil { log.Print("cannot read result from buildenv") continue } var results []Result if err := json.Unmarshal(resultData, &results); err != nil { log.Print("cannot parse result.json") continue } log.Print(results) success++ } return success }