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