semaph 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +39 -0
  4. data/.semaphore/semaphore.yml +5 -1
  5. data/Gemfile.lock +21 -1
  6. data/README.md +85 -11
  7. data/Rakefile +4 -1
  8. data/lib/semaph/client.rb +115 -0
  9. data/lib/semaph/commands.rb +63 -0
  10. data/lib/semaph/commands/reload_command.rb +5 -4
  11. data/lib/semaph/commands/rerun_workflow_command.rb +16 -0
  12. data/lib/semaph/commands/stop_workflow_command.rb +16 -0
  13. data/lib/semaph/formatting.rb +15 -1
  14. data/lib/semaph/model/job.rb +70 -1
  15. data/lib/semaph/model/job_collection.rb +18 -5
  16. data/lib/semaph/model/pipeline.rb +54 -2
  17. data/lib/semaph/model/pipeline_collection.rb +0 -1
  18. data/lib/semaph/model/project.rb +1 -1
  19. data/lib/semaph/model/project_collection.rb +0 -1
  20. data/lib/semaph/model/promotion.rb +31 -0
  21. data/lib/semaph/model/promotion_collection.rb +21 -0
  22. data/lib/semaph/model/workflow.rb +28 -4
  23. data/lib/semaph/model/workflow_collection.rb +0 -1
  24. data/lib/semaph/shells/organisation/organisation_shell.rb +15 -11
  25. data/lib/semaph/shells/organisation/projects_list_command.rb +1 -0
  26. data/lib/semaph/shells/organisation/projects_select_command.rb +6 -0
  27. data/lib/semaph/shells/organisations/organisations_select_command.rb +8 -1
  28. data/lib/semaph/shells/organisations/organisations_shell.rb +6 -2
  29. data/lib/semaph/shells/pipeline/job_debug_command.rb +29 -0
  30. data/lib/semaph/shells/pipeline/job_log_command.rb +44 -0
  31. data/lib/semaph/shells/pipeline/job_log_grep_command.rb +28 -0
  32. data/lib/semaph/shells/pipeline/job_show_command.rb +28 -0
  33. data/lib/semaph/shells/pipeline/job_stop_command.rb +28 -0
  34. data/lib/semaph/shells/pipeline/jobs_list_command.rb +6 -16
  35. data/lib/semaph/shells/pipeline/jobs_poll_command.rb +55 -0
  36. data/lib/semaph/shells/pipeline/pipeline_shell.rb +42 -7
  37. data/lib/semaph/shells/pipeline/promote_command.rb +21 -0
  38. data/lib/semaph/shells/pipeline/promotions_list_command.rb +21 -0
  39. data/lib/semaph/shells/project/project_shell.rb +31 -18
  40. data/lib/semaph/shells/project/workflows_list_command.rb +4 -16
  41. data/lib/semaph/shells/project/workflows_select_command.rb +10 -3
  42. data/lib/semaph/shells/workflow/pipelines_list_command.rb +4 -1
  43. data/lib/semaph/shells/workflow/pipelines_select_command.rb +9 -3
  44. data/lib/semaph/shells/workflow/workflow_shell.rb +13 -42
  45. data/lib/semaph/version.rb +1 -1
  46. data/semaph.gemspec +1 -0
  47. metadata +33 -6
  48. data/lib/semaph/api.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b22217dbedbfa234279e494e23d64af812563e6fa8d57e2e2b14f185b3fbe52c
4
- data.tar.gz: 36c3a0944ef6408730c18b2f147f734616300023792e9e82b0c5ebc0956bab05
3
+ metadata.gz: 7db37cbca829e1b13372c7a8687b86080b1e4a579bbb7707e6cc207ebdcb4ba1
4
+ data.tar.gz: 4b5015379f62ad0382991f26b525583d2a6e077889071e5bde58dc1399d69ada
5
5
  SHA512:
6
- metadata.gz: 4fdda4999337920b39947b2c2fe09ea2cf62830998f948fed2d835dd74f5436172276dace4d59f0e493f14381aae349aaa0aafaa123b5f452ab4ed6a1fba0db0
7
- data.tar.gz: c5f969a3450d98f8a9b7a9eeb66d134856558e0376259070206ad0a8e76d4e3c38e6a12e3b27312aa272a48cc08af50288e99b443983ffc1d374d5401f82eb85
6
+ metadata.gz: 243de9cc3425299a5d7a5a11d3a6e06a89e56ad22e0bab59fe4568544fc8e05f0a6e902363497d493811a00c30f84a95456a0679417fb7d4d39b10ad35ece692
7
+ data.tar.gz: f1fd8d22e22885357f0b5c1ba962ac1edb91b61b6ab3b9e03cfec9aeda88197a40bfcfe2b047417d3d2629e5eb8f9fa3e32b9dde40e8feefeb09bae8e7e5527d
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .DS_Store
2
+ *.log
2
3
 
3
4
  /.bundle/
4
5
  /.yardoc
@@ -7,4 +8,4 @@
7
8
  /doc/
8
9
  /pkg/
9
10
  /spec/reports/
10
- /tmp/
11
+ /tmp/
@@ -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
 
@@ -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.1.0)
4
+ semaph (0.7.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
@@ -1,28 +1,102 @@
1
1
  # Semaph
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/semaph`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ This is command line shell for interacting with [semaphoreci 2.0](http://docs.semaphoreci.com/).
4
+
5
+ It complements the [sem](https://docs.semaphoreci.com/reference/sem-command-line-tool/) tool with some
6
+ overlap in responsibilities.
4
7
 
5
- TODO: Delete this and the text above, and describe your gem
6
8
 
7
9
  ## Installation
8
10
 
9
- Add this line to your application's Gemfile:
11
+ gem install semaph
12
+
13
+ ## Usage
14
+
15
+ To set up authentication to semaphoreci, you can just follow the instructions to install and setup
16
+ [sem](https://docs.semaphoreci.com/reference/sem-command-line-tool/). This will result
17
+ in your authentication details being written to `~/.sem.yaml`.
10
18
 
11
- ```ruby
12
- gem 'semaph'
19
+ You should see something like the following in this file:
20
+
21
+ ```yaml
22
+ active-context: foo_semaphoreci_com
23
+ contexts:
24
+ foo_semaphoreci_com:
25
+ auth:
26
+ token: qwerty
27
+ host: foo.semaphoreci.com
13
28
  ```
14
29
 
15
- And then execute:
30
+ Now you can run `semaph` and will be presented with a prompt like one of the following:
16
31
 
17
- $ bundle install
32
+ 🏗 >
18
33
 
19
- Or install it yourself as:
34
+ or
20
35
 
21
- $ gem install semaph
36
+ 🏗 foo.semaphoreci.com >
22
37
 
23
- ## Usage
38
+ This is the first of a series of nested shells. Each shell represents a different context
39
+ and has different commands available. To see the commands available, execute `help` and
40
+ to see the details of a particular command, execute `help <command name>`. Once you are
41
+ familar with the commands for each context, you can type the first couple of characters
42
+ and then hit tab to complete the rest. For some commands you can also tab complete the
43
+ parameters. Once you enter a shell, return to the previous shell or exit `semaph`, you can
44
+ press ctrl-d.
45
+
46
+ ### organisations shell
47
+
48
+ 🏗 >
49
+
50
+ You will only see this shell if you have multiple sets of credentials in your `~/.sem.yml` file.
51
+
52
+ From this shell, you can `list-organisations` you are authenticated to and `select-organisation`
53
+ to enter a shell for that organisation.
54
+
55
+ ### organisation shell
56
+
57
+ 🏗 foo.semaphoreci.com >
58
+
59
+ If you have only one set of credentials in `~/.sem.yml` then this will be the initial shell.
60
+
61
+ From here you can `list-projects` and then `select-project` to enter a shell for that project.
62
+
63
+ ### project shell
64
+
65
+ 🏗 foo.semaphoreci.com my-app >
66
+
67
+ From this shell, you will mostly be interested in `list-workflows <branch>` (where 'branch' is any
68
+ substring of the git branch you want to see workflows for) and then `select-workflow <index>`
69
+ (where 'index' is the number displayed for each workflow `list-workflows` - tab completion on uuids
70
+ seemed a bad idea) to enter a shell for that specific workflow.
71
+
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).
74
+
75
+ ### workflow shell
76
+
77
+ 🏗 foo.semaphoreci.com my-app workflowuuid >
78
+
79
+ From this shell, you can `list-pipelines` for the selected workflow. These are the builds usually starting
80
+ from `semaphore.yml` plus any promotion pipelines that might be executed. You can `select-pipeline` to monitor
81
+ what's happening with the pipeline.
82
+
83
+ You can also `open-github-branch`, `open-github-commit` to see the branch/commit in a browser and
84
+ `open-workflow`, `open-branch` to see the semaphore branch/workflow in a browser.
85
+
86
+ ### pipeline shell
87
+
88
+ 🏗 foo.semaphoreci.com my-app workflowuuid semaphore.yml >
89
+
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.
24
98
 
25
- TODO: Write usage instructions here
99
+ You can also open the same browser views in semaphoreci and github from this shell.
26
100
 
27
101
  ## Development
28
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]
@@ -0,0 +1,115 @@
1
+ require "faraday"
2
+ require "json"
3
+
4
+ module Semaph
5
+ # Refer to https://docs.semaphoreci.com/reference/api-v1alpha/
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
+
17
+ attr_reader :host, :name
18
+
19
+ def initialize(token, host)
20
+ @token = token
21
+ @host = host
22
+ @name = host.split(".").first
23
+ @host_url = "https://#{host}"
24
+ @base = "#{@host_url}/api/v1alpha"
25
+ end
26
+
27
+ def projects
28
+ get "projects"
29
+ end
30
+
31
+ def workflows(project_id)
32
+ get "plumber-workflows", { project_id: project_id }
33
+ end
34
+
35
+ def workflow(workflow_id)
36
+ get "plumber-workflows/#{workflow_id}"
37
+ end
38
+
39
+ def stop_workflow(workflow_id)
40
+ post "plumber-workflows/#{workflow_id}/terminate"
41
+ end
42
+
43
+ def rerun_workflow(workflow_id)
44
+ post "plumber-workflows/#{workflow_id}/reschedule?request_token=#{workflow_id}"
45
+ end
46
+
47
+ def pipelines(options)
48
+ get "pipelines", options
49
+ end
50
+
51
+ def pipeline(id)
52
+ get "pipelines/#{id}", { detailed: true }
53
+ end
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
+
63
+ def job(id)
64
+ get "jobs/#{id}"
65
+ end
66
+
67
+ def stop_job(id)
68
+ post "jobs/#{id}/stop"
69
+ end
70
+
71
+ def job_log(id)
72
+ get_raw "jobs/#{id}/plain_logs.json"
73
+ end
74
+
75
+ private
76
+
77
+ def get_raw(path, params = {})
78
+ url = "#{@host_url}/#{path}"
79
+ puts url if ENV["SEMAPH_DEBUG"]
80
+ response = Faraday.get(url, params, { "Authorization" => "Token #{@token}" })
81
+ check_response(response, url)
82
+ end
83
+
84
+ def get(path, params = {})
85
+ url = "#{@base}/#{path}"
86
+ puts url if ENV["SEMAPH_DEBUG"]
87
+ response = Faraday.get(url, params, headers)
88
+ JSON.parse(check_response(response, url)).tap do |hash|
89
+ pp hash if ENV["SEMAPH_DEBUG"]
90
+ end
91
+ end
92
+
93
+ def post(path, params = {})
94
+ url = "#{@base}/#{path}"
95
+ response = Faraday.post(url, params.to_json, headers)
96
+ JSON.parse(check_response(response, url)).tap do |hash|
97
+ pp hash if ENV["SEMAPH_DEBUG"]
98
+ end
99
+ end
100
+
101
+ def headers
102
+ {
103
+ "Authorization" => "Token #{@token}",
104
+ "Content-Type" => "application/json",
105
+ "Accept" => "application/json",
106
+ }
107
+ end
108
+
109
+ def check_response(response, url)
110
+ return response.body if response.status == 200
111
+
112
+ raise RequestException.new(url, response)
113
+ end
114
+ end
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
@@ -3,13 +3,14 @@ module Semaph
3
3
  class ReloadCommand
4
4
  attr_reader :help
5
5
 
6
- def initialize(entity, help)
7
- @entity = entity
8
- @help = help
6
+ def initialize
7
+ @help = "reload all code"
9
8
  end
10
9
 
11
10
  def execute(_whatever)
12
- @entity.reload
11
+ Dir["lib/**/*.rb"].each do |path|
12
+ load path
13
+ end
13
14
  end
14
15
  end
15
16
  end