semaph 0.1.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +18 -0
  4. data/.semaphore/semaphore.yml +23 -0
  5. data/.tool-versions +1 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +55 -0
  8. data/README.md +85 -11
  9. data/Rakefile +4 -1
  10. data/exe/semaph +5 -0
  11. data/lib/semaph.rb +17 -1
  12. data/lib/semaph/api.rb +94 -0
  13. data/lib/semaph/commands.rb +63 -0
  14. data/lib/semaph/commands/reload_command.rb +17 -0
  15. data/lib/semaph/commands/rerun_workflow_command.rb +16 -0
  16. data/lib/semaph/commands/stop_workflow_command.rb +16 -0
  17. data/lib/semaph/commands/visit_url_command.rb +16 -0
  18. data/lib/semaph/formatting.rb +9 -0
  19. data/lib/semaph/model/job.rb +101 -0
  20. data/lib/semaph/model/job_collection.rb +47 -0
  21. data/lib/semaph/model/pipeline.rb +48 -0
  22. data/lib/semaph/model/pipeline_collection.rb +20 -0
  23. data/lib/semaph/model/project.rb +34 -0
  24. data/lib/semaph/model/project_collection.rb +19 -0
  25. data/lib/semaph/model/workflow.rb +48 -0
  26. data/lib/semaph/model/workflow_collection.rb +19 -0
  27. data/lib/semaph/shells/organisation/organisation_shell.rb +33 -0
  28. data/lib/semaph/shells/organisation/projects_list_command.rb +21 -0
  29. data/lib/semaph/shells/organisation/projects_select_command.rb +26 -0
  30. data/lib/semaph/shells/organisations/organisations_list_command.rb +20 -0
  31. data/lib/semaph/shells/organisations/organisations_select_command.rb +25 -0
  32. data/lib/semaph/shells/organisations/organisations_shell.rb +24 -0
  33. data/lib/semaph/shells/pipeline/job_log_command.rb +44 -0
  34. data/lib/semaph/shells/pipeline/job_log_grep_command.rb +28 -0
  35. data/lib/semaph/shells/pipeline/jobs_list_command.rb +21 -0
  36. data/lib/semaph/shells/pipeline/jobs_poll_command.rb +62 -0
  37. data/lib/semaph/shells/pipeline/pipeline_shell.rb +45 -0
  38. data/lib/semaph/shells/project/project_shell.rb +61 -0
  39. data/lib/semaph/shells/project/workflows_list_command.rb +24 -0
  40. data/lib/semaph/shells/project/workflows_select_command.rb +31 -0
  41. data/lib/semaph/shells/workflow/pipelines_list_command.rb +21 -0
  42. data/lib/semaph/shells/workflow/pipelines_select_command.rb +30 -0
  43. data/lib/semaph/shells/workflow/workflow_shell.rb +35 -0
  44. data/lib/semaph/version.rb +1 -1
  45. data/semaph.gemspec +12 -7
  46. metadata +115 -7
@@ -0,0 +1,17 @@
1
+ module Semaph
2
+ module Commands
3
+ class ReloadCommand
4
+ attr_reader :help
5
+
6
+ def initialize
7
+ @help = "reload all code"
8
+ end
9
+
10
+ def execute(_whatever)
11
+ Dir["lib/**/*.rb"].each do |path|
12
+ load path
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -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
@@ -0,0 +1,16 @@
1
+ module Semaph
2
+ module Commands
3
+ class VisitUrlCommand
4
+ attr_reader :help
5
+
6
+ def initialize(url, help)
7
+ @url = url
8
+ @help = help
9
+ end
10
+
11
+ def execute(_whatever)
12
+ system("open #{@url}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Semaph
2
+ module Formatting
3
+ TIME_FORMAT = "%m-%d %H:%M".freeze
4
+
5
+ def self.time(time)
6
+ time.strftime(TIME_FORMAT)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ require "fileutils"
2
+
3
+ module Semaph
4
+ module Model
5
+ class Job
6
+ attr_reader(
7
+ :pipeline,
8
+ :id,
9
+ :name,
10
+ :status,
11
+ :result,
12
+ :block_name,
13
+ :block_state,
14
+ :block_result,
15
+ )
16
+
17
+ def initialize(pipeline, raw_block, raw_job)
18
+ @pipeline = pipeline
19
+ @raw_block = raw_block
20
+ @raw_job = raw_job
21
+ assign_from_job(raw_job)
22
+ assign_from_block(raw_block)
23
+ end
24
+
25
+ def write_log(base)
26
+ FileUtils.mkdir_p(base)
27
+ filename = "#{base}/#{id}.log"
28
+ return filename if File.exist?(filename)
29
+
30
+ puts "retrieving log for job #{id}"
31
+ File.open(filename, "w") do |file|
32
+ file.puts pipeline.workflow.project.client.job_log(id)
33
+ end
34
+
35
+ filename
36
+ end
37
+
38
+ def description
39
+ [
40
+ block_icon,
41
+ @block_name,
42
+ job_icon,
43
+ @name,
44
+ ].compact.join(" ")
45
+ end
46
+
47
+ def finished?
48
+ @status == "FINISHED"
49
+ end
50
+
51
+ def failed?
52
+ @result == "FAILED"
53
+ end
54
+
55
+ # block_state can be waiting/running/done
56
+ # block_result can be passed/failed/canceled/stopped
57
+ def block_icon
58
+ return "🟠" if @block_state == "waiting"
59
+
60
+ return "🔵" if @block_state == "running"
61
+
62
+ return "⚪" if @block_result == "canceled"
63
+
64
+ return "⛔" if @block_result == "stopped"
65
+
66
+ return "🟢" if @block_result == "passed"
67
+
68
+ "🔴"
69
+ end
70
+
71
+ # status can be FINISHED/RUNNING
72
+ # result can be PASSED/FAILED/STOPPED
73
+ def job_icon
74
+ return nil unless @status
75
+
76
+ return "🔵" unless @status == "FINISHED"
77
+
78
+ return "⛔" if @result == "STOPPED"
79
+
80
+ return "🟢" if @result == "PASSED"
81
+
82
+ "🔴"
83
+ end
84
+
85
+ private
86
+
87
+ def assign_from_job(raw)
88
+ @id = raw["job_id"]
89
+ @status = raw["status"]
90
+ @name = raw["name"]
91
+ @result = raw["result"]
92
+ end
93
+
94
+ def assign_from_block(raw)
95
+ @block_name = raw["name"]
96
+ @block_state = raw["state"]
97
+ @block_result = raw["result"]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,47 @@
1
+ require "semaph/model/job"
2
+
3
+ module Semaph
4
+ module Model
5
+ class JobCollection
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 = build_jobs(project.client.pipeline(@pipeline.id))
16
+ end
17
+
18
+ def incomplete
19
+ @all.reject(&:finished?)
20
+ end
21
+
22
+ def failed
23
+ @all.select(&:failed?)
24
+ end
25
+
26
+ private
27
+
28
+ def build_jobs(content)
29
+ result = []
30
+ blocks = content.delete("blocks")
31
+ blocks.each do |block|
32
+ jobs = block.delete("jobs").sort_by { |job| job["index"] }
33
+ append_jobs(result, block, jobs)
34
+ end
35
+ result
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
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ require "semaph/model/job_collection"
2
+
3
+ module Semaph
4
+ module Model
5
+ class Pipeline
6
+ attr_reader :workflow, :raw, :id, :yaml, :state, :result
7
+
8
+ def initialize(workflow, raw)
9
+ @workflow = workflow
10
+ @raw = raw
11
+ @id = raw["ppl_id"]
12
+ @yaml = raw["yaml_file_name"]
13
+ @state = raw["state"]
14
+ @result = raw["result"]
15
+ %w[created done pending queuing running stopping].each do |name|
16
+ extract_time(name)
17
+ end
18
+ end
19
+
20
+ def job_collection
21
+ @job_collection ||= JobCollection.new(self)
22
+ end
23
+
24
+ def description
25
+ "#{icon} #{yaml}"
26
+ end
27
+
28
+ def icon
29
+ return "🔵" unless @state == "DONE"
30
+
31
+ return "⛔" if @result == "STOPPED"
32
+
33
+ return "🟢" if @result == "PASSED"
34
+
35
+ "🔴"
36
+ end
37
+
38
+ private
39
+
40
+ def extract_time(name)
41
+ key = "#{name}_at"
42
+ return if raw[key]["seconds"].zero?
43
+
44
+ instance_variable_set("@#{key}", Time.at(raw[key]["seconds"]))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ require "semaph/model/pipeline"
2
+
3
+ module Semaph
4
+ module Model
5
+ class PipelineCollection
6
+ attr_reader :all
7
+
8
+ def initialize(workflow)
9
+ @workflow = workflow
10
+ end
11
+
12
+ def reload
13
+ project = @workflow.project
14
+ @all = project.client.pipelines({ wf_id: @workflow.id }).map do |content|
15
+ Pipeline.new @workflow, content
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ require "semaph/model/workflow_collection"
2
+
3
+ module Semaph
4
+ module Model
5
+ class Project
6
+ GITHUB_REGGEXP = %r{git@github.com:(.*)/(.*).git}.freeze
7
+
8
+ attr_reader :client, :raw, :id, :name
9
+
10
+ def initialize(client, raw)
11
+ @client = client
12
+ @raw = raw
13
+ @id = raw["metadata"]["id"]
14
+ @name = raw["metadata"]["name"]
15
+ repo = raw["spec"]["repository"]["url"]
16
+ match = GITHUB_REGGEXP.match(repo)
17
+ return unless match
18
+
19
+ @repo_owner = match[1]
20
+ @repo_name = match[2]
21
+ end
22
+
23
+ def github_url
24
+ return nil unless @repo_owner && @repo_name
25
+
26
+ "https://github.com/#{@repo_owner}/#{@repo_name}"
27
+ end
28
+
29
+ def workflow_collection
30
+ @workflow_collection ||= WorkflowCollection.new(self)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ require "semaph/model/project"
2
+
3
+ module Semaph
4
+ module Model
5
+ class ProjectCollection
6
+ attr_reader :all
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def reload
13
+ @all = @client.projects.map do |content|
14
+ Project.new @client, content
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ require "semaph/formatting"
2
+ require "semaph/model/pipeline_collection"
3
+
4
+ module Semaph
5
+ module Model
6
+ class Workflow
7
+ attr_reader :project, :raw, :id, :sha, :commit, :branch, :branch_id, :created_at
8
+
9
+ def initialize(project, raw)
10
+ @project = project
11
+ @raw = raw
12
+ @id = raw["wf_id"]
13
+ @created_at = Time.at(raw["created_at"]["seconds"].to_i)
14
+ @branch = raw["branch_name"]
15
+ @branch_id = raw["branch_id"]
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"
23
+ end
24
+
25
+ def pipeline_collection
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)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ require "semaph/model/workflow"
2
+
3
+ module Semaph
4
+ module Model
5
+ class WorkflowCollection
6
+ attr_reader :all
7
+
8
+ def initialize(project)
9
+ @project = project
10
+ end
11
+
12
+ def reload
13
+ @all = @project.client.workflows(@project.id).map do |content|
14
+ Workflow.new @project, content
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ require "semaph/api"
2
+ require "semaph/commands/reload_command"
3
+ require "semaph/model/project_collection"
4
+ require "semaph/shells/organisation/projects_list_command"
5
+ require "semaph/shells/organisation/projects_select_command"
6
+ require "shell_shock/context"
7
+
8
+ module Semaph
9
+ module Shells
10
+ module Organisation
11
+ class OrganisationShell
12
+ include ShellShock::Context
13
+
14
+ def initialize(organisation)
15
+ @client = ::Semaph::Api.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"]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end