Merge branch 'epontan-root-option'
This commit is contained in:
commit
301705edea
|
@ -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}"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 == "-"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 $
|
||||||
|
|
Loading…
Reference in New Issue