sem 0.1.4 → 0.2.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.
@@ -0,0 +1,29 @@
1
+ module Sem
2
+ class Pagination
3
+ class << self
4
+ def pages(pages_per_call, &block)
5
+ instances = []
6
+ index = 1
7
+
8
+ loop do
9
+ pages = range_map(index, pages_per_call, &block)
10
+
11
+ instances += pages.select { |page| !(page.nil? || page.empty?) }
12
+ index += pages_per_call
13
+
14
+ break instances if pages.any? { |page| page.nil? || page.empty? }
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def range_map(start_index, pages_per_call)
21
+ end_index = start_index + pages_per_call - 1
22
+
23
+ (start_index..end_index).to_a.pmap do |page_index|
24
+ yield(page_index)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/sem/srn.rb ADDED
@@ -0,0 +1,42 @@
1
+ class Sem::SRN
2
+ # Semaphore Resource Name
3
+
4
+ class << self
5
+ def parse_org(semaphore_resource_name)
6
+ parse_srn(semaphore_resource_name, "org_name")
7
+ end
8
+
9
+ def parse_team(semaphore_resource_name)
10
+ parse_srn(semaphore_resource_name, "org_name/team_name")
11
+ end
12
+
13
+ def parse_project(semaphore_resource_name)
14
+ parse_srn(semaphore_resource_name, "org_name/project_name")
15
+ end
16
+
17
+ def parse_shared_config(semaphore_resource_name)
18
+ parse_srn(semaphore_resource_name, "org_name/shared_config_name")
19
+ end
20
+
21
+ def parse_user(semaphore_resource_name)
22
+ parse_srn(semaphore_resource_name, "user_name")
23
+ end
24
+
25
+ private
26
+
27
+ def parse_srn(semaphore_resource_name, format)
28
+ srn_tokens = semaphore_resource_name.to_s.split("/")
29
+ format_tokens = format.split("/")
30
+
31
+ if srn_tokens.count != format_tokens.count
32
+ resource = format_tokens.last.split("_").tap(&:pop).join(" ").capitalize
33
+
34
+ message = "#{resource} \"#{semaphore_resource_name}\" not found."
35
+
36
+ raise Sem::Errors::InvalidSRN, message
37
+ end
38
+
39
+ srn_tokens
40
+ end
41
+ end
42
+ end
data/lib/sem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sem
2
- VERSION = "0.1.4".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -1,7 +1,12 @@
1
1
  class Sem::Views::Base
2
+ class << self
3
+ def print_table(table)
4
+ Dracula::UI.print_table(table)
5
+ end
2
6
 
3
- def self.print_table(table)
4
- Dracula::UI.print_table(table)
7
+ def org_names_not_matching(resource1, resource2, srn1, srn2)
8
+ "[ERROR] Organization names not matching.\n\n" \
9
+ "#{resource1.capitalize} \"#{srn1}\" and #{resource2} \"#{srn2}\" are not in the same organization."
10
+ end
5
11
  end
6
-
7
12
  end
@@ -1,6 +1,15 @@
1
1
  class Sem::Views::Orgs < Sem::Views::Base
2
2
 
3
3
  def self.list(orgs)
4
+ if orgs.empty?
5
+ puts "You don't belong to any organization."
6
+ puts ""
7
+ puts "Create your first organization: https://semaphoreci.com/organizations/new."
8
+ puts ""
9
+
10
+ return
11
+ end
12
+
4
13
  header = ["ID", "NAME"]
5
14
 
6
15
  body = orgs.map do |org|
@@ -1,22 +1,39 @@
1
1
  class Sem::Views::Projects < Sem::Views::Base
2
+ class << self
3
+ def list(projects)
4
+ if projects.empty?
5
+ puts "You don't have any project configured on Semaphore."
6
+ puts ""
7
+ puts "Add your first project: https://semaphoreci.com/new"
8
+ puts ""
2
9
 
3
- def self.list(projects)
4
- header = ["ID", "NAME"]
10
+ return
11
+ end
5
12
 
6
- body = projects.map do |project|
7
- [project[:id], project[:name]]
13
+ header = ["ID", "NAME"]
14
+
15
+ body = projects.map do |project|
16
+ [project[:id], name(project)]
17
+ end
18
+
19
+ print_table([header, *body])
8
20
  end
9
21
 
10
- print_table([header, *body])
11
- end
22
+ def info(project)
23
+ print_table [
24
+ ["ID", project[:id]],
25
+ ["Name", name(project)],
26
+ ["Created", project[:created_at]],
27
+ ["Updated", project[:updated_at]]
28
+ ]
29
+ end
12
30
 
13
- def self.info(org)
14
- print_table [
15
- ["ID", org[:id]],
16
- ["Name", org[:name]],
17
- ["Created", org[:created_at]],
18
- ["Updated", org[:updated_at]]
19
- ]
20
- end
31
+ private
32
+
33
+ def name(project)
34
+ return unless project[:org] && project[:name]
21
35
 
36
+ "#{project[:org]}/#{project[:name]}"
37
+ end
38
+ end
22
39
  end
@@ -1,6 +1,17 @@
1
1
  class Sem::Views::SharedConfigs < Sem::Views::Base
2
2
 
3
3
  def self.list(configs)
4
+ if configs.empty?
5
+ puts "You don't have any shared configurations on Semaphore."
6
+ puts ""
7
+ puts "Create your first shared configuration:"
8
+ puts ""
9
+ puts " sem shared-config:create ORG_NAME/SHARED_CONFIG"
10
+ puts ""
11
+
12
+ return
13
+ end
14
+
4
15
  header = ["ID", "NAME", "CONFIG FILES", "ENV VARS"]
5
16
 
6
17
  body = configs.map do |config|
@@ -1,24 +1,76 @@
1
1
  class Sem::Views::Teams < Sem::Views::Base
2
+ class << self
3
+ def list(teams)
4
+ if teams.empty?
5
+ puts "You don't have any teams on Semaphore."
6
+ puts ""
7
+ puts "Create your first team:"
8
+ puts ""
9
+ puts " sem teams:create ORG_NAME/TEAM"
10
+ puts ""
2
11
 
3
- def self.list(teams)
4
- header = ["ID", "NAME", "PERMISSION", "MEMBERS"]
12
+ return
13
+ end
5
14
 
6
- body = teams.map do |team|
7
- [team[:id], team[:name], team[:permission], "#{team[:members]} members"]
15
+ header = ["ID", "NAME", "PERMISSION", "MEMBERS"]
16
+
17
+ body = teams.map do |team|
18
+ [team[:id], "#{team[:org]}/#{team[:name]}", permission(team), "#{team[:members]} members"]
19
+ end
20
+
21
+ print_table [header, *body]
8
22
  end
9
23
 
10
- print_table [header, *body]
11
- end
24
+ def info(team)
25
+ print_table [
26
+ ["ID", team[:id]],
27
+ ["Name", name(team)],
28
+ ["Permission", permission(team)],
29
+ ["Members", members(team)],
30
+ ["Created", team[:created_at]],
31
+ ["Updated", team[:updated_at]]
32
+ ]
33
+ end
12
34
 
13
- def self.info(team)
14
- print_table [
15
- ["ID", team[:id]],
16
- ["Name", team[:name]],
17
- ["Permission", team[:permission]],
18
- ["Members", "#{team[:members]} members"],
19
- ["Created", team[:created_at]],
20
- ["Updated", team[:updated_at]]
21
- ]
22
- end
35
+ def list_members(team, members)
36
+ if members.empty?
37
+ puts "You don't have any members in the team."
38
+ puts ""
39
+ puts "Add your first member:"
40
+ puts ""
41
+ puts " sem teams:members:add #{team} USERNAME"
42
+ puts ""
43
+
44
+ return
45
+ end
46
+
47
+ header = ["NAME"]
48
+
49
+ body = members.map do |user|
50
+ [user[:id]]
51
+ end
52
+
53
+ print_table [header, *body]
54
+ end
23
55
 
56
+ private
57
+
58
+ def name(team)
59
+ return unless team[:org] && team[:name]
60
+
61
+ "#{team[:org]}/#{team[:name]}"
62
+ end
63
+
64
+ def members(team)
65
+ return unless team[:members]
66
+
67
+ "#{team[:members]} members"
68
+ end
69
+
70
+ def permission(team)
71
+ return "write" if team[:permission] == "edit"
72
+
73
+ team[:permission]
74
+ end
75
+ end
24
76
  end
@@ -1,13 +1,11 @@
1
1
  class Sem::Views::Users < Sem::Views::Base
2
+ class << self
3
+ def list(users)
4
+ header = ["NAME"]
2
5
 
3
- def self.list(users)
4
- header = ["NAME"]
6
+ body = users.map { |user| [user[:id]] }
5
7
 
6
- body = users.map do |user|
7
- [user[:id]]
8
+ print_table [header, *body]
8
9
  end
9
-
10
- print_table [header, *body]
11
10
  end
12
-
13
11
  end
data/lib/sem.rb CHANGED
@@ -2,21 +2,45 @@ require "sem/version"
2
2
  require "dracula"
3
3
  require "semaphore_client"
4
4
  require "fileutils"
5
+ require "pmap"
5
6
 
6
7
  module Sem
7
8
  require "sem/errors"
8
- require "sem/credentials"
9
+ require "sem/configuration"
10
+ require "sem/srn"
11
+ require "sem/pagination"
9
12
  require "sem/cli"
10
13
  require "sem/api"
11
14
  require "sem/views"
15
+ LOG_LEVEL_TRACE = :trace
16
+ LOG_LEVEL_ERROR = :error
12
17
 
13
18
  class << self
19
+ attr_writer :log_level
20
+
21
+ def log_level
22
+ @log_level || LOG_LEVEL_ERROR
23
+ end
14
24
 
15
25
  # Returns exit status as a number.
16
26
  def start(args)
27
+ if args.include?("--trace")
28
+ @log_level = LOG_LEVEL_TRACE
29
+
30
+ args.delete("--trace")
31
+ end
32
+
17
33
  Sem::CLI.start(args)
18
34
 
19
35
  0
36
+ rescue Sem::Errors::ResourceException => e
37
+ puts e.message
38
+
39
+ 1
40
+ rescue Sem::Errors::InvalidSRN => e
41
+ on_invalid_srn(e)
42
+
43
+ 1
20
44
  rescue Sem::Errors::Auth::NoCredentials
21
45
  on_no_credentials
22
46
 
@@ -33,6 +57,12 @@ module Sem
33
57
 
34
58
  private
35
59
 
60
+ def on_invalid_srn(exception)
61
+ puts "[ERROR] Invalid parameter formatting."
62
+ puts ""
63
+ puts exception.message
64
+ end
65
+
36
66
  def on_no_credentials
37
67
  puts "[ERROR] You are not logged in."
38
68
  puts ""
@@ -48,7 +78,10 @@ module Sem
48
78
  def on_unhandled_error(exception)
49
79
  puts "[PANIC] Unhandled error."
50
80
  puts ""
51
- puts "Well, this is emberassing. An unknown error was detected."
81
+ puts "Well, this is embarrassing. An unknown error was detected."
82
+ puts ""
83
+ puts "Exception:"
84
+ puts exception.message
52
85
  puts ""
53
86
  puts "Backtrace: "
54
87
  puts exception.backtrace
data/sem.gemspec CHANGED
@@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "semaphore_client", "2.0.9"
25
- spec.add_dependency "dracula", "~> 0.1.1"
24
+ spec.add_dependency "semaphore_client", "2.1.3"
25
+ spec.add_dependency "dracula", "~> 0.3.0"
26
+ spec.add_dependency "pmap", "~> 1.1.1"
26
27
 
27
28
  spec.add_development_dependency "bundler", "~> 1.14"
28
29
  spec.add_development_dependency "rake", "~> 10.0"
@@ -30,5 +31,5 @@ Gem::Specification.new do |spec|
30
31
  spec.add_development_dependency "rubocop", "~> 0.47.1"
31
32
  spec.add_development_dependency "rubocop-rspec", "1.5.0"
32
33
  spec.add_development_dependency "simplecov", "~> 0.13"
33
- spec.add_development_dependency "byebug"
34
+ spec.add_development_dependency "byebug", "~> 9.0.0"
34
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Šarčević
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-18 00:00:00.000000000 Z
11
+ date: 2017-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semaphore_client
@@ -16,28 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.9
19
+ version: 2.1.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.9
26
+ version: 2.1.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dracula
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.1
33
+ version: 0.3.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.1.1
40
+ version: 0.3.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pmap
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +140,16 @@ dependencies:
126
140
  name: byebug
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - ">="
143
+ - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '0'
145
+ version: 9.0.0
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - ">="
150
+ - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '0'
152
+ version: 9.0.0
139
153
  description: Semaphore CLI
140
154
  email:
141
155
  - igor@renderedtext.com
@@ -166,17 +180,19 @@ files:
166
180
  - lib/sem/api/teams.rb
167
181
  - lib/sem/api/traits.rb
168
182
  - lib/sem/api/traits/associated_with_org.rb
183
+ - lib/sem/api/traits/associated_with_project.rb
169
184
  - lib/sem/api/traits/associated_with_shared_config.rb
170
185
  - lib/sem/api/traits/associated_with_team.rb
171
186
  - lib/sem/api/users.rb
172
- - lib/sem/api/users_with_permissions.rb
173
187
  - lib/sem/cli.rb
174
188
  - lib/sem/cli/orgs.rb
175
189
  - lib/sem/cli/projects.rb
176
190
  - lib/sem/cli/shared_configs.rb
177
191
  - lib/sem/cli/teams.rb
178
- - lib/sem/credentials.rb
192
+ - lib/sem/configuration.rb
179
193
  - lib/sem/errors.rb
194
+ - lib/sem/pagination.rb
195
+ - lib/sem/srn.rb
180
196
  - lib/sem/version.rb
181
197
  - lib/sem/views.rb
182
198
  - lib/sem/views/base.rb
@@ -187,7 +203,6 @@ files:
187
203
  - lib/sem/views/shared_configs.rb
188
204
  - lib/sem/views/teams.rb
189
205
  - lib/sem/views/users.rb
190
- - lib/sem/views/users_with_permissions.rb
191
206
  - sem.gemspec
192
207
  homepage: https://github.com/renderedtext/cli
193
208
  licenses:
@@ -1,57 +0,0 @@
1
- module Sem
2
- module API
3
- class UsersWithPermissions < Base
4
- class << self
5
- LEVELS = { "owner" => 3, "admin" => 2, "write" => 1, "read" => 0 }.freeze
6
-
7
- def list_owners_for_org(org_name)
8
- Sem::API::Orgs.list_owners(org_name).map { |user| user.merge(:permission => "owner") }
9
- end
10
-
11
- def list_admins_for_org(org_name)
12
- Sem::API::Orgs.list_admins(org_name).map { |user| user.merge(:permission => "admin") }
13
- end
14
-
15
- def list_for_org(org_name)
16
- all_teams = client.teams.list_for_org(org_name)
17
-
18
- team_groups = teams_by_permission(all_teams)
19
-
20
- user_groups = users_for_team_groups(team_groups)
21
-
22
- user_groups.reduce({}) { |acc, teams| acc.merge(teams) }.values
23
- end
24
-
25
- private
26
-
27
- def teams_by_permission(all_teams)
28
- groups = all_teams.group_by(&:permission)
29
-
30
- to_hash(groups.sort_by { |permission, _| LEVELS[permission] }).values
31
- end
32
-
33
- def users_for_team_groups(groups)
34
- groups.map { |teams| users_for_teams(teams) }.flatten
35
- end
36
-
37
- def users_for_teams(teams)
38
- teams.map { |team| users_for_team(team) }.flatten.uniq
39
- end
40
-
41
- def users_for_team(team)
42
- users = client.users.list_for_team(team.id)
43
-
44
- to_hash(users.map { |user| [user.username, transform_user(user, team)] })
45
- end
46
-
47
- def transform_user(user, team)
48
- Users.to_hash(user).merge(:permission => team.permission)
49
- end
50
-
51
- def to_hash(array)
52
- Hash[array]
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,19 +0,0 @@
1
- module Sem
2
- class Credentials
3
- PATH = File.expand_path("~/.sem/credentials").freeze
4
-
5
- def self.write(auth_token)
6
- dirname = File.dirname(PATH)
7
- FileUtils.mkdir_p(dirname)
8
-
9
- File.write(PATH, auth_token)
10
- File.chmod(0o0600, PATH)
11
- end
12
-
13
- def self.read
14
- raise Sem::Errors::Auth::NoCredentials unless File.file?(PATH)
15
-
16
- File.read(PATH).strip
17
- end
18
- end
19
- end
@@ -1,13 +0,0 @@
1
- class Sem::Views::UsersWithPermissions < Sem::Views::Base
2
-
3
- def self.list(users)
4
- header = ["NAME", "PERMISSION"]
5
-
6
- body = users.map do |user|
7
- [user[:id], user[:permission]]
8
- end
9
-
10
- print_table [header, *body]
11
- end
12
-
13
- end