semaph 0.2.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +39 -0
- data/.semaphore/semaphore.yml +5 -1
- data/Gemfile.lock +21 -1
- data/README.md +85 -11
- data/Rakefile +4 -1
- data/lib/semaph/client.rb +115 -0
- data/lib/semaph/commands.rb +63 -0
- data/lib/semaph/commands/reload_command.rb +5 -4
- data/lib/semaph/commands/rerun_workflow_command.rb +16 -0
- data/lib/semaph/commands/stop_workflow_command.rb +16 -0
- data/lib/semaph/formatting.rb +15 -1
- data/lib/semaph/model/job.rb +70 -1
- data/lib/semaph/model/job_collection.rb +18 -5
- data/lib/semaph/model/pipeline.rb +54 -2
- data/lib/semaph/model/pipeline_collection.rb +0 -1
- data/lib/semaph/model/project.rb +1 -1
- data/lib/semaph/model/project_collection.rb +0 -1
- data/lib/semaph/model/promotion.rb +31 -0
- data/lib/semaph/model/promotion_collection.rb +21 -0
- data/lib/semaph/model/workflow.rb +28 -4
- data/lib/semaph/model/workflow_collection.rb +0 -1
- data/lib/semaph/shells/organisation/organisation_shell.rb +15 -11
- data/lib/semaph/shells/organisation/projects_list_command.rb +1 -0
- data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
- data/lib/semaph/shells/organisations/organisations_select_command.rb +8 -1
- data/lib/semaph/shells/organisations/organisations_shell.rb +6 -2
- data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
- data/lib/semaph/shells/pipeline/job_log_command.rb +44 -0
- data/lib/semaph/shells/pipeline/job_log_grep_command.rb +28 -0
- data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
- data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
- data/lib/semaph/shells/pipeline/jobs_list_command.rb +6 -16
- data/lib/semaph/shells/pipeline/jobs_poll_command.rb +55 -0
- data/lib/semaph/shells/pipeline/pipeline_shell.rb +42 -7
- data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
- data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
- data/lib/semaph/shells/project/project_shell.rb +31 -18
- data/lib/semaph/shells/project/workflows_list_command.rb +4 -16
- data/lib/semaph/shells/project/workflows_select_command.rb +10 -3
- data/lib/semaph/shells/workflow/pipelines_list_command.rb +4 -1
- data/lib/semaph/shells/workflow/pipelines_select_command.rb +9 -3
- data/lib/semaph/shells/workflow/workflow_shell.rb +13 -42
- data/lib/semaph/version.rb +1 -1
- data/semaph.gemspec +1 -0
- metadata +33 -6
- data/lib/semaph/api.rb +0 -61
@@ -18,6 +18,12 @@ module Semaph
|
|
18
18
|
|
19
19
|
def execute(name)
|
20
20
|
selected_project = @project_collection.all.find { |project| project.name == name }
|
21
|
+
|
22
|
+
unless selected_project
|
23
|
+
puts "There is no project called #{name}"
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
21
27
|
::Semaph::Shells::Project::ProjectShell.new(selected_project).push
|
22
28
|
end
|
23
29
|
end
|
@@ -17,7 +17,14 @@ module Semaph
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def execute(name)
|
20
|
-
|
20
|
+
organisation = @organisations[name]
|
21
|
+
|
22
|
+
unless organisation
|
23
|
+
puts "There is no organisation called #{name}"
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
::Semaph::Shells::Organisation::OrganisationShell.new(organisation).push
|
21
28
|
end
|
22
29
|
end
|
23
30
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "semaph/commands/reload_command"
|
1
2
|
require "semaph/shells/organisations/organisations_list_command"
|
2
3
|
require "semaph/shells/organisations/organisations_select_command"
|
3
4
|
require "shell_shock/context"
|
@@ -11,8 +12,11 @@ module Semaph
|
|
11
12
|
def initialize(organisations)
|
12
13
|
@organisations = organisations
|
13
14
|
@prompt = "🏗 > "
|
14
|
-
|
15
|
-
add_command
|
15
|
+
organisations_list_command = OrganisationsListCommand.new(organisations)
|
16
|
+
add_command organisations_list_command, "list-organisations", "ls"
|
17
|
+
add_command OrganisationsSelectCommand.new(organisations), "select-organisation", "cd"
|
18
|
+
add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
|
19
|
+
organisations_list_command.execute("")
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class JobDebugCommand
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize(job_collection)
|
8
|
+
@job_collection = job_collection
|
9
|
+
@usage = "<job index>"
|
10
|
+
@help = "debug job"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(index_string)
|
14
|
+
index = index_string.to_i - 1
|
15
|
+
|
16
|
+
job = @job_collection.all[index]
|
17
|
+
|
18
|
+
unless job
|
19
|
+
puts "There is no job at position #{index}"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
puts "Debugging #{job.description}"
|
24
|
+
system("sem debug job #{job.id}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Semaph
|
4
|
+
module Shells
|
5
|
+
module Pipeline
|
6
|
+
class JobLogCommand
|
7
|
+
attr_reader :usage, :help
|
8
|
+
|
9
|
+
def initialize(job_collection)
|
10
|
+
@job_collection = job_collection
|
11
|
+
@usage = "<job index>"
|
12
|
+
@help = "retrieve log for job"
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(index_string)
|
16
|
+
base = "tmp/logs/pipeline/#{@job_collection.pipeline.id}"
|
17
|
+
with_job(index_string) do |job|
|
18
|
+
unless job.finished?
|
19
|
+
puts "This job has not finished yet"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
system("less #{job.write_log(base)}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def with_job(index_string)
|
30
|
+
index = index_string.to_i - 1
|
31
|
+
|
32
|
+
job = @job_collection.all[index]
|
33
|
+
|
34
|
+
unless job
|
35
|
+
puts "There is no job at position #{index}"
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
yield job
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class JobLogGrepCommand
|
5
|
+
attr_reader :usage, :help, :job_collection
|
6
|
+
|
7
|
+
def initialize(job_collection, scope)
|
8
|
+
@job_collection = job_collection
|
9
|
+
@scope = scope
|
10
|
+
@usage = "<expression>"
|
11
|
+
@help = "retrieve logs for #{scope} jobs and grep for text"
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(expression)
|
15
|
+
base = "tmp/logs/pipeline/#{job_collection.pipeline.id}"
|
16
|
+
@job_collection.send(@scope).each do |job|
|
17
|
+
unless job.finished?
|
18
|
+
puts "skipping incomplete job #{job.id}"
|
19
|
+
next
|
20
|
+
end
|
21
|
+
job.write_log(base)
|
22
|
+
end
|
23
|
+
system("ag #{expression} #{base} | less")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class JobShowCommand
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize(job_collection)
|
8
|
+
@job_collection = job_collection
|
9
|
+
@usage = "<job index>"
|
10
|
+
@help = "show job"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(index_string)
|
14
|
+
index = index_string.to_i - 1
|
15
|
+
|
16
|
+
job = @job_collection.all[index]
|
17
|
+
|
18
|
+
unless job
|
19
|
+
puts "There is no job at position #{index}"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
puts job.show
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class JobStopCommand
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize(job_collection)
|
8
|
+
@job_collection = job_collection
|
9
|
+
@usage = "<job index>"
|
10
|
+
@help = "stop job"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(index_string)
|
14
|
+
index = index_string.to_i - 1
|
15
|
+
|
16
|
+
job = @job_collection.all[index]
|
17
|
+
|
18
|
+
unless job
|
19
|
+
puts "There is no job at position #{index}"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
job.stop
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "semaph/formatting"
|
2
|
+
|
1
3
|
module Semaph
|
2
4
|
module Shells
|
3
5
|
module Pipeline
|
@@ -9,24 +11,12 @@ module Semaph
|
|
9
11
|
@help = "list jobs"
|
10
12
|
end
|
11
13
|
|
12
|
-
def execute(_whatever)
|
13
|
-
@job_collection.
|
14
|
-
|
14
|
+
def execute(_whatever = nil)
|
15
|
+
@job_collection.reload
|
16
|
+
@job_collection.all.each_with_index do |job, index|
|
17
|
+
puts [::Semaph::Formatting.index(index + 1), job.description].join(" ")
|
15
18
|
end
|
16
19
|
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def description(job)
|
21
|
-
[
|
22
|
-
job.block_name,
|
23
|
-
job.block_state,
|
24
|
-
job.block_result,
|
25
|
-
job.name,
|
26
|
-
job.status,
|
27
|
-
job.result,
|
28
|
-
].join(" ")
|
29
|
-
end
|
30
20
|
end
|
31
21
|
end
|
32
22
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "semaph/formatting"
|
2
|
+
|
3
|
+
module Semaph
|
4
|
+
module Shells
|
5
|
+
module Pipeline
|
6
|
+
class JobsPollCommand
|
7
|
+
attr_reader :usage, :help, :job_collection
|
8
|
+
|
9
|
+
def initialize(job_collection, list_command)
|
10
|
+
@job_collection = job_collection
|
11
|
+
@list_command = list_command
|
12
|
+
@help = "poll jobs"
|
13
|
+
@can_notify = !`which terminal-notifier`.chomp.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(_whatever = nil)
|
17
|
+
report_and_reload(15) while job_collection.incomplete.count.positive? && job_collection.failed.count.zero?
|
18
|
+
report_final
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def report_and_reload(period)
|
24
|
+
report_incomplete(job_collection.incomplete)
|
25
|
+
sleep period
|
26
|
+
job_collection.reload
|
27
|
+
end
|
28
|
+
|
29
|
+
def report_final
|
30
|
+
@list_command.execute
|
31
|
+
failed_job_count = job_collection.failed.count
|
32
|
+
notify(
|
33
|
+
"Workflow completed",
|
34
|
+
"#{job_collection.pipeline.workflow.description} completed with #{failed_job_count} failed jobs",
|
35
|
+
failed_job_count.positive?,
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def report_incomplete(incomplete_jobs)
|
40
|
+
puts "polling #{job_collection.pipeline.workflow.description}"
|
41
|
+
puts "#{incomplete_jobs.count} incomplete jobs remaining:"
|
42
|
+
incomplete_jobs.each { |job| puts job.description }
|
43
|
+
end
|
44
|
+
|
45
|
+
def notify(title, message, failed)
|
46
|
+
return unless @can_notify
|
47
|
+
|
48
|
+
sound = failed ? "basso" : "blow"
|
49
|
+
|
50
|
+
`terminal-notifier -group semaph -message "#{message}" -title "#{title}" -sound #{sound}`
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,23 +1,58 @@
|
|
1
|
+
require "semaph/commands"
|
2
|
+
require "semaph/shells/pipeline/job_debug_command"
|
3
|
+
require "semaph/shells/pipeline/job_log_command"
|
4
|
+
require "semaph/shells/pipeline/job_log_grep_command"
|
5
|
+
require "semaph/shells/pipeline/job_show_command"
|
6
|
+
require "semaph/shells/pipeline/job_stop_command"
|
1
7
|
require "semaph/shells/pipeline/jobs_list_command"
|
8
|
+
require "semaph/shells/pipeline/jobs_poll_command"
|
9
|
+
require "semaph/shells/pipeline/promote_command"
|
10
|
+
require "semaph/shells/pipeline/promotions_list_command"
|
2
11
|
require "shell_shock/context"
|
3
12
|
|
4
13
|
module Semaph
|
5
14
|
module Shells
|
6
15
|
module Pipeline
|
7
16
|
class PipelineShell
|
17
|
+
attr_reader :pipeline
|
18
|
+
|
8
19
|
include ShellShock::Context
|
9
20
|
|
10
21
|
def initialize(pipeline)
|
11
22
|
@pipeline = pipeline
|
12
23
|
workflow = pipeline.workflow
|
13
24
|
project = workflow.project
|
14
|
-
@prompt = "🏗 #{project.client.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
@prompt = "🏗 #{project.client.name} #{project.name} #{workflow.id} #{pipeline.yaml} > "
|
26
|
+
add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
|
27
|
+
add_commands
|
28
|
+
jobs_list_command.execute
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add_commands
|
34
|
+
::Semaph::Commands.workflow_commands(self, pipeline.workflow)
|
35
|
+
add_job_collection_commands(pipeline.job_collection)
|
36
|
+
add_command PromoteCommand.new(pipeline), "promote"
|
37
|
+
add_command PromotionsListCommand.new(pipeline.promotion_collection), "list-promotions"
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_job_collection_commands(job_collection)
|
41
|
+
add_command JobsPollCommand.new(job_collection, jobs_list_command), "poll"
|
42
|
+
add_command JobLogCommand.new(job_collection), "log"
|
43
|
+
add_command JobDebugCommand.new(job_collection), "debug"
|
44
|
+
add_command JobShowCommand.new(job_collection), "show"
|
45
|
+
add_command JobStopCommand.new(job_collection), "stop"
|
46
|
+
add_command JobLogGrepCommand.new(job_collection, :all), "grep-all-logs"
|
47
|
+
add_command JobLogGrepCommand.new(job_collection, :failed), "grep-failed-logs"
|
48
|
+
end
|
49
|
+
|
50
|
+
def jobs_list_command
|
51
|
+
return @jobs_list_command if @jobs_list_command
|
52
|
+
|
53
|
+
@jobs_list_command = JobsListCommand.new(pipeline.job_collection)
|
54
|
+
add_command @jobs_list_command, "list-jobs", "ls"
|
55
|
+
@jobs_list_command
|
21
56
|
end
|
22
57
|
end
|
23
58
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class PromoteCommand
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize(pipeline)
|
8
|
+
@pipeline = pipeline
|
9
|
+
@usage = "<promotion name>"
|
10
|
+
@help = "promote pipeline"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(name)
|
14
|
+
@pipeline.promote(name.chomp.strip)
|
15
|
+
rescue ::Semaph::Client::RequestException => e
|
16
|
+
puts e.message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Semaph
|
2
|
+
module Shells
|
3
|
+
module Pipeline
|
4
|
+
class PromotionsListCommand
|
5
|
+
attr_reader :usage, :help
|
6
|
+
|
7
|
+
def initialize(promotion_collection)
|
8
|
+
@promotion_collection = promotion_collection
|
9
|
+
@help = "list promotions"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(_whatever)
|
13
|
+
@promotion_collection.reload
|
14
|
+
@promotion_collection.all.each_with_index do |promotion, index|
|
15
|
+
puts "#{index + 1} #{promotion.description}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require "semaph/commands/visit_url_command"
|
2
1
|
require "semaph/commands/reload_command"
|
2
|
+
require "semaph/commands/visit_url_command"
|
3
3
|
require "semaph/shells/project/workflows_list_command"
|
4
4
|
require "semaph/shells/project/workflows_select_command"
|
5
5
|
require "shell_shock/context"
|
@@ -8,30 +8,33 @@ module Semaph
|
|
8
8
|
module Shells
|
9
9
|
module Project
|
10
10
|
class ProjectShell
|
11
|
+
attr_reader :project
|
12
|
+
|
11
13
|
include ShellShock::Context
|
12
14
|
|
13
15
|
def initialize(project)
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
::Semaph::Commands::ReloadCommand.new(workflow_collection, "reload workflows"),
|
19
|
-
"reload-workflows",
|
20
|
-
)
|
21
|
-
add_command(
|
22
|
-
::Semaph::Commands::VisitUrlCommand.new(
|
23
|
-
"https://#{project.client.host}/projects/#{project.name}",
|
24
|
-
"browse to project",
|
25
|
-
),
|
26
|
-
"open-project",
|
27
|
-
)
|
28
|
-
add_command WorkflowsListCommand.new(workflow_collection), "list-workflows"
|
29
|
-
add_command WorkflowsSelectCommand.new(workflow_collection), "select-workflow"
|
16
|
+
@project = project
|
17
|
+
@prompt = "🏗 #{project.client.name} #{project.name} > "
|
18
|
+
add_commands
|
19
|
+
@workflows_list_command.execute("")
|
30
20
|
end
|
31
21
|
|
32
22
|
private
|
33
23
|
|
34
|
-
def
|
24
|
+
def workflow_collection
|
25
|
+
project.workflow_collection
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_commands
|
29
|
+
add_github_command
|
30
|
+
add_open_project_command
|
31
|
+
@workflows_list_command = WorkflowsListCommand.new(workflow_collection)
|
32
|
+
add_command @workflows_list_command, "list-workflows", "ls"
|
33
|
+
add_command WorkflowsSelectCommand.new(workflow_collection), "select-workflow", "cd"
|
34
|
+
add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_github_command
|
35
38
|
return unless project.github_url
|
36
39
|
|
37
40
|
add_command(
|
@@ -42,6 +45,16 @@ module Semaph
|
|
42
45
|
"open-github",
|
43
46
|
)
|
44
47
|
end
|
48
|
+
|
49
|
+
def add_open_project_command
|
50
|
+
add_command(
|
51
|
+
::Semaph::Commands::VisitUrlCommand.new(
|
52
|
+
"https://#{project.client.host}/projects/#{project.name}",
|
53
|
+
"browse to project",
|
54
|
+
),
|
55
|
+
"open-project",
|
56
|
+
)
|
57
|
+
end
|
45
58
|
end
|
46
59
|
end
|
47
60
|
end
|