Skip to content

Commit

Permalink
Handle differences in user environments while executing hooks (#696)
Browse files Browse the repository at this point in the history
* Handle user shell not being bash

* Better handling for differences evaluating PATH in user shells

* Follow Git behavior and fallback to using sh to execute hooks
  • Loading branch information
zwaldowski authored Aug 28, 2020
1 parent 0d4bb0c commit 515ffe8
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 9 deletions.
1 change: 1 addition & 0 deletions GitUpKit/Core/GCPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ extern int git_submodule_foreach_block(git_repository* repo, int (^block)(git_su
@property(nonatomic) NSTimeInterval executionTimeOut; // Default is 0.0 i.e. no timeout
@property(nonatomic, copy) NSDictionary* additionalEnvironment;
@property(nonatomic, copy) NSString* currentDirectoryPath;
@property(nonatomic) BOOL fallBackToDefaultInterpreter;
- (instancetype)initWithExecutablePath:(NSString*)path;
- (BOOL)runWithArguments:(NSArray*)arguments stdin:(NSData*)stdin stdout:(NSData**)stdout stderr:(NSData**)stderr exitStatus:(int*)exitStatus error:(NSError**)error; // Returns NO if "exitStatus" is NULL and executable exits with a non-zero status
@end
Expand Down
12 changes: 9 additions & 3 deletions GitUpKit/Core/GCPrivate.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,21 @@ - (void)_fileHandleDataAvailable:(NSNotification*)notification {
}
}

- (BOOL)runWithArguments:(NSArray*)arguments stdin:(NSData*)stdin stdout:(NSData**)stdout stderr:(NSData**)stderr exitStatus:(int*)exitStatus error:(NSError**)error {
- (BOOL)_runWithDefaultInterpreter:(BOOL)useSH arguments:(NSArray*)arguments stdin:(NSData*)stdin stdout:(NSData**)stdout stderr:(NSData**)stderr exitStatus:(int*)exitStatus error:(NSError**)error {
BOOL success = NO;
NSPipe* inPipe = nil;
NSPipe* outPipe = nil;
NSPipe* errorPipe = nil;
NSTimer* timer = nil;

NSTask* task = [[NSTask alloc] init];
task.launchPath = _executablePath;
task.launchPath = useSH ? @"/bin/sh" : _executablePath;
NSMutableDictionary* environment = [[NSMutableDictionary alloc] initWithDictionary:[[NSProcessInfo processInfo] environment]];
[environment addEntriesFromDictionary:_additionalEnvironment];
task.environment = environment;
task.currentDirectoryPath = _currentDirectoryPath ? _currentDirectoryPath : [[NSFileManager defaultManager] currentDirectoryPath];
task.arguments = arguments ? arguments : @[];
NSArray* interpreterArguments = useSH ? @[ _executablePath ] : @[];
task.arguments = [interpreterArguments arrayByAddingObjectsFromArray:arguments];

if (stdin) {
inPipe = [[NSPipe alloc] init];
Expand Down Expand Up @@ -170,6 +171,11 @@ - (BOOL)runWithArguments:(NSArray*)arguments stdin:(NSData*)stdin stdout:(NSData
return success;
}


- (BOOL)runWithArguments:(NSArray*)arguments stdin:(NSData*)stdin stdout:(NSData**)stdout stderr:(NSData**)stderr exitStatus:(int*)exitStatus error:(NSError**)error {
return [self _runWithDefaultInterpreter:NO arguments:arguments stdin:stdin stdout:stdout stderr:stderr exitStatus:exitStatus error:error] || (self.fallBackToDefaultInterpreter && [self _runWithDefaultInterpreter:YES arguments:arguments stdin:stdin stdout:stdout stderr:stderr exitStatus:exitStatus error:error]);
}

@end

#endif
20 changes: 14 additions & 6 deletions GitUpKit/Core/GCRepository.m
Original file line number Diff line number Diff line change
Expand Up @@ -310,24 +310,32 @@ - (NSString*)pathForHookWithName:(NSString*)name {
return [[NSFileManager defaultManager] isExecutableFileAtPath:path] ? path : nil;
}

- (NSString*)getPATHUsingShell:(NSString*)shell error:(NSError**)error {
GCTask* task = [[GCTask alloc] initWithExecutablePath:shell];
NSData* data;
// `-l` is not supported with `-c` in all shells (tcsh), so try without.
// Some shells use quoting of $PATH to trigger POSIX compatibility behavior (fish).
// Not all shells support `-n` on `echo`.
if (![task runWithArguments:@[ @"-l", @"-c", @"echo \"$PATH\"" ] stdin:NULL stdout:&data stderr:NULL exitStatus:NULL error:error] && ![task runWithArguments:@[ @"-c", @"echo \"$PATH\"" ] stdin:NULL stdout:&data stderr:NULL exitStatus:NULL error:error]) {
return nil;
}
return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
}

- (BOOL)runHookWithName:(NSString*)name arguments:(NSArray*)arguments standardInput:(NSString*)standardInput error:(NSError**)error {
NSString* path = [self pathForHookWithName:name];
if (path) {
static NSString* cachedPATH = nil;
if (cachedPATH == nil) {
GCTask* task = [[GCTask alloc] initWithExecutablePath:@"/bin/bash"]; // TODO: Handle user shell not being bash
NSData* data;
if (![task runWithArguments:@[ @"-l", @"-c", @"echo -n $PATH" ] stdin:NULL stdout:&data stderr:NULL exitStatus:NULL error:error]) {
return NO;
}
cachedPATH = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
cachedPATH = [self getPATHUsingShell:NSProcessInfo.processInfo.environment[@"SHELL"] error:error] ?: [self getPATHUsingShell:@"/bin/sh" error:error];
XLOG_DEBUG_CHECK(cachedPATH);
}

CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
GCTask* task = [[GCTask alloc] initWithExecutablePath:path];
task.currentDirectoryPath = self.workingDirectoryPath; // TODO: Is this the right working directory?
task.additionalEnvironment = @{@"PATH" : cachedPATH};
task.fallBackToDefaultInterpreter = YES;
int status;
NSData* stdoutData;
NSData* stderrData;
Expand Down

0 comments on commit 515ffe8

Please sign in to comment.