termcity 0.4.0
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 +7 -0
- data/.gitignore +10 -0
- data/Gemfile +8 -0
- data/Rakefile +2 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/exe/termcity +126 -0
- data/lib/termcity.rb +5 -0
- data/lib/termcity/api.rb +42 -0
- data/lib/termcity/cli.rb +49 -0
- data/lib/termcity/formatters/iterm2.rb +30 -0
- data/lib/termcity/formatters/simple.rb +83 -0
- data/lib/termcity/formatters/util/color.rb +33 -0
- data/lib/termcity/summary.rb +91 -0
- data/lib/termcity/version.rb +3 -0
- data/termcity.gemspec +28 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc6bd575a30f6f6645b4b570814226738ac30c94
|
4
|
+
data.tar.gz: 8a17bd4fcbe547978d0fe25cf950ac157f251c8f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: feb982a06dfe3285d17df91e688758bf17928f2186f325d59ac3e8e394f0336c6e34ce7159c99c485f6f6888fef93f7aba0d5391dcf625552641310a007ff4bb
|
7
|
+
data.tar.gz: 58a7a64dfb71260546b71d4b6dae2d3b81c5732cd456f963eedf09a6755a32ebef7345a8ad6a31c23a9cf98bb599bd1d133fde1fd1f2c72f8bd93137d1fb5367
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "termcity"
|
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
|
data/bin/setup
ADDED
data/exe/termcity
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'base64'
|
3
|
+
require 'yaml'
|
4
|
+
require "termcity/cli"
|
5
|
+
require "optparse"
|
6
|
+
require "uri"
|
7
|
+
|
8
|
+
HELP = <<~TXT
|
9
|
+
Run termcity to see your test results on TeamCity:
|
10
|
+
|
11
|
+
termcity --branch myBranchName --project MyProjectId
|
12
|
+
|
13
|
+
[Options]
|
14
|
+
|
15
|
+
-b, --branch: defaults to current repo branch
|
16
|
+
-p, --project: defaults to camelcasing repo directory
|
17
|
+
-r, --revision: defaults to latest revision known to CI
|
18
|
+
|
19
|
+
[CREDENTIALS]:
|
20
|
+
|
21
|
+
The backend uses your Github organization memberships to verify your
|
22
|
+
access to the data. You must configure the CLI with a token granting
|
23
|
+
it access to read your org memberships:
|
24
|
+
|
25
|
+
1. Visit https://github.com/settings/tokens/new
|
26
|
+
2. Generate a new token
|
27
|
+
3. Only grant the `read:org` permission (leave all others unchecked)
|
28
|
+
4. Paste the token in the configuration file as specified below.
|
29
|
+
|
30
|
+
[CONFIGURATION]
|
31
|
+
Put a json formatted file at ~/.termcity with the following data:
|
32
|
+
|
33
|
+
{
|
34
|
+
"host": "https://my.termcity.api.com",
|
35
|
+
"token": "your github auth token"
|
36
|
+
}
|
37
|
+
|
38
|
+
[OUTPUT]
|
39
|
+
|
40
|
+
Builds are listed in alphabetical order. Builds that have a result for
|
41
|
+
the latest revision but are also re-enqueued have a ",q" next to them.
|
42
|
+
For example, "failed,q" means the latest build on the branch has
|
43
|
+
failed, but it is enqueued to run again.
|
44
|
+
TXT
|
45
|
+
|
46
|
+
options = {}
|
47
|
+
OptionParser.new do |opts|
|
48
|
+
opts.banner = "Usage: termcity [options]"
|
49
|
+
|
50
|
+
opts.on("-b BRANCH", "--branch BRANCH", "git branch (defaults to current repo branch)") do |b|
|
51
|
+
options[:branch] = b
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on("-p PROJECT_ID", "--project PROJECT_ID", "The project id (defaults to camelCasing on repo directory)") do |p|
|
55
|
+
options[:project_id] = p
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on("-r REVISION", "--revision REVISION", "The git revision to look for (defaults to revision of latest test run)") do |r|
|
59
|
+
options[:revision] = r
|
60
|
+
|
61
|
+
if !r.match(/\h{40}/)
|
62
|
+
warn("#{r} is not a full/valid git revision")
|
63
|
+
exit(1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on("-h", "--help", "See help") do
|
68
|
+
puts HELP
|
69
|
+
exit(0)
|
70
|
+
end
|
71
|
+
|
72
|
+
end.parse!
|
73
|
+
|
74
|
+
if ARGV.any?
|
75
|
+
puts HELP
|
76
|
+
exit(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
if !options[:branch]
|
80
|
+
options[:branch] = `git rev-parse --abbrev-ref @`.chomp
|
81
|
+
if $? != 0
|
82
|
+
warn "Could not identify branch. Please specify one"
|
83
|
+
exit(1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if !options[:project_id]
|
88
|
+
root_dir =
|
89
|
+
`git rev-parse --show-toplevel`
|
90
|
+
.chomp
|
91
|
+
.tap { raise "detecting root dir failed" unless $?==0}
|
92
|
+
|
93
|
+
camelcase_root_dir =
|
94
|
+
File
|
95
|
+
.basename(root_dir)
|
96
|
+
.sub(/^[a-z\d]*/) { $&.capitalize }
|
97
|
+
.gsub(/(?:_|(\/))([a-z\d]*)/) { $2.capitalize }
|
98
|
+
|
99
|
+
options[:project_id] = camelcase_root_dir
|
100
|
+
end
|
101
|
+
|
102
|
+
creds_file = File.expand_path("~/.termcity")
|
103
|
+
if !File.exist?(creds_file)
|
104
|
+
warn "Could not find a credentials file. See --help for info."
|
105
|
+
exit(1)
|
106
|
+
end
|
107
|
+
|
108
|
+
yaml = YAML.load_file(creds_file)
|
109
|
+
|
110
|
+
if yaml["host"].nil?
|
111
|
+
warn "Could not find `host` in credentials file. See --help for more info"
|
112
|
+
exit(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
if yaml["token"].nil?
|
116
|
+
warn "Could not find `token` in credentials file. See --help for more info"
|
117
|
+
exit(1)
|
118
|
+
end
|
119
|
+
|
120
|
+
Termcity::CLI.simple_format(
|
121
|
+
token: yaml["token"],
|
122
|
+
host: yaml["host"],
|
123
|
+
branch: options[:branch],
|
124
|
+
revision: options[:revision],
|
125
|
+
project_id: options[:project_id]
|
126
|
+
)
|
data/lib/termcity.rb
ADDED
data/lib/termcity/api.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "json"
|
2
|
+
require "net/http"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module Termcity
|
6
|
+
class Api
|
7
|
+
ALLOWED_BUILD_FILTERS = [
|
8
|
+
:branch,
|
9
|
+
:count,
|
10
|
+
:defaultFilter,
|
11
|
+
:failedtoStart,
|
12
|
+
:project,
|
13
|
+
:revision,
|
14
|
+
:state,
|
15
|
+
]
|
16
|
+
|
17
|
+
def initialize(token:, host:)
|
18
|
+
@token = token
|
19
|
+
@host = host
|
20
|
+
end
|
21
|
+
|
22
|
+
def summary(branch:, project_id:, revision:)
|
23
|
+
path = "/builds?branch=#{branch}&project_id=#{project_id}"
|
24
|
+
path = "#{path}&revision=#{revision}" if revision
|
25
|
+
url = URI.join(@host, path).to_s
|
26
|
+
get(url)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def get(url)
|
32
|
+
uri = URI.parse(url)
|
33
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
34
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
35
|
+
request["authorization"] = @token
|
36
|
+
http.use_ssl = true
|
37
|
+
response = http.request(request)
|
38
|
+
raise "request failure:\n\n#{response.body}" unless response.code == "200"
|
39
|
+
JSON.parse(response.body)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/termcity/cli.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "termcity/api"
|
2
|
+
require "termcity/version"
|
3
|
+
require "termcity/formatters/simple"
|
4
|
+
require "termcity/formatters/iterm2"
|
5
|
+
require "termcity/summary"
|
6
|
+
|
7
|
+
module Termcity
|
8
|
+
class CLI
|
9
|
+
def self.simple_format(**args)
|
10
|
+
new(**args).simple_format
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(branch:, token:, host:, project_id:, revision:)
|
14
|
+
@branch = branch
|
15
|
+
@token = token
|
16
|
+
@host = host
|
17
|
+
@project_id = project_id
|
18
|
+
@revision = revision
|
19
|
+
end
|
20
|
+
|
21
|
+
def simple_format
|
22
|
+
api = Termcity::Api.new(
|
23
|
+
token: @token,
|
24
|
+
host: @host,
|
25
|
+
)
|
26
|
+
|
27
|
+
summary = Termcity::Summary.new(
|
28
|
+
api: api,
|
29
|
+
branch: @branch,
|
30
|
+
revision: @revision,
|
31
|
+
project_id: @project_id,
|
32
|
+
)
|
33
|
+
|
34
|
+
formatter =
|
35
|
+
if using_iterm?
|
36
|
+
Termcity::Formatters::Iterm2
|
37
|
+
else
|
38
|
+
Termcity::Formatters::Simple
|
39
|
+
end
|
40
|
+
|
41
|
+
formatter.new($stdout).format(summary)
|
42
|
+
end
|
43
|
+
|
44
|
+
def using_iterm?
|
45
|
+
ENV["TERM_PROGRAM"] == "iTerm.app" &&
|
46
|
+
ENV.fetch("TERM_PROGRAM_VERSION", "").match(/3.[23456789].[123456789]/)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "termcity/formatters/simple"
|
2
|
+
|
3
|
+
module Termcity
|
4
|
+
module Formatters
|
5
|
+
class Iterm2
|
6
|
+
|
7
|
+
attr_reader :io, :simple_formatter
|
8
|
+
def initialize(io)
|
9
|
+
@io = io
|
10
|
+
@simple_formatter = Simple.new(io)
|
11
|
+
end
|
12
|
+
|
13
|
+
def format(summary)
|
14
|
+
rows = summary.builds.map do |raw:, status:|
|
15
|
+
cols = []
|
16
|
+
cols << simple_formatter.status_string(status)
|
17
|
+
cols << linkify(raw.fetch("web_url"))
|
18
|
+
cols << raw.fetch("build_type")
|
19
|
+
cols.join(" ")
|
20
|
+
end
|
21
|
+
|
22
|
+
@io.puts(simple_formatter.summarize(summary, rows))
|
23
|
+
end
|
24
|
+
|
25
|
+
def linkify(url)
|
26
|
+
"\e]8;;#{url}\aLink\e]8;;\a"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "termcity/formatters/util/color"
|
2
|
+
|
3
|
+
module Termcity
|
4
|
+
module Formatters
|
5
|
+
class Simple
|
6
|
+
include Utils::Color
|
7
|
+
|
8
|
+
STATUSES = {
|
9
|
+
queued: "queued",
|
10
|
+
failing: "failing",
|
11
|
+
running: "running",
|
12
|
+
failed_to_start: "failstrt",
|
13
|
+
failed: "failed",
|
14
|
+
success: "success"
|
15
|
+
}
|
16
|
+
|
17
|
+
COLORS = {
|
18
|
+
failing: :red,
|
19
|
+
queued: :yellow,
|
20
|
+
running: :blue,
|
21
|
+
failed_to_start: :default,
|
22
|
+
failed: :red,
|
23
|
+
success: :green
|
24
|
+
}
|
25
|
+
|
26
|
+
attr_reader :io
|
27
|
+
def initialize(io)
|
28
|
+
@io = io
|
29
|
+
end
|
30
|
+
|
31
|
+
def format(summary)
|
32
|
+
rows = summary.builds.map do |raw:, status:|
|
33
|
+
cols = []
|
34
|
+
cols << status_string(status)
|
35
|
+
cols << raw.fetch("build_type")
|
36
|
+
cols << raw.fetch("web_url")
|
37
|
+
|
38
|
+
cols.join(" : ")
|
39
|
+
end
|
40
|
+
|
41
|
+
@io.puts(summarize(summary, rows))
|
42
|
+
end
|
43
|
+
|
44
|
+
def status_string(type:, re_enqueued:)
|
45
|
+
name = STATUSES.fetch(type)
|
46
|
+
name = "#{name},q" if re_enqueued
|
47
|
+
color = COLORS[type]
|
48
|
+
|
49
|
+
colorize(name.ljust(10), color)
|
50
|
+
end
|
51
|
+
|
52
|
+
def summarize(summary, rows)
|
53
|
+
if summary.builds.empty?
|
54
|
+
"No builds found"
|
55
|
+
elsif summary.counts[:queued] == summary.counts[:total]
|
56
|
+
"This revision may still be in the queue (or may be unkown/old)"
|
57
|
+
else
|
58
|
+
[
|
59
|
+
rows,
|
60
|
+
"",
|
61
|
+
"Revision: #{summary.builds.first.dig(:raw, "sha")}",
|
62
|
+
"Overview: #{summary.overview_link}",
|
63
|
+
counts(summary),
|
64
|
+
].join("\n")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def counts(summary)
|
69
|
+
[
|
70
|
+
["Total", summary.counts[:total]],
|
71
|
+
["Success", summary.counts[:success]],
|
72
|
+
["Failure", summary.counts[:failure]],
|
73
|
+
["Running", summary.counts[:running]],
|
74
|
+
["FailedToStart", summary.counts[:failed_to_start]],
|
75
|
+
["Queued", summary.counts[:queued]],
|
76
|
+
["Re-Queued", summary.counts[:re_enqueued]]
|
77
|
+
]
|
78
|
+
.map {|name, count| "#{name}: #{count}"}
|
79
|
+
.join(", ")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Termcity
|
2
|
+
module Formatters
|
3
|
+
module Utils
|
4
|
+
module Color
|
5
|
+
CODES = {
|
6
|
+
red: 31,
|
7
|
+
green: 32,
|
8
|
+
yellow: 33,
|
9
|
+
blue: 34,
|
10
|
+
pink: 35,
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
CODES.each do |color, code|
|
14
|
+
define_method(color) do |string|
|
15
|
+
colorize_code(string, code)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def colorize(string, color_name)
|
20
|
+
return string unless CODES.key?(color_name)
|
21
|
+
colorize_code(string, CODES[color_name])
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# colorization
|
27
|
+
def colorize_code(string, color_code)
|
28
|
+
"\e[#{color_code}m#{string}\e[0m"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Termcity
|
2
|
+
class Summary
|
3
|
+
def initialize(revision:, api:, branch:, project_id:)
|
4
|
+
@branch = branch
|
5
|
+
@project_id = project_id
|
6
|
+
@api = api
|
7
|
+
@revision = revision
|
8
|
+
end
|
9
|
+
|
10
|
+
def builds
|
11
|
+
data[:builds]
|
12
|
+
end
|
13
|
+
|
14
|
+
def counts
|
15
|
+
data[:counts]
|
16
|
+
end
|
17
|
+
|
18
|
+
def overview_link
|
19
|
+
data.dig(:links, :overview)
|
20
|
+
end
|
21
|
+
|
22
|
+
def data
|
23
|
+
# this is messy but allows for a single pass
|
24
|
+
@data ||= begin
|
25
|
+
counts = {
|
26
|
+
total: 0,
|
27
|
+
success: 0,
|
28
|
+
failure: 0,
|
29
|
+
running: 0,
|
30
|
+
queued: 0,
|
31
|
+
failed_to_start: 0,
|
32
|
+
re_enqueued: 0,
|
33
|
+
}
|
34
|
+
|
35
|
+
summary = @api.summary(branch: @branch, project_id: @project_id, revision: @revision)
|
36
|
+
|
37
|
+
builds = summary.fetch("builds")
|
38
|
+
.sort_by {|b| b.fetch("build_type")}
|
39
|
+
.map {|b| summarize(b, counts) }
|
40
|
+
|
41
|
+
{
|
42
|
+
links: {overview: summary.dig("links", "overview")},
|
43
|
+
builds: builds,
|
44
|
+
counts: counts
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def summarize(build, counts)
|
50
|
+
status = status(build)
|
51
|
+
|
52
|
+
count_type =
|
53
|
+
if [:failing, :failed].include?(status[:type])
|
54
|
+
:failure
|
55
|
+
else
|
56
|
+
status[:type]
|
57
|
+
end
|
58
|
+
counts[count_type] += 1
|
59
|
+
counts[:total] += 1
|
60
|
+
counts[:re_enqueued] +=1 if status[:re_enqueued]
|
61
|
+
|
62
|
+
{
|
63
|
+
raw: build,
|
64
|
+
status: status
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def status(build)
|
69
|
+
re_enqueued = build.fetch("re_enqueued")
|
70
|
+
|
71
|
+
if build.fetch("state") == "queued"
|
72
|
+
{type: :queued, re_enqueued: false}
|
73
|
+
elsif build.fetch("state") == "running"
|
74
|
+
if build.fetch("status") == "FAILURE"
|
75
|
+
{type: :failing, re_enqueued: re_enqueued}
|
76
|
+
else
|
77
|
+
{type: :running, re_enqueued: re_enqueued}
|
78
|
+
end
|
79
|
+
else
|
80
|
+
if build.fetch("failed_to_start", false)
|
81
|
+
{type: :failed_to_start, re_enqueued: re_enqueued}
|
82
|
+
elsif build.fetch("status") == "FAILURE"
|
83
|
+
{type: :failed, re_enqueued: re_enqueued}
|
84
|
+
else
|
85
|
+
{type: :success, re_enqueued: re_enqueued}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
data/termcity.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "termcity/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "termcity"
|
8
|
+
spec.version = Termcity::VERSION
|
9
|
+
spec.authors = ["Pete Kinnecom"]
|
10
|
+
spec.email = ["git@k7u7.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Terminal view of TeamCity}
|
13
|
+
spec.description = %q{See TeamCity build status for a branch in your terminal. Pipe it to grep or whatever. Get nicely formatted links if you use iTerm2.}
|
14
|
+
# spec.homepage = "none"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
19
|
+
spec.files = Dir.chdir(__dir__) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: termcity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pete Kinnecom
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-24 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
|
+
description: See TeamCity build status for a branch in your terminal. Pipe it to grep
|
42
|
+
or whatever. Get nicely formatted links if you use iTerm2.
|
43
|
+
email:
|
44
|
+
- git@k7u7.com
|
45
|
+
executables:
|
46
|
+
- termcity
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- ".gitignore"
|
51
|
+
- Gemfile
|
52
|
+
- Rakefile
|
53
|
+
- bin/console
|
54
|
+
- bin/setup
|
55
|
+
- exe/termcity
|
56
|
+
- lib/termcity.rb
|
57
|
+
- lib/termcity/api.rb
|
58
|
+
- lib/termcity/cli.rb
|
59
|
+
- lib/termcity/formatters/iterm2.rb
|
60
|
+
- lib/termcity/formatters/simple.rb
|
61
|
+
- lib/termcity/formatters/util/color.rb
|
62
|
+
- lib/termcity/summary.rb
|
63
|
+
- lib/termcity/version.rb
|
64
|
+
- termcity.gemspec
|
65
|
+
homepage:
|
66
|
+
licenses: []
|
67
|
+
metadata:
|
68
|
+
allowed_push_host: https://rubygems.org
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.5.2
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Terminal view of TeamCity
|
89
|
+
test_files: []
|