From e62c5abf78916697dbc4b25f590deb28f26f5184 Mon Sep 17 00:00:00 2001 From: Jeff Principe Date: Sat, 17 Jun 2023 09:50:01 -0700 Subject: [PATCH] feat(cmd): add --exclude-dirs option (#101) --- README.md | 9 +++++ cmd/gomarkdoc/command.go | 54 ++++++++++++++++++++++++- doc.go | 13 ++++++ logger/README.md | 85 ++++++++++++++++++++++++++++++++++++++++ magefile.go | 10 ++--- 5 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 logger/README.md diff --git a/README.md b/README.md index 0a428be..47673e3 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Flags: -c, --check Check the output to see if it matches the generated documentation. --output must be specified to use this. --config string File from which to load configuration (default: .gomarkdoc.yml) -e, --embed Embed documentation into existing markdown files if available, otherwise append to file. + --exclude-dirs strings List of package directories to ignore when producing documentation. --footer string Additional content to inject at the end of each output file. --footer-file string File containing additional content to inject at the end of each output file. -f, --format string Format to use for writing output data. Valid options: github (default), azure-devops, plain (default "github") @@ -68,6 +69,14 @@ gomarkdoc --output doc.md . The gomarkdoc tool supports generating documentation for both local packages and remote ones. To specify a local package, start the name of the package with a period \(.\) or specify an absolute path on the filesystem. All other package signifiers are assumed to be remote packages. You may specify both local and remote packages in the same command invocation as separate arguments. +If you have a project with many packages but you want to skip documentation generation for some, you can use the \-\-exclude\-dirs option. This will remove any matching directories from the list of directories to process. Excluded directories are specified using the same pathing syntax as the packages to process. Multiple expressions may be comma\-separated or specified by using the \-\-exclude\-dirs flag multiple times. + +For example, in this repository we generate documentation for the entire project while excluding our test packages by running: + +``` +gomarkdoc --exclude-dirs ./testData/... ./... +``` + ### Output Redirection By default, the documentation generated by the gomarkdoc command is sent to standard output, where it can be redirected to a file. This can be useful if you want to perform additional modifications to the documentation or send it somewhere other than a file. However, keep in mind that there are some inconsistencies in how various shells/platforms handle redirected command output \(for example, Powershell encodes in UTF\-16, not UTF\-8\). As a result, the \-\-output option described below is recommended for most use cases. diff --git a/cmd/gomarkdoc/command.go b/cmd/gomarkdoc/command.go index fc3f250..77d73a0 100644 --- a/cmd/gomarkdoc/command.go +++ b/cmd/gomarkdoc/command.go @@ -52,6 +52,7 @@ type commandOptions struct { footerFile string format string tags []string + excludeDirs []string templateOverrides map[string]string templateFileOverrides map[string]string verbosity int @@ -96,6 +97,7 @@ func buildCommand() *cobra.Command { opts.footer = viper.GetString("footer") opts.footerFile = viper.GetString("footerFile") opts.tags = viper.GetStringSlice("tags") + opts.excludeDirs = viper.GetStringSlice("excludeDirs") opts.repository.Remote = viper.GetString("repository.url") opts.repository.DefaultBranch = viper.GetString("repository.defaultBranch") opts.repository.PathFromRoot = viper.GetString("repository.path") @@ -197,6 +199,12 @@ func buildCommand() *cobra.Command { defaultTags(), "Set of build tags to apply when choosing which files to include for documentation generation.", ) + command.Flags().StringSliceVar( + &opts.excludeDirs, + "exclude-dirs", + nil, + "List of package directories to ignore when producing documentation.", + ) command.Flags().CountVarP( &opts.verbosity, "verbose", @@ -241,6 +249,7 @@ func buildCommand() *cobra.Command { _ = viper.BindPFlag("footer", command.Flags().Lookup("footer")) _ = viper.BindPFlag("footerFile", command.Flags().Lookup("footer-file")) _ = viper.BindPFlag("tags", command.Flags().Lookup("tags")) + _ = viper.BindPFlag("excludeDirs", command.Flags().Lookup("exclude-dirs")) _ = viper.BindPFlag("repository.url", command.Flags().Lookup("repository.url")) _ = viper.BindPFlag("repository.defaultBranch", command.Flags().Lookup("repository.default-branch")) _ = viper.BindPFlag("repository.path", command.Flags().Lookup("repository.path")) @@ -294,6 +303,13 @@ func runCommand(paths []string, opts commandOptions) error { specs := getSpecs(paths...) + excluded := getSpecs(opts.excludeDirs...) + if err := validateExcludes(excluded); err != nil { + return err + } + + specs = removeExcludes(specs, excluded) + if err := resolveOutput(specs, outputTmpl); err != nil { return err } @@ -565,13 +581,49 @@ func isIgnoredDir(dirname string) bool { return false } +// validateExcludes checks that the exclude dirs are all directories, not +// packages. +func validateExcludes(specs []*PackageSpec) error { + for _, s := range specs { + if !s.isLocal { + return fmt.Errorf("gomarkdoc: invalid directory specified as an exclude directory: %s", s.ImportPath) + } + } + + return nil +} + +// removeExcludes removes any package specs that were specified as excluded. +func removeExcludes(specs []*PackageSpec, excludes []*PackageSpec) []*PackageSpec { + out := make([]*PackageSpec, 0, len(specs)) + for _, s := range specs { + var exclude bool + for _, e := range excludes { + if !s.isLocal || !e.isLocal { + continue + } + + if r, err := filepath.Rel(s.Dir, e.Dir); err == nil && r == "." { + exclude = true + break + } + } + + if !exclude { + out = append(out, s) + } + } + + return out +} + const ( cwdPathPrefix = "." + string(os.PathSeparator) parentPathPrefix = ".." + string(os.PathSeparator) ) func isLocalPath(path string) bool { - return strings.HasPrefix(path, cwdPathPrefix) || strings.HasPrefix(path, parentPathPrefix) || filepath.IsAbs(path) + return strings.HasPrefix(path, ".") || strings.HasPrefix(path, parentPathPrefix) || filepath.IsAbs(path) } func compare(r1, r2 io.Reader) (bool, error) { diff --git a/doc.go b/doc.go index 0157b9a..f234d2c 100644 --- a/doc.go +++ b/doc.go @@ -28,6 +28,7 @@ // -c, --check Check the output to see if it matches the generated documentation. --output must be specified to use this. // --config string File from which to load configuration (default: .gomarkdoc.yml) // -e, --embed Embed documentation into existing markdown files if available, otherwise append to file. +// --exclude-dirs strings List of package directories to ignore when producing documentation. // --footer string Additional content to inject at the end of each output file. // --footer-file string File containing additional content to inject at the end of each output file. // -f, --format string Format to use for writing output data. Valid options: github (default), azure-devops, plain (default "github") @@ -61,6 +62,18 @@ // local and remote packages in the same command invocation as separate // arguments. // +// If you have a project with many packages but you want to skip documentation +// generation for some, you can use the --exclude-dirs option. This will remove +// any matching directories from the list of directories to process. Excluded +// directories are specified using the same pathing syntax as the packages to +// process. Multiple expressions may be comma-separated or specified by using +// the --exclude-dirs flag multiple times. +// +// For example, in this repository we generate documentation for the entire +// project while excluding our test packages by running: +// +// gomarkdoc --exclude-dirs ./testData/... ./... +// // # Output Redirection // // By default, the documentation generated by the gomarkdoc command is sent to diff --git a/logger/README.md b/logger/README.md new file mode 100644 index 0000000..ec3f250 --- /dev/null +++ b/logger/README.md @@ -0,0 +1,85 @@ + + +# logger + +```go +import "github.com/princjef/gomarkdoc/logger" +``` + +Package logger provides a simple console logger for reporting information about execution to stderr. + +## Index + +- [type Level](<#Level>) +- [type Logger](<#Logger>) + - [func New\(level Level, opts ...Option\) Logger](<#New>) +- [type Option](<#Option>) + - [func WithField\(key string, value interface\{\}\) Option](<#WithField>) + + + +## type [Level]() + +Level defines valid logging levels for a Logger. + +```go +type Level int +``` + +Valid logging levels + +```go +const ( + DebugLevel Level = iota + 1 + InfoLevel + WarnLevel + ErrorLevel +) +``` + + +## type [Logger]() + +Logger provides basic logging capabilities at different logging levels. + +```go +type Logger interface { + Debug(a ...interface{}) + Debugf(format string, a ...interface{}) + Info(a ...interface{}) + Infof(format string, a ...interface{}) + Warn(a ...interface{}) + Warnf(format string, a ...interface{}) + Error(a ...interface{}) + Errorf(format string, a ...interface{}) +} +``` + + +### func [New]() + +```go +func New(level Level, opts ...Option) Logger +``` + +New initializes a new Logger. + + +## type [Option]() + +Option defines an option for configuring the logger. + +```go +type Option func(opts *options) +``` + + +### func [WithField]() + +```go +func WithField(key string, value interface{}) Option +``` + +WithField sets the provided key/value pair for use on all logs. + +Generated by [gomarkdoc]() diff --git a/magefile.go b/magefile.go index 76350c5..ac15d59 100644 --- a/magefile.go +++ b/magefile.go @@ -22,7 +22,7 @@ func Lint() error { return err } - return linter.Command(`run`).Run() + return linter.Command(`run --timeout 5m`).Run() } func Generate() error { @@ -36,18 +36,14 @@ func Build() error { func Doc() error { return shellcmd.RunAll( `go run ./cmd/gomarkdoc .`, - `go run ./cmd/gomarkdoc --header "" ./lang/...`, - `go run ./cmd/gomarkdoc --header "" ./format/...`, - `go run ./cmd/gomarkdoc --header "" ./cmd/...`, + `go run ./cmd/gomarkdoc --header "" --exclude-dirs . --exclude-dirs ./testData/... ./...`, ) } func DocVerify() error { return shellcmd.RunAll( `go run ./cmd/gomarkdoc -c .`, - `go run ./cmd/gomarkdoc -c --header "" ./lang/...`, - `go run ./cmd/gomarkdoc -c --header "" ./format/...`, - `go run ./cmd/gomarkdoc -c --header "" ./cmd/...`, + `go run ./cmd/gomarkdoc -c --header "" --exclude-dirs . --exclude-dirs ./testData/... ./...`, ) }