semaph 0.2.0 → 0.7.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 +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
|