bitbar-concourse 1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2a747d280c77cfd104af098c2819fe9609c948925b163bb1cf0e81ec9208aba1
4
+ data.tar.gz: 71f7b0b0eeda50383dab819c33a9a38e0bcfd2ff0afbc9fbc5b445ee965522d3
5
+ SHA512:
6
+ metadata.gz: 66b09490e537d9c5317bc37e610ea15e3e2894f71f7e7216309c1c8e26f46f7dfd5bed4f935a3ce66790cfb39bee42d9125431b5bcfe61200f41f35e4d3a7010
7
+ data.tar.gz: 30b72614fd0689af6dcd49ec66eb2b60fabc9f6ce68dd31499665c8188db6fce4cc3c7191908d8bf4985eda8aa65b68658bae39234b13c4b1a482a68b07a4933
@@ -0,0 +1,4 @@
1
+ export CONCOURSE_URI=https://ci.example.com/
2
+ export CONCOURSE_USER=pipeline
3
+ export CONCOURSE_PASSWORD=REDACTED
4
+ export CONCOURSE_PIPELINE="Example Pipeline"
@@ -0,0 +1,3 @@
1
+ .cache_rake_t
2
+ pkg
3
+ .envrc
@@ -0,0 +1,18 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.5
5
+
6
+ DisplayCopNames: true
7
+
8
+ DisplayStyleGuide:
9
+ Enabled: true
10
+
11
+ Documentation:
12
+ Enabled: false
13
+
14
+ Style/Encoding:
15
+ Enabled: false
16
+
17
+ Metrics/LineLength:
18
+ Max: 120
@@ -0,0 +1,85 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-08-05 17:49:10 +0200 using RuboCop version 0.58.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: AllowSafeAssignment.
11
+ Lint/AssignmentInCondition:
12
+ Exclude:
13
+ - 'exe/bitbar-concourse'
14
+ - 'lib/bitbar/concourse/build_presenter.rb'
15
+
16
+ # Offense count: 1
17
+ Lint/DuplicateMethods:
18
+ Exclude:
19
+ - 'lib/concourse/pipeline.rb'
20
+
21
+ # Offense count: 4
22
+ Metrics/AbcSize:
23
+ Max: 34
24
+
25
+ # Offense count: 9
26
+ # Configuration parameters: CountComments, ExcludedMethods.
27
+ # ExcludedMethods: refine
28
+ Metrics/BlockLength:
29
+ Max: 146
30
+
31
+ # Offense count: 1
32
+ Metrics/CyclomaticComplexity:
33
+ Max: 12
34
+
35
+ # Offense count: 6
36
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
37
+ # URISchemes: http, https
38
+ Metrics/LineLength:
39
+ Max: 176
40
+
41
+ # Offense count: 3
42
+ # Configuration parameters: CountComments.
43
+ Metrics/MethodLength:
44
+ Max: 16
45
+
46
+ # Offense count: 1
47
+ # Configuration parameters: CountComments.
48
+ Metrics/ModuleLength:
49
+ Max: 148
50
+
51
+ # Offense count: 1
52
+ Metrics/PerceivedComplexity:
53
+ Max: 8
54
+
55
+ # Offense count: 4
56
+ # Configuration parameters: EnforcedStyle.
57
+ # SupportedStyles: snake_case, normalcase, non_integer
58
+ Naming/VariableNumber:
59
+ Exclude:
60
+ - 'spec/unit/job_spec.rb'
61
+ - 'spec/unit/pipeline_spec.rb'
62
+
63
+ # Offense count: 2
64
+ Security/Open:
65
+ Exclude:
66
+ - 'lib/concourse/client.rb'
67
+
68
+ # Offense count: 1
69
+ Style/DoubleNegation:
70
+ Exclude:
71
+ - 'lib/concourse/build.rb'
72
+
73
+ # Offense count: 1
74
+ # Configuration parameters: MinBodyLength.
75
+ Style/GuardClause:
76
+ Exclude:
77
+ - 'lib/concourse/client.rb'
78
+
79
+ # Offense count: 1
80
+ # Cop supports --auto-correct.
81
+ # Configuration parameters: EnforcedStyle, MinSize.
82
+ # SupportedStyles: percent, brackets
83
+ Style/SymbolArray:
84
+ Exclude:
85
+ - 'Rakefile'
@@ -0,0 +1 @@
1
+ 2.5.1
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
@@ -0,0 +1,76 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bitbar-concourse (1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.5.2)
10
+ public_suffix (>= 2.0.2, < 4.0)
11
+ ast (2.4.0)
12
+ byebug (10.0.2)
13
+ coderay (1.1.2)
14
+ crack (0.4.3)
15
+ safe_yaml (~> 1.0.0)
16
+ diff-lcs (1.3)
17
+ hashdiff (0.3.7)
18
+ jaro_winkler (1.5.1)
19
+ method_source (0.9.0)
20
+ parallel (1.12.1)
21
+ parser (2.5.1.2)
22
+ ast (~> 2.4.0)
23
+ powerpack (0.1.2)
24
+ pry (0.11.3)
25
+ coderay (~> 1.1.0)
26
+ method_source (~> 0.9.0)
27
+ pry-byebug (3.6.0)
28
+ byebug (~> 10.0)
29
+ pry (~> 0.10)
30
+ public_suffix (3.0.2)
31
+ rainbow (3.0.0)
32
+ rake (12.3.1)
33
+ rspec (3.8.0)
34
+ rspec-core (~> 3.8.0)
35
+ rspec-expectations (~> 3.8.0)
36
+ rspec-mocks (~> 3.8.0)
37
+ rspec-core (3.8.0)
38
+ rspec-support (~> 3.8.0)
39
+ rspec-expectations (3.8.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.8.0)
42
+ rspec-mocks (3.8.0)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.8.0)
45
+ rspec-support (3.8.0)
46
+ rubocop (0.58.2)
47
+ jaro_winkler (~> 1.5.1)
48
+ parallel (~> 1.10)
49
+ parser (>= 2.5, != 2.5.1.1)
50
+ powerpack (~> 0.1)
51
+ rainbow (>= 2.2.2, < 4.0)
52
+ ruby-progressbar (~> 1.7)
53
+ unicode-display_width (~> 1.0, >= 1.0.1)
54
+ ruby-progressbar (1.9.0)
55
+ safe_yaml (1.0.4)
56
+ unicode-display_width (1.4.0)
57
+ webmock (3.4.2)
58
+ addressable (>= 2.3.6)
59
+ crack (>= 0.3.2)
60
+ hashdiff
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ bitbar-concourse!
67
+ bundler
68
+ pry
69
+ pry-byebug
70
+ rake
71
+ rspec
72
+ rubocop
73
+ webmock
74
+
75
+ BUNDLED WITH
76
+ 1.16.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Jo Liss
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ # bitbar-concourse
2
+
3
+ Presents the status of a [Concourse](https://concourse.ci/release-notes.html) pipeline in [bitbar](https://github.com/matryer/bitbar).
4
+
5
+ This started out as fork of Checkman's [concourse.check](https://github.com/cppforlife/checkman/blob/master/scripts/concourse.check), but has evolved since.
6
+
7
+ # Usage
8
+
9
+ * Install bitbar (`brew install bitbar`)
10
+ * Install this gem
11
+ * Add the following script as `~/.bitbar/concourse.1m.sh` and make it executable:
12
+
13
+ ```sh
14
+ #!/bin/bash
15
+
16
+ # Change this URL to point to your own Concourse instance
17
+ # but the public one should be ok as a demo
18
+ export CONCOURSE_URI=http://concourse.ci/
19
+
20
+ # username and password are optional
21
+ export CONCOURSE_USER=user
22
+ export CONCOURSE_PASSWORD=password
23
+
24
+ # invoke the plugin
25
+ bitbar-concourse
26
+ ```
27
+
28
+ I do not install bitbar-concourse as system gem, but I use chruby. For some reason bitbar does not seem to run a login shell, so I had to load `chruby` manually in `~/.bitbar/concourse.1m.sh` (just before the last line):
29
+
30
+ ```sh
31
+ export PATH=/usr/local/bin:$PATH
32
+ source "$(brew --prefix)/share/chruby/chruby.sh"
33
+ chruby 2.5.1
34
+ ```
35
+
36
+ * Check the bitbar icon in the system tray for an updated status of your pipeline:
37
+
38
+ Example:
39
+
40
+ ![Flintstone CI](public/flintstone.png)
41
+
42
+ # Installation
43
+
44
+ $ gem install bitbar-concourse
45
+
46
+ # Development
47
+
48
+ * Use `fswatch` to run specs whenever code or tests change. Install it with `brew install fswatch` if not available.
49
+
50
+ ```
51
+ $ fswatch --one-per-batch spec lib | xargs -n1 -I{} rspec
52
+ ```
53
+
54
+ * Since the bitbar protocol is text-based, the bitbar-concourse gem can be tested in the terminal. Just execute the script in `~/.bitbar/` in a terminal window.
55
+
56
+ # Tests
57
+
58
+ Running the integration tests requires a real Concourse server. By default, [ci.concourse-ci.org](https://ci.concourse-ci.org) is used. If you want to test with your own, which is required for the authentication tests, supply the environment variables listed in `.envrc.sample`.
59
+
60
+ # Misc
61
+
62
+ The propeller logo is in the [public domain](https://thenounproject.com/search/?q=propeller&i=13111).
63
+
64
+ # TODO
65
+
66
+ * Use Excon in order to get easy tracing
67
+ * Order latest builds in presenter first by status and then by last-run date so that the broken ones appear at the top
68
+ * Use TerminalNotifier when a build is failing:
69
+
70
+ TerminalNotifier.notify('Hello World', :open => 'http://twitter.com/alloy')
71
+
72
+ * Keep state about each job and notify only on a change (red => green etc.)
73
+ * Ignore certain tasks, e.g. release lock
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rspec/core/rake_task'
5
+ require 'bundler/gem_tasks'
6
+ require 'rubocop/rake_task'
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: [:rubocop, :'spec:all']
11
+
12
+ namespace :spec do
13
+ desc 'Run all specs'
14
+ task all: %i[unit integration]
15
+
16
+ RSpec::Core::RakeTask.new(:unit) do |t|
17
+ t.pattern = 'spec/unit/**/*_spec.rb'
18
+ end
19
+
20
+ RSpec::Core::RakeTask.new(:integration) do |t|
21
+ t.pattern = 'spec/integration/**/*_spec.rb'
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'bitbar-concourse'
9
+ spec.version = '1.1'
10
+ spec.authors = ['Steffen Uhlig']
11
+ spec.email = ['Steffen.Uhlig@de.ibm.com']
12
+ spec.summary = 'bitbar-plugin for concourse.ci'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ spec.bindir = 'exe'
16
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_development_dependency 'bundler'
20
+ spec.add_development_dependency 'pry'
21
+ spec.add_development_dependency 'pry-byebug'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'rubocop'
25
+ spec.add_development_dependency 'webmock'
26
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+
6
+ require 'concourse'
7
+ require 'bitbar'
8
+
9
+ client = Concourse::Client.new(
10
+ ENV.fetch('CONCOURSE_URI'),
11
+ username: ENV['CONCOURSE_USER'],
12
+ password: ENV['CONCOURSE_PASSWORD']
13
+ )
14
+
15
+ target_name = ENV.fetch('CONCOURSE_TARGET', 'Concourse')
16
+ ci = Concourse::Target.new(client)
17
+
18
+ begin
19
+ pipelines = ci.pipelines
20
+ rescue StandardError => e
21
+ warn Bitbar::Concourse::ErrorPresenter.new(e, target_name)
22
+ exit 1
23
+ end
24
+
25
+ pipelines = pipelines.select { |pipeline| ARGV.include?(pipeline.name) } if ARGV.any?
26
+
27
+ latest_builds = pipelines.map do |pipeline|
28
+ pipeline.jobs.map do |job|
29
+ if build = job.finished_build
30
+ Bitbar::Concourse::BuildPresenter.new(build)
31
+ end
32
+ end
33
+ end.flatten.compact.sort_by!(&:end_time).reverse
34
+
35
+ puts Bitbar::Concourse::AggregatePresenter.new(latest_builds, target_name)
36
+ puts '---'
37
+ puts latest_builds
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bitbar/concourse/build_presenter'
4
+ require 'bitbar/concourse/error_presenter'
5
+ require 'bitbar/concourse/aggregate_presenter'
6
+
7
+ module Bitbar
8
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bitbar
4
+ module Concourse
5
+ class AggregatePresenter
6
+ def initialize(builds, target_name = nil)
7
+ @builds = Array(builds.dup)
8
+ @target_name = target_name
9
+ end
10
+
11
+ def to_s
12
+ if success?
13
+ "✅ #{@target_name} | color=green"
14
+ else
15
+ "❌ #{@target_name} | color=red"
16
+ end
17
+ end
18
+
19
+ def success?
20
+ @builds.delete_if(&:success?).empty?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'relative_time'
5
+
6
+ module Bitbar
7
+ module Concourse
8
+ class BuildPresenter
9
+ extend Forwardable
10
+ def_delegator :@build, :end_time
11
+ def_delegator :@build, :success?
12
+
13
+ def initialize(build)
14
+ raise 'Build must not be nil' if build.nil?
15
+ @build = build
16
+ end
17
+
18
+ def to_s
19
+ icon = @build.success? ? '✅' : '❌'
20
+
21
+ lines = [
22
+ "#{icon} #{@build.job_name} - build ##{@build.name} | href=#{@build.url}",
23
+ "finished #{relative_end_time}; took #{elapsed_time}"
24
+ ]
25
+
26
+ if next_build = @build.next
27
+ started_at = next_build.start_time.extend(RelativeTime).to_relative
28
+ lines << "next build (##{next_build.name}) started #{started_at} | href=#{next_build.url}"
29
+ end
30
+
31
+ lines.join("\n")
32
+ end
33
+
34
+ def elapsed_time
35
+ case difference = @build.end_time.to_i - @build.start_time.to_i
36
+ when 0..59
37
+ "#{difference} seconds"
38
+ when 60..80
39
+ 'one minute'
40
+ when 81..(60 * 25)
41
+ "#{difference / 60} minutes"
42
+ when (60 * 26)..(60 * 35)
43
+ 'about half an hour'
44
+ else
45
+ "#{difference}s"
46
+ end
47
+ end
48
+
49
+ def relative_end_time
50
+ @build.end_time&.extend(RelativeTime)&.to_relative
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bitbar
4
+ module Concourse
5
+ class ErrorPresenter
6
+ def initialize(error, target_name)
7
+ @error = error
8
+ @target_name = target_name
9
+ end
10
+
11
+ def to_s
12
+ "#{@target_name}\n---\n#{@error.message}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concourse/client'
4
+ require 'concourse/target'
5
+ require 'concourse/pipeline'
6
+ require 'concourse/job'
7
+ require 'concourse/build'
8
+
9
+ module Concourse
10
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Concourse
4
+ #
5
+ # A build belongs to a job
6
+ #
7
+ class Build
8
+ attr_reader :job
9
+
10
+ def initialize(job, info)
11
+ @job = job
12
+ @info = info
13
+ end
14
+
15
+ def success?
16
+ status == 'succeeded'
17
+ end
18
+
19
+ def finished?
20
+ status != 'started'
21
+ end
22
+
23
+ def changing?
24
+ !!@info['next_build']
25
+ end
26
+
27
+ def url
28
+ @info['api_url']
29
+ end
30
+
31
+ def status
32
+ @info['status']
33
+ end
34
+
35
+ def job_name
36
+ @info['job_name']
37
+ end
38
+
39
+ def name
40
+ @info['name']
41
+ end
42
+
43
+ def start_time
44
+ Time.at(@info['start_time']) if @info['start_time']
45
+ end
46
+
47
+ def end_time
48
+ Time.at(@info['end_time']) if @info['end_time']
49
+ end
50
+
51
+ def next
52
+ @job.next_build
53
+ end
54
+
55
+ def to_s
56
+ "#{self.class.name.split('::').last.downcase} #{name} of #{@job}"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'open-uri'
5
+ require 'net/https'
6
+
7
+ module Concourse
8
+ class Client
9
+ API_BASE_PATH = '/api/v1/teams/main/'
10
+
11
+ attr_reader :base_uri
12
+
13
+ def initialize(concourse_uri, username: nil, password: nil, options: {})
14
+ @concourse_uri = URI(concourse_uri)
15
+ @username = username
16
+ @password = password
17
+ @options = options
18
+ @base_uri = @concourse_uri.merge(API_BASE_PATH)
19
+ end
20
+
21
+ def get(path)
22
+ path = path[1..-1] if path.start_with?('/')
23
+
24
+ begin
25
+ options_with_auth = if authenticated?
26
+ options.merge('Cookie' => "ATC-Authorization=\"#{bearer_token}\"")
27
+ else
28
+ options
29
+ end
30
+
31
+ open(base_uri.merge(path), options_with_auth).read
32
+ rescue OpenURI::HTTPError => e
33
+ if e.message == '401 Unauthorized' && @bearer_token
34
+ @bearer_token = nil
35
+ retry
36
+ else
37
+ raise
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :options, :username, :password
45
+
46
+ def bearer_token
47
+ @bearer_token ||= authenticate
48
+ end
49
+
50
+ def authenticated?
51
+ username && password
52
+ end
53
+
54
+ def authenticate
55
+ io = open(
56
+ @concourse_uri.merge('/auth/basic/token?team_name=main'),
57
+ options.merge(http_basic_authentication: [@username, @password])
58
+ )
59
+
60
+ code, message = io.status
61
+ raise message unless code == '200'
62
+
63
+ json = JSON.parse(io.read)
64
+ "#{json['type']} #{json['value']}"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><g><path fill-rule="evenodd" clip-rule="evenodd" d="M50.071,24.743c6.475,0,11.733,5.208,11.733,11.625 c0,6.423-5.259,11.631-11.733,11.631c-6.48,0-11.733-5.208-11.733-11.631C38.338,29.951,43.591,24.743,50.071,24.743"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M63.21,32.222l0.093,0.092c14.102-2.42,26.854-9.807,35.923-20.8l0.012-0.023 C99.729,10.934,100,10.229,100,9.5c0-1.697-1.425-3.067-3.171-3.067c-0.179,0-0.351,0.011-0.528,0.04l-0.092-0.109 c-14.134,2.294-26.954,9.589-36.106,20.53l0.172,0.029c1.422,1.543,2.474,3.384,3.083,5.396L63.21,32.222z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3.596,6.535l0.098-0.062c-0.175-0.029-0.35-0.04-0.522-0.04 C1.423,6.433,0,7.803,0,9.5c0,0.7,0.247,1.382,0.703,1.928l0.043,0.045C9.758,22.49,22.476,29.894,36.543,32.313l-0.006,0.006 c0.608-2.002,1.663-3.842,3.088-5.38l-0.005-0.046C30.51,15.952,17.732,8.657,3.646,6.363L3.596,6.535z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M52.953,49.874v0.022c-1.001,0.195-2.026,0.299-3.062,0.299 c-1.063,0-2.124-0.109-3.165-0.321l-0.089,0.132c-2.392,6.526-3.613,13.425-3.613,20.376c0,7.197,1.308,14.332,3.866,21.059 l0.04,0.086c0.464,1.273,1.646,2.11,2.961,2.11c1.316,0,2.498-0.837,2.968-2.11l-0.062-0.086 c2.552-6.727,3.859-13.861,3.859-21.059c0-6.951-1.221-13.85-3.607-20.376L52.953,49.874z"></path></g></svg>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module Concourse
6
+ #
7
+ # A job belongs to a pipeline
8
+ # A job has many builds
9
+ # A job has one finished_build
10
+ # A job has one next_build
11
+ #
12
+ class Job
13
+ attr_reader :pipeline
14
+
15
+ def initialize(pipeline, info)
16
+ @pipeline = pipeline
17
+ @info = info
18
+ end
19
+
20
+ def name
21
+ @info['name']
22
+ end
23
+
24
+ def to_s
25
+ "#{self.class.name.split('::').last.downcase} #{name} of #{pipeline}"
26
+ end
27
+
28
+ def builds
29
+ JSON.parse(@pipeline.get("/#{CGI.escape(name)}/builds")).map do |build|
30
+ Build.new(self, build)
31
+ end
32
+ end
33
+
34
+ def finished_build
35
+ Build.new(self, @info['finished_build']) if @info['finished_build']
36
+ end
37
+
38
+ def next_build
39
+ next_build = @info['next_build']
40
+ Build.new(self, next_build) if next_build
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module Concourse
6
+ #
7
+ # A pipeline belongs to a target
8
+ # A pipeline has many jobs
9
+ #
10
+ class Pipeline
11
+ attr_reader :name
12
+
13
+ def initialize(target, info)
14
+ @target = target
15
+ @info = info
16
+ end
17
+
18
+ def name
19
+ @info['name']
20
+ end
21
+
22
+ def jobs
23
+ JSON.parse(get).map do |job|
24
+ Job.new(self, job)
25
+ end
26
+ end
27
+
28
+ def get(path = '')
29
+ @target.get("/#{CGI.escape(name)}/jobs#{path}")
30
+ end
31
+
32
+ def to_s
33
+ "#{self.class.name.split('::').last.downcase} #{name} of #{@target}"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'json'
5
+ require 'open-uri'
6
+ require 'net/https'
7
+
8
+ module Concourse
9
+ #
10
+ # A target has many pipelines
11
+ #
12
+ class Target
13
+ attr_reader :name
14
+
15
+ def initialize(client, name = nil)
16
+ @client = client
17
+ @name = name
18
+ end
19
+
20
+ def pipelines
21
+ JSON.parse(get).map do |json|
22
+ Pipeline.new(self, json)
23
+ end
24
+ end
25
+
26
+ def get(path = '')
27
+ @client.get('pipelines' + path)
28
+ end
29
+
30
+ def to_s
31
+ "#{self.class.name.split('::').last.downcase} #{name}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RelativeTime
4
+ # derived from http://stackoverflow.com/a/195894
5
+ def to_relative
6
+ a = (Time.now - self).to_i
7
+
8
+ case a
9
+ when 0 then 'just now'
10
+ when 1 then 'a second ago'
11
+ when 2..59 then a.to_s + ' seconds ago'
12
+ when 60..119 then 'a minute ago' # 120 = 2 minutes
13
+ when 120..3540 then (a / 60).to_i.to_s + ' minutes ago'
14
+ when 3541..7100 then 'an hour ago' # 3600 = 1 hour
15
+ when 7101..82_800 then ((a + 99) / 3600).to_i.to_s + ' hours ago'
16
+ when 82_801..172_000 then 'yesterday ' + (hour < 12 ? 'morning' : 'afternoon')
17
+ when 172_001..518_400 then ((a + 800) / (60 * 60 * 24)).to_i.to_s + ' days ago'
18
+ when 518_400..1_036_800 then 'a week ago'
19
+ else ((a + 180_000) / (60 * 60 * 24 * 7)).to_i.to_s + ' weeks ago'
20
+ end
21
+ end
22
+ end
Binary file
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitbar-concourse
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.1'
5
+ platform: ruby
6
+ authors:
7
+ - Steffen Uhlig
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-08-06 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: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
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: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description:
112
+ email:
113
+ - Steffen.Uhlig@de.ibm.com
114
+ executables:
115
+ - bitbar-concourse
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".envrc.sample"
120
+ - ".gitignore"
121
+ - ".rubocop.yml"
122
+ - ".rubocop_todo.yml"
123
+ - ".ruby-version"
124
+ - ".travis.yml"
125
+ - Gemfile
126
+ - Gemfile.lock
127
+ - LICENSE
128
+ - README.markdown
129
+ - Rakefile
130
+ - bitbar-concourse.gemspec
131
+ - exe/bitbar-concourse
132
+ - lib/bitbar.rb
133
+ - lib/bitbar/concourse/aggregate_presenter.rb
134
+ - lib/bitbar/concourse/build_presenter.rb
135
+ - lib/bitbar/concourse/error_presenter.rb
136
+ - lib/concourse.rb
137
+ - lib/concourse/build.rb
138
+ - lib/concourse/client.rb
139
+ - lib/concourse/concourse-logo.png
140
+ - lib/concourse/concourse.svg
141
+ - lib/concourse/job.rb
142
+ - lib/concourse/pipeline.rb
143
+ - lib/concourse/target.rb
144
+ - lib/relative_time.rb
145
+ - public/flintstone.png
146
+ homepage:
147
+ licenses: []
148
+ metadata: {}
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 2.7.6
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: bitbar-plugin for concourse.ci
169
+ test_files: []