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
@@ -0,0 +1,16 @@
1
+ module Semaph
2
+ module Commands
3
+ class RerunWorkflowCommand
4
+ attr_reader :help
5
+
6
+ def initialize(workflow)
7
+ @workflow = workflow
8
+ @help = "rerun workflow"
9
+ end
10
+
11
+ def execute(_whatever)
12
+ ::Semaph::Shells::Workflow::WorkflowShell.new(@workflow.rerun).push
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Semaph
2
+ module Commands
3
+ class StopWorkflowCommand
4
+ attr_reader :help
5
+
6
+ def initialize(workflow)
7
+ @workflow = workflow
8
+ @help = "stop workflow"
9
+ end
10
+
11
+ def execute(_whatever)
12
+ @workflow.stop
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,9 +1,23 @@
1
+ require "rainbow"
2
+
1
3
  module Semaph
2
4
  module Formatting
3
- TIME_FORMAT = "%Y-%m-%d %H:%M:%S".freeze
5
+ TIME_FORMAT = "%m-%d %H:%M".freeze
4
6
 
5
7
  def self.time(time)
6
8
  time.strftime(TIME_FORMAT)
7
9
  end
10
+
11
+ def self.hours_minutes_seconds(total_seconds)
12
+ seconds = total_seconds % 60
13
+ minutes = (total_seconds / 60) % 60
14
+ hours = total_seconds / (60 * 60)
15
+
16
+ format("%02<hours>d:%02<minutes>d:%02<seconds>d", hours: hours, minutes: minutes, seconds: seconds)
17
+ end
18
+
19
+ def self.index(number)
20
+ Rainbow(number.to_s.rjust(2)).yellow
21
+ end
8
22
  end
9
23
  end
@@ -1,3 +1,5 @@
1
+ require "fileutils"
2
+
1
3
  module Semaph
2
4
  module Model
3
5
  class Job
@@ -20,6 +22,74 @@ module Semaph
20
22
  assign_from_block(raw_block)
21
23
  end
22
24
 
25
+ def stop
26
+ pipeline.workflow.project.client.stop_job(id)
27
+ end
28
+
29
+ def show
30
+ pp pipeline.workflow.project.client.job(id)
31
+ end
32
+
33
+ def write_log(base)
34
+ FileUtils.mkdir_p(base)
35
+ filename = "#{base}/#{id}.log"
36
+ return filename if File.exist?(filename)
37
+
38
+ puts "retrieving log for job #{id}"
39
+ File.open(filename, "w") do |file|
40
+ file.puts pipeline.workflow.project.client.job_log(id)
41
+ end
42
+
43
+ filename
44
+ end
45
+
46
+ def description
47
+ [
48
+ block_icon,
49
+ @block_name,
50
+ job_icon,
51
+ @name,
52
+ ].compact.join(" ")
53
+ end
54
+
55
+ def finished?
56
+ @status == "FINISHED"
57
+ end
58
+
59
+ def failed?
60
+ @result == "FAILED"
61
+ end
62
+
63
+ # block_state can be waiting/running/done
64
+ # block_result can be passed/failed/canceled/stopped
65
+ def block_icon
66
+ return "🟠" if @block_state == "waiting"
67
+
68
+ return "🔵" if @block_state == "running"
69
+
70
+ return "⚪" if @block_result == "canceled"
71
+
72
+ return "⛔" if @block_result == "stopped"
73
+
74
+ return "🟢" if @block_result == "passed"
75
+
76
+ "🔴"
77
+ end
78
+
79
+ # status can be FINISHED/RUNNING
80
+ # result can be PASSED/FAILED/STOPPED
81
+ def job_icon
82
+ return nil unless @status
83
+
84
+ return "🔵" unless @status == "FINISHED"
85
+
86
+ return "⛔" if @result == "STOPPED"
87
+
88
+ return "🟢" if @result == "PASSED"
89
+
90
+ "🔴"
91
+ end
92
+
23
93
  private
24
94
 
25
95
  def assign_from_job(raw)
@@ -37,4 +107,3 @@ module Semaph
37
107
  end
38
108
  end
39
109
  end
40
-
@@ -3,11 +3,10 @@ require "semaph/model/job"
3
3
  module Semaph
4
4
  module Model
5
5
  class JobCollection
6
- attr_reader :all
6
+ attr_reader :all, :pipeline
7
7
 
8
8
  def initialize(pipeline)
9
9
  @pipeline = pipeline
10
- reload
11
10
  end
12
11
 
13
12
  def reload
@@ -16,6 +15,14 @@ module Semaph
16
15
  @all = build_jobs(project.client.pipeline(@pipeline.id))
17
16
  end
18
17
 
18
+ def incomplete
19
+ @all.reject(&:finished?)
20
+ end
21
+
22
+ def failed
23
+ @all.select(&:failed?)
24
+ end
25
+
19
26
  private
20
27
 
21
28
  def build_jobs(content)
@@ -23,12 +30,18 @@ module Semaph
23
30
  blocks = content.delete("blocks")
24
31
  blocks.each do |block|
25
32
  jobs = block.delete("jobs").sort_by { |job| job["index"] }
26
- jobs.each do |job|
27
- result << Job.new(@pipeline, block, job)
28
- end
33
+ append_jobs(result, block, jobs)
29
34
  end
30
35
  result
31
36
  end
37
+
38
+ def append_jobs(result, block, jobs)
39
+ if jobs.count.positive?
40
+ jobs.each { |job| result << Job.new(@pipeline, block, job) }
41
+ else
42
+ result << Job.new(@pipeline, block, {})
43
+ end
44
+ end
32
45
  end
33
46
  end
34
47
  end
@@ -1,21 +1,73 @@
1
+ require "semaph/formatting"
1
2
  require "semaph/model/job_collection"
3
+ require "semaph/model/promotion_collection"
2
4
 
3
5
  module Semaph
4
6
  module Model
5
7
  class Pipeline
6
- attr_reader :workflow, :raw, :id, :yaml, :state, :result
8
+ attr_reader :workflow, :raw, :id, :name, :yaml, :state, :result
7
9
 
8
10
  def initialize(workflow, raw)
9
11
  @workflow = workflow
10
12
  @raw = raw
11
13
  @id = raw["ppl_id"]
12
14
  @yaml = raw["yaml_file_name"]
15
+ @name = raw["name"]
13
16
  @state = raw["state"]
14
17
  @result = raw["result"]
18
+ %w[created done pending queuing running stopping].each do |name|
19
+ extract_time(name)
20
+ end
21
+ end
22
+
23
+ def promote(name)
24
+ workflow.project.client.promote(id, name)
15
25
  end
16
26
 
17
27
  def job_collection
18
- JobCollection.new(self)
28
+ @job_collection ||= JobCollection.new(self)
29
+ end
30
+
31
+ def promotion_collection
32
+ @promotion_collection ||= PromotionCollection.new(self)
33
+ end
34
+
35
+ def description
36
+ [
37
+ icon,
38
+ time,
39
+ name,
40
+ "(#{yaml})",
41
+ ].compact.join(" ")
42
+ end
43
+
44
+ def done?
45
+ @state == "DONE"
46
+ end
47
+
48
+ def icon
49
+ return "🔵" unless done?
50
+
51
+ return "⛔" if @result == "STOPPED"
52
+
53
+ return "🟢" if @result == "PASSED"
54
+
55
+ "🔴"
56
+ end
57
+
58
+ private
59
+
60
+ def time
61
+ return ::Semaph::Formatting.hours_minutes_seconds(@done_at.to_i - @created_at.to_i) if done?
62
+
63
+ ::Semaph::Formatting.hours_minutes_seconds(Time.now.to_i - @created_at.to_i)
64
+ end
65
+
66
+ def extract_time(name)
67
+ key = "#{name}_at"
68
+ return if raw[key]["seconds"].zero?
69
+
70
+ instance_variable_set("@#{key}", Time.at(raw[key]["seconds"]))
19
71
  end
20
72
  end
21
73
  end
@@ -7,7 +7,6 @@ module Semaph
7
7
 
8
8
  def initialize(workflow)
9
9
  @workflow = workflow
10
- reload
11
10
  end
12
11
 
13
12
  def reload
@@ -27,7 +27,7 @@ module Semaph
27
27
  end
28
28
 
29
29
  def workflow_collection
30
- WorkflowCollection.new(self)
30
+ @workflow_collection ||= WorkflowCollection.new(self)
31
31
  end
32
32
  end
33
33
  end
@@ -7,7 +7,6 @@ module Semaph
7
7
 
8
8
  def initialize(client)
9
9
  @client = client
10
- reload
11
10
  end
12
11
 
13
12
  def reload
@@ -0,0 +1,31 @@
1
+ require "semaph/formatting"
2
+
3
+ module Semaph
4
+ module Model
5
+ class Promotion
6
+ attr_reader :pipeline, :raw
7
+
8
+ def initialize(pipeline, raw)
9
+ @pipeline = pipeline
10
+ @raw = raw
11
+ @name = raw["name"]
12
+ @status = raw["status"]
13
+ @triggered_at = Time.at(raw["triggered_at"]["seconds"])
14
+ end
15
+
16
+ def description
17
+ [
18
+ status_icon,
19
+ Semaph::Formatting.time(@triggered_at),
20
+ @name,
21
+ ].join(" ")
22
+ end
23
+
24
+ def status_icon
25
+ return "🟢" if @status == "passed"
26
+
27
+ "🔴"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ require "semaph/model/promotion"
2
+
3
+ module Semaph
4
+ module Model
5
+ class PromotionCollection
6
+ attr_reader :all, :pipeline
7
+
8
+ def initialize(pipeline)
9
+ @pipeline = pipeline
10
+ end
11
+
12
+ def reload
13
+ workflow = @pipeline.workflow
14
+ project = workflow.project
15
+ @all = project.client.promotions(@pipeline.id).map do |promotion_response|
16
+ Promotion.new(@pipeline, promotion_response)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,23 +1,47 @@
1
+ require "semaph/formatting"
1
2
  require "semaph/model/pipeline_collection"
2
3
 
3
4
  module Semaph
4
5
  module Model
5
6
  class Workflow
6
- attr_reader :project, :raw, :id, :sha, :branch, :branch_id, :created_at
7
+ attr_reader :project, :raw, :id, :sha, :commit, :branch, :branch_id, :created_at
7
8
 
8
9
  def initialize(project, raw)
9
10
  @project = project
10
11
  @raw = raw
11
12
  @id = raw["wf_id"]
12
- @sha = raw["commit_sha"]
13
13
  @created_at = Time.at(raw["created_at"]["seconds"].to_i)
14
14
  @branch = raw["branch_name"]
15
15
  @branch_id = raw["branch_id"]
16
- # @summary = `git log -n 1 --format="%h %an %ci %s" #{sha}`
16
+ extract_git_details
17
+ end
18
+
19
+ def extract_git_details
20
+ @sha = raw["commit_sha"]
21
+ @commit = @sha.slice(0..10)
22
+ @commit = `git log -n 1 --format="%h %an %s" #{sha}`.chomp if `git cat-file -t #{sha} 2>&1`.chomp == "commit"
17
23
  end
18
24
 
19
25
  def pipeline_collection
20
- PipelineCollection.new(self)
26
+ @pipeline_collection ||= PipelineCollection.new(self)
27
+ end
28
+
29
+ def rerun
30
+ rerun_response = project.client.rerun_workflow(@id)
31
+ workflow_response = project.client.workflow(rerun_response["wf_id"])
32
+ Workflow.new(project, workflow_response["workflow"])
33
+ end
34
+
35
+ def description
36
+ [
37
+ Semaph::Formatting.time(created_at),
38
+ branch,
39
+ commit,
40
+ ].join(" ")
41
+ end
42
+
43
+ def stop
44
+ project.client.stop_workflow(@id)
21
45
  end
22
46
  end
23
47
  end
@@ -7,7 +7,6 @@ module Semaph
7
7
 
8
8
  def initialize(project)
9
9
  @project = project
10
- reload
11
10
  end
12
11
 
13
12
  def reload
@@ -1,4 +1,4 @@
1
- require "semaph/api"
1
+ require "semaph/client"
2
2
  require "semaph/commands/reload_command"
3
3
  require "semaph/model/project_collection"
4
4
  require "semaph/shells/organisation/projects_list_command"
@@ -12,16 +12,20 @@ module Semaph
12
12
  include ShellShock::Context
13
13
 
14
14
  def initialize(organisation)
15
- host = organisation["host"]
16
- @prompt = "🏗 #{host} > "
17
- client = ::Semaph::Api.new(organisation["auth"]["token"], host)
18
- project_collection = ::Semaph::Model::ProjectCollection.new(client)
19
- add_command ProjectsListCommand.new(project_collection), "list-projects"
20
- add_command ProjectsSelectCommand.new(project_collection), "select-project"
21
- add_command(
22
- ::Semaph::Commands::ReloadCommand.new(project_collection, "reload projects"),
23
- "reload-projects",
24
- )
15
+ @client = ::Semaph::Client.new(organisation["auth"]["token"], organisation["host"])
16
+ @prompt = "🏗 #{@client.name} > "
17
+ add_commands
18
+ @project_list_command.execute("")
19
+ end
20
+
21
+ private
22
+
23
+ def add_commands
24
+ project_collection = ::Semaph::Model::ProjectCollection.new(@client)
25
+ @project_list_command = ProjectsListCommand.new(project_collection)
26
+ add_command @project_list_command, "list-projects", "ls"
27
+ add_command ProjectsSelectCommand.new(project_collection), "select-project", "cd"
28
+ add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
25
29
  end
26
30
  end
27
31
  end