package main import ( "errors" "fmt" "os" "path/filepath" "strconv" "strings" "time" "github.com/go-cmd/cmd" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) func buildPackage(repo *gitpb.Repo) (bool, error) { // TODO: if dirty, set GO111MODULE // also, if last tag != version /* go install -ldflags " \ -X main.GITCOMMIT=${GITCOMMIT} \ -X main.GOVERSION='${GOVERSION}' \ -X main.BUILDTIME='${BUILDTIME}' \ -X main.VERSION=${VERSION}" */ // ldflags := "main.GOTAG=" + repo.LastTag() filename := repo.Control["Package"] // c.Package.String() if filename == "" { return false, errors.New("filename is blank") } homeDir, err := os.UserHomeDir() if err != nil { return false, err } arch := repo.Control["Architecture"] // c.Architecture.String() version := repo.Control["Version"] log.Info("version is:", version) debname := filename + "_" + version + "_" + arch + ".deb" var fulldebname string if argv.OutDir == "" { fulldebname = debname } else { fulldebname = filepath.Join(argv.OutDir, debname) } if shell.Exists(fulldebname) { log.Info("debian package already built: " + fulldebname) return true, errors.New("debian package already built: " + fulldebname) } var fullfilename string if argv.Release { fullfilename = filepath.Join(homeDir, "go/bin", filename) } else { fullfilename = filename } shell.Run([]string{"rm", "-f", fullfilename}) if shell.Exists(fullfilename) { // something wrong return false, errors.New("binary existed before build") } if argv.Release { os.Unsetenv("GO111MODULE") cmd := []string{"go"} cmd = append(cmd, "install") if argv.Verbose { cmd = append(cmd, "-v") cmd = append(cmd, "-x") } /* cmd = append(cmd, "some path"+"@v"+version) if err := shell.PathExecVerbose("", cmd); err != nil { badExit(err) return false, fmt.Errorf("go build err %v", err) } */ cmd = []string{"go"} cmd = append(cmd, "build") if argv.Verbose { cmd = append(cmd, "-v") cmd = append(cmd, "-x") } cmd = append(cmd, "this should be the path") if err := shell.PathExecVerbose("", cmd); err != nil { badExit(err) return false, fmt.Errorf("go build err %v", err) } log.Warn("build worked") } else { // set the GO111 build var to true. pass the versions to the compiler manually os.Setenv("GO111MODULE", "off") cmd := []string{"go", "build"} // set standard ldflag options now := time.Now() datestamp := now.UTC().Format("2006/01/02_1504_UTC") log.Info("datestamp =", datestamp) // add some standard golang flags ldflags := "-X main.VERSION=" + version + " " ldflags += "-X main.BUILDTIME=" + datestamp + " " ldflags += "-X main.GUIVERSION=" + version + "" // todo: git this from the filesystem cmd = append(cmd, "-ldflags", ldflags) // add any flags from the command line // this might not actually work // todo: test this for _, flag := range argv.Ldflags { cmd = append(cmd, "-ldflags", "-X "+flag) } _, err := shell.RunVerbose(cmd) if err != nil { return false, fmt.Errorf("go build err %v", err) } log.Warn("go build worked") } filebase := filepath.Base(repo.Control["pathL"]) // c.pathL.String()) if fullfilename != filebase { // this exception is for when you want to override a package name // sometimes that's the best option. This way you can keep your // name, but the .deb package name can be different so you can // still apt-get it. For an example, look at the gozookeeper package fullfilename = filebase } if !shell.Exists(fullfilename) { log.Warn("build failed. filename does not exist", fullfilename) return false, errors.New("missing " + fullfilename) } if shell.Exists("files") { shell.Run([]string{"rm", "-rf", "files"}) // log.Info("running sync") shell.Run([]string{"sync"}) if shell.Exists("files") { log.Warn("rm failed for some reason") return false, errors.New("rm files/") } } shell.Run([]string{"sync"}) // for some reason the next check fails sometimes? if shell.Exists("files") { // probably the 'shell' package id being stupid and not waiting for the process to actually exit log.Warn("rm failed. files/ still exists. is golang doing these in parallel?") return false, errors.New("rm files/") } if err := os.MkdirAll("files/DEBIAN", os.ModePerm); err != nil { return false, errors.New("mkdir files/DEBIAN") } if err := os.MkdirAll("files/usr/bin", os.ModePerm); err != nil { log.Warn("mkdir failed") return false, errors.New("mkdir files/usr/bin") } if os.Getenv("GO_DEB_CUSTOM") == "true" { // skip cp & strip on custom 'control' files } else { if r := shell.Run([]string{"cp", fullfilename, "files/usr/bin"}); r.Error != nil { log.Warn("cp failed") return false, r.Error } if r := shell.Run([]string{"strip", "files/usr/bin/" + filename}); r.Error != nil { log.Warn("strip failed") return false, r.Error } } // put the README in there (if missing, generate it?) var readme string = "" if shell.Exists("README.md") { readme = "README.md" } if shell.Exists("README") { readme = "README" } if readme != "" { path := filepath.Join("files/usr/lib/" + filename) if err := os.MkdirAll(path, os.ModePerm); err != nil { return false, errors.New("no files/usr/lib") } if r := shell.Run([]string{"cp", readme, path}); r.Error != nil { return false, r.Error } } if !writeDebianControlFile(repo) { return false, errors.New("write control file") } if shell.Exists("postinst") { shell.Run([]string{"cp", "postinst", "files/DEBIAN/"}) } // experiment for the toolkit package // if the git repo has a "./build" script run it before packaging // this way the user can put custom files in the .deb package if shell.Exists("build") { shell.Run([]string{"./build"}) } shell.Run([]string{"dpkg-deb", "--build", "files", fulldebname}) if shell.Exists(fulldebname) { } else { log.Warn("build failed", fulldebname) return false, errors.New("dpkg-deb --build failed") } shell.Run([]string{"dpkg-deb", "-I", fulldebname}) shell.Run([]string{"dpkg-deb", "-c", fulldebname}) // cleanup files if shell.Exists("files") { if argv.KeepFiles { log.Info("keeping the build files/") } else { shell.Run([]string{"rm", "-rf", "files"}) // log.Info("running sync") shell.Run([]string{"sync"}) if shell.Exists("files") { log.Warn("rm -rf files/ failed. Run() returned false") return false, errors.New("rm files/") } } } return true, nil } func writeDebianControlFile(repo *gitpb.Repo) bool { cf, err := os.OpenFile("files/DEBIAN/control", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { log.Info("open control file failed", err) return false } fmt.Fprintln(cf, "Package:", repo.Control["Package"]) // c.Package.String()) fmt.Fprintln(cf, "Source:", repo.Control["Source"]) // c.Source.String()) fmt.Fprintln(cf, "Version:", repo.Control["Version"]) // c.Version.String()) fmt.Fprintln(cf, "Architecture:", repo.Control["Architecture"]) // c.Architecture.String()) writeControlVar(cf, repo, "Depends") writeControlVar(cf, repo, "Build-Depends") stamp := time.Now().UTC().Format("2006/01/02 15:04:05 UTC") // update to now now despite what the GUI is showing fmt.Fprintln(cf, "Package-Build-Date:", stamp) fmt.Fprintln(cf, "Git-Tag-Date:", "todo: get from repo") writeControlVar(cf, repo, "Maintainer") writeControlVar(cf, repo, "Packager") writeControlVar(cf, repo, "GoPath") writeControlVar(cf, repo, "URL") writeControlVar(cf, repo, "Conflicts") desc, _ := repo.Control["Description"] // c.Description.String() parts := strings.Split(desc, "\n") fmt.Fprintln(cf, "Description:", strings.Join(parts, "\n ")) return true } func writeControlVar(f *os.File, repo *gitpb.Repo, varname string) { val, _ := repo.Control[varname] if val == "" { return } fmt.Fprintln(f, val+":", val) } // try to guess or figure out the config file values // if there is not a control file func computeControlValues(repo *gitpb.Repo) bool { if repo.Control["Package"] == "" { // get the package name from the repo name path := repo.Control["pathL"] // c.pathL.String() parts := strings.Split(path, "/") name := parts[len(parts)-1] repo.Control["Package"] = name } if repo.Control["Source"] == "" { repo.Control["Source"] = repo.Control["Package"] } if repo.Control["Build-Depends"] == "" { repo.Control["Build-Depends"] = repo.Control["golang"] } if repo.Control["Recommends"] == "" { repo.Control["Recommends"] = repo.Control["go-gui-toolkits"] } if repo.Control["Maintainer"] == "" { repo.Control["Maintainer"] = "todo: get from ENV" } if repo.Control["Description"] == "" { repo.Control["Description"] = "todo: put URL here" } return true } // stamp := time.Now().UTC().Format("2006/01/02 15:04:05 UTC") func getDateStamp(tag string) string { var r cmd.Status if me.repo == nil { r = shell.Run([]string{"git", "log", "-1", "--format=%at", tag}) } else { r = me.repo.Run([]string{"git", "log", "-1", "--format=%at", tag}) } out := strings.Join(r.Stdout, "\n") out = strings.TrimSpace(out) // Convert the string to an integer gitTagTimestampInt, err := strconv.ParseInt(out, 10, 64) if err != nil { fmt.Println("Error converting timestamp:", err) return "git tag " + tag + " unknown" } // Parse the Unix timestamp into a time.Time object gitTagDate := time.Unix(gitTagTimestampInt, 0) return gitTagDate.UTC().Format("2006-01-02_15:04:05_UTC") // close to RFC3339 }