bitbar-concourse 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.
@@ -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: []