func (a BugApplication) List(args []string) { issues, _ := ioutil.ReadDir(string(bugs.GetRootDir()) + "/issues") // No parameters, print a list of all bugs if len(args) == 0 { for idx, issue := range issues { var dir bugs.Directory = bugs.Directory(issue.Name()) fmt.Printf("Issue %d: %s\n", idx+1, dir.ToTitle()) } return } // There were parameters, so show the full description of each // of those issues b := bugs.Bug{} for i, length := 0, len(args); i < length; i += 1 { idx, err := strconv.Atoi(args[i]) if err != nil { listTags(issues, args) return } if idx > len(issues) || idx < 1 { fmt.Printf("Invalid issue number %d\n", idx) continue } if err == nil { b.LoadBug(bugs.Directory(bugs.GetRootDir() + "/issues/" + bugs.Directory(issues[idx-1].Name()))) b.ViewBug() if i < length-1 { fmt.Printf("\n--\n\n") } } } fmt.Printf("\n") }
func (a BugApplication) Edit(args []string) { issues, _ := ioutil.ReadDir(string(bugs.GetRootDir()) + "/issues") // No parameters, print a list of all bugs if len(args) == 1 { idx, err := strconv.Atoi(args[0]) if idx > len(issues) || idx < 1 { fmt.Printf("Invalid issue number %d\n", idx) return } dir := bugs.Directory(bugs.GetRootDir()) + "/issues/" + bugs.Directory(issues[idx-1].Name()) cmd := exec.Command(getEditor(), string(dir)+"/Description") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { log.Fatal(err) } } else { fmt.Printf("Usage: %s edit issuenum\n", os.Args[0]) fmt.Printf("\nNo issue number specified\n") } }
func (a BugApplication) Roadmap() { issues, _ := ioutil.ReadDir(string(bugs.GetRootDir()) + "/issues") var bgs [](bugs.Bug) for idx, _ := range issues { b := bugs.Bug{} b.LoadBug(bugs.Directory(bugs.GetRootDir() + "/issues/" + bugs.Directory(issues[idx].Name()))) bgs = append(bgs, b) } sort.Sort(BugListByMilestone(bgs)) fmt.Printf("# Roadmap for %s\n", bugs.GetRootDir().GetShortName().ToTitle()) milestone := "" for i := len(bgs) - 1; i >= 0; i -= 1 { b := bgs[i] newMilestone := b.Milestone() if milestone != newMilestone { if newMilestone == "" { fmt.Printf("\n## No milestone set:\n") } else { fmt.Printf("\n## %s:\n", newMilestone) } } fmt.Printf("- %s\n", b.Title) milestone = newMilestone } }
func (s SettingsHandler) Get(r *http.Request, p map[string]interface{}) (string, error) { settings := struct { Title string Directory string }{bugs.GetRootDir().GetShortName().ToTitle(), string(bugs.GetRootDir())} retVal, _ := json.Marshal(settings) return string(retVal), nil }
func (a BugApplication) Roadmap(args ArgumentList) { bgs := bugs.GetAllBugs() sort.Sort(BugListByMilestone(bgs)) fmt.Printf("# Roadmap for %s\n", bugs.GetRootDir().GetShortName().ToTitle()) milestone := "" for i := len(bgs) - 1; i >= 0; i -= 1 { b := bgs[i] newMilestone := b.Milestone() if milestone != newMilestone { if newMilestone == "" { fmt.Printf("\n## No milestone set:\n") } else { fmt.Printf("\n## %s:\n", newMilestone) } } if args.HasArgument("--simple") == false { fmt.Printf("- %s\n", b.Title("status priority")) } else { fmt.Printf("- %s\n", b.Title("")) } milestone = newMilestone } }
func (m BugPageHandler) Get(r *http.Request, extras map[string]interface{}) (string, error) { if r.URL.Path == "/issues" || r.URL.Path == "/issues/" { return getBugList() } bugDir := string(bugs.GetRootDir()) + r.URL.Path b := bugs.Bug{} b.LoadBug(bugs.Directory(bugDir)) switch r.URL.Query().Get("format") { case "json": bJSON, _ := json.Marshal(b) return string(bJSON), nil default: page := BugRenderer{Bug: b} page.RootElement = "RBugPage" page.Title = b.Title("") page.JSFiles = []string{ // Bootstrap JS //"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js", // React JS "https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js", "https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js", "/js/BugPage.js", } page.CSSFiles = []string{ "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"} return HTMLPageRenderer.Render(page), nil } }
func (a BugApplication) Purge() { cmd := exec.Command("git", "clean", "-fd", string(bugs.GetRootDir())+"/issues") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { log.Fatal(err) } }
func getBugList() (string, error) { issues, _ := ioutil.ReadDir(string(bugs.GetRootDir()) + "/issues") var issuesSlice []string for _, issue := range issues { issuesSlice = append(issuesSlice, issue.Name()) } retVal, _ := json.Marshal(issuesSlice) return string(retVal), nil }
func main() { app := BugApplication{} if bugs.GetRootDir() == "" { fmt.Printf("Could not find issues directory.\n") fmt.Printf("Make sure either the PMIT environment variable is set, or a parent directory of your working directory has an issues folder.\n") fmt.Printf("Aborting.\n") os.Exit(2) } if len(os.Args) > 1 { switch os.Args[1] { case "add", "new", "create": app.Create(os.Args[2:]) case "view", "list": app.List(os.Args[2:]) case "priority": app.Priority(os.Args[2:]) case "status": app.Status(os.Args[2:]) case "milestone": app.Milestone(os.Args[2:]) case "tag": app.Tag(os.Args[2:]) case "mv", "rename", "retitle", "relabel": app.Relabel(os.Args[2:]) case "purge": app.Purge() case "rm", "close": app.Close(os.Args[2:]) case "edit": app.Edit(os.Args[2:]) case "--version", "version": app.Version() case "env": app.Env() case "dir", "pwd": app.Pwd() case "commit": app.Commit() case "roadmap": app.Roadmap(os.Args[2:]) case "help": fallthrough default: app.Help(os.Args[1:]...) } } else { app.Help() } }
func (a BugApplication) Close(args []string) { issues, _ := ioutil.ReadDir(string(bugs.GetRootDir()) + "/issues") // No parameters, print a list of all bugs if len(args) == 0 { fmt.Printf("Usage: %s close IssueNumber\n\nMust provide an IssueNumber to close as parameter\n", os.Args[0]) return } // There were parameters, so show the full description of each // of those issues for i := 0; i < len(args); i += 1 { idx, err := strconv.Atoi(args[i]) if idx > len(issues) || idx < 1 { fmt.Printf("Invalid issue number %d\n", idx) continue } if err == nil { dir := bugs.GetRootDir() + "/issues/" + bugs.Directory(issues[idx-1].Name()) fmt.Printf("Removing %s\n", dir) os.RemoveAll(string(dir)) } } }
func Roadmap(args ArgumentList) { var bgs []bugs.Bug if args.HasArgument("--filter") { tags := strings.Split(args.GetArgument("--filter", ""), ",") fmt.Printf("%s", tags) bgs = bugs.FindBugsByTag(tags) } else { bgs = bugs.GetAllBugs() } sort.Sort(BugListByMilestone(bgs)) fmt.Printf("# Roadmap for %s\n", bugs.GetRootDir().GetShortName().ToTitle()) milestone := "" for i := len(bgs) - 1; i >= 0; i -= 1 { b := bgs[i] newMilestone := b.Milestone() if milestone != newMilestone { if newMilestone == "" { fmt.Printf("\n## No milestone set:\n") } else { fmt.Printf("\n## %s:\n", newMilestone) } } if args.HasArgument("--simple") { fmt.Printf("- %s\n", b.Title("")) } else { options := "" if !args.HasArgument("--no-status") { options += "status" } if !args.HasArgument("--no-priority") { options += " priority" } if !args.HasArgument("--no-identifier") { options += " identifier" } if args.HasArgument("--tags") { options += "tags" } fmt.Printf("- %s\n", b.Title(options)) } milestone = newMilestone } }
func (a BugApplication) Relabel(Args []string) { if len(Args) < 2 { fmt.Printf("Usage: %s relabel issuenum New Title\n", os.Args[0]) return } b, err := bugs.LoadBugByStringIndex(Args[0]) if err != nil { fmt.Printf("Could not load bug: %s\n", err.Error()) return } currentDir, _ := b.GetDirectory() newDir := bugs.GetRootDir() + "/issues/" + bugs.TitleToDir(strings.Join(Args[1:], " ")) fmt.Printf("Moving %s to %s\n", currentDir, newDir) err = os.Rename(string(currentDir), string(newDir)) if err != nil { fmt.Printf("Error moving directory\n") } }
func listTags(files []os.FileInfo, args []string) { hasTag := func(tags []string, tag string) bool { for _, candidate := range tags { if candidate == tag { return true } } return false } b := bugs.Bug{} for idx, _ := range files { b.LoadBug(bugs.Directory(bugs.GetRootDir() + "/issues/" + bugs.Directory(files[idx].Name()))) tags := b.StringTags() for _, tag := range args { if hasTag(tags, tag) { fmt.Printf("Issue %d: %s (%s)\n", idx+1, b.Title, strings.Join(tags, ", ")) } } } }
func (a BugApplication) Pwd() { fmt.Printf("%s", bugs.GetRootDir()+"/issues") }
func (a BugApplication) Env() { fmt.Printf("Settings used by this command:\n") fmt.Printf("\nIssues directory:\t%s/issues/", bugs.GetRootDir()) fmt.Printf("\nEditor:\t%s", getEditor()) fmt.Printf("\n") }
// This will try and commit the $(bug pwd) directory // transparently. It does the following steps: // // 1. "git stash create" // 2. "git reset --mixed" (unstage the user's currently staged files) // 3. "git add $(bug pwd)" // 4. "git commit" // 5a. "git reset --hard" (if there was any stash created, // this is necessary for 5b to work.) // 5b. "git stash apply --index" the stash from step 1 func (a BugApplication) Commit() { type FileStatus struct { IndexStatus string WorkingDirStatus string Filename string } statusOutput := func(dir bugs.Directory) []FileStatus { cmd := exec.Command("git", "status", "--porcelain", "-z", string(dir)) output, err := cmd.Output() if err != nil { fmt.Printf("Could not check git status") return nil } fileStatusLines := strings.Split(string(output), "\000") var files []FileStatus for _, line := range fileStatusLines { if len(line) == 0 { continue } files = append(files, FileStatus{ IndexStatus: line[0:1], WorkingDirStatus: line[1:2], Filename: line[2:], }) } return files } // Before doing anything, check git status to see if // the index is in a state that's going to cause an // error sOutput := statusOutput(bugs.GetIssuesDir()) for _, file := range sOutput { if file.IndexStatus == "D" { fmt.Printf("You have manually staged changes in your issue directory which will conflict with %s commit.\n", os.Args[0]) return } } sOutput = statusOutput(bugs.GetRootDir()) for _, file := range sOutput { if file.IndexStatus == "A" { fmt.Printf("You have a new file staged in your git index, which will cause conflicts with %s commit. Please either commit your changes or unstage %s.\n", os.Args[0], file.Filename) return } } cmd := exec.Command("git", "stash", "create") output, err := cmd.Output() if err != nil { fmt.Printf("Could not execute git stash create") return } var stashHash string = strings.Trim(string(output), "\n") // Unstage everything, if there was anything stashed, so that // we don't commit things that the user has staged that aren't // issues if stashHash != "" { cmd = exec.Command("git", "reset", "--mixed") err = cmd.Run() if err != nil { } } // Commit the issues directory // git add $(bug pwd) // git commit -m "Added new issues" -q cmd = exec.Command("git", "add", "-A", string(bugs.GetRootDir())+"/issues") err = cmd.Run() if err != nil { fmt.Printf("Could not add to index?\n") } cmd = exec.Command("git", "commit", "-m", "Added or removes issues with the tool \"bug\"", "-q") err = cmd.Run() if err != nil { // If nothing was added commit will have an error, // but we don't care it just means there's nothing // to commit. fmt.Printf("No new issues commited\n") } // There were changes that had been stashed, so we need // to restore them with git stash apply.. first, we // need to do a "git reset --hard" so that the dirty working // tree doesn't cause an error. This isn't as scary as it // sounds, since immediately after git reset --hard we apply // a stash which has the exact same changes that we just threw // away. if stashHash != "" { cmd = exec.Command("git", "reset", "--hard") err = cmd.Run() if err != nil { fmt.Printf("Error resetting the git working tree\n") fmt.Printf("The stash which should have your changes is: %s\n", stashHash) } cmd = exec.Command("git", "stash", "apply", "--index", stashHash) err = cmd.Run() if err != nil { fmt.Printf("Error restoring the git working tree") fmt.Printf("The stash which should have your changes is: %s\n", stashHash) // If nothing was stashed, it's not the end of the world. //fmt.Printf("Could not pop from stash\n") } } }
func main() { if bugs.GetRootDir() == "" { fmt.Printf("Could not find issues directory.\n") fmt.Printf("Make sure either the PMIT environment variable is set, or a parent directory of your working directory has an issues folder.\n") fmt.Println("(If you just started new repo, you probably want to create directory named `issues`).") fmt.Printf("Aborting.\n") os.Exit(2) } // Create a pipe for a pager to use r, w, err := os.Pipe() if err != nil { fmt.Fprintln(os.Stderr, err) } // Capture STDOUT for the Pager stdout := os.Stdout // Don't capture the output on MacOS, because for some reason // it doesn't work and results in nothing getting printed if runtime.GOOS != "darwin" { os.Stdout = w } // Invoke less -RF attached to the pipe // we created cmd := exec.Command("less", "-RF") cmd.Stdin = r cmd.Stdout = stdout cmd.Stderr = os.Stderr // Make sure the pipe is closed after we // finish, then restore STDOUT defer func() { w.Close() if err := cmd.Run(); err != nil { fmt.Fprintln(os.Stderr, err) } os.Stdout = stdout }() if len(os.Args) > 1 { if len(os.Args) >= 3 && os.Args[2] == "--help" { os.Args[1], os.Args[2] = "help", os.Args[1] } switch os.Args[1] { case "add", "new", "create": os.Stdout = stdout bugapp.Create(os.Args[2:]) case "view", "list": // bug list with no parameters shouldn't autopage, // bug list with bugs to view should. So the original // stdout is passed as a parameter. bugapp.List(os.Args[2:], stdout) case "priority": bugapp.Priority(os.Args[2:]) case "status": bugapp.Status(os.Args[2:]) case "milestone": bugapp.Milestone(os.Args[2:]) case "id", "identifier": bugapp.Identifier(os.Args[2:]) case "tag": bugapp.Tag(os.Args[2:]) case "mv", "rename", "retitle", "relabel": bugapp.Relabel(os.Args[2:]) case "purge": // This shouldn't autopage os.Stdout = stdout bugapp.Purge() case "rm", "close": bugapp.Close(os.Args[2:]) case "edit": // Edit needs the original Stdout since it // invokes an editor os.Stdout = stdout bugapp.Edit(os.Args[2:]) case "--version", "version": bugapp.Version() case "env": bugapp.Env() case "dir", "pwd": bugapp.Pwd() case "commit": bugapp.Commit(os.Args[2:]) case "roadmap": bugapp.Roadmap(os.Args[2:]) case "help": fallthrough default: bugapp.Help(os.Args[1:]...) } } else { bugapp.Help() } }