Skip to content
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

improving failure output per SR-3275 #921

Merged
merged 1 commit into from Feb 2, 2017
Merged

Conversation

heckj
Copy link
Contributor

@heckj heckj commented Jan 26, 2017

  • resolves https://bugs.swift.org/browse/SR-3275
  • handing through process result utf8output to error for more informative string result
  • adds functional test fixture that will intentionally fail
  • adds test to validate error from underlying git is propogated on failure
  • adds Error capture for tool commands to handle pretty-print of output

Copy link
Member

@ddunbar ddunbar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably test this by running a swift package update subprocess (see existing infrastructure for this) with an overriden PATH, and a dummy "git" script which errors out, or is a symlink to missing file (so it fails to launch), etc.

@@ -208,7 +209,7 @@ public class GitRepository: Repository, WorkingCheckout {
/// The name of the object.
let name: String
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try and leave spurious whitespace changes out of PRs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - sorry about that - didn't intend the whitespace changes, but did a quick save & edit with VM and it cleans extra whitespace from lines from previous settings. I'll undo that with a future update.

@heckj
Copy link
Contributor Author

heckj commented Jan 29, 2017

I tried fiddling with SWIFT_GIT environment variable to simulate a missing git, and the error response I got was a bit different from the error when I simulated "git" existing but failing for some other reason.

The "missing git" scenario:

swift-build: error: posix_spawn error: No such file or directory (2), `["fakegit2", "clone", "--bare", "/private/var/folders/28/jxrtkq8d2tn55zjt1z9dzvqc0000gn/T/Miscellaneous_MissingDependency.1lJNdx/NonExistantPackage", "/private/var/folders/28/jxrtkq8d2tn55zjt1z9dzvqc0000gn/T/Miscellaneous_MissingDependency.1lJNdx/Bar/.build/repositories/NonExistantPackage-8229628763048067322"]

And the "git exists, but failed for some reason" scenario

error: Failed to clone /private/var/folders/28/jxrtkq8d2tn55zjt1z9dzvqc0000gn/T/Miscellaneous_MissingDependency.ilMdJx/NonExistantPackage to <AbsolutePath:"/private/var/folders/28/jxrtkq8d2tn55zjt1z9dzvqc0000gn/T/Miscellaneous_MissingDependency.ilMdJx/Bar/.build/repositories/NonExistantPackage-7794419893060704563">: Some Error Reported Here
Fetching /private/var/folders/28/jxrtkq8d2tn55zjt1z9dzvqc0000gn/T/Miscellaneous_MissingDependency.ilMdJx/NonExistantPackage

I wasn't sure how thorough to be with these variant error messages and validating them, so the example in place uses a faked-up git command (per other examples in that functional test suite) and validates that the error on STDOUT appears in the process output.

Is this a reasonable test setup? I think it at least illustrates that more error information is being presented.

There's left-over nuggets in there to clean up before it's ready to merge, but I wanted to expose the WIP for feedback.

@ankitspd
Copy link
Member

We could do what llbuild does, try to lookup the executable. If we don't have one we can throw before even trying to spawn.
See: https://github.com/apple/swift-llbuild/blob/c324ee3375fa9b128fe1502c74790ee99f925f9b/lib/llvm/Support/Unix/Program.inc

@heckj
Copy link
Contributor Author

heckj commented Jan 30, 2017

@aciidb0mb3r I was thinking of something along those lines as well - something like that was my original thought for what to do to solve this bug, but then I also thought that getting the error from Git (if one happened) reflected out was also valuable per @ddunbar suggestion in the bug report (https://bugs.swift.org/browse/SR-3275).

I'm happy to extend this (and testing) to accomodate that use case. Is there thought or a desire to support other command-line programs as pre-requisites? (I.e. svn perchance?) or any other CLI pieces that we need to verify exists as well as git?

@ankitspd
Copy link
Member

We should do both:

  1. Try to locate an executable before spawning a process and throw if not found.
  2. If cloning a repository errors out, display the underlying git error (i.e. this PR).

These are orthogonal to each other and this PR looks almost complete. Thanks!

Copy link
Member

@ankitspd ankitspd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good in general, minor cleanup comments inline. Thanks!

try localFileSystem.writeFileContents(fakeGit, bytes: stream.bytes)

// Make it executable.
_ = try Process.popen(args: "chmod", "+x", fakeGit.asString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine but we can make an utility method in TestSupport to create executable scripts. Something like:

createExectutableScript(at: fakeGit) { stream in
    stream <<< "..."
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool - I'll give that a shot and refactor these methods to use it.

if let oldPath = oldPath {
env["PATH"] = env["PATH"]! + ":" + oldPath
}
// print("Running with ENV[PATH] = \(String(describing: env["PATH"]))")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray print

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover from learning/debugging and will clean

func testReportingErrorFromGitCommand() throws {
fixture(name: "Miscellaneous/MissingDependency") { prefix in

let fakeGit = prefix.appending(components: "bin", "fakegit")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be git and not fakegit in string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
// print("Running with ENV[PATH] = \(String(describing: env["PATH"]))")

// ?? is it acceptable to use the ENV var override for of 'SWIFT_GIT' to make it seem like one doesn't exist to simulate lack of git installed?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get away by naming the executable as git, at least for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll switch it back to git and remove the ENV var override pieces


let result = try process.waitUntilExit()
let resultOutputString = try result.utf8Output()
// print("FAILED PROCESS RESULT: \(resultOutputString)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray print

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will clean the commented bits

// Verify our fake git's error output appears in the process results
XCTAssert(resultOutputString.contains("Some Error Reported Here"), "Error from fake git was not propogated to process output")

XCTAssertFalse(try Process.running(process.processID))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove

let result = try process.waitUntilExit()
let resultOutputString = try result.utf8Output()
// print("FAILED PROCESS RESULT: \(resultOutputString)")
// We should not have exited with 555 from our fake "git".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to write we should?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, will fix

@@ -16,13 +16,13 @@ import func POSIX.getenv
import enum POSIX.Error

enum GitRepositoryProviderError: Swift.Error {
case gitCloneFailure(url: String, path: AbsolutePath)
case gitCloneFailure(url: String, path: AbsolutePath, errorOutput: String)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should catch this error in Commands/Error.swift so that it can be a bit pretty printed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following along with GitRepositoryProviderError already being defined here as an extension, although I'm not entirely clear on what the conventions on for when you update here vs. Commons/Error.swift. Should I move the whole error enum extension and it's description over to Common/Error.swift? (i.e. lines 18 through 28 in this file?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No no, I meant we catch and handle this error in Commands/Error.swift so it is printed better for the user. For example this can be caught like this in the handle(error:) method in that file.

    ...
    case GitRepositoryProviderError. gitCloneFailure(let url, _, let output):
        print(error: "Failed to clone \(url.asString):\n" + output)
    ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - I think I get it - I see the errors being handled, and this one wasn't handled there previously it seems. Is the convention to keep the relevant specific errors with the code they relate to, but handle printing the output in Common/Error.swift? To be honest, I hadn't traced where the returned description: String was used. While was looking at the other errors, I noticed there is a .gitCloneFailure defined in Get/Error.swift (seperate from the specific GitRepositoryProviderError that I was poking at originally. It doesn't expose this additional detail - does that need to be extended as well?

Copy link
Member

@ankitspd ankitspd Jan 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think of it as API and clients, this module provides an API and Commands is a client which uses this API (and handles that error).
Command is basically a high level client which contains the cli tools: swift-{build, test, package}

You can ignore Get, it's dead code now.

@@ -105,6 +107,11 @@ public func handle(error: Any) -> Never {

case PackageToolOperationError.insufficientOptions(let usage):
print(usage, to: &stderr)

case GitRepositoryProviderError.gitCloneFailure(let url, let path, let errorOutput):
//should I be doing something to utilize the gitCloneFailure CustomStringConvertible description here?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the CustomStringConvertible conformance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do


case GitRepositoryProviderError.gitCloneFailure(let url, let path, let errorOutput):
//should I be doing something to utilize the gitCloneFailure CustomStringConvertible description here?
print(error: "Failed to clone \(url) to \(path): \(errorOutput)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use path.asString
Add \n after colon

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing

@heckj
Copy link
Contributor Author

heckj commented Jan 30, 2017

Once I've added the "checking for git" prior to invoking it mechanism, I'll also rebase this down...

Copy link
Member

@ankitspd ankitspd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, please remove the commented code and we can take this in.
The checking for executable should be separate PR.
Thanks!

}
//extension GitRepositoryProviderError: CustomStringConvertible {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the commented code

@ankitspd
Copy link
Member

ankitspd commented Feb 2, 2017

@swift-ci please test

1 similar comment
@ankitspd
Copy link
Member

ankitspd commented Feb 2, 2017

@swift-ci please test

@ankitspd ankitspd merged commit 860b8dc into apple:master Feb 2, 2017
@heckj heckj deleted the SR-3275 branch February 13, 2017 18:50
@heckj heckj mentioned this pull request Mar 2, 2017
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants