From 4b91909ae8bca0cbb5d4b7a8315bd4c4bc08dad8 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 20 Mar 2019 14:00:20 +0100 Subject: [PATCH 1/2] generate: hide empty sections/stanzas if empty --- cmd/sdkch/main.go | 64 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/cmd/sdkch/main.go b/cmd/sdkch/main.go index b87129299..93a1fca15 100644 --- a/cmd/sdkch/main.go +++ b/cmd/sdkch/main.go @@ -23,10 +23,12 @@ const ( ) var ( - progName string + progName string + verboseLog *log.Logger entriesDir string pruneAfterGenerate bool + verboseLogging bool // sections name-title map sections = map[string]string{ @@ -51,15 +53,24 @@ func init() { if err != nil { log.Fatal(err) } + flag.StringVar(&entriesDir, "d", filepath.Join(cwd, entriesDirName), "entry files directory") flag.BoolVar(&pruneAfterGenerate, "prune", false, "prune old entries after changelog generation") + flag.BoolVar(&verboseLogging, "v", false, "enable verbose logging") flag.Usage = printUsage + } func main() { + logPrefix := fmt.Sprintf("%s: ", filepath.Base(progName)) log.SetFlags(0) - log.SetPrefix(fmt.Sprintf("%s: ", filepath.Base(progName))) + log.SetPrefix(logPrefix) flag.Parse() + verboseLog = log.New(ioutil.Discard, "", 0) + if verboseLogging { + verboseLog.SetOutput(os.Stderr) + verboseLog.SetPrefix(logPrefix) + } if flag.NArg() < 1 { errInsufficientArgs() @@ -131,18 +142,53 @@ func generateFileName(line string) string { return ret[:int(math.Min(float64(len(ret)), float64(maxEntryFilenameLength)))] } +func directoryContents(dirPath string) ([]os.FileInfo, error) { + contents, err := ioutil.ReadDir(dirPath) + if err != nil { + return nil, errors.New("couldn't read directory") + } + + if len(contents) == 0 { + return nil, nil + } + + // Filter out hidden files + newContents := contents[:0] + for _, f := range contents { + if f.Name()[0] != '.' { + newContents = append(newContents, f) + } + } + for i := len(newContents); i < len(contents); i++ { + contents[i] = nil + } + + return newContents, nil +} + func generateChangelog(version string, prune bool) { fmt.Printf("# %s\n\n", version) for sectionDir, sectionTitle := range sections { - - fmt.Printf("## %s\n\n", sectionTitle) + sectionTitlePrinted := false for stanzaDir, stanzaTitle := range stanzas { - fmt.Printf("### %s\n\n", stanzaTitle) path := filepath.Join(entriesDir, sectionDir, stanzaDir) + if contents, err := directoryContents(path); err != nil || len(contents) == 0 { + if err != nil { + verboseLog.Printf("skipping %s: %v", filepath.Join(filepath.Base(entriesDir), sectionDir, stanzaDir), err) + } + continue + } + if !sectionTitlePrinted { + fmt.Printf("## %s\n\n", sectionTitle) + sectionTitlePrinted = true + } + + fmt.Printf("### %s\n\n", stanzaTitle) files, err := ioutil.ReadDir(path) if err != nil && !os.IsNotExist(err) { log.Fatal(err) } + for _, f := range files { if f.Name()[0] == '.' { continue // skip hidden files @@ -154,7 +200,7 @@ func generateChangelog(version string, prune bool) { if prune { if err := os.Remove(filename); err != nil { - fmt.Fprintln(os.Stderr, "couldn't delete file:", filename) + verboseLog.Printf("couldn't delete %s: %v", filename, err) } } } @@ -210,8 +256,8 @@ func writeEntryFile(filename string, bs []byte) { log.Fatal(err) } - fmt.Fprintf(os.Stderr, "Unreleased changelog entry written to: %s\n", filename) - fmt.Fprintln(os.Stderr, "To modify this entry please edit or delete the above file directly.") + log.Printf("Unreleased changelog entry written to: %s\n", filename) + log.Println("To modify this entry please edit or delete the above file directly.") } func validateSectionStanzaDirs(sectionDir, stanzaDir string) { @@ -282,7 +328,7 @@ func launchUserEditor() (string, error) { } func printUsage() { - usageText := fmt.Sprintf(`usage: %s [-d directory] [-prune] command + usageText := fmt.Sprintf(`usage: %s [-d directory] [-prune] [-v] command Maintain unreleased changelog entries in a modular fashion. From 283294171130fe0e0142dcb98a53e0c4dbe703b8 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 20 Mar 2019 14:29:41 +0100 Subject: [PATCH 2/2] Remove -prune flag. -prune is unnecessary and misleading. Once a release is cut, .pending/ contents must be recursively removed by git rm -r and changes committed and pushed. Ref #3892 --- cmd/sdkch/main.go | 71 ++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/cmd/sdkch/main.go b/cmd/sdkch/main.go index 93a1fca15..df3a78145 100644 --- a/cmd/sdkch/main.go +++ b/cmd/sdkch/main.go @@ -26,9 +26,8 @@ var ( progName string verboseLog *log.Logger - entriesDir string - pruneAfterGenerate bool - verboseLogging bool + entriesDir string + verboseLogging bool // sections name-title map sections = map[string]string{ @@ -55,7 +54,6 @@ func init() { } flag.StringVar(&entriesDir, "d", filepath.Join(cwd, entriesDirName), "entry files directory") - flag.BoolVar(&pruneAfterGenerate, "prune", false, "prune old entries after changelog generation") flag.BoolVar(&verboseLogging, "v", false, "enable verbose logging") flag.Usage = printUsage @@ -99,7 +97,10 @@ func main() { if flag.NArg() > 1 { version = strings.Join(flag.Args()[1:], " ") } - generateChangelog(version, pruneAfterGenerate) + generateChangelog(version) + + case "prune": + pruneEmptyDirectories() default: unknownCommand(cmd) @@ -142,20 +143,20 @@ func generateFileName(line string) string { return ret[:int(math.Min(float64(len(ret)), float64(maxEntryFilenameLength)))] } -func directoryContents(dirPath string) ([]os.FileInfo, error) { +func directoryContents(dirPath string) []os.FileInfo { contents, err := ioutil.ReadDir(dirPath) - if err != nil { - return nil, errors.New("couldn't read directory") + if err != nil && !os.IsNotExist(err) { + log.Fatalf("couldn't read directory %s: %v", dirPath, err) } if len(contents) == 0 { - return nil, nil + return nil } // Filter out hidden files newContents := contents[:0] for _, f := range contents { - if f.Name()[0] != '.' { + if f.Name()[0] != '.' { // skip hidden files newContents = append(newContents, f) } } @@ -163,46 +164,32 @@ func directoryContents(dirPath string) ([]os.FileInfo, error) { contents[i] = nil } - return newContents, nil + return newContents } -func generateChangelog(version string, prune bool) { +func generateChangelog(version string) { fmt.Printf("# %s\n\n", version) for sectionDir, sectionTitle := range sections { sectionTitlePrinted := false for stanzaDir, stanzaTitle := range stanzas { path := filepath.Join(entriesDir, sectionDir, stanzaDir) - if contents, err := directoryContents(path); err != nil || len(contents) == 0 { - if err != nil { - verboseLog.Printf("skipping %s: %v", filepath.Join(filepath.Base(entriesDir), sectionDir, stanzaDir), err) - } + files := directoryContents(path) + if len(files) == 0 { continue } + if !sectionTitlePrinted { fmt.Printf("## %s\n\n", sectionTitle) sectionTitlePrinted = true } fmt.Printf("### %s\n\n", stanzaTitle) - files, err := ioutil.ReadDir(path) - if err != nil && !os.IsNotExist(err) { - log.Fatal(err) - } - for _, f := range files { - if f.Name()[0] == '.' { - continue // skip hidden files - } + verboseLog.Println("processing", f.Name()) filename := filepath.Join(path, f.Name()) if err := indentAndPrintFile(filename); err != nil { log.Fatal(err) } - - if prune { - if err := os.Remove(filename); err != nil { - verboseLog.Printf("couldn't delete %s: %v", filename, err) - } - } } fmt.Println() @@ -210,6 +197,15 @@ func generateChangelog(version string, prune bool) { } } +func pruneEmptyDirectories() { + for sectionDir, _ := range sections { + for stanzaDir, _ := range stanzas { + mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir, stanzaDir)) + } + mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir)) + } +} + // nolint: errcheck func indentAndPrintFile(filename string) error { file, err := os.Open(filename) @@ -327,8 +323,20 @@ func launchUserEditor() (string, error) { return tempfilename, nil } +func mustPruneDirIfEmpty(path string) { + if contents := directoryContents(path); len(contents) == 0 { + if err := os.Remove(path); err != nil { + if !os.IsNotExist(err) { + log.Fatal(err) + } + return + } + log.Println(path, "removed") + } +} + func printUsage() { - usageText := fmt.Sprintf(`usage: %s [-d directory] [-prune] [-v] command + usageText := fmt.Sprintf(`usage: %s [-d directory] [-v] command Maintain unreleased changelog entries in a modular fashion. @@ -337,6 +345,7 @@ Commands: the editor to edit the message. generate [version] Generate a changelog in Markdown format and print it to STDOUT. version defaults to UNRELEASED. + prune Delete empty sub-directories recursively. Sections Stanzas --- ---