diff --git a/Checkman.xcodeproj/project.pbxproj b/Checkman.xcodeproj/project.pbxproj index 3a37a46..d3bfff4 100644 --- a/Checkman.xcodeproj/project.pbxproj +++ b/Checkman.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 22B0D7F319BE5DAD001F35CD /* concourse.check in Resources */ = {isa = PBXBuildFile; fileRef = 22B0D7F219BE5DAD001F35CD /* concourse.check */; }; 69CCFAD4174804B60097A6CC /* tracker.check in Resources */ = {isa = PBXBuildFile; fileRef = 69CCFAD3174804B60097A6CC /* tracker.check */; }; + 7A5F321F2103B8B8001D0B39 /* bitrise.check in Resources */ = {isa = PBXBuildFile; fileRef = 7A5F321E2103B8B8001D0B39 /* bitrise.check */; }; 960C9F0816C7760100F5FAA2 /* WebUIHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 960C9F0716C7760100F5FAA2 /* WebUIHandler.m */; }; 960C9F0C16C77A2700F5FAA2 /* WebSocketConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 960C9F0B16C77A2700F5FAA2 /* WebSocketConnection.m */; }; 960C9F1216C7831400F5FAA2 /* WebSocketFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 960C9F1116C7831400F5FAA2 /* WebSocketFrame.m */; }; @@ -175,6 +176,7 @@ 62BF3B9716AF60320051A1CC /* semaphore.check */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = semaphore.check; sourceTree = ""; }; 62BF3B9A16AF625C0051A1CC /* Checkman.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = Checkman.zip; sourceTree = ""; }; 69CCFAD3174804B60097A6CC /* tracker.check */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tracker.check; sourceTree = ""; }; + 7A5F321E2103B8B8001D0B39 /* bitrise.check */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = bitrise.check; sourceTree = ""; }; 960C9F0616C7760100F5FAA2 /* WebUIHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebUIHandler.h; sourceTree = ""; }; 960C9F0716C7760100F5FAA2 /* WebUIHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebUIHandler.m; sourceTree = ""; }; 960C9F0A16C77A2700F5FAA2 /* WebSocketConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketConnection.h; sourceTree = ""; }; @@ -687,6 +689,7 @@ children = ( AE2F71591B72BA900092CA6C /* snapci.check */, AE40A98C1AD218B3006841E1 /* circleci.check */, + 7A5F321E2103B8B8001D0B39 /* bitrise.check */, AE40A98D1AD218B3006841E1 /* circlecijson.check */, AE40A98E1AD218B3006841E1 /* codeship.check */, AE40A98F1AD218B3006841E1 /* tddium.check */, @@ -972,6 +975,7 @@ AE1E7D2116FD57070026129A /* github_issues.check in Resources */, 69CCFAD4174804B60097A6CC /* tracker.check in Resources */, AE2F715A1B72BA900092CA6C /* snapci.check in Resources */, + 7A5F321F2103B8B8001D0B39 /* bitrise.check in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/README.md b/README.md index 54c6c9a..ca11cdc 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,9 @@ If you ever need to kill Checkman: * `circlecijson.check ` checks specific Circle CI build status using the JSON interface which provides build time data e.g. `circlecijson.check myusername myproject master 6cadaa96f7c455a658e00dd4500adc8f654342cc` + +* `bitrise.check ` + (Tip: open project's setting page, then find the "API Tokens" tab to create an API token of type 'all') * `test.check ... ` returns predefined check result diff --git a/bin/Checkman.zip b/bin/Checkman.zip index 518fa1c..d16bec4 100644 Binary files a/bin/Checkman.zip and b/bin/Checkman.zip differ diff --git a/scripts/bitrise.check b/scripts/bitrise.check new file mode 100755 index 0000000..f17228a --- /dev/null +++ b/scripts/bitrise.check @@ -0,0 +1,160 @@ +#!/usr/bin/env ruby +require "rubygems" +require "json" +require "time" + +class BitriseBuildStatus + def initialize(hash) + @build_data = hash + end + + def number + @build_data["build_number"] + end + + def ok? + @build_data['status'] == 1 + end + + def building? + @build_data['status'] == 0 + end + + def build_url + @build_data["build_url"] + end + + def started_at + iso8601 = @build_data["started_on_worker_at"] + Time.parse(iso8601) if iso8601 + end + + def finished_at + iso8601 = @build_data["finished_at"] + Time.parse(iso8601) if iso8601 + end + + def duration + if started_at + secs = (finished_at || Time.now) - started_at + Time.at(secs).gmtime.strftime("%R:%S") + end + end + + def formatted_started_at + started_at.getlocal.strftime("%I:%M%p %m/%d/%Y %Z") if started_at + end + + def last_commit_short_sha + @build_data["commit_hash"][0..5] + end + + def last_commit_author + @build_data["committer_name"] + end + + def last_commit_message + @build_data["commit_message"] + end + + def as_json(options={}) + { + :result => ok?, + :changing => building?, + :url => build_url, + :info => [ + [:Build, number], + [:Duration, duration], + [:Started, formatted_started_at] + ] + optional_info(options) + } + end + + def to_json(*) + JSON.dump(as_json) + end + + private + + def optional_info(options) + if last_commit_short_sha + [ ["-", ""], + [:SHA, last_commit_short_sha], + [:Branch, @build_data['branch']], + [:Message, last_commit_message], + [:Author, last_commit_author] ] + else + [] + end + end +end + +class BitriseBranchStatus + def initialize(json) + @branch_data = JSON.parse(json)['data'] + @build_statuses = + @branch_data.map { |d| BitriseBuildStatus.new(d) } + + raise StandardError, "Status for branch '#{branch_name}' is not available" \ + unless last_build_status + + rescue JSON::ParserError + raise RuntimeError, "invalid json: '#{json}'" + end + + def ok? + if last_build_status.building? && last_non_pending_build_status + last_non_pending_build_status.ok? + else + last_build_status.ok? + end + end + + def as_json(*) + last_build_status.as_json.tap do |hash| + hash[:result] = ok? + end + end + + def to_json(*) + JSON.dump(as_json) + end + + def last_build_status + @build_statuses.first + end + + def last_non_pending_build_status + @build_statuses.find { |d| !d.building? } + end +end + +class Bitrise + def initialize(app_id, branch_name, auth_token) + raise ArgumentError "app_id must not be nil" \ + unless @app_id = app_id + + raise ArgumentError "branch_name must not be nil" \ + unless @branch_name = branch_name + + raise ArgumentError "auth_token must not be nil" \ + unless @auth_token = auth_token + end + + def latest_status + BitriseBranchStatus.new(http_get(app_url)) + end + + private + + def app_url + "https://api.bitrise.io/v0.1/apps/#{@app_id}/builds?branch=#{@branch_name}" + end + + def http_get(url) + curl = "curl -s -H 'Authorization: token #{@auth_token}' '#{app_url}'" + `#{curl}`.tap { |o| $stderr.puts curl, o } + end +end + +puts Bitrise.new(*ARGV).latest_status.to_json if __FILE__ == $0 diff --git a/scripts/specs/bitrise_spec.rb b/scripts/specs/bitrise_spec.rb new file mode 100644 index 0000000..d20b044 --- /dev/null +++ b/scripts/specs/bitrise_spec.rb @@ -0,0 +1,8 @@ +$:.unshift(File.dirname(__FILE__)) +require "spec_helper" + +describe_check :Bitrise do + pending "trial account expired" + # it_returns_ok %w( ) + # it_returns_fail %w( ) +end