wassup 0.2.0 → 0.3.2

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile.lock +19 -1
  4. data/README.md +11 -11
  5. data/bin/wassup +6 -1
  6. data/docs/.gitignore +20 -0
  7. data/docs/README.md +41 -0
  8. data/docs/babel.config.js +3 -0
  9. data/docs/blog/2021-12-09-welcome/index.md +8 -0
  10. data/docs/blog/authors.yml +5 -0
  11. data/docs/docs/Supfile-basics/_category_.json +4 -0
  12. data/docs/docs/Supfile-basics/understanding-the-supfile.md +50 -0
  13. data/docs/docs/intro.md +54 -0
  14. data/docs/docusaurus.config.js +110 -0
  15. data/docs/package-lock.json +21196 -0
  16. data/docs/package.json +43 -0
  17. data/docs/sidebars.js +31 -0
  18. data/docs/src/components/HomepageFeatures.module.css +10 -0
  19. data/docs/src/components/HomepageFeatures.tsx +60 -0
  20. data/docs/src/css/custom.css +28 -0
  21. data/docs/src/pages/index.module.css +36 -0
  22. data/docs/src/pages/index.tsx +39 -0
  23. data/docs/src/pages/markdown-page.md +7 -0
  24. data/docs/static/.nojekyll +0 -0
  25. data/docs/static/img/demo-supfile.png +0 -0
  26. data/docs/static/img/favicon.ico +0 -0
  27. data/docs/static/img/logo.svg +27 -0
  28. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  29. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  30. data/docs/static/img/tutorial-intro-starter-screenshot.png +0 -0
  31. data/docs/static/img/undraw_docusaurus_mountain.svg +170 -0
  32. data/docs/static/img/undraw_docusaurus_react.svg +169 -0
  33. data/docs/static/img/undraw_docusaurus_tree.svg +1 -0
  34. data/docs/static/img/wassup-long.png +0 -0
  35. data/docs/static/img/wassup-screenshot.png +0 -0
  36. data/docs/static/img/wassup.png +0 -0
  37. data/docs/static/video/wassup-demo.mov +0 -0
  38. data/docs/tsconfig.json +7 -0
  39. data/examples/basic/Supfile +183 -16
  40. data/examples/debug/Supfile +18 -0
  41. data/examples/josh-fastlane/Supfile +37 -94
  42. data/examples/simple/Supfile +17 -0
  43. data/examples/starter/Supfile +44 -0
  44. data/lib/wassup/app.rb +166 -6
  45. data/lib/wassup/color.rb +5 -0
  46. data/lib/wassup/helpers/circleci.rb +75 -0
  47. data/lib/wassup/helpers/github.rb +137 -0
  48. data/lib/wassup/helpers/netlify.rb +70 -0
  49. data/lib/wassup/helpers/shortcut.rb +114 -0
  50. data/lib/wassup/pane.rb +85 -16
  51. data/lib/wassup/pane_builder.rb +14 -1
  52. data/lib/wassup/version.rb +1 -1
  53. data/lib/wassup.rb +5 -0
  54. data/wassup.gemspec +1 -0
  55. metadata +59 -6
@@ -20,6 +20,9 @@ add_pane do |pane|
20
20
  pane.highlight = false
21
21
 
22
22
  pane.title = "Stats: fastlane-community"
23
+ pane.description = [
24
+ "Highlevel stats from fastlane-community about PR count"
25
+ ]
23
26
 
24
27
  pane.interval = 2
25
28
  pane.show_refresh = false
@@ -62,6 +65,9 @@ add_pane do |pane|
62
65
  pane.highlight = false
63
66
 
64
67
  pane.title = "Stats: fastlane/fastlane"
68
+ pane.description = [
69
+ "Highlevel stats from fastlane/fastlane about PR count"
70
+ ]
65
71
 
66
72
  pane.interval = 2
67
73
  pane.show_refresh = false
@@ -107,52 +113,22 @@ add_pane do |pane|
107
113
  pane.highlight = true
108
114
 
109
115
  pane.title = "Circle CI - fastlane/fastlane"
116
+ pane.description = [
117
+ "Shows workflows from last 14 days"
118
+ ]
110
119
 
111
120
  pane.interval = 60 * 5
112
121
  pane.show_refresh = true
122
+
113
123
  pane.content do |builder|
114
- resp = RestClient::Request.execute(
115
- method: :get,
116
- url: "https://circleci.com/api/v2/project/github/fastlane/fastlane/pipeline",
117
- headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
118
- )
119
- json = JSON.parse(resp)
120
- json["items"].select do |item|
121
- date = Time.parse(item["updated_at"])
122
- days = (Time.now - date).to_i / (24 * 60 * 60)
123
- days < 14
124
- end.map do |item|
125
- id = item["id"]
126
- number = item["number"]
127
- message = (item["vcs"]["commit"] || {})["subject"]
128
- login = item["trigger"]["actor"]["login"]
129
-
130
- resp = RestClient::Request.execute(
131
- method: :get,
132
- url: "https://circleci.com/api/v2/pipeline/#{id}/workflow",
133
- headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
134
- )
135
- json = JSON.parse(resp)
136
- workflow = json["items"].first
137
- status = workflow["status"]
138
-
139
- if status == "failed"
140
- status = "[fg=red]#{status}[fg=white]"
141
- elsif status == "success"
142
- status = "[fg=green]#{status}[fg=white]"
143
- else
144
- status = "[fg=yellow]#{status}[fg=white]"
145
- end
146
-
147
- display = "#{number} (#{status}) by #{login} - #{message}"
148
- object = [item, workflow]
149
-
150
- builder.add_row(display, object)
124
+ workflows = Helpers::CircleCI.workflows(vcs: 'github', org: 'fastlane', repo: 'fastlane', limit_days: 14)
125
+ workflows.each do |workflow|
126
+ display = Helpers::CircleCI::Formatter.workflow(workflow)
127
+ builder.add_row(display, workflow)
151
128
  end
152
129
  end
153
- pane.selection do |data|
154
- workflow = data[1]
155
130
 
131
+ pane.selection('enter', 'Opens up CirceCI workflow') do |workflow|
156
132
  slug = workflow["project_slug"]
157
133
  pipeline_number = workflow["pipeline_number"]
158
134
  workflow_id = workflow["id"]
@@ -160,8 +136,9 @@ add_pane do |pane|
160
136
  url = "https://app.circleci.com/pipelines/#{slug}/#{pipeline_number}/workflows/#{workflow_id}"
161
137
  `open #{url}`
162
138
  end
163
- pane.selection('o') do |data|
164
- pipeline = data[0]
139
+
140
+ pane.selection('o', 'Opens up version control review URL') do |workflow|
141
+ pipeline = workflow["pipeline"]
165
142
  url = pipeline["vcs"]["review_url"]
166
143
  `open #{url}`
167
144
  end
@@ -179,49 +156,26 @@ add_pane do |pane|
179
156
  pane.highlight = true
180
157
 
181
158
  pane.title = "Open PRs - fastlane-community"
159
+ pane.description = [
160
+ "Open PRs from all the fastlane-community repos"
161
+ ]
182
162
 
183
163
  pane.interval = 60 * 5
184
164
  pane.show_refresh = true
185
165
  pane.content do |builder|
186
166
  fastlane_community_prs = []
187
167
 
188
- resp = RestClient::Request.execute(
189
- method: :get,
190
- url: "https://api.github.com/orgs/fastlane-community/repos",
191
- user: ENV["WASSUP_GITHUB_USERNAME"],
192
- password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
193
- )
194
- json = JSON.parse(resp)
195
- json.map do |repo|
196
- name = repo["name"]
197
- full_name = repo["full_name"]
198
-
199
- resp = RestClient::Request.execute(
200
- method: :get,
201
- url: "https://api.github.com/repos/#{full_name}/pulls",
202
- user: ENV["WASSUP_GITHUB_USERNAME"],
203
- password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
204
- )
205
- json = JSON.parse(resp)
206
- prs = json.map do |pr|
207
- fastlane_community_prs << pr
208
-
209
- number = pr["number"]
210
- title = pr["title"]
211
- created_at = pr["created_at"]
212
-
213
- number_formatted = '%5.5s' % "##{number}"
214
-
215
- date = Time.parse(created_at)
216
- days = (Time.now - date).to_i / (24 * 60 * 60)
217
- days_formatted = '%3.3s' % days.to_s
218
-
219
- display = "[fg=yellow]#{number_formatted}[fg=cyan] #{days_formatted}d ago[fg=white] #{title}"
220
- builder.add_row(display, pr, page: name)
221
- end
168
+ prs = Wassup::Helpers::GitHub.pull_requests(org: 'fastlane-community')
169
+ prs.each do |pr|
170
+ fastlane_community_prs << pr
171
+
172
+ repo_name = pr["base"]["repo"]["name"]
173
+
174
+ display = Helpers::GitHub::Formatter.pr(pr)
175
+ builder.add_row(display, pr, page: repo_name)
222
176
  end
223
177
  end
224
- pane.selection do |data|
178
+ pane.selection('enter', 'Open PR in web browser') do |data|
225
179
  url = data["html_url"]
226
180
  `open #{url}`
227
181
  end
@@ -240,35 +194,24 @@ add_pane do |pane|
240
194
  pane.highlight = true
241
195
 
242
196
  pane.title = "Open PRs - fastlane/fastlane"
197
+ pane.description = [
198
+ "Open PRs from all the fastlane/fastlane repo"
199
+ ]
243
200
 
244
201
  pane.interval = 60 * 5
245
202
  pane.show_refresh = true
246
203
  pane.content do |builder|
247
204
  fastlane_prs = []
248
205
 
249
- resp = RestClient::Request.execute(
250
- method: :get,
251
- url: "https://api.github.com/repos/fastlane/fastlane/pulls?per_page=100",
252
- user: ENV["WASSUP_GITHUB_USERNAME"],
253
- password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
254
- )
255
- json = JSON.parse(resp)
256
- json.map do |pr|
206
+ prs = Wassup::Helpers::GitHub.pull_requests(org: 'fastlane', repo: 'fastlane')
207
+ prs.each do |pr|
257
208
  fastlane_prs << pr
258
209
 
259
- number = pr["number"]
260
- title = pr["title"]
261
- created_at = pr["created_at"]
262
-
263
- date = Time.parse(created_at)
264
- days = (Time.now - date).to_i / (24 * 60 * 60)
265
- days_formatted = '%3.3s' % days.to_s
266
-
267
- display = "[fg=yellow]##{number}[fg=cyan] #{days_formatted}d ago[fg=white] #{title}"
210
+ display = Helpers::GitHub::Formatter.pr(pr)
268
211
  builder.add_row(display, pr)
269
212
  end
270
213
  end
271
- pane.selection do |data|
214
+ pane.selection('enter', 'Open PR in browser') do |data|
272
215
  url = data["html_url"]
273
216
  `open #{url}`
274
217
  end
@@ -0,0 +1,17 @@
1
+ add_pane do |pane|
2
+ pane.height = 0.25
3
+ pane.width = 0.25
4
+ pane.top = 0
5
+ pane.left = 0
6
+
7
+ pane.highlight = false
8
+ pane.title = "The Title"
9
+
10
+ pane.interval = 1
11
+ pane.content do |content|
12
+ date = `date`
13
+ content.add_row(date)
14
+ content.add_row(date, page: "page 2")
15
+ end
16
+ end
17
+
@@ -0,0 +1,44 @@
1
+ add_pane do |pane|
2
+ pane.height = 0.5
3
+ pane.width = 0.4
4
+ pane.top = 0
5
+ pane.left = 0
6
+
7
+ pane.highlight = false
8
+ pane.title = "Current Time"
9
+
10
+ pane.interval = 1
11
+ pane.content do |content|
12
+ date = `date`
13
+
14
+ content.add_row(date)
15
+ end
16
+ end
17
+
18
+ add_pane do |pane|
19
+ pane.height = 0.5
20
+ pane.width = 0.6
21
+ pane.top = 0
22
+ pane.left = 0.4
23
+
24
+ pane.highlight = false
25
+ pane.title = "Open PRs - fastlane/fastlane"
26
+
27
+ pane.interval = 60 * 5
28
+ pane.content do |content|
29
+ require 'uri'
30
+ require 'net/http'
31
+ require 'json'
32
+
33
+ uri = URI('https://api.github.com/repos/fastlane/fastlane/pulls')
34
+ res = Net::HTTP.get_response(uri)
35
+
36
+ JSON.parse(res.body).each do |pr|
37
+ display = "[fg=cyan]##{pr['number']} - [fg=white]#{pr['title']}"
38
+ content.add_row(display)
39
+ end
40
+ end
41
+ pane.selection do |pr|
42
+ `open #{pr['html_url']}`
43
+ end
44
+ end
data/lib/wassup/app.rb CHANGED
@@ -20,6 +20,44 @@ module Wassup
20
20
  app = App.new(path: path)
21
21
  end
22
22
 
23
+ def self.debug(path:)
24
+ app = App.new(path: path, debug: true)
25
+
26
+ app.panes.each do |k, pane|
27
+ puts "#{k} - #{pane.title}"
28
+ end
29
+
30
+ puts ""
31
+ puts "Choose a pane to run:"
32
+
33
+ selection = $stdin.gets.chomp.to_s
34
+
35
+ pane = app.panes[selection]
36
+ if pane.nil?
37
+ puts "That was not a valid option"
38
+ else
39
+ puts "Going to run: \"#{pane.title}\""
40
+
41
+ builder = Wassup::PaneBuilder::ContentBuilder.new(pane.contents)
42
+ pane.content_block.call(builder)
43
+
44
+ builder.contents.each_with_index do |content, idx|
45
+ puts "#########################"
46
+ puts "# #{content.title || (idx == 0 ? "Default" : "<No Title>")}"
47
+ puts "#########################"
48
+
49
+ content.data.each do |data|
50
+ puts data.display
51
+ .split(/\[.*?\]/).join('') # Removes colors but make this an option probably
52
+ end
53
+
54
+ puts ""
55
+ puts ""
56
+ puts ""
57
+ end
58
+ end
59
+ end
60
+
23
61
  def add_pane
24
62
  pane_builder = Wassup::PaneBuilder.new
25
63
  yield(pane_builder)
@@ -31,28 +69,62 @@ module Wassup
31
69
  pane_builder.top,
32
70
  pane_builder.left,
33
71
  title: pane_builder.title,
72
+ description: pane_builder.description,
34
73
  highlight: pane_builder.highlight,
35
74
  focus_number: number,
36
75
  interval: pane_builder.interval,
37
76
  show_refresh: pane_builder.show_refresh,
38
77
  content_block: pane_builder.content_block,
39
- selection_blocks: pane_builder.selection_blocks
78
+ selection_blocks: pane_builder.selection_blocks,
79
+ selection_blocks_description: pane_builder.selection_blocks_description,
80
+ debug: debug
40
81
  )
41
82
  pane.focus_handler = @focus_handler
42
83
  @panes[number.to_s] = pane
43
84
  end
44
85
 
45
- def initialize(path:)
86
+ attr_accessor :panes
87
+ attr_accessor :debug
88
+
89
+ def initialize(path:, debug: false)
46
90
  @hidden_pane = nil
91
+ @help_pane = nil
47
92
  @focused_pane = nil
48
93
  @panes = {}
94
+ @debug = debug
95
+
96
+ if debug
97
+ self.start_debug(path)
98
+ else
99
+ self.start_curses(path)
100
+ end
101
+ end
102
+
103
+ def start_debug(path)
104
+ begin
105
+ eval(File.new(path).read)
106
+ rescue => err
107
+ puts err
108
+ puts err.backtrace
109
+ end
110
+ end
111
+
112
+ def start_curses(path)
113
+ @redraw_panes = false
49
114
 
50
115
  # TODO: this could maybe get replaced with selection_blocks now
51
116
  @focus_handler = Proc.new do |input|
117
+ is_help_open = !@help_pane.nil?
118
+
52
119
  if input == "q"
53
120
  exit
121
+ elsif input == "?"
122
+ toggle_help
123
+ next true
54
124
  end
55
125
 
126
+ next true if is_help_open
127
+
56
128
  if (pane = @panes[input.to_s])
57
129
  @focused_pane.focused = false
58
130
 
@@ -74,8 +146,7 @@ module Wassup
74
146
  end
75
147
 
76
148
  begin
77
-
78
- @hidden_pane = Pane.new(0, 0, 0, 0, highlight: false, focus_number: 0, interval: nil, show_refresh: false, content_block: nil, selection_blocks: nil)
149
+ @hidden_pane = Pane.new(0, 0, 0, 0, highlight: false, focus_number: 0, interval: nil, show_refresh: false, content_block: nil, selection_blocks: nil, selection_blocks_description: nil)
79
150
  @hidden_pane.focus_handler = @focus_handler
80
151
  @focused_pane = @hidden_pane
81
152
 
@@ -83,13 +154,102 @@ module Wassup
83
154
 
84
155
  loop do
85
156
  @focused_pane.handle_keyboard
86
- @panes.each do |id, pane|
87
- pane.refresh()
157
+
158
+ if @redraw_panes
159
+ Curses.clear
160
+ Curses.refresh
161
+ end
162
+
163
+ # This isn't ideal to now refresh other panes when help is open
164
+ # But it prevents things from getting drawn where the help is showing
165
+ if @help_pane.nil?
166
+ @panes.each do |id, pane|
167
+ pane.redraw() if @redraw_panes
168
+ pane.refresh()
169
+ end
170
+ @redraw_panes = false
171
+ else
172
+ @help_pane.refresh()
88
173
  end
89
174
  end
90
175
  ensure
91
176
  Curses.close_screen
92
177
  end
93
178
  end
179
+
180
+ def row_help
181
+ {
182
+ "j" => "moves row highlight down",
183
+ "k" => "moves row highlight up",
184
+ "enter" => "perform selection on highlighted row"
185
+ }
186
+ end
187
+
188
+ def page_help
189
+ {
190
+ "h" => "previous page in pane",
191
+ "l" => "next page in pane"
192
+ }
193
+ end
194
+
195
+ def toggle_help
196
+ if @help_pane.nil?
197
+ if @focused_pane == @hidden_pane
198
+ content_block = Proc.new do |content|
199
+ items = [
200
+ "Welcome to Wassup!",
201
+ "",
202
+ "Press any number key to focus a pane",
203
+ "",
204
+ row_help.map { |k,v| "#{k} - #{v}"},
205
+ "",
206
+ page_help.map { |k,v| "#{k} - #{v}"},
207
+ "",
208
+ "? - opens help for focused pane"
209
+ ].flatten
210
+
211
+ items.each do |item|
212
+ content.add_row(item)
213
+ end
214
+ end
215
+ else
216
+ content_block = Proc.new do |content|
217
+ hash = {}
218
+
219
+ hash = hash.merge(row_help)
220
+ hash = hash.merge(@focused_pane.selection_blocks_description)
221
+
222
+ row_help.map { |k,v| "#{k} - #{v}"}
223
+
224
+ copy_error = @focused_pane.caught_error.nil? ? [] : [
225
+ "c - copy stacktrace to clipboard",
226
+ ""
227
+ ]
228
+
229
+ items = [
230
+ @focused_pane.description,
231
+ "",
232
+ hash.map do |k,v|
233
+ "#{k} - #{v}"
234
+ end,
235
+ "",
236
+ copy_error,
237
+ page_help.map { |k,v| "#{k} - #{v}"},
238
+ ].flatten.compact
239
+
240
+ items.each do |item|
241
+ content.add_row(item)
242
+ end
243
+ end
244
+ end
245
+
246
+ # Maybe find a way to add some a second border or an clear border to add more space to show its floating
247
+ @help_pane = Pane.new(0.5, 0.5, 0.25, 0.25, title: "Help", highlight: false, focus_number: nil, interval: 100, show_refresh: false, content_block: content_block, selection_blocks: nil, selection_blocks_description: nil)
248
+ else
249
+ @help_pane.close
250
+ @help_pane = nil
251
+ @redraw_panes = true
252
+ end
253
+ end
94
254
  end
95
255
  end
data/lib/wassup/color.rb CHANGED
@@ -17,6 +17,8 @@ module Wassup
17
17
  NORMAL = 20
18
18
  HIGHLIGHT = 21
19
19
 
20
+ GRAY = 22
21
+
20
22
  BORDER = 20
21
23
  BORDER_FOCUS = 7
22
24
 
@@ -44,6 +46,7 @@ module Wassup
44
46
  Curses.init_pair(Pair::RED, Curses::COLOR_RED, 0)
45
47
  Curses.init_pair(Pair::WHITE, Pair::WHITE, 0)
46
48
  Curses.init_pair(Pair::YELLOW, Curses::COLOR_YELLOW, 0)
49
+ Curses.init_pair(Pair::GRAY, Curses::COLOR_WHITE, 0)
47
50
  end
48
51
 
49
52
  def initialize(string_name)
@@ -64,6 +67,8 @@ module Wassup
64
67
  Pair::WHITE
65
68
  when "yellow"
66
69
  Pair::YELLOW
70
+ when "gray"
71
+ Pair::GRAY
67
72
  else
68
73
  if string_name.to_i.to_s == string_name
69
74
  string_name.to_i
@@ -0,0 +1,75 @@
1
+ module Wassup
2
+ module Helpers
3
+ module CircleCI
4
+ def self.workflows(vcs:, org:, repo:, limit_days: nil)
5
+ require 'json'
6
+ require 'rest-client'
7
+
8
+ resp = RestClient::Request.execute(
9
+ method: :get,
10
+ url: "https://circleci.com/api/v2/project/#{vcs}/#{org}/#{repo}/pipeline",
11
+ headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
12
+ )
13
+ json = JSON.parse(resp)
14
+
15
+ return json["items"].select do |item|
16
+ if !limit_days.nil?
17
+ date = Time.parse(item["updated_at"])
18
+ days = (Time.now - date).to_i / (24 * 60 * 60)
19
+ days < limit_days
20
+ else
21
+ true
22
+ end
23
+ end.map do |pipeline|
24
+ id = pipeline["id"]
25
+
26
+ resp = RestClient::Request.execute(
27
+ method: :get,
28
+ url: "https://circleci.com/api/v2/pipeline/#{id}/workflow",
29
+ headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
30
+ )
31
+ json = JSON.parse(resp)
32
+ workflow = json["items"].first
33
+
34
+ if workflow
35
+ workflow["pipeline"] = pipeline
36
+ end
37
+
38
+ workflow
39
+ end.compact
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module Wassup
46
+ module Helpers
47
+ module CircleCI
48
+ module Formatter
49
+ def self.workflow(workflow)
50
+ pipeline = workflow["pipeline"]
51
+ number = pipeline["number"]
52
+ message = (pipeline["vcs"]["commit"] || {})["subject"]
53
+ login = pipeline["trigger"]["actor"]["login"]
54
+
55
+ status = workflow["status"]
56
+ status_formatted = '%-8.8s' % status
57
+
58
+ number_formatted = '%-7.7s' % "##{number}"
59
+
60
+ if status == "failed"
61
+ status_formatted = "[fg=red]#{status_formatted}[fg=white]"
62
+ elsif status == "success"
63
+ status_formatted = "[fg=green]#{status_formatted}[fg=white]"
64
+ else
65
+ status_formatted = "[fg=yellow]#{status_formatted}[fg=white]"
66
+ end
67
+
68
+ display = "[fg=yellow]#{number_formatted} [fg=while]#{status_formatted} [fg=white]#{login} [fg=gray]#{message}"
69
+
70
+ return display
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end