semaph 0.4.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +39 -0
  3. data/.semaph +9 -0
  4. data/.semaphore/semaphore.yml +5 -1
  5. data/Gemfile.lock +21 -1
  6. data/README.md +12 -8
  7. data/Rakefile +4 -1
  8. data/lib/semaph.rb +43 -8
  9. data/lib/semaph/{api.rb → client.rb} +24 -3
  10. data/lib/semaph/commands.rb +63 -0
  11. data/lib/semaph/commands/rerun_workflow_command.rb +0 -2
  12. data/lib/semaph/commands/stop_workflow_command.rb +0 -2
  13. data/lib/semaph/formatting.rb +19 -1
  14. data/lib/semaph/model/job.rb +21 -2
  15. data/lib/semaph/model/job_collection.rb +19 -3
  16. data/lib/semaph/model/pipeline.rb +29 -3
  17. data/lib/semaph/model/promotion.rb +31 -0
  18. data/lib/semaph/model/promotion_collection.rb +21 -0
  19. data/lib/semaph/model/workflow.rb +17 -3
  20. data/lib/semaph/shells/organisation/organisation_shell.rb +4 -4
  21. data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
  22. data/lib/semaph/shells/organisations/organisations_list_command.rb +2 -2
  23. data/lib/semaph/shells/organisations/organisations_select_command.rb +11 -2
  24. data/lib/semaph/shells/organisations/organisations_shell.rb +2 -2
  25. data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
  26. data/lib/semaph/shells/pipeline/job_log_command.rb +17 -15
  27. data/lib/semaph/shells/pipeline/job_log_grep_command.rb +5 -11
  28. data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
  29. data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
  30. data/lib/semaph/shells/pipeline/jobs_list_command.rb +4 -2
  31. data/lib/semaph/shells/pipeline/jobs_poll_command.rb +58 -22
  32. data/lib/semaph/shells/pipeline/pipeline_shell.rb +28 -68
  33. data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
  34. data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
  35. data/lib/semaph/shells/project/project_shell.rb +4 -2
  36. data/lib/semaph/shells/project/save_command.rb +26 -0
  37. data/lib/semaph/shells/project/workflows_list_command.rb +11 -17
  38. data/lib/semaph/shells/workflow/pipelines_list_command.rb +3 -1
  39. data/lib/semaph/shells/workflow/workflow_shell.rb +6 -66
  40. data/lib/semaph/version.rb +1 -1
  41. data/semaph.gemspec +1 -0
  42. metadata +30 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0677122d6857a85e92dc0a74346b4f130f1f715ad9cfede85ffab143540867e
4
- data.tar.gz: c6165a0966dd6cd3088fcece1d56d304188c79552c03a0d48cb3addbe1e4649f
3
+ metadata.gz: 0653a67cbd9a978c6650491d39594eaf22d59b0be26d43bacc7a2f0933d5432c
4
+ data.tar.gz: 1ffb51d996af36938a6fc8b61e26da3db154161b7c420f31a8570a77545d6e1e
5
5
  SHA512:
6
- metadata.gz: d2ea65f9d5fdaf2b4b0597dc3fd0205eaa7fad89b596dff1fcf6619c6d39d11c80c9b5f9acd4e375049c6e16a91bcc4df5708cfd6e1725e834c80576d1442dbf
7
- data.tar.gz: 2505c4088ef17b9efdaa9c9d2dfc80159e459dfcc5f097d4761b69b2e390cb3371b611d9a913783812b5b1dd7435063ac8db3de2fb58f90168822068bcdc2ebf
6
+ metadata.gz: e7f196dfd55567cbd4621c0b2223b0367020d0c1d104560b108fc03a73846ccb56bbbca248321b0fb0c02c2ad4122c3093fe8280336440a2fc80c555bc191a5a
7
+ data.tar.gz: 59b468eccf7a64f79d4ea8317df870f24c4d3271d08a3616b992c9994fb4838276865ffc3fc47805795dec5f921323d53864a75c529e4852c9ac3cbe0f4b4ebd
@@ -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
 
data/.semaph ADDED
@@ -0,0 +1,9 @@
1
+ ---
2
+ :host: markryall.semaphoreci.com
3
+ :project:
4
+ spec:
5
+ repository:
6
+ url: git@github.com:markryall/semaph.git
7
+ metadata:
8
+ name: semaph
9
+ id: e75c6c57-2665-4e45-9cab-560508eb0b22
@@ -16,4 +16,8 @@ blocks:
16
16
  - name: Build
17
17
  commands:
18
18
  - checkout
19
- - echo "done"
19
+ - sem-version ruby $(grep ruby .tool-versions | cut -d ' ' -f 2)
20
+ - cache restore $(git ls-tree HEAD Gemfile.lock | cut -f3 -d$' ' | cut -f1 -d$'\t')
21
+ - bundle install --deployment --path vendor/bundle
22
+ - cache store $(git ls-tree HEAD Gemfile.lock | cut -f3 -d$' ' | cut -f1 -d$'\t') vendor/bundle
23
+ - bundle exec rake
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- semaph (0.4.0)
4
+ semaph (0.9.0)
5
5
  faraday
6
6
  rainbow
7
7
  shell_shock
@@ -9,18 +9,37 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
+ ast (2.4.0)
12
13
  coderay (1.1.2)
13
14
  faraday (1.0.1)
14
15
  multipart-post (>= 1.2, < 3)
15
16
  method_source (1.0.0)
16
17
  minitest (5.14.1)
17
18
  multipart-post (2.1.1)
19
+ parallel (1.19.1)
20
+ parser (2.7.1.3)
21
+ ast (~> 2.4.0)
18
22
  pry (0.13.1)
19
23
  coderay (~> 1.1)
20
24
  method_source (~> 1.0)
21
25
  rainbow (3.0.0)
22
26
  rake (12.3.3)
27
+ regexp_parser (1.7.0)
28
+ rexml (3.2.4)
29
+ rubocop (0.85.0)
30
+ parallel (~> 1.10)
31
+ parser (>= 2.7.0.1)
32
+ rainbow (>= 2.2.2, < 4.0)
33
+ regexp_parser (>= 1.7)
34
+ rexml
35
+ rubocop-ast (>= 0.0.3)
36
+ ruby-progressbar (~> 1.7)
37
+ unicode-display_width (>= 1.4.0, < 2.0)
38
+ rubocop-ast (0.0.3)
39
+ parser (>= 2.7.0.1)
40
+ ruby-progressbar (1.10.1)
23
41
  shell_shock (0.0.5)
42
+ unicode-display_width (1.7.0)
24
43
 
25
44
  PLATFORMS
26
45
  ruby
@@ -29,6 +48,7 @@ DEPENDENCIES
29
48
  minitest (~> 5.0)
30
49
  pry
31
50
  rake (~> 12.0)
51
+ rubocop
32
52
  semaph!
33
53
 
34
54
  BUNDLED WITH
data/README.md CHANGED
@@ -60,8 +60,6 @@ If you have only one set of credentials in `~/.sem.yml` then this will be the in
60
60
 
61
61
  From here you can `list-projects` and then `select-project` to enter a shell for that project.
62
62
 
63
- If new projects have been added/removed while you are using this shell, you can `reload-projects`.
64
-
65
63
  ### project shell
66
64
 
67
65
  🏗 foo.semaphoreci.com my-app >
@@ -71,8 +69,8 @@ substring of the git branch you want to see workflows for) and then `select-work
71
69
  (where 'index' is the number displayed for each workflow `list-workflows` - tab completion on uuids
72
70
  seemed a bad idea) to enter a shell for that specific workflow.
73
71
 
74
- You can also `open-github` (opens a web browser for the github project associated with the project), `open-project`
75
- (opens the semaphoreci project in a browser) and `reload-workflows` (to see any new/changed workflows).
72
+ You can also `open-github` (opens a web browser for the github project associated with the project) and `open-project`
73
+ (opens the semaphoreci project in a browser).
76
74
 
77
75
  ### workflow shell
78
76
 
@@ -83,16 +81,22 @@ from `semaphore.yml` plus any promotion pipelines that might be executed. You c
83
81
  what's happening with the pipeline.
84
82
 
85
83
  You can also `open-github-branch`, `open-github-commit` to see the branch/commit in a browser and
86
- `open-workflow`, `open-branch` to see the semaphore branch/workflow in a browser and `reload-pipelines` to see
87
- any changes that have happened in semaphore.
84
+ `open-workflow`, `open-branch` to see the semaphore branch/workflow in a browser.
88
85
 
89
86
  ### pipeline shell
90
87
 
91
88
  🏗 foo.semaphoreci.com my-app workflowuuid semaphore.yml >
92
89
 
93
- From this shell, you can `list-jobs` and `reload-jobs`.
90
+ From this shell, you can `list-jobs`, `poll-jobs`, `job-log` and `grep-logs`.
91
+
92
+ Job polling will stop as soon as any one job has failed and send a system notification
93
+ (as long as `terminal-notifier` is installed).
94
+
95
+ You can look at the log for a specific job (by the index presented in `list-jobs`) using `less`.
96
+
97
+ You can grep across all jobs (using `ag`) with `grep-logs` which will download the logs for all completed jobs.
94
98
 
95
- Jobs are displayed with a flattened view of blocks and jobs for compactness.
99
+ You can also open the same browser views in semaphoreci and github from this shell.
96
100
 
97
101
  ## Development
98
102
 
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require "rubocop/rake_task"
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << "test"
@@ -7,4 +8,6 @@ Rake::TestTask.new(:test) do |t|
7
8
  t.test_files = FileList["test/**/*_test.rb"]
8
9
  end
9
10
 
10
- task default: :test
11
+ RuboCop::RakeTask.new(:cop)
12
+
13
+ task default: %i[cop test]
@@ -1,22 +1,57 @@
1
+ require "semaph/client"
2
+ require "semaph/model/project"
1
3
  require "semaph/version"
2
4
  require "semaph/shells/organisations/organisations_shell"
3
5
  require "semaph/shells/organisation/organisation_shell"
6
+ require "semaph/shells/project/project_shell"
4
7
  require "yaml"
5
8
 
6
9
  module Semaph
7
10
  class Error < StandardError; end
8
11
 
9
12
  def self.console
10
- yaml_path = File.join(File.expand_path("~"), ".sem.yaml")
11
- raise "Please install the sem tool and authenticate to semaphoreci.com" unless File.exist?(yaml_path)
12
-
13
- sem_config = YAML.load_file(yaml_path)
14
- contexts = sem_config["contexts"]
13
+ organisations = load_organisations
14
+ config = load_config
15
15
 
16
- if contexts.count == 1
17
- Shells::Organisation::OrganisationShell.new(contexts.values.first).push
16
+ if config
17
+ console_with_config(organisations, config)
18
+ elsif organisations.count == 1
19
+ Shells::Organisation::OrganisationShell.new(organisations.first).push
18
20
  else
19
- Shells::Organisations::OrganisationsShell.new(contexts).push
21
+ Shells::Organisations::OrganisationsShell.new(organisations).push
22
+ end
23
+ end
24
+
25
+ def self.console_with_config(organisations, config)
26
+ organisation = organisations.find { |o| o["host"] == config[:host] }
27
+
28
+ Shells::Project::ProjectShell.new(
29
+ Model::Project.new(
30
+ ::Semaph::Client.new(
31
+ organisation["auth"]["token"],
32
+ organisation["host"],
33
+ ),
34
+ config[:project],
35
+ ),
36
+ ).push
37
+ end
38
+
39
+ def self.load_organisations
40
+ yaml_path = File.join(File.expand_path("~"), ".sem.yaml")
41
+
42
+ unless File.exist?(yaml_path)
43
+ unless organisations
44
+ puts "Please install the sem tool and authenticate to semaphoreci.com"
45
+ exit 1
46
+ end
20
47
  end
48
+
49
+ YAML.load_file(yaml_path)["contexts"].values
50
+ end
51
+
52
+ def self.load_config
53
+ return nil unless File.exist?(".semaph")
54
+
55
+ YAML.load_file(".semaph")
21
56
  end
22
57
  end
@@ -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
@@ -0,0 +1,63 @@
1
+ require "semaph/commands/reload_command"
2
+ require "semaph/commands/rerun_workflow_command"
3
+ require "semaph/commands/stop_workflow_command"
4
+ require "semaph/commands/visit_url_command"
5
+
6
+ module Semaph
7
+ module Commands
8
+ def self.workflow_commands(shell, workflow)
9
+ shell.add_command ::Semaph::Commands::RerunWorkflowCommand.new(workflow), "rerun"
10
+ shell.add_command ::Semaph::Commands::StopWorkflowCommand.new(workflow), "stop"
11
+ add_open_workflow_command(shell, workflow)
12
+ add_open_branch_command(shell, workflow)
13
+ add_github_commands(shell, workflow)
14
+ end
15
+
16
+ def self.add_open_workflow_command(shell, workflow)
17
+ shell.add_command(
18
+ ::Semaph::Commands::VisitUrlCommand.new(
19
+ "https://#{workflow.project.client.host}/workflows/#{workflow.id}",
20
+ "browse to workflow",
21
+ ),
22
+ "open-workflow",
23
+ )
24
+ end
25
+
26
+ def self.add_open_branch_command(shell, workflow)
27
+ shell.add_command(
28
+ ::Semaph::Commands::VisitUrlCommand.new(
29
+ "https://#{workflow.project.client.host}/branches/#{workflow.branch_id}",
30
+ "browse to branch in semaphore",
31
+ ),
32
+ "open-branch",
33
+ )
34
+ end
35
+
36
+ def self.add_github_commands(shell, workflow)
37
+ return unless workflow.project.github_url
38
+
39
+ add_github_branch(shell, workflow)
40
+ add_github_commit(shell, workflow)
41
+ end
42
+
43
+ def self.add_github_branch(shell, workflow)
44
+ shell.add_command(
45
+ ::Semaph::Commands::VisitUrlCommand.new(
46
+ "#{workflow.project.github_url}/tree/#{workflow.branch}",
47
+ "browse to the branch in github",
48
+ ),
49
+ "open-github-branch",
50
+ )
51
+ end
52
+
53
+ def self.add_github_commit(shell, workflow)
54
+ shell.add_command(
55
+ ::Semaph::Commands::VisitUrlCommand.new(
56
+ "#{workflow.project.github_url}/commit/#{workflow.sha}",
57
+ "browse to the commit in github",
58
+ ),
59
+ "open-github-commit",
60
+ )
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,3 @@
1
- require "semaph/shells/workflow/workflow_shell"
2
-
3
1
  module Semaph
4
2
  module Commands
5
3
  class RerunWorkflowCommand
@@ -1,5 +1,3 @@
1
- require "semaph/shells/workflow/workflow_shell"
2
-
3
1
  module Semaph
4
2
  module Commands
5
3
  class StopWorkflowCommand
@@ -1,9 +1,27 @@
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
22
+
23
+ def self.length(collection)
24
+ Rainbow(collection.length.to_s).cyan
25
+ end
8
26
  end
9
27
  end
@@ -1,3 +1,5 @@
1
+ require "fileutils"
2
+
1
3
  module Semaph
2
4
  module Model
3
5
  class Job
@@ -20,8 +22,25 @@ module Semaph
20
22
  assign_from_block(raw_block)
21
23
  end
22
24
 
23
- def log
24
- pipeline.workflow.project.client.job_log(id)
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
25
44
  end
26
45
 
27
46
  def description