buildkite_graphql_ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 60011358ae2bd0da0340216a969928e3064f3aa3
4
+ data.tar.gz: 42724f7d4f9adc116ea9bb7175541822007d146a
5
+ SHA512:
6
+ metadata.gz: 1632a5a1b97a1aa6144e024ce1b1289b493d269856169443a40f950c752245c759079a0f370b501d8f36c1e6bba89dcb59887e29e165fc52490686999f50a3e5
7
+ data.tar.gz: 6f3f90034ef75e47242b32b64ff0d31cf3d353e364d22fb914e7ee02ab4b36a5620b3de35cebebae430adf4ee9f113aa4b37440778f6f4ea58d5ce17ba02a51c
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "buildkite_graphql_ruby/cli"
4
+ require "buildkite_graphql_ruby/query_builder"
5
+ require "buildkite_graphql_ruby/query_runner"
6
+ require "buildkite_graphql_ruby/commands/command_map"
7
+ require 'pry'
8
+
9
+ options = BuildkiteGraphqlRuby::CLI.parse(args: ARGV)
10
+ command = BuildkiteGraphqlRuby::Commands::CommandMap.get_command(command_string: options.command)
11
+ result = command.run!(options: options)
12
+ command.report_result(result: result, options: options)
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "buildkite_graphql_ruby"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,66 @@
1
+ module BuildkiteGraphqlRuby
2
+ class CLI
3
+ require 'optparse'
4
+ require 'optparse/time'
5
+ require 'ostruct'
6
+ require 'pp'
7
+
8
+ #
9
+ # Return a structure describing the options.
10
+ #
11
+ def self.parse(args:)
12
+ # The options specified on the command line will be collected in *options*.
13
+ # We set default values here.
14
+ options = OpenStruct.new
15
+ options.branch = `git rev-parse --abbrev-ref HEAD`.chomp # TODO: move this into git helper
16
+ options.command = 'branch_status'
17
+
18
+ opt_parser = OptionParser.new do |opts|
19
+ opts.banner = "Usage: buildkite_graphql_ruby [options]"
20
+
21
+ opts.separator ""
22
+ opts.separator "Specific options:"
23
+
24
+ opts.on("-b", "--branch BRANCH",
25
+ "Git branch to get stats on (default: current)") do |branch|
26
+ options.branch = branch.chomp
27
+ end
28
+
29
+ opts.on("-C", "--command COMMAND",
30
+ "What type of query to run (default and only option: branch_status") do |command|
31
+ # TODO: Error on unaccepted query types
32
+ options.command = command
33
+ end
34
+
35
+ opts.on("-a", "--api-token API TOKEN",
36
+ "Git branch to get stats on (default: current)") do |api_token|
37
+ options.api_token = api_token.chomp
38
+ end
39
+
40
+ # # Boolean switch.
41
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
42
+ # options.verbose = v
43
+ # end
44
+
45
+ opts.separator ""
46
+ opts.separator "Common options:"
47
+
48
+ # No argument, shows at tail. This will print an options summary.
49
+ # Try it and see!
50
+ opts.on_tail("-h", "--help", "Show this message") do
51
+ puts opts
52
+ exit
53
+ end
54
+
55
+ # Another typical switch to print the version.
56
+ opts.on_tail("--version", "Show version") do
57
+ puts ::Version.join('.')
58
+ exit
59
+ end
60
+ end
61
+
62
+ opt_parser.parse!(args)
63
+ options
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,106 @@
1
+ module BuildkiteGraphqlRuby
2
+ module Commands
3
+ require 'rainbow'
4
+ require "buildkite_graphql_ruby/results_parsers/build"
5
+ require "buildkite_graphql_ruby/results_parsers/rspec_results"
6
+
7
+ class BranchStatus
8
+ def run!(options:)
9
+ query = BuildkiteGraphqlRuby::QueryBuilder.new.branch_status(branch: options.branch)
10
+ query_runner = BuildkiteGraphqlRuby::QueryRunner.new.run_query(query: query, options: options)
11
+ end
12
+
13
+ def report_result(result:, options:)
14
+ builds_for_branch = result['data']['viewer']['user']['builds']
15
+ total_builds = builds_for_branch['count']
16
+
17
+ return if report_if_no_builds(total_builds, options)
18
+
19
+ builds = builds_for_branch["edges"].map{|build_response| ResultsParsers::Build.from_response(build_response) }
20
+
21
+ return if report_if_no_finished_builds(builds, options)
22
+
23
+ latest_build = builds.select(&:finished?).sort_by(&:finished_at).last
24
+
25
+ return if report_if_latest_build_passed(latest_build, options)
26
+
27
+ # For each failing job, print out the label of the job and its status
28
+ puts border
29
+ puts Rainbow("Branch #{options.branch.inspect} has a failing build").red
30
+ puts Rainbow("URL: #{latest_build.url}").red
31
+ puts ""
32
+
33
+ failing_jobs = latest_build.jobs.reject(&:passed)
34
+ failing_jobs.each do |failing_job|
35
+ puts Rainbow("Failing Job: #{failing_job.label}").red
36
+ # This only works if rspec is configured to upload a file called `tmp/rspec_json_results.json` for
37
+ # rspec output.
38
+ artifacts_with_results = failing_job.artifacts.select{|a| a.path == 'tmp/rspec_json_results.json'}
39
+
40
+ artifacts_with_results.each do |artifact|
41
+ rspec_result = ResultsParsers::RspecResults.from_response(artifact.download)
42
+ puts rspec_result['summary_line']
43
+ puts "Failing tests:"
44
+ rspec_result.examples.reject(&:passed?).each do |e|
45
+ puts e.file_path
46
+ end
47
+ end
48
+ end
49
+
50
+ puts border
51
+ end
52
+
53
+ private
54
+
55
+ def border
56
+ Rainbow("=" * 80).blue
57
+ end
58
+
59
+ def report_if_no_builds(total_builds, options)
60
+ if total_builds == 0
61
+ report = [
62
+ border,
63
+ Rainbow("There are no builds for the branch #{options.branch.inspect}").lightblue,
64
+ border
65
+ ]
66
+
67
+ puts report
68
+ true
69
+ else
70
+ false
71
+ end
72
+ end
73
+
74
+ def report_if_no_finished_builds(builds, options)
75
+ if builds.select(&:finished?).none?
76
+ report = [
77
+ border,
78
+ Rainbow("None of the builds for branch #{options.branch.inspect} have finished").lightblue,
79
+ border,
80
+ ]
81
+
82
+ puts report
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+
89
+ def report_if_latest_build_passed(latest_build, options)
90
+ if latest_build.passed?
91
+ report = [
92
+ border,
93
+ Rainbow("Branch #{options.branch.inspect} has a passing build").green,
94
+ Rainbow("URL: #{latest_build.url}").green,
95
+ border,
96
+ ]
97
+
98
+ puts report
99
+ true
100
+ else
101
+ false
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,15 @@
1
+ module BuildkiteGraphqlRuby
2
+ module Commands
3
+ class CommandMap
4
+ require "buildkite_graphql_ruby/commands/branch_status"
5
+
6
+ COMMAND_MAP = {
7
+ 'branch_status' => BranchStatus
8
+ }
9
+
10
+ def self.get_command(command_string:)
11
+ COMMAND_MAP.fetch(command_string).new
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ module BuildkiteGraphqlRuby
2
+ class QueryBuilder
3
+ def branch_status(branch:)
4
+ query = <<~EOS
5
+ {
6
+ viewer {
7
+ user {
8
+ name
9
+ builds(branch:"#{branch}") {
10
+ count
11
+ edges {
12
+ node{
13
+ branch
14
+ state
15
+ url
16
+ uuid
17
+ scheduledAt
18
+ startedAt
19
+ finishedAt
20
+ pullRequest {
21
+ id
22
+ }
23
+ jobs(last: 20) {
24
+ edges {
25
+ node {
26
+ \.\.\. on JobTypeCommand {
27
+ agent {
28
+ id
29
+ }
30
+ passed
31
+ label
32
+ artifacts(first: 100) {
33
+ edges {
34
+ node {
35
+ id
36
+ path
37
+
38
+ state
39
+ downloadURL
40
+ }
41
+ }
42
+ }
43
+ command
44
+ url
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ EOS
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ module BuildkiteGraphqlRuby
2
+ class QueryRunner
3
+
4
+ class ResponseError < StandardError; end
5
+
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'json'
9
+
10
+ def run_query(query:, options:)
11
+ request_from_api(query, options)
12
+ end
13
+
14
+ private
15
+
16
+ def request_from_api(query, options)
17
+ payload = {
18
+ query: query,
19
+ }.to_json
20
+
21
+ uri = URI.parse("https://graphql.buildkite.com/v1")
22
+ https = Net::HTTP.new(uri.host,uri.port)
23
+ https.use_ssl = true
24
+ https.read_timeout = 500
25
+ req = Net::HTTP::Post.new(uri.path, initheader = {'Authorization' =>"Bearer #{options.api_token}"})
26
+ req.body = payload
27
+ res = https.request(req)
28
+
29
+ raise ResponseError, res.message if res.code_type != Net::HTTPOK
30
+
31
+ JSON.parse(res.body)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module BuildkiteGraphqlRuby
2
+ module ResultsParsers
3
+ class Artifact < OpenStruct
4
+ def self.from_response(response)
5
+ node = response['node']
6
+ # node.keys ["id", "path", "state", "downloadURL"]
7
+
8
+ new(
9
+ id: node['id'],
10
+ path: node['path'],
11
+ state: node['state'],
12
+ download_url: node['downloadURL'],
13
+ )
14
+ end
15
+
16
+ private_class_method :new
17
+
18
+ def download
19
+ require 'open-uri'
20
+ file_contents = open(self.download_url) { |f| f.read }
21
+ file_contents
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ module BuildkiteGraphqlRuby
2
+ module ResultsParsers
3
+ class Build < OpenStruct
4
+ require "buildkite_graphql_ruby/results_parsers/job"
5
+
6
+ # Potential build states (from graphQL docs)
7
+ # SKIPPED
8
+ # The build was skipped
9
+
10
+ # SCHEDULED
11
+ # The build has yet to start running jobs
12
+
13
+ # RUNNING
14
+ # The build is currently running jobs
15
+
16
+ # PASSED
17
+ # The build passed
18
+
19
+ # FAILED
20
+ # The build failed
21
+
22
+ # CANCELING
23
+ # The build is currently being canceled
24
+
25
+ # CANCELED
26
+ # The build was canceled
27
+
28
+ # BLOCKED
29
+ # The build is blocked
30
+
31
+ # NOT_RUN
32
+ # The build wasn't run
33
+
34
+ def self.from_response(response)
35
+ node = response['node']
36
+
37
+ jobs = node['jobs']["edges"].select{|j| j['node'].keys.count > 0 }.map{|build_response| ResultsParsers::Job.from_response(build_response) }
38
+
39
+ new(
40
+ branch: node['branch'],
41
+ state: node['state'],
42
+ url: node['url'],
43
+ started_at: node['startedAt'] && Time.parse(node['startedAt']),
44
+ finished_at: node['finishedAt'] && Time.parse(node['finishedAt']),
45
+ pull_request: node['pullRequest'],
46
+ jobs: jobs,
47
+ )
48
+ end
49
+
50
+ private_class_method :new
51
+
52
+ def finished?
53
+ !self.finished_at.nil?
54
+ end
55
+
56
+ def passed?
57
+ self.state == 'PASSED'
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ module BuildkiteGraphqlRuby
2
+ module ResultsParsers
3
+ class Job < OpenStruct
4
+ require "buildkite_graphql_ruby/results_parsers/artifact"
5
+
6
+ def self.from_response(response)
7
+ node = response['node']
8
+
9
+ # node.keys: ["agent", "passed", "label", "artifacts", "command", "url"]
10
+ artifacts = node['artifacts']["edges"].map{|build_response| ResultsParsers::Artifact.from_response(build_response) }
11
+
12
+ new(
13
+ agent: node['agent'],
14
+ passed: node['passed'],
15
+ label: node['label'],
16
+ command: node['command'],
17
+ url: node['url'],
18
+ artifacts: artifacts,
19
+ )
20
+ end
21
+
22
+ private_class_method :new
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ module BuildkiteGraphqlRuby
2
+ module ResultsParsers
3
+ class RspecResults < OpenStruct
4
+
5
+ class Example < OpenStruct
6
+ def self.from_response(response)
7
+ keys = ["id", "description", "full_description", "status", "file_path", "line_number", "run_time", "pending_message", "screenshot", "exception"]
8
+ example_data = keys.map do |key|
9
+ [key, response[key]]
10
+ end
11
+
12
+ new(example_data.to_h)
13
+ end
14
+
15
+ private_class_method :new
16
+
17
+ def passed?
18
+ self.status == 'passed'
19
+ end
20
+ end
21
+
22
+ def self.from_response(raw_response)
23
+ response = JSON.parse(raw_response)
24
+ examples = response['examples'].map{|e| Example.from_response(e)}
25
+
26
+ new(
27
+ summary: response['summary'],
28
+ summary_line: response['summary_line'],
29
+ examples: examples,
30
+ )
31
+ end
32
+
33
+ private_class_method :new
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module BuildkiteGraphqlRuby
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "buildkite_graphql_ruby/version"
2
+
3
+ module BuildkiteGraphqlRuby
4
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: buildkite_graphql_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Evanczuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rainbow
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - alexevanczuk@gmail.com
86
+ executables:
87
+ - buildkite_graphql_ruby
88
+ - console
89
+ - setup
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - bin/buildkite_graphql_ruby
94
+ - bin/console
95
+ - bin/setup
96
+ - lib/buildkite_graphql_ruby.rb
97
+ - lib/buildkite_graphql_ruby/cli.rb
98
+ - lib/buildkite_graphql_ruby/commands/branch_status.rb
99
+ - lib/buildkite_graphql_ruby/commands/command_map.rb
100
+ - lib/buildkite_graphql_ruby/query_builder.rb
101
+ - lib/buildkite_graphql_ruby/query_runner.rb
102
+ - lib/buildkite_graphql_ruby/results_parsers/artifact.rb
103
+ - lib/buildkite_graphql_ruby/results_parsers/build.rb
104
+ - lib/buildkite_graphql_ruby/results_parsers/job.rb
105
+ - lib/buildkite_graphql_ruby/results_parsers/rspec_results.rb
106
+ - lib/buildkite_graphql_ruby/version.rb
107
+ homepage:
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.2.3
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Wrapper for buildkite graphql library
131
+ test_files: []