From 72733e8203654cd26464c8c5a6c955b025102cca Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Tue, 2 May 2017 21:22:20 +0200 Subject: restructure rtree into gofu (with rtree as the first applet) --- pkg/rtree/index.go | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 pkg/rtree/index.go (limited to 'pkg/rtree/index.go') diff --git a/pkg/rtree/index.go b/pkg/rtree/index.go new file mode 100644 index 0000000..a4a0b74 --- /dev/null +++ b/pkg/rtree/index.go @@ -0,0 +1,187 @@ +/******************************************************************************* +* +* Copyright 2017 Stefan Majewsky +* +* This program is free software: you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +* A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +*******************************************************************************/ + +package rtree + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/majewsky/gofu/pkg/util" + + yaml "gopkg.in/yaml.v2" +) + +//Index represents the contents of the index file. +type Index struct { + Repos []*Repo `yaml:"repos"` +} + +func indexPath() string { + homeDir := os.Getenv("HOME") + if homeDir == "" { + util.FatalIfError(errors.New("$HOME is not set (rtree needs the HOME variable to locate its index file)")) + } + return filepath.Join(homeDir, ".rtree/index.yaml") +} + +//ReadIndex reads the index file. +func ReadIndex() *Index { + //read contents of index file + path := indexPath() + buf, err := ioutil.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return &Index{Repos: nil} + } + util.FatalIfError(err) + } + + //deserialize YAML + var index Index + util.FatalIfError(yaml.Unmarshal(buf, &index)) + + //validate YAML + valid := true + for idx, repo := range index.Repos { + if repo.CheckoutPath == "" { + util.ShowError(fmt.Errorf("missing \"repos[%d].path\"", idx)) + valid = false + } + if len(repo.Remotes) == 0 { + util.ShowError(fmt.Errorf("missing \"repos[%d].remotes\"", idx)) + valid = false + } + for idx2, remote := range repo.Remotes { + switch { + case remote.Name == "": + util.ShowError(fmt.Errorf("missing \"repos[%d].remotes[%d].name\"", idx, idx2)) + valid = false + case remote.URL == "": + util.ShowError(fmt.Errorf("missing \"repos[%d].remotes[%d].url\"", idx, idx2)) + valid = false + } + } + } + + if !valid { + util.FatalIfError(errors.New("index file is corrupted; see errors above")) + } + + sort.Sort(reposByAbsPath(index.Repos)) + return &index +} + +type reposByAbsPath []*Repo + +func (r reposByAbsPath) Len() int { return len(r) } +func (r reposByAbsPath) Less(i, j int) bool { return r[i].AbsolutePath() < r[j].AbsolutePath() } +func (r reposByAbsPath) Swap(i, j int) { r[i], r[j] = r[j], r[i] } + +//Write writes the index file to disk. +func (i *Index) Write() { + buf, err := yaml.Marshal(i) + util.FatalIfError(err) + path := indexPath() + util.FatalIfError(os.MkdirAll(filepath.Dir(path), 0755)) + util.FatalIfError(ioutil.WriteFile(path, buf, 0644)) +} + +//InteractiveRebuild implements the `rtree index` subcommand. +func (i *Index) InteractiveRebuild() error { + //check if existing index entries are still checked out + existingRepos := make(map[string]*Repo) + var newRepos []*Repo + for _, repo := range i.Repos { + gitDirPath := filepath.Join(repo.AbsolutePath(), ".git") + fi, err := os.Stat(gitDirPath) + if err == nil { + if fi.IsDir() { + //everything okay with this repo + existingRepos[repo.CheckoutPath] = repo + newRepos = append(newRepos, repo) + continue + } + return fmt.Errorf("%s is not a directory: I'm seriously confused", gitDirPath) + } + if err != nil && !os.IsNotExist(err) { + return err + } + + //repo has been deleted - ask what to do + fmt.Printf("repository %s has been deleted\n", filepath.Join(RootPath, repo.CheckoutPath)) + + var remoteURLs []string + for _, remote := range repo.Remotes { + if remote.Name == "origin" { + remoteURLs = []string{remote.URL} + break + } + remoteURLs = append(remoteURLs, remote.URL) + } + + var choice string + if len(remoteURLs) == 0 { + choice = util.Prompt( + "no remote to restore from; (d)elete from index or (s)kip?", + []string{"d", "s"}, + ) + } else { + choice = util.Prompt( + fmt.Sprintf("(r)estore from %s, (d)elete from index, or (s)kip?", strings.Join(remoteURLs, " and ")), + []string{"r", "d", "s"}, + ) + } + + switch choice { + case "r": + err := repo.Checkout() + if err != nil { + return err + } + newRepos = append(newRepos, repo) + case "d": + continue + case "s": + newRepos = append(newRepos, repo) + } + } + + //index new repos + err := ForeachPhysicalRepo(func(newRepo Repo) error { + repo, exists := existingRepos[newRepo.CheckoutPath] + if exists { + //update the existing index entry with the new remotes + repo.Remotes = newRepo.Remotes + } else { + newRepos = append(newRepos, &newRepo) + } + return nil + }) + if err != nil { + return err + } + + i.Repos = newRepos + return nil +} -- cgit v1.3.1