Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# xcode-github Change Log

## v1.1.1 - February 6, 2019
* Fixed a bug where the PR branch was not checked out: Instead the template bot branch was used.

## v1.1.0 - January 29, 2019
* Fixed Xcode server login.
* Updated documentation.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ GitHub "Close PR" Event -> xcode-github deletes Xcode Bot
### Xcode Bot Documentation

* [Xcode Bot Documentation](https://developer.apple.com/library/content/documentation/Xcode/Conceptual/XcodeServerAPIReference/Bots.html)
* Debugging hint: In console include messages with subsystem contains `xcsd`.

#### Xcode Schemes
* [Xcode URI Scheme Examples](https://cocoaengineering.com/2018/01/01/some-useful-url-schemes-in-xcode-9/)
Expand Down
3 changes: 2 additions & 1 deletion XcodeGitHub/XGGitHubPullRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ FOUNDATION_EXPORT NSString*_Nonnull NSStringFromXGPullRequestStatus(XGPullReques
@property (strong, readonly) NSString*_Nullable body;
@property (strong, readonly) NSString*_Nullable state;
@property (strong, readonly) NSDictionary*_Nullable dictionary;
@property (strong, readonly) NSString*_Nullable sha;
@property (strong, readonly) NSString*_Nullable headSHA;
@property (strong, readonly) NSString*_Nullable baseSHA;
@property (strong, readonly) NSString*_Nullable githubPRURL;

+ (instancetype _Nonnull) new NS_UNAVAILABLE;
Expand Down
9 changes: 5 additions & 4 deletions XcodeGitHub/XGGitHubPullRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ - (instancetype) initWithDictionary:(NSDictionary *)dictionary {
NSInteger index = range.location + range.length;
if (index < fullname.length) _repoName = [fullname substringFromIndex:index];
}
_sha = _dictionary[@"head"][@"sha"];
_headSHA = _dictionary[@"head"][@"sha"];
_baseSHA = _dictionary[@"base"][@"sha"];
_githubPRURL = _dictionary[@"url"];
return self;
}
Expand Down Expand Up @@ -231,7 +232,7 @@ + (NSString*) stringFromStatus:(XGPullRequestStatus)status {
{
NSString* string = [NSString stringWithFormat:
@"https://github.com/ghapi/repos/%@/%@/commits/%@/statuses",
self.repoOwner, self.repoName, self.sha];
self.repoOwner, self.repoName, self.headSHA];
NSURL *URL = [NSURL URLWithString:string];
if (!URL) {
error =
Expand Down Expand Up @@ -305,7 +306,7 @@ - (NSError*_Nullable) setStatus:(XGPullRequestStatus)status
NSError *error = nil;
NSString* string = [NSString stringWithFormat:
@"https://github.com/ghapi/repos/%@/%@/statuses/%@",
self.repoOwner, self.repoName, self.sha];
self.repoOwner, self.repoName, self.headSHA];
NSURL *URL = [NSURL URLWithString:string];
if (!URL) {
error =
Expand Down Expand Up @@ -366,7 +367,7 @@ - (NSError*_Nullable) addComment:(NSString*)comment {
NSError *error = nil;
NSString* string = [NSString stringWithFormat:
@"https://github.com/ghapi/repos/%@/%@/commits/%@/comments",
self.repoOwner, self.repoName, self.sha];
self.repoOwner, self.repoName, self.headSHA];
NSURL *URL = [NSURL URLWithString:string];
if (!URL) {
error =
Expand Down
170 changes: 140 additions & 30 deletions XcodeGitHub/XGXcodeBot.m
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,10 @@ - (instancetype) initWithServer:(XGServer*)server dictionary:(NSDictionary *)dic
_repoOwner = [self.sourceControlRepository
substringWithRange:NSMakeRange(ownerRange.location+1, repoRange.location - ownerRange.location - 1)];
_repoName = [self.sourceControlRepository
substringWithRange:NSMakeRange(repoRange.location+1, self.sourceControlRepository.length - repoRange.location - 1)];
substringWithRange:NSMakeRange(
repoRange.location+1,
self.sourceControlRepository.length - repoRange.location - 1
)];
if ([_repoName hasSuffix:@".git"]) {
_repoName = [_repoName substringWithRange:NSMakeRange(0, _repoName.length-4)];
}
Expand Down Expand Up @@ -511,10 +514,21 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName
XGXcodeBot *bot = nil;
NSError *localError = nil;
{
// Simply using the 'duplicate' bot api and changing the git branch won't actually change
// the git branch to the branch of the PR.
//
// Steps:
// 1. Duplicate the bot.
// 2. Get the new bot.
// 3. Modify the bot to the new branch with a PATCH.

//
// Duplicate the bot:
//

NSString *string =
[NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate",
self.server.server,
self.botID];
self.server.server, self.botID];
NSURL *URL = [NSURL URLWithString:string];
if (!URL) {
localError =
Expand All @@ -528,47 +542,143 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName
BNCLogError(@"Bad server name '%@'.", self.server.server);
goto exit;
}
__auto_type dictionary = [NSMutableDictionary new];
dictionary[@"name"] = newBotName;
dictionary[@"templateBotName"] = self.name;
dictionary[@"pullRequestNumber"] = pullRequestNumber;
dictionary[@"pullRequestTitle"] = pullRequestTitle;

NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError];
if (!data) {
localError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSKeyValueValidationError
userInfo:@{ NSLocalizedDescriptionKey: @"Can't create bot dictionary."}];
}
if (localError || !data) goto exit;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
BNCNetworkOperation *operation =
[[BNCNetworkService shared]
postOperationWithURL:URL
contentType:@"application/json"
data:data
completion:^(BNCNetworkOperation *operation) {
dispatch_semaphore_signal(semaphore);
}];
if (self.server.user.length > 0)
[operation setUser:self.server.user password:self.server.password];
[operation.request addValue:@"7" forHTTPHeaderField:@"X-XCSClientVersion"];
[operation start];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (operation.error) {
localError = operation.error;
goto exit;
}
if (operation.HTTPStatusCode != 201) {
localError = [NSError errorWithDomain:NSNetServicesErrorDomain
code:NSNetServicesInvalidError userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"HTTP Status %ld", (long) operation.HTTPStatusCode]}];
BNCLogDebug(@"Response was: %@.", [operation stringFromResponseData]);
goto exit;
}
[operation deserializeJSONResponseData];
NSDictionary *d = (id) operation.responseData;
if (!([d isKindOfClass:NSDictionary.class] && [d[@"_id"] isKindOfClass:NSString.class])) {
localError =
[NSError errorWithDomain:NSNetServicesErrorDomain
code:NSURLErrorBadServerResponse
userInfo:@{ NSLocalizedDescriptionKey: @"Expected an Xcode bot response." }];
goto exit;
}
NSString*newBotID = d[@"_id"];

//
// Get the just created bot:
//

string =
[NSString stringWithFormat:@"https://%@:20343/api/bots/%@",
self.server.server, newBotID];
URL = [NSURL URLWithString:string];
semaphore = dispatch_semaphore_create(0);
operation =
[[BNCNetworkService shared]
getOperationWithURL:URL
completion:^(BNCNetworkOperation *operation) {
dispatch_semaphore_signal(semaphore);
}];
if (self.server.user.length > 0)
[operation setUser:self.server.user password:self.server.password];
[operation.request addValue:@"7" forHTTPHeaderField:@"X-XCSClientVersion"];
[operation start];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (operation.error) {
localError = operation.error;
goto exit;
}
if (operation.HTTPStatusCode != 200) {
localError = [NSError errorWithDomain:NSNetServicesErrorDomain
code:NSNetServicesInvalidError userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"HTTP Status %ld", (long) operation.HTTPStatusCode]}];
BNCLogDebug(@"Response was: %@.", [operation stringFromResponseData]);
goto exit;
}
[operation deserializeJSONResponseData];
d = (id) operation.responseData;
if (!([d isKindOfClass:NSDictionary.class] &&
[d[@"_id"] isKindOfClass:NSString.class] &&
[newBotID isEqualToString:d[@"_id"]])) {
localError =
[NSError errorWithDomain:NSNetServicesErrorDomain
code:NSURLErrorBadServerResponse
userInfo:@{ NSLocalizedDescriptionKey: @"Expected an Xcode bot response." }];
goto exit;
}

//
// Fix up the git branch of the duplicated bot:
//

NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary*)
string =
[NSString stringWithFormat:@"https://%@:20343/api/bots/%@?overwriteBlueprint=true",
self.server.server, newBotID];
URL = [NSURL URLWithString:string];

dictionary = (__bridge_transfer NSMutableDictionary*)
CFPropertyListCreateDeepCopy(
kCFAllocatorDefault,
(CFDictionaryRef)self.dictionary,
(CFDictionaryRef)d,
kCFPropertyListMutableContainers
);
dictionary[@"configuration"]
[@"sourceControlBlueprint"]
[@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] =
[NSUUID UUID].UUIDString;
dictionary[@"_id"] = nil;
dictionary[@"_rev"] = nil;
dictionary[@"tinyID"] = nil;
dictionary[@"configuration"]
[@"sourceControlBlueprint"]
[@"DVTSourceControlWorkspaceBlueprintLocationsKey"]
[self.sourceControlWorkspaceBlueprintLocationsID]
[@"DVTSourceControlBranchIdentifierKey"] =
branchName;
[@"DVTSourceControlBranchIdentifierKey"]
= branchName;
dictionary[@"configuration"]
[@"sourceControlBlueprint"]
[@"DVTSourceControlWorkspaceBlueprintLocationsKey"]
[self.sourceControlWorkspaceBlueprintLocationsID]
[@"DVTSourceControlLocationRevisionKey"] =
nil;
[@"DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey"]
= @{};
dictionary[@"configuration"]
[@"sourceControlBlueprint"]
[@"DVTSourceControlWorkspaceBlueprintIdentifierKey"]
= [NSUUID UUID].UUIDString;
dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit
dictionary[@"integration_counter"] = @0;
dictionary[@"lastRevisionBlueprint"] = @{};
dictionary[@"name"] = newBotName;
dictionary[@"templateBotName"] = self.name;
dictionary[@"pullRequestNumber"] = pullRequestNumber;
dictionary[@"pullRequestTitle"] = pullRequestTitle;
dictionary[@"sourceControlBlueprintIdentifier"] = [NSUUID UUID].UUIDString;

NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError];
dictionary[@"sourceControlBlueprintIdentifier"] = nil;
dictionary[@"lastRevisionBlueprint"] = nil;

data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError];
if (!data) {
localError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSKeyValueValidationError
userInfo:@{ NSLocalizedDescriptionKey: @"Can't create bot dictionary."}];
}
if (localError || !data) goto exit;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
BNCNetworkOperation *operation =
semaphore = dispatch_semaphore_create(0);
operation =
[[BNCNetworkService shared]
postOperationWithURL:URL
contentType:@"application/json"
Expand All @@ -578,23 +688,23 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName
}];
if (self.server.user.length > 0)
[operation setUser:self.server.user password:self.server.password];
operation.request.HTTPMethod = @"PATCH";
[operation.request addValue:@"7" forHTTPHeaderField:@"X-XCSClientVersion"];
[operation start];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (operation.error) {
localError = operation.error;
goto exit;
}

if (operation.HTTPStatusCode != 201) {
if (operation.HTTPStatusCode != 200) {
localError = [NSError errorWithDomain:NSNetServicesErrorDomain
code:NSNetServicesInvalidError userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"HTTP Status %ld", (long) operation.HTTPStatusCode]}];
BNCLogDebug(@"Response was: %@.", [operation stringFromResponseData]);
goto exit;
}

[operation deserializeJSONResponseData];
NSDictionary *d = (id) operation.responseData;
d = (id) operation.responseData;
if ([d isKindOfClass:NSDictionary.class]) {
bot = [[XGXcodeBot alloc] initWithServer:self.server dictionary:d];
if (bot) {
Expand Down
4 changes: 3 additions & 1 deletion xcode-github-app.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@

/* Begin PBXFileReference section */
2BB6F4D221F9E3FF001E8693 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Base; path = Base.lproj/Credits.rtf; sourceTree = "<group>"; };
4D0DD54420597FE6001721C1 /* xcode-github-app.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-app.md"; path = "xcode-github-app/xcode-github-app.md"; sourceTree = "<group>"; };
2BC31A1222041978003B0EE4 /* xcode-github-cli.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-cli.md"; path = "Documentation/xcode-github-cli.md"; sourceTree = "<group>"; };
4D0DD54420597FE6001721C1 /* xcode-github-app.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-app.md"; path = "Documentation/xcode-github-app.md"; sourceTree = "<group>"; };
4D12BA0A20A252CE00E5B1DB /* XGALogViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XGALogViewController.h; sourceTree = "<group>"; };
4D12BA0B20A252CE00E5B1DB /* XGALogViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XGALogViewController.m; sourceTree = "<group>"; };
4D12BA0C20A252CE00E5B1DB /* XGALogViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = XGALogViewController.xib; sourceTree = "<group>"; };
Expand Down Expand Up @@ -114,6 +115,7 @@
isa = PBXGroup;
children = (
4D0DD54420597FE6001721C1 /* xcode-github-app.md */,
2BC31A1222041978003B0EE4 /* xcode-github-cli.md */,
4DC9E5C4216DCD6700F1F4EE /* XcodeGitHub.xcodeproj */,
4D76616A2057565200216B72 /* xcode-github-app */,
4D7661692057565200216B72 /* Products */,
Expand Down
2 changes: 1 addition & 1 deletion xcode-github-app/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="Add New Server..." keyEquivalent="n" id="Was-JA-tGl">
<menuItem title="Add New Xcode Server..." keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="addNewServer:" target="Ady-hI-5gd" id="Nkp-Lz-WIo"/>
</connections>
Expand Down
12 changes: 10 additions & 2 deletions xcode-github-app/XGAAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
#import "XGASettings.h"
#import <XcodeGitHub/XcodeGitHub.h>

/*
All about About Panels:
http://cocoadevcentral.com/articles/000071.php
*/

@interface XGAAppDelegate () <NSWindowDelegate>
@property (nonatomic, strong) IBOutlet XGALogViewController*logController;
@property (nonatomic, strong) IBOutlet XGAStatusViewController*statusController;
Expand Down Expand Up @@ -54,8 +59,11 @@ - (IBAction)addNewServer:(id)sender {
}

- (BOOL)windowShouldClose:(NSWindow*)window {
self.preferencesController = nil;
return YES;
if (window == self.preferencesController.window) {
self.preferencesController = nil;
return YES;
}
return NO;
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
Expand Down
Loading