-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do not attempt to find an executable if a script is referenced #216
Do not attempt to find an executable if a script is referenced #216
Conversation
CI reports some broken tests under Linux:
Could you please have a look what causes these and how to update your PR accordingly, @adinapoli ? |
In a lucky turn of events I will be working remotely from Rome and my digital nomad workstation is a Linux machine (as opposed to my main Mac mini), so I can definitely look into this one later this week. Thanks for the heads up! |
Thanks for adding a test case. With enough testing we can get make a change. |
c1dae8b
to
51b2fa1
Compare
@andreasabel @gregwebs Ok, I think we should be in business now. Two failures were due to the fact I had forget to add As far as windows testing goes, obviously running that bash script doesn't make sense, so I have guarded the test using |
src/Shelly.hs
Outdated
-- it would be better to specifically detect that bug | ||
= case fp of | ||
-- If the 'FilePath' contains a more articulated path than just | ||
-- an executable, don't try to find it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this rather check whether there is a directory component in exe
? What about bla/hello.sh
instead of ./hello.sh
? Currently you would accept e.g. .bla/hello.sh
or .hello.sh
.
I think checking for starting with dot isn't the correct criterion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I take your point this is too simplistic. I will have to think about this a little bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andreasabel Aha! After a more thorough look, I have finally nailed down exactly why the regression happened. We have this code, which is responsible for locating the file, in the guts of whichEith
:
whichFull fp = do
(trace . mappend "which " . toTextIgnore) fp >> whichUntraced
where
whichUntraced | isAbsolute fp = checkFile
| dotSlash splitOnDirs = checkFile
| length splitOnDirs > 0 = lookupPath >>= leftPathError
| otherwise = lookupCache >>= leftPathError
splitOnDirs = splitDirectories fp
dotSlash ("./":_) = True
dotSlash _ = False
As you can see, we are already accounting for dotSlash
, so why things fail? Well, it turns out that both system-filepath
and filepath
have a splitDirectory
function, but hey behave differently:
> import qualified Filesystem.Path.CurrentOS as SFP
> import qualified Data.Text as T
> import System.FilePath as FP
> FP.splitDirectories "./test/data/hello.sh"
[".","test","data","hello.sh"]
> SFP.splitDirectories (SFP.fromText $ T.pack "./test/data/hello.sh")
[FilePath "./",FilePath "test/",FilePath "data/",FilePath "hello.sh"]
>
As you can see the former doesn't include the "./", so the pattern match on dotSlash
fails, and here is our regression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent analysis! Thanks, @adinapoli!
51b2fa1
to
51aff5e
Compare
src/Shelly.hs
Outdated
| otherwise = lookupCache >>= leftPathError | ||
whichUntraced | isAbsolute fp = checkFile | ||
| startsWithDot splitOnDirs = checkFile | ||
| length splitOnDirs > 0 = lookupPath >>= leftPathError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand when the guard length splitOnDirs > 0
can fail (be False
)... It seems this happens only when fp
is []
. But this should not happen, should it? So is the last case dead?
Here are some experiments:
ghci> :t splitDirectories
splitDirectories :: FilePath -> [FilePath]
ghci> splitDirectories "."
["."]
ghci> splitDirectories ""
[]
ghci> splitDirectories "/"
["/"]
ghci> splitDirectories "//"
["//"]
ghci> splitDirectories "///"
["///"]
ghci> splitDirectories "/a"
["/","a"]
ghci> splitDirectories "/a/"
["/","a"]
ghci> splitDirectories "/a/b"
["/","a","b"]
ghci> splitDirectories "c:/a"
["c:","a"]
ghci> splitDirectories "c:a"
["c:a"]
ghci> splitDirectories "c:foo.bat"
["c:foo.bat"]
ghci> splitDirectories "c:\\foo.bat"
["c:\\foo.bat"]
ghci> splitDirectories "c:\\\\foo.bat"
["c:\\\\foo.bat"]
ghci> splitDirectories "c:/foo.bat"
["c:","foo.bat"]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which output of splitDirectories
am I looking at? the one from system-filepath
or filepath
? 😅 Hard to tell why that guard was added, as it's alas not documented -- possibly some weird corner case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry, this is the one of filepath
which is used in latest shelly
. But the system-filepath
one is not much different.
@gregwebs : Can you explain the mystery of the length splitOnDirs > 0
guard? You introduced it in d441583#r70391227
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greg agrees that the guard length splitOnDirs > 0
is unnecessary (and the clause below can be dropped).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! I will amend this PR with this and the other changes between today and tomorrow. Thanks!
src/Shelly.hs
Outdated
-- function, but it returns \"./\" as its first argument, | ||
-- so we pattern match on both for backward-compatibility. | ||
startsWithDot (".":_) = True | ||
startsWithDot ("./":_) = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it actually make sense to keep the old test for backwards-compatibility? I mean, System.FilePath.splitDirectories
will never return list that contains "./"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look harmless -- the only reason why I have kept it was mostly for documenting purposes and to defend ourselves from draconian cases where people would attempt frankestein builds that uses newer Shelly-s but system-filepath
(assuming that is possible). I suppose it gives us a bit more flexibility at a very little cost, but I am also happy to get rid of the redundant test 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't get me wrong: I am very much for documenting the why and also the history.
However, an impossible case shouldn't be there, because it will create insecurity in the reader of the code. It will actually suggest that the case is possible and should be handled. If you want to keep it in, it should be
startsWithDot ("./":_) = undefined -- this case is impossible
but I think this isn't necessary.
I'd keep references to previous behavior in the comment.
@andreasabel hehe yes, my temptation would be to eschew any temptation to improve Shelly as part of this PR and instead deliver the smallest fix that restores the old behaviour, and perhaps we flag all these low hanging fruits in a ticket, as those sounds very inviting for newcomers willing to contribute to the project 😉 |
@andreasabel Ok, I have addressed all the review remarks. Reshuffling that Something I should point out though is that, at least on my Linux machine, > import System.FilePath as FP
> FP.splitDirectories ""
[]
> This is very weird, because you also ran the same test here but got something different. Having said that, the tests seem to still all pass, so perhaps that's nothing to worry about. Furthermore, I couldn't conceivably see why somebody would try to run a command by passing the empty string as the executable (and I agree with your analysis, garbage in, garbage out). A case could be made that Shelly could get rid of these unrepresentable states if it had its own (internal) definition of something like |
@andreasabel I have lost track of this -- are you folks expecting anything else from me or is this essentially good to go? Thanks! 😉 |
This commit simplifies the local `whichFull` function (local to `whichEith`) by dropping a redundant guard as well as /always/ running `lookupPath >>= leftPathError`. Previously we had this: ``` whichFull fp = do (trace . mappend "which " . toTextIgnore) fp >> whichUntraced where whichUntraced | isAbsolute fp = checkFile | dotSlash splitOnDirs = checkFile | length splitOnDirs > 0 = lookupPath >>= leftPathError | otherwise = lookupCache >>= leftPathError splitOnDirs = splitDirectories fp ``` However `splitOnDirs` can never return the empty list, so that guard was redundant as that code path was always executed, and the /otherwise/ case never executed.
238b5f4
to
c68825e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again @adinapoli and apologies for the long break.
I am planning to release this as 1.11.0 now, assuming you haven't experienced any problems with this code in the last 9 month.
I rebased this on latest master
but otherwise left the commits untouched.
No worries, and thanks for picking this one up 😉 In interest of full transparency, I ended up not using (just yet) this branch/commit, as I try to avoid depending on unreleased/unapproved forks/branches/patches for production code, so I just ended up downgrading to a suitable Shelly, but given how much we spent thinking about this issue, I think it should be fine. We know precisely where and why the regression happened and we have a test to prevent that from happening again, so we should be good 😉 |
Thanks again, @adinapoli ! |
Beautiful, I'll keep you posted, thanks! |
@andreasabel This is a (potentially naive) attempt at fixing #189 (and perhaps #107?).
This was the result of a quick hacking session so I am sure the story is more complicated than this, and perhaps we should also have @gregwebs chime in on this one.
I am not sure why the switch from, but the crux of the issue is that unlesssystem-filepath
tofilepath
triggered itshelly
is run withescaping False
, the inputFilePath
fed intorun
& co will be "escaped", which in this context means calling therunCommand
function, which will attempt to find an executable atPATH
.This works most of the time, but if we have something like
./foo/bar/baz.sh
, this won't work, andShelly
will complain.The easiest fix I could think of was to simply check if we had a
FilePath
commencing with.
and, in this case, we don't attempt to find an executable, but we run the command as-if we usedescaping False
.I have also added a regression test, to catch this in the future.
Thoughts?
EDIT: For visibility, here is the postmortem copied from an inline comment:
I have finally nailed down exactly why the regression happened. We have this code, which is responsible for locating the file, in the guts of
whichEith
:As you can see, we are already accounting for
dotSlash
, so why things fail? Well, it turns out that bothsystem-filepath
andfilepath
have asplitDirectory
function, but hey behave differently:As you can see the former doesn't include the "./", so the pattern match on
dotSlash
fails, and here is our regression.