sem 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.rubocop.yml +103 -0
- data/Gemfile +6 -0
- data/README.md +27 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/sem +5 -0
- data/guides.md +168 -0
- data/ids.md +74 -0
- data/lib/sem.rb +53 -0
- data/lib/sem/api.rb +14 -0
- data/lib/sem/api/base.rb +17 -0
- data/lib/sem/api/env_vars.rb +20 -0
- data/lib/sem/api/files.rb +19 -0
- data/lib/sem/api/orgs.rb +54 -0
- data/lib/sem/api/projects.rb +33 -0
- data/lib/sem/api/shared_configs.rb +75 -0
- data/lib/sem/api/teams.rb +54 -0
- data/lib/sem/api/traits.rb +9 -0
- data/lib/sem/api/traits/associated_with_org.rb +13 -0
- data/lib/sem/api/traits/associated_with_shared_config.rb +29 -0
- data/lib/sem/api/traits/associated_with_team.rb +29 -0
- data/lib/sem/api/users.rb +26 -0
- data/lib/sem/api/users_with_permissions.rb +53 -0
- data/lib/sem/cli.rb +20 -0
- data/lib/sem/cli/orgs.rb +45 -0
- data/lib/sem/cli/projects.rb +17 -0
- data/lib/sem/cli/shared_configs.rb +97 -0
- data/lib/sem/cli/teams.rb +126 -0
- data/lib/sem/errors.rb +8 -0
- data/lib/sem/version.rb +3 -0
- data/lib/sem/views.rb +7 -0
- data/lib/sem/views/base.rb +7 -0
- data/lib/sem/views/env_vars.rb +13 -0
- data/lib/sem/views/files.rb +13 -0
- data/lib/sem/views/orgs.rb +22 -0
- data/lib/sem/views/projects.rb +22 -0
- data/lib/sem/views/shared_configs.rb +24 -0
- data/lib/sem/views/teams.rb +24 -0
- data/lib/sem/views/users.rb +13 -0
- data/lib/sem/views/users_with_permissions.rb +13 -0
- data/sem.gemspec +33 -0
- metadata +201 -0
data/lib/sem.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require "sem/version"
|
2
|
+
require "dracula"
|
3
|
+
require "semaphore_client"
|
4
|
+
|
5
|
+
module Sem
|
6
|
+
require "sem/errors"
|
7
|
+
require "sem/cli"
|
8
|
+
require "sem/api"
|
9
|
+
require "sem/views"
|
10
|
+
|
11
|
+
# Returns exit status as a number.
|
12
|
+
def self.start(args)
|
13
|
+
Sem::CLI.start(args)
|
14
|
+
|
15
|
+
0
|
16
|
+
rescue Sem::Errors::Auth::NoCredentials
|
17
|
+
on_no_credentials
|
18
|
+
|
19
|
+
1
|
20
|
+
rescue Sem::Errors::Auth::InvalidCredentials
|
21
|
+
on_invalid_credentials
|
22
|
+
|
23
|
+
1
|
24
|
+
rescue StandardError => e
|
25
|
+
on_unhandled_error(e)
|
26
|
+
|
27
|
+
1
|
28
|
+
end
|
29
|
+
|
30
|
+
private_class_method def self.on_no_credentials
|
31
|
+
puts "[ERROR] You are not logged in."
|
32
|
+
puts ""
|
33
|
+
puts "Log in with '#{Sem::CLI.program_name} login --auth-token <token>'"
|
34
|
+
end
|
35
|
+
|
36
|
+
private_class_method def self.on_invalid_credentials
|
37
|
+
puts "[ERROR] Your credentials are invalid."
|
38
|
+
puts ""
|
39
|
+
puts "Log in with '#{Sem::CLI.program_name} login --auth-token <token>'"
|
40
|
+
end
|
41
|
+
|
42
|
+
private_class_method def self.on_unhandled_error(exception)
|
43
|
+
puts "[PANIC] Unhandled error."
|
44
|
+
puts ""
|
45
|
+
puts "Well, this is emberassing. An unknown error was detected."
|
46
|
+
puts ""
|
47
|
+
puts "Backtrace: "
|
48
|
+
puts exception.backtrace
|
49
|
+
puts ""
|
50
|
+
puts "Please report this issue to https://semaphoreci.com/support."
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/sem/api.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
require_relative "api/traits"
|
4
|
+
require_relative "api/base"
|
5
|
+
require_relative "api/users_with_permissions"
|
6
|
+
require_relative "api/orgs"
|
7
|
+
require_relative "api/users"
|
8
|
+
require_relative "api/projects"
|
9
|
+
require_relative "api/env_vars"
|
10
|
+
require_relative "api/files"
|
11
|
+
require_relative "api/shared_configs"
|
12
|
+
require_relative "api/teams"
|
13
|
+
end
|
14
|
+
end
|
data/lib/sem/api/base.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Base
|
4
|
+
CREDENTIALS_PATH = "~/.sem/credentials".freeze
|
5
|
+
|
6
|
+
def self.client
|
7
|
+
@client ||= begin
|
8
|
+
path = File.expand_path(CREDENTIALS_PATH)
|
9
|
+
|
10
|
+
auth_token = File.read(path).delete("\n")
|
11
|
+
|
12
|
+
SemaphoreClient.new(auth_token)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class EnvVars < Base
|
4
|
+
extend Traits::AssociatedWithSharedConfig
|
5
|
+
|
6
|
+
def self.api
|
7
|
+
client.env_vars
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.to_hash(env_var)
|
11
|
+
{
|
12
|
+
:id => env_var.id,
|
13
|
+
:name => env_var.name,
|
14
|
+
:encrypted? => env_var.encrypted,
|
15
|
+
:content => env_var.content
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Files < Base
|
4
|
+
extend Traits::AssociatedWithSharedConfig
|
5
|
+
|
6
|
+
def self.api
|
7
|
+
client.config_files
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.to_hash(files)
|
11
|
+
{
|
12
|
+
:id => files.id,
|
13
|
+
:name => files.path,
|
14
|
+
:encrypted? => files.encrypted
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/sem/api/orgs.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Orgs < Base
|
4
|
+
def self.list
|
5
|
+
orgs = api.list
|
6
|
+
|
7
|
+
orgs.map { |org| to_hash(org) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.info(name)
|
11
|
+
org = api.get(name)
|
12
|
+
|
13
|
+
to_hash(org)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.list_teams(org_name)
|
17
|
+
Sem::API::Teams.list_for_org(org_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.list_users(org_name)
|
21
|
+
Sem::API::Users.list_for_org(org_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.list_admins(org_name)
|
25
|
+
admin_teams = list_teams(org_name).select { |team| team[:permission] == "admin" }
|
26
|
+
|
27
|
+
admins = admin_teams.map { |team| client.users.list_for_team(team[:id]) }.flatten
|
28
|
+
|
29
|
+
admins.map { |admin| Sem::API::Users.to_hash(admin) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.list_owners(org_name)
|
33
|
+
owners_team = list_teams(org_name).find { |team| team[:name] == "Owners" }
|
34
|
+
|
35
|
+
owners = client.users.list_for_team(owners_team[:id])
|
36
|
+
|
37
|
+
owners.map { |owner| Sem::API::Users.to_hash(owner) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.api
|
41
|
+
client.orgs
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.to_hash(org)
|
45
|
+
{
|
46
|
+
:id => org.id,
|
47
|
+
:username => org.username,
|
48
|
+
:created_at => org.created_at,
|
49
|
+
:updated_at => org.updated_at
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Projects < Base
|
4
|
+
extend Traits::AssociatedWithOrg
|
5
|
+
extend Traits::AssociatedWithTeam
|
6
|
+
|
7
|
+
def self.list
|
8
|
+
org_names = Orgs.list.map { |org| org[:username] }
|
9
|
+
|
10
|
+
org_names.map { |name| list_for_org(name) }.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.info(path)
|
14
|
+
org_name, project_name = path.split("/")
|
15
|
+
|
16
|
+
list_for_org(org_name).find { |project| project[:name] == project_name }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.api
|
20
|
+
client.projects
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.to_hash(project)
|
24
|
+
{
|
25
|
+
:id => project.id,
|
26
|
+
:name => project.name,
|
27
|
+
:created_at => project.created_at,
|
28
|
+
:updated_at => project.updated_at
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class SharedConfigs < Base
|
4
|
+
extend Traits::AssociatedWithTeam
|
5
|
+
extend Traits::AssociatedWithOrg
|
6
|
+
|
7
|
+
def self.list
|
8
|
+
org_names = Orgs.list.map { |org| org[:username] }
|
9
|
+
|
10
|
+
org_names.map { |name| list_for_org(name) }.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.info(path)
|
14
|
+
org_name, config_name = path.split("/")
|
15
|
+
|
16
|
+
list_for_org(org_name).find { |config| config[:name] == config_name }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create(org_name, args)
|
20
|
+
config = api.create_for_org(org_name, args)
|
21
|
+
|
22
|
+
to_hash(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.update(path, args)
|
26
|
+
shared_config = info(path)
|
27
|
+
|
28
|
+
shared_config = api.update(shared_config[:id], args)
|
29
|
+
|
30
|
+
to_hash(shared_config)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.delete(path)
|
34
|
+
id = info(path)[:id]
|
35
|
+
|
36
|
+
api.delete(id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.list_env_vars(path)
|
40
|
+
Sem::API::EnvVars.list_for_shared_config(path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.list_files(path)
|
44
|
+
Sem::API::Files.list_for_shared_config(path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.api
|
48
|
+
client.shared_configs
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.to_hash(config)
|
52
|
+
{
|
53
|
+
:id => config.id,
|
54
|
+
:name => config.name,
|
55
|
+
:config_files => config_files_count(config.id),
|
56
|
+
:env_vars => env_vars_count(config.id),
|
57
|
+
:created_at => config.created_at,
|
58
|
+
:updated_at => config.updated_at
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
private
|
64
|
+
|
65
|
+
def config_files_count(config_id)
|
66
|
+
client.config_files.list_for_shared_config(config_id).to_a.size
|
67
|
+
end
|
68
|
+
|
69
|
+
def env_vars_count(config_id)
|
70
|
+
client.env_vars.list_for_shared_config(config_id).to_a.size
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Teams < Base
|
4
|
+
extend Traits::AssociatedWithOrg
|
5
|
+
|
6
|
+
def self.list
|
7
|
+
org_names = Sem::API::Orgs.list.map { |org| org[:username] }
|
8
|
+
|
9
|
+
org_names.map { |name| list_for_org(name) }.flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.info(path)
|
13
|
+
org_name, team_name = path.split("/")
|
14
|
+
|
15
|
+
list_for_org(org_name).find { |team| team[:name] == team_name }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.create(org_name, args)
|
19
|
+
team = api.create_for_org(org_name, args)
|
20
|
+
|
21
|
+
to_hash(team)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.update(path, args)
|
25
|
+
team = info(path)
|
26
|
+
|
27
|
+
team = api.update(team[:id], args)
|
28
|
+
|
29
|
+
to_hash(team)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.delete(path)
|
33
|
+
id = info(path)[:id]
|
34
|
+
|
35
|
+
api.delete(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.api
|
39
|
+
client.teams
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.to_hash(team)
|
43
|
+
{
|
44
|
+
:id => team.id,
|
45
|
+
:name => team.name,
|
46
|
+
:permission => team.permission,
|
47
|
+
:members => client.users.list_for_team(team.id).count.to_s,
|
48
|
+
:created_at => team.created_at,
|
49
|
+
:updated_at => team.updated_at
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
module Traits
|
4
|
+
module AssociatedWithSharedConfig
|
5
|
+
def list_for_shared_config(shared_config_path)
|
6
|
+
shared_config = SharedConfigs.info(shared_config_path)
|
7
|
+
|
8
|
+
instances = api.list_for_shared_config(shared_config[:id])
|
9
|
+
|
10
|
+
instances.map { |instance| to_hash(instance) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_to_shared_config(shared_config_path, params)
|
14
|
+
shared_config = SharedConfigs.info(shared_config_path)
|
15
|
+
|
16
|
+
api.create_for_shared_config(shared_config[:id], params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_from_shared_config(shared_config_path, instance_name)
|
20
|
+
instances = list_for_shared_config(shared_config_path)
|
21
|
+
|
22
|
+
selected_instance = instances.find { |instance| instance[:name] == instance_name }
|
23
|
+
|
24
|
+
api.delete(selected_instance[:id])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
module Traits
|
4
|
+
module AssociatedWithTeam
|
5
|
+
def list_for_team(team_path)
|
6
|
+
team = Teams.info(team_path)
|
7
|
+
|
8
|
+
instances = api.list_for_team(team[:id])
|
9
|
+
|
10
|
+
instances.map { |instance| to_hash(instance) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_to_team(team_path, instance_path)
|
14
|
+
instance = info(instance_path)
|
15
|
+
team = Teams.info(team_path)
|
16
|
+
|
17
|
+
api.attach_to_team(instance[:id], team[:id])
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_from_team(team_path, instance_path)
|
21
|
+
instance = info(instance_path)
|
22
|
+
team = Teams.info(team_path)
|
23
|
+
|
24
|
+
api.detach_from_team(instance[:id], team[:id])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sem
|
2
|
+
module API
|
3
|
+
class Users < Base
|
4
|
+
extend Traits::AssociatedWithOrg
|
5
|
+
extend Traits::AssociatedWithTeam
|
6
|
+
|
7
|
+
def self.list
|
8
|
+
org_names = Orgs.list.map { |org| org[:username] }
|
9
|
+
|
10
|
+
org_names.map { |name| list_for_org(name) }.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.info(name)
|
14
|
+
list.find { |user| user[:id] == name }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.api
|
18
|
+
client.users
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.to_hash(user)
|
22
|
+
{ :id => user.username }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|