src/main.go

changeset 1
05a8e80e577e
parent 0
75d2ad4c7dee
child 2
edeb8e8e02b6
--- a/src/main.go	Sun Dec 14 16:51:29 2025 +0100
+++ b/src/main.go	Mon Dec 15 10:59:58 2025 +0100
@@ -3,38 +3,45 @@
 import (
 	"bytes"
 	"encoding/xml"
+	"errors"
 	"fmt"
 	"log"
 	"os"
 	"os/exec"
 	"path"
+	"strings"
 	"text/template"
 )
 
 type Config struct {
-	XMLName  xml.Name `xml:"config"`
-	BuildEnv []struct {
-		Name string `xml:"name,attr"`
-		Host string `xml:"host"`
-		User string `xml:"user"`
-	} `xml:"buildenv"`
+	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"`
+}
 
-	Repository []struct {
-		Path      string `xml:"path"`
-		BuildEnvs string `xml:"buildenvs"`
-		Build     []struct {
-			IsPlatform  string   `xml:"isplatform,attr"`
-			NotPlatform string   `xml:"not,attr"`
-			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"`
-	} `xml:"repository"`
+type Repository struct {
+	Path      string `xml:"path"`
+	BuildEnvs string `xml:"buildenvs"`
+	Build     []struct {
+		IsPlatform  string   `xml:"isplatform,attr"`
+		NotPlatform string   `xml:"not,attr"`
+		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"`
 }
 
 func main() {
@@ -47,6 +54,10 @@
 	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
+	}
 
 	templateStr, err := os.ReadFile("build.template")
 	if err != nil {
@@ -63,87 +74,158 @@
 		log.Fatal(err)
 	}
 
-	tmpDir := "tmp"
+	tmp := "tmp"
 
 	for _, repo := range config.Repository {
-		// create a build directory, that contains the repository source
-		// and build scripts
-		buildPath := path.Join(tmpDir, "build")
-
-		if err = os.RemoveAll(buildPath); err != nil {
-			log.Fatal(err)
-		}
-		if err = os.RemoveAll(path.Join(tmpDir, "build.tar")); err != nil {
-			log.Fatal(err)
-		}
-		if err = os.RemoveAll(path.Join(tmpDir, "build.tar.gz")); err != nil {
-			log.Fatal(err)
-		}
-
-		err = os.Mkdir(buildPath, 0755)
-		if err != nil {
-			log.Fatal(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())
+		if err = create_repo_archive(&repo, tpl, tmp); err != nil {
 			log.Fatal(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())
+		if err = exec_buildenvs(config, &repo, tmp); err != nil {
 			log.Fatal(err)
 		}
+	}
+}
 
-		// copy repository to the tmp build directory
-		cmd = exec.Command("cp", "-r", repo.Path, path.Join(buildPath, "src"))
-		err = cmd.Run()
-		if err != nil {
-			log.Fatal(err)
+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
+	}
+
+	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) error {
+	if len(repo.BuildEnvs) == 0 {
+		log.Print("repo %s has no buildenvs", repo.Path)
+		return nil
+	}
+	buildenvs := strings.Split(repo.BuildEnvs, ",")
+	for _, buildenv := range buildenvs {
+		env, ok := config.BuildEnvMap[buildenv]
+		if !ok {
+			return errors.New("unknown build env: " + buildenv)
 		}
-
-		// create build script
-		file, err := os.Create(path.Join(buildPath, "build.sh"))
-		if err != nil {
-			log.Fatal(err)
+		if !host_is_available(env.Host) {
+			log.Print("skip unavailable host ", env.Host)
+			return nil
 		}
 
-		err = tpl.Execute(file, repo)
-
-		if err != nil {
-			log.Fatal(err)
-		}
-		file.Close()
+		// steps:
+		// upload build.tar.gz
+		// extract build.tar.gz on the buildenv host and run build.sh
 
-		// create tarball
-		cmd = exec.Command("tar", "cvf", "../build.tar", "src", "build.sh")
-		cmd.Dir = buildPath
+		// upload using scp
+		cmd := exec.Command("scp", path.Join(tmp_dir, "build.tar.gz"), env.Host+":")
 		if err := cmd.Run(); err != nil {
-			log.Print("tar error")
-			log.Fatal(err)
+			log.Print("cannot copy build env to ", env.Host)
+			continue
 		}
 
-		fmt.Println(path.Join(buildPath, "build.tar"))
-		cmd = exec.Command("gzip", path.Join(tmpDir, "build.tar"))
-		if err := cmd.Run(); err != nil {
-			log.Print("gzip error")
-			log.Fatal(err)
+		// 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())
 		}
+	}
 
-		// TODO: upload build.tar.gz to buildenvs and execute the build.sh script remotely
-	}
+	return nil
 }

mercurial