bitbar-concourse 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.envrc.sample +4 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +85 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +76 -0
- data/LICENSE +21 -0
- data/README.markdown +73 -0
- data/Rakefile +23 -0
- data/bitbar-concourse.gemspec +26 -0
- data/exe/bitbar-concourse +37 -0
- data/lib/bitbar.rb +8 -0
- data/lib/bitbar/concourse/aggregate_presenter.rb +24 -0
- data/lib/bitbar/concourse/build_presenter.rb +54 -0
- data/lib/bitbar/concourse/error_presenter.rb +16 -0
- data/lib/concourse.rb +10 -0
- data/lib/concourse/build.rb +59 -0
- data/lib/concourse/client.rb +67 -0
- data/lib/concourse/concourse-logo.png +0 -0
- data/lib/concourse/concourse.svg +1 -0
- data/lib/concourse/job.rb +43 -0
- data/lib/concourse/pipeline.rb +36 -0
- data/lib/concourse/target.rb +34 -0
- data/lib/relative_time.rb +22 -0
- data/public/flintstone.png +0 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -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
|
data/.envrc.sample
ADDED
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -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
|
data/.rubocop_todo.yml
ADDED
@@ -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'
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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.
|
data/README.markdown
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/lib/bitbar.rb
ADDED
@@ -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
|
data/lib/concourse.rb
ADDED
@@ -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
|
Binary file
|
@@ -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: []
|