├── .gitignore ├── app-firstSkin-release-unsigned.apk ├── app-secondSkin-release-unsigned.apk ├── util.rb ├── step.yml ├── step.sh ├── git.rb ├── README.md ├── bitrise.yml ├── main.rb ├── hockey.rb └── slack.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .bitrise* 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /app-firstSkin-release-unsigned.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ml-archive/ci-bitrise-deploy-step/master/app-firstSkin-release-unsigned.apk -------------------------------------------------------------------------------- /app-secondSkin-release-unsigned.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ml-archive/ci-bitrise-deploy-step/master/app-secondSkin-release-unsigned.apk -------------------------------------------------------------------------------- /util.rb: -------------------------------------------------------------------------------- 1 | require 'shellwords' 2 | 3 | def runCurlJson(data, url) 4 | json_data = JSON.generate(data) 5 | escaped_data = Shellwords.escape(json_data) 6 | curl = "curl -sS -X POST -H \'Content-type: application/json\' --data #{escaped_data} -o /dev/null -w \"%{http_code}\" #{url}" 7 | result = `#{curl}` 8 | return result 9 | end 10 | 11 | def wasCurlOk(result) 12 | if !result.is_a? String 13 | return false 14 | end 15 | if result[0, 2] == "20" 16 | return true 17 | else 18 | return false 19 | end 20 | end 21 | 22 | def validJson?(string) 23 | begin 24 | !!JSON.parse(string) 25 | rescue JSON::ParserError => e 26 | puts "JSON failed parsing: #{e}" 27 | false 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /step.yml: -------------------------------------------------------------------------------- 1 | title: "Hockey CI Multi Deploy" 2 | summary: "Step for deploying one or more builds to hockey" 3 | description: |- 4 | Deploys any build artifacts and uploads to Hockey. 5 | Accepts a json structure with hockey id, app id and a file path to the build. 6 | Read more on the github page. 7 | website: https://github.com/nodes-android/ci-bitrise-deploy-step 8 | source_code_url: https://github.com/nodes-android/ci-bitrise-deploy-step 9 | support_url: https://github.com/nodes-android/ci-bitrise-deploy-step/issues 10 | host_os_tags: 11 | - osx-10.10 12 | project_type_tags: 13 | - android 14 | - ios 15 | type_tags: 16 | - script 17 | is_requires_admin_user: false 18 | is_always_run: false 19 | is_skippable: false 20 | deps: 21 | brew: 22 | - name: git 23 | - name: wget 24 | - name: ruby 25 | apt_get: 26 | - name: git 27 | - name: wget 28 | - name: ruby 29 | run_if: true 30 | inputs: -------------------------------------------------------------------------------- /step.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | ruby "${THIS_SCRIPT_DIR}/main.rb" 6 | #pwd 7 | 8 | # 9 | # --- Export Environment Variables for other Steps: 10 | # You can export Environment Variables for other Steps with 11 | # envman, which is automatically installed by `bitrise setup`. 12 | # A very simple example: 13 | # envman add --key EXAMPLE_STEP_OUTPUT --value 'the value you want to share' 14 | # Envman can handle piped inputs, which is useful if the text you want to 15 | # share is complex and you don't want to deal with proper bash escaping: 16 | # cat file_with_complex_input | envman add --KEY EXAMPLE_STEP_OUTPUT 17 | # You can find more usage examples on envman's GitHub page 18 | # at: https://github.com/bitrise-io/envman 19 | 20 | # 21 | # --- Exit codes: 22 | # The exit code of your Step is very important. If you return 23 | # with a 0 exit code `bitrise` will register your Step as "successful". 24 | # Any non zero exit code will be registered as "failed" by `bitrise`. 25 | -------------------------------------------------------------------------------- /git.rb: -------------------------------------------------------------------------------- 1 | def getCommitterName() 2 | result = `git --no-pager show -s --format='%cn'` 3 | return result.strip() 4 | end 5 | 6 | def getCommitterMail() 7 | result = `git --no-pager show -s --format='%ce'` 8 | return result.strip() 9 | end 10 | 11 | def getCommitComment() 12 | result = `git log -1 --pretty=%B` 13 | return result.strip() 14 | end 15 | 16 | # Git has no knowledge of a project name, this basically just prunes the URL 17 | def getProjectName() 18 | curl = 'git config --local remote.origin.url|sed -n "s#.*/\([^.]*\)\.git#\1#p"' 19 | result = `#{curl}` 20 | return result.strip() 21 | end 22 | 23 | def getRemoteOriginUrl() 24 | result = `git config --local remote.origin.url` 25 | return result.strip() 26 | end 27 | 28 | def getGithubPageUrl() 29 | origin = getRemoteOriginUrl() 30 | last_part = origin.split(":")[-1] 31 | url = "https://github.com/" + last_part 32 | return url 33 | end 34 | 35 | def getCurrentBranchName() 36 | gitcommit = ENV['GIT_CLONE_COMMIT_HASH']; 37 | result = `git rev-parse --abbrev-ref ${gitcommit}` 38 | return result.strip() 39 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ci-bitrise-deploy-step 2 | This step deploys multiple apk's (android) or ipa's (IOS) to hockey app. It expects to find information about the builds in an ENV called HOCKEYBUILDSJSON 3 | generated by the gradle plugin (insert url to gradle plugin github project here) 4 | 5 | The build information format is: 6 | ``` 7 | [ 8 | { 9 | "build": "app-first.apk", 10 | "hockeyId": "11111111111111111111111111111", 11 | "appId": "dk.nodes.citestflavors.firstskin","mappingFile": "null" 12 | }, 13 | { 14 | "build": "app-second.apk", 15 | "hockeyId": "11111111111111111111111111111", 16 | "appId": "dk.nodes.citestflavors.secondskin","mappingFile": "null" 17 | } 18 | ] 19 | ``` 20 | 21 | ## Inputs 22 | 23 | Following inputs are needed. Project slack channel are where the builds are announced and error slack channel are where the errors get posted 24 | - PROJECT_SLACK_CHANNEL: "#bitrise" 25 | - ERROR_SLACK_CHANNEL: "#bitrise" 26 | 27 | ## Secrets 28 | 29 | In order to post to slack and deploy to hockey to secret values are defined in .bitrise.secret.yml: 30 | - HOCKEY_TOKEN: 11111111111111111111111 31 | - SLACK_WEBHOOK_URL: ttps://hooks.slack.com/services/SOMETHING/SOMETHING 32 | -------------------------------------------------------------------------------- /bitrise.yml: -------------------------------------------------------------------------------- 1 | format_version: 1.1.0 2 | default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git 3 | 4 | app: 5 | envs: 6 | # define these in your .bitrise.secrets.yml 7 | - HOCKEY_TOKEN: $HOCKEY_TOKEN 8 | - SLACK_WEBHOOK_URL: $SLACK_WEBHOOK_URL 9 | - RELEASE_VERSION: 1.0.1 10 | 11 | workflows: 12 | test: 13 | steps: 14 | - path::./: 15 | title: Hockey CI Deploy Test 16 | description: |- 17 | The example input has a default value, 18 | you can overwrite it if you want to, just like we did below, 19 | but the step would use the default value specified in the `step.yml` 20 | file if you would not specify another value. 21 | run_if: true 22 | inputs: 23 | - PROJECT_SLACK_CHANNEL: "@joso" 24 | - ERROR_SLACK_CHANNEL: "@joso" 25 | - HOCKEYBUILDSJSON: '[{"build": "app-firstSkin-release-unsigned.apk","hockeyId": "9aad7c10facc4f569dd6deec2e37a795","appId": "dk.nodes.citestflavors.firstskin","mappingFile": "null"},{"build": "app-secondSkin-release-unsigned.apk","hockeyId": "0c7b2da8e5354a26b8d6d4406c387c6f","appId": "dk.nodes.citestflavors.secondskin","mappingFile": "null"}]' 26 | 27 | # ---------------------------------------------------------------- 28 | # --- workflows to Share this step into a Step Library 29 | audit-this-step: 30 | steps: 31 | - script: 32 | inputs: 33 | - content: |- 34 | #!/bin/bash 35 | set -ex 36 | stepman audit --step-yml ./step.yml 37 | 38 | share-this-step: 39 | envs: 40 | # if you want to share this step into a StepLib 41 | - MY_STEPLIB_REPO_FORK_GIT_URL: $MY_STEPLIB_REPO_FORK_GIT_URL 42 | - STEP_ID_IN_STEPLIB: hockey-multi-deploy 43 | - STEP_GIT_VERION_TAG_TO_SHARE: $RELEASE_VERSION 44 | - STEP_GIT_CLONE_URL: https://github.com/nodes-android/ci-bitrise-deploy-step 45 | description: |- 46 | If this is the first time you try to share a Step you should 47 | first call: $ bitrise share 48 | 49 | This will print you a guide, and information about how Step sharing 50 | works. Please read it at least once! 51 | 52 | As noted in the Step sharing guide you'll have to fork the 53 | StepLib you want to share this step into. Once you're done with forking 54 | the repository you should set your own fork's git clone URL 55 | in the `.bitrise.secrets.yml` file, or here in the `envs` section, 56 | as the value of the `MY_STEPLIB_REPO_FORK_GIT_URL` environment. 57 | 58 | You're now ready to share this Step, just make sure that 59 | the `STEP_ID_IN_STEPLIB` and `STEP_GIT_VERION_TAG_TO_SHARE` 60 | environments are set to the desired values! 61 | 62 | To share this Step into a StepLib you can just run: $ bitrise run share-this-step 63 | 64 | Once it finishes the only thing left is to actually create a Pull Request, 65 | the way described in the guide printed at the end of the process. 66 | before_run: 67 | - audit-this-step 68 | steps: 69 | - script: 70 | inputs: 71 | - content: |- 72 | #!/bin/bash 73 | set -ex 74 | bitrise share start -c ${MY_STEPLIB_REPO_FORK_GIT_URL} 75 | bitrise share create --stepid ${STEP_ID_IN_STEPLIB} --tag ${STEP_GIT_VERION_TAG_TO_SHARE} --git ${STEP_GIT_CLONE_URL} 76 | bitrise share finish 77 | -------------------------------------------------------------------------------- /main.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require_relative 'hockey' 3 | require_relative 'slack' 4 | require_relative 'git' 5 | 6 | puts 'Nodes CI Deploy' 7 | 8 | 9 | # for testing purposes, when running bitrise run test remove the final festival (FF) at the end 10 | =begin 11 | ENV['HOCKEYBUILDSJSON'] = '[ 12 | { 13 | "build": "app-firstSkin-release-unsigned.apk", 14 | "hockeyId": "9aad7c10facc4f569dd6deec2e37a795", 15 | "appId": "dk.nodes.citestflavors.firstskin", 16 | "mappingFile": "null" 17 | }, 18 | { 19 | "build": "app-secondSkin-release-unsigned.apk", 20 | "hockeyId": "0c7b2da8e5354a26b8d6d4406c387c6f", 21 | "appId": "dk.nodes.citestflavors.secondskin", 22 | "mappingFile": "null" 23 | } 24 | ]' 25 | =end 26 | 27 | $hockeyToken = ENV['HOCKEY_TOKEN'] 28 | $slackUrl = ENV['SLACK_WEBHOOK_URL'] 29 | $errorSlackChannel = ENV['ERROR_SLACK_CHANNEL'] 30 | $projectSlackChannel = ENV['PROJECT_SLACK_CHANNEL'] 31 | $hockeyJsonBuilds = ENV['HOCKEYBUILDSJSON'] 32 | 33 | puts "Hockey Token: #{$hockeyToken}" 34 | puts "Slack URL: #{$slackUrl}" 35 | puts "Error Channel: #{$errorSlackChannel}" 36 | puts "Project Slack Channel: #{$projectSlackChannel}" 37 | puts "Json: #{$hockeyJsonBuilds}" 38 | 39 | 40 | 41 | def initBuilds(builds) 42 | builds.each do |build| 43 | build['error'] = false 44 | end 45 | end 46 | 47 | def sanityCheckBuilds(builds) 48 | builds.each do |build| 49 | # skip builds with error or no hockeyinfo 50 | if build['error'] || build['hockeyInfo'] == nil 51 | next 52 | end 53 | #puts build['hockeyInfo'].inspect.gsub(",", "\n") 54 | #puts build['latestHockeyVersion'].inspect.gsub(",", "\n") 55 | 56 | if(build['hockeyInfo']['visibility'] == "private") 57 | $url = "https://rink.hockeyapp.net/manage/apps/#{build['hockeyInfo']['id']}/settings?type=distribution" 58 | reportError("Could not post build " + build['hockeyInfo']['title'] + ", go to <" + $url + "| Distribution Settings> and set download page to Public") 59 | build['error'] = true 60 | next 61 | end 62 | 63 | if(build['appId'] != build['hockeyInfo']['bundle_identifier']) 64 | reportError("appId #{build['appId']} from build.gradle is not the same as the hockey bundle identifier #{build['hockeyInfo']['bundle_identifier']}") 65 | build['error'] = true 66 | next 67 | end 68 | end 69 | end 70 | 71 | $version = "1.0" 72 | 73 | if $hockeyToken == nil || $hockeyToken.empty? 74 | puts "HOCKEY_TOKEN missing in Bitrise app secrets, please add it. Stopping." 75 | exit 1 76 | end 77 | 78 | puts "Parsing build info" 79 | # retrieve build info json from env variable 80 | json = ENV['HOCKEYBUILDSJSON'] 81 | buildPath = ENV['BITRISE_SOURCE_DIR'] 82 | 83 | if json == nil || json.to_s.empty? 84 | puts "Env var: HOCKEYBUILDSJSON was empty, trying to read from file: #{buildPath}/hockeybuilds.json" 85 | json = File.read("#{buildPath}/hockeybuilds.json") 86 | end 87 | if json == nil || json.to_s.empty? 88 | reportError("Build info could not be parsed from json (empty)") 89 | exit 1 90 | end 91 | if(!validJson?(json)) 92 | reportError("Build info could not be parsed from json (json not valid)") 93 | exit 1 94 | end 95 | builds = JSON.parse(json) 96 | if builds == nil 97 | reportError("Build info could not be parsed from json (parse failed)") 98 | exit 1 99 | end 100 | 101 | puts "Build info (size: #{builds.length}): #{json}" 102 | 103 | initBuilds builds 104 | 105 | puts "Downloading info about latest app versions from hockeyapp..." 106 | # lookup each build on hockey and add info it build exists 107 | addInfoToBuildsHockey builds 108 | #puts builds.inspect.gsub(",", "\n") 109 | sanityCheckBuilds(builds) 110 | 111 | 112 | puts "Uploading builds to hockeyapp..." 113 | #puts builds.inspect.gsub(",", "\n") 114 | uploadBuildsHockey(builds) 115 | 116 | puts "Downloading info about latest app versions from hockeyapp..." 117 | # get hockey info about the just uploaded builds 118 | addInfoToBuildsHockey builds 119 | 120 | puts "Posting builds to slack" 121 | #if shouldAbortBuildsPostEntirely(builds) 122 | # reportError("No builds to post due to previous errors") 123 | # exit(1) 124 | #end 125 | postBuildsSlack builds 126 | 127 | 128 | #reportError("Error", "Av for helvede") 129 | 130 | -------------------------------------------------------------------------------- /hockey.rb: -------------------------------------------------------------------------------- 1 | require 'shellwords' 2 | require_relative 'util' 3 | require_relative 'git' 4 | 5 | def escapeNotes(notes) 6 | result = Shellwords.escape(notes) 7 | #result = notes 8 | puts "escaped notes: " + result 9 | return result 10 | end 11 | 12 | def setAppVisibilityPublicHockey(build) 13 | url = "https://rink.hockeyapp.net/api/2/apps/#{build['hockeyId']}/meta" 14 | curl = 'curl -sS -X PUT \ 15 | -F "visibility=public" \ 16 | -H "X-HockeyAppToken: ' + $hockeyToken + '" ' + url; 17 | result = `#{curl}` 18 | if(!validJson?(result)) 19 | return false 20 | end 21 | data = JSON.parse(result) 22 | if(data['status']) 23 | if(data['status'] == "success") 24 | return true 25 | else 26 | puts "setAppVisibilityPublicHockey error: ${data['message']}" 27 | end 28 | end 29 | return false 30 | end 31 | 32 | def getAppInfoHockey(build) 33 | url = "https://rink.hockeyapp.net/api/2/apps/#{build['hockeyId']}/meta" 34 | curl = 'curl -sS \ 35 | -H "X-HockeyAppToken: ' + $hockeyToken + '" ' + url; 36 | result = `#{curl}` 37 | 38 | if(!validJson?(result)) 39 | return nil 40 | end 41 | data = JSON.parse(result) 42 | if(data['status']) 43 | if(data['status'] == "success") 44 | return data['app'] 45 | else 46 | puts "getAppInfoHockey error: ${data['message']}" 47 | end 48 | end 49 | return nil 50 | end 51 | 52 | def uploadBuildHockey(build) 53 | url = "https://rink.hockeyapp.net/api/2/apps/#{build['hockeyId']}/app_versions/upload" 54 | notes = "notes=" 55 | changelog = ENV['COMMIT_CHANGELOG'] 56 | if changelog != nil 57 | puts "uploadBuildHockey: " + changelog 58 | end 59 | 60 | if build['latestHockeyVersion'] 61 | if changelog != nil && !changelog.to_s.empty? 62 | notes += changelog 63 | else 64 | notes += getCommitComment() 65 | end 66 | end 67 | 68 | puts "uploadBuildHockey: " + notes 69 | 70 | #notes = Shellwords.escape("notes=line 1\nline 2\nline 3") 71 | 72 | if build['mappingFile'] != "null" 73 | curl = 'curl -sS \ 74 | -F "status=2" \ 75 | -F "notify=0" \ 76 | -F "ipa=@' + build['build'] + '" \ 77 | -F "dsym=@' + build['mappingFile'] + '" \ 78 | -F ' + escapeNotes(notes) + ' \ 79 | -F "notes_type=0" \ 80 | -H "X-HockeyAppToken: ' + $hockeyToken + '" \ 81 | -o /dev/null -w "%{http_code}" ' + url; 82 | else 83 | curl = 'curl -sS \ 84 | -F "status=2" \ 85 | -F "notify=0" \ 86 | -F "ipa=@' + build['build'] + '" \ 87 | -F ' + escapeNotes(notes) + ' \ 88 | -F "notes_type=1" \ 89 | -H "X-HockeyAppToken: ' + $hockeyToken + '" \ 90 | -o /dev/null -w "%{http_code}" ' + url; 91 | end 92 | #puts "curl = #{curl}" 93 | #exit 0 94 | puts "Uploading #{build['build']}" 95 | result = `#{curl}` 96 | return result 97 | end 98 | 99 | def uploadBuildsHockey(builds) 100 | builds.each do |build| 101 | if build['error'] 102 | next 103 | end 104 | # Is this actually what we want? 105 | #if !build['latestHockeyVersion'] 106 | # reportError "Build #{build['build']} hockeyapp id #{build['hockeyId']} no latest hockey version found, skipping.\nCheck hockeyIds in gradle.build" 107 | # build['error'] = true 108 | # next 109 | #end 110 | result = uploadBuildHockey build 111 | if(!wasCurlOk(result)) 112 | reportError("Uploading build #{build['build']} to hockey app id #{build['hockeyId']} failed with code #{result}") 113 | exit 1 114 | else 115 | puts "Uploaded build #{build['build']} to hockey app id #{build['hockeyId']}" 116 | end 117 | end 118 | end 119 | 120 | def getAppVersionsHockey(appId) 121 | curl = 'curl -sS \ 122 | -H "X-HockeyAppToken: ' + $hockeyToken + '" \ 123 | https://rink.hockeyapp.net/api/2/apps/' + appId + '/app_versions' 124 | result = `#{curl}` 125 | if validJson?(result) == false 126 | return nil 127 | else 128 | return JSON.parse(result) 129 | end 130 | end 131 | 132 | def getLatestAppVersionHockey(appId) 133 | versions = getAppVersionsHockey(appId) 134 | #puts "getLatestAppVersionHockey for appId #{appId} --> #{versions}" 135 | success = versions["status"] == "success" 136 | if(versions == nil) 137 | #puts "getLatestAppVersionHockey -> no versions found" 138 | return success, nil 139 | end 140 | if(versions && versions.key?("app_versions")) 141 | versions = versions["app_versions"] 142 | else 143 | #puts "getLatestAppVersionHockey -> could not find key app_versions in response #{versions}" 144 | return success, nil 145 | end 146 | if(versions) 147 | #puts "getLatestAppVersionHockey -> hockey versions #{versions}" 148 | versions.each do |version| 149 | version['img_url'] = "https://rink.hockeyapp.net/api/2/apps/" + appId + "?format=png"; 150 | end 151 | return success, versions[0] 152 | else 153 | #puts "getLatestAppVersionHockey -> versions was empty or nil" 154 | return success, nil 155 | end 156 | end 157 | 158 | 159 | def fetchAndAddHockeyInfoToBuild(build) 160 | app = getAppInfoHockey(build) 161 | if app == nil 162 | reportError "Error attempting to fetch info about hockeyapp #{build['hockeyId']}. Please check if the app is marked as private or created on your own HockeyApp account." 163 | return false 164 | end 165 | success, latest = getLatestAppVersionHockey(build['hockeyId']) 166 | if latest == nil && !success 167 | reportError "Error attempting to fetch latest hockeyapp version (#{build['hockeyId']})" 168 | return false 169 | else 170 | build['latestHockeyVersion'] = latest 171 | end 172 | build['hockeyInfo'] = app 173 | #puts build.inspect.gsub(",", "\n") 174 | return true 175 | end 176 | 177 | def addInfoToBuildsHockey(builds) 178 | # iterate trough each build and deploy 179 | builds.each do |build| 180 | if !build['error'] 181 | if fetchAndAddHockeyInfoToBuild(build) == false 182 | build['error'] = true 183 | end 184 | end 185 | end 186 | end 187 | 188 | -------------------------------------------------------------------------------- /slack.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require_relative 'util' 3 | require_relative 'git' 4 | require_relative 'slack' 5 | 6 | $slackErrorColor = "#e03131" 7 | $slackBuildColor = "#36a64f" 8 | 9 | def formatCodeString(code) 10 | return '```' + code + '```' 11 | end 12 | 13 | def getProjectChannelName() 14 | if ENV['PROJECT_SLACK_CHANNEL'] != nil && !ENV['PROJECT_SLACK_CHANNEL'].to_s.empty? 15 | return ENV['PROJECT_SLACK_CHANNEL'].to_s 16 | else 17 | return '#bitrise' 18 | end 19 | end 20 | 21 | def getBitriseBuildURL() 22 | if ENV['BITRISE_BUILD_URL'] != nil && !ENV['BITRISE_BUILD_URL'].to_s.empty? 23 | return ENV['BITRISE_BUILD_URL'].to_s 24 | else 25 | return 'https://www.bitrise.io/dashboard' 26 | end 27 | end 28 | 29 | def getBitriseTag() 30 | if ENV['BITRISE_GIT_TAG'] != nil && !ENV['BITRISE_GIT_TAG'].to_s.empty? 31 | return ENV['BITRISE_GIT_TAG'].to_s 32 | else 33 | return '(No tag found)' 34 | end 35 | end 36 | 37 | def getBitriseTimestamp() 38 | if ENV['BITRISE_BUILD_TRIGGER_TIMESTAMP'] != nil && !ENV['BITRISE_BUILD_TRIGGER_TIMESTAMP'].to_s.empty? 39 | return ENV['BITRISE_BUILD_TRIGGER_TIMESTAMP'].to_s 40 | else 41 | return '(No time found)' 42 | end 43 | end 44 | 45 | def getBitriseBranch() 46 | if ENV['BITRISE_GIT_BRANCH'] != nil && !ENV['BITRISE_GIT_BRANCH'].to_s.empty? 47 | return ENV['BITRISE_GIT_BRANCH'].to_s 48 | else 49 | return '(unknown branch)' 50 | end 51 | end 52 | 53 | def getErrorChannelName() 54 | if ENV['ERROR_SLACK_CHANNEL'] != nil && !ENV['ERROR_SLACK_CHANNEL'].to_s.empty? 55 | return ENV['ERROR_SLACK_CHANNEL'].to_s 56 | else 57 | return '#bitrise' 58 | end 59 | end 60 | 61 | def postMsg(channel, msg) 62 | data = { 63 | "channel" => channel, 64 | "text" => msg, 65 | "username" => 'android-ci' 66 | } 67 | runCurlJson(data, $slackUrl) 68 | end 69 | 70 | # do checking to determine if we should even attempt to post the builds 71 | def shouldAbortBuildsPostEntirely(builds) 72 | builds.each do |build| 73 | if (build['latestHockeyVersion'] && !build['error']) 74 | return false 75 | end 76 | end 77 | return true 78 | end 79 | 80 | def postBuildsSlack(builds) 81 | comment = getCommitComment() 82 | text = "" 83 | if (comment.length > 0) 84 | text = formatCodeString(comment) 85 | end 86 | 87 | attachments = [] 88 | # Bitrise attachment 89 | attachments.push({ 90 | "fallback" => "Tag *#{getBitriseTag()}* triggered on *#{getBitriseBranch()}*, started *#{getBitriseTimestamp()}* by #{getCommitterName} (#{getCommitterMail}).", 91 | "title" => "Bitrise status", 92 | "text" => "Tag *#{getBitriseTag()}* triggered on *#{getBitriseBranch()}*, started *#{getBitriseTimestamp()}* by #{getCommitterName} (#{getCommitterMail}).", 93 | "mrkdwn_in" => ["footer", "text"], 94 | "actions" => [ 95 | { 96 | "type" => "button", 97 | "text" => "Build log", 98 | "url" => getBitriseBuildURL(), 99 | "style" => "primary" 100 | } 101 | ] 102 | }) 103 | 104 | builds.each do |build| 105 | if build['error'] 106 | parts = build['build'].split("/") 107 | apk = parts[-1] 108 | attachments.push({ 109 | "fallback" => "Apk #{apk} (Hockey id: #{build['hockeyId']}) could not be deployed due to errors", 110 | "color" => "#F50057", 111 | "title" => "Apk #{apk} (Hockey id: #{build['hockeyId']}) could not be deployed due to errors", 112 | "actions" => [ 113 | { 114 | "type" => "button", 115 | "text" => "Hockey page", 116 | "url" => "https://rink.hockeyapp.net/manage/apps/#{build['hockeyId']}", 117 | "style" => "danger" 118 | } 119 | ] 120 | }) 121 | next 122 | end 123 | if (build['latestHockeyVersion'] && !build['error']) 124 | attachments.push({ 125 | "fallback" => "#{build['latestHockeyVersion']['title']} v#{build['latestHockeyVersion']['shortversion']} (#{build['latestHockeyVersion']['version']})", 126 | "color" => $slackBuildColor, 127 | "title" => "#{build['latestHockeyVersion']['title']} v#{build['latestHockeyVersion']['shortversion']} (#{build['latestHockeyVersion']['version']})", 128 | "actions" => [ 129 | { 130 | "type" => "button", 131 | "text" => "Install", 132 | "url" => "#{build['latestHockeyVersion']['download_url']}", 133 | "style" => "primary" 134 | } 135 | ] 136 | }) 137 | end 138 | end 139 | 140 | #text += "\nNew " + type + " build (v " + version + ", branch: " + getCurrentBranchName() + ") deployed, download from <" + hockey_link + "|HockeyApp>"; 141 | title = "#{getProjectName()} (branch: #{getCurrentBranchName()}) builds deployed:" 142 | fallback = "#{getCommitterName} deployed new build(s) of #{getProjectName()}" 143 | 144 | data = { 145 | "channel" => getProjectChannelName(), 146 | "username" => 'bitrise-ci', 147 | "mrkdwn" => true, 148 | "attachments" => attachments 149 | } 150 | #puts "data = #{data}" 151 | result = runCurlJson(data, $slackUrl) 152 | #puts "result = #{result}" 153 | end 154 | 155 | def reportErrorSlack(msg) 156 | title = "Error deploying " + getProjectName() + " (branch: " + getCurrentBranchName() + ")" 157 | puts "title = #{title}" 158 | data = { 159 | "channel" => getErrorChannelName(), 160 | "username" => 'bitrise-ci', 161 | "mrkdwn" => true, 162 | "attachments" => [{ 163 | "fallback" => msg, 164 | "title" => title, 165 | "title_link" => getGithubPageUrl(), 166 | "text" => msg, 167 | "color" => $slackErrorColor, 168 | "footer" => "Bitrise CI " + $version + ", report bugs and feature requests on the ", 169 | "mrkdwn_in" => ["text"] 170 | }] 171 | } 172 | #puts "data = #{data}" 173 | result = runCurlJson(data, $slackUrl) 174 | end 175 | 176 | def reportError(msg, detail = nil) 177 | 178 | if (detail) 179 | puts "Error: #{msg}\n#{detail}" 180 | else 181 | puts "Error: #{msg}" 182 | end 183 | 184 | if (detail) 185 | msg += "\n#{detail}" 186 | end 187 | reportErrorSlack msg 188 | end 189 | 190 | --------------------------------------------------------------------------------