src/main.go

changeset 1
05a8e80e577e
parent 0
75d2ad4c7dee
child 2
edeb8e8e02b6
equal deleted inserted replaced
0:75d2ad4c7dee 1:05a8e80e577e
1 package main 1 package main
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "encoding/xml" 5 "encoding/xml"
6 "errors"
6 "fmt" 7 "fmt"
7 "log" 8 "log"
8 "os" 9 "os"
9 "os/exec" 10 "os/exec"
10 "path" 11 "path"
12 "strings"
11 "text/template" 13 "text/template"
12 ) 14 )
13 15
14 type Config struct { 16 type Config struct {
15 XMLName xml.Name `xml:"config"` 17 XMLName xml.Name `xml:"config"`
16 BuildEnv []struct { 18 BuildEnv []BuildEnv `xml:"buildenv"`
17 Name string `xml:"name,attr"` 19 BuildEnvMap map[string]BuildEnv
18 Host string `xml:"host"` 20
19 User string `xml:"user"` 21 Repository []Repository `xml:"repository"`
20 } `xml:"buildenv"` 22 }
21 23
22 Repository []struct { 24 type BuildEnv struct {
23 Path string `xml:"path"` 25 Name string `xml:"name,attr"`
24 BuildEnvs string `xml:"buildenvs"` 26 Host string `xml:"host"`
25 Build []struct { 27 User string `xml:"user"`
26 IsPlatform string `xml:"isplatform,attr"` 28 }
27 NotPlatform string `xml:"not,attr"` 29
28 Test []string `xml:"test"` 30 type Repository struct {
29 Var []struct { 31 Path string `xml:"path"`
30 Name string `xml:"name,attr"` 32 BuildEnvs string `xml:"buildenvs"`
31 Value string `xml:",chardata"` 33 Build []struct {
32 } 34 IsPlatform string `xml:"isplatform,attr"`
33 Configure string `xml:"configure"` 35 NotPlatform string `xml:"not,attr"`
34 Compile string `xml:"compile"` 36 Test []string `xml:"test"`
35 Check string `xml:"check"` 37 Var []struct {
36 } `xml:"build"` 38 Name string `xml:"name,attr"`
37 } `xml:"repository"` 39 Value string `xml:",chardata"`
40 }
41 Configure string `xml:"configure"`
42 Compile string `xml:"compile"`
43 Check string `xml:"check"`
44 } `xml:"build"`
38 } 45 }
39 46
40 func main() { 47 func main() {
41 data, err := os.ReadFile("testconfig.xml") 48 data, err := os.ReadFile("testconfig.xml")
42 if err != nil { 49 if err != nil {
45 52
46 config := &Config{} 53 config := &Config{}
47 if err := xml.Unmarshal(data, &config); err != nil { 54 if err := xml.Unmarshal(data, &config); err != nil {
48 log.Fatal(err) 55 log.Fatal(err)
49 } 56 }
57 config.BuildEnvMap = make(map[string]BuildEnv)
58 for _, env := range config.BuildEnv {
59 config.BuildEnvMap[env.Name] = env
60 }
50 61
51 templateStr, err := os.ReadFile("build.template") 62 templateStr, err := os.ReadFile("build.template")
52 if err != nil { 63 if err != nil {
53 log.Fatal(err) 64 log.Fatal(err)
54 } 65 }
61 err = os.Mkdir("tmp", 0755) 72 err = os.Mkdir("tmp", 0755)
62 if err != nil && !os.IsExist(err) { 73 if err != nil && !os.IsExist(err) {
63 log.Fatal(err) 74 log.Fatal(err)
64 } 75 }
65 76
66 tmpDir := "tmp" 77 tmp := "tmp"
67 78
68 for _, repo := range config.Repository { 79 for _, repo := range config.Repository {
69 // create a build directory, that contains the repository source 80 if err = create_repo_archive(&repo, tpl, tmp); err != nil {
70 // and build scripts
71 buildPath := path.Join(tmpDir, "build")
72
73 if err = os.RemoveAll(buildPath); err != nil {
74 log.Fatal(err) 81 log.Fatal(err)
75 } 82 }
76 if err = os.RemoveAll(path.Join(tmpDir, "build.tar")); err != nil { 83
84 if err = exec_buildenvs(config, &repo, tmp); err != nil {
77 log.Fatal(err) 85 log.Fatal(err)
78 } 86 }
79 if err = os.RemoveAll(path.Join(tmpDir, "build.tar.gz")); err != nil { 87 }
80 log.Fatal(err) 88 }
81 } 89
82 90 func create_repo_archive(repo *Repository, tpl *template.Template, tmp_dir string) error {
83 err = os.Mkdir(buildPath, 0755) 91 // create a build directory, that contains the repository source
84 if err != nil { 92 // and build scripts
85 log.Fatal(err) 93 buildPath := path.Join(tmp_dir, "build")
86 } 94
87 95 if err := os.RemoveAll(buildPath); err != nil {
88 // update repository and copy it to the build directory 96 log.Print("remove build dir failed: ", err)
89 cmd := exec.Command("hg", "pull") 97 return err
98 }
99 if err := os.RemoveAll(path.Join(tmp_dir, "build.tar")); err != nil {
100 log.Print("remove build.tar failed")
101 return err
102 }
103 if err := os.RemoveAll(path.Join(tmp_dir, "build.tar.gz")); err != nil {
104 log.Print("remove build.tar.gz failed")
105 return err
106 }
107
108 err := os.Mkdir(buildPath, 0755)
109 if err != nil {
110 log.Print("mkdir build dir failed: ", err)
111 return err
112 }
113
114 // update repository and copy it to the build directory
115 cmd := exec.Command("hg", "pull")
116 var outb, errb bytes.Buffer
117 cmd.Dir = repo.Path
118 cmd.Stdout = &outb
119 cmd.Stderr = &errb
120 err = cmd.Run()
121 if err != nil {
122 log.Print("hg: ", errb.String())
123 return err
124 }
125
126 outb.Reset()
127 errb.Reset()
128 cmd = exec.Command("hg", "update")
129 cmd.Dir = repo.Path
130 cmd.Stdout = &outb
131 cmd.Stderr = &errb
132 err = cmd.Run()
133 if err != nil {
134 log.Print("hg: ", errb.String())
135 return err
136 }
137
138 // copy repository to the tmp_dir build directory
139 cmd = exec.Command("cp", "-r", repo.Path, path.Join(buildPath, "src"))
140 err = cmd.Run()
141 if err != nil {
142 log.Print("cannot copy the repository to the build dir")
143 return err
144 }
145
146 // create build script
147 file, err := os.Create(path.Join(buildPath, "build.sh"))
148 if err != nil {
149 log.Print("cannot create build.sh")
150 return err
151 }
152 defer file.Close()
153
154 err = tpl.Execute(file, repo)
155 if err != nil {
156 log.Print("cannot execute build.sh template")
157 return err
158 }
159 file.Chmod(0755)
160
161 // create tarball
162 cmd = exec.Command("tar", "cvf", "../build.tar", "src", "build.sh")
163 cmd.Dir = buildPath
164 if err := cmd.Run(); err != nil {
165 log.Print("tar error")
166 return err
167 }
168
169 cmd = exec.Command("gzip", path.Join(tmp_dir, "build.tar"))
170 if err := cmd.Run(); err != nil {
171 log.Print("gzip error")
172 return err
173 }
174
175 return nil
176 }
177
178 func host_is_available(host string) bool {
179 cmd := exec.Command("ping", "-c", "1", host)
180 if err := cmd.Run(); err != nil {
181 return false
182 }
183 return true
184 }
185
186 func exec_buildenvs(config *Config, repo *Repository, tmp_dir string) error {
187 if len(repo.BuildEnvs) == 0 {
188 log.Print("repo %s has no buildenvs", repo.Path)
189 return nil
190 }
191 buildenvs := strings.Split(repo.BuildEnvs, ",")
192 for _, buildenv := range buildenvs {
193 env, ok := config.BuildEnvMap[buildenv]
194 if !ok {
195 return errors.New("unknown build env: " + buildenv)
196 }
197 if !host_is_available(env.Host) {
198 log.Print("skip unavailable host ", env.Host)
199 return nil
200 }
201
202 // steps:
203 // upload build.tar.gz
204 // extract build.tar.gz on the buildenv host and run build.sh
205
206 // upload using scp
207 cmd := exec.Command("scp", path.Join(tmp_dir, "build.tar.gz"), env.Host+":")
208 if err := cmd.Run(); err != nil {
209 log.Print("cannot copy build env to ", env.Host)
210 continue
211 }
212
213 // run build.sh
214 build_dir := "build"
215 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)
216 log.Printf("host: %s: command: %s", env.Host, remote_command)
217
90 var outb, errb bytes.Buffer 218 var outb, errb bytes.Buffer
91 cmd.Dir = repo.Path 219 cmd = exec.Command("ssh", "-t", env.Host, remote_command)
92 cmd.Stdout = &outb 220 cmd.Stdout = &outb
93 cmd.Stderr = &errb 221 cmd.Stderr = &errb
94 err = cmd.Run() 222 err := cmd.Run()
223 log.Printf("host: %s: stdout: %s", env.Host, outb.String())
95 if err != nil { 224 if err != nil {
96 log.Print("hg: ", errb.String()) 225 log.Print("cannot execute build.sh on buildenv host ", env.Host)
97 log.Fatal(err) 226 log.Print("stderr: ", errb.String())
98 } 227 }
99 228 }
100 outb.Reset() 229
101 errb.Reset() 230 return nil
102 cmd = exec.Command("hg", "update") 231 }
103 cmd.Dir = repo.Path
104 cmd.Stdout = &outb
105 cmd.Stderr = &errb
106 err = cmd.Run()
107 if err != nil {
108 log.Print("hg: ", errb.String())
109 log.Fatal(err)
110 }
111
112 // copy repository to the tmp build directory
113 cmd = exec.Command("cp", "-r", repo.Path, path.Join(buildPath, "src"))
114 err = cmd.Run()
115 if err != nil {
116 log.Fatal(err)
117 }
118
119 // create build script
120 file, err := os.Create(path.Join(buildPath, "build.sh"))
121 if err != nil {
122 log.Fatal(err)
123 }
124
125 err = tpl.Execute(file, repo)
126
127 if err != nil {
128 log.Fatal(err)
129 }
130 file.Close()
131
132 // create tarball
133 cmd = exec.Command("tar", "cvf", "../build.tar", "src", "build.sh")
134 cmd.Dir = buildPath
135 if err := cmd.Run(); err != nil {
136 log.Print("tar error")
137 log.Fatal(err)
138 }
139
140 fmt.Println(path.Join(buildPath, "build.tar"))
141 cmd = exec.Command("gzip", path.Join(tmpDir, "build.tar"))
142 if err := cmd.Run(); err != nil {
143 log.Print("gzip error")
144 log.Fatal(err)
145 }
146
147 // TODO: upload build.tar.gz to buildenvs and execute the build.sh script remotely
148 }
149 }

mercurial