diff options
Diffstat (limited to 'build/main.go')
-rw-r--r-- | build/main.go | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/build/main.go b/build/main.go new file mode 100644 index 0000000..624d25b --- /dev/null +++ b/build/main.go @@ -0,0 +1,441 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "strings" + + "github.com/gohugoio/hugo/modules" + "github.com/gohugoio/testmodBuilder/mods" + "github.com/pkg/errors" + "github.com/shurcooL/go/osutil" + "github.com/spf13/afero" +) + +const ( + + // Increment the minor version. + versionTemplate = "v1.%d.0" + testGitRepoBase = "hugoTestModules1" +) + +func main() { + // Run this on darwin, linux and windows. + goos := runtime.GOOS + gitRepo := testGitRepoBase + "_" + goos + + dir, err := ioutil.TempDir("", "hugotestmods") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + fs := afero.NewOsFs() + m := mods.CreateModules() + + b := &mb{ + fs: fs, + mods: m.Collect(), + environ: osutil.Environ(os.Environ()), + } + + b.cdir(dir) + + must(b.run("git", "clone", fmt.Sprintf("git@github.com:gohugoio/%s.git", gitRepo))) + b.cdir(filepath.Join(dir, gitRepo)) + must(b.all()) + +} + +type mb struct { + + // Increments from cfg.Version + currentMinorVersion int + + fs afero.Fs + mods []*mods.Md + dir string + environ osutil.Environ +} + +func (b *mb) cdir(dir string) { + b.environ.Set("PWD", dir) + b.dir = dir +} + +func (b *mb) createFiles() error { + if err := b.mkDirs(); err != nil { + return err + } + + if err := b.mkConfigs(); err != nil { + return err + } + + if err := b.mkDataFiles(); err != nil { + return err + } + + return nil +} + +func (b *mb) initGoMods() error { + b.running("initGoMods") + for _, m := range b.mods { + hm := b.newModulesHandler(m) + if err := hm.Init(m.Path()); err != nil { + return err + } + } + + return nil +} + +func (b *mb) newModulesHandler(m *mods.Md) *modules.Handler { + return modules.New(b.fs, b.abs(m.Name()), "", m.Paths()) +} + +func (b *mb) abs(name string) string { + return filepath.Join(b.dir, name) +} + +func (b *mb) clean() error { + b.running("clean") + + // The clean script will be run with the target repo's PWD, so + // we need to make the path absolute. + dir, _ := os.Getwd() + if err := b.run("bash", filepath.Join(dir, "clean.sh")); err != nil { + return err + } + + // Clean the relevant part of the mod cache. + gp := os.Getenv("GOPATH") + if gp == "" { + return errors.New("GOPATH not set, cannot clean") + } + + modDir := filepath.Join(gp, "pkg/mod") + + // Walk the directory and remove any directory with a name matching one + // of our modules. + err := afero.Walk(b.fs, modDir, func(path string, info os.FileInfo, err error) error { + if err != nil && !os.IsNotExist(err) { + return err + } + if info.IsDir() { + for _, m := range b.mods { + if strings.Contains(path, m.Name()) { + b.removeModDir(path) + break + } + } + } + return nil + }) + + return err + +} + +func (b *mb) collectModules() error { + b.running("collectModules") + for _, m := range b.mods { + hm := b.newModulesHandler(m) + if _, err := hm.Collect(); err != nil { + return err + } + } + return nil + +} + +func (b *mb) commit(msg string, force bool) error { + b.running("commit") + must(b.run("git", "add", ".")) + err := b.run("git", "commit", "-m", fmt.Sprintf("[%s] %s", b.version(), msg)) + if err != nil { + log.Println("warning: commit: ", err) + } + + if force { + return b.pushForce() + } else { + return b.push() + } +} + +func (b *mb) mkConfigs() error { + b.running("mkconfigs") + for _, m := range b.mods { + config := fmt.Sprintf(` +theme = %s +`, m.PathsStr()) + + if err := afero.WriteFile(b.fs, b.abs(filepath.Join(m.Name(), "config.toml")), []byte(config), 0666); err != nil { + return err + } + } + + return nil +} + +func (b *mb) mkDirs() error { + b.running("mkdirs") + for _, m := range b.mods { + if err := b.fs.Mkdir(b.abs(m.Name()), 0777); err != nil { + return err + } + } + return nil +} + +func (b *mb) mkDataFiles() error { + fileContentPairs := b.dataFiles() + + for _, m := range b.mods { + dataDir := b.abs(filepath.Join(m.Name(), "data")) + if err := b.fs.RemoveAll(dataDir); err != nil { + return err + } + if err := b.fs.MkdirAll(filepath.Join(dataDir, "modinfo"), 0777); err != nil { + return err + } + } + + for i := 0; i < len(fileContentPairs); i += 2 { + path, content := fileContentPairs[i], fileContentPairs[i+1] + if err := afero.WriteFile(b.fs, b.abs(path), []byte(content), 0666); err != nil { + return err + } + } + + return nil +} + +func (b *mb) nextVersion() { + b.currentMinorVersion++ +} + +func (b *mb) push() error { + return b.run("git", "push") +} + +func (b *mb) pushForce() error { + b.running("pushForce") + return b.run("git", "push", "-f", "--tags") +} + +func (b *mb) pushTags() error { + b.running("pushTags") + return b.run("git", "push", "--tags") +} + +func (b *mb) removeModDir(dir string) error { + // Module cache has 0555 directories; make them writable in order to remove content. + afero.Walk(b.fs, dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if info.IsDir() { + os.Chmod(path, 0777) + } + return nil + }) + return b.fs.RemoveAll(dir) + +} + +func (b *mb) run(bin string, args ...string) error { + + stderr := new(bytes.Buffer) + cmd := exec.Command(bin, args...) + + cmd.Env = b.environ + cmd.Dir = b.dir + cmd.Stdout = os.Stdout + cmd.Stderr = io.MultiWriter(stderr, os.Stderr) + + if err := cmd.Run(); err != nil { + if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { + return errors.Errorf("%s not found", bin) + } + + exitErr, ok := err.(*exec.ExitError) + if !ok { + return errors.Errorf("failed to execute '%s %v': %s %T", bin, args, err, err) + } + + return errors.Errorf("%s command failed: %s: %s", bin, exitErr, stderr) + + } + + return nil +} + +func (b *mb) running(name string) { + fmt.Printf("\nRunning %s…\n\n", name) +} + +// Return path/content pairs for the data files. +func (b *mb) dataFiles() []string { + // module.toml + // name.toml + var sf []string + + version := b.version() + + for _, m := range b.mods { + name := m.Name() + content := fmt.Sprintf(`name = %q +version = %q +`, name, version) + sf = append(sf, filepath.Join(name, "data/modinfo", "module.toml"), content) + sf = append(sf, filepath.Join(name, "data/modinfo", name+".toml"), content) + } + + return sf +} + +func (b *mb) tagAndUpdateMods() error { + if err := b.tagVersions(); err != nil { + return err + } + + if err := b.updateModules(); err != nil { + return err + } + + if err := b.commit("Update modules", false); err != nil { + return err + } + + b.nextVersion() + + return nil +} + +func (b *mb) tagVersions() error { + b.running("tagVersions") + version := fmt.Sprintf(versionTemplate, b.currentMinorVersion) + for _, m := range b.mods { + tag := path.Join(m.Name(), version) + if err := b.run("git", "tag", "-a", tag, "-m", fmt.Sprintf("version %s of %s", version, m.Name())); err != nil { + return err + } + } + + if err := b.pushTags(); err != nil { + return err + } + + return b.run("git", "--no-pager", "tag", "-l") +} + +func (b *mb) tidyModules() error { + b.running("tidyModules") + for _, m := range b.mods { + hm := b.newModulesHandler(m) + if err := hm.Tidy(); err != nil { + return err + } + } + return nil +} + +func (b *mb) updateModules() error { + b.running("updateModules") + for _, m := range b.mods { + hm := b.newModulesHandler(m) + if err := hm.Get("-u"); err != nil { + return err + } + } + return nil +} + +func (b *mb) vendorModules() error { + b.running("vendorModules") + var vendored bool + for _, m := range b.mods { + if !m.Vendor { + continue + } + vendored = true + hm := b.newModulesHandler(m) + if err := hm.Vendor(); err != nil { + return err + } + } + + if vendored { + must(b.commit("Vendor modules", false)) + } + + return nil + +} + +func (b *mb) version() string { + return b.versionFor(b.currentMinorVersion) +} + +func (b *mb) versionFor(minorVersion int) string { + return fmt.Sprintf(versionTemplate, minorVersion) +} + +// Start fresh and build all test modules. +// TODO(bep) set up a travis build that builds from Linux, OSX and Windows and +// use all of those in the tests. +func (b *mb) all() error { + must(b.allToVendor()) + + must(b.mkDataFiles()) + must(b.commit("Add new set of static files", false)) + must(b.tagAndUpdateMods()) + + must(b.tidyModules()) + must(b.commit("Tidy modules", false)) + must(b.mkDataFiles()) + must(b.commit("Add new set of static files", false)) + must(b.tagAndUpdateMods()) + + return nil +} + +func (b *mb) allToVendor() error { + must(b.clean()) + + must(b.createFiles()) + must(b.initGoMods()) + must(b.commit("Add initial version of modules", false)) + + must(b.collectModules()) + must(b.commit("Collect modules from Hugo config", false)) + + must(b.tagAndUpdateMods()) + + must(b.mkDataFiles()) + must(b.commit("Add new set of static files", false)) + + must(b.tagAndUpdateMods()) + + must(b.vendorModules()) + must(b.tagAndUpdateMods()) + + return nil +} + +func must(err error) { + if err != nil { + log.Fatal(err) + } +} |