buildkite_graphql_ruby 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []