autowow 0.14.1 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
File without changes
File without changes
File without changes
@@ -1,282 +1,282 @@
1
- require "uri"
2
- require "net/https"
3
- require "net/http"
4
- require "json"
5
- require "launchy"
6
-
7
- require_relative "../commands/vcs"
8
- require_relative "fs"
9
- require_relative "rbenv"
10
- require_relative "gem"
11
- require_relative "../time_difference"
12
-
13
- module Autowow
14
- module Features
15
- module Vcs
16
- include EasyLogging
17
- include Commands::Vcs
18
- include Executor
19
- include StringDecorator
20
-
21
- using RefinedTimeDifference
22
-
23
- def self.hi!
24
- logger.error("In a git repository. Try 1 level higher.") && return if is_git?
25
- hi do
26
- logger.info("Removing unused branches...")
27
- clear_branches
28
- logger.info("Adding upstream...")
29
- add_upstream
30
- logger.info("Removing unused gems...")
31
- Gem.gem_clean
32
- end
33
- end
34
-
35
- def self.hi
36
- logger.error("In a git repository. Try 1 level higher.") && return if is_git?
37
- latest_project_info = get_latest_repo_info
38
- logger.info("\nHang on, updating your local projects and remote forks...\n\n")
39
- git_projects.each do |project|
40
- Dir.chdir(project) do
41
- logger.info("\nGetting #{project} in shape...")
42
- yield if block_given?
43
- update_project
44
- end
45
- end
46
- greet(latest_project_info)
47
- end
48
-
49
- def self.force_pull
50
- pretty_with_output.run(git_status)
51
- branch = working_branch
52
-
53
- pretty_with_output.run(fetch("--all"))
54
- pretty_with_output.run(hard_reset("origin/#{branch}"))
55
-
56
- pretty_with_output.run(git_status)
57
- end
58
-
59
- def self.open
60
- url = origin_push_url(quiet.run(remotes).out)
61
- logger.info("Opening #{url}")
62
- Launchy.open(url)
63
- end
64
-
65
- def self.add_upstream
66
- logger.error("Not a git repository.") and return unless is_git?
67
- logger.warn("Already has upstream.") and return if has_upstream?
68
- remote_list = pretty_with_output.run(remotes).out
69
-
70
- url = URI.parse(origin_push_url(remote_list))
71
- host = "api.#{url.host}"
72
- path = "/repos#{url.path}"
73
- request = Net::HTTP.new(host, url.port)
74
- request.verify_mode = OpenSSL::SSL::VERIFY_NONE
75
- request.use_ssl = url.scheme == "https"
76
- logger.info("Fetching repo info from #{host}#{path}\n\n")
77
- response = request.get(path)
78
-
79
- if response.kind_of?(Net::HTTPRedirection)
80
- logger.error("Repository moved / renamed. Update remote or implement redirect handling. :)")
81
- elsif response.kind_of?(Net::HTTPNotFound)
82
- logger.error("Repository not found. Maybe it is private.")
83
- elsif response.kind_of?(Net::HTTPSuccess)
84
- parsed_response = JSON.parse(response.body)
85
- logger.warn("Not a fork.") and return unless parsed_response["fork"]
86
- parent_url = parsed_response.dig("parent", "html_url")
87
- pretty.run(add_remote("upstream", parent_url)) unless parent_url.to_s.empty?
88
- pretty_with_output.run(remotes)
89
- else
90
- logger.error("Github API (#{url.scheme}://#{host}#{path}) could not be reached: #{response.body}")
91
- end
92
- end
93
-
94
- def self.origin_push_url(remotes)
95
- # Order is important: first try to match "url" in "#{url}.git" as non-dot_git matchers would include ".git" in the match
96
- origin_push_url_ssl_dot_git(remotes) or
97
- origin_push_url_ssl(remotes) or
98
- origin_push_url_https_dot_git(remotes) or
99
- origin_push_url_https(remotes)
100
- end
101
-
102
- def self.origin_push_url_https(remotes)
103
- remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\s)\(push\))}]
104
- end
105
-
106
- def self.origin_push_url_https_dot_git(remotes)
107
- remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\.)git(\s)\(push\))}]
108
- end
109
-
110
- def self.origin_push_url_ssl_dot_git(remotes)
111
- url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\.)git(\s)\(push\))}]
112
- "https://#{url.gsub(':', '/')}" if url
113
- end
114
-
115
- def self.origin_push_url_ssl(remotes)
116
- url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\s)\(push\))}]
117
- "https://#{url.gsub(':', '/')}" if url
118
- end
119
-
120
- def self.clear_branches
121
- pretty_with_output.run(branch)
122
- branch_removed = false
123
-
124
- (branches - ["master", working_branch]).each do |branch|
125
- if branch_pushed(branch)
126
- pretty.run(branch_force_delete(branch))
127
- branch_removed = true
128
- end
129
- end
130
-
131
- pretty_with_output.run(branch) if branch_removed
132
- end
133
-
134
- def update_projects
135
- Fs.in_place_or_subdirs(is_git?) do
136
- update_project
137
- end
138
- end
139
-
140
- def update_project
141
- logger.info("Updating #{File.expand_path('.')} ...")
142
- logger.error("Not a git repository.") and return unless is_git?
143
- status = quiet.run(git_status).out
144
- if uncommitted_changes?(status) && working_branch.eql?("master")
145
- logger.warn("Skipped: uncommitted changes on master.") and return
146
- end
147
-
148
- on_branch("master") do
149
- has_upstream? ? pull_upstream : pretty_with_output.run(pull)
150
- end
151
- end
152
-
153
- def pull_upstream
154
- upstream_remote = "upstream"
155
- remote = "origin"
156
- branch = "master"
157
- pretty_with_output.run(fetch(upstream_remote)).out
158
- pretty_with_output.run(merge("#{upstream_remote}/#{branch}")).out
159
- pretty_with_output.run(push(remote, branch))
160
- end
161
-
162
- def has_upstream?
163
- quiet.run(remotes).out.include?("upstream")
164
- end
165
-
166
- def on_branch(branch)
167
- keep_changes do
168
- start_branch = working_branch
169
- switch_needed = !start_branch.eql?(branch)
170
- if switch_needed
171
- result = pretty.run!(checkout(branch))
172
- pretty.run(create(branch)) unless result.success?
173
- end
174
-
175
- begin
176
- yield if block_given?
177
- ensure
178
- pretty.run(checkout(start_branch)) if switch_needed
179
- end
180
- end
181
- end
182
-
183
- def branch_merged
184
- pretty_with_output.run(git_status)
185
- branch = working_branch
186
- logger.error("Nothing to do.") and return if branch.eql?("master")
187
-
188
- keep_changes do
189
- pretty_with_output.run(checkout("master"))
190
- pretty_with_output.run(pull)
191
- end
192
- pretty_with_output.run(branch_force_delete(branch))
193
-
194
- pretty_with_output.run(git_status)
195
- end
196
-
197
- def working_branch
198
- quiet.run(current_branch).out.strip
199
- end
200
-
201
- def branch_pushed(branch)
202
- quiet.run(changes_not_on_remote(branch)).out.empty?
203
- end
204
-
205
- def greet(latest_project_info = nil)
206
- logger.info("\nGood morning!\n\n")
207
- if is_git?
208
- logger.error("Inside repo, cannot show report about all repos.")
209
- else
210
- latest_project_info ||= get_latest_repo_info
211
- logger.info(latest_project_info)
212
- check_projects_older_than(1, :months)
213
- end
214
- obsolete_rubies = Rbenv.obsolete_versions
215
- if obsolete_rubies.any?
216
- logger.info("\nThe following Ruby versions are not used by any projects, maybe consider removing them?")
217
- obsolete_rubies.each do |ruby_verion|
218
- logger.info(" #{ruby_verion}")
219
- end
220
- end
221
- end
222
-
223
- def check_projects_older_than(quantity, unit)
224
- old_projects = Fs.older_than(git_projects, quantity, unit)
225
- deprecated_projects = old_projects.reject do |project|
226
- Dir.chdir(project) { branches.reject { |branch| branch_pushed(branch) }.any? }
227
- end
228
-
229
- logger.info("The following projects have not been touched for more than #{quantity} #{unit} and all changes have been pushed, maybe consider removing them?") unless deprecated_projects.empty?
230
- deprecated_projects.each do |project|
231
- time_diff = TimeDifference.between(File.mtime(project), Time.now).humanize_higher_than(:weeks).downcase
232
- logger.info(" #{File.basename(project)} (#{time_diff})")
233
- end
234
- end
235
-
236
- def get_latest_repo_info
237
- latest = latest_repo
238
- time_diff = TimeDifference.between(File.mtime(latest), Time.now).humanize_higher_than(:days).downcase
239
- time_diff_text = time_diff.empty? ? "recently" : "#{time_diff} ago"
240
- "It looks like you were working on #{File.basename(latest)} #{time_diff_text}.\n\n"
241
- end
242
-
243
- def latest_repo
244
- Fs.latest(git_projects)
245
- end
246
-
247
- def branches
248
- quiet.run(branch_list).out.clean_lines.map { |line| line[%r{(?<=refs/heads/)(.*)}] }
249
- end
250
-
251
- def uncommitted_changes?(status)
252
- !(status.include?("nothing to commit, working tree clean") or status.include?("nothing added to commit but untracked files present"))
253
- end
254
-
255
- def keep_changes
256
- status = quiet.run(git_status).out
257
- pop_stash = uncommitted_changes?(status)
258
- quiet.run(stash) if pop_stash
259
- begin
260
- yield if block_given?
261
- ensure
262
- quiet.run(stash_pop) if pop_stash
263
- end
264
- end
265
-
266
- def is_git?
267
- status = quiet.run!(git_status)
268
- Fs.git_folder_present && status.success? && !status.out.include?("Initial commit")
269
- end
270
-
271
- def git_projects
272
- Fs.ls_dirs.select do |dir|
273
- Dir.chdir(dir) do
274
- is_git?
275
- end
276
- end
277
- end
278
-
279
- include ReflectionUtils::CreateModuleFunctions
280
- end
281
- end
282
- end
1
+ require "uri"
2
+ require "net/https"
3
+ require "net/http"
4
+ require "json"
5
+ require "launchy"
6
+
7
+ require_relative "../commands/vcs"
8
+ require_relative "fs"
9
+ require_relative "rbenv"
10
+ require_relative "gem"
11
+ require_relative "../time_difference"
12
+
13
+ module Autowow
14
+ module Features
15
+ module Vcs
16
+ include EasyLogging
17
+ include Commands::Vcs
18
+ include Executor
19
+ include StringDecorator
20
+
21
+ using RefinedTimeDifference
22
+
23
+ def self.hi!
24
+ logger.error("In a git repository. Try 1 level higher.") && return if is_git?
25
+ hi do
26
+ logger.info("Removing unused branches...")
27
+ clear_branches
28
+ logger.info("Adding upstream...")
29
+ add_upstream
30
+ logger.info("Removing unused gems...")
31
+ Gem.gem_clean
32
+ end
33
+ end
34
+
35
+ def self.hi
36
+ logger.error("In a git repository. Try 1 level higher.") && return if is_git?
37
+ latest_project_info = get_latest_repo_info
38
+ logger.info("\nHang on, updating your local projects and remote forks...\n\n")
39
+ git_projects.each do |project|
40
+ Dir.chdir(project) do
41
+ logger.info("\nGetting #{project} in shape...")
42
+ yield if block_given?
43
+ update_project
44
+ end
45
+ end
46
+ greet(latest_project_info)
47
+ end
48
+
49
+ def self.force_pull
50
+ pretty_with_output.run(git_status)
51
+ branch = working_branch
52
+
53
+ pretty_with_output.run(fetch("--all"))
54
+ pretty_with_output.run(hard_reset("origin/#{branch}"))
55
+
56
+ pretty_with_output.run(git_status)
57
+ end
58
+
59
+ def self.open
60
+ url = origin_push_url(quiet.run(remotes).out)
61
+ logger.info("Opening #{url}")
62
+ Launchy.open(url)
63
+ end
64
+
65
+ def self.add_upstream
66
+ logger.error("Not a git repository.") and return unless is_git?
67
+ logger.warn("Already has upstream.") and return if has_upstream?
68
+ remote_list = pretty_with_output.run(remotes).out
69
+
70
+ url = URI.parse(origin_push_url(remote_list))
71
+ host = "api.#{url.host}"
72
+ path = "/repos#{url.path}"
73
+ request = Net::HTTP.new(host, url.port)
74
+ request.verify_mode = OpenSSL::SSL::VERIFY_NONE
75
+ request.use_ssl = url.scheme == "https"
76
+ logger.info("Fetching repo info from #{host}#{path}\n\n")
77
+ response = request.get(path)
78
+
79
+ if response.kind_of?(Net::HTTPRedirection)
80
+ logger.error("Repository moved / renamed. Update remote or implement redirect handling. :)")
81
+ elsif response.kind_of?(Net::HTTPNotFound)
82
+ logger.error("Repository not found. Maybe it is private.")
83
+ elsif response.kind_of?(Net::HTTPSuccess)
84
+ parsed_response = JSON.parse(response.body)
85
+ logger.warn("Not a fork.") and return unless parsed_response["fork"]
86
+ parent_url = parsed_response.dig("parent", "html_url")
87
+ pretty.run(add_remote("upstream", parent_url)) unless parent_url.to_s.empty?
88
+ pretty_with_output.run(remotes)
89
+ else
90
+ logger.error("Github API (#{url.scheme}://#{host}#{path}) could not be reached: #{response.body}")
91
+ end
92
+ end
93
+
94
+ def self.origin_push_url(remotes)
95
+ # Order is important: first try to match "url" in "#{url}.git" as non-dot_git matchers would include ".git" in the match
96
+ origin_push_url_ssl_dot_git(remotes) or
97
+ origin_push_url_ssl(remotes) or
98
+ origin_push_url_https_dot_git(remotes) or
99
+ origin_push_url_https(remotes)
100
+ end
101
+
102
+ def self.origin_push_url_https(remotes)
103
+ remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\s)\(push\))}]
104
+ end
105
+
106
+ def self.origin_push_url_https_dot_git(remotes)
107
+ remotes[%r{(?<=origin(\s))http(s?)://[a-zA-Z\-_./]*(?=(\.)git(\s)\(push\))}]
108
+ end
109
+
110
+ def self.origin_push_url_ssl_dot_git(remotes)
111
+ url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\.)git(\s)\(push\))}]
112
+ "https://#{url.gsub(':', '/')}" if url
113
+ end
114
+
115
+ def self.origin_push_url_ssl(remotes)
116
+ url = remotes[%r{(?<=origin(\s)git@)[a-zA-Z\-_./:]*(?=(\s)\(push\))}]
117
+ "https://#{url.gsub(':', '/')}" if url
118
+ end
119
+
120
+ def self.clear_branches
121
+ pretty_with_output.run(branch)
122
+ branch_removed = false
123
+
124
+ (branches - ["master", working_branch]).each do |branch|
125
+ if branch_pushed(branch)
126
+ pretty.run(branch_force_delete(branch))
127
+ branch_removed = true
128
+ end
129
+ end
130
+
131
+ pretty_with_output.run(branch) if branch_removed
132
+ end
133
+
134
+ def update_projects
135
+ Fs.in_place_or_subdirs(is_git?) do
136
+ update_project
137
+ end
138
+ end
139
+
140
+ def update_project
141
+ logger.info("Updating #{File.expand_path('.')} ...")
142
+ logger.error("Not a git repository.") and return unless is_git?
143
+ status = quiet.run(git_status).out
144
+ if uncommitted_changes?(status) && working_branch.eql?("master")
145
+ logger.warn("Skipped: uncommitted changes on master.") and return
146
+ end
147
+
148
+ on_branch("master") do
149
+ has_upstream? ? pull_upstream : pretty_with_output.run(pull)
150
+ end
151
+ end
152
+
153
+ def pull_upstream
154
+ upstream_remote = "upstream"
155
+ remote = "origin"
156
+ branch = "master"
157
+ pretty_with_output.run(fetch(upstream_remote)).out
158
+ pretty_with_output.run(merge("#{upstream_remote}/#{branch}")).out
159
+ pretty_with_output.run(push(remote, branch))
160
+ end
161
+
162
+ def has_upstream?
163
+ quiet.run(remotes).out.include?("upstream")
164
+ end
165
+
166
+ def on_branch(branch)
167
+ keep_changes do
168
+ start_branch = working_branch
169
+ switch_needed = !start_branch.eql?(branch)
170
+ if switch_needed
171
+ result = pretty.run!(checkout(branch))
172
+ pretty.run(create(branch)) unless result.success?
173
+ end
174
+
175
+ begin
176
+ yield if block_given?
177
+ ensure
178
+ pretty.run(checkout(start_branch)) if switch_needed
179
+ end
180
+ end
181
+ end
182
+
183
+ def branch_merged
184
+ pretty_with_output.run(git_status)
185
+ branch = working_branch
186
+ logger.error("Nothing to do.") and return if branch.eql?("master")
187
+
188
+ keep_changes do
189
+ pretty_with_output.run(checkout("master"))
190
+ pretty_with_output.run(pull)
191
+ end
192
+ pretty_with_output.run(branch_force_delete(branch))
193
+
194
+ pretty_with_output.run(git_status)
195
+ end
196
+
197
+ def working_branch
198
+ quiet.run(current_branch).out.strip
199
+ end
200
+
201
+ def branch_pushed(branch)
202
+ quiet.run(changes_not_on_remote(branch)).out.empty?
203
+ end
204
+
205
+ def greet(latest_project_info = nil)
206
+ logger.info("\nGood morning!\n\n")
207
+ if is_git?
208
+ logger.error("Inside repo, cannot show report about all repos.")
209
+ else
210
+ latest_project_info ||= get_latest_repo_info
211
+ logger.info(latest_project_info)
212
+ check_projects_older_than(1, :months)
213
+ end
214
+ obsolete_rubies = Rbenv.obsolete_versions
215
+ if obsolete_rubies.any?
216
+ logger.info("\nThe following Ruby versions are not used by any projects, maybe consider removing them?")
217
+ obsolete_rubies.each do |ruby_verion|
218
+ logger.info(" #{ruby_verion}")
219
+ end
220
+ end
221
+ end
222
+
223
+ def check_projects_older_than(quantity, unit)
224
+ old_projects = Fs.older_than(git_projects, quantity, unit)
225
+ deprecated_projects = old_projects.reject do |project|
226
+ Dir.chdir(project) { branches.reject { |branch| branch_pushed(branch) }.any? }
227
+ end
228
+
229
+ logger.info("The following projects have not been touched for more than #{quantity} #{unit} and all changes have been pushed, maybe consider removing them?") unless deprecated_projects.empty?
230
+ deprecated_projects.each do |project|
231
+ time_diff = TimeDifference.between(File.mtime(project), Time.now).humanize_higher_than(:weeks).downcase
232
+ logger.info(" #{File.basename(project)} (#{time_diff})")
233
+ end
234
+ end
235
+
236
+ def get_latest_repo_info
237
+ latest = latest_repo
238
+ time_diff = TimeDifference.between(File.mtime(latest), Time.now).humanize_higher_than(:days).downcase
239
+ time_diff_text = time_diff.empty? ? "recently" : "#{time_diff} ago"
240
+ "It looks like you were working on #{File.basename(latest)} #{time_diff_text}.\n\n"
241
+ end
242
+
243
+ def latest_repo
244
+ Fs.latest(git_projects)
245
+ end
246
+
247
+ def branches
248
+ quiet.run(branch_list).out.clean_lines.map { |line| line[%r{(?<=refs/heads/)(.*)}] }
249
+ end
250
+
251
+ def uncommitted_changes?(status)
252
+ !(status.include?("nothing to commit, working tree clean") or status.include?("nothing added to commit but untracked files present") or status.include?("nothing to commit, working directory clean"))
253
+ end
254
+
255
+ def keep_changes
256
+ status = quiet.run(git_status).out
257
+ pop_stash = uncommitted_changes?(status)
258
+ quiet.run(stash) if pop_stash
259
+ begin
260
+ yield if block_given?
261
+ ensure
262
+ quiet.run(stash_pop) if pop_stash
263
+ end
264
+ end
265
+
266
+ def is_git?
267
+ status = quiet.run!(git_status)
268
+ Fs.git_folder_present && status.success? && !status.out.include?("Initial commit")
269
+ end
270
+
271
+ def git_projects
272
+ Fs.ls_dirs.select do |dir|
273
+ Dir.chdir(dir) do
274
+ is_git?
275
+ end
276
+ end
277
+ end
278
+
279
+ include ReflectionUtils::CreateModuleFunctions
280
+ end
281
+ end
282
+ end