Merge branch 'epontan-root-option'

This commit is contained in:
Vidar Holen 2019-04-24 18:52:04 -07:00
commit 301705edea
6 changed files with 77 additions and 1 deletions

View File

@ -4,6 +4,7 @@
- Files containing Bats tests can now be checked - Files containing Bats tests can now be checked
- Directory wide directives can now be placed in a `.shellcheckrc` - Directory wide directives can now be placed in a `.shellcheckrc`
- Verbose mode: Use `-S verbose` for especially pedantic suggestions - Verbose mode: Use `-S verbose` for especially pedantic suggestions
- Source paths: Use `-P dir1:dir2` to specify path for sourced files
- SC2249: Warn about `case` with missing default case (verbose) - SC2249: Warn about `case` with missing default case (verbose)
- SC2248: Warn about unquoted variables without special chars (verbose) - SC2248: Warn about unquoted variables without special chars (verbose)
- SC2247: Warn about $"(cmd)" and $"{var}" - SC2247: Warn about $"(cmd)" and $"{var}"

View File

@ -67,6 +67,14 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts.
: Don't try to look for .shellcheckrc configuration files. : Don't try to look for .shellcheckrc configuration files.
**-P**\ *SOURCEPATH*,\ **--source-path=***SOURCEPATH*
: Specify paths to search for sourced files, separated by `:` on Unix and
`;` on Windows. Absolute paths will also be rooted in these. The special
path `SCRIPTDIR` can be used to specify the currently checked script's
directory, as in `-P SCRIPTDIR` or `-P SCRIPTDIR/../libs`. Subsequent
`-P` flags accumulate and take predecence.
**-S**\ *SEVERITY*,\ **--severity=***severity* **-S**\ *SEVERITY*,\ **--severity=***severity*
: Specify minimum severity of errors to consider. Valid values in order of : Specify minimum severity of errors to consider. Valid values in order of

View File

@ -69,6 +69,7 @@ instance Monoid Status where
data Options = Options { data Options = Options {
checkSpec :: CheckSpec, checkSpec :: CheckSpec,
externalSources :: Bool, externalSources :: Bool,
sourcePaths :: [FilePath],
formatterOptions :: FormatterOptions, formatterOptions :: FormatterOptions,
minSeverity :: Severity minSeverity :: Severity
} }
@ -76,6 +77,7 @@ data Options = Options {
defaultOptions = Options { defaultOptions = Options {
checkSpec = emptyCheckSpec, checkSpec = emptyCheckSpec,
externalSources = False, externalSources = False,
sourcePaths = [],
formatterOptions = newFormatterOptions { formatterOptions = newFormatterOptions {
foColorOption = ColorAuto foColorOption = ColorAuto
}, },
@ -98,6 +100,9 @@ options = [
"Output format (" ++ formatList ++ ")", "Output format (" ++ formatList ++ ")",
Option "" ["norc"] Option "" ["norc"]
(NoArg $ Flag "norc" "true") "Don't look for .shellcheckrc files", (NoArg $ Flag "norc" "true") "Don't look for .shellcheckrc files",
Option "P" ["source-path"]
(ReqArg (Flag "source-path") "SOURCEPATHS")
"Specify path when looking for sourced files (\"SCRIPTDIR\" for script's dir)",
Option "s" ["shell"] Option "s" ["shell"]
(ReqArg (Flag "shell") "SHELLNAME") (ReqArg (Flag "shell") "SHELLNAME")
"Specify dialect (sh, bash, dash, ksh)", "Specify dialect (sh, bash, dash, ksh)",
@ -311,6 +316,12 @@ parseOption flag options =
} }
} }
Flag "source-path" str -> do
let paths = splitSearchPath str
return options {
sourcePaths = (sourcePaths options) ++ paths
}
Flag "sourced" _ -> Flag "sourced" _ ->
return options { return options {
checkSpec = (checkSpec options) { checkSpec = (checkSpec options) {
@ -364,6 +375,7 @@ ioInterface options files = do
configCache <- newIORef ("", Nothing) configCache <- newIORef ("", Nothing)
return SystemInterface { return SystemInterface {
siReadFile = get cache inputs, siReadFile = get cache inputs,
siFindSource = findSourceFile inputs (sourcePaths options),
siGetConfig = getConfig configCache siGetConfig = getConfig configCache
} }
where where
@ -455,6 +467,30 @@ ioInterface options files = do
putStrLn $ file ++ ": " ++ show err putStrLn $ file ++ ": " ++ show err
return ("", True) return ("", True)
andM a b arg = do
first <- a arg
if not first then return False else b arg
findSourceFile inputs sourcePaths currentScript original =
if isAbsolute original
then
let (_, relative) = splitDrive original
in find relative original
else
find original original
where
find filename deflt = do
sources <- filterM ((allowable inputs) `andM` doesFileExist)
(map (</> filename) $ map adjustPath sourcePaths)
case sources of
[] -> return deflt
(first:_) -> return first
scriptdir = dropFileName currentScript
adjustPath str =
case (splitDirectories str) of
("SCRIPTDIR":rest) -> joinPath (scriptdir:rest)
_ -> str
inputFile file = do inputFile file = do
(handle, shouldCache) <- (handle, shouldCache) <-
if file == "-" if file == "-"

View File

@ -150,6 +150,11 @@ checkOptionIncludes includes src =
checkWithRc rc = getErrors checkWithRc rc = getErrors
(mockRcFile rc $ mockedSystemInterface []) (mockRcFile rc $ mockedSystemInterface [])
checkWithIncludesAndSourcePath includes mapper = getErrors
(mockedSystemInterface includes) {
siFindSource = mapper
}
prop_findsParseIssue = check "echo \"$12\"" == [1037] prop_findsParseIssue = check "echo \"$12\"" == [1037]
prop_commentDisablesParseIssue1 = prop_commentDisablesParseIssue1 =
@ -335,5 +340,24 @@ prop_brokenRcGetsWarning = result == [1134, 2086]
csIgnoreRC = False csIgnoreRC = False
} }
prop_sourcePathRedirectsName = result == [2086]
where
f "dir/myscript" "lib" = return "foo/lib"
result = checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] f emptyCheckSpec {
csScript = "#!/bin/bash\nsource lib",
csFilename = "dir/myscript",
csCheckSourced = True
}
prop_sourcePathRedirectsDirective = result == [2086]
where
f "dir/myscript" "lib" = return "foo/lib"
f _ _ = return "/dev/null"
result = checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] f emptyCheckSpec {
csScript = "#!/bin/bash\n# shellcheck source=lib\nsource kittens",
csFilename = "dir/myscript",
csCheckSourced = True
}
return [] return []
runTests = $quickCheckAll runTests = $quickCheckAll

View File

@ -73,6 +73,8 @@ import qualified Data.Map as Map
data SystemInterface m = SystemInterface { data SystemInterface m = SystemInterface {
-- Read a file by filename, or return an error -- Read a file by filename, or return an error
siReadFile :: String -> m (Either ErrorMessage String), siReadFile :: String -> m (Either ErrorMessage String),
-- Given the current script and a sourced file, find the sourced file
siFindSource :: String -> String -> m FilePath,
-- Get the configuration file (name, contents) for a filename -- Get the configuration file (name, contents) for a filename
siGetConfig :: String -> m (Maybe (FilePath, String)) siGetConfig :: String -> m (Maybe (FilePath, String))
} }
@ -287,6 +289,7 @@ data ColorOption =
mockedSystemInterface :: [(String, String)] -> SystemInterface Identity mockedSystemInterface :: [(String, String)] -> SystemInterface Identity
mockedSystemInterface files = SystemInterface { mockedSystemInterface files = SystemInterface {
siReadFile = rf, siReadFile = rf,
siFindSource = fs,
siGetConfig = const $ return Nothing siGetConfig = const $ return Nothing
} }
where where
@ -294,6 +297,7 @@ mockedSystemInterface files = SystemInterface {
case filter ((== file) . fst) files of case filter ((== file) . fst) files of
[] -> return $ Left "File not included in mock." [] -> return $ Left "File not included in mock."
[(_, contents)] -> return $ Right contents [(_, contents)] -> return $ Right contents
fs _ file = return file
mockRcFile rcfile mock = mock { mockRcFile rcfile mock = mock {
siGetConfig = const . return $ Just (".shellcheckrc", rcfile) siGetConfig = const . return $ Just (".shellcheckrc", rcfile)

View File

@ -2087,7 +2087,10 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
input <- input <-
if filename == "/dev/null" -- always allow /dev/null if filename == "/dev/null" -- always allow /dev/null
then return (Right "") then return (Right "")
else system $ siReadFile sys filename else do
currentScript <- Mr.asks currentFilename
filename' <- system $ siFindSource sys currentScript filename
system $ siReadFile sys filename'
case input of case input of
Left err -> do Left err -> do
parseNoteAtId (getId file) InfoC 1091 $ parseNoteAtId (getId file) InfoC 1091 $