semaph 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +39 -0
  4. data/.semaphore/semaphore.yml +5 -1
  5. data/Gemfile.lock +21 -1
  6. data/README.md +85 -11
  7. data/Rakefile +4 -1
  8. data/lib/semaph/client.rb +115 -0
  9. data/lib/semaph/commands.rb +63 -0
  10. data/lib/semaph/commands/reload_command.rb +5 -4
  11. data/lib/semaph/commands/rerun_workflow_command.rb +16 -0
  12. data/lib/semaph/commands/stop_workflow_command.rb +16 -0
  13. data/lib/semaph/formatting.rb +15 -1
  14. data/lib/semaph/model/job.rb +70 -1
  15. data/lib/semaph/model/job_collection.rb +18 -5
  16. data/lib/semaph/model/pipeline.rb +54 -2
  17. data/lib/semaph/model/pipeline_collection.rb +0 -1
  18. data/lib/semaph/model/project.rb +1 -1
  19. data/lib/semaph/model/project_collection.rb +0 -1
  20. data/lib/semaph/model/promotion.rb +31 -0
  21. data/lib/semaph/model/promotion_collection.rb +21 -0
  22. data/lib/semaph/model/workflow.rb +28 -4
  23. data/lib/semaph/model/workflow_collection.rb +0 -1
  24. data/lib/semaph/shells/organisation/organisation_shell.rb +15 -11
  25. data/lib/semaph/shells/organisation/projects_list_command.rb +1 -0
  26. data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
  27. data/lib/semaph/shells/organisations/organisations_select_command.rb +8 -1
  28. data/lib/semaph/shells/organisations/organisations_shell.rb +6 -2
  29. data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
  30. data/lib/semaph/shells/pipeline/job_log_command.rb +44 -0
  31. data/lib/semaph/shells/pipeline/job_log_grep_command.rb +28 -0
  32. data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
  33. data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
  34. data/lib/semaph/shells/pipeline/jobs_list_command.rb +6 -16
  35. data/lib/semaph/shells/pipeline/jobs_poll_command.rb +55 -0
  36. data/lib/semaph/shells/pipeline/pipeline_shell.rb +42 -7
  37. data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
  38. data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
  39. data/lib/semaph/shells/project/project_shell.rb +31 -18
  40. data/lib/semaph/shells/project/workflows_list_command.rb +4 -16
  41. data/lib/semaph/shells/project/workflows_select_command.rb +10 -3
  42. data/lib/semaph/shells/workflow/pipelines_list_command.rb +4 -1
  43. data/lib/semaph/shells/workflow/pipelines_select_command.rb +9 -3
  44. data/lib/semaph/shells/workflow/workflow_shell.rb +13 -42
  45. data/lib/semaph/version.rb +1 -1
  46. data/semaph.gemspec +1 -0
  47. metadata +33 -6
  48. data/lib/semaph/api.rb +0 -61
@@ -10,6 +10,7 @@ module Semaph
10
10
  end
11
11
 
12
12
  def execute(_whatever)
13
+ @project_collection.reload
13
14
  @project_collection.all.each do |project|
14
15
  puts project.name
15
16
  end
@@ -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
- ::Semaph::Shells::Organisation::OrganisationShell.new(@organisations[name]).push
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
- add_command OrganisationsListCommand.new(organisations), "list-organisations"
15
- add_command OrganisationsSelectCommand.new(organisations), "select-organisation"
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.all.each do |job|
14
- puts description(job)
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.host} #{project.name} #{workflow.id} #{pipeline.yaml} > "
15
- job_collection = pipeline.job_collection
16
- add_command(
17
- ::Semaph::Commands::ReloadCommand.new(job_collection, "reload jobs"),
18
- "reload-jobs",
19
- )
20
- add_command JobsListCommand.new(job_collection), "list-jobs"
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
- @prompt = "🏗 #{project.client.host} #{project.name} > "
15
- workflow_collection = project.workflow_collection
16
- add_github_command(project)
17
- add_command(
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 add_github_command(project)
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