semaph 0.2.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|