From 43bdd79b3684dcd11f11adb10cd023182a4798e0 Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Thu, 31 Jan 2019 17:19:05 -0800 Subject: [PATCH 1/8] Checking bot duplication bug. --- XcodeGitHub/XGXcodeBot.m | 2 +- xcode-github-app/Base.lproj/Main.storyboard | 2 +- xcode-github-app/XGAAppDelegate.m | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index 6cb710f..22151da 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -552,7 +552,7 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName [@"DVTSourceControlLocationRevisionKey"] = nil; dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit - dictionary[@"integration_counter"] = @0; + dictionary[@"integration_counter"] = @1; dictionary[@"lastRevisionBlueprint"] = @{}; dictionary[@"name"] = newBotName; dictionary[@"templateBotName"] = self.name; diff --git a/xcode-github-app/Base.lproj/Main.storyboard b/xcode-github-app/Base.lproj/Main.storyboard index 00d7a93..11cdb75 100644 --- a/xcode-github-app/Base.lproj/Main.storyboard +++ b/xcode-github-app/Base.lproj/Main.storyboard @@ -63,7 +63,7 @@ - + diff --git a/xcode-github-app/XGAAppDelegate.m b/xcode-github-app/XGAAppDelegate.m index 2d92f8f..2b2d386 100644 --- a/xcode-github-app/XGAAppDelegate.m +++ b/xcode-github-app/XGAAppDelegate.m @@ -15,6 +15,11 @@ #import "XGASettings.h" #import +/* + All about About Panels: + http://cocoadevcentral.com/articles/000071.php +*/ + @interface XGAAppDelegate () @property (nonatomic, strong) IBOutlet XGALogViewController*logController; @property (nonatomic, strong) IBOutlet XGAStatusViewController*statusController; @@ -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 { From eda45da9cc4215e69f8f7bfbc764b5c9901c257f Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Mon, 4 Feb 2019 13:58:55 -0800 Subject: [PATCH 2/8] Working on the SCM clone. --- .../xcode-github-app.md | 0 .../xcode-github-cli.md | 0 XcodeGitHub/XGXcodeBot.m | 115 ++++++++++++++++++ xcode-github-app.xcodeproj/project.pbxproj | 4 +- xcode-github-app/XGAStatusViewController.m | 31 ++++- xcode-github-app/XGAStatusViewController.xib | 6 + xcode-github-cli.xcodeproj/project.pbxproj | 2 +- 7 files changed, 152 insertions(+), 6 deletions(-) rename {xcode-github-app => Documentation}/xcode-github-app.md (100%) rename {xcode-github-cli => Documentation}/xcode-github-cli.md (100%) diff --git a/xcode-github-app/xcode-github-app.md b/Documentation/xcode-github-app.md similarity index 100% rename from xcode-github-app/xcode-github-app.md rename to Documentation/xcode-github-app.md diff --git a/xcode-github-cli/xcode-github-cli.md b/Documentation/xcode-github-cli.md similarity index 100% rename from xcode-github-cli/xcode-github-cli.md rename to Documentation/xcode-github-cli.md diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index 22151da..f05351f 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -503,6 +503,9 @@ - (NSError*_Nullable) deleteBot { return nil; } +/* + Original + - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber @@ -608,6 +611,118 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName userInfo:@{ NSLocalizedDescriptionKey: @"Expected an Xcode bot response." }]; } +exit: + if (error) *error = localError; + return bot; +} +*/ + +- (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName + branchName:(NSString*_Nonnull)branchName + gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber + gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle + error:(NSError*__autoreleasing _Nullable*_Nullable)error { + XGXcodeBot *bot = nil; + NSError *localError = nil; + { + NSString *string = + [NSString stringWithFormat:@"https://%@:20343/api/bots", self.server.server]; + NSURL *URL = [NSURL URLWithString:string]; + if (!URL) { + localError = + [NSError errorWithDomain:NSNetServicesErrorDomain + code:NSURLErrorBadURL + userInfo:@{ + NSLocalizedDescriptionKey: + [NSString stringWithFormat:@"Bad server name '%@'.", self.server.server] + } + ]; + BNCLogError(@"Bad server name '%@'.", self.server.server); + goto exit; + } + + NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary*) + CFPropertyListCreateDeepCopy( + kCFAllocatorDefault, + (CFDictionaryRef)self.dictionary, + kCFPropertyListMutableContainers + ); + dictionary[@"configuration"] + [@"sourceControlBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] = + [NSUUID UUID].UUIDString; + dictionary[@"configuration"] + [@"sourceControlBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlBranchIdentifierKey"] = + branchName; + dictionary[@"configuration"] + [@"sourceControlBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlLocationRevisionKey"] = + nil; + dictionary[@"_id"] = nil; + dictionary[@"_rev"] = nil; + dictionary[@"tinyID"] = nil; + dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit + dictionary[@"integration_counter"] = @1; + 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]; + 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 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]) { + bot = [[XGXcodeBot alloc] initWithServer:self.server dictionary:d]; + if (bot) { + [bot startIntegration]; + goto exit; + } + } + localError = + [NSError errorWithDomain:NSNetServicesErrorDomain + code:NSURLErrorBadServerResponse + userInfo:@{ NSLocalizedDescriptionKey: @"Expected an Xcode bot response." }]; + } + exit: if (error) *error = localError; return bot; diff --git a/xcode-github-app.xcodeproj/project.pbxproj b/xcode-github-app.xcodeproj/project.pbxproj index beba32b..da32cd5 100644 --- a/xcode-github-app.xcodeproj/project.pbxproj +++ b/xcode-github-app.xcodeproj/project.pbxproj @@ -53,7 +53,8 @@ /* Begin PBXFileReference section */ 2BB6F4D221F9E3FF001E8693 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Base; path = Base.lproj/Credits.rtf; sourceTree = ""; }; - 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 = ""; }; + 2BC31A1222041978003B0EE4 /* xcode-github-cli.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-cli.md"; path = "Documentation/xcode-github-cli.md"; sourceTree = ""; }; + 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 = ""; }; 4D12BA0A20A252CE00E5B1DB /* XGALogViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XGALogViewController.h; sourceTree = ""; }; 4D12BA0B20A252CE00E5B1DB /* XGALogViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XGALogViewController.m; sourceTree = ""; }; 4D12BA0C20A252CE00E5B1DB /* XGALogViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = XGALogViewController.xib; sourceTree = ""; }; @@ -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 */, diff --git a/xcode-github-app/XGAStatusViewController.m b/xcode-github-app/XGAStatusViewController.m index 0598144..b351507 100644 --- a/xcode-github-app/XGAStatusViewController.m +++ b/xcode-github-app/XGAStatusViewController.m @@ -113,6 +113,9 @@ - (IBAction) showInfo:(id)sender { [detail append:status.statusDetail]; if (status.botName.length) [detail italicText:@"\n\nBot: %@", status.botName]; + if (status.branchOrPRName.length) + [detail italicText:@"\nRepo: %@/%@ %@", + status.bot.repoOwner, status.bot.repoName, status.bot.branch]; self.statusPopover.detailTextField.attributedStringValue = [detail renderAttributedStringWithFont:font]; @@ -231,6 +234,18 @@ - (IBAction)startStopIntegration:(id)sender { }); } +- (IBAction)showPullRequest:(id)sender { + XGAStatusViewItem*item = [self selectedTableItem]; + if (item.bot.repoOwner == nil || item.bot.repoName == nil || item.bot.pullRequestNumber == nil) + return; + + NSString*string = [NSString stringWithFormat:@"https://github.com/%@/%@/pull/%@", + item.bot.repoOwner, item.bot.repoName, item.bot.pullRequestNumber]; + NSURL*URL = [NSURL URLWithString:string]; + if (!URL) return; + [[NSWorkspace sharedWorkspace] openURL:URL]; +} + - (IBAction)reload:(id)sender { [self updateStatusNow]; } @@ -267,16 +282,24 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { } return NO; } + XGAStatusViewItem*statusItem = [self selectedTableItem]; if (menuItem.action == @selector(startStopIntegration:)) { - XGAStatusViewItem*item = [self selectedTableItem]; - if (!item) return NO; - if (item.botStatus.integrationID != nil && - ![item.botStatus.currentStep isEqualToString:@"completed"]) + if (!statusItem) return NO; + if (statusItem.botStatus.integrationID != nil && + ![statusItem.botStatus.currentStep isEqualToString:@"completed"]) menuItem.title = @"Cancel Integration"; else menuItem.title = @"Start Integration"; return YES; } + if (menuItem.action == @selector(showPullRequest:)) { + if (statusItem.bot.repoOwner == nil || + statusItem.bot.repoName == nil || + statusItem.bot.pullRequestNumber == nil) + return NO; + return YES; + } + SEL contextMenuItems[] = { @selector(showInfo:), @selector(monitorRepo:), diff --git a/xcode-github-app/XGAStatusViewController.xib b/xcode-github-app/XGAStatusViewController.xib index c74d983..9f82e98 100644 --- a/xcode-github-app/XGAStatusViewController.xib +++ b/xcode-github-app/XGAStatusViewController.xib @@ -306,6 +306,12 @@ + + + + + + diff --git a/xcode-github-cli.xcodeproj/project.pbxproj b/xcode-github-cli.xcodeproj/project.pbxproj index 76c9493..0a847ca 100644 --- a/xcode-github-cli.xcodeproj/project.pbxproj +++ b/xcode-github-cli.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 4D7335452097CF6700A0D416 /* xcode-github-cli.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-cli.md"; path = "xcode-github-cli/xcode-github-cli.md"; sourceTree = ""; }; + 4D7335452097CF6700A0D416 /* xcode-github-cli.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "xcode-github-cli.md"; path = "Documentation/xcode-github-cli.md"; sourceTree = ""; }; 4D744C032047A53B002CA796 /* xcode-github */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "xcode-github"; sourceTree = BUILT_PRODUCTS_DIR; }; 4D744C062047A53B002CA796 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 4DDAA55A216AEC95002F3F8E /* XcodeGitHub.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XcodeGitHub.framework; path = Products/XcodeGitHub.framework; sourceTree = ""; }; From 99ec0e2c1b41c34cf1674aed9ca3ce7973b95591 Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Tue, 5 Feb 2019 16:34:49 -0800 Subject: [PATCH 3/8] Fixed. --- XcodeGitHub/XGXcodeBot.m | 126 ++++----------------------------------- 1 file changed, 11 insertions(+), 115 deletions(-) diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index f05351f..af7ca8a 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -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)]; } @@ -503,9 +506,6 @@ - (NSError*_Nullable) deleteBot { return nil; } -/* - Original - - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber @@ -514,117 +514,13 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName XGXcodeBot *bot = nil; NSError *localError = nil; { - NSString *string = - [NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate", - self.server.server, - self.botID]; - NSURL *URL = [NSURL URLWithString:string]; - if (!URL) { - localError = - [NSError errorWithDomain:NSNetServicesErrorDomain - code:NSURLErrorBadURL - userInfo:@{ - NSLocalizedDescriptionKey: - [NSString stringWithFormat:@"Bad server name '%@'.", self.server.server] - } - ]; - BNCLogError(@"Bad server name '%@'.", self.server.server); - goto exit; - } - - NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary*) - CFPropertyListCreateDeepCopy( - kCFAllocatorDefault, - (CFDictionaryRef)self.dictionary, - kCFPropertyListMutableContainers - ); - dictionary[@"configuration"] - [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] = - [NSUUID UUID].UUIDString; - dictionary[@"configuration"] - [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlBranchIdentifierKey"] = - branchName; - dictionary[@"configuration"] - [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlLocationRevisionKey"] = - nil; - dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit - dictionary[@"integration_counter"] = @1; - 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]; - if (!data) { - localError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSKeyValueValidationError - userInfo:@{ NSLocalizedDescriptionKey: @"Can't create bot dictionary."}]; - } - if (localError || !data) goto exit; + /* + I tried the 'duplicate' bot command, 'https://%@:20343/api/bots/%@/duplicate', and changing + just the banch to the PR branch, but XCServer always created the duplicate with the original + template branch. - 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 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]) { - bot = [[XGXcodeBot alloc] initWithServer:self.server dictionary:d]; - if (bot) { - [bot startIntegration]; - goto exit; - } - } - localError = - [NSError errorWithDomain:NSNetServicesErrorDomain - code:NSURLErrorBadServerResponse - userInfo:@{ NSLocalizedDescriptionKey: @"Expected an Xcode bot response." }]; - } - -exit: - if (error) *error = localError; - return bot; -} -*/ - -- (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName - branchName:(NSString*_Nonnull)branchName - gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber - gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle - error:(NSError*__autoreleasing _Nullable*_Nullable)error { - XGXcodeBot *bot = nil; - NSError *localError = nil; - { + I switched to just creating a whole new bot and it seems to work fine now. + */ NSString *string = [NSString stringWithFormat:@"https://%@:20343/api/bots", self.server.server]; NSURL *URL = [NSURL URLWithString:string]; @@ -668,7 +564,7 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName dictionary[@"tinyID"] = nil; dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit dictionary[@"integration_counter"] = @1; - dictionary[@"lastRevisionBlueprint"] = @{}; + dictionary[@"lastRevisionBlueprint"] = nil; dictionary[@"name"] = newBotName; dictionary[@"templateBotName"] = self.name; dictionary[@"pullRequestNumber"] = pullRequestNumber; From 07c3dccb3454341432c71b09b512ffb30bc73f2a Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Wed, 6 Feb 2019 21:26:06 -0800 Subject: [PATCH 4/8] Fixed. --- Documentation/xcode-github-cli.md | 1 + XcodeGitHub/XGXcodeBot.m | 53 +++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Documentation/xcode-github-cli.md b/Documentation/xcode-github-cli.md index 1acde35..eec1738 100644 --- a/Documentation/xcode-github-cli.md +++ b/Documentation/xcode-github-cli.md @@ -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/) diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index af7ca8a..68cdd47 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -514,15 +514,14 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName XGXcodeBot *bot = nil; NSError *localError = nil; { - /* - I tried the 'duplicate' bot command, 'https://%@:20343/api/bots/%@/duplicate', and changing - just the banch to the PR branch, but XCServer always created the duplicate with the original - template branch. + // Simply using the 'duplicate' bot api and changing the git branch won't actually change + // the git branch to the branch of the PR. + // + // Use the duplicate api and include all the original fields plus the git branch change. - I switched to just creating a whole new bot and it seems to work fine now. - */ NSString *string = - [NSString stringWithFormat:@"https://%@:20343/api/bots", self.server.server]; + [NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate", + self.server.server, self.botID]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { localError = @@ -545,32 +544,57 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName ); dictionary[@"configuration"] [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] = - [NSUUID UUID].UUIDString; + [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] + = [NSUUID UUID].UUIDString; dictionary[@"configuration"] [@"sourceControlBlueprint"] [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlBranchIdentifierKey"] = - branchName; + [@"DVTSourceControlBranchIdentifierKey"] + = branchName; dictionary[@"configuration"] [@"sourceControlBlueprint"] [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlLocationRevisionKey"] = - nil; + [@"DVTSourceControlBranchOptionsKey"] + = @4; + dictionary[@"configuration"] + [@"sourceControlBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlLocationRevisionKey"] + = nil; dictionary[@"_id"] = nil; dictionary[@"_rev"] = nil; dictionary[@"tinyID"] = nil; dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit dictionary[@"integration_counter"] = @1; - dictionary[@"lastRevisionBlueprint"] = nil; dictionary[@"name"] = newBotName; dictionary[@"templateBotName"] = self.name; dictionary[@"pullRequestNumber"] = pullRequestNumber; dictionary[@"pullRequestTitle"] = pullRequestTitle; dictionary[@"sourceControlBlueprintIdentifier"] = [NSUUID UUID].UUIDString; + // dictionary[@"lastRevisionBlueprint"] = nil; + dictionary[@"lastRevisionBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] + = [NSUUID UUID].UUIDString; + dictionary[@"lastRevisionBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlBranchIdentifierKey"] + = branchName; + dictionary[@"lastRevisionBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlBranchOptionsKey"] + = @4; + dictionary[@"lastRevisionBlueprint"] + [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] + [self.sourceControlWorkspaceBlueprintLocationsID] + [@"DVTSourceControlLocationRevisionKey"] + = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError]; if (!data) { localError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSKeyValueValidationError @@ -589,6 +613,7 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName }]; 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) { From 58231b5070f882d2cfbb0ececbf12ac8baa07478 Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Wed, 6 Feb 2019 22:38:40 -0800 Subject: [PATCH 5/8] Updated CHANGELOG. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24be967..0b1db50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. From 05f0990ed30e0ddf9a1bf910ed1822597f97ad72 Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Mon, 11 Feb 2019 15:55:57 -0800 Subject: [PATCH 6/8] Save work. --- XcodeGitHub/XGCommand.m | 1 + XcodeGitHub/XGXcodeBot.h | 1 + XcodeGitHub/XGXcodeBot.m | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/XcodeGitHub/XGCommand.m b/XcodeGitHub/XGCommand.m index 3afeabd..0df741e 100644 --- a/XcodeGitHub/XGCommand.m +++ b/XcodeGitHub/XGCommand.m @@ -35,6 +35,7 @@ statusURL:nil]; [templateBot duplicateBotWithNewName:newBotName branchName:pr.branch + branchSHA:pr.sha gitHubPullRequestNumber:pr.number gitHubPullRequestTitle:pr.title error:&error]; diff --git a/XcodeGitHub/XGXcodeBot.h b/XcodeGitHub/XGXcodeBot.h index 8388fca..92cb125 100644 --- a/XcodeGitHub/XGXcodeBot.h +++ b/XcodeGitHub/XGXcodeBot.h @@ -135,6 +135,7 @@ NS_ASSUME_NONNULL_BEGIN - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName + branchSHA:(NSString*_Nonnull)branchSHA gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle error:(NSError*__autoreleasing _Nullable*_Nullable)error; diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index 68cdd47..ca0d5be 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -508,6 +508,7 @@ - (NSError*_Nullable) deleteBot { - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName + branchSHA:(NSString*_Nonnull)branchSHA gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle error:(NSError*__autoreleasing _Nullable*_Nullable)error { @@ -575,7 +576,6 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName dictionary[@"pullRequestTitle"] = pullRequestTitle; dictionary[@"sourceControlBlueprintIdentifier"] = [NSUUID UUID].UUIDString; - // dictionary[@"lastRevisionBlueprint"] = nil; dictionary[@"lastRevisionBlueprint"] [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] = [NSUUID UUID].UUIDString; @@ -593,7 +593,7 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] [self.sourceControlWorkspaceBlueprintLocationsID] [@"DVTSourceControlLocationRevisionKey"] - = nil; + = branchSHA; NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError]; if (!data) { From 3eb467e1211ac6fd741f7de04165685ad74aaa5f Mon Sep 17 00:00:00 2001 From: Edward Smith Date: Tue, 12 Feb 2019 10:47:22 -0800 Subject: [PATCH 7/8] Updated URL for duplicate. --- XcodeGitHub/XGXcodeBot.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index ca0d5be..dc4f6a1 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -521,7 +521,7 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName // Use the duplicate api and include all the original fields plus the git branch change. NSString *string = - [NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate", + [NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate?overwriteBlueprint=true", self.server.server, self.botID]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { From ee32c7555337e2bb70afe2253a3c7917184cfd7f Mon Sep 17 00:00:00 2001 From: "Edward B. Smith" Date: Thu, 14 Feb 2019 23:05:42 -0800 Subject: [PATCH 8/8] When a bot is duplicated the new branch is updated correctly. --- XcodeGitHub/XGCommand.m | 1 - XcodeGitHub/XGGitHubPullRequest.h | 3 +- XcodeGitHub/XGGitHubPullRequest.m | 9 +- XcodeGitHub/XGXcodeBot.h | 1 - XcodeGitHub/XGXcodeBot.m | 178 +++++++++++++++++++++--------- 5 files changed, 133 insertions(+), 59 deletions(-) diff --git a/XcodeGitHub/XGCommand.m b/XcodeGitHub/XGCommand.m index 0df741e..3afeabd 100644 --- a/XcodeGitHub/XGCommand.m +++ b/XcodeGitHub/XGCommand.m @@ -35,7 +35,6 @@ statusURL:nil]; [templateBot duplicateBotWithNewName:newBotName branchName:pr.branch - branchSHA:pr.sha gitHubPullRequestNumber:pr.number gitHubPullRequestTitle:pr.title error:&error]; diff --git a/XcodeGitHub/XGGitHubPullRequest.h b/XcodeGitHub/XGGitHubPullRequest.h index ba51b9e..a2b97d6 100644 --- a/XcodeGitHub/XGGitHubPullRequest.h +++ b/XcodeGitHub/XGGitHubPullRequest.h @@ -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; diff --git a/XcodeGitHub/XGGitHubPullRequest.m b/XcodeGitHub/XGGitHubPullRequest.m index 48d169f..46c0061 100644 --- a/XcodeGitHub/XGGitHubPullRequest.m +++ b/XcodeGitHub/XGGitHubPullRequest.m @@ -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; } @@ -231,7 +232,7 @@ + (NSString*) stringFromStatus:(XGPullRequestStatus)status { { NSString* string = [NSString stringWithFormat: @"https://api.github.com/repos/%@/%@/commits/%@/statuses", - self.repoOwner, self.repoName, self.sha]; + self.repoOwner, self.repoName, self.headSHA]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { error = @@ -305,7 +306,7 @@ - (NSError*_Nullable) setStatus:(XGPullRequestStatus)status NSError *error = nil; NSString* string = [NSString stringWithFormat: @"https://api.github.com/repos/%@/%@/statuses/%@", - self.repoOwner, self.repoName, self.sha]; + self.repoOwner, self.repoName, self.headSHA]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { error = @@ -366,7 +367,7 @@ - (NSError*_Nullable) addComment:(NSString*)comment { NSError *error = nil; NSString* string = [NSString stringWithFormat: @"https://api.github.com/repos/%@/%@/commits/%@/comments", - self.repoOwner, self.repoName, self.sha]; + self.repoOwner, self.repoName, self.headSHA]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { error = diff --git a/XcodeGitHub/XGXcodeBot.h b/XcodeGitHub/XGXcodeBot.h index 92cb125..8388fca 100644 --- a/XcodeGitHub/XGXcodeBot.h +++ b/XcodeGitHub/XGXcodeBot.h @@ -135,7 +135,6 @@ NS_ASSUME_NONNULL_BEGIN - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName - branchSHA:(NSString*_Nonnull)branchSHA gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle error:(NSError*__autoreleasing _Nullable*_Nullable)error; diff --git a/XcodeGitHub/XGXcodeBot.m b/XcodeGitHub/XGXcodeBot.m index dc4f6a1..abf3632 100644 --- a/XcodeGitHub/XGXcodeBot.m +++ b/XcodeGitHub/XGXcodeBot.m @@ -508,7 +508,6 @@ - (NSError*_Nullable) deleteBot { - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName branchName:(NSString*_Nonnull)branchName - branchSHA:(NSString*_Nonnull)branchSHA gitHubPullRequestNumber:(NSString*_Nonnull)pullRequestNumber gitHubPullRequestTitle:(NSString*_Nonnull)pullRequestTitle error:(NSError*__autoreleasing _Nullable*_Nullable)error { @@ -518,10 +517,17 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName // Simply using the 'duplicate' bot api and changing the git branch won't actually change // the git branch to the branch of the PR. // - // Use the duplicate api and include all the original fields plus the git branch change. + // 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?overwriteBlueprint=true", + [NSString stringWithFormat:@"https://%@:20343/api/bots/%@/duplicate", self.server.server, self.botID]; NSURL *URL = [NSURL URLWithString:string]; if (!URL) { @@ -536,17 +542,116 @@ - (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; + } - NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary*) + // + // Fix up the git branch of the duplicated bot: + // + + 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"] @@ -555,55 +660,25 @@ - (XGXcodeBot*_Nullable) duplicateBotWithNewName:(NSString*_Nonnull)newBotName = branchName; dictionary[@"configuration"] [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlBranchOptionsKey"] - = @4; + [@"DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey"] + = @{}; dictionary[@"configuration"] [@"sourceControlBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlLocationRevisionKey"] - = nil; - dictionary[@"_id"] = nil; - dictionary[@"_rev"] = nil; - dictionary[@"tinyID"] = nil; - dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit - dictionary[@"integration_counter"] = @1; - dictionary[@"name"] = newBotName; - dictionary[@"templateBotName"] = self.name; - dictionary[@"pullRequestNumber"] = pullRequestNumber; - dictionary[@"pullRequestTitle"] = pullRequestTitle; - dictionary[@"sourceControlBlueprintIdentifier"] = [NSUUID UUID].UUIDString; - - dictionary[@"lastRevisionBlueprint"] [@"DVTSourceControlWorkspaceBlueprintIdentifierKey"] = [NSUUID UUID].UUIDString; - dictionary[@"lastRevisionBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlBranchIdentifierKey"] - = branchName; - dictionary[@"lastRevisionBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlBranchOptionsKey"] - = @4; - dictionary[@"lastRevisionBlueprint"] - [@"DVTSourceControlWorkspaceBlueprintLocationsKey"] - [self.sourceControlWorkspaceBlueprintLocationsID] - [@"DVTSourceControlLocationRevisionKey"] - = branchSHA; + dictionary[@"configuration"][@"scheduleType"] = @2; // 2: On commit + dictionary[@"sourceControlBlueprintIdentifier"] = nil; + dictionary[@"lastRevisionBlueprint"] = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&localError]; + 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" @@ -613,24 +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 addValue:@"7" forHTTPHeaderField:@"x-xcsclientversion"]; + 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) {