sem 0.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.
- 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
|