semaph 0.4.0 → 0.9.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.
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