semaph 0.4.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +39 -0
  3. data/.semaph +9 -0
  4. data/.semaphore/semaphore.yml +5 -1
  5. data/Gemfile.lock +21 -1
  6. data/README.md +12 -8
  7. data/Rakefile +4 -1
  8. data/lib/semaph.rb +43 -8
  9. data/lib/semaph/{api.rb → client.rb} +24 -3
  10. data/lib/semaph/commands.rb +63 -0
  11. data/lib/semaph/commands/rerun_workflow_command.rb +0 -2
  12. data/lib/semaph/commands/stop_workflow_command.rb +0 -2
  13. data/lib/semaph/formatting.rb +19 -1
  14. data/lib/semaph/model/job.rb +21 -2
  15. data/lib/semaph/model/job_collection.rb +19 -3
  16. data/lib/semaph/model/pipeline.rb +29 -3
  17. data/lib/semaph/model/promotion.rb +31 -0
  18. data/lib/semaph/model/promotion_collection.rb +21 -0
  19. data/lib/semaph/model/workflow.rb +17 -3
  20. data/lib/semaph/shells/organisation/organisation_shell.rb +4 -4
  21. data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
  22. data/lib/semaph/shells/organisations/organisations_list_command.rb +2 -2
  23. data/lib/semaph/shells/organisations/organisations_select_command.rb +11 -2
  24. data/lib/semaph/shells/organisations/organisations_shell.rb +2 -2
  25. data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
  26. data/lib/semaph/shells/pipeline/job_log_command.rb +17 -15
  27. data/lib/semaph/shells/pipeline/job_log_grep_command.rb +5 -11
  28. data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
  29. data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
  30. data/lib/semaph/shells/pipeline/jobs_list_command.rb +4 -2
  31. data/lib/semaph/shells/pipeline/jobs_poll_command.rb +58 -22
  32. data/lib/semaph/shells/pipeline/pipeline_shell.rb +28 -68
  33. data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
  34. data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
  35. data/lib/semaph/shells/project/project_shell.rb +4 -2
  36. data/lib/semaph/shells/project/save_command.rb +26 -0
  37. data/lib/semaph/shells/project/workflows_list_command.rb +11 -17
  38. data/lib/semaph/shells/workflow/pipelines_list_command.rb +3 -1
  39. data/lib/semaph/shells/workflow/workflow_shell.rb +6 -66
  40. data/lib/semaph/version.rb +1 -1
  41. data/semaph.gemspec +1 -0
  42. metadata +30 -6
@@ -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,10 +11,10 @@ module Semaph
9
11
  @help = "list jobs"
10
12
  end
11
13
 
12
- def execute(_whatever)
14
+ def execute(_whatever = nil)
13
15
  @job_collection.reload
14
16
  @job_collection.all.each_with_index do |job, index|
15
- puts "#{index + 1} #{job.description}"
17
+ puts [::Semaph::Formatting.index(index + 1), job.description].join(" ")
16
18
  end
17
19
  end
18
20
  end
@@ -1,35 +1,71 @@
1
+ require "semaph/formatting"
2
+
1
3
  module Semaph
2
4
  module Shells
3
5
  module Pipeline
4
6
  class JobsPollCommand
5
- attr_reader :usage, :help
7
+ attr_reader :usage, :help, :job_collection
6
8
 
7
- def initialize(job_collection)
9
+ def initialize(job_collection, list_command)
8
10
  @job_collection = job_collection
11
+ @list_command = list_command
9
12
  @help = "poll jobs"
10
13
  @can_notify = !`which terminal-notifier`.chomp.empty?
11
14
  end
12
15
 
13
- def execute(_whatever)
14
- incomplete_jobs = @job_collection.incomplete
15
- while incomplete_jobs.count.positive?
16
- puts "#{incomplete_jobs.count} incomplete jobs remaining:"
17
- incomplete_jobs.each { |job| puts job.description }
18
- failed_jobs = @job_collection.failed
19
- if failed_jobs.count.positive?
20
- puts "Some jobs have already failed:"
21
- failed_jobs.each { |job| puts job.description }
22
- `terminal-notifier -group semaph -message "#{failed_jobs.count} jobs have failed" -title "Job failures"` if @can_notify
23
- return
24
- end
25
- sleep 20
26
- @job_collection.reload
27
- incomplete_jobs = @job_collection.incomplete
28
- end
29
- @job_collection.all.each_with_index do |job, index|
30
- puts "#{index + 1} #{job.description}"
31
- end
32
- `terminal-notifier -group semaph -message "All jobs have completed" -title "Workflow completed"` if @can_notify
16
+ def execute(_whatever = nil)
17
+ puts "Polling #{job_collection.pipeline.workflow.description}:"
18
+ report_and_reload(15) while job_collection.incomplete.count.positive? && job_collection.failed.count.zero?
19
+ report_final
20
+ end
21
+
22
+ private
23
+
24
+ def report_and_reload(period)
25
+ period.times { report_incomplete }
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
40
+ base = [nil, elapsed, report_ratio, "jobs remaining"].join(" ")
41
+ erase base
42
+ end
43
+
44
+ def report_ratio
45
+ [
46
+ job_collection.incomplete.count.to_s.rjust(2, "0"),
47
+ job_collection.all.count.to_s.rjust(2, "0"),
48
+ ].join("/")
49
+ end
50
+
51
+ def elapsed
52
+ duration = Time.now.to_i - job_collection.created_at.to_i
53
+ mins = duration / 60
54
+ secs = duration % 60
55
+ [mins.to_s.rjust(2, "0"), secs.to_s.rjust(2, "0")].join(":")
56
+ end
57
+
58
+ def erase(string)
59
+ print string
60
+ print "\b" * string.length
61
+ end
62
+
63
+ def notify(title, message, failed)
64
+ return unless @can_notify
65
+
66
+ sound = failed ? "basso" : "blow"
67
+
68
+ `terminal-notifier -group semaph -message "#{message}" -title "#{title}" -sound #{sound}`
33
69
  end
34
70
  end
35
71
  end
@@ -1,9 +1,13 @@
1
- require "semaph/commands/reload_command"
2
- require "semaph/commands/rerun_workflow_command"
3
- require "semaph/shells/pipeline/jobs_list_command"
1
+ require "semaph/commands"
2
+ require "semaph/shells/pipeline/job_debug_command"
4
3
  require "semaph/shells/pipeline/job_log_command"
5
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"
7
+ require "semaph/shells/pipeline/jobs_list_command"
6
8
  require "semaph/shells/pipeline/jobs_poll_command"
9
+ require "semaph/shells/pipeline/promote_command"
10
+ require "semaph/shells/pipeline/promotions_list_command"
7
11
  require "shell_shock/context"
8
12
 
9
13
  module Semaph
@@ -16,83 +20,39 @@ module Semaph
16
20
 
17
21
  def initialize(pipeline)
18
22
  @pipeline = pipeline
23
+ workflow = pipeline.workflow
24
+ project = workflow.project
19
25
  @prompt = "🏗 #{project.client.name} #{project.name} #{workflow.id} #{pipeline.yaml} > "
26
+ add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
20
27
  add_commands
21
- @jobs_list_command.execute("")
28
+ jobs_list_command.execute
22
29
  end
23
30
 
24
31
  private
25
32
 
26
- def project
27
- workflow.project
28
- end
29
-
30
- def workflow
31
- pipeline.workflow
32
- end
33
-
34
- def job_collection
35
- pipeline.job_collection
36
- end
37
-
38
33
  def add_commands
39
- @jobs_list_command = JobsListCommand.new(job_collection)
40
- add_command @jobs_list_command, "list-jobs"
41
- add_command JobsPollCommand.new(job_collection), "poll-jobs"
42
- add_command JobLogCommand.new(job_collection), "job-log"
43
- add_command JobLogGrepCommand.new(job_collection), "grep-logs"
44
- add_command ::Semaph::Commands::RerunWorkflowCommand.new(workflow), "rerun"
45
- add_open_branch_command
46
- add_open_workflow_command
47
- add_github_commands
48
- add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
49
- end
50
-
51
- def add_open_workflow_command
52
- add_command(
53
- ::Semaph::Commands::VisitUrlCommand.new(
54
- "https://#{project.client.host}/workflows/#{workflow.id}",
55
- "browse to workflow",
56
- ),
57
- "open-workflow",
58
- )
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"
59
38
  end
60
39
 
61
- def add_open_branch_command
62
- add_command(
63
- ::Semaph::Commands::VisitUrlCommand.new(
64
- "https://#{project.client.host}/branches/#{workflow.branch_id}",
65
- "browse to branch in semaphore",
66
- ),
67
- "open-branch",
68
- )
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"
69
48
  end
70
49
 
71
- def add_github_commands
72
- return unless workflow.project.github_url
73
-
74
- add_github_branch
75
- add_github_commit
76
- end
77
-
78
- def add_github_branch
79
- add_command(
80
- ::Semaph::Commands::VisitUrlCommand.new(
81
- "#{workflow.project.github_url}/tree/#{workflow.branch}",
82
- "browse to the branch in github",
83
- ),
84
- "open-github-branch",
85
- )
86
- end
50
+ def jobs_list_command
51
+ return @jobs_list_command if @jobs_list_command
87
52
 
88
- def add_github_commit
89
- add_command(
90
- ::Semaph::Commands::VisitUrlCommand.new(
91
- "#{workflow.project.github_url}/commit/#{workflow.sha}",
92
- "browse to the commit in github",
93
- ),
94
- "open-github-commit",
95
- )
53
+ @jobs_list_command = JobsListCommand.new(pipeline.job_collection)
54
+ add_command @jobs_list_command, "list-jobs", "ls"
55
+ @jobs_list_command
96
56
  end
97
57
  end
98
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,6 @@
1
1
  require "semaph/commands/reload_command"
2
2
  require "semaph/commands/visit_url_command"
3
+ require "semaph/shells/project/save_command"
3
4
  require "semaph/shells/project/workflows_list_command"
4
5
  require "semaph/shells/project/workflows_select_command"
5
6
  require "shell_shock/context"
@@ -28,9 +29,10 @@ module Semaph
28
29
  def add_commands
29
30
  add_github_command
30
31
  add_open_project_command
32
+ add_command SaveCommand.new(project), "save"
31
33
  @workflows_list_command = WorkflowsListCommand.new(workflow_collection)
32
- add_command @workflows_list_command, "list-workflows"
33
- add_command WorkflowsSelectCommand.new(workflow_collection), "select-workflow"
34
+ add_command @workflows_list_command, "list-workflows", "ls"
35
+ add_command WorkflowsSelectCommand.new(workflow_collection), "select-workflow", "cd"
34
36
  add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
35
37
  end
36
38
 
@@ -0,0 +1,26 @@
1
+ require "yaml"
2
+
3
+ module Semaph
4
+ module Shells
5
+ module Project
6
+ class SaveCommand
7
+ attr_reader :help, :project
8
+
9
+ def initialize(project)
10
+ @project = project
11
+ @help = "save current project as default for this folder"
12
+ end
13
+
14
+ def execute(_ignored)
15
+ config = {
16
+ host: project.client.host,
17
+ project: project.raw,
18
+ }
19
+ File.open(".semaph", "w") do |file|
20
+ file.puts config.to_yaml
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,9 +1,9 @@
1
+ require "semaph/formatting"
2
+
1
3
  module Semaph
2
4
  module Shells
3
5
  module Project
4
6
  class WorkflowsListCommand
5
- TIME_FORMAT = "%Y-%m-%d %H:%M:%S".freeze
6
-
7
7
  attr_reader :usage, :help
8
8
 
9
9
  def initialize(workflow_collection)
@@ -14,24 +14,18 @@ module Semaph
14
14
 
15
15
  def execute(branch)
16
16
  @workflow_collection.reload
17
- @workflow_collection.all.each_with_index do |workflow, index|
17
+ @workflow_collection.all.slice(0..9).each_with_index do |workflow, index|
18
18
  next unless workflow.branch.include?(branch)
19
19
 
20
- puts description(index, workflow)
21
- end
22
- end
23
-
24
- private
20
+ pipelines = workflow.pipeline_collection.reload
25
21
 
26
- def description(index, workflow)
27
- [
28
- index + 1,
29
- workflow.created_at.strftime(TIME_FORMAT),
30
- "SHA",
31
- workflow.sha,
32
- "on branch",
33
- workflow.branch,
34
- ].join(" ")
22
+ puts [
23
+ ::Semaph::Formatting.index(index + 1),
24
+ pipelines.last&.icon,
25
+ ::Semaph::Formatting.length(pipelines),
26
+ workflow.description,
27
+ ].join(" ")
28
+ end
35
29
  end
36
30
  end
37
31
  end
@@ -1,3 +1,5 @@
1
+ require "semaph/formatting"
2
+
1
3
  module Semaph
2
4
  module Shells
3
5
  module Workflow
@@ -12,7 +14,7 @@ module Semaph
12
14
  def execute(_whatever)
13
15
  @pipeline_collection.reload
14
16
  @pipeline_collection.all.each_with_index do |pipeline, index|
15
- puts "#{index + 1} #{pipeline.description}"
17
+ puts [::Semaph::Formatting.index(index + 1), pipeline.description].join(" ")
16
18
  end
17
19
  end
18
20
  end
@@ -1,7 +1,4 @@
1
- require "semaph/commands/reload_command"
2
- require "semaph/commands/rerun_workflow_command"
3
- require "semaph/commands/stop_workflow_command"
4
- require "semaph/commands/visit_url_command"
1
+ require "semaph/commands"
5
2
  require "semaph/shells/workflow/pipelines_list_command"
6
3
  require "semaph/shells/workflow/pipelines_select_command"
7
4
  require "shell_shock/context"
@@ -16,6 +13,7 @@ module Semaph
16
13
 
17
14
  def initialize(workflow)
18
15
  @workflow = workflow
16
+ project = @workflow.project
19
17
  @prompt = "🏗 #{project.client.name} #{project.name} #{workflow.id} > "
20
18
  add_commands
21
19
  @list_command.execute("")
@@ -23,71 +21,13 @@ module Semaph
23
21
 
24
22
  private
25
23
 
26
- def project
27
- @workflow.project
28
- end
29
-
30
- def pipeline_collection
31
- @workflow.pipeline_collection
32
- end
33
-
34
24
  def add_commands
25
+ pipeline_collection = @workflow.pipeline_collection
35
26
  @list_command = PipelinesListCommand.new(pipeline_collection)
36
- add_command @list_command, "list-pipelines"
37
- add_command PipelinesSelectCommand.new(pipeline_collection), "select-pipeline"
38
- add_command ::Semaph::Commands::RerunWorkflowCommand.new(workflow), "rerun"
39
- add_command ::Semaph::Commands::StopWorkflowCommand.new(workflow), "stop"
40
- add_open_branch_command
41
- add_open_workflow_command
42
- add_github_commands
27
+ add_command @list_command, "list-pipelines", "ls"
28
+ add_command PipelinesSelectCommand.new(pipeline_collection), "select-pipeline", "cd"
43
29
  add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
44
- end
45
-
46
- def add_open_workflow_command
47
- add_command(
48
- ::Semaph::Commands::VisitUrlCommand.new(
49
- "https://#{project.client.host}/workflows/#{workflow.id}",
50
- "browse to workflow",
51
- ),
52
- "open-workflow",
53
- )
54
- end
55
-
56
- def add_open_branch_command
57
- add_command(
58
- ::Semaph::Commands::VisitUrlCommand.new(
59
- "https://#{project.client.host}/branches/#{workflow.branch_id}",
60
- "browse to branch in semaphore",
61
- ),
62
- "open-branch",
63
- )
64
- end
65
-
66
- def add_github_commands
67
- return unless workflow.project.github_url
68
-
69
- add_github_branch
70
- add_github_commit
71
- end
72
-
73
- def add_github_branch
74
- add_command(
75
- ::Semaph::Commands::VisitUrlCommand.new(
76
- "#{workflow.project.github_url}/tree/#{workflow.branch}",
77
- "browse to the branch in github",
78
- ),
79
- "open-github-branch",
80
- )
81
- end
82
-
83
- def add_github_commit
84
- add_command(
85
- ::Semaph::Commands::VisitUrlCommand.new(
86
- "#{workflow.project.github_url}/commit/#{workflow.sha}",
87
- "browse to the commit in github",
88
- ),
89
- "open-github-commit",
90
- )
30
+ ::Semaph::Commands.workflow_commands(self, workflow)
91
31
  end
92
32
  end
93
33
  end