Skip to content

Commit

Permalink
ANE-1380 - Add color and update formatting in cli (#1367)
Browse files Browse the repository at this point in the history
* Add color and improve formatting in cli help commands
  • Loading branch information
JeffreyHuynh1 authored Jan 26, 2024
1 parent 97bcbc9 commit b78c3ba
Show file tree
Hide file tree
Showing 20 changed files with 444 additions and 173 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# FOSSA CLI Changelog

## v3.8.34
- Add color and update formatting in cli help commands ([#1367](https://github.com/fossas/fossa-cli/pull/1367))

## v3.8.33
- Removes warnings and tracebacks to stderr [#1358](https://github.com/fossas/fossa-cli/pull/1358)

Expand Down
1 change: 1 addition & 0 deletions spectrometer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ library
Strategy.Swift.Xcode.Pbxproj
Strategy.Swift.Xcode.PbxprojParser
Strategy.SwiftPM
Style
System.Args
Text.URI.Builder
Type.Operator
Expand Down
128 changes: 85 additions & 43 deletions src/App/Fossa/Config/Analyze.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module App.Fossa.Config.Analyze (
loadConfig,
cliParser,
mergeOpts,
branchHelp,
) where

import App.Fossa.Config.Common (
Expand Down Expand Up @@ -97,28 +98,25 @@ import Options.Applicative (
InfoMod,
Parser,
eitherReader,
help,
helpDoc,
hidden,
long,
metavar,
option,
optional,
progDesc,
progDescDoc,
short,
strOption,
switch,
(<|>),
)
import Path (Abs, Dir, Path, Rel)
import Path.Extra (SomePath)
import Prettyprinter (Doc, annotate, defaultLayoutOptions, layoutPretty)
import Prettyprinter.Render.Terminal (AnsiStyle, Color (Red), color, renderStrict)
import Prettyprinter (Doc, annotate, indent)
import Prettyprinter.Render.Terminal (AnsiStyle, Color (Green, Red), color)
import Style (applyFossaStyle, boldItalicized, coloredBoldItalicized, formatDoc, stringToHelpDoc)
import Types (ArchiveUploadType (..), LicenseScanPathFilters (..), TargetFilter)

-- Utility functions
coloredText :: Color -> Doc AnsiStyle -> String
coloredText clr str = toString . renderStrict . layoutPretty defaultLayoutOptions $ annotate (color clr) str

-- CLI flags, for use with 'Data.Flag'
data DeprecatedAllowNativeLicenseScan = DeprecatedAllowNativeLicenseScan deriving (Generic)
data ForceVendoredDependencyRescans = ForceVendoredDependencyRescans deriving (Generic)
Expand Down Expand Up @@ -265,43 +263,72 @@ instance ToJSON ExperimentalAnalyzeConfig where
toEncoding = genericToEncoding defaultOptions

mkSubCommand :: (AnalyzeConfig -> EffStack ()) -> SubCommand AnalyzeCliOpts AnalyzeConfig
mkSubCommand = SubCommand "analyze" analyzeInfo cliParser loadConfig mergeOpts
mkSubCommand = SubCommand ("analyze") analyzeInfo cliParser loadConfig mergeOpts

analyzeInfo :: InfoMod a
analyzeInfo = progDesc "Scan for projects and their dependencies"
analyzeInfo =
progDescDoc $
Just . formatDoc $
vsep
[ "Scan for projects and their dependencies"
, ""
, "Examples:"
, indent 2 $
vsep
[ "- " <> coloredBoldItalicized Green "fossa analyze" <> " # Analyze current directory"
, "- " <> coloredBoldItalicized Green "fossa analyze {path/to/specific/directory}" <> " # Analyze specific directory"
, "- " <> coloredBoldItalicized Green "fossa analyze --detect-vendored" <> " # Analyze and detect vendored open source libraries in current directory"
, "- " <> coloredBoldItalicized Green "fossa analyze --debug" <> " # Analyze current directory in debug mode"
]
]

cliParser :: Parser AnalyzeCliOpts
cliParser =
AnalyzeCliOpts
<$> commonOpts
<*> switch (long "output" <> short 'o' <> help "Output results to stdout instead of uploading to fossa")
<*> flagOpt UnpackArchives (long "unpack-archives" <> help "Recursively unpack and analyze discovered archives")
<*> flagOpt JsonOutput (long "json" <> help "Output project metadata as json to the console. Useful for communicating with the FOSSA API")
<*> flagOpt IncludeAll (long "include-unused-deps" <> help "Include all deps found, instead of filtering non-production deps. Ignored by VSI.")
<*> flagOpt NoDiscoveryExclusion (long "debug-no-discovery-exclusion" <> help "Ignore filters during discovery phase. This is for debugging only and may be removed without warning." <> hidden)
<*> switch (applyFossaStyle <> long "output" <> short 'o' <> stringToHelpDoc "Output results to stdout instead of uploading to FOSSA")
<*> flagOpt UnpackArchives (applyFossaStyle <> long "unpack-archives" <> stringToHelpDoc "Recursively unpack and analyze discovered archives")
<*> flagOpt JsonOutput (applyFossaStyle <> long "json" <> stringToHelpDoc "Output project metadata as JSON to the console. This is useful for communicating with the FOSSA API.")
<*> flagOpt IncludeAll (applyFossaStyle <> long "include-unused-deps" <> stringToHelpDoc "Include all deps found, instead of filtering non-production deps. Ignored by VSI.")
<*> flagOpt NoDiscoveryExclusion (applyFossaStyle <> long "debug-no-discovery-exclusion" <> stringToHelpDoc "Ignore filters during discovery phase. This is for debugging only and may be removed without warning." <> hidden)
-- AllowNativeLicenseScan is no longer used. We started emitting a warning if it was used in https://github.com/fossas/fossa-cli/pull/1113
<*> flagOpt DeprecatedAllowNativeLicenseScan (long "experimental-native-license-scan" <> hidden)
<*> flagOpt DeprecatedAllowNativeLicenseScan (applyFossaStyle <> long "experimental-native-license-scan" <> hidden)
<*> optional vendoredDependencyModeOpt
<*> flagOpt ForceVendoredDependencyRescans (long "force-vendored-dependency-rescans" <> help "Force vendored dependencies to be rescanned even if the revision has been previously analyzed by FOSSA. This currently only works for CLI-side license scans.")
<*> optional (strOption (long "branch" <> short 'b' <> help "this repository's current branch (default: current VCS branch)"))
<*> flagOpt ForceVendoredDependencyRescans (applyFossaStyle <> long "force-vendored-dependency-rescans" <> stringToHelpDoc "Force vendored dependencies to be rescanned even if the revision has been previously analyzed by FOSSA. This currently only works for CLI-side license scans.")
<*> optional (strOption (applyFossaStyle <> long "branch" <> short 'b' <> helpDoc branchHelp))
<*> metadataOpts
<*> many (option (eitherReader targetOpt) (long "only-target" <> help "Only scan these targets. See targets.only in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader targetOpt) (long "exclude-target" <> help "Exclude these targets from scanning. See targets.exclude in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader pathOpt) (long "only-path" <> help "Only scan these paths. See paths.only in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader pathOpt) (long "exclude-path" <> help "Exclude these paths from scanning. See paths.exclude in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader targetOpt) (applyFossaStyle <> long "only-target" <> stringToHelpDoc "Only scan these targets. See `targets.only` in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader targetOpt) (applyFossaStyle <> long "exclude-target" <> stringToHelpDoc "Exclude these targets from scanning. See `targets.exclude` in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader pathOpt) (applyFossaStyle <> long "only-path" <> stringToHelpDoc "Only scan these paths. See `paths.only` in the fossa.yml spec." <> metavar "PATH"))
<*> many (option (eitherReader pathOpt) (applyFossaStyle <> long "exclude-path" <> stringToHelpDoc "Exclude these paths from scanning. See `paths.exclude` in the fossa.yml spec." <> metavar "PATH"))
<*> vsiEnableOpt
<*> flagOpt BinaryDiscovery (long "experimental-enable-binary-discovery" <> help "Reports binary files as unlicensed dependencies")
<*> optional (strOption (long "experimental-link-project-binary" <> metavar "DIR" <> help "Links output binary files to this project in FOSSA"))
<*> flagOpt BinaryDiscovery (applyFossaStyle <> long "experimental-enable-binary-discovery" <> stringToHelpDoc "Reports binary files as unlicensed dependencies")
<*> optional (strOption (applyFossaStyle <> long "experimental-link-project-binary" <> metavar "DIR" <> stringToHelpDoc "Links output binary files to this project in FOSSA"))
<*> optional dynamicLinkInspectOpt
<*> many skipVSIGraphResolutionOpt
<*> baseDirArg
<*> experimentalUseV3GoResolver
<*> experimentalAnalyzePathDependencies
<*> flagOpt ForceFirstPartyScans (long "experimental-force-first-party-scans" <> help "Force first party scans")
<*> flagOpt ForceNoFirstPartyScans (long "experimental-block-first-party-scans" <> help "Block first party scans. This can be used to forcibly turn off first-party scans if your organization defaults to first-party scans.")
<*> flagOpt IgnoreOrgWideCustomLicenseScanConfigs (long "ignore-org-wide-custom-license-scan-configs" <> help "Ignore custom-license scan configurations for your organization. These configurations are defined in the \"Integrations\" section of the Admin settings in the FOSSA web app")
<*> optional (strOption (long "fossa-deps-file" <> help "Path to fossa-deps file including filename (default: fossa-deps.{yaml|yml|json})"))
<*> flagOpt StaticOnlyTactics (long "static-only-analysis" <> help "Only analyze the project using static strategies.")
<*> flagOpt ForceFirstPartyScans (applyFossaStyle <> long "experimental-force-first-party-scans" <> stringToHelpDoc "Force first party scans")
<*> flagOpt ForceNoFirstPartyScans (applyFossaStyle <> long "experimental-block-first-party-scans" <> stringToHelpDoc "Block first party scans. This can be used to forcibly turn off first-party scans if your organization defaults to first-party scans.")
<*> flagOpt IgnoreOrgWideCustomLicenseScanConfigs (applyFossaStyle <> long "ignore-org-wide-custom-license-scan-configs" <> stringToHelpDoc "Ignore custom-license scan configurations for your organization. These configurations are defined in the `Integrations` section of the Admin settings in the FOSSA web app")
<*> optional (strOption (applyFossaStyle <> long "fossa-deps-file" <> helpDoc fossaDepsFileHelp <> metavar "FILEPATH"))
<*> flagOpt StaticOnlyTactics (applyFossaStyle <> long "static-only-analysis" <> stringToHelpDoc "Only analyze the project using static strategies.")
where
fossaDepsFileHelp :: Maybe (Doc AnsiStyle)
fossaDepsFileHelp =
Just . formatDoc $
vsep
[ "Path to fossa-deps file including filename"
, boldItalicized "Default:" <> " fossa-deps.{yaml|yml|json}"
]
branchHelp :: Maybe (Doc AnsiStyle)
branchHelp =
Just . formatDoc $
vsep
[ "This repository's current branch"
, boldItalicized "Default: " <> "Current VCS branch"
]

data GoDynamicTactic
= GoModulesBasedTactic
Expand All @@ -320,40 +347,54 @@ experimentalUseV3GoResolver =
)
. switch
$ long "experimental-use-v3-go-resolver"
<> help
( coloredText Red "DEPRECATED: This is now default and will be removed in the future."
<> " For Go: generate a graph of module deps based on package deps. This will be the default in the future."
)
<> applyFossaStyle
<> helpDoc helpMsg
where
helpMsg :: Maybe (Doc AnsiStyle)
helpMsg =
Just . formatDoc $
vsep
[ annotate (color Red) "DEPRECATED: This is now default and will be removed in the future"
, boldItalicized "For Go: " <> "Generate a graph of module deps based on package deps. This will be the default in the future."
]

experimentalAnalyzePathDependencies :: Parser Bool
experimentalAnalyzePathDependencies =
switch $
long "experimental-analyze-path-dependencies"
<> help
( "License scan dependencies sourced from file system, as indicated in manifest files. This will be enabled by default in the future."
)
<> applyFossaStyle
<> stringToHelpDoc "License scan dependencies sourced from file system, as indicated in manifest files. This will be enabled by default in the future."

vendoredDependencyModeOpt :: Parser ArchiveUploadType
vendoredDependencyModeOpt = option (eitherReader parseType) (long "force-vendored-dependency-scan-method" <> metavar "METHOD" <> help "Force the vendored dependency scan method. The options are 'CLILicenseScan' or 'ArchiveUpload'. 'CLILicenseScan' is usually the default unless your organization has overridden this.")
vendoredDependencyModeOpt = option (eitherReader parseType) (applyFossaStyle <> long "force-vendored-dependency-scan-method" <> metavar "METHOD" <> helpDoc vendoredDependencyScanMethodHelp)
where
parseType :: String -> Either String ArchiveUploadType
parseType = \case
"ArchiveUpload" -> Right ArchiveUpload
"CLILicenseScan" -> Right CLILicenseScan
val -> Left ("must be either 'CLILicenseScan' or 'ArchiveUpload'. Found " <> val)

vendoredDependencyScanMethodHelp :: Maybe (Doc AnsiStyle)
vendoredDependencyScanMethodHelp =
Just . formatDoc $
vsep
[ "Force the vendored dependency scan method"
, boldItalicized "Options: " <> coloredBoldItalicized Green "CLILicenseScan" <> boldItalicized "|" <> coloredBoldItalicized Green "ArchiveUpload"
, boldItalicized "Note: " <> coloredBoldItalicized Green "CLILicenseScan" <> " is usually the default unless your organization has overridden this"
]

dynamicLinkInspectOpt :: Parser FilePath
dynamicLinkInspectOpt = visible <|> legacy
where
visible = strOption (long "detect-dynamic" <> metavar "BINARY" <> help "Analyzes dynamically linked libraries in the target binary and reports them as dependencies")
legacy = strOption (long "experimental-analyze-dynamic-deps" <> hidden)
visible = strOption (applyFossaStyle <> long "detect-dynamic" <> metavar "BINARY" <> stringToHelpDoc "Analyzes dynamically linked libraries in the target binary and reports them as dependencies")
legacy = strOption (applyFossaStyle <> long "experimental-analyze-dynamic-deps" <> hidden)

vsiEnableOpt :: Parser (Flag VSIAnalysis)
vsiEnableOpt = visible <|> legacyExperimental <|> legacy
where
visible = flagOpt VSIAnalysis (long "detect-vendored" <> help "Analyzes project files on disk to detect vendored open source libraries")
legacyExperimental = flagOpt VSIAnalysis (long "experimental-enable-vsi" <> hidden)
legacy = flagOpt VSIAnalysis (long "enable-vsi" <> hidden)
visible = flagOpt VSIAnalysis (applyFossaStyle <> long "detect-vendored" <> stringToHelpDoc "Analyzes project files on disk to detect vendored open source libraries")
legacyExperimental = flagOpt VSIAnalysis (applyFossaStyle <> long "experimental-enable-vsi" <> hidden)
legacy = flagOpt VSIAnalysis (applyFossaStyle <> long "enable-vsi" <> hidden)

skipVSIGraphResolutionOpt :: Parser VSI.Locator
skipVSIGraphResolutionOpt = (option (eitherReader parseLocator) details)
Expand All @@ -362,8 +403,9 @@ skipVSIGraphResolutionOpt = (option (eitherReader parseLocator) details)
mconcat
[ long "experimental-skip-vsi-graph"
, metavar "LOCATOR"
, help "Skip resolving the dependencies of the given project in FOSSA"
, stringToHelpDoc "Skip resolving the dependencies of the given project in FOSSA"
]
<> applyFossaStyle
parseLocator :: String -> Either String VSI.Locator
parseLocator s = case VSI.parseLocator (toText s) of
Left err -> Left $ toString (toText err)
Expand Down
Loading

0 comments on commit b78c3ba

Please sign in to comment.