semaph 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31aeabe99b27b7a10bc26f91c6a60e87e3374b309df5e41b8354694d99bd1a90
4
- data.tar.gz: 34499279ff5fedb14739093655ad29ff703c9c3d40222208783106d73d746841
3
+ metadata.gz: 7db37cbca829e1b13372c7a8687b86080b1e4a579bbb7707e6cc207ebdcb4ba1
4
+ data.tar.gz: 4b5015379f62ad0382991f26b525583d2a6e077889071e5bde58dc1399d69ada
5
5
  SHA512:
6
- metadata.gz: 47939331aee8435b5584f29ab39cf9647730dc0ead3b340c20175df3da849120aec5e9b97d3cd7dc116aa6044ac4fd9e1c4648b6ece862afe01a40b8e5007da9
7
- data.tar.gz: 4f7b74225fd180ea85fad14c73a8a808733fc1d4836b69b99069a25314996bdd70066c1c1bb35b5e247cbeeefe5191760af50102d338e65992fdbd44ebfe529c
6
+ metadata.gz: 243de9cc3425299a5d7a5a11d3a6e06a89e56ad22e0bab59fe4568544fc8e05f0a6e902363497d493811a00c30f84a95456a0679417fb7d4d39b10ad35ece692
7
+ data.tar.gz: f1fd8d22e22885357f0b5c1ba962ac1edb91b61b6ab3b9e03cfec9aeda88197a40bfcfe2b047417d3d2629e5eb8f9fa3e32b9dde40e8feefeb09bae8e7e5527d
@@ -1,3 +1,42 @@
1
+ Layout/EmptyLinesAroundAttributeAccessor:
2
+ Enabled: true
3
+
4
+ Layout/SpaceAroundMethodCallOperator:
5
+ Enabled: true
6
+
7
+ Lint/DeprecatedOpenSSLConstant:
8
+ Enabled: true
9
+
10
+ Lint/MixedRegexpCaptureTypes:
11
+ Enabled: true
12
+
13
+ Lint/RaiseException:
14
+ Enabled: true
15
+
16
+ Lint/StructNewOverride:
17
+ Enabled: true
18
+
19
+ Style/ExponentialNotation:
20
+ Enabled: true
21
+
22
+ Style/HashEachMethods:
23
+ Enabled: true
24
+
25
+ Style/HashTransformKeys:
26
+ Enabled: true
27
+
28
+ Style/HashTransformValues:
29
+ Enabled: true
30
+
31
+ Style/RedundantRegexpCharacterClass:
32
+ Enabled: true
33
+
34
+ Style/RedundantRegexpEscape:
35
+ Enabled: true
36
+
37
+ Style/SlicingWithRange:
38
+ Enabled: true
39
+
1
40
  Style/Documentation:
2
41
  Enabled: false
3
42
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- semaph (0.6.0)
4
+ semaph (0.7.0)
5
5
  faraday
6
6
  rainbow
7
7
  shell_shock
@@ -3,7 +3,17 @@ require "json"
3
3
 
4
4
  module Semaph
5
5
  # Refer to https://docs.semaphoreci.com/reference/api-v1alpha/
6
- class Api
6
+ class Client
7
+ class RequestException < RuntimeError
8
+ attr_reader :url, :response
9
+
10
+ def initialize(url, response)
11
+ @url = url
12
+ @response = response
13
+ super("http response #{response.status} received for #{url}:\n#{response.body}")
14
+ end
15
+ end
16
+
7
17
  attr_reader :host, :name
8
18
 
9
19
  def initialize(token, host)
@@ -42,10 +52,22 @@ module Semaph
42
52
  get "pipelines/#{id}", { detailed: true }
43
53
  end
44
54
 
55
+ def promotions(pipeline_id)
56
+ get "promotions", { pipeline_id: pipeline_id }
57
+ end
58
+
59
+ def promote(pipeline_id, name)
60
+ post "promotions", { pipeline_id: pipeline_id, name: name }
61
+ end
62
+
45
63
  def job(id)
46
64
  get "jobs/#{id}"
47
65
  end
48
66
 
67
+ def stop_job(id)
68
+ post "jobs/#{id}/stop"
69
+ end
70
+
49
71
  def job_log(id)
50
72
  get_raw "jobs/#{id}/plain_logs.json"
51
73
  end
@@ -87,8 +109,7 @@ module Semaph
87
109
  def check_response(response, url)
88
110
  return response.body if response.status == 200
89
111
 
90
- puts "http response #{response.status} received for #{url}:\n#{response.body}"
91
- exit 1
112
+ raise RequestException.new(url, response)
92
113
  end
93
114
  end
94
115
  end
@@ -1,3 +1,5 @@
1
+ require "rainbow"
2
+
1
3
  module Semaph
2
4
  module Formatting
3
5
  TIME_FORMAT = "%m-%d %H:%M".freeze
@@ -5,5 +7,17 @@ module Semaph
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
@@ -22,6 +22,14 @@ module Semaph
22
22
  assign_from_block(raw_block)
23
23
  end
24
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
+
25
33
  def write_log(base)
26
34
  FileUtils.mkdir_p(base)
27
35
  filename = "#{base}/#{id}.log"
@@ -1,15 +1,18 @@
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"]
15
18
  %w[created done pending queuing running stopping].each do |name|
@@ -17,16 +20,33 @@ module Semaph
17
20
  end
18
21
  end
19
22
 
23
+ def promote(name)
24
+ workflow.project.client.promote(id, name)
25
+ end
26
+
20
27
  def job_collection
21
28
  @job_collection ||= JobCollection.new(self)
22
29
  end
23
30
 
31
+ def promotion_collection
32
+ @promotion_collection ||= PromotionCollection.new(self)
33
+ end
34
+
24
35
  def description
25
- "#{icon} #{yaml}"
36
+ [
37
+ icon,
38
+ time,
39
+ name,
40
+ "(#{yaml})",
41
+ ].compact.join(" ")
42
+ end
43
+
44
+ def done?
45
+ @state == "DONE"
26
46
  end
27
47
 
28
48
  def icon
29
- return "🔵" unless @state == "DONE"
49
+ return "🔵" unless done?
30
50
 
31
51
  return "⛔" if @result == "STOPPED"
32
52
 
@@ -37,6 +57,12 @@ module Semaph
37
57
 
38
58
  private
39
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
+
40
66
  def extract_time(name)
41
67
  key = "#{name}_at"
42
68
  return if raw[key]["seconds"].zero?
@@ -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,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,7 +12,7 @@ module Semaph
12
12
  include ShellShock::Context
13
13
 
14
14
  def initialize(organisation)
15
- @client = ::Semaph::Api.new(organisation["auth"]["token"], organisation["host"])
15
+ @client = ::Semaph::Client.new(organisation["auth"]["token"], organisation["host"])
16
16
  @prompt = "🏗 #{@client.name} > "
17
17
  add_commands
18
18
  @project_list_command.execute("")
@@ -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
@@ -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,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,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,34 +1,33 @@
1
+ require "semaph/formatting"
2
+
1
3
  module Semaph
2
4
  module Shells
3
5
  module Pipeline
4
6
  class JobsPollCommand
5
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
- while job_collection.incomplete.count.positive?
15
- report_incomplete(job_collection.incomplete)
16
- if job_collection.failed.count.positive?
17
- report_failures(job_collection.failed)
18
- return
19
- end
20
- sleep 20
21
- job_collection.reload
22
- end
16
+ def execute(_whatever = nil)
17
+ report_and_reload(15) while job_collection.incomplete.count.positive? && job_collection.failed.count.zero?
23
18
  report_final
24
19
  end
25
20
 
26
21
  private
27
22
 
23
+ def report_and_reload(period)
24
+ report_incomplete(job_collection.incomplete)
25
+ sleep period
26
+ job_collection.reload
27
+ end
28
+
28
29
  def report_final
29
- job_collection.all.each_with_index do |job, index|
30
- puts "#{index + 1} #{job.description}"
31
- end
30
+ @list_command.execute
32
31
  failed_job_count = job_collection.failed.count
33
32
  notify(
34
33
  "Workflow completed",
@@ -43,12 +42,6 @@ module Semaph
43
42
  incomplete_jobs.each { |job| puts job.description }
44
43
  end
45
44
 
46
- def report_failures(failed_jobs)
47
- puts "Some jobs have failed:"
48
- failed_jobs.each { |job| puts job.description }
49
- notify("Job Failures", "#{failed_jobs.count} jobs have failed", true)
50
- end
51
-
52
45
  def notify(title, message, failed)
53
46
  return unless @can_notify
54
47
 
@@ -1,8 +1,13 @@
1
1
  require "semaph/commands"
2
- require "semaph/shells/pipeline/jobs_list_command"
2
+ require "semaph/shells/pipeline/job_debug_command"
3
3
  require "semaph/shells/pipeline/job_log_command"
4
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"
5
8
  require "semaph/shells/pipeline/jobs_poll_command"
9
+ require "semaph/shells/pipeline/promote_command"
10
+ require "semaph/shells/pipeline/promotions_list_command"
6
11
  require "shell_shock/context"
7
12
 
8
13
  module Semaph
@@ -18,27 +23,37 @@ module Semaph
18
23
  workflow = pipeline.workflow
19
24
  project = workflow.project
20
25
  @prompt = "🏗 #{project.client.name} #{project.name} #{workflow.id} #{pipeline.yaml} > "
26
+ add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
21
27
  add_commands
22
- @jobs_list_command.execute("")
28
+ jobs_list_command.execute
23
29
  end
24
30
 
25
31
  private
26
32
 
27
33
  def add_commands
28
- add_command ::Semaph::Commands::ReloadCommand.new, "reload" if ENV["SEMAPH_RELOAD"]
29
- workflow = pipeline.workflow
30
- ::Semaph::Commands.workflow_commands(self, workflow)
34
+ ::Semaph::Commands.workflow_commands(self, pipeline.workflow)
31
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"
32
38
  end
33
39
 
34
40
  def add_job_collection_commands(job_collection)
35
- @jobs_list_command = JobsListCommand.new(job_collection)
36
- add_command @jobs_list_command, "list-jobs", "ls"
37
- add_command JobsPollCommand.new(job_collection), "poll-jobs"
38
- add_command JobLogCommand.new(job_collection), "job-log"
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"
39
46
  add_command JobLogGrepCommand.new(job_collection, :all), "grep-all-logs"
40
47
  add_command JobLogGrepCommand.new(job_collection, :failed), "grep-failed-logs"
41
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
56
+ end
42
57
  end
43
58
  end
44
59
  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,3 +1,5 @@
1
+ require "semaph/formatting"
2
+
1
3
  module Semaph
2
4
  module Shells
3
5
  module Project
@@ -15,7 +17,7 @@ module Semaph
15
17
  @workflow_collection.all.each_with_index do |workflow, index|
16
18
  next unless workflow.branch.include?(branch)
17
19
 
18
- puts "#{index + 1} #{workflow.description}"
20
+ puts [::Semaph::Formatting.index(index + 1), workflow.description].join(" ")
19
21
  end
20
22
  end
21
23
  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,3 +1,3 @@
1
1
  module Semaph
2
- VERSION = "0.6.0".freeze
2
+ VERSION = "0.7.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semaph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Ryall
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-05 00:00:00.000000000 Z
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -103,7 +103,7 @@ files:
103
103
  - bin/setup
104
104
  - exe/semaph
105
105
  - lib/semaph.rb
106
- - lib/semaph/api.rb
106
+ - lib/semaph/client.rb
107
107
  - lib/semaph/commands.rb
108
108
  - lib/semaph/commands/reload_command.rb
109
109
  - lib/semaph/commands/rerun_workflow_command.rb
@@ -116,6 +116,8 @@ files:
116
116
  - lib/semaph/model/pipeline_collection.rb
117
117
  - lib/semaph/model/project.rb
118
118
  - lib/semaph/model/project_collection.rb
119
+ - lib/semaph/model/promotion.rb
120
+ - lib/semaph/model/promotion_collection.rb
119
121
  - lib/semaph/model/workflow.rb
120
122
  - lib/semaph/model/workflow_collection.rb
121
123
  - lib/semaph/shells/organisation/organisation_shell.rb
@@ -124,11 +126,16 @@ files:
124
126
  - lib/semaph/shells/organisations/organisations_list_command.rb
125
127
  - lib/semaph/shells/organisations/organisations_select_command.rb
126
128
  - lib/semaph/shells/organisations/organisations_shell.rb
129
+ - lib/semaph/shells/pipeline/job_debug_command.rb
127
130
  - lib/semaph/shells/pipeline/job_log_command.rb
128
131
  - lib/semaph/shells/pipeline/job_log_grep_command.rb
132
+ - lib/semaph/shells/pipeline/job_show_command.rb
133
+ - lib/semaph/shells/pipeline/job_stop_command.rb
129
134
  - lib/semaph/shells/pipeline/jobs_list_command.rb
130
135
  - lib/semaph/shells/pipeline/jobs_poll_command.rb
131
136
  - lib/semaph/shells/pipeline/pipeline_shell.rb
137
+ - lib/semaph/shells/pipeline/promote_command.rb
138
+ - lib/semaph/shells/pipeline/promotions_list_command.rb
132
139
  - lib/semaph/shells/project/project_shell.rb
133
140
  - lib/semaph/shells/project/workflows_list_command.rb
134
141
  - lib/semaph/shells/project/workflows_select_command.rb