semaph 0.2.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +39 -0
- data/.semaphore/semaphore.yml +5 -1
- data/Gemfile.lock +21 -1
- data/README.md +85 -11
- data/Rakefile +4 -1
- data/lib/semaph/client.rb +115 -0
- data/lib/semaph/commands.rb +63 -0
- data/lib/semaph/commands/reload_command.rb +5 -4
- data/lib/semaph/commands/rerun_workflow_command.rb +16 -0
- data/lib/semaph/commands/stop_workflow_command.rb +16 -0
- data/lib/semaph/formatting.rb +15 -1
- data/lib/semaph/model/job.rb +70 -1
- data/lib/semaph/model/job_collection.rb +18 -5
- data/lib/semaph/model/pipeline.rb +54 -2
- data/lib/semaph/model/pipeline_collection.rb +0 -1
- data/lib/semaph/model/project.rb +1 -1
- data/lib/semaph/model/project_collection.rb +0 -1
- data/lib/semaph/model/promotion.rb +31 -0
- data/lib/semaph/model/promotion_collection.rb +21 -0
- data/lib/semaph/model/workflow.rb +28 -4
- data/lib/semaph/model/workflow_collection.rb +0 -1
- data/lib/semaph/shells/organisation/organisation_shell.rb +15 -11
- data/lib/semaph/shells/organisation/projects_list_command.rb +1 -0
- data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
- data/lib/semaph/shells/organisations/organisations_select_command.rb +8 -1
- data/lib/semaph/shells/organisations/organisations_shell.rb +6 -2
- data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
- data/lib/semaph/shells/pipeline/job_log_command.rb +44 -0
- data/lib/semaph/shells/pipeline/job_log_grep_command.rb +28 -0
- data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
- data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
- data/lib/semaph/shells/pipeline/jobs_list_command.rb +6 -16
- data/lib/semaph/shells/pipeline/jobs_poll_command.rb +55 -0
- data/lib/semaph/shells/pipeline/pipeline_shell.rb +42 -7
- data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
- data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
- data/lib/semaph/shells/project/project_shell.rb +31 -18
- data/lib/semaph/shells/project/workflows_list_command.rb +4 -16
- data/lib/semaph/shells/project/workflows_select_command.rb +10 -3
- data/lib/semaph/shells/workflow/pipelines_list_command.rb +4 -1
- data/lib/semaph/shells/workflow/pipelines_select_command.rb +9 -3
- data/lib/semaph/shells/workflow/workflow_shell.rb +13 -42
- data/lib/semaph/version.rb +1 -1
- data/semaph.gemspec +1 -0
- metadata +33 -6
- 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
|
data/lib/semaph/formatting.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
|
+
require "rainbow"
|
2
|
+
|
1
3
|
module Semaph
|
2
4
|
module Formatting
|
3
|
-
TIME_FORMAT = "%
|
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
|
data/lib/semaph/model/job.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/semaph/model/project.rb
CHANGED
@@ -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
|
-
|
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
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "semaph/
|
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
|
-
|
16
|
-
@prompt = "🏗 #{
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|