├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Playgrounds └── XcodeServerSDK.playground │ ├── Contents.swift │ ├── Sources │ └── SupportCode.swift │ ├── contents.xcplayground │ └── timeline.xctimeline ├── Podfile ├── Podfile.lock ├── README.md ├── XcodeServerSDK.podspec ├── XcodeServerSDK.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── XcodeServerSDK.xccheckout └── xcshareddata │ └── xcschemes │ └── XcodeServerSDK.xcscheme ├── XcodeServerSDK.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── XcodeServerSDK.xccheckout ├── XcodeServerSDK ├── API Routes │ ├── XcodeServer+Auth.swift │ ├── XcodeServer+Bot.swift │ ├── XcodeServer+Device.swift │ ├── XcodeServer+Integration.swift │ ├── XcodeServer+LiveUpdates.swift │ ├── XcodeServer+Miscs.swift │ ├── XcodeServer+Platform.swift │ ├── XcodeServer+Repository.swift │ ├── XcodeServer+SCM.swift │ └── XcodeServer+Toolchain.swift ├── Info.plist ├── Server Entities │ ├── Bot.swift │ ├── BotConfiguration.swift │ ├── BotSchedule.swift │ ├── Commit.swift │ ├── Contributor.swift │ ├── Device.swift │ ├── DeviceSpecification.swift │ ├── EmailConfiguration.swift │ ├── File.swift │ ├── Integration.swift │ ├── IntegrationCommits.swift │ ├── IntegrationIssue.swift │ ├── IntegrationIssues.swift │ ├── LiveUpdateMessage.swift │ ├── Repository.swift │ ├── SourceControlBlueprint.swift │ ├── TestHierarchy.swift │ ├── Toolchain.swift │ ├── Trigger.swift │ └── TriggerConditions.swift ├── Server Helpers │ ├── CIServer.swift │ ├── SocketIOHelper.swift │ └── XcodeServerConstants.swift ├── XcodeServer.swift ├── XcodeServerConfig.swift ├── XcodeServerEndpoints.swift ├── XcodeServerEntity.swift ├── XcodeServerFactory.swift └── XcodeServerSDK.h ├── XcodeServerSDKTests ├── BotConfigurationTests.swift ├── BotParsingTests.swift ├── Casettes │ ├── bot_deletion.json │ ├── get_devices.json │ ├── get_integration.json │ ├── get_integration_commits.json │ ├── get_integration_issues.json │ ├── get_platforms.json │ ├── get_repositories.json │ ├── get_toolchains.json │ ├── hostname.json │ └── osx_bot.json ├── ContributorTests.swift ├── Data │ ├── create_ios_bot.json │ ├── create_osx_bot.json │ ├── create_watch_bot.json │ ├── platforms.json │ ├── scm_branches_request_no_fingerprint.json │ ├── scm_branches_request_with_fingerprint.json │ ├── scm_branches_response_error.json │ └── scm_branches_response_success.json ├── DevicesTests.swift ├── FileTests.swift ├── Info.plist ├── IntegrationTests.swift ├── IssueTests.swift ├── LiveUpdatesTests.swift ├── MiscsTests.swift ├── PlatformTests.swift ├── RepositoryTests.swift ├── TestUtils.swift ├── ToolchainTests.swift ├── XcodeServerConfigTests.swift ├── XcodeServerEndpointsTests.swift ├── XcodeServerEntityTests.swift └── XcodeServerTests.swift └── fastlane ├── Fastfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | ## Build generated 4 | build/ 5 | .AppleDouble 6 | DerivedData 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | ## Various settings 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | 31 | ### Xcode ### 32 | build/ 33 | *.pbxuser 34 | !default.pbxuser 35 | *.mode1v3 36 | !default.mode1v3 37 | *.mode2v3 38 | !default.mode2v3 39 | *.perspectivev3 40 | !default.perspectivev3 41 | xcuserdata 42 | 43 | ## Other 44 | *.moved-aside 45 | DerivedData 46 | *.xcuserstate 47 | 48 | ## Obj-C/Swift specific 49 | *.hmap 50 | *.ipa 51 | 52 | # CocoaPods 53 | # 54 | # We recommend against adding the Pods directory to your .gitignore. However 55 | # you should judge for yourself, the pros and cons are mentioned at: 56 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 57 | # 58 | Pods/ 59 | 60 | # Carthage 61 | # 62 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 63 | Carthage/Checkouts 64 | 65 | # project files should be checked into the repository, unless a significant 66 | # proportion of contributors will probably not be using SublimeText 67 | # *.sublime-project 68 | 69 | # sftp configuration file 70 | sftp-config.json 71 | 72 | 73 | ### AppCode ### 74 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 75 | 76 | *.iml 77 | 78 | ## Directory-based project format: 79 | Carthage/Build 80 | # if you remove the above rule, at least ignore the following: 81 | 82 | # User-specific stuff: 83 | # .idea/workspace.xml 84 | # .idea/tasks.xml 85 | # .idea/dictionaries 86 | 87 | # Sensitive or high-churn files: 88 | # .idea/dataSources.ids 89 | # .idea/dataSources.xml 90 | # .idea/sqlDataSources.xml 91 | # .idea/dynamic.xml 92 | # .idea/uiDesigner.xml 93 | 94 | # Gradle: 95 | # .idea/gradle.xml 96 | # .idea/libraries 97 | 98 | # Mongo Explorer plugin: 99 | # .idea/mongoSettings.xml 100 | 101 | ## File-based project format: 102 | *.ipr 103 | *.iws 104 | 105 | ## Plugin-specific files: 106 | 107 | # IntelliJ 108 | /out/ 109 | 110 | # mpeltonen/sbt-idea plugin 111 | .idea_modules/ 112 | 113 | # JIRA plugin 114 | atlassian-ide-plugin.xml 115 | 116 | # Crashlytics plugin (for Android Studio and IntelliJ) 117 | com_crashlytics_export_strings.xml 118 | crashlytics.properties 119 | crashlytics-build.properties 120 | 121 | # fastlane 122 | fastlane/report.xml 123 | fastlane/test_output/ 124 | 125 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at czechboy0@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Honza Dvorsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Playgrounds/XcodeServerSDK.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Foundation 4 | import XcodeServerSDK 5 | import XCPlayground 6 | 7 | let serverConfig = try! XcodeServerConfig(host: "https://127.0.0.1", user: "MacUser", password: "Secr3t") 8 | 9 | let server = XcodeServerFactory.server(serverConfig) 10 | 11 | server.getBots { (bots, error) -> () in 12 | 13 | print(error) 14 | print(bots) 15 | } 16 | 17 | XCPSetExecutionShouldContinueIndefinitely(true) 18 | -------------------------------------------------------------------------------- /Playgrounds/XcodeServerSDK.playground/Sources/SupportCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This file (and all other Swift source files in the Sources directory of this playground) will be precompiled into a framework which is automatically made available to TestOSX_Playground.playground. 3 | // 4 | -------------------------------------------------------------------------------- /Playgrounds/XcodeServerSDK.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/XcodeServerSDK.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | 2 | use_frameworks! 3 | 4 | def utils 5 | pod 'BuildaUtils', '~> 0.3.2' 6 | end 7 | 8 | def tests 9 | pod 'DVR', :git => "https://github.com/czechboy0/DVR.git", :tag => "v0.0.5-czechboy0" 10 | # pod 'Nimble', '~> 3.1.0' 11 | pod 'Nimble', :git => "https://github.com/Quick/Nimble.git", :commit => "b9256b0bdecc4ef1f659b7663dcd3aab6f43fb5f" 12 | end 13 | 14 | target 'XcodeServerSDK' do 15 | utils 16 | end 17 | 18 | target 'XcodeServerSDKTests' do 19 | utils 20 | tests 21 | end 22 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BuildaUtils (0.3.2): 3 | - SwiftSafe (~> 0.1) 4 | - DVR (0.0.4-czechboy0) 5 | - Nimble (3.1.0) 6 | - SwiftSafe (0.1) 7 | 8 | DEPENDENCIES: 9 | - BuildaUtils (~> 0.3.2) 10 | - DVR (from `https://github.com/czechboy0/DVR.git`, tag `v0.0.5-czechboy0`) 11 | - Nimble (from `https://github.com/Quick/Nimble.git`, commit `b9256b0bdecc4ef1f659b7663dcd3aab6f43fb5f`) 12 | 13 | EXTERNAL SOURCES: 14 | DVR: 15 | :git: https://github.com/czechboy0/DVR.git 16 | :tag: v0.0.5-czechboy0 17 | Nimble: 18 | :commit: b9256b0bdecc4ef1f659b7663dcd3aab6f43fb5f 19 | :git: https://github.com/Quick/Nimble.git 20 | 21 | CHECKOUT OPTIONS: 22 | DVR: 23 | :git: https://github.com/czechboy0/DVR.git 24 | :tag: v0.0.5-czechboy0 25 | Nimble: 26 | :commit: b9256b0bdecc4ef1f659b7663dcd3aab6f43fb5f 27 | :git: https://github.com/Quick/Nimble.git 28 | 29 | SPEC CHECKSUMS: 30 | BuildaUtils: 1c6bf3a28948c3aae242171abf2474cd4746a2d1 31 | DVR: 386f347071f55f3f9105239db6764483009ec875 32 | Nimble: eb2a9b164b9a3f16df6581c692a0bfced7d072a4 33 | SwiftSafe: 77ffd12b02678790bec1ef56a2d14ec5036f1fd6 34 | 35 | PODFILE CHECKSUM: 2d0c8056afb549d3590407decab7568949826dc2 36 | 37 | COCOAPODS: 1.0.1 38 | -------------------------------------------------------------------------------- /XcodeServerSDK.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "XcodeServerSDK" 4 | s.version = "0.7.1" 5 | s.summary = "Access Xcode Server API with native Swift objects." 6 | 7 | s.homepage = "https://github.com/czechboy0/XcodeServerSDK" 8 | s.license = { :type => "MIT", :file => "LICENSE" } 9 | 10 | s.author = { "Honza Dvorsky" => "honzadvorsky.com" } 11 | s.social_media_url = "http://twitter.com/czechboy0" 12 | 13 | s.ios.deployment_target = "8.0" 14 | s.osx.deployment_target = "10.10" 15 | s.watchos.deployment_target = "2.0" 16 | s.tvos.deployment_target = "9.0" 17 | 18 | s.source = { :git => "https://github.com/czechboy0/XcodeServerSDK.git", :tag => "v#{s.version}" } 19 | 20 | s.source_files = "XcodeServerSDK/**/*.{swift}" 21 | 22 | # load the dependencies from the podfile for target ekgclient 23 | podfile_deps = Podfile.from_file(Dir["Podfile"].first).target_definitions["XcodeServerSDK"].dependencies 24 | podfile_deps.each do |dep| 25 | s.dependency dep.name, dep.requirement.to_s 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /XcodeServerSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XcodeServerSDK.xcodeproj/project.xcworkspace/xcshareddata/XcodeServerSDK.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 1D9650B0-8D54-46C2-BCB1-C46BB6D63901 9 | IDESourceControlProjectName 10 | XcodeServerSDK 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 14 | github.com:czechboy0/XcodeServerSDK.git 15 | 16 | IDESourceControlProjectPath 17 | XcodeServerSDK.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:czechboy0/XcodeServerSDK.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 36 | IDESourceControlWCCName 37 | XcodeServerSDK 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /XcodeServerSDK.xcodeproj/xcshareddata/xcschemes/XcodeServerSDK.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /XcodeServerSDK.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /XcodeServerSDK.xcworkspace/xcshareddata/XcodeServerSDK.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 26859E05-0D4F-4F4D-BD1E-10E0E64B6B5B 9 | IDESourceControlProjectName 10 | XcodeServerSDK 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 14 | github.com:czechboy0/XcodeServerSDK.git 15 | 16 | IDESourceControlProjectPath 17 | XcodeServerSDK.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 21 | .. 22 | 23 | IDESourceControlProjectURL 24 | github.com:czechboy0/XcodeServerSDK.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | A36AEFA3F9FF1F738E92F0C497C14977DCE02B97 36 | IDESourceControlWCCName 37 | XcodeServerSDK 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Auth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Auth.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 30.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - XcodeSever API Routes for Authorization 13 | extension XcodeServer { 14 | 15 | // MARK: Sign in/Sign out 16 | 17 | /** 18 | XCS API call for user sign in. 19 | 20 | - parameter success: Indicates whether sign in was successful. 21 | - parameter error: Error indicating failure of sign in. 22 | */ 23 | public final func login(completion: (success: Bool, error: NSError?) -> ()) { 24 | 25 | self.sendRequestWithMethod(.POST, endpoint: .Login, params: nil, query: nil, body: nil) { (response, body, error) -> () in 26 | 27 | if error != nil { 28 | completion(success: false, error: error) 29 | return 30 | } 31 | 32 | if let response = response { 33 | if response.statusCode == 204 { 34 | completion(success: true, error: nil) 35 | } else { 36 | completion(success: false, error: Error.withInfo("Wrong status code: \(response.statusCode)")) 37 | } 38 | return 39 | } 40 | completion(success: false, error: Error.withInfo("Nil response")) 41 | } 42 | } 43 | 44 | /** 45 | XCS API call for user sign out. 46 | 47 | - parameter success: Indicates whether sign out was successful. 48 | - parameter error: Error indicating failure of sign out. 49 | */ 50 | public final func logout(completion: (success: Bool, error: NSError?) -> ()) { 51 | 52 | self.sendRequestWithMethod(.POST, endpoint: .Logout, params: nil, query: nil, body: nil) { (response, body, error) -> () in 53 | 54 | if error != nil { 55 | completion(success: false, error: error) 56 | return 57 | } 58 | 59 | if let response = response { 60 | if response.statusCode == 204 { 61 | completion(success: true, error: nil) 62 | } else { 63 | completion(success: false, error: Error.withInfo("Wrong status code: \(response.statusCode)")) 64 | } 65 | return 66 | } 67 | completion(success: false, error: Error.withInfo("Nil response")) 68 | } 69 | } 70 | 71 | // MARK: User access verification 72 | 73 | /** 74 | XCS API call to verify if logged in user can create bots. 75 | 76 | - parameter canCreateBots: Indicator of bot creation accessibility. 77 | - parameter error: Optional error. 78 | */ 79 | public final func getUserCanCreateBots(completion: (canCreateBots: Bool, error: NSError?) -> ()) { 80 | 81 | self.sendRequestWithMethod(.GET, endpoint: .UserCanCreateBots, params: nil, query: nil, body: nil) { (response, body, error) -> () in 82 | 83 | if let error = error { 84 | completion(canCreateBots: false, error: error) 85 | return 86 | } 87 | 88 | if let body = body as? NSDictionary { 89 | if let canCreateBots = body["result"] as? Bool where canCreateBots == true { 90 | completion(canCreateBots: true, error: nil) 91 | } else { 92 | completion(canCreateBots: false, error: Error.withInfo("Specified user cannot create bots")) 93 | } 94 | } else { 95 | completion(canCreateBots: false, error: Error.withInfo("Wrong body \(body)")) 96 | } 97 | } 98 | } 99 | 100 | /** 101 | Checks whether the current user has the rights to create bots and perform other similar "write" actions. 102 | Xcode Server offers two tiers of users, ones for reading only ("viewers") and others for management. 103 | Here we check the current user can manage XCS, which is useful for projects like Buildasaur. 104 | 105 | - parameter success: Indicates if user can create bots. 106 | - parameter error: Error if something went wrong. 107 | */ 108 | public final func verifyXCSUserCanCreateBots(completion: (success: Bool, error: NSError?) -> ()) { 109 | 110 | //the way we check availability is first by logging out (does nothing if not logged in) and then 111 | //calling getUserCanCreateBots, which, if necessary, automatically authenticates with Basic auth before resolving to true or false in JSON. 112 | 113 | self.logout { (success, error) -> () in 114 | 115 | if let error = error { 116 | completion(success: false, error: error) 117 | return 118 | } 119 | 120 | self.getUserCanCreateBots { (canCreateBots, error) -> () in 121 | 122 | if let error = error { 123 | completion(success: false, error: error) 124 | return 125 | } 126 | 127 | completion(success: canCreateBots, error: nil) 128 | } 129 | } 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Device.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Device.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 01/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - XcodeSever API Routes for Devices management 13 | extension XcodeServer { 14 | 15 | /** 16 | XCS API call for retrieving all registered devices on OS X Server. 17 | 18 | - parameter devices: Optional array of available devices. 19 | - parameter error: Optional error indicating that something went wrong. 20 | */ 21 | public final func getDevices(completion: (devices: [Device]?, error: NSError?) -> ()) { 22 | 23 | self.sendRequestWithMethod(.GET, endpoint: .Devices, params: nil, query: nil, body: nil) { (response, body, error) -> () in 24 | 25 | if error != nil { 26 | completion(devices: nil, error: error) 27 | return 28 | } 29 | 30 | if let array = (body as? NSDictionary)?["results"] as? NSArray { 31 | let (result, error): ([Device]?, NSError?) = unthrow { 32 | return try XcodeServerArray(array) 33 | } 34 | completion(devices: result, error: error) 35 | } else { 36 | completion(devices: nil, error: Error.withInfo("Wrong body \(body)")) 37 | } 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+LiveUpdates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+LiveUpdates.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 25/09/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - XcodeSever API Routes for Live Updates 13 | extension XcodeServer { 14 | 15 | public typealias MessageHandler = (messages: [LiveUpdateMessage]) -> () 16 | public typealias StopHandler = () -> () 17 | public typealias ErrorHandler = (error: ErrorType) -> () 18 | 19 | private class LiveUpdateState { 20 | var task: NSURLSessionTask? 21 | var messageHandler: MessageHandler? 22 | var errorHandler: ErrorHandler? 23 | var pollId: String? 24 | var terminated: Bool = false 25 | 26 | func cancel() { 27 | self.task?.cancel() 28 | self.task = nil 29 | self.terminated = true 30 | } 31 | 32 | func error(error: ErrorType) { 33 | self.cancel() 34 | self.errorHandler?(error: error) 35 | } 36 | 37 | deinit { 38 | self.cancel() 39 | } 40 | } 41 | 42 | /** 43 | * Returns StopHandler - call it when you want to stop receiving updates. 44 | */ 45 | public func startListeningForLiveUpdates(message: MessageHandler, error: ErrorHandler? = nil) -> StopHandler { 46 | 47 | let state = LiveUpdateState() 48 | state.errorHandler = error 49 | state.messageHandler = message 50 | self.startPolling(state) 51 | return { 52 | state.cancel() 53 | } 54 | } 55 | 56 | private func queryWithTimestamp() -> [String: String] { 57 | let timestamp = Int(NSDate().timeIntervalSince1970)*1000 58 | return [ 59 | "t": "\(timestamp)" 60 | ] 61 | } 62 | 63 | private func sendRequest(state: LiveUpdateState, params: [String: String]?, completion: (message: String) -> ()) { 64 | 65 | let query = queryWithTimestamp() 66 | let task = self.sendRequestWithMethod(.GET, endpoint: .LiveUpdates, params: params, query: query, body: nil, portOverride: 443) { 67 | (response, body, error) -> () in 68 | 69 | if let error = error { 70 | state.error(error) 71 | return 72 | } 73 | 74 | guard let message = body as? String else { 75 | let e = Error.withInfo("Wrong body: \(body)") 76 | state.error(e) 77 | return 78 | } 79 | 80 | completion(message: message) 81 | } 82 | state.task = task 83 | } 84 | 85 | private func startPolling(state: LiveUpdateState) { 86 | 87 | self.sendRequest(state, params: nil) { [weak self] (message) -> () in 88 | self?.processInitialResponse(message, state: state) 89 | } 90 | } 91 | 92 | private func processInitialResponse(initial: String, state: LiveUpdateState) { 93 | if let pollId = initial.componentsSeparatedByString(":").first { 94 | state.pollId = pollId 95 | self.poll(state) 96 | } else { 97 | state.error(Error.withInfo("Unexpected initial poll message: \(initial)")) 98 | } 99 | } 100 | 101 | private func poll(state: LiveUpdateState) { 102 | precondition(state.pollId != nil) 103 | let params = [ 104 | "poll_id": state.pollId! 105 | ] 106 | 107 | self.sendRequest(state, params: params) { [weak self] (message) -> () in 108 | 109 | let packets = SocketIOHelper.parsePackets(message) 110 | 111 | do { 112 | try self?.handlePackets(packets, state: state) 113 | } catch { 114 | state.error(error) 115 | } 116 | } 117 | } 118 | 119 | private func handlePackets(packets: [SocketIOPacket], state: LiveUpdateState) throws { 120 | 121 | //check for errors 122 | if let lastPacket = packets.last where lastPacket.type == .Error { 123 | let (_, advice) = lastPacket.parseError() 124 | if 125 | let advice = advice, 126 | case .Reconnect = advice { 127 | //reconnect! 128 | self.startPolling(state) 129 | return 130 | } 131 | print("Unrecognized socket.io error: \(lastPacket.stringPayload)") 132 | } 133 | 134 | //we good? 135 | let events = packets.filter { $0.type == .Event } 136 | let validEvents = events.filter { $0.jsonPayload != nil } 137 | let messages = try validEvents.map { try LiveUpdateMessage(json: $0.jsonPayload!) } 138 | if messages.count > 0 { 139 | state.messageHandler?(messages: messages) 140 | } 141 | if !state.terminated { 142 | self.poll(state) 143 | } 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Miscs.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Miscs.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 10/10/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import BuildaUtils 12 | 13 | // MARK: - Miscellaneous XcodeSever API Routes 14 | extension XcodeServer { 15 | 16 | /** 17 | XCS API call for retrieving its canonical hostname. 18 | */ 19 | public final func getHostname(completion: (hostname: String?, error: NSError?) -> ()) { 20 | 21 | self.sendRequestWithMethod(.GET, endpoint: .Hostname, params: nil, query: nil, body: nil) { (response, body, error) -> () in 22 | 23 | if error != nil { 24 | completion(hostname: nil, error: error) 25 | return 26 | } 27 | 28 | if let hostname = (body as? NSDictionary)?["hostname"] as? String { 29 | completion(hostname: hostname, error: nil) 30 | } else { 31 | completion(hostname: nil, error: Error.withInfo("Wrong body \(body)")) 32 | } 33 | } 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Platform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Platform.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 01/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - XcodeSever API Routes for Platform management 13 | extension XcodeServer { 14 | 15 | /** 16 | XCS API method for getting available testing platforms on OS X Server. 17 | 18 | - parameter platforms: Optional array of platforms. 19 | - parameter error: Optional error indicating some problems. 20 | */ 21 | public final func getPlatforms(completion: (platforms: [DevicePlatform]?, error: NSError?) -> ()) { 22 | 23 | self.sendRequestWithMethod(.GET, endpoint: .Platforms, params: nil, query: nil, body: nil) { (response, body, error) -> () in 24 | 25 | if error != nil { 26 | completion(platforms: nil, error: error) 27 | return 28 | } 29 | 30 | if let array = (body as? NSDictionary)?["results"] as? NSArray { 31 | let (result, error): ([DevicePlatform]?, NSError?) = unthrow { 32 | return try XcodeServerArray(array) 33 | } 34 | completion(platforms: result, error: error) 35 | } else { 36 | completion(platforms: nil, error: Error.withInfo("Wrong body \(body)")) 37 | } 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Repository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Repository.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 30.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - // MARK: - XcodeSever API Routes for Repositories management 13 | extension XcodeServer { 14 | 15 | /** 16 | XCS API call for getting all repositories stored on Xcode Server. 17 | 18 | - parameter repositories: Optional array of repositories. 19 | - parameter error: Optional error 20 | */ 21 | public final func getRepositories(completion: (repositories: [Repository]?, error: NSError?) -> ()) { 22 | 23 | self.sendRequestWithMethod(.GET, endpoint: .Repositories, params: nil, query: nil, body: nil) { (response, body, error) -> () in 24 | guard error == nil else { 25 | completion(repositories: nil, error: error) 26 | return 27 | } 28 | 29 | guard let repositoriesBody = (body as? NSDictionary)?["results"] as? NSArray else { 30 | completion(repositories: nil, error: Error.withInfo("Wrong body \(body)")) 31 | return 32 | } 33 | 34 | let (result, error): ([Repository]?, NSError?) = unthrow { 35 | return try XcodeServerArray(repositoriesBody) 36 | } 37 | completion(repositories: result, error: error) 38 | } 39 | } 40 | 41 | /** 42 | Enum with response from creation of repository. 43 | 44 | - RepositoryAlreadyExists: Repository with this name already exists on OS X Server. 45 | - NilResponse: Self explanatory. 46 | - CorruptedJSON: JSON you've used to create repository. 47 | - WrongStatusCode: Something wrong with HHTP status. 48 | - Error: There was an error during netwotk activity. 49 | - Success: Repository was successfully created 🎉 50 | */ 51 | public enum CreateRepositoryResponse { 52 | case RepositoryAlreadyExists 53 | case NilResponse 54 | case CorruptedJSON 55 | case WrongStatusCode(Int) 56 | case Error(ErrorType) 57 | case Success(Repository) 58 | } 59 | 60 | /** 61 | XCS API call for creating new repository on configured Xcode Server. 62 | 63 | - parameter repository: Repository object. 64 | - parameter repository: Optional object of created repository. 65 | - parameter error: Optional error. 66 | */ 67 | public final func createRepository(repository: Repository, completion: (response: CreateRepositoryResponse) -> ()) { 68 | let body = repository.dictionarify() 69 | 70 | self.sendRequestWithMethod(.POST, endpoint: .Repositories, params: nil, query: nil, body: body) { (response, body, error) -> () in 71 | if let error = error { 72 | completion(response: XcodeServer.CreateRepositoryResponse.Error(error)) 73 | return 74 | } 75 | 76 | guard let response = response else { 77 | completion(response: XcodeServer.CreateRepositoryResponse.NilResponse) 78 | return 79 | } 80 | 81 | guard let repositoryBody = body as? NSDictionary where response.statusCode == 204 else { 82 | switch response.statusCode { 83 | case 200: 84 | completion(response: XcodeServer.CreateRepositoryResponse.CorruptedJSON) 85 | case 409: 86 | completion(response: XcodeServer.CreateRepositoryResponse.RepositoryAlreadyExists) 87 | default: 88 | completion(response: XcodeServer.CreateRepositoryResponse.WrongStatusCode(response.statusCode)) 89 | } 90 | 91 | return 92 | } 93 | 94 | let (result, error): (Repository?, NSError?) = unthrow { 95 | return try Repository(json: repositoryBody) 96 | } 97 | if let error = error { 98 | completion(response: .Error(error)) 99 | } else { 100 | completion(response: .Success(result!)) 101 | } 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+SCM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+SCM.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 01.07.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - XcodeSever API Routes for Source Control Management 13 | extension XcodeServer { 14 | 15 | /** 16 | Verifies that the blueprint contains valid Git credentials and that the blueprint contains a valid 17 | server certificate fingerprint for client <-> XCS communication. 18 | 19 | - parameter blueprint: SC blueprint which should be verified. 20 | - parameter response: SCM response. 21 | */ 22 | public final func verifyGitCredentialsFromBlueprint(blueprint: SourceControlBlueprint, completion: (response: SCMBranchesResponse) -> ()) { 23 | 24 | //just a proxy with a more explicit name 25 | self.postSCMBranchesWithBlueprint(blueprint, completion: completion) 26 | } 27 | 28 | // MARK: Helpers 29 | 30 | /** 31 | Enum for Source Control Management responses. 32 | 33 | - Error: Error. 34 | - SSHFingerprintFailedToVerify: Couldn't verify SSH fingerprint. 35 | - Success: Verification was successful. 36 | */ 37 | public enum SCMBranchesResponse { 38 | case Error(ErrorType) 39 | case SSHFingerprintFailedToVerify(fingerprint: String, repository: String) 40 | 41 | //the valid blueprint will have the right certificateFingerprint 42 | case Success(branches: [(name: String, isPrimary: Bool)], validBlueprint: SourceControlBlueprint) 43 | } 44 | 45 | final func postSCMBranchesWithBlueprint(blueprint: SourceControlBlueprint, completion: (response: SCMBranchesResponse) -> ()) { 46 | 47 | let blueprintDict = blueprint.dictionarifyRemoteAndCredentials() 48 | 49 | self.sendRequestWithMethod(.POST, endpoint: .SCM_Branches, params: nil, query: nil, body: blueprintDict) { (response, body, error) -> () in 50 | 51 | if let error = error { 52 | completion(response: XcodeServer.SCMBranchesResponse.Error(error)) 53 | return 54 | } 55 | 56 | guard let responseObject = body as? NSDictionary else { 57 | let e = Error.withInfo("Wrong body: \(body)") 58 | completion(response: XcodeServer.SCMBranchesResponse.Error(e)) 59 | return 60 | } 61 | 62 | //take the primary repository's key. XCS officially supports multiple checkouts (submodules) 63 | let primaryRepoId = blueprint.projectWCCIdentifier 64 | 65 | //communication worked, now let's see what we got 66 | //check for errors first 67 | if 68 | let repoErrors = responseObject["repositoryErrors"] as? [NSDictionary], 69 | let repoErrorWrap = repoErrors.findFirst({ $0["repository"] as? String == primaryRepoId }), 70 | let repoError = repoErrorWrap["error"] as? NSDictionary 71 | where repoErrors.count > 0 { 72 | 73 | if let code = repoError["code"] as? Int { 74 | 75 | //ok, we got an error. do we recognize it? 76 | switch code { 77 | case -1004: 78 | //ok, this is failed fingerprint validation 79 | //pull out the new fingerprint and complete. 80 | if let fingerprint = repoError["fingerprint"] as? String { 81 | 82 | //optionally offer to resolve this issue by adopting the new fingerprint 83 | if self.config.automaticallyTrustSelfSignedCertificates { 84 | 85 | blueprint.certificateFingerprint = fingerprint 86 | self.postSCMBranchesWithBlueprint(blueprint, completion: completion) 87 | return 88 | 89 | } else { 90 | completion(response: XcodeServer.SCMBranchesResponse.SSHFingerprintFailedToVerify(fingerprint: fingerprint, repository: primaryRepoId)) 91 | } 92 | 93 | } else { 94 | completion(response: XcodeServer.SCMBranchesResponse.Error(Error.withInfo("No fingerprint provided in error \(repoError)"))) 95 | } 96 | 97 | default: 98 | completion(response: XcodeServer.SCMBranchesResponse.Error(Error.withInfo("Unrecognized error: \(repoError)"))) 99 | } 100 | } else { 101 | completion(response: XcodeServer.SCMBranchesResponse.Error(Error.withInfo("No code provided in error \(repoError)"))) 102 | } 103 | return 104 | } 105 | 106 | //cool, no errors. now try to parse branches! 107 | guard 108 | let branchesAllRepos = responseObject["branches"] as? NSDictionary, 109 | let branches = branchesAllRepos[primaryRepoId] as? NSArray else { 110 | 111 | completion(response: XcodeServer.SCMBranchesResponse.Error(Error.withInfo("No branches provided for our primary repo id: \(primaryRepoId)."))) 112 | return 113 | } 114 | 115 | //cool, we gots ourselves some branches, let's parse 'em 116 | let parsedBranches = branches.map({ (name: $0["name"] as! String, isPrimary: $0["primary"] as! Bool) }) 117 | completion(response: XcodeServer.SCMBranchesResponse.Success(branches: parsedBranches, validBlueprint: blueprint)) 118 | } 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /XcodeServerSDK/API Routes/XcodeServer+Toolchain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer+Toolchain.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Laurent Gaches on 21/04/16. 6 | // Copyright © 2016 Laurent Gaches. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: - Toolchain XcodeSever API Routes 13 | extension XcodeServer { 14 | 15 | /** 16 | XCS API call for getting all available toolchains. 17 | 18 | - parameter toolchains: Optional array of available toolchains. 19 | - parameter error: Optional error. 20 | */ 21 | public final func getToolchains(completion: (toolchains: [Toolchain]?,error: NSError?) -> ()) { 22 | self.sendRequestWithMethod(.GET, endpoint: .Toolchains, params: nil, query: nil, body: nil) { (response, body, error) in 23 | if error != nil { 24 | completion(toolchains: nil, error: error) 25 | return 26 | } 27 | 28 | if let body = (body as? NSDictionary)?["results"] as? NSArray { 29 | let (result, error): ([Toolchain]?, NSError?) = unthrow { _ in 30 | return try XcodeServerArray(body) 31 | } 32 | completion(toolchains: result, error: error) 33 | } else { 34 | completion(toolchains: nil, error: Error.withInfo("Wrong body \(body)")) 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Honza Dvorsky. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Bot.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bot.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Bot : XcodeServerEntity { 12 | 13 | public let name: String 14 | public let configuration: BotConfiguration 15 | public let integrationsCount: Int 16 | 17 | public required init(json: NSDictionary) throws { 18 | 19 | self.name = try json.stringForKey("name") 20 | self.configuration = try BotConfiguration(json: try json.dictionaryForKey("configuration")) 21 | self.integrationsCount = json.optionalIntForKey("integration_counter") ?? 0 22 | 23 | try super.init(json: json) 24 | } 25 | 26 | /** 27 | * Creating bots on the server. Needs dictionary representation. 28 | */ 29 | public init(name: String, configuration: BotConfiguration) { 30 | 31 | self.name = name 32 | self.configuration = configuration 33 | self.integrationsCount = 0 34 | 35 | super.init() 36 | } 37 | 38 | public override func dictionarify() -> NSDictionary { 39 | 40 | let dictionary = NSMutableDictionary() 41 | 42 | //name 43 | dictionary["name"] = self.name 44 | 45 | //configuration 46 | dictionary["configuration"] = self.configuration.dictionarify() 47 | 48 | //others 49 | dictionary["type"] = 1 //magic more 50 | dictionary["requiresUpgrade"] = false 51 | dictionary["group"] = [ 52 | "name": NSUUID().UUIDString 53 | ] 54 | 55 | return dictionary 56 | } 57 | 58 | 59 | } 60 | 61 | extension Bot : CustomStringConvertible { 62 | public var description : String { 63 | get { 64 | return "[Bot \(self.name)]" 65 | } 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/BotSchedule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BotSchedule.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class BotSchedule : XcodeServerEntity { 12 | 13 | public enum Schedule : Int { 14 | 15 | case Periodical = 1 16 | case Commit 17 | case Manual 18 | 19 | public func toString() -> String { 20 | switch self { 21 | case .Periodical: 22 | return "Periodical" 23 | case .Commit: 24 | return "On Commit" 25 | case .Manual: 26 | return "Manual" 27 | } 28 | } 29 | } 30 | 31 | public enum Period : Int { 32 | case Hourly = 1 33 | case Daily 34 | case Weekly 35 | } 36 | 37 | public enum Day : Int { 38 | case Monday = 1 39 | case Tuesday 40 | case Wednesday 41 | case Thursday 42 | case Friday 43 | case Saturday 44 | case Sunday 45 | } 46 | 47 | public let schedule: Schedule! 48 | 49 | public let period: Period? 50 | 51 | public let day: Day! 52 | public let hours: Int! 53 | public let minutes: Int! 54 | 55 | public required init(json: NSDictionary) throws { 56 | 57 | let schedule = Schedule(rawValue: try json.intForKey("scheduleType"))! 58 | self.schedule = schedule 59 | 60 | if schedule == .Periodical { 61 | 62 | let period = Period(rawValue: try json.intForKey("periodicScheduleInterval"))! 63 | self.period = period 64 | 65 | let minutes = json.optionalIntForKey("minutesAfterHourToIntegrate") 66 | let hours = json.optionalIntForKey("hourOfIntegration") 67 | 68 | switch period { 69 | case .Hourly: 70 | self.minutes = minutes! 71 | self.hours = nil 72 | self.day = nil 73 | case .Daily: 74 | self.minutes = minutes! 75 | self.hours = hours! 76 | self.day = nil 77 | case .Weekly: 78 | self.minutes = minutes! 79 | self.hours = hours! 80 | self.day = Day(rawValue: try json.intForKey("weeklyScheduleDay")) 81 | } 82 | } else { 83 | self.period = nil 84 | self.minutes = nil 85 | self.hours = nil 86 | self.day = nil 87 | } 88 | 89 | try super.init(json: json) 90 | } 91 | 92 | private init(schedule: Schedule, period: Period?, day: Day?, hours: Int?, minutes: Int?) { 93 | 94 | self.schedule = schedule 95 | self.period = period 96 | self.day = day 97 | self.hours = hours 98 | self.minutes = minutes 99 | 100 | super.init() 101 | } 102 | 103 | public class func manualBotSchedule() -> BotSchedule { 104 | return BotSchedule(schedule: .Manual, period: nil, day: nil, hours: nil, minutes: nil) 105 | } 106 | 107 | public class func commitBotSchedule() -> BotSchedule { 108 | return BotSchedule(schedule: .Commit, period: nil, day: nil, hours: nil, minutes: nil) 109 | } 110 | 111 | public override func dictionarify() -> NSDictionary { 112 | 113 | let dictionary = NSMutableDictionary() 114 | 115 | dictionary["scheduleType"] = self.schedule.rawValue 116 | dictionary["periodicScheduleInterval"] = self.period?.rawValue ?? 0 117 | dictionary["weeklyScheduleDay"] = self.day?.rawValue ?? 0 118 | dictionary["hourOfIntegration"] = self.hours ?? 0 119 | dictionary["minutesAfterHourToIntegrate"] = self.minutes ?? 0 120 | 121 | return dictionary 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Commit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Commit.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 21/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Commit: XcodeServerEntity { 12 | 13 | public let hash: String 14 | public let filePaths: [File] 15 | public let message: String? 16 | public let date: NSDate 17 | public let repositoryID: String 18 | public let contributor: Contributor 19 | 20 | // MARK: Initializers 21 | public required init(json: NSDictionary) throws { 22 | self.hash = try json.stringForKey("XCSCommitHash") 23 | self.filePaths = try json.arrayForKey("XCSCommitCommitChangeFilePaths").map { try File(json: $0) } 24 | self.message = json.optionalStringForKey("XCSCommitMessage") 25 | self.date = try json.dateForKey("XCSCommitTimestamp") 26 | self.repositoryID = try json.stringForKey("XCSBlueprintRepositoryID") 27 | self.contributor = try Contributor(json: try json.dictionaryForKey("XCSCommitContributor")) 28 | 29 | try super.init(json: json) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Contributor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Contributor.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 21/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: Constants 12 | let kContributorName = "XCSContributorName" 13 | let kContributorDisplayName = "XCSContributorDisplayName" 14 | let kContributorEmails = "XCSContributorEmails" 15 | 16 | public class Contributor: XcodeServerEntity { 17 | 18 | public let name: String 19 | public let displayName: String 20 | public let emails: [String] 21 | 22 | public required init(json: NSDictionary) throws { 23 | self.name = try json.stringForKey(kContributorName) 24 | self.displayName = try json.stringForKey(kContributorDisplayName) 25 | self.emails = try json.arrayForKey(kContributorEmails) 26 | 27 | try super.init(json: json) 28 | } 29 | 30 | public override func dictionarify() -> NSDictionary { 31 | return [ 32 | kContributorName: self.name, 33 | kContributorDisplayName: self.displayName, 34 | kContributorEmails: self.emails 35 | ] 36 | } 37 | 38 | public func description() -> String { 39 | return "\(displayName) [\(emails[0])]" 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Device.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Device.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 15/03/2015. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public class Device : XcodeServerEntity { 13 | 14 | public let osVersion: String 15 | public let connected: Bool 16 | public let simulator: Bool 17 | public let modelCode: String? // Enum? 18 | public let deviceType: String? // Enum? 19 | public let modelName: String? 20 | public let deviceECID: String? 21 | public let modelUTI: String? 22 | public let activeProxiedDevice: Device? 23 | public let trusted: Bool 24 | public let name: String 25 | public let supported: Bool 26 | public let processor: String? 27 | public let identifier: String 28 | public let enabledForDevelopment: Bool 29 | public let serialNumber: String? 30 | public let platform: DevicePlatform.PlatformType 31 | public let architecture: String // Enum? 32 | public let isServer: Bool 33 | public let retina: Bool 34 | 35 | public required init(json: NSDictionary) throws { 36 | 37 | self.connected = try json.boolForKey("connected") 38 | self.osVersion = try json.stringForKey("osVersion") 39 | self.simulator = try json.boolForKey("simulator") 40 | self.modelCode = json.optionalStringForKey("modelCode") 41 | self.deviceType = json.optionalStringForKey("deviceType") 42 | self.modelName = json.optionalStringForKey("modelName") 43 | self.deviceECID = json.optionalStringForKey("deviceECID") 44 | self.modelUTI = json.optionalStringForKey("modelUTI") 45 | if let proxyDevice = json.optionalDictionaryForKey("activeProxiedDevice") { 46 | self.activeProxiedDevice = try Device(json: proxyDevice) 47 | } else { 48 | self.activeProxiedDevice = nil 49 | } 50 | self.trusted = json.optionalBoolForKey("trusted") ?? false 51 | self.name = try json.stringForKey("name") 52 | self.supported = try json.boolForKey("supported") 53 | self.processor = json.optionalStringForKey("processor") 54 | self.identifier = try json.stringForKey("identifier") 55 | self.enabledForDevelopment = try json.boolForKey("enabledForDevelopment") 56 | self.serialNumber = json.optionalStringForKey("serialNumber") 57 | self.platform = DevicePlatform.PlatformType(rawValue: try json.stringForKey("platformIdentifier")) ?? .Unknown 58 | self.architecture = try json.stringForKey("architecture") 59 | 60 | //for some reason which is not yet clear to me (probably old/new XcS versions), sometimes 61 | //the key is "server" and sometimes "isServer". this just picks up the present one. 62 | self.isServer = json.optionalBoolForKey("server") ?? json.optionalBoolForKey("isServer") ?? false 63 | self.retina = try json.boolForKey("retina") 64 | 65 | try super.init(json: json) 66 | } 67 | 68 | public override func dictionarify() -> NSDictionary { 69 | 70 | return [ 71 | "device_id": self.id 72 | ] 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/EmailConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmailConfiguration.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class EmailConfiguration : XcodeServerEntity { 12 | 13 | public let additionalRecipients: [String] 14 | public let emailCommitters: Bool 15 | public let includeCommitMessages: Bool 16 | public let includeIssueDetails: Bool 17 | 18 | public init(additionalRecipients: [String], emailCommitters: Bool, includeCommitMessages: Bool, includeIssueDetails: Bool) { 19 | 20 | self.additionalRecipients = additionalRecipients 21 | self.emailCommitters = emailCommitters 22 | self.includeCommitMessages = includeCommitMessages 23 | self.includeIssueDetails = includeIssueDetails 24 | 25 | super.init() 26 | } 27 | 28 | public override func dictionarify() -> NSDictionary { 29 | 30 | let dict = NSMutableDictionary() 31 | 32 | dict["emailCommitters"] = self.emailCommitters 33 | dict["includeCommitMessages"] = self.includeCommitMessages 34 | dict["includeIssueDetails"] = self.includeIssueDetails 35 | dict["additionalRecipients"] = self.additionalRecipients 36 | 37 | return dict 38 | } 39 | 40 | public required init(json: NSDictionary) throws { 41 | 42 | self.emailCommitters = try json.boolForKey("emailCommitters") 43 | self.includeCommitMessages = try json.boolForKey("includeCommitMessages") 44 | self.includeIssueDetails = try json.boolForKey("includeIssueDetails") 45 | self.additionalRecipients = try json.arrayForKey("additionalRecipients") 46 | 47 | try super.init(json: json) 48 | } 49 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 21/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class File: XcodeServerEntity { 12 | 13 | public let status: FileStatus 14 | public let filePath: String 15 | 16 | public init(filePath: String, status: FileStatus) { 17 | self.filePath = filePath 18 | self.status = status 19 | 20 | super.init() 21 | } 22 | 23 | public required init(json: NSDictionary) throws { 24 | self.filePath = try json.stringForKey("filePath") 25 | self.status = FileStatus(rawValue: try json.intForKey("status")) ?? .Other 26 | 27 | try super.init(json: json) 28 | } 29 | 30 | public override func dictionarify() -> NSDictionary { 31 | return [ 32 | "status": self.status.rawValue, 33 | "filePath": self.filePath 34 | ] 35 | } 36 | 37 | } 38 | 39 | /** 40 | * Enum which describes file statuses. 41 | */ 42 | public enum FileStatus: Int { 43 | case Added = 1 44 | case Deleted = 2 45 | case Modified = 4 46 | case Moved = 8192 47 | case Other 48 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Integration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Integration.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 15/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Integration : XcodeServerEntity { 12 | 13 | //usually available during the whole integration's lifecycle 14 | public let queuedDate: NSDate 15 | public let shouldClean: Bool 16 | public let currentStep: Step! 17 | public let number: Int 18 | 19 | //usually available only after the integration has finished 20 | public let successStreak: Int? 21 | public let startedDate: NSDate? 22 | public let endedTime: NSDate? 23 | public let duration: NSTimeInterval? 24 | public let result: Result? 25 | public let buildResultSummary: BuildResultSummary? 26 | public let testedDevices: [Device]? 27 | public let testHierarchy: TestHierarchy? 28 | public let assets: NSDictionary? //TODO: add typed array with parsing 29 | public let blueprint: SourceControlBlueprint? 30 | 31 | //new keys 32 | public let expectedCompletionDate: NSDate? 33 | 34 | public enum Step : String { 35 | case Unknown = "" 36 | case Pending = "pending" 37 | case Preparing = "preparing" 38 | case Checkout = "checkout" 39 | case BeforeTriggers = "before-triggers" 40 | case Building = "building" 41 | case Testing = "testing" 42 | case Archiving = "archiving" 43 | case Processing = "processing" 44 | case AfterTriggers = "after-triggers" 45 | case Uploading = "uploading" 46 | case Completed = "completed" 47 | } 48 | 49 | public enum Result : String { 50 | case Unknown = "unknown" 51 | case Succeeded = "succeeded" 52 | case BuildErrors = "build-errors" 53 | case TestFailures = "test-failures" 54 | case Warnings = "warnings" 55 | case AnalyzerWarnings = "analyzer-warnings" 56 | case BuildFailed = "build-failed" 57 | case CheckoutError = "checkout-error" 58 | case InternalError = "internal-error" 59 | case InternalCheckoutError = "internal-checkout-error" 60 | case InternalBuildError = "internal-build-error" 61 | case InternalProcessingError = "internal-processing-error" 62 | case Canceled = "canceled" 63 | case TriggerError = "trigger-error" 64 | } 65 | 66 | public required init(json: NSDictionary) throws { 67 | 68 | self.queuedDate = try json.dateForKey("queuedDate") 69 | self.startedDate = json.optionalDateForKey("startedTime") 70 | self.endedTime = json.optionalDateForKey("endedTime") 71 | self.duration = json.optionalDoubleForKey("duration") 72 | self.shouldClean = try json.boolForKey("shouldClean") 73 | self.currentStep = Step(rawValue: try json.stringForKey("currentStep")) ?? .Unknown 74 | self.number = try json.intForKey("number") 75 | self.successStreak = try json.intForKey("success_streak") 76 | self.expectedCompletionDate = json.optionalDateForKey("expectedCompletionDate") 77 | 78 | if let raw = json.optionalStringForKey("result") { 79 | self.result = Result(rawValue: raw) 80 | } else { 81 | self.result = nil 82 | } 83 | 84 | if let raw = json.optionalDictionaryForKey("buildResultSummary") { 85 | self.buildResultSummary = try BuildResultSummary(json: raw) 86 | } else { 87 | self.buildResultSummary = nil 88 | } 89 | 90 | if let testedDevices = json.optionalArrayForKey("testedDevices") { 91 | self.testedDevices = try XcodeServerArray(testedDevices) 92 | } else { 93 | self.testedDevices = nil 94 | } 95 | 96 | if let testHierarchy = json.optionalDictionaryForKey("testHierarchy") where testHierarchy.count > 0 { 97 | self.testHierarchy = try TestHierarchy(json: testHierarchy) 98 | } else { 99 | self.testHierarchy = nil 100 | } 101 | 102 | self.assets = json.optionalDictionaryForKey("assets") 103 | 104 | if let blueprint = json.optionalDictionaryForKey("revisionBlueprint") { 105 | self.blueprint = try SourceControlBlueprint(json: blueprint) 106 | } else { 107 | self.blueprint = nil 108 | } 109 | 110 | try super.init(json: json) 111 | } 112 | } 113 | 114 | public class BuildResultSummary : XcodeServerEntity { 115 | 116 | public let analyzerWarningCount: Int 117 | public let testFailureCount: Int 118 | public let testsChange: Int 119 | public let errorCount: Int 120 | public let testsCount: Int 121 | public let testFailureChange: Int 122 | public let warningChange: Int 123 | public let regressedPerfTestCount: Int 124 | public let warningCount: Int 125 | public let errorChange: Int 126 | public let improvedPerfTestCount: Int 127 | public let analyzerWarningChange: Int 128 | public let codeCoveragePercentage: Int 129 | public let codeCoveragePercentageDelta: Int 130 | 131 | public required init(json: NSDictionary) throws { 132 | 133 | self.analyzerWarningCount = try json.intForKey("analyzerWarningCount") 134 | self.testFailureCount = try json.intForKey("testFailureCount") 135 | self.testsChange = try json.intForKey("testsChange") 136 | self.errorCount = try json.intForKey("errorCount") 137 | self.testsCount = try json.intForKey("testsCount") 138 | self.testFailureChange = try json.intForKey("testFailureChange") 139 | self.warningChange = try json.intForKey("warningChange") 140 | self.regressedPerfTestCount = try json.intForKey("regressedPerfTestCount") 141 | self.warningCount = try json.intForKey("warningCount") 142 | self.errorChange = try json.intForKey("errorChange") 143 | self.improvedPerfTestCount = try json.intForKey("improvedPerfTestCount") 144 | self.analyzerWarningChange = try json.intForKey("analyzerWarningChange") 145 | self.codeCoveragePercentage = json.optionalIntForKey("codeCoveragePercentage") ?? 0 146 | self.codeCoveragePercentageDelta = json.optionalIntForKey("codeCoveragePercentageDelta") ?? 0 147 | 148 | try super.init(json: json) 149 | } 150 | 151 | } 152 | 153 | extension Integration : Hashable { 154 | 155 | public var hashValue: Int { 156 | get { 157 | return self.number 158 | } 159 | } 160 | } 161 | 162 | public func ==(lhs: Integration, rhs: Integration) -> Bool { 163 | return lhs.number == rhs.number 164 | } 165 | 166 | 167 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/IntegrationCommits.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrationCommits.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 23/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public class IntegrationCommits: XcodeServerEntity { 13 | 14 | public let integration: String 15 | public let botTinyID: String 16 | public let botID: String 17 | public let commits: [String: [Commit]] 18 | public let endedTimeDate: NSDate? 19 | 20 | public required init(json: NSDictionary) throws { 21 | self.integration = try json.stringForKey("integration") 22 | self.botTinyID = try json.stringForKey("botTinyID") 23 | self.botID = try json.stringForKey("botID") 24 | self.commits = try IntegrationCommits.populateCommits(try json.dictionaryForKey("commits")) 25 | self.endedTimeDate = IntegrationCommits.parseDate(try json.arrayForKey("endedTimeDate")) 26 | 27 | try super.init(json: json) 28 | } 29 | 30 | /** 31 | Method for populating commits property with data from JSON dictionary. 32 | 33 | - parameter json: JSON dictionary with blueprints and commits for each one. 34 | 35 | - returns: Dictionary of parsed Commit objects. 36 | */ 37 | class func populateCommits(json: NSDictionary) throws -> [String: [Commit]] { 38 | var resultsDictionary: [String: [Commit]] = Dictionary() 39 | 40 | for (key, value) in json { 41 | guard let blueprintID = key as? String, let commitsArray = value as? [NSDictionary] else { 42 | Log.error("Couldn't parse key \(key) and value \(value)") 43 | continue 44 | } 45 | 46 | resultsDictionary[blueprintID] = try commitsArray.map { try Commit(json: $0) } 47 | } 48 | 49 | return resultsDictionary 50 | } 51 | 52 | /** 53 | Parser for data objects which comes in form of array. 54 | 55 | - parameter array: Array with date components. 56 | 57 | - returns: Optional parsed date to the format used by Xcode Server. 58 | */ 59 | class func parseDate(array: NSArray) -> NSDate? { 60 | guard let dateArray = array as? [Int] else { 61 | Log.error("Couldn't parse XCS date array") 62 | return nil 63 | } 64 | 65 | do { 66 | let stringDate = try dateArray.dateString() 67 | 68 | guard let date = NSDate.dateFromXCSString(stringDate) else { 69 | Log.error("Formatter couldn't parse date") 70 | return nil 71 | } 72 | 73 | return date 74 | } catch DateParsingError.WrongNumberOfElements(let elements) { 75 | Log.error("Couldn't parse date as Array has \(elements) elements") 76 | } catch { 77 | Log.error("Something went wrong while parsing date") 78 | } 79 | 80 | return nil 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/IntegrationIssue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Issue.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 04.08.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class IntegrationIssue: XcodeServerEntity { 12 | 13 | public enum IssueType: String { 14 | case BuildServiceError = "buildServiceError" 15 | case BuildServiceWarning = "buildServiceWarning" 16 | case TriggerError = "triggerError" 17 | case Error = "error" 18 | case Warning = "warning" 19 | case TestFailure = "testFailure" 20 | case AnalyzerWarning = "analyzerWarning" 21 | } 22 | 23 | public enum IssueStatus: Int { 24 | case Fresh = 0 25 | case Unresolved 26 | case Resolved 27 | case Silenced 28 | } 29 | 30 | /// Payload is holding whole Dictionary of the Issue 31 | public let payload: NSDictionary 32 | 33 | public let message: String? 34 | public let type: IssueType 35 | public let issueType: String 36 | public let commits: [Commit] 37 | public let integrationID: String 38 | public let age: Int 39 | public let status: IssueStatus 40 | 41 | // MARK: Initialization 42 | public required init(json: NSDictionary) throws { 43 | self.payload = json.copy() as? NSDictionary ?? NSDictionary() 44 | 45 | self.message = json.optionalStringForKey("message") 46 | self.type = try IssueType(rawValue: json.stringForKey("type"))! 47 | self.issueType = try json.stringForKey("issueType") 48 | self.commits = try json.arrayForKey("commits").map { try Commit(json: $0) } 49 | self.integrationID = try json.stringForKey("integrationID") 50 | self.age = try json.intForKey("age") 51 | self.status = IssueStatus(rawValue: try json.intForKey("status"))! 52 | 53 | try super.init(json: json) 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/IntegrationIssues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrationIssues.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 12.08.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public class IntegrationIssues: XcodeServerEntity { 13 | 14 | public let buildServiceErrors: [IntegrationIssue] 15 | public let buildServiceWarnings: [IntegrationIssue] 16 | public let triggerErrors: [IntegrationIssue] 17 | public let errors: [IntegrationIssue] 18 | public let warnings: [IntegrationIssue] 19 | public let testFailures: [IntegrationIssue] 20 | public let analyzerWarnings: [IntegrationIssue] 21 | 22 | // MARK: Initialization 23 | 24 | public required init(json: NSDictionary) throws { 25 | self.buildServiceErrors = try json.arrayForKey("buildServiceErrors").map { try IntegrationIssue(json: $0) } 26 | self.buildServiceWarnings = try json.arrayForKey("buildServiceWarnings").map { try IntegrationIssue(json: $0) } 27 | self.triggerErrors = try json.arrayForKey("triggerErrors").map { try IntegrationIssue(json: $0) } 28 | 29 | // Nested issues 30 | self.errors = try json 31 | .dictionaryForKey("errors") 32 | .allValues 33 | .filter { $0.count != 0 } 34 | .flatMap { 35 | try ($0 as! NSArray).map { try IntegrationIssue(json: $0 as! NSDictionary) } 36 | } 37 | self.warnings = try json 38 | .dictionaryForKey("warnings") 39 | .allValues 40 | .filter { $0.count != 0 } 41 | .flatMap { 42 | try ($0 as! NSArray).map { try IntegrationIssue(json: $0 as! NSDictionary) } 43 | } 44 | self.testFailures = try json 45 | .dictionaryForKey("testFailures") 46 | .allValues 47 | .filter { $0.count != 0 } 48 | .flatMap { 49 | try ($0 as! NSArray).map { try IntegrationIssue(json: $0 as! NSDictionary) } 50 | } 51 | self.analyzerWarnings = try json 52 | .dictionaryForKey("analyzerWarnings") 53 | .allValues 54 | .filter { $0.count != 0 } 55 | .flatMap { 56 | try ($0 as! NSArray).map { try IntegrationIssue(json: $0 as! NSDictionary) } 57 | } 58 | 59 | try super.init(json: json) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/LiveUpdateMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiveUpdateMessage.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 26/09/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class LiveUpdateMessage: XcodeServerEntity { 12 | 13 | public enum MessageType: String { 14 | 15 | //bots 16 | case BotCreated = "botCreated" 17 | case BotUpdated = "botUpdated" 18 | case BotRemoved = "botRemoved" 19 | 20 | //devices 21 | case DeviceCreated = "deviceCreated" 22 | case DeviceUpdated = "deviceUpdated" 23 | case DeviceRemoved = "deviceRemoved" 24 | 25 | //integrations 26 | case PendingIntegrations = "pendingIntegrations" 27 | case IntegrationCreated = "integrationCreated" 28 | case IntegrationStatus = "integrationStatus" 29 | case IntegrationCanceled = "cancelIntegration" 30 | case IntegrationRemoved = "integrationRemoved" 31 | case AdvisoryIntegrationStatus = "advisoryIntegrationStatus" 32 | 33 | //repositories 34 | case ListRepositories = "listRepositories" 35 | case CreateRepository = "createRepository" 36 | 37 | //boilerplate 38 | case Ping = "ping" 39 | case Pong = "pong" 40 | case ACLUpdated = "aclUpdated" 41 | case RequestPortalSync = "requestPortalSync" 42 | 43 | case Unknown = "" 44 | } 45 | 46 | public let type: MessageType 47 | public let message: String? 48 | public let progress: Double? 49 | public let integrationId: String? 50 | public let botId: String? 51 | public let result: Integration.Result? 52 | public let currentStep: Integration.Step? 53 | 54 | required public init(json: NSDictionary) throws { 55 | 56 | let typeString = json.optionalStringForKey("name") ?? "" 57 | 58 | self.type = MessageType(rawValue: typeString) ?? .Unknown 59 | 60 | let args = (json["args"] as? NSArray)?[0] as? NSDictionary 61 | 62 | self.message = args?["message"] as? String 63 | self.progress = args?["percentage"] as? Double 64 | self.integrationId = args?["_id"] as? String 65 | self.botId = args?["botId"] as? String 66 | 67 | if 68 | let resultString = args?["result"] as? String, 69 | let result = Integration.Result(rawValue: resultString) { 70 | self.result = result 71 | } else { 72 | self.result = nil 73 | } 74 | if 75 | let stepString = args?["currentStep"] as? String, 76 | let step = Integration.Step(rawValue: stepString) { 77 | self.currentStep = step 78 | } else { 79 | self.currentStep = nil 80 | } 81 | 82 | try super.init(json: json) 83 | } 84 | 85 | } 86 | 87 | extension LiveUpdateMessage: CustomStringConvertible { 88 | 89 | public var description: String { 90 | 91 | let empty = "" //fixed in Swift 2.1 92 | 93 | let nonNilComps = [ 94 | self.message, 95 | "\(self.progress?.description ?? empty)", 96 | self.result?.rawValue, 97 | self.currentStep?.rawValue 98 | ] 99 | .filter { $0 != nil } 100 | .map { $0! } 101 | .filter { $0.characters.count > 0 } 102 | .map { "\"\($0)\"" } 103 | 104 | let str = nonNilComps.joinWithSeparator(", ") 105 | return "LiveUpdateMessage \"\(self.type)\", \(str)" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Repository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Repository.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 28.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Repository: XcodeServerEntity { 12 | 13 | /** 14 | Enumeration describing HTTP access to the repository 15 | 16 | - None: No users are not allowed to read or write 17 | - LoggedIn: Logged in users are allowed to read and write 18 | */ 19 | public enum HTTPAccessType: Int { 20 | 21 | case None = 0 22 | case LoggedIn 23 | 24 | public func toString() -> String { 25 | switch self { 26 | case .None: 27 | return "No users are not allowed to read or write" 28 | case .LoggedIn: 29 | return "Logged in users are allowed to read and write" 30 | } 31 | } 32 | 33 | } 34 | 35 | /** 36 | Enumeration describing HTTPS access to the repository 37 | 38 | - SelectedReadWrite: Only selected users can read and/or write 39 | - LoggedInReadSelectedWrite: Only selected users can write but all logged in can read 40 | - LoggedInReadWrite: All logged in users can read and write 41 | */ 42 | public enum SSHAccessType: Int { 43 | 44 | case SelectedReadWrite = 0 45 | case LoggedInReadSelectedWrite 46 | case LoggedInReadWrite 47 | 48 | public func toString() -> String { 49 | switch self { 50 | case .SelectedReadWrite: 51 | return "Only selected users can read and/or write" 52 | case .LoggedInReadSelectedWrite: 53 | return "Only selected users can write but all logged in can read" 54 | case .LoggedInReadWrite: 55 | return "All logged in users can read and write" 56 | } 57 | } 58 | 59 | } 60 | 61 | public let name: String 62 | public var httpAccess: HTTPAccessType = HTTPAccessType.None 63 | // XCS's defualt if SelectedReadWrite but if you don't provide 64 | // array of IDs, nobody will have access to the repository 65 | public var sshAccess: SSHAccessType = SSHAccessType.LoggedInReadWrite 66 | public var writeAccessExternalIds: [String] = [] 67 | public var readAccessExternalIds: [String] = [] 68 | 69 | /** 70 | Designated initializer. 71 | 72 | - parameter name: Name of the repository. 73 | - parameter httpsAccess: HTTPS access type for the users. 74 | - parameter sshAccess: SSH access type for the users. 75 | - parameter writeAccessExternalIds: ID of users allowed to write to the repository. 76 | - parameter readAccessExternalIds: ID of users allowed to read from the repository. 77 | 78 | - returns: Initialized repository struct. 79 | */ 80 | public init(name: String, httpAccess: HTTPAccessType?, sshAccess: SSHAccessType?, writeAccessExternalIds: [String]?, readAccessExternalIds: [String]?) { 81 | self.name = name 82 | 83 | if let httpAccess = httpAccess { 84 | self.httpAccess = httpAccess 85 | } 86 | 87 | if let sshAccess = sshAccess { 88 | self.sshAccess = sshAccess 89 | } 90 | 91 | if let writeIDs = writeAccessExternalIds { 92 | self.writeAccessExternalIds = writeIDs 93 | } 94 | 95 | if let readIDs = readAccessExternalIds { 96 | self.readAccessExternalIds = readIDs 97 | } 98 | 99 | super.init() 100 | } 101 | 102 | /** 103 | Convenience initializer. 104 | This initializer will only allow you to provie name and will create a 105 | deault repository with values set to: 106 | - **HTTP Access** - No user is allowed to read/write to repository 107 | - **SSH Access** - Only selected users are allowed to read/write to repository 108 | - **Empty arrays** of write and rad external IDs 109 | 110 | - parameter name: Name of the repository. 111 | 112 | - returns: Initialized default repository wwith provided name 113 | */ 114 | public convenience init(name: String) { 115 | self.init(name: name, httpAccess: nil, sshAccess: nil, writeAccessExternalIds: nil, readAccessExternalIds: nil) 116 | } 117 | 118 | /** 119 | Repository constructor from JSON object. 120 | 121 | - parameter json: JSON dictionary representing repository. 122 | 123 | - returns: Initialized repository struct. 124 | */ 125 | public required init(json: NSDictionary) throws { 126 | self.name = try json.stringForKey("name") 127 | 128 | self.httpAccess = HTTPAccessType(rawValue: try json.intForKey("httpAccessType"))! 129 | self.sshAccess = SSHAccessType(rawValue: try json.intForKey("posixPermissions"))! 130 | 131 | self.writeAccessExternalIds = try json.arrayForKey("writeAccessExternalIDs") 132 | self.readAccessExternalIds = try json.arrayForKey("readAccessExternalIDs") 133 | 134 | try super.init(json: json) 135 | } 136 | 137 | /** 138 | Method for returning object in form of Dictionary. 139 | 140 | - returns: Dictionary representing JSON value of Repository object. 141 | */ 142 | public override func dictionarify() -> NSMutableDictionary { 143 | let dict = NSMutableDictionary() 144 | 145 | dict["name"] = self.name 146 | dict["httpAccessType"] = self.httpAccess.rawValue 147 | dict["posixPermissions"] = self.sshAccess.rawValue 148 | dict["writeAccessExternalIDs"] = self.writeAccessExternalIds 149 | dict["readAccessExternalIDs"] = self.readAccessExternalIds 150 | 151 | return dict 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/TestHierarchy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestHierarchy.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 15/07/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let TestResultAggregateKey = "_xcsAggrDeviceStatus" 12 | 13 | public class TestHierarchy : XcodeServerEntity { 14 | 15 | typealias TestResult = [String: Double] 16 | typealias AggregateResult = TestResult 17 | 18 | enum TestMethod { 19 | case Method(TestResult) 20 | case Aggregate(AggregateResult) 21 | } 22 | 23 | enum TestClass { 24 | case Class([String: TestMethod]) 25 | case Aggregate(AggregateResult) 26 | } 27 | 28 | typealias TestTarget = [String: TestClass] 29 | typealias TestData = [String: TestTarget] 30 | 31 | let testData: TestData 32 | 33 | /* 34 | the json looks like this: 35 | { 36 | //target 37 | "XcodeServerSDKTest": { 38 | 39 | //class 40 | "BotParsingTests": { 41 | 42 | //method 43 | "testParseOSXBot()": { 44 | 45 | //device -> number (bool) 46 | "12345-67890": 1, 47 | "09876-54321": 0 48 | }, 49 | "testShared()": { 50 | "12345-67890": 1, 51 | "09876-54321": 1 52 | } 53 | "_xcsAggrDeviceStatus": { 54 | "12345-67890": 1, 55 | "09876-54321": 0 56 | } 57 | }, 58 | "_xcsAggrDeviceStatus": { 59 | "12345-67890": 1, 60 | "09876-54321": 0 61 | } 62 | } 63 | } 64 | 65 | As a class and a method, there's always another key-value pair, with key "_xcsAggrDeviceStatus", 66 | which is the aggregated status, so that you don't have to iterate through all tests to figure it out yourself. 1 if all are 1, 0 otherwise. 67 | */ 68 | 69 | public required init(json: NSDictionary) throws { 70 | 71 | //TODO: come up with useful things to parse 72 | //TODO: add search capabilities, aggregate generation etc 73 | 74 | self.testData = TestHierarchy.pullData(json) 75 | 76 | try super.init(json: json) 77 | } 78 | 79 | class func pullData(json: NSDictionary) -> TestData { 80 | 81 | var data = TestData() 82 | 83 | for (_targetName, _targetData) in json { 84 | let targetName = _targetName as! String 85 | let targetData = _targetData as! NSDictionary 86 | data[targetName] = pullTarget(targetName, targetData: targetData) 87 | } 88 | 89 | return data 90 | } 91 | 92 | class func pullTarget(targetName: String, targetData: NSDictionary) -> TestTarget { 93 | 94 | var target = TestTarget() 95 | 96 | for (_className, _classData) in targetData { 97 | let className = _className as! String 98 | let classData = _classData as! NSDictionary 99 | target[className] = pullClass(className, classData: classData) 100 | } 101 | 102 | return target 103 | } 104 | 105 | class func pullClass(className: String, classData: NSDictionary) -> TestClass { 106 | 107 | let classy: TestClass 108 | if className == TestResultAggregateKey { 109 | classy = TestClass.Aggregate(classData as! AggregateResult) 110 | } else { 111 | 112 | var newData = [String: TestMethod]() 113 | 114 | for (_methodName, _methodData) in classData { 115 | let methodName = _methodName as! String 116 | let methodData = _methodData as! NSDictionary 117 | newData[methodName] = pullMethod(methodName, methodData: methodData) 118 | } 119 | 120 | classy = TestClass.Class(newData) 121 | } 122 | return classy 123 | } 124 | 125 | class func pullMethod(methodName: String, methodData: NSDictionary) -> TestMethod { 126 | 127 | let method: TestMethod 128 | if methodName == TestResultAggregateKey { 129 | method = TestMethod.Aggregate(methodData as! AggregateResult) 130 | } else { 131 | method = TestMethod.Method(methodData as! TestResult) 132 | } 133 | return method 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Toolchain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Toolchain.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Laurent Gaches on 21/04/16. 6 | // Copyright © 2016 Laurent Gaches. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Toolchain: XcodeServerEntity { 12 | 13 | public let displayName: String 14 | public let path: String 15 | public let signatureVerified: Bool 16 | 17 | public required init(json: NSDictionary) throws { 18 | 19 | self.displayName = try json.stringForKey("displayName") 20 | self.path = try json.stringForKey("path") 21 | self.signatureVerified = try json.boolForKey("signatureVerified") 22 | 23 | try super.init(json: json) 24 | } 25 | } -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/Trigger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Trigger.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public struct TriggerConfig: XcodeRead, XcodeWrite { 13 | 14 | public let id: RefType 15 | 16 | public enum Phase: Int { 17 | case Prebuild = 1 18 | case Postbuild 19 | 20 | public func toString() -> String { 21 | switch self { 22 | case .Prebuild: 23 | return "Run Before the Build" 24 | case .Postbuild: 25 | return "Run After the Build" 26 | } 27 | } 28 | } 29 | 30 | public enum Kind: Int { 31 | case RunScript = 1 32 | case EmailNotification 33 | 34 | public func toString() -> String { 35 | switch self { 36 | case .RunScript: 37 | return "Run Script" 38 | case .EmailNotification: 39 | return "Send Email" 40 | } 41 | } 42 | } 43 | 44 | public var phase: Phase 45 | public var kind: Kind 46 | public var scriptBody: String 47 | public var name: String 48 | public var conditions: TriggerConditions? 49 | public var emailConfiguration: EmailConfiguration? 50 | 51 | //creates a default trigger config 52 | public init() { 53 | self.phase = .Prebuild 54 | self.kind = .RunScript 55 | self.scriptBody = "cd *\n" 56 | self.name = "" 57 | self.conditions = nil 58 | self.emailConfiguration = nil 59 | self.id = Ref.new() 60 | } 61 | 62 | public init?(phase: Phase, kind: Kind, scriptBody: String?, name: String?, 63 | conditions: TriggerConditions?, emailConfiguration: EmailConfiguration?, id: RefType? = Ref.new()) { 64 | 65 | self.phase = phase 66 | self.kind = kind 67 | self.scriptBody = scriptBody ?? "" 68 | self.name = name ?? kind.toString() 69 | self.conditions = conditions 70 | self.emailConfiguration = emailConfiguration 71 | self.id = id ?? Ref.new() 72 | 73 | //post build triggers must have conditions 74 | if phase == Phase.Postbuild { 75 | if conditions == nil { 76 | return nil 77 | } 78 | } 79 | 80 | //email type must have a configuration 81 | if kind == Kind.EmailNotification { 82 | if emailConfiguration == nil { 83 | return nil 84 | } 85 | } 86 | } 87 | 88 | public init(json: NSDictionary) throws { 89 | 90 | let phase = Phase(rawValue: try json.intForKey("phase"))! 91 | self.phase = phase 92 | if let conditionsJSON = json.optionalDictionaryForKey("conditions") where phase == .Postbuild { 93 | //also parse conditions 94 | self.conditions = try TriggerConditions(json: conditionsJSON) 95 | } else { 96 | self.conditions = nil 97 | } 98 | 99 | let kind = Kind(rawValue: try json.intForKey("type"))! 100 | self.kind = kind 101 | if let configurationJSON = json.optionalDictionaryForKey("emailConfiguration") where kind == .EmailNotification { 102 | //also parse email config 103 | self.emailConfiguration = try EmailConfiguration(json: configurationJSON) 104 | } else { 105 | self.emailConfiguration = nil 106 | } 107 | 108 | self.name = try json.stringForKey("name") 109 | self.scriptBody = try json.stringForKey("scriptBody") 110 | 111 | self.id = json.optionalStringForKey("id") ?? Ref.new() 112 | } 113 | 114 | public func dictionarify() -> NSDictionary { 115 | 116 | let dict = NSMutableDictionary() 117 | 118 | dict["id"] = self.id 119 | dict["phase"] = self.phase.rawValue 120 | dict["type"] = self.kind.rawValue 121 | dict["scriptBody"] = self.scriptBody 122 | dict["name"] = self.name 123 | dict.optionallyAddValueForKey(self.conditions?.dictionarify(), key: "conditions") 124 | dict.optionallyAddValueForKey(self.emailConfiguration?.dictionarify(), key: "emailConfiguration") 125 | 126 | return dict 127 | } 128 | } 129 | 130 | public class Trigger : XcodeServerEntity { 131 | 132 | public let config: TriggerConfig 133 | 134 | public init(config: TriggerConfig) { 135 | self.config = config 136 | super.init() 137 | } 138 | 139 | required public init(json: NSDictionary) throws { 140 | 141 | self.config = try TriggerConfig(json: json) 142 | try super.init(json: json) 143 | } 144 | 145 | public override func dictionarify() -> NSDictionary { 146 | let dict = self.config.dictionarify() 147 | return dict 148 | } 149 | } 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Entities/TriggerConditions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TriggerConditions.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class TriggerConditions : XcodeServerEntity { 12 | 13 | public let status: Int 14 | public let onAnalyzerWarnings: Bool 15 | public let onBuildErrors: Bool 16 | public let onFailingTests: Bool 17 | public let onInternalErrors: Bool 18 | public let onSuccess: Bool 19 | public let onWarnings: Bool 20 | 21 | public init(status: Int = 2, onAnalyzerWarnings: Bool, onBuildErrors: Bool, onFailingTests: Bool, onInternalErrors: Bool, onSuccess: Bool, onWarnings: Bool) { 22 | 23 | self.status = status 24 | self.onAnalyzerWarnings = onAnalyzerWarnings 25 | self.onBuildErrors = onBuildErrors 26 | self.onFailingTests = onFailingTests 27 | self.onInternalErrors = onInternalErrors 28 | self.onSuccess = onSuccess 29 | self.onWarnings = onWarnings 30 | 31 | super.init() 32 | } 33 | 34 | public override func dictionarify() -> NSDictionary { 35 | 36 | let dict = NSMutableDictionary() 37 | 38 | dict["status"] = self.status 39 | dict["onAnalyzerWarnings"] = self.onAnalyzerWarnings 40 | dict["onBuildErrors"] = self.onBuildErrors 41 | dict["onFailingTests"] = self.onFailingTests 42 | dict["onInternalErrors"] = self.onInternalErrors 43 | dict["onSuccess"] = self.onSuccess 44 | dict["onWarnings"] = self.onWarnings 45 | 46 | return dict 47 | } 48 | 49 | public required init(json: NSDictionary) throws { 50 | 51 | self.status = json.optionalIntForKey("status") ?? 2 52 | self.onAnalyzerWarnings = try json.boolForKey("onAnalyzerWarnings") 53 | self.onBuildErrors = try json.boolForKey("onBuildErrors") 54 | self.onFailingTests = try json.boolForKey("onFailingTests") 55 | 56 | //not present in Xcode 8 anymore, make it optional & default to false 57 | let internalErrors = try? json.boolForKey("onInternalErrors") 58 | self.onInternalErrors = internalErrors ?? false 59 | self.onSuccess = try json.boolForKey("onSuccess") 60 | self.onWarnings = try json.boolForKey("onWarnings") 61 | 62 | try super.init(json: json) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Helpers/CIServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CIServer.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public class CIServer : HTTPServer { 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Helpers/SocketIOHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketIOHelper.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 27/09/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /* 12 | inspired by: /Applications/Xcode.app/Contents/Developer/usr/share/xcs/xcsd/node_modules/socket.io/lib/parser.js 13 | also helped: https://github.com/Crphang/Roadents/blob/df59d10bd102f04962e933f9a477066ea0c84da7/socket.IO-objc-master/SocketIOTransportXHR.m 14 | */ 15 | 16 | public struct SocketIOPacket { 17 | 18 | public enum PacketType: Int { 19 | case Disconnect = 0 20 | case Connect = 1 21 | case Heartbeat = 2 22 | case Message = 3 23 | case JSON = 4 24 | case Event = 5 25 | case Ack = 6 26 | case Error = 7 27 | case Noop = 8 28 | } 29 | 30 | public enum ErrorReason: Int { 31 | case TransportNotSupported = 0 32 | case ClientNotHandshaken = 1 33 | case Unauthorized = 2 34 | } 35 | 36 | public enum ErrorAdvice: Int { 37 | case Reconnect = 0 38 | } 39 | 40 | private let dataString: String 41 | public let type: PacketType 42 | public let stringPayload: String 43 | public var jsonPayload: NSDictionary? { 44 | guard let data = self.stringPayload.dataUsingEncoding(NSUTF8StringEncoding) else { return nil } 45 | let obj = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) 46 | let dict = obj as? NSDictionary 47 | return dict 48 | } 49 | 50 | public init?(data: String) { 51 | self.dataString = data 52 | guard let comps = SocketIOPacket.parseComps(data) else { return nil } 53 | (self.type, self.stringPayload) = comps 54 | } 55 | 56 | private static func countInitialColons(data: String) -> Int { 57 | var initialColonsCount = 0 58 | for i in data.characters { 59 | if i == ":" { 60 | initialColonsCount += 1 61 | } else { 62 | if initialColonsCount > 0 { 63 | break 64 | } 65 | } 66 | } 67 | return initialColonsCount 68 | } 69 | 70 | private static func parseComps(data: String) -> (type: PacketType, stringPayload: String)? { 71 | 72 | //find the initial sequence of colons and count them, to know how to split the packet 73 | let initialColonsCount = self.countInitialColons(data) 74 | let splitter = String(count: initialColonsCount, repeatedValue: Character(":")) 75 | 76 | let comps = data 77 | .componentsSeparatedByString(splitter) 78 | .filter { $0.characters.count > 0 } 79 | guard comps.count > 0 else { return nil } 80 | guard 81 | let typeString = comps.first, 82 | let typeInt = Int(typeString), 83 | let type = PacketType(rawValue: typeInt) 84 | else { return nil } 85 | let stringPayload = comps.count == 1 ? "" : (comps.last ?? "") 86 | return (type, stringPayload) 87 | } 88 | 89 | //e.g. "7:::1+0" 90 | public func parseError() -> (reason: ErrorReason?, advice: ErrorAdvice?) { 91 | let comps = self.stringPayload.componentsSeparatedByString("+") 92 | let reasonString = comps.first ?? "" 93 | let reasonInt = Int(reasonString) ?? -1 94 | let adviceString = comps.count == 2 ? comps.last! : "" 95 | let adviceInt = Int(adviceString) ?? -1 96 | let reason = ErrorReason(rawValue: reasonInt) 97 | let advice = ErrorAdvice(rawValue: adviceInt) 98 | return (reason, advice) 99 | } 100 | } 101 | 102 | public class SocketIOHelper { 103 | 104 | public static func parsePackets(message: String) -> [SocketIOPacket] { 105 | 106 | let packetStrings = self.parsePacketStrings(message) 107 | let packets = packetStrings.map { SocketIOPacket(data: $0) }.filter { $0 != nil }.map { $0! } 108 | return packets 109 | } 110 | 111 | private static func parsePacketStrings(message: String) -> [String] { 112 | 113 | // Sometimes Socket.IO "batches" up messages in one packet, so we have to split them. 114 | // "Batched" format is: 115 | // �[packet_0 length]�[packet_0]�[packet_1 length]�[packet_1]�[packet_n length]�[packet_n] 116 | let splitChar = "\u{fffd}" 117 | guard message.hasPrefix(splitChar) else { return [message] } 118 | 119 | let comps = message 120 | .substringFromIndex(message.startIndex.advancedBy(1)) 121 | .componentsSeparatedByString(splitChar) 122 | .filter { $0.componentsSeparatedByString(":::").count > 1 } 123 | return comps 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /XcodeServerSDK/Server Helpers/XcodeServerConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerConstants.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 11/01/2015. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //blueprint 12 | let XcodeBlueprintLocationsKey = "DVTSourceControlWorkspaceBlueprintLocationsKey" //dictionary 13 | let XcodeBlueprintPrimaryRemoteRepositoryKey = "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" //string 14 | let XcodeRepositoryAuthenticationStrategiesKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey" //dictionary 15 | let XcodeBlueprintWorkingCopyStatesKey = "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" //dictionary 16 | let XcodeBlueprintIdentifierKey = "DVTSourceControlWorkspaceBlueprintIdentifierKey" //string 17 | let XcodeBlueprintNameKey = "DVTSourceControlWorkspaceBlueprintNameKey" //string 18 | let XcodeBlueprintVersion = "DVTSourceControlWorkspaceBlueprintVersion" //number 19 | let XcodeBlueprintRelativePathToProjectKey = "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" //string 20 | let XcodeBlueprintWorkingCopyPathsKey = "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" //dictionary 21 | let XcodeBlueprintRemoteRepositoriesKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" //array 22 | 23 | 24 | //locations 25 | let XcodeBranchIdentifierKey = "DVTSourceControlBranchIdentifierKey" 26 | let XcodeLocationRevisionKey = "DVTSourceControlLocationRevisionKey" 27 | let XcodeBranchOptionsKey = "DVTSourceControlBranchOptionsKey" 28 | let XcodeBlueprintLocationTypeKey = "DVTSourceControlWorkspaceBlueprintLocationTypeKey" 29 | let XcodeBlueprintRemoteRepositoryURLKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" 30 | let XcodeBlueprintRemoteRepositorySystemKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" 31 | let XcodeBlueprintRemoteRepositoryIdentifierKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" 32 | let XcodeBlueprintRemoteRepositoryCertFingerprintKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustedCertFingerprintKey" 33 | let XcodeBlueprintRemoteRepositoryTrustSelfSignedCertKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustSelfSignedCertKey" 34 | 35 | //repo 36 | let XcodeRepoUsernameKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey" 37 | let XcodeRepoAuthenticationStrategiesKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey" 38 | let XcodeRepoAuthenticationTypeKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey" 39 | let XcodeRepoPublicKeyDataKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey" 40 | let XcodeRepoPasswordKey = "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey" 41 | 42 | let XcodeRepoSSHKeysAuthenticationStrategy = "DVTSourceControlSSHKeysAuthenticationStrategy" -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServer.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | // MARK: XcodeServer Class 13 | public class XcodeServer : CIServer { 14 | 15 | public var config: XcodeServerConfig 16 | let endpoints: XcodeServerEndpoints 17 | 18 | public var availabilityState: AvailabilityCheckState = .Unchecked 19 | 20 | public init(config: XcodeServerConfig, endpoints: XcodeServerEndpoints) { 21 | 22 | self.config = config 23 | self.endpoints = endpoints 24 | 25 | super.init() 26 | 27 | let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration() 28 | let delegate: NSURLSessionDelegate = self 29 | let queue = NSOperationQueue.mainQueue() 30 | let session = NSURLSession(configuration: sessionConfig, delegate: delegate, delegateQueue: queue) 31 | self.http.session = session 32 | } 33 | } 34 | 35 | // MARK: NSURLSession delegate implementation 36 | extension XcodeServer : NSURLSessionDelegate { 37 | 38 | var credential: NSURLCredential? { 39 | 40 | if 41 | let user = self.config.user, 42 | let password = self.config.password { 43 | return NSURLCredential(user: user, password: password, persistence: NSURLCredentialPersistence.None) 44 | } 45 | return nil 46 | } 47 | 48 | public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 49 | 50 | var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling 51 | var credential: NSURLCredential? 52 | 53 | if challenge.previousFailureCount > 0 { 54 | disposition = .CancelAuthenticationChallenge 55 | } else { 56 | 57 | switch challenge.protectionSpace.authenticationMethod { 58 | 59 | case NSURLAuthenticationMethodServerTrust: 60 | credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!) 61 | default: 62 | credential = self.credential ?? session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace) 63 | } 64 | 65 | if credential != nil { 66 | disposition = .UseCredential 67 | } 68 | } 69 | 70 | completionHandler(disposition, credential) 71 | } 72 | } 73 | 74 | // MARK: Header constants 75 | let Headers_APIVersion = "X-XCSAPIVersion" 76 | let VerifiedAPIVersions: Set = [6, 7, 9, 10] //will change with time, this codebase supports these versions 77 | 78 | // MARK: XcodeServer API methods 79 | public extension XcodeServer { 80 | 81 | private func verifyAPIVersion(response: NSHTTPURLResponse) -> NSError? { 82 | 83 | guard let headers = response.allHeaderFields as? [String: AnyObject] else { 84 | return Error.withInfo("No headers provided in response") 85 | } 86 | 87 | let apiVersionString = (headers[Headers_APIVersion] as? String) ?? "-1" 88 | let apiVersion = Int(apiVersionString) 89 | 90 | if let apiVersion = apiVersion where 91 | apiVersion > 0 && !VerifiedAPIVersions.contains(apiVersion) { 92 | var common = "Version mismatch: response from API version \(apiVersion), but we only verified versions \(VerifiedAPIVersions). " 93 | 94 | let maxVersion = VerifiedAPIVersions.sort().last! 95 | if apiVersion > maxVersion { 96 | Log.info("You're using a newer Xcode Server than we've verified (\(apiVersion), last verified is \(maxVersion)). Please visit https://github.com/czechboy0/XcodeServerSDK to check whether there's a new version of the SDK for it. If not, please file an issue in the XcodeServerSDK repository. The requests are still going through, however we haven't verified this API version, so here be dragons.") 97 | } else { 98 | common += "You're using an old Xcode Server which we don't support any more. Please look for an older version of the SDK at https://github.com/czechboy0/XcodeServerSDK or consider upgrading your Xcode Server to the current version." 99 | return Error.withInfo(common) 100 | } 101 | } 102 | 103 | //all good 104 | return nil 105 | } 106 | 107 | /** 108 | Internal usage generic method for sending HTTP requests. 109 | 110 | - parameter method: HTTP method. 111 | - parameter endpoint: API endpoint. 112 | - parameter params: URL paramaters. 113 | - parameter query: URL query. 114 | - parameter body: POST method request body. 115 | - parameter completion: Completion. 116 | */ 117 | internal func sendRequestWithMethod(method: HTTP.Method, endpoint: XcodeServerEndpoints.Endpoint, params: [String: String]?, query: [String: String]?, body: NSDictionary?, portOverride: Int? = nil, completion: HTTP.Completion) -> NSURLSessionTask? { 118 | if let request = self.endpoints.createRequest(method, endpoint: endpoint, params: params, query: query, body: body, portOverride: portOverride) { 119 | 120 | return self.http.sendRequest(request, completion: { (response, body, error) -> () in 121 | 122 | //TODO: fix hack, make completion always return optionals 123 | let resp: NSHTTPURLResponse? = response 124 | 125 | guard let r = resp else { 126 | let e = error ?? Error.withInfo("Nil response") 127 | completion(response: nil, body: body, error: e) 128 | return 129 | } 130 | 131 | if let versionError = self.verifyAPIVersion(r) { 132 | completion(response: response, body: body, error: versionError) 133 | return 134 | } 135 | 136 | if case (200...299) = r.statusCode { 137 | //pass on 138 | completion(response: response, body: body, error: error) 139 | } else { 140 | //see if we haven't received a XCS failure in headers 141 | if let xcsStatusMessage = r.allHeaderFields["X-XCSResponse-Status-Message"] as? String { 142 | let e = Error.withInfo(xcsStatusMessage) 143 | completion(response: response, body: body, error: e) 144 | } else { 145 | completion(response: response, body: body, error: error) 146 | } 147 | } 148 | }) 149 | 150 | } else { 151 | completion(response: nil, body: nil, error: Error.withInfo("Couldn't create Request")) 152 | return nil 153 | } 154 | } 155 | 156 | } 157 | 158 | -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServerConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerConfig.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | /// Posible errors thrown by `XcodeServerConfig` 13 | public enum ConfigurationErrors : ErrorType { 14 | /// Thrown when no host was provided 15 | case NoHostProvided 16 | /// Thrown when an invalid host is provided (host is returned) 17 | case InvalidHostProvided(String) 18 | /// Thrown when a host is provided with an invalid scheme (explanation message returned) 19 | case InvalidSchemeProvided(String) 20 | /// Thrown when only one of (username, password) is provided, which is not valid 21 | case InvalidCredentialsProvided 22 | } 23 | 24 | private struct Keys { 25 | static let Host = "host" 26 | static let User = "user" 27 | static let Password = "password" 28 | static let Id = "id" 29 | } 30 | 31 | public struct XcodeServerConfig : JSONSerializable { 32 | 33 | public let id: RefType 34 | public var host: String 35 | public var user: String? 36 | public var password: String? 37 | 38 | public let port: Int = 20343 39 | 40 | //if set to false, fails if server certificate is not trusted yet 41 | public let automaticallyTrustSelfSignedCertificates: Bool = true 42 | 43 | public func jsonify() -> NSDictionary { 44 | let dict = NSMutableDictionary() 45 | dict[Keys.Id] = self.id 46 | dict[Keys.Host] = self.host 47 | dict.optionallyAddValueForKey(self.user, key: Keys.User) 48 | return dict 49 | } 50 | 51 | //creates a new default config 52 | public init() { 53 | self.id = Ref.new() 54 | self.host = "" 55 | self.user = nil 56 | self.password = nil 57 | } 58 | 59 | /** 60 | Initializes a server configuration with the provided host. 61 | - parameter host: `Xcode` server host. 62 | - paramater user: Username that will be used to authenticate against the `host` provided. 63 | Can be `nil`. 64 | 65 | - parameter password: Password that will be used to authenticate against the `host` provided. 66 | Can be `nil`. 67 | 68 | - returns: A fully initialized `XcodeServerConfig` instance. 69 | 70 | - throws: 71 | - `NoHostProvided`: When the host string is empty. 72 | - `InvalidHostProvided`: When the host provided doesn't produce a valid `URL` 73 | - `InvalidSchemeProvided`: When the provided scheme is not `HTTPS` 74 | */ 75 | public init(host _host: String, user: String? = nil, password: String? = nil, id: RefType? = nil) throws { 76 | 77 | var host = _host 78 | 79 | guard !host.isEmpty else { 80 | throw ConfigurationErrors.NoHostProvided 81 | } 82 | 83 | guard let url = NSURL(string: host) else { 84 | throw ConfigurationErrors.InvalidHostProvided(host) 85 | } 86 | 87 | guard url.scheme.isEmpty || url.scheme == "https" else { 88 | let errMsg = "Xcode Server generally uses https, please double check your hostname" 89 | Log.error(errMsg) 90 | throw ConfigurationErrors.InvalidSchemeProvided(errMsg) 91 | } 92 | 93 | // validate if host is a valid URL 94 | if url.scheme.isEmpty { 95 | // exted host with https scheme 96 | host = "https://" + host 97 | } 98 | 99 | self.host = host 100 | self.user = user 101 | self.password = password 102 | self.id = id ?? Ref.new() 103 | } 104 | 105 | /** 106 | Initializes a server configuration with the provided `json`. 107 | - parameter json: `NSDictionary` containing the `XcodeServerConfig` «configuration». 108 | 109 | - returns: A fully initialized `XcodeServerConfig` instance. 110 | 111 | - throws: 112 | - `NoHostProvided`: When no `host` key was found on the provided `json` dictionary. 113 | - `InvalidHostProvided`: When the host provided doesn't produce a valid `URL` 114 | - `InvalidSchemeProvided`: When the provided scheme is not `HTTPS` 115 | */ 116 | public init(json: NSDictionary) throws { 117 | 118 | guard let host = json.optionalStringForKey(Keys.Host) else { 119 | throw ConfigurationErrors.NoHostProvided 120 | } 121 | 122 | let user = json.optionalStringForKey(Keys.User) 123 | let password = json.optionalStringForKey(Keys.Password) 124 | let id = json.optionalStringForKey(Keys.Id) 125 | try self.init(host: host, user: user, password: password, id: id) 126 | } 127 | } -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServerEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerEndpoints.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BuildaUtils 11 | 12 | public class XcodeServerEndpoints { 13 | 14 | enum Endpoint { 15 | case Bots 16 | case CancelIntegration 17 | case Commits 18 | case Devices 19 | case Hostname 20 | case Integrations 21 | case Issues 22 | case LiveUpdates 23 | case Login 24 | case Logout 25 | case Platforms 26 | case Repositories 27 | case SCM_Branches 28 | case UserCanCreateBots 29 | case Versions 30 | case Toolchains 31 | } 32 | 33 | let serverConfig: XcodeServerConfig 34 | 35 | /** 36 | Designated initializer. 37 | 38 | - parameter serverConfig: Object of XcodeServerConfig class 39 | 40 | - returns: Initialized object of XcodeServer endpoints 41 | */ 42 | public init(serverConfig: XcodeServerConfig) { 43 | self.serverConfig = serverConfig 44 | } 45 | 46 | func endpointURL(endpoint: Endpoint, params: [String: String]? = nil) -> String { 47 | 48 | let base = "/api" 49 | 50 | switch endpoint { 51 | 52 | case .Bots: 53 | 54 | let bots = "\(base)/bots" 55 | if let bot = params?["bot"] { 56 | let bot = "\(bots)/\(bot)" 57 | if 58 | let rev = params?["rev"], 59 | let method = params?["method"] where method == "DELETE" 60 | { 61 | let rev = "\(bot)/\(rev)" 62 | return rev 63 | } 64 | return bot 65 | } 66 | return bots 67 | 68 | case .Integrations: 69 | 70 | if let _ = params?["bot"] { 71 | //gets a list of integrations for this bot 72 | let bots = self.endpointURL(.Bots, params: params) 73 | return "\(bots)/integrations" 74 | } 75 | 76 | let integrations = "\(base)/integrations" 77 | if let integration = params?["integration"] { 78 | 79 | let oneIntegration = "\(integrations)/\(integration)" 80 | return oneIntegration 81 | } 82 | return integrations 83 | 84 | case .CancelIntegration: 85 | 86 | let integration = self.endpointURL(.Integrations, params: params) 87 | let cancel = "\(integration)/cancel" 88 | return cancel 89 | 90 | case .Devices: 91 | 92 | let devices = "\(base)/devices" 93 | return devices 94 | 95 | case .UserCanCreateBots: 96 | 97 | let users = "\(base)/auth/isBotCreator" 98 | return users 99 | 100 | case .Login: 101 | 102 | let login = "\(base)/auth/login" 103 | return login 104 | 105 | case .Logout: 106 | 107 | let logout = "\(base)/auth/logout" 108 | return logout 109 | 110 | case .Platforms: 111 | 112 | let platforms = "\(base)/platforms" 113 | return platforms 114 | 115 | case .SCM_Branches: 116 | 117 | let branches = "\(base)/scm/branches" 118 | return branches 119 | 120 | case .Repositories: 121 | 122 | let repositories = "\(base)/repositories" 123 | return repositories 124 | 125 | case .Commits: 126 | 127 | let integration = self.endpointURL(.Integrations, params: params) 128 | let commits = "\(integration)/commits" 129 | return commits 130 | 131 | case .Issues: 132 | 133 | let integration = self.endpointURL(.Integrations, params: params) 134 | let issues = "\(integration)/issues" 135 | return issues 136 | 137 | case .LiveUpdates: 138 | 139 | let base = "/xcode/internal/socket.io/1" 140 | if let pollId = params?["poll_id"] { 141 | return "\(base)/xhr-polling/\(pollId)" 142 | } 143 | return base 144 | 145 | case .Hostname: 146 | 147 | let hostname = "\(base)/hostname" 148 | return hostname 149 | 150 | case .Versions: 151 | let versions = "\(base)/versions" 152 | return versions 153 | 154 | case .Toolchains: 155 | let toolchains = "\(base)/toolchains" 156 | return toolchains 157 | } 158 | } 159 | 160 | /** 161 | Builder method for URlrequests based on input parameters. 162 | 163 | - parameter method: HTTP method used for request (GET, POST etc.) 164 | - parameter endpoint: Endpoint object 165 | - parameter params: URL params (default is nil) 166 | - parameter query: Query parameters (default is nil) 167 | - parameter body: Request's body (default is nil) 168 | - parameter doBasicAuth: Requirement of authorization (default is true) 169 | 170 | - returns: NSMutableRequest or nil if wrong URL was provided 171 | */ 172 | func createRequest(method: HTTP.Method, endpoint: Endpoint, params: [String : String]? = nil, query: [String : String]? = nil, body: NSDictionary? = nil, doBasicAuth auth: Bool = true, portOverride: Int? = nil) -> NSMutableURLRequest? { 173 | var allParams = [ 174 | "method": method.rawValue 175 | ] 176 | 177 | //merge the two params 178 | if let params = params { 179 | for (key, value) in params { 180 | allParams[key] = value 181 | } 182 | } 183 | 184 | let port = portOverride ?? self.serverConfig.port 185 | let endpointURL = self.endpointURL(endpoint, params: allParams) 186 | let queryString = HTTP.stringForQuery(query) 187 | let wholePath = "\(self.serverConfig.host):\(port)\(endpointURL)\(queryString)" 188 | 189 | guard let url = NSURL(string: wholePath) else { 190 | return nil 191 | } 192 | 193 | let request = NSMutableURLRequest(URL: url) 194 | 195 | request.HTTPMethod = method.rawValue 196 | 197 | if auth { 198 | //add authorization header 199 | let user = self.serverConfig.user ?? "" 200 | let password = self.serverConfig.password ?? "" 201 | let plainString = "\(user):\(password)" as NSString 202 | let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding) 203 | let base64String = plainData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) 204 | request.setValue("Basic \(base64String!)", forHTTPHeaderField: "Authorization") 205 | } 206 | 207 | if let body = body { 208 | do { 209 | let data = try NSJSONSerialization.dataWithJSONObject(body, options: .PrettyPrinted) 210 | request.HTTPBody = data 211 | request.setValue("application/json", forHTTPHeaderField: "Content-Type") 212 | } catch { 213 | let error = error as NSError 214 | Log.error("Parsing error \(error.description)") 215 | return nil 216 | } 217 | } 218 | 219 | return request 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServerEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerEntity.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol XcodeRead { 12 | init(json: NSDictionary) throws 13 | } 14 | 15 | public protocol XcodeWrite { 16 | func dictionarify() -> NSDictionary 17 | } 18 | 19 | public class XcodeServerEntity : XcodeRead, XcodeWrite { 20 | 21 | public let id: String! 22 | public let rev: String! 23 | public let tinyID: String! 24 | public let docType: String! 25 | 26 | //when created from json, let's save the original data here. 27 | public let originalJSON: NSDictionary? 28 | 29 | //initializer which takes a dictionary and fills in values for recognized keys 30 | public required init(json: NSDictionary) throws { 31 | 32 | self.id = json.optionalStringForKey("_id") 33 | self.rev = json.optionalStringForKey("_rev") 34 | self.tinyID = json.optionalStringForKey("tinyID") 35 | self.docType = json.optionalStringForKey("doc_type") 36 | self.originalJSON = json.copy() as? NSDictionary 37 | } 38 | 39 | public init() { 40 | self.id = nil 41 | self.rev = nil 42 | self.tinyID = nil 43 | self.docType = nil 44 | self.originalJSON = nil 45 | } 46 | 47 | public func dictionarify() -> NSDictionary { 48 | assertionFailure("Must be overriden by subclasses that wish to dictionarify their data") 49 | return NSDictionary() 50 | } 51 | 52 | public class func optional(json: NSDictionary?) throws -> T? { 53 | if let json = json { 54 | return try T(json: json) 55 | } 56 | return nil 57 | } 58 | } 59 | 60 | //parse an array of dictionaries into an array of parsed entities 61 | public func XcodeServerArray(jsonArray: NSArray) throws -> [T] { 62 | 63 | let array = jsonArray as! [NSDictionary] 64 | let parsed = try array.map { (json: NSDictionary) -> (T) in 65 | return try T(json: json) 66 | } 67 | return parsed 68 | } 69 | 70 | -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServerFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerFactory.swift 3 | // Buildasaur 4 | // 5 | // Created by Honza Dvorsky on 14/12/2014. 6 | // Copyright (c) 2014 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class XcodeServerFactory { 12 | 13 | public class func server(config: XcodeServerConfig) -> XcodeServer { 14 | 15 | let endpoints = XcodeServerEndpoints(serverConfig: config) 16 | let server = XcodeServer(config: config, endpoints: endpoints) 17 | 18 | return server 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /XcodeServerSDK/XcodeServerSDK.h: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerSDK.h 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 11/06/2015. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | //! Project version number for XcodeServerSDK. 12 | FOUNDATION_EXPORT double XcodeServerSDKVersionNumber; 13 | 14 | //! Project version string for XcodeServerSDK. 15 | FOUNDATION_EXPORT const unsigned char XcodeServerSDKVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import -------------------------------------------------------------------------------- /XcodeServerSDKTests/BotConfigurationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BotConfigurationTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 24.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class BotConfigurationTests: XCTestCase { 13 | 14 | func testCleaningPolicyToString() { 15 | var policy: BotConfiguration.CleaningPolicy 16 | 17 | policy = .Never 18 | XCTAssertEqual(policy.toString(), "Never") 19 | 20 | policy = .Always 21 | XCTAssertEqual(policy.toString(), "Always") 22 | 23 | policy = .Once_a_Day 24 | XCTAssertEqual(policy.toString(), "Once a day (first build)") 25 | 26 | policy = .Once_a_Week 27 | XCTAssertEqual(policy.toString(), "Once a week (first build)") 28 | } 29 | 30 | func testDeviceFilterToString() { 31 | 32 | var filter: DeviceFilter.FilterType 33 | 34 | filter = .AllAvailableDevicesAndSimulators 35 | XCTAssertEqual(filter.toString(), "All Available Devices and Simulators") 36 | 37 | filter = .AllDevices 38 | XCTAssertEqual(filter.toString(), "All Devices") 39 | 40 | filter = .AllSimulators 41 | XCTAssertEqual(filter.toString(), "All Simulators") 42 | 43 | filter = .SelectedDevicesAndSimulators 44 | XCTAssertEqual(filter.toString(), "Selected Devices and Simulators") 45 | } 46 | 47 | func testAvailableFiltersForPlatform() { 48 | 49 | XCTAssertEqual(DeviceFilter.FilterType.availableFiltersForPlatform(.iOS), [ 50 | .AllAvailableDevicesAndSimulators, 51 | .AllDevices, 52 | .AllSimulators, 53 | .SelectedDevicesAndSimulators 54 | ]) 55 | 56 | XCTAssertEqual(DeviceFilter.FilterType.availableFiltersForPlatform(.OSX), [ 57 | .AllAvailableDevicesAndSimulators 58 | ]) 59 | 60 | XCTAssertEqual(DeviceFilter.FilterType.availableFiltersForPlatform(.watchOS), [ 61 | .AllAvailableDevicesAndSimulators 62 | ]) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/BotParsingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BotParsingTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 17/06/15. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import XcodeServerSDK 12 | 13 | class BotParsingTests: XCTestCase { 14 | 15 | //MARK: shared stuff 16 | 17 | func testParseOSXBot() { 18 | 19 | let exp = self.expectationWithDescription("Network") 20 | let server = self.getRecordingXcodeServer("osx_bot") 21 | 22 | server.getBot("963bc95f1c1a56f69f3392b4aa03302f") { (bots, error) in 23 | exp.fulfill() 24 | } 25 | 26 | self.waitForExpectationsWithTimeout(10, handler: nil) 27 | } 28 | 29 | func testShared() throws { 30 | 31 | let bot = try self.botInCassetteWithName("osx_bot") 32 | 33 | XCTAssertEqual(bot.id, "963bc95f1c1a56f69f3392b4aa03302f") 34 | XCTAssertEqual(bot.rev, "10-117b73f201ea103229a2e8cd26a01845") 35 | XCTAssertEqual(bot.tinyID, "4807518") 36 | } 37 | 38 | //MARK: bot 39 | 40 | func testDeleteBot() { 41 | 42 | let exp = self.expectationWithDescription("Network") 43 | let server = self.getRecordingXcodeServer("bot_deletion") 44 | 45 | server.deleteBot("5f79bd4f6eb05c645336606cc790ccd7", revision: "5-4939a8253e8243107a0d4733490fd36e") { (success, error) in 46 | XCTAssertNil(error) 47 | XCTAssertTrue(success) 48 | exp.fulfill() 49 | } 50 | self.waitForExpectationsWithTimeout(10, handler: nil) 51 | } 52 | 53 | // func testBots() { 54 | // 55 | // let exp = self.expectationWithDescription("Network") 56 | // let config = try! XcodeServerConfig(host: "192.168.1.64") 57 | // let server = XcodeServerFactory.server(config) 58 | // server.getBots { (bots, error) -> () in 59 | // exp.fulfill() 60 | // } 61 | // self.waitForExpectationsWithTimeout(10, handler: nil) 62 | // } 63 | // func testBot() { 64 | // 65 | // let bot = self.botInFileWithName("bot_mac_xcode6") 66 | // 67 | // XCTAssertEqual(bot.name, "Builda Archiver") 68 | // XCTAssertEqual(bot.integrationsCount, 7) 69 | // } 70 | // 71 | // //MARK: configuration 72 | // func testConfiguration() { 73 | // 74 | // let configuration = self.configurationFromBotWithName("bot_mac_xcode6") 75 | // 76 | // XCTAssertEqual(configuration.test, true) 77 | // XCTAssertEqual(configuration.analyze, true) 78 | // XCTAssertEqual(configuration.archive, true) 79 | // XCTAssertEqual(configuration.schemeName, "Buildasaur") 80 | // } 81 | 82 | //MARK: cleaning policy 83 | // func testCleanPolicy() { 84 | // //TODO: will be a whole set of tests, add all possible cases 85 | // } 86 | // 87 | // //MARK: test schedule 88 | // func testSchedule() { 89 | // //TODO: will be a whole set of tests, add all possible cases 90 | // } 91 | // 92 | // //MARK: blueprint (good luck with this one) 93 | // func testBlueprint() { 94 | // //TODO: will be a whole set of tests, add all possible cases 95 | // } 96 | // 97 | // //MARK: destination type 98 | // func testDestinationType() { 99 | // //TODO: will be a whole set of tests, add all possible cases 100 | // } 101 | // 102 | // //MARK: triggers 103 | // func testTriggers() { 104 | // //TODO: will be a whole set of tests, add all possible cases 105 | // } 106 | // 107 | // //MARK: testing device ids (on both Xcode 6 and 7 formats!!!) 108 | // func testTestingDeviceIds() { 109 | // //TODO: will be a whole set of tests, add all possible cases 110 | // } 111 | } 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/Casettes/bot_deletion.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactions" : [ 3 | { 4 | "recorded_at" : 1437766154.706348, 5 | "response" : { 6 | "body" : "", 7 | "body_format" : "base64_string", 8 | "status" : 204, 9 | "url" : "https:\/\/127.0.0.1:20343\/api\/bots\/5f79bd4f6eb05c645336606cc790ccd7\/5-4939a8253e8243107a0d4733490fd36e", 10 | "headers" : { 11 | "Etag" : "W\/\"a-b541a50d\"", 12 | "X-XCSAPIVersion" : "6", 13 | "X-XCSResponse-Status-Title" : "No Content", 14 | "Keep-Alive" : "timeout=5; max=100", 15 | "Connection" : "keep-alive", 16 | "Date" : "Fri, 24 Jul 2015 19:29:13 GMT" 17 | } 18 | }, 19 | "request" : { 20 | "method" : "DELETE", 21 | "url" : "https:\/\/127.0.0.1:20343\/api\/bots\/5f79bd4f6eb05c645336606cc790ccd7\/5-4939a8253e8243107a0d4733490fd36e", 22 | "headers" : { 23 | "Authorization" : "Basic saGVzdGskbfluOfdsfmJJleWbGRz" 24 | } 25 | } 26 | } 27 | ], 28 | "name" : "bot_deletion" 29 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Casettes/get_platforms.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactions" : [ 3 | { 4 | "recorded_at" : 1436788154.310033, 5 | "response" : { 6 | "body" : { 7 | "count" : 3, 8 | "results" : [ 9 | { 10 | "_id" : "1ad0e8785cacca73d980cdb236002dc6", 11 | "displayName" : "OS X", 12 | "_rev" : "3-4fab2e1d78397a4b10b23493de9e333c", 13 | "tinyID" : "67F229A", 14 | "identifier" : "com.apple.platform.macosx", 15 | "buildNumber" : "15A216g", 16 | "version" : "1.1", 17 | "doc_type" : "platform" 18 | }, 19 | { 20 | "_id" : "1ad0e8785cacca73d980cdb236002f3d", 21 | "displayName" : "Watch OS", 22 | "_rev" : "3-ed75f59aab9305de47f062a3349deb91", 23 | "tinyID" : "87C0EB5", 24 | "simulatorIdentifier" : "com.apple.platform.watchsimulator", 25 | "identifier" : "com.apple.platform.watchos", 26 | "buildNumber" : "13S5293f", 27 | "version" : "2.0", 28 | "doc_type" : "platform" 29 | }, 30 | { 31 | "_id" : "1ad0e8785cacca73d980cdb236003378", 32 | "displayName" : "iOS", 33 | "_rev" : "3-e059eb79237844f062c28ea5a723aa06", 34 | "tinyID" : "80A6C18", 35 | "simulatorIdentifier" : "com.apple.platform.iphonesimulator", 36 | "identifier" : "com.apple.platform.iphoneos", 37 | "buildNumber" : "13A4293g", 38 | "version" : "9.0", 39 | "doc_type" : "platform" 40 | } 41 | ] 42 | }, 43 | "body_format" : "json", 44 | "status" : 200, 45 | "url" : "https:\/\/127.0.0.1:20343\/api\/platforms", 46 | "headers" : { 47 | "X-XCSResultsList" : "true", 48 | "Content-Type" : "application\/json", 49 | "Connection" : "keep-alive", 50 | "Transfer-Encoding" : "Identity", 51 | "Date" : "Mon, 13 Jul 2015 11:49:14 GMT", 52 | "Content-Encoding" : "gzip", 53 | "X-XCSResponse-Status-Title" : "OK", 54 | "Keep-Alive" : "timeout=5; max=100", 55 | "X-XCSAPIVersion" : "6", 56 | "Vary" : "Accept-Encoding" 57 | } 58 | }, 59 | "request" : { 60 | "method" : "GET", 61 | "url" : "https:\/\/127.0.0.1:20343\/api\/platforms", 62 | "headers" : { 63 | "Authorization" : "Basic SUNhbkNyZWF0ZUJvdHM6c3VwZXJTZWNyM3Q=" 64 | } 65 | } 66 | } 67 | ], 68 | "name" : "get_platforms" 69 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Casettes/get_repositories.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactions" : [ 3 | { 4 | "recorded_at" : 1435837811.718786, 5 | "response" : { 6 | "body" : { 7 | "count" : 2, 8 | "results" : [ 9 | { 10 | "posixPermissions" : 2, 11 | "httpAccessType" : 1, 12 | "writeAccessExternalIDs" : [ 13 | 14 | ], 15 | "readAccessExternalIDs" : [ 16 | 17 | ], 18 | "name" : "Test1" 19 | }, 20 | { 21 | "posixPermissions" : 0, 22 | "httpAccessType" : 1, 23 | "writeAccessExternalIDs" : [ 24 | "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050", 25 | "D024C308-CEBE-4E72-BE40-E1E4115F38F9" 26 | ], 27 | "readAccessExternalIDs" : [ 28 | 29 | ], 30 | "name" : "Test2" 31 | } 32 | ] 33 | }, 34 | "body_format" : "json", 35 | "status" : 200, 36 | "url" : "https:\/\/127.0.0.1:20343\/api\/repositories", 37 | "headers" : { 38 | "X-XCSResultsList" : "true", 39 | "Set-Cookie" : "session=s%3A7X-FU2POL2F2HJ7gUU3MnPWarkx-zGmg.xW86z02rn7PdQgUzaZYkixSBKrGoi5bCinPpIN3MKs0; Path=\/; Expires=Fri, 03 Jul 2015 11:50:11 GMT; HttpOnly; Secure", 40 | "Content-Type" : "application\/json", 41 | "Connection" : "keep-alive", 42 | "Transfer-Encoding" : "Identity", 43 | "Date" : "Thu, 02 Jul 2015 11:50:11 GMT", 44 | "Content-Encoding" : "gzip", 45 | "X-XCSResponse-Status-Title" : "OK", 46 | "Keep-Alive" : "timeout=5; max=100", 47 | "X-XCSAPIVersion" : "6", 48 | "Vary" : "Accept-Encoding" 49 | } 50 | }, 51 | "request" : { 52 | "method" : "GET", 53 | "url" : "https:\/\/127.0.0.1:20343\/api\/repositories", 54 | "headers" : { 55 | "Authorization" : "Basic SUNhbkNyZWF0ZUJvdHM6c3VwZXJTZWNyM3Q=" 56 | } 57 | } 58 | } 59 | ], 60 | "name" : "get_repositories" 61 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Casettes/get_toolchains.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactions" : [ 3 | { 4 | "recorded_at" : 1436788154.310033, 5 | "response" : { 6 | "body" : { 7 | "count" : 2, 8 | "results" : [ 9 | { 10 | "_id":"ed7790b0905576627eda64fcf600ae9f", 11 | "_rev":"3-bcc83acf9c184e22a5d1b66700d38dd9", 12 | "path":"/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a.xctoolchain", 13 | "displayName":"Xcode Swift DEVELOPMENT Snapshot 2016-04-12 (a)", 14 | "identifier":"org.swift.3020160412a", 15 | "signatureVerified":true, 16 | "doc_type":"toolchain", 17 | "tinyID":"D759F95" 18 | }, 19 | { 20 | "_id":"ed7790b0905576627eda64fcf6036a67", 21 | "_rev":"3-57e317f69d7decb33c706274c811ec9b", 22 | "path":"/Library/Developer/Toolchains/swift-2.2.1-SNAPSHOT-2016-04-12-a.xctoolchain", 23 | "displayName":"Xcode Swift 2.2.1 Snapshot 2016-04-12 (a)", 24 | "identifier":"org.swift.22120160412a", 25 | "signatureVerified":true, 26 | "doc_type":"toolchain", 27 | "tinyID":"48A44E8" 28 | } 29 | ] 30 | }, 31 | "body_format" : "json", 32 | "status" : 200, 33 | "url" : "https:\/\/127.0.0.1:20343\/api\/toolchains", 34 | "headers" : { 35 | "X-XCSResultsList" : "true", 36 | "Content-Type" : "application\/json", 37 | "Connection" : "keep-alive", 38 | "Transfer-Encoding" : "Identity", 39 | "Date" : "Mon, 13 Jul 2015 11:49:14 GMT", 40 | "Content-Encoding" : "gzip", 41 | "X-XCSResponse-Status-Title" : "OK", 42 | "Keep-Alive" : "timeout=5; max=100", 43 | "X-XCSAPIVersion" : "6", 44 | "Vary" : "Accept-Encoding" 45 | } 46 | }, 47 | "request" : { 48 | "method" : "GET", 49 | "url" : "https:\/\/127.0.0.1:20343\/api\/toolchains", 50 | "headers" : { 51 | "Authorization" : "Basic SUNhbkNyZWF0ZUJvdHM6c3VwZXJTZWNyM3Q=" 52 | } 53 | } 54 | } 55 | ], 56 | "name" : "get_toolchains" 57 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Casettes/hostname.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactions" : [ 3 | { 4 | "recorded_at" : 1444476701.622059, 5 | "response" : { 6 | "body" : { 7 | "hostname" : "honzadvysmbpr14.home" 8 | }, 9 | "body_format" : "json", 10 | "status" : 200, 11 | "url" : "https:\/\/127.0.0.1:20343\/api\/hostname", 12 | "headers" : { 13 | "Connection" : "keep-alive", 14 | "Content-Type" : "application\/json", 15 | "Set-Cookie" : "session=s%3AnnpNukOtB_DB3TD6HtlEZRfDSuFAvk8_.XWcOAW2wfgWTaQXWtfc%2BSGIhWI75cUJLiSdCw6vYHEM; Path=\/; Expires=Sun, 11 Oct 2015 11:31:41 GMT; HttpOnly; Secure", 16 | "Transfer-Encoding" : "Identity", 17 | "Date" : "Sat, 10 Oct 2015 11:31:41 GMT", 18 | "Content-Encoding" : "gzip", 19 | "Keep-Alive" : "timeout=5; max=100", 20 | "X-XCSAPIVersion" : "6", 21 | "Vary" : "Accept-Encoding" 22 | } 23 | }, 24 | "request" : { 25 | "method" : "GET", 26 | "url" : "https:\/\/127.0.0.1:20343\/api\/hostname", 27 | "headers" : { 28 | "Authorization" : "Basic SUNhbkNyZWF0ZUJvdHM6c3VwZXJTZWNyM3Q=" 29 | } 30 | } 31 | } 32 | ], 33 | "name" : "hostname" 34 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/ContributorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContributorTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 21/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeServerSDK 11 | 12 | class ContributorTests: XCTestCase { 13 | 14 | let singleEmailContributor = [ 15 | kContributorName: "Foo Bar", 16 | kContributorDisplayName: "Foo", 17 | kContributorEmails: [ 18 | "foo@bar.com" 19 | ] 20 | ] 21 | 22 | let multiEmailContributor = [ 23 | kContributorName: "Baz Bar", 24 | kContributorDisplayName: "Baz", 25 | kContributorEmails: [ 26 | "baz@bar.com", 27 | "baz@example.com" 28 | ] 29 | ] 30 | 31 | var singleEmail: Contributor! 32 | var multiEmail: Contributor! 33 | 34 | override func setUp() { 35 | super.setUp() 36 | 37 | singleEmail = try! Contributor(json: singleEmailContributor) 38 | multiEmail = try! Contributor(json: multiEmailContributor) 39 | } 40 | 41 | // MARK: Test cases 42 | func testInitialization() { 43 | XCTAssertEqual(singleEmail.name, "Foo Bar") 44 | XCTAssertEqual(singleEmail.emails.count, 1) 45 | 46 | XCTAssertEqual(multiEmail.name, "Baz Bar") 47 | XCTAssertEqual(multiEmail.emails.count, 2) 48 | } 49 | 50 | // MARK: Dictionarify 51 | func testDictionarify() { 52 | XCTAssertEqual(singleEmail.dictionarify(), singleEmailContributor) 53 | XCTAssertEqual(multiEmail.dictionarify(), multiEmailContributor) 54 | } 55 | 56 | func testDescription() { 57 | XCTAssertEqual(multiEmail.description(), "Baz [baz@bar.com]") 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/create_ios_bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": { 3 | "name": "E4F21264-9C46-4B10-8613-244E48958233" 4 | }, 5 | "configuration": { 6 | "builtFromClean": 0, 7 | "periodicScheduleInterval": 1, 8 | "codeCoveragePreference": 2, 9 | "performsTestAction": true, 10 | "triggers": [], 11 | "performsAnalyzeAction": true, 12 | "schemeName": "XcodeServerSDK - iOS", 13 | "exportsProductFromArchive": true, 14 | "testingDeviceIDs": [], 15 | "deviceSpecification": { 16 | "filters": [ 17 | { 18 | "platform": { 19 | "_id": "a85553a5b26a7c1a4998f3b237000da9", 20 | "displayName": "iOS", 21 | "_rev": "12-162b2b9d57b73b66724426eea53bfcaf", 22 | "simulatorIdentifier": "com.apple.platform.iphonesimulator", 23 | "identifier": "com.apple.platform.iphoneos", 24 | "buildNumber": "13A4280e", 25 | "version": "9.0" 26 | }, 27 | "filterType": 0, 28 | "architectureType": 0 29 | } 30 | ], 31 | "deviceIdentifiers": [] 32 | }, 33 | "weeklyScheduleDay": 0, 34 | "minutesAfterHourToIntegrate": 0, 35 | "scheduleType": 1, 36 | "hourOfIntegration": 0, 37 | "performsArchiveAction": true, 38 | "sourceControlBlueprint": { 39 | "DVTSourceControlWorkspaceBlueprintLocationsKey": { 40 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 41 | "DVTSourceControlBranchIdentifierKey": "hd/tested_devices_xcode_7", 42 | "DVTSourceControlBranchOptionsKey": 156, 43 | "DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch" 44 | } 45 | }, 46 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 47 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": { 48 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 49 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey": "git", 50 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": "MY_PRIVATE_KEY", 51 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlSSHKeysAuthenticationStrategy", 52 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey": "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFETUlyMHIyK3pt\r\nZ2JIajhBcnczRDQzZ0N5WURMc3p6ZWtIdTZZK0FKd29xZC8venFXU1FqamxlMkRt\r\nSmFueXNORnRhbFU3U005M2dtYXEzYTI4YjM3aWZnRmMrMXlhSDRNOC9EUk1yaHRR\r\ndk9JSEhYcWNzeUpwU0RWRkl3YjVxRm5LblNGaW96NGdSTkV0R1VOWWdCVTNYNHRs\r\nQzNMQkV1RmZhdS9RVXZZOEY5NmdMS3BGMTF0ZHdaanlDREN5ZWtIT2JaeWU2RS9R\r\nelRKZXppWjF5RXdHcXdvS3Q1NlpoZ1JHNG5yRHMwaEU2dDYxUG90WkdiMEpSbVRm\r\nYThFM05Mckp5ZklreS9DK2hpQ2tsSmZRelZndGk2MWtwMDJuZFN1ODlHK3dBY0xJ\r\nUjJ2Qzk2TndWRW9pZG1MTnN0bXlrMlhEbG1jVzB2Z0ZHVmptNWx2ZkljWVYgaG9u\r\nemFAc3dpZnRrZXkubmV0Cg==", 53 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey": "" 54 | } 55 | }, 56 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": { 57 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": 0 58 | }, 59 | "DVTSourceControlWorkspaceBlueprintIdentifierKey": "B67842EF-128D-4AB7-A3C7-7E560085550A", 60 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": { 61 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": "XcodeServerSDK/" 62 | }, 63 | "DVTSourceControlWorkspaceBlueprintNameKey": "XcodeServerSDK", 64 | "DVTSourceControlWorkspaceBlueprintVersion": 203, 65 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "XcodeServerSDK.xcworkspace", 66 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [ 67 | { 68 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git", 69 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git@github.com:czechboy0/XcodeServerSDK.git", 70 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustedCertFingerprintKey": "1627ACA576282D36631B564DEBDFA648", 71 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 72 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustSelfSignedCertKey": true 73 | } 74 | ] 75 | }, 76 | "testingDestinationType": 0 77 | }, 78 | "requiresUpgrade": false, 79 | "name": "Default iOS Bot", 80 | "type": 1 81 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/create_osx_bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": { 3 | "name": "2FDD3BEB-D750-47EE-B386-07EA9BABA98B" 4 | }, 5 | "configuration": { 6 | "builtFromClean": 0, 7 | "periodicScheduleInterval": 1, 8 | "codeCoveragePreference": 2, 9 | "performsTestAction": true, 10 | "triggers": [ 11 | { 12 | "phase": 1, 13 | "scriptBody": "echo \"Run before\"", 14 | "type": 1, 15 | "name": "Run Script" 16 | }, 17 | { 18 | "phase": 2, 19 | "scriptBody": "echo \"Run after\"", 20 | "type": 1, 21 | "name": "Run Script", 22 | "conditions": { 23 | "status": 2, 24 | "onWarnings": true, 25 | "onBuildErrors": true, 26 | "onInternalErrors": true, 27 | "onAnalyzerWarnings": true, 28 | "onFailingTests": true, 29 | "onSuccess": true 30 | } 31 | }, 32 | { 33 | "phase": 2, 34 | "scriptBody": "", 35 | "type": 2, 36 | "name": "Send Email Notification", 37 | "conditions": { 38 | "status": 2, 39 | "onWarnings": true, 40 | "onBuildErrors": true, 41 | "onInternalErrors": true, 42 | "onAnalyzerWarnings": true, 43 | "onFailingTests": true, 44 | "onSuccess": false 45 | }, 46 | "emailConfiguration": { 47 | "includeCommitMessages": true, 48 | "additionalRecipients": [ 49 | "email@committers.yo" 50 | ], 51 | "emailCommitters": true, 52 | "scmOptions": { 53 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": true 54 | }, 55 | "includeIssueDetails": true 56 | } 57 | } 58 | ], 59 | "performsAnalyzeAction": true, 60 | "schemeName": "XcodeServerSDK - OS X", 61 | "exportsProductFromArchive": true, 62 | "testingDeviceIDs": [], 63 | "deviceSpecification": { 64 | "filters": [ 65 | { 66 | "platform": { 67 | "buildNumber": "15A204f", 68 | "_id": "a85553a5b26a7c1a4998f3b237000b18", 69 | "_rev": "12-b005bffe3337cfe101fd597c300b0208", 70 | "displayName": "OS X", 71 | "identifier": "com.apple.platform.macosx", 72 | "version": "1.1" 73 | }, 74 | "filterType": 0, 75 | "architectureType": 1 76 | } 77 | ], 78 | "deviceIdentifiers": [] 79 | }, 80 | "weeklyScheduleDay": 0, 81 | "minutesAfterHourToIntegrate": 0, 82 | "scheduleType": 1, 83 | "hourOfIntegration": 0, 84 | "performsArchiveAction": true, 85 | "sourceControlBlueprint": { 86 | "DVTSourceControlWorkspaceBlueprintLocationsKey": { 87 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 88 | "DVTSourceControlBranchIdentifierKey": "hd/tested_devices_xcode_7", 89 | "DVTSourceControlBranchOptionsKey": 156, 90 | "DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch" 91 | } 92 | }, 93 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 94 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": { 95 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 96 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey": "git", 97 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": "MY_PRIVATE_KEY", 98 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlSSHKeysAuthenticationStrategy", 99 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey": "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFETUlyMHIyK3pt\r\nZ2JIajhBcnczRDQzZ0N5WURMc3p6ZWtIdTZZK0FKd29xZC8venFXU1FqamxlMkRt\r\nSmFueXNORnRhbFU3U005M2dtYXEzYTI4YjM3aWZnRmMrMXlhSDRNOC9EUk1yaHRR\r\ndk9JSEhYcWNzeUpwU0RWRkl3YjVxRm5LblNGaW96NGdSTkV0R1VOWWdCVTNYNHRs\r\nQzNMQkV1RmZhdS9RVXZZOEY5NmdMS3BGMTF0ZHdaanlDREN5ZWtIT2JaeWU2RS9R\r\nelRKZXppWjF5RXdHcXdvS3Q1NlpoZ1JHNG5yRHMwaEU2dDYxUG90WkdiMEpSbVRm\r\nYThFM05Mckp5ZklreS9DK2hpQ2tsSmZRelZndGk2MWtwMDJuZFN1ODlHK3dBY0xJ\r\nUjJ2Qzk2TndWRW9pZG1MTnN0bXlrMlhEbG1jVzB2Z0ZHVmptNWx2ZkljWVYgaG9u\r\nemFAc3dpZnRrZXkubmV0Cg==", 100 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey": "" 101 | } 102 | }, 103 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": { 104 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": 0 105 | }, 106 | "DVTSourceControlWorkspaceBlueprintIdentifierKey": "04531A78-B36E-4E1E-B304-92BD53E2E173", 107 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": { 108 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": "XcodeServerSDK/" 109 | }, 110 | "DVTSourceControlWorkspaceBlueprintNameKey": "XcodeServerSDK", 111 | "DVTSourceControlWorkspaceBlueprintVersion": 203, 112 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "XcodeServerSDK.xcworkspace", 113 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [ 114 | { 115 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git", 116 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git@github.com:czechboy0/XcodeServerSDK.git", 117 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustedCertFingerprintKey": "1627ACA576282D36631B564DEBDFA648", 118 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 119 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustSelfSignedCertKey": true 120 | } 121 | ] 122 | }, 123 | "testingDestinationType": 7 124 | }, 125 | "requiresUpgrade": false, 126 | "name": "Default OS X Bot", 127 | "type": 1 128 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/create_watch_bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": { 3 | "name": "77F7F792-7D3B-499E-9D3B-FE10858AF8E3" 4 | }, 5 | "configuration": { 6 | "builtFromClean": 0, 7 | "periodicScheduleInterval": 1, 8 | "codeCoveragePreference": 2, 9 | "performsTestAction": false, 10 | "triggers": [], 11 | "performsAnalyzeAction": true, 12 | "schemeName": "XcodeServerSDK - watchOS", 13 | "exportsProductFromArchive": true, 14 | "testingDeviceIDs": [], 15 | "deviceSpecification": { 16 | "filters": [ 17 | { 18 | "platform": { 19 | "_id": "a85553a5b26a7c1a4998f3b237000c7d", 20 | "displayName": "watchOS", 21 | "_rev": "12-58b9b255374d71f362f830256dfa65e4", 22 | "simulatorIdentifier": "com.apple.platform.watchsimulator", 23 | "identifier": "com.apple.platform.watchos", 24 | "buildNumber": "13S5255c", 25 | "version": "2.0" 26 | }, 27 | "filterType": 0, 28 | "architectureType": 0 29 | } 30 | ], 31 | "deviceIdentifiers": [] 32 | }, 33 | "weeklyScheduleDay": 0, 34 | "minutesAfterHourToIntegrate": 0, 35 | "scheduleType": 1, 36 | "hourOfIntegration": 0, 37 | "performsArchiveAction": true, 38 | "sourceControlBlueprint": { 39 | "DVTSourceControlWorkspaceBlueprintLocationsKey": { 40 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 41 | "DVTSourceControlBranchIdentifierKey": "hd/tested_devices_xcode_7", 42 | "DVTSourceControlBranchOptionsKey": 156, 43 | "DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch" 44 | } 45 | }, 46 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 47 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": { 48 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 49 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey": "git", 50 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": "MY_PRIVATE_KEY", 51 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlSSHKeysAuthenticationStrategy", 52 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey": "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFETUlyMHIyK3pt\r\nZ2JIajhBcnczRDQzZ0N5WURMc3p6ZWtIdTZZK0FKd29xZC8venFXU1FqamxlMkRt\r\nSmFueXNORnRhbFU3U005M2dtYXEzYTI4YjM3aWZnRmMrMXlhSDRNOC9EUk1yaHRR\r\ndk9JSEhYcWNzeUpwU0RWRkl3YjVxRm5LblNGaW96NGdSTkV0R1VOWWdCVTNYNHRs\r\nQzNMQkV1RmZhdS9RVXZZOEY5NmdMS3BGMTF0ZHdaanlDREN5ZWtIT2JaeWU2RS9R\r\nelRKZXppWjF5RXdHcXdvS3Q1NlpoZ1JHNG5yRHMwaEU2dDYxUG90WkdiMEpSbVRm\r\nYThFM05Mckp5ZklreS9DK2hpQ2tsSmZRelZndGk2MWtwMDJuZFN1ODlHK3dBY0xJ\r\nUjJ2Qzk2TndWRW9pZG1MTnN0bXlrMlhEbG1jVzB2Z0ZHVmptNWx2ZkljWVYgaG9u\r\nemFAc3dpZnRrZXkubmV0Cg==", 53 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey": "" 54 | } 55 | }, 56 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": { 57 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": 0 58 | }, 59 | "DVTSourceControlWorkspaceBlueprintIdentifierKey": "D11746DF-6E40-49FF-9AAB-8FF4BFCF0082", 60 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": { 61 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": "XcodeServerSDK/" 62 | }, 63 | "DVTSourceControlWorkspaceBlueprintNameKey": "XcodeServerSDK", 64 | "DVTSourceControlWorkspaceBlueprintVersion": 203, 65 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "XcodeServerSDK.xcworkspace", 66 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [ 67 | { 68 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git", 69 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git@github.com:czechboy0/XcodeServerSDK.git", 70 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustedCertFingerprintKey": "1627ACA576282D36631B564DEBDFA648", 71 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 72 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustSelfSignedCertKey": true 73 | } 74 | ] 75 | }, 76 | "testingDestinationType": 0 77 | }, 78 | "requiresUpgrade": false, 79 | "name": "Default Watch Bot", 80 | "type": 1 81 | } 82 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/platforms.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 3, 3 | "results": [ 4 | { 5 | "_id": "a85553a5b26a7c1a4998f3b237000b18", 6 | "_rev": "12-b005bffe3337cfe101fd597c300b0208", 7 | "buildNumber": "15A204f", 8 | "displayName": "OS X", 9 | "identifier": "com.apple.platform.macosx", 10 | "version": "1.1", 11 | "doc_type": "platform", 12 | "tinyID": "0224D30" 13 | }, 14 | { 15 | "_id": "a85553a5b26a7c1a4998f3b237000c7d", 16 | "_rev": "12-58b9b255374d71f362f830256dfa65e4", 17 | "buildNumber": "13S5255c", 18 | "simulatorIdentifier": "com.apple.platform.watchsimulator", 19 | "displayName": "watchOS", 20 | "identifier": "com.apple.platform.watchos", 21 | "version": "2.0", 22 | "doc_type": "platform", 23 | "tinyID": "BA84EFD" 24 | }, 25 | { 26 | "_id": "a85553a5b26a7c1a4998f3b237000da9", 27 | "_rev": "12-162b2b9d57b73b66724426eea53bfcaf", 28 | "buildNumber": "13A4280e", 29 | "simulatorIdentifier": "com.apple.platform.iphonesimulator", 30 | "displayName": "iOS", 31 | "identifier": "com.apple.platform.iphoneos", 32 | "version": "9.0", 33 | "doc_type": "platform", 34 | "tinyID": "0273BAD" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/scm_branches_request_no_fingerprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintLocationsKey": { 3 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 4 | "DVTSourceControlBranchIdentifierKey": "hd/tested_devices_xcode_7", 5 | "DVTSourceControlBranchOptionsKey": 220, 6 | "DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch" 7 | } 8 | }, 9 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 10 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey": {}, 11 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": { 12 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 13 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey": "git", 14 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": "THIS_IS_MY_SECRET_PRIVATE_KEY_MUHAHA", 15 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlSSHKeysAuthenticationStrategy", 16 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey": "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFETUlyMHIyK3pt\r\nZ2JIajhBcnczRDQzZ0N5WURMc3p6ZWtIdTZZK0FKd29xZC8venFXU1FqamxlMkRt\r\nSmFueXNORnRhbFU3U005M2dtYXEzYTI4YjM3aWZnRmMrMXlhSDRNOC9EUk1yaHRR\r\ndk9JSEhYcWNzeUpwU0RWRkl3YjVxRm5LblNGaW96NGdSTkV0R1VOWWdCVTNYNHRs\r\nQzNMQkV1RmZhdS9RVXZZOEY5NmdMS3BGMTF0ZHdaanlDREN5ZWtIT2JaeWU2RS9R\r\nelRKZXppWjF5RXdHcXdvS3Q1NlpoZ1JHNG5yRHMwaEU2dDYxUG90WkdiMEpSbVRm\r\nYThFM05Mckp5ZklreS9DK2hpQ2tsSmZRelZndGk2MWtwMDJuZFN1ODlHK3dBY0xJ\r\nUjJ2Qzk2TndWRW9pZG1MTnN0bXlrMlhEbG1jVzB2Z0ZHVmptNWx2ZkljWVYgaG9u\r\nemFAc3dpZnRrZXkubmV0Cg==", 17 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey": "" 18 | } 19 | }, 20 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": { 21 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": 0 22 | }, 23 | "DVTSourceControlWorkspaceBlueprintIdentifierKey": "9520BFD3-FFF2-455B-B838-00306AAE916A", 24 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": { 25 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": "XcodeServerSDK/" 26 | }, 27 | "DVTSourceControlWorkspaceBlueprintNameKey": "XcodeServerSDK", 28 | "DVTSourceControlWorkspaceBlueprintVersion": 203, 29 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "XcodeServerSDK.xcworkspace", 30 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [ 31 | { 32 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git@github.com:czechboy0/XcodeServerSDK.git", 33 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git", 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/scm_branches_request_with_fingerprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintLocationsKey": { 3 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 4 | "DVTSourceControlBranchIdentifierKey": "hd/tested_devices_xcode_7", 5 | "DVTSourceControlBranchOptionsKey": 220, 6 | "DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch" 7 | } 8 | }, 9 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 10 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey": {}, 11 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": { 12 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": { 13 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryUsernameKey": "git", 14 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": "THIS_IS_MY_SECRET_PRIVATE_KEY_MUHAHA", 15 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlSSHKeysAuthenticationStrategy", 16 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPublicKeyDataKey": "c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFETUlyMHIyK3pt\r\nZ2JIajhBcnczRDQzZ0N5WURMc3p6ZWtIdTZZK0FKd29xZC8venFXU1FqamxlMkRt\r\nSmFueXNORnRhbFU3U005M2dtYXEzYTI4YjM3aWZnRmMrMXlhSDRNOC9EUk1yaHRR\r\ndk9JSEhYcWNzeUpwU0RWRkl3YjVxRm5LblNGaW96NGdSTkV0R1VOWWdCVTNYNHRs\r\nQzNMQkV1RmZhdS9RVXZZOEY5NmdMS3BGMTF0ZHdaanlDREN5ZWtIT2JaeWU2RS9R\r\nelRKZXppWjF5RXdHcXdvS3Q1NlpoZ1JHNG5yRHMwaEU2dDYxUG90WkdiMEpSbVRm\r\nYThFM05Mckp5ZklreS9DK2hpQ2tsSmZRelZndGk2MWtwMDJuZFN1ODlHK3dBY0xJ\r\nUjJ2Qzk2TndWRW9pZG1MTnN0bXlrMlhEbG1jVzB2Z0ZHVmptNWx2ZkljWVYgaG9u\r\nemFAc3dpZnRrZXkubmV0Cg==", 17 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryPasswordKey": "" 18 | } 19 | }, 20 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": { 21 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": 0 22 | }, 23 | "DVTSourceControlWorkspaceBlueprintIdentifierKey": "9520BFD3-FFF2-455B-B838-00306AAE916A", 24 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": { 25 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": "XcodeServerSDK/" 26 | }, 27 | "DVTSourceControlWorkspaceBlueprintNameKey": "XcodeServerSDK", 28 | "DVTSourceControlWorkspaceBlueprintVersion": 203, 29 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "XcodeServerSDK.xcworkspace", 30 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [ 31 | { 32 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git", 33 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git@github.com:czechboy0/XcodeServerSDK.git", 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustedCertFingerprintKey": "1627ACA576282D36631B564DEBDFA648", 35 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 36 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryTrustSelfSignedCertKey": true 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/scm_branches_response_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "repositoryErrors": [ 3 | { 4 | "error": { 5 | "code": -1004, 6 | "message": "The server SSH fingerprint failed to verify.", 7 | "underlyingError": "", 8 | "domain": "com.apple.dt.SourceControlErrorDomain", 9 | "fingerprint": "1627ACA576282D36631B564DEBDFA648" 10 | }, 11 | "repository": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/Data/scm_branches_response_success.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": { 3 | "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97": [ 4 | { 5 | "name": "buildasaur", 6 | "primary": false 7 | }, 8 | { 9 | "name": "hd/tested_devices_xcode_7", 10 | "primary": false 11 | }, 12 | { 13 | "name": "master", 14 | "primary": false 15 | }, 16 | { 17 | "name": "swift-2", 18 | "primary": true 19 | } 20 | ] 21 | } 22 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/DevicesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DevicesTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class DevicesTests: XCTestCase { 13 | 14 | let macMini = try! Device(json: [ 15 | "osVersion" : "10.11", 16 | "connected" : true, 17 | "simulator" : false, 18 | "modelCode" : "Macmini6,1", 19 | "deviceType" : "com.apple.mac", 20 | "modelName" : "Mac mini", 21 | "_id" : "1ad0e8785cacca73d980cdb23600383e", 22 | "modelUTI" : "com.apple.macmini-unibody-no-optical", 23 | "doc_type" : "device", 24 | "trusted" : true, 25 | "name" : "bozenka", 26 | "supported" : true, 27 | "processor" : "2,5 GHz Intel Core i5", 28 | "identifier" : "FFFFFFFF-0F3C-5893-AF8A-D9CFF068B82C-x86_64", 29 | "enabledForDevelopment" : true, 30 | "serialNumber" : "C07JT7H5DWYL", 31 | "platformIdentifier" : "com.apple.platform.macosx", 32 | "_rev" : "3-6a49ba8135f3f4ddcbaefe1b469f479c", 33 | "architecture" : "x86_64", 34 | "retina" : false, 35 | "isServer" : true, 36 | "tinyID" : "7DCD98A" 37 | ]) 38 | 39 | func testDictionarify() { 40 | let expected = [ "device_id": "1ad0e8785cacca73d980cdb23600383e" ] 41 | XCTAssertEqual(macMini.dictionarify(), expected) 42 | } 43 | 44 | func testGetDevices() { 45 | let expectation = self.expectationWithDescription("Get Devices") 46 | let server = self.getRecordingXcodeServer("get_devices") 47 | 48 | server.getDevices { (devices, error) -> () in 49 | XCTAssertNil(error) 50 | XCTAssertNotNil(devices) 51 | 52 | if let devices = devices { 53 | XCTAssertEqual(devices.count, 15, "There should be 15 devices") 54 | XCTAssertEqual(devices.filter { $0.platform == DevicePlatform.PlatformType.iOS }.count, 2, "There should be 2 real iPhones") 55 | XCTAssertEqual(devices.filter { $0.platform == DevicePlatform.PlatformType.OSX }.count, 2, "There should be 2 real Macs") 56 | XCTAssertEqual(devices.filter { $0.platform == DevicePlatform.PlatformType.iOS_Simulator }.count, 9, "There should be 9 iOS simulators") 57 | XCTAssertEqual(devices.filter { $0.platform == DevicePlatform.PlatformType.watchOS_Simulator }.count, 2, "There should be 2 watchOS simulators") 58 | XCTAssertEqual(devices.filter { $0.activeProxiedDevice != nil }.count, 2, "There should be 2 active proxied devices (watchOS)") 59 | } 60 | 61 | expectation.fulfill() 62 | } 63 | 64 | self.waitForExpectationsWithTimeout(10.0, handler: nil) 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/FileTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 22/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeServerSDK 11 | 12 | class FileTests: XCTestCase { 13 | 14 | let sampleAdded = [ 15 | "status": 1, 16 | "filePath": "File1.swift" 17 | ] 18 | 19 | let sampleOther = [ 20 | "status": 1024, 21 | "filePath": "File2.swift" 22 | ] 23 | 24 | // MARK: Initialization 25 | func testDictionaryInit() throws { 26 | var file = try File(json: sampleAdded) 27 | 28 | XCTAssertEqual(file.filePath, "File1.swift") 29 | XCTAssertEqual(file.status, FileStatus.Added) 30 | 31 | file = try File(json: sampleOther) 32 | 33 | XCTAssertEqual(file.filePath, "File2.swift") 34 | XCTAssertEqual(file.status, FileStatus.Other) 35 | } 36 | 37 | func testInit() { 38 | let file = File(filePath: "File1.swift", status: .Added) 39 | 40 | XCTAssertEqual(file.filePath, "File1.swift") 41 | XCTAssertEqual(file.status, FileStatus.Added) 42 | } 43 | 44 | // MARK: Dictioninarifying 45 | func testDictionarify() throws { 46 | let file = try File(json: sampleAdded) 47 | 48 | XCTAssertEqual(file.dictionarify(), sampleAdded) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/IntegrationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrationTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 17/07/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XcodeServerSDK 11 | import XCTest 12 | import Nimble 13 | 14 | class IntegrationTests: XCTestCase { 15 | 16 | func test_GetIntegration() { 17 | 18 | let server = self.getRecordingXcodeServer("get_integration") 19 | var done = false 20 | server.getIntegration("ad2fac04895bd1bb06c1d50e3400fd35") { (integration, error) in 21 | expect(error).to(beNil()) 22 | expect(integration).toNot(beNil()) 23 | done = true 24 | } 25 | expect(done).toEventually(beTrue()) 26 | } 27 | 28 | // MARK: Commits 29 | 30 | var expectedDate: NSDate = { 31 | let formatter = NSDateFormatter() 32 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZ" 33 | 34 | return formatter.dateFromString("2015-07-24T09:40:58.462Z")! 35 | }() 36 | 37 | func testGetIntegrationCommits() { 38 | 39 | var done = false 40 | let server = self.getRecordingXcodeServer("get_integration_commits") 41 | server.getIntegrationCommits("56ad016e2e3993ca0b8ed276050150e8") { 42 | 43 | (integrationCommits, error) in 44 | 45 | expect(error).to(beNil()) 46 | guard let integrationCommits = integrationCommits, 47 | let expectationDate = integrationCommits.endedTimeDate, 48 | let commits = integrationCommits.commits["A36AEFA3F9FF1F738E92F0C497C14977DCE02B97"] else { 49 | fail("Integration commits are empty") 50 | return 51 | } 52 | 53 | expect(expectationDate) == self.expectedDate 54 | expect(commits.count) == 6 55 | 56 | let commiters = Set(commits.map { $0.contributor.name }) 57 | expect(commiters.count) == 2 58 | 59 | done = true 60 | } 61 | 62 | expect(done).toEventually(beTrue()) 63 | } 64 | 65 | // MARK: Issues 66 | 67 | func testGetIntegrationIssues() { 68 | 69 | var done = false 70 | let server = self.getRecordingXcodeServer("get_integration_issues") 71 | server.getIntegrationIssues("960f6989b4c7289433ff04db71033d28") { (integrationIssues, error) -> () in 72 | 73 | expect(error).to(beNil()) 74 | 75 | guard let issues = integrationIssues else { 76 | fail("Integration issues should be present") 77 | return 78 | } 79 | 80 | expect(issues.errors.count) == 1 81 | 82 | let expectation = issues.warnings.filter { $0.status == .Fresh } 83 | expect(expectation.count) == 2 84 | expect(issues.analyzerWarnings.isEmpty).to(beTrue()) 85 | 86 | done = true 87 | } 88 | 89 | expect(done).toEventually(beTrue()) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/IssueTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IssueTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 05.08.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeServerSDK 11 | 12 | class IssueTests: XCTestCase { 13 | 14 | let buildServiceError = try! IntegrationIssue(json: [ 15 | "_id": "99e84a22b20df344df2217d5e4186094", 16 | "_rev": "3-0cf0b25e995f1a050617d3c5824007f7", 17 | "message": "This integration was canceled.", 18 | "type": "buildServiceError", 19 | "issueType": "Build Service Error", 20 | "commits": [], 21 | "integrationID": "99e84a22b20df344df2217d5e4183bce", 22 | "age": 0, 23 | "status": 0 24 | ]) 25 | 26 | let errorWithCommits = try! IntegrationIssue(json: [ 27 | "_id": "99e84a22b20df344df2217d5e413f43e", 28 | "_rev": "3-7dfc0aa57a8119c025cd2d86143589f2", 29 | "message": "Expected declaration", 30 | "type": "error", 31 | "issueType": "Swift Compiler Error", 32 | "commits": [ 33 | [ 34 | "XCSCommitCommitChangeFilePaths": [ 35 | [ 36 | "status": 4, 37 | "filePath": "XcodeServerSDK/Server Entities/Bot.swift" 38 | ], 39 | [ 40 | "status": 4, 41 | "filePath": "XcodeServerSDK/Server Entities/Trigger.swift" 42 | ] 43 | ], 44 | "XCSCommitMessage": "Break regular code\n", 45 | "XCSBlueprintRepositoryID": "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", 46 | "XCSCommitContributor": [ 47 | "XCSContributorEmails": [ 48 | "example@example.com" 49 | ], 50 | "XCSContributorName": "tester", 51 | "XCSContributorDisplayName": "tester" 52 | ], 53 | "XCSCommitHash": "1a37d7ce39297ad822626fc2061a1ba343aed343", 54 | "XCSCommitTimestamp": "2015-08-03T08:23:17.000Z" 55 | ] 56 | ], 57 | "target": "XcodeServerSDK - OS X", 58 | "documentFilePath": "XcodeServerSDK/XcodeServerSDK/Server Entities/Bot.swift", 59 | "documentLocationData": "YnBsaXN0MDDUAQIDBAUGIyRYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKQHCBobVSRudWxs2gkKCwwNDg8QERITFBQVFhcYGRgVW2RvY3VtZW50VVJMXxAUc3RhcnRpbmdDb2x1bW5OdW1iZXJfEBJlbmRpbmdDb2x1bW5OdW1iZXJfEBFjaGFyYWN0ZXJSYW5nZUxlbl8QEGxvY2F0aW9uRW5jb2RpbmdWJGNsYXNzXxAQZW5kaW5nTGluZU51bWJlcll0aW1lc3RhbXBfEBJzdGFydGluZ0xpbmVOdW1iZXJfEBFjaGFyYWN0ZXJSYW5nZUxvY4ACEAQQABABgAMQHIAAXxCbZmlsZTovLy9MaWJyYXJ5L0RldmVsb3Blci9YY29kZVNlcnZlci9JbnRlZ3JhdGlvbnMvQ2FjaGVzLzk5ZTg0YTIyYjIwZGYzNDRkZjIyMTdkNWU0MDNmM2U0L1NvdXJjZS9YY29kZVNlcnZlclNESy9YY29kZVNlcnZlclNESy9TZXJ2ZXIlMjBFbnRpdGllcy9Cb3Quc3dpZnTSHB0eH1okY2xhc3NuYW1lWCRjbGFzc2VzXxAXRFZUVGV4dERvY3VtZW50TG9jYXRpb26jICEiXxAXRFZUVGV4dERvY3VtZW50TG9jYXRpb25fEBNEVlREb2N1bWVudExvY2F0aW9uWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0SUmVHJvb3SAAQAIABEAGgAjAC0AMgA3ADwAQgBXAGMAegCPAKMAtgC9ANAA2gDvAQMBBQEHAQkBCwENAQ8BEQGvAbQBvwHIAeIB5gIAAhYCHwIxAjQCOQAAAAAAAAIBAAAAAAAAACcAAAAAAAAAAAAAAAAAAAI7", 60 | "lineNumber": 29, 61 | "integrationID": "99e84a22b20df344df2217d5e413adf7", 62 | "age": 0, 63 | "status": 0 64 | ]) 65 | 66 | // MARK: Test cases 67 | func testJSONInitialization() { 68 | // Shouldn't be nil 69 | XCTAssertNotNil(buildServiceError) 70 | XCTAssertNotNil(errorWithCommits) 71 | 72 | // Check types 73 | XCTAssertEqual(buildServiceError.type, IntegrationIssue.IssueType.BuildServiceError) 74 | XCTAssertEqual(errorWithCommits.type, IntegrationIssue.IssueType.Error) 75 | } 76 | 77 | func testPayload() { 78 | XCTAssertEqual(buildServiceError.payload.allKeys.count, 9) 79 | XCTAssertEqual(errorWithCommits.payload.allKeys.count, 13) 80 | 81 | // Check if Error payload contain line number 82 | let expectation = errorWithCommits.payload["lineNumber"] as! Int 83 | XCTAssertEqual(expectation, 29) 84 | } 85 | 86 | func testCommitsArray() { 87 | // Build Service Error doesn't have commits 88 | XCTAssertTrue(buildServiceError.commits.isEmpty) 89 | 90 | // Error, on the other hand, has one... 91 | XCTAssertEqual(errorWithCommits.commits.count, 1) 92 | XCTAssertEqual(errorWithCommits.commits.first!.filePaths.count, 2) 93 | XCTAssertEqual(errorWithCommits.commits.first!.contributor.name, "tester") 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/LiveUpdatesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiveUpdatesTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 27/09/2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | @testable import XcodeServerSDK 12 | import Nimble 13 | 14 | class LiveUpdatesTests: XCTestCase { 15 | 16 | func testParsing_ConnectPacket() { 17 | 18 | let message = "1::" 19 | let packets: [SocketIOPacket] = SocketIOHelper.parsePackets(message) 20 | expect(packets.count) == 1 21 | let packet = packets.first! 22 | expect(packet.type) == SocketIOPacket.PacketType.Connect 23 | expect(packet.jsonPayload).to(beNil()) 24 | expect(packet.stringPayload) == "" 25 | } 26 | 27 | func testParsing_ErrorPacket() { 28 | let message = "7:::1+0" 29 | let packets: [SocketIOPacket] = SocketIOHelper.parsePackets(message) 30 | expect(packets.count) == 1 31 | let packet = packets.first! 32 | expect(packet.type) == SocketIOPacket.PacketType.Error 33 | let (reason, advice) = packet.parseError() 34 | expect(reason) == SocketIOPacket.ErrorReason.ClientNotHandshaken 35 | expect(advice) == SocketIOPacket.ErrorAdvice.Reconnect 36 | } 37 | 38 | func testParsing_SingleEventMessage() throws { 39 | 40 | let message = "5:::{\"name\":\"advisoryIntegrationStatus\",\"args\":[{\"message\":\"BuildaKit : Linking\",\"_id\":\"07a63fae4ff2d5a37eee830be556d143\",\"percentage\":0.7578125,\"botId\":\"07a63fae4ff2d5a37eee830be50c502a\"},null]}" 41 | let packets: [SocketIOPacket] = SocketIOHelper.parsePackets(message) 42 | expect(packets.count) == 1 43 | let packet = packets.first! 44 | expect(packet.jsonPayload).toNot(beNil()) 45 | let msg = try LiveUpdateMessage(json: packet.jsonPayload!) 46 | expect(msg.type) == LiveUpdateMessage.MessageType.AdvisoryIntegrationStatus 47 | expect(msg.message) == "BuildaKit : Linking" 48 | expect(msg.integrationId) == "07a63fae4ff2d5a37eee830be556d143" 49 | expect(msg.progress) == 0.7578125 50 | expect(msg.botId) == "07a63fae4ff2d5a37eee830be50c502a" 51 | } 52 | 53 | func testParsing_MultipleEventMessages() throws { 54 | let message = "�205�5:::{\"name\":\"advisoryIntegrationStatus\",\"args\":[{\"message\":\"Buildasaur : Linking\",\"_id\":\"07a63fae4ff2d5a37eee830be556d143\",\"percentage\":0.8392857360839844,\"botId\":\"07a63fae4ff2d5a37eee830be50c502a\"},null]}�218�5:::{\"name\":\"advisoryIntegrationStatus\",\"args\":[{\"message\":\"Buildasaur : Copying 1 of 3 files\",\"_id\":\"07a63fae4ff2d5a37eee830be556d143\",\"percentage\":0.8571428680419921,\"botId\":\"07a63fae4ff2d5a37eee830be50c502a\"},null]}�218�5:::{\"name\":\"advisoryIntegrationStatus\",\"args\":[{\"message\":\"Buildasaur : Copying 2 of 3 files\",\"_id\":\"07a63fae4ff2d5a37eee830be556d143\",\"percentage\":0.8607142639160156,\"botId\":\"07a63fae4ff2d5a37eee830be50c502a\"},null]}�228�5:::{\"name\":\"advisoryIntegrationStatus\",\"args\":[{\"message\":\"BuildaUtils : Compiling Swift source files\",\"_id\":\"07a63fae4ff2d5a37eee830be556d143\",\"percentage\":0.05511363506317139,\"botId\":\"07a63fae4ff2d5a37eee830be50c502a\"},null]}" 55 | let packets: [SocketIOPacket] = SocketIOHelper.parsePackets(message) 56 | expect(packets.count) == 4 57 | for packet in packets { 58 | expect(packet.jsonPayload).toNot(beNil()) 59 | let msg = try LiveUpdateMessage(json: packet.jsonPayload!) 60 | expect(msg.type) == LiveUpdateMessage.MessageType.AdvisoryIntegrationStatus 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/MiscsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiscsTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 10/10/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | @testable import XcodeServerSDK 12 | import Nimble 13 | 14 | class MiscsTests: XCTestCase { 15 | 16 | func testHostname_Success() { 17 | 18 | let server = self.getRecordingXcodeServer("hostname") 19 | var done = false 20 | 21 | server.getHostname { (hostname, error) -> () in 22 | expect(error).to(beNil()) 23 | expect(hostname) == "honzadvysmbpr14.home" 24 | done = true 25 | } 26 | 27 | expect(done).toEventually(beTrue()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/PlatformTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlatformTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 13/07/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class PlatformTests: XCTestCase { 13 | 14 | func testGetPlatforms() { 15 | let expectation = self.expectationWithDescription("Get Platforms") 16 | let server = self.getRecordingXcodeServer("get_platforms") 17 | 18 | server.getPlatforms { (platforms, error) -> () in 19 | XCTAssertNil(error) 20 | XCTAssertNotNil(platforms) 21 | 22 | if let platforms = platforms { 23 | XCTAssertEqual(platforms.count, 3, "There should be 3 platforms (watchOS, iOS and OS X)") 24 | 25 | let displayNames = platforms.map { $0.displayName } 26 | XCTAssertTrue(displayNames.contains("Watch OS")) 27 | XCTAssertTrue(displayNames.contains("iOS")) 28 | XCTAssertTrue(displayNames.contains("OS X")) 29 | } 30 | 31 | expectation.fulfill() 32 | } 33 | 34 | self.waitForExpectationsWithTimeout(10.0, handler: nil) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/RepositoryTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Repository.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 28.06.2015. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class RepositoryTests: XCTestCase { 13 | 14 | let json = [ 15 | "readAccessExternalIDs": [], 16 | "writeAccessExternalIDs": [ 17 | "FDF283F5-B9C3-4B43-9000-EF6A54934D4E", 18 | "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050" 19 | ], 20 | "name": "Test", 21 | "posixPermissions": 1, 22 | "httpAccessType": 1 23 | ] 24 | 25 | // MARK: Initialization 26 | func testInit() throws { 27 | let repo = try Repository(json: json) 28 | 29 | XCTAssertEqual(repo.name, "Test") 30 | XCTAssertEqual(repo.httpAccess, Repository.HTTPAccessType.LoggedIn) 31 | XCTAssertEqual(repo.sshAccess, Repository.SSHAccessType.LoggedInReadSelectedWrite) 32 | XCTAssertEqual(repo.writeAccessExternalIds, [ "FDF283F5-B9C3-4B43-9000-EF6A54934D4E", "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050" ]) 33 | XCTAssertEqual(repo.readAccessExternalIds, []) 34 | } 35 | 36 | func testManualInit() { 37 | let repo = Repository(name: "Test", httpAccess: .LoggedIn, sshAccess: .LoggedInReadSelectedWrite, writeAccessExternalIds: [ "FDF283F5-B9C3-4B43-9000-EF6A54934D4E", "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050" ], readAccessExternalIds: []) 38 | 39 | XCTAssertEqual(repo.name, "Test") 40 | XCTAssertEqual(repo.httpAccess, Repository.HTTPAccessType.LoggedIn) 41 | XCTAssertEqual(repo.sshAccess, Repository.SSHAccessType.LoggedInReadSelectedWrite) 42 | XCTAssertEqual(repo.writeAccessExternalIds, [ "FDF283F5-B9C3-4B43-9000-EF6A54934D4E", "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050" ]) 43 | XCTAssertEqual(repo.readAccessExternalIds, []) 44 | } 45 | 46 | func testConvenienceInit() { 47 | let repo = Repository(name: "Test") 48 | 49 | XCTAssertEqual(repo.name, "Test") 50 | XCTAssertEqual(repo.httpAccess, Repository.HTTPAccessType.None) 51 | XCTAssertEqual(repo.sshAccess, Repository.SSHAccessType.LoggedInReadWrite) 52 | XCTAssertEqual(repo.writeAccessExternalIds, []) 53 | XCTAssertEqual(repo.readAccessExternalIds, []) 54 | } 55 | 56 | // MARK: JSONifying 57 | func testDictionarify() { 58 | let repo = Repository(name: "Test", httpAccess: .LoggedIn, sshAccess: .LoggedInReadSelectedWrite, writeAccessExternalIds: [ "FDF283F5-B9C3-4B43-9000-EF6A54934D4E", "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050" ], readAccessExternalIds: []) 59 | 60 | XCTAssertEqual(repo.dictionarify(), json) 61 | } 62 | 63 | // MARK: Enum tests 64 | func testHTTPEnum() { 65 | var httpEnum = Repository.HTTPAccessType.None 66 | XCTAssertEqual(httpEnum.toString(), "No users are not allowed to read or write") 67 | 68 | httpEnum = .LoggedIn 69 | XCTAssertEqual(httpEnum.toString(), "Logged in users are allowed to read and write") 70 | } 71 | 72 | func testSSHEnum() { 73 | var sshEnum = Repository.SSHAccessType.SelectedReadWrite 74 | XCTAssertEqual(sshEnum.toString(), "Only selected users can read and/or write") 75 | 76 | sshEnum = .LoggedInReadSelectedWrite 77 | XCTAssertEqual(sshEnum.toString(), "Only selected users can write but all logged in can read") 78 | 79 | sshEnum = .LoggedInReadWrite 80 | XCTAssertEqual(sshEnum.toString(), "All logged in users can read and write") 81 | } 82 | 83 | // MARK: API Routes tests 84 | func testGetRepositories() { 85 | let expectation = self.expectationWithDescription("Get Repositories") 86 | let server = self.getRecordingXcodeServer("get_repositories") 87 | 88 | server.getRepositories() { (repositories, error) in 89 | XCTAssertNil(error, "Error should be nil") 90 | XCTAssertNotNil(repositories, "Repositories shouldn't be nil") 91 | 92 | if let repos = repositories { 93 | XCTAssertEqual(repos.count, 2, "There should be two repositories available") 94 | 95 | let reposNames = Set(repos.map { $0.name }) 96 | let reposSSHAccess = Set(repos.map { $0.sshAccess.rawValue }) 97 | let writeAccessExternalIDs = Set(repos.flatMap { $0.writeAccessExternalIds }) 98 | 99 | for (index, _) in repos.enumerate() { 100 | XCTAssertTrue(reposNames.contains("Test\(index + 1)")) 101 | XCTAssertTrue(reposSSHAccess.elementsEqual(Set([2, 0]))) 102 | XCTAssertTrue(writeAccessExternalIDs.elementsEqual(Set([ "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050", "D024C308-CEBE-4E72-BE40-E1E4115F38F9" ]))) 103 | } 104 | } 105 | 106 | expectation.fulfill() 107 | } 108 | 109 | self.waitForExpectationsWithTimeout(10.0, handler: nil) 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/TestUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestUtils.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Honza Dvorsky on 17/06/15. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import XcodeServerSDK 12 | import DVR 13 | 14 | struct StringError: ErrorType { 15 | 16 | let description: String 17 | let _domain: String = "" 18 | let _code: Int = 0 19 | 20 | init(_ description: String) { 21 | self.description = description 22 | } 23 | } 24 | 25 | extension XCTestCase { 26 | 27 | func getRecordingXcodeServer(cassetteName: String) -> XcodeServer { 28 | 29 | let config = try! XcodeServerConfig( 30 | host: "https://127.0.0.1", 31 | user: "ICanCreateBots", 32 | password: "superSecr3t") 33 | return self.getRecordingXcodeServerWithConfig(config, cassetteName: cassetteName) 34 | } 35 | 36 | func getRecordingXcodeServerWithConfig(config: XcodeServerConfig, cassetteName: String) -> XcodeServer 37 | { 38 | let server = XcodeServerFactory.server(config) 39 | let backingSession = server.http.session 40 | 41 | let session = DVR.Session(cassetteName: cassetteName, testBundle: NSBundle(forClass: self.classForCoder), backingSession: backingSession) 42 | server.http.session = session 43 | 44 | return server 45 | } 46 | } 47 | 48 | // MARK: Mock JSON helper methods 49 | extension XCTestCase { 50 | 51 | func stringAtPath(path: String) -> String { 52 | return try! NSString(contentsOfFile: (path as NSString).stringByExpandingTildeInPath, encoding: NSUTF8StringEncoding) as String 53 | } 54 | 55 | func loadJSONResponseFromCassetteWithName(name: String) -> NSDictionary { 56 | 57 | let dictionary = self.loadJSONWithName(name) 58 | 59 | let interactions = dictionary["interactions"] as! [NSDictionary] 60 | let response = interactions.first!["response"] as! NSDictionary 61 | 62 | //make sure it's json 63 | assert(response["body_format"] as! String == "json") 64 | 65 | //get the response data out 66 | let body = response["body"] as! NSDictionary 67 | return body 68 | } 69 | 70 | func loadJSONWithName(name: String) -> NSDictionary { 71 | 72 | let bundle = NSBundle(forClass: BotParsingTests.classForCoder()) 73 | do { 74 | 75 | if let url = bundle.URLForResource(name, withExtension: "json") { 76 | 77 | let data = try NSData(contentsOfURL: url, options: NSDataReadingOptions()) 78 | if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? NSDictionary { 79 | return json 80 | } 81 | 82 | } else { 83 | throw StringError("File with name \(name) not found in the bundle") 84 | } 85 | 86 | } catch { 87 | XCTFail("Error reading file with name \(name), error: \(error)") 88 | } 89 | return NSDictionary() 90 | } 91 | 92 | func botInCassetteWithName(name: String) throws -> Bot { 93 | let json = self.loadJSONResponseFromCassetteWithName(name) 94 | let bot = try Bot(json: json) 95 | return bot 96 | } 97 | 98 | func botInFileWithName(name: String) throws -> Bot { 99 | let json = self.loadJSONWithName(name) 100 | let bot = try Bot(json: json) 101 | return bot 102 | } 103 | 104 | func configurationFromBotWithName(name: String) throws -> BotConfiguration { 105 | let bot = try self.botInFileWithName(name) 106 | let configuration = bot.configuration 107 | return configuration 108 | } 109 | } 110 | 111 | // MARK: Exception assertions 112 | // Based on: https://forums.developer.apple.com/thread/5824 113 | extension XCTestCase { 114 | /** 115 | Replacement method for XCTAssertThrowsError which isn't currently supported. 116 | 117 | - parameter message: Message which should be displayed 118 | - parameter file: File in which assertion happened 119 | - parameter line: Line in which assertion happened 120 | - parameter block: Block of code against which assertion should be matched 121 | */ 122 | func XCTempAssertThrowsError(message: String = "", file: StaticString = #file, line: UInt = #line, _ block: () throws -> ()) { 123 | do { 124 | try block() 125 | 126 | let msg = (message == "") ? "Tested block did not throw error as expected." : message 127 | XCTFail(msg, file: file, line: line) 128 | } catch {} 129 | } 130 | 131 | /** 132 | Replacement method for XCTAssertThrowsSpecificError which isn't currently supported. 133 | 134 | - parameter kind: ErrorType which is expected to be thrown from block 135 | - parameter message: Message which should be displayed 136 | - parameter file: File in which assertion happened 137 | - parameter line: Line in which assertion happened 138 | - parameter block: Block of code against which assertion should be matched 139 | */ 140 | func XCTempAssertThrowsSpecificError(kind: ErrorType, _ message: String = "", file: StaticString = #file, line: UInt = #line, _ block: () throws -> ()) { 141 | do { 142 | try block() 143 | 144 | let msg = (message == "") ? "Tested block did not throw expected \(kind) error." : message 145 | XCTFail(msg, file: file, line: line) 146 | } catch let error as NSError { 147 | let expected = kind as NSError 148 | if ((error.domain != expected.domain) || (error.code != expected.code)) { 149 | let msg = (message == "") ? "Tested block threw \(error), not expected \(kind) error." : message 150 | XCTFail(msg, file: file, line: line) 151 | } 152 | } 153 | } 154 | 155 | /** 156 | Replacement method for XCTAssertNoThrowsError which isn't currently supported. 157 | 158 | - parameter message: Message which should be displayed 159 | - parameter file: File in which assertion happened 160 | - parameter line: Line in which assertion happened 161 | - parameter block: Block of code against which assertion should be matched 162 | */ 163 | func XCTempAssertNoThrowError(message: String = "", file: StaticString = #file, line: UInt = #line, _ block: () throws -> ()) { 164 | do { 165 | try block() 166 | } catch { 167 | let msg = (message == "") ? "Tested block threw unexpected error." : message 168 | XCTFail(msg, file: file, line: line) 169 | } 170 | } 171 | 172 | /** 173 | Replacement method for XCTAssertNoThrowsSpecificError which isn't currently supported. 174 | 175 | - parameter kind: ErrorType which isn't expected to be thrown from block 176 | - parameter message: Message which should be displayed 177 | - parameter file: File in which assertion happened 178 | - parameter line: Line in which assertion happened 179 | - parameter block: Block of code against which assertion should be matched 180 | */ 181 | func XCTempAssertNoThrowSpecificError(kind: ErrorType, _ message: String = "", file: StaticString = #file, line: UInt = #line, _ block: () throws -> ()) { 182 | do { 183 | try block() 184 | } catch let error as NSError { 185 | let unwanted = kind as NSError 186 | if ((error.domain == unwanted.domain) && (error.code == unwanted.code)) { 187 | let msg = (message == "") ? "Tested block threw unexpected \(kind) error." : message 188 | XCTFail(msg, file: file, line: line) 189 | } 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/ToolchainTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToolchainTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Laurent Gaches on 21/04/16. 6 | // Copyright © 2016 Laurent Gaches. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class ToolchainTest: XCTestCase { 13 | 14 | func testGetToolchains() { 15 | let expectation = self.expectationWithDescription("Get Toolchains") 16 | let server = self.getRecordingXcodeServer("get_toolchains") 17 | 18 | server.getToolchains { (toolchains, error) -> () in 19 | XCTAssertNil(error) 20 | XCTAssertNotNil(toolchains) 21 | 22 | if let toolchains = toolchains { 23 | XCTAssertEqual(toolchains.count, 2, "There should be 2 toolchains") 24 | 25 | let displayNames = toolchains.map { $0.displayName } 26 | XCTAssertTrue(displayNames.contains("Xcode Swift 2.2.1 Snapshot 2016-04-12 (a)")) 27 | XCTAssertTrue(displayNames.contains("Xcode Swift DEVELOPMENT Snapshot 2016-04-12 (a)")) 28 | 29 | } 30 | 31 | expectation.fulfill() 32 | } 33 | 34 | self.waitForExpectationsWithTimeout(10.0, handler: nil) 35 | } 36 | } -------------------------------------------------------------------------------- /XcodeServerSDKTests/XcodeServerConfigTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerConfigTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 20/06/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class XcodeServerConfigTests: XCTestCase { 13 | 14 | // MARK: Initialization testing 15 | func testManualInit() { 16 | XCTempAssertNoThrowError("Failed to initialize the server configuration") { 17 | let config = try XcodeServerConfig(host: "127.0.0.1", user: "ICanCreateBots", password: "superSecr3t") 18 | 19 | XCTAssertEqual(config.host, "https://127.0.0.1", "Should create proper host address") 20 | XCTAssertEqual(config.user!, "ICanCreateBots") 21 | XCTAssertEqual(config.password!, "superSecr3t") 22 | } 23 | } 24 | 25 | func testInvalidHostProvidingForManualInit() { 26 | XCTempAssertThrowsSpecificError(ConfigurationErrors.InvalidHostProvided("Invalid host name provided, please double check your host name")) { 27 | _ = try XcodeServerConfig(host: "<>127.0.0.1", user: "ICanCreateBots", password: "superSecr3t") 28 | } 29 | } 30 | 31 | func testDictionaryInit() { 32 | XCTempAssertNoThrowError("Failed to initialize the server configuration") { 33 | let json = [ 34 | "host": "https://127.0.0.1", 35 | "user": "ICanCreateBots", 36 | "password": "superSecr3t" 37 | ] 38 | 39 | let config = try XcodeServerConfig(json: json) 40 | 41 | XCTAssertEqual(config.host, "https://127.0.0.1", "Should create proper host address") 42 | XCTAssertEqual(config.user!, "ICanCreateBots") 43 | XCTAssertEqual(config.password!, "superSecr3t") 44 | } 45 | } 46 | 47 | func testInvalidDictionaryInit() { 48 | let json = [ 49 | "user": "ICanCreateBots", 50 | "password": "superSecr3t" 51 | ] 52 | 53 | XCTempAssertThrowsSpecificError(ConfigurationErrors.NoHostProvided) { 54 | _ = try XcodeServerConfig(json: json) 55 | } 56 | } 57 | 58 | func testInvalidSchemeProvided() { 59 | XCTempAssertThrowsSpecificError(ConfigurationErrors.InvalidSchemeProvided("Xcode Server generally uses https, please double check your hostname")) { 60 | _ = try XcodeServerConfig(host: "http://127.0.0.1") 61 | } 62 | } 63 | 64 | // MARK: Returning JSON 65 | func testJsonify() { 66 | XCTempAssertNoThrowError("Failed to initialize the server configuration") { 67 | let config = try XcodeServerConfig(host: "127.0.0.1", user: "ICanCreateBots", password: "superSecr3t", id: "12345") 68 | let expected = [ 69 | "host": "https://127.0.0.1", 70 | "user": "ICanCreateBots", 71 | "id": "12345" 72 | ] 73 | 74 | XCTAssertEqual(config.jsonify(), expected) 75 | } 76 | } 77 | 78 | //just to have a perf test 79 | func testPerformance() { 80 | self.measureBlock { () -> Void in 81 | _ = try! XcodeServerConfig(host: "127.0.0.1", user: "ICanCreateBots", password: "superSecr3t") 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/XcodeServerEntityTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerEntityTests.swift 3 | // XcodeServerSDK 4 | // 5 | // Created by Mateusz Zając on 20/06/15. 6 | // Copyright © 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import XcodeServerSDK 11 | 12 | class XcodeServerEntityTests: XCTestCase { 13 | 14 | // MARK: No arguments init 15 | func testInit() { 16 | let defaultEntity = XcodeServerEntity() 17 | 18 | XCTAssertNil(defaultEntity.id, "ID should be nil") 19 | XCTAssertNil(defaultEntity.rev, "Rev should be nil") 20 | XCTAssertNil(defaultEntity.tinyID, "Tiny ID should be nil") 21 | } 22 | 23 | func testDictionarify() { 24 | // let defaultEntity = XcodeServerEntity() 25 | 26 | // Unable to test assertions in Swift... 27 | // XCTAssertNotNil(defaultEntity.dictionarify(), "Should return empty NSDictionary") 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /XcodeServerSDKTests/XcodeServerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeServerTests.swift 3 | // XcodeServerSDKTests 4 | // 5 | // Created by Honza Dvorsky on 11/06/2015. 6 | // Copyright (c) 2015 Honza Dvorsky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import XcodeServerSDK 12 | 13 | class XcodeServerTests: XCTestCase { 14 | 15 | var server: XcodeServer! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | do { 20 | let config = try XcodeServerConfig( 21 | host: "https://127.0.0.1", 22 | user: "ICanCreateBots", 23 | password: "superSecr3t") 24 | self.server = XcodeServerFactory.server(config) 25 | } catch { 26 | XCTFail("Failed to initialize the server configuration: \(error)") 27 | } 28 | } 29 | 30 | func testServerCreation() { 31 | XCTAssertNotNil(self.server) 32 | } 33 | 34 | // MARK: Creadentials tests 35 | 36 | func testCredentials() { 37 | let user = server.credential?.user 38 | let pass = server.credential?.password 39 | 40 | XCTAssertEqual(user!, "ICanCreateBots") 41 | XCTAssertEqual(pass!, "superSecr3t") 42 | } 43 | 44 | func testNoUserCredentials() { 45 | let noUserConfig = try! XcodeServerConfig(host: "https://127.0.0.1") 46 | let server = XcodeServerFactory.server(noUserConfig) 47 | 48 | XCTAssertNil(server.credential) 49 | } 50 | 51 | func DEV_testLiveUpdates() { 52 | 53 | let exp = self.expectationWithDescription("Network") 54 | let stopHandler = self.server.startListeningForLiveUpdates({ (messages: [LiveUpdateMessage]) -> () in 55 | print(messages) 56 | }) 57 | 58 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(5000 * Double(NSEC_PER_SEC))) 59 | dispatch_after(delayTime, dispatch_get_main_queue(), { () -> Void in 60 | print("stopping") 61 | stopHandler() 62 | exp.fulfill() 63 | }) 64 | self.waitForExpectationsWithTimeout(1000) { (_) -> Void in 65 | stopHandler() 66 | } 67 | } 68 | 69 | func DEV_testLive_GetBots() { 70 | 71 | let exp = self.expectationWithDescription("Network") 72 | self.server.getBots { (bots, error) in 73 | exp.fulfill() 74 | } 75 | self.waitForExpectationsWithTimeout(10, handler: nil) 76 | } 77 | 78 | func DEV_testLive_FetchAndRecordBot() { 79 | 80 | let exp = self.expectationWithDescription("Network") 81 | let server = self.getRecordingXcodeServer("test_bot") 82 | 83 | server.getBots { (bots, error) in 84 | exp.fulfill() 85 | } 86 | 87 | self.waitForExpectationsWithTimeout(10, handler: nil) 88 | } 89 | 90 | func DEV_testLive_BotCreation() { 91 | 92 | let exp = self.expectationWithDescription("wait") 93 | 94 | let privateKey = self.stringAtPath("~/.ssh/id_rsa") 95 | let publicKey = self.stringAtPath("~/.ssh/id_rsa.pub") 96 | 97 | let blueprint = SourceControlBlueprint(branch: "swift-2", projectWCCIdentifier: "A36AEFA3F9FF1F738E92F0C497C14977DCE02B97", wCCName: "XcodeServerSDK", projectName: "XcodeServerSDK", projectURL: "git@github.com:czechboy0/XcodeServerSDK.git", projectPath: "XcodeServerSDK.xcworkspace", publicSSHKey: publicKey, privateSSHKey: privateKey, sshPassphrase: nil, certificateFingerprint: nil) 98 | 99 | let scriptBody = "cd XcodeServerSDK; /usr/local/bin/carthage update --no-build" 100 | let scriptTrigger = Trigger(config: TriggerConfig(phase: .Prebuild, kind: .RunScript, scriptBody: scriptBody, name: "Carthage", conditions: nil, emailConfiguration: nil)!) 101 | 102 | let devices = [ 103 | "a85553a5b26a7c1a4998f3b237005ac7", 104 | "a85553a5b26a7c1a4998f3b237004afd" 105 | ] 106 | let deviceSpec = DeviceSpecification.iOS(.SelectedDevicesAndSimulators, deviceIdentifiers: devices) 107 | let config = BotConfiguration(builtFromClean: BotConfiguration.CleaningPolicy.Once_a_Day, codeCoveragePreference: .UseSchemeSetting, buildConfiguration: .UseSchemeSetting, analyze: true, test: true, archive: true, exportsProductFromArchive: true, schemeName: "XcodeServerSDK - iOS", schedule: BotSchedule.commitBotSchedule(), triggers: [scriptTrigger], deviceSpecification: deviceSpec, sourceControlBlueprint: blueprint) 108 | 109 | let bot = Bot(name: "TestBot From XcodeServerSDK", configuration: config) 110 | 111 | self.server.createBot(bot) { (response) -> () in 112 | 113 | print("") 114 | switch response { 115 | case .Success(let newBot): 116 | 117 | self.server.postIntegration(newBot.id) { (integration, error) -> () in 118 | 119 | print("") 120 | exp.fulfill() 121 | } 122 | 123 | default: break 124 | } 125 | } 126 | 127 | self.waitForExpectationsWithTimeout(1000, handler: nil) 128 | } 129 | } 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # Customise this file, documentation can be found here: 2 | # https://github.com/KrauseFx/fastlane/tree/master/docs 3 | # All available actions: https://github.com/KrauseFx/fastlane/blob/master/docs/Actions.md 4 | # can also be listed using the `fastlane actions` command 5 | 6 | # If you want to automatically update fastlane if a new version is available: 7 | update_fastlane 8 | 9 | # This is the minimum version number required. 10 | # Update this, if you use features of a newer version 11 | fastlane_version "1.24.0" 12 | 13 | # ----------- 14 | 15 | def project_name 16 | "XcodeServerSDK" 17 | end 18 | 19 | def project_github_name 20 | "czechboy0/#{project_name}" 21 | end 22 | 23 | def release_branch 24 | "master" 25 | end 26 | 27 | # ----------- 28 | 29 | lane :prebuild do 30 | cocoapods 31 | end 32 | 33 | lane :release do 34 | 35 | # prep the local state 36 | ensure_git_status_clean 37 | ensure_git_branch(branch: release_branch) 38 | git_pull 39 | 40 | # lint podspec first 41 | sh "cd .. && pod lib lint" 42 | 43 | # regen the changelog and open it 44 | sh "cd .. && github_changelog_generator -t $GITHUB_TOKEN && subl CHANGELOG.md" 45 | 46 | # assume version from the podspec 47 | version = read_podspec['source']['tag'] 48 | 49 | # ask for info 50 | title = prompt(text: 'Release Title: ') 51 | description = prompt(text: "Release changelog: ", 52 | multi_line_end_keyword: "END") 53 | 54 | # create a new release on GitHub 55 | repo_url = project_github_name 56 | ENV["FL_GITHUB_RELEASE_API_TOKEN"] = ENV["GITHUB_TOKEN"] 57 | release = set_github_release( 58 | repository_name: repo_url, 59 | commitish: release_branch, 60 | name: [version, title].join(" - "), 61 | tag_name: version, 62 | description: description, 63 | is_draft: false, 64 | is_prerelease: false 65 | ) 66 | 67 | # release podspec to cocoapods 68 | sh "cd .. && pod trunk push" 69 | 70 | # notify us on slack 71 | # slack( 72 | # slack_url: ENV['SLACK_RELEASES_URL'], 73 | # message: "Successfully released [#{project_name} #{version}](#{release['html_url']}) :rocket:", 74 | # payload: { 75 | # "New" => release['body'] 76 | # } 77 | # ) 78 | 79 | # regenerate changelog to get it committed 80 | sh "cd .. && github_changelog_generator -t $GITHUB_TOKEN" 81 | sh "cd .. && git commit -am \"changelog\" && git push" 82 | 83 | end 84 | 85 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | ``` 5 | sudo gem install fastlane 6 | ``` 7 | # Available Actions 8 | ### prebuild 9 | ``` 10 | fastlane prebuild 11 | ``` 12 | 13 | ### release 14 | ``` 15 | fastlane release 16 | ``` 17 | 18 | 19 | ---- 20 | 21 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 22 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools). 23 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane). --------------------------------------------------------------------------------