coo 0.1.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 +7 -0
- data/.gitignore +53 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +214 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/Rakefile +44 -0
- data/TODO.md +16 -0
- data/bin/coo +40 -0
- data/coo.gemspec +26 -0
- data/lib/coo.rb +5 -0
- data/lib/coo/commands/git.rb +121 -0
- data/lib/coo/commands/pod.rb +140 -0
- data/lib/coo/helper/cm.rb +78 -0
- data/lib/coo/helper/file_hunter.rb +73 -0
- data/lib/coo/helper/git_helper.rb +138 -0
- data/lib/coo/helper/helper.rb +11 -0
- data/lib/coo/helper/podspec_helper.rb +75 -0
- data/lib/coo/helper/question.rb +116 -0
- data/lib/coo/helper/regexp_collection.rb +26 -0
- data/lib/coo/version.rb +3 -0
- metadata +122 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
class App
|
3
|
+
desc 'Git 命令快捷操作'
|
4
|
+
arg_name 'Describe arguments to git here'
|
5
|
+
command :git do |c|
|
6
|
+
|
7
|
+
c.desc '测试'
|
8
|
+
c.command :test do |test|
|
9
|
+
test.action do |global, options, args|
|
10
|
+
# /Users/ripper/Desktop/ZYDebugoxx
|
11
|
+
# /Users/ripper/Ripper/Code/Picooc/Picooc
|
12
|
+
# Dir.chdir('/Users/ripper/Desktop/ZYDebugoxx') do
|
13
|
+
# puts GitHelper.get_remote_urls_webs_table
|
14
|
+
# end
|
15
|
+
|
16
|
+
# s = `git ls-files`.split
|
17
|
+
# s.each_with_index do |a, b|
|
18
|
+
# puts "#{a} : #{b}"
|
19
|
+
# end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
c.desc 'Git 远程仓库信息 '
|
25
|
+
c.command [:webpage, :w] do |webpage|
|
26
|
+
webpage.action do |global, options, args|
|
27
|
+
puts GitHelper.get_remote_urls_webs_table
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
c.desc '快速 push 🚀'
|
32
|
+
c.arg_name '<commit info...>', %i(:multiple)
|
33
|
+
c.command [:push, :p] do |push|
|
34
|
+
push.action do |global_options, options, args|
|
35
|
+
commit_info = ''
|
36
|
+
args.each_with_index do |arg, idx|
|
37
|
+
commit_info.concat(' ') if idx != 0
|
38
|
+
commit_info.concat(arg)
|
39
|
+
end
|
40
|
+
|
41
|
+
if commit_info.empty?
|
42
|
+
commit_info = 'no commit info'
|
43
|
+
end
|
44
|
+
|
45
|
+
commands = [
|
46
|
+
'git add .',
|
47
|
+
"git commit -m \"#{commit_info}\"",
|
48
|
+
'git push'
|
49
|
+
]
|
50
|
+
|
51
|
+
all_finish = CM.sys_commands(*commands) do |idx, flag|
|
52
|
+
new_flag = flag
|
53
|
+
if idx == 2 && flag != 0
|
54
|
+
new_flag = CM.sys 'git push --set-upstream origin master'
|
55
|
+
end
|
56
|
+
new_flag
|
57
|
+
end
|
58
|
+
|
59
|
+
if all_finish
|
60
|
+
puts GitHelper.get_remote_urls_webs_table
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
c.desc '各种快捷删除 🗑'
|
67
|
+
c.command [:remove, :rm] do |remove|
|
68
|
+
|
69
|
+
remove.desc '清空所有文件, 并推送到远端 (不删除 commit 记录)❗'
|
70
|
+
remove.command [:files, :f] do |files|
|
71
|
+
files.action do |global_options, options, args|
|
72
|
+
|
73
|
+
Question.make_sure? 'Are you sure you want to empty the repository?' do |answer|
|
74
|
+
if answer
|
75
|
+
commands = [
|
76
|
+
"git rm -rf *",
|
77
|
+
"git commit -m 'Empty the repository'",
|
78
|
+
"git push"
|
79
|
+
]
|
80
|
+
|
81
|
+
all_finish = CM.sys_commands(*commands)
|
82
|
+
if all_finish
|
83
|
+
puts GitHelper.get_remote_urls_webs_table
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
remove.desc '删除本地及远端所有 commit 记录 (不删除文件), 慎重操作❗'
|
92
|
+
remove.command [:commits, :c] do |commits|
|
93
|
+
commits.action do |global_options, options, args|
|
94
|
+
Question.make_sure? 'Are you sure you want to remove all commits?' do |answer|
|
95
|
+
if answer
|
96
|
+
commands = [
|
97
|
+
'git checkout --orphan latest_branch',
|
98
|
+
'git add -A',
|
99
|
+
"git commit -am 'initial'",
|
100
|
+
"git branch -D master",
|
101
|
+
"git branch -m master",
|
102
|
+
"git push -f origin master"
|
103
|
+
]
|
104
|
+
all_finish = CM.sys_commands(*commands)
|
105
|
+
if all_finish
|
106
|
+
puts GitHelper.get_remote_urls_webs_table
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
class App
|
2
|
+
|
3
|
+
desc 'CocoaPods 的快捷操作'
|
4
|
+
command :pod do |c|
|
5
|
+
|
6
|
+
c.desc '快速发布 pod 🚀'
|
7
|
+
c.command :release do |release|
|
8
|
+
release.action do |global, options, args|
|
9
|
+
# TODO:
|
10
|
+
# 检查是否为 git 仓库
|
11
|
+
# 检查是否登录 trunk
|
12
|
+
|
13
|
+
# Spec 文件 及 Project 名字
|
14
|
+
specs = Dir["*.podspec"]
|
15
|
+
selected_spec = nil
|
16
|
+
selected_spec_name = nil
|
17
|
+
if specs.count <= 0
|
18
|
+
puts '未找到 spec 文件'
|
19
|
+
exit
|
20
|
+
elsif specs.count == 1
|
21
|
+
selected_spec = specs.first
|
22
|
+
selected_spec_name = selected_spec[0..-('.podspec'.length + 1)]
|
23
|
+
puts "已检索到 Spec 文件: #{selected_spec}".green
|
24
|
+
else
|
25
|
+
pod_names = specs.map do |spec|
|
26
|
+
name = spec[0..-('.podspec'.length + 1)]
|
27
|
+
end
|
28
|
+
|
29
|
+
Question.question_and_options("请问需要发布哪个框架?", true, *pod_names) do |idx, value|
|
30
|
+
if idx >= 0
|
31
|
+
selected_spec = specs[idx]
|
32
|
+
selected_spec_name = pod_names[idx]
|
33
|
+
else
|
34
|
+
selected_spec = specs.first
|
35
|
+
selected_spec_name = pod_names.first
|
36
|
+
end
|
37
|
+
puts "已选中 Spec 文件: #{selected_spec}".green
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# version
|
42
|
+
podspec_file = PodspecHelper.new(selected_spec, true)
|
43
|
+
current_spec_version = podspec_file.version_value
|
44
|
+
|
45
|
+
reg = RegexpCollection.n_a_u_h_1_n
|
46
|
+
version_comp = current_spec_version.to_s.split(/\./)
|
47
|
+
if version_comp.count == 3
|
48
|
+
reg = RegexpCollection.n_a_u_h_0_n
|
49
|
+
end
|
50
|
+
|
51
|
+
version = nil
|
52
|
+
Question.remind_input("当前 spec 中的 version 为 #{current_spec_version}, 请输入框架即将发布的 version: ", RegexpCollection.n_a_u_h_0_n) do |value|
|
53
|
+
if value.to_s.empty?
|
54
|
+
version = podspec_file.bump_version('patch')
|
55
|
+
else
|
56
|
+
version = value
|
57
|
+
end
|
58
|
+
puts "已设置即将发布的版本号: #{version}".green
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# pod repo
|
63
|
+
pod_repos_list = `pod repo list`
|
64
|
+
list_components = pod_repos_list.to_s.split(/\n/)
|
65
|
+
list_components_last = list_components.last.to_s.split
|
66
|
+
pod_repo_count = list_components_last.first.to_i
|
67
|
+
if list_components_last.count != 2 || !list_components_last.last.to_s.include?('repo') || pod_repo_count <= 0
|
68
|
+
puts '未找到 CocoaPods Spec 仓库'
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
|
72
|
+
pod_repo_names = Array.new
|
73
|
+
for i in 0..(pod_repo_count - 1)
|
74
|
+
name = list_components[i * 5 + 1]
|
75
|
+
pod_repo_names.push name
|
76
|
+
end
|
77
|
+
|
78
|
+
is_master = true
|
79
|
+
selected_repo = 'master'
|
80
|
+
unless pod_repo_names.count == 1 && pod_repo_names.first.to_s.eql?('master')
|
81
|
+
# 除非只有官方库,不然要选 spec 库
|
82
|
+
Question.question_and_options("请问需要发布到哪个 Spec 仓库?", true, *pod_repo_names) do |idx, name|
|
83
|
+
if idx > 0
|
84
|
+
is_master = false
|
85
|
+
selected_repo = name
|
86
|
+
end
|
87
|
+
puts "已选中 pod repo: #{selected_repo}".green
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# fastlane fastfile
|
92
|
+
fast_dir_name = 'fastlane'
|
93
|
+
fast_file_name = 'Fastfile'
|
94
|
+
fastlane_file = fast_dir_name + '/' + fast_file_name
|
95
|
+
|
96
|
+
fastlane_file_content = FileHunter.get_file_content(fastlane_file)
|
97
|
+
fastlane_source = "import_from_git(url: 'https://github.com/ripperhe/fastlane-files', branch: 'master')"
|
98
|
+
|
99
|
+
if File.exist?(fast_dir_name)
|
100
|
+
# 存在名为 fastlane 的文件
|
101
|
+
unless File.directory?(fast_dir_name)
|
102
|
+
puts "请将根目录的中名为 #{fast_dir_name} 的文件删除, 或者重命名, 然后重新操作!".red
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
else
|
106
|
+
Dir.mkdir(fast_dir_name)
|
107
|
+
end
|
108
|
+
|
109
|
+
if File.exist?(fastlane_file)
|
110
|
+
unless !File.directory?(fastlane_file)
|
111
|
+
puts "请将 #{fast_dir_name} 的文件夹删除, 或者重命名, 然后重新操作!".red
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
unless fastlane_file_content.include?(fastlane_source)
|
115
|
+
fastlane_file_content = fastlane_source + '\n' + fastlane_file_content
|
116
|
+
FileHunter.create_file(fastlane_file, fastlane_file_content)
|
117
|
+
puts "已更新 #{fastlane_file} 文件".green
|
118
|
+
end
|
119
|
+
else
|
120
|
+
FileHunter.create_file(fastlane_file, fastlane_source)
|
121
|
+
puts "已创建 #{fastlane_file} 文件".green
|
122
|
+
end
|
123
|
+
|
124
|
+
# 确认发布
|
125
|
+
Question.easy_make_sure?("确认发布框架 #{selected_spec_name} 的 #{version} 版本到 #{is_master ? 'CocoaPods/Specs' : selected_repo } 吗?") do |value|
|
126
|
+
if value
|
127
|
+
|
128
|
+
if is_master
|
129
|
+
CM.sys("fastlane release_pod project:#{selected_spec_name} version:#{version}")
|
130
|
+
else
|
131
|
+
CM.sys("fastlane release_pod repo:#{selected_repo} project:#{selected_spec_name} version:#{version}")
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Coo
|
4
|
+
module Helper
|
5
|
+
module CM
|
6
|
+
|
7
|
+
# 利用 system 语句执行 shell 命令
|
8
|
+
def self.sys(command_str)
|
9
|
+
puts current_time_str(command_str)
|
10
|
+
system command_str
|
11
|
+
result = $?
|
12
|
+
flag = result.to_s.split(' ').last.to_i
|
13
|
+
if flag != 0
|
14
|
+
# 非0,执行失败
|
15
|
+
puts "error with \"#{command_str}\" !".red
|
16
|
+
# exit
|
17
|
+
end
|
18
|
+
return flag
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.sys_commands(*commands)
|
22
|
+
all_finish = true
|
23
|
+
commands.each_with_index do |command, idx|
|
24
|
+
if !command.kind_of? String
|
25
|
+
puts "sys_commands 参数错误: #{command}"
|
26
|
+
break
|
27
|
+
end
|
28
|
+
|
29
|
+
flag = self.sys(command)
|
30
|
+
if block_given?
|
31
|
+
flag = yield idx, flag
|
32
|
+
end
|
33
|
+
if flag != 0
|
34
|
+
puts 'command break 💥'.red
|
35
|
+
all_finish = false
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return all_finish
|
40
|
+
end
|
41
|
+
|
42
|
+
# 利用 open3 执行 shell 命令
|
43
|
+
def self.open3(command_str, print_command: true, print_command_output: true)
|
44
|
+
puts current_time_str(command_str) if print_command
|
45
|
+
|
46
|
+
result = ''
|
47
|
+
exit_status = nil
|
48
|
+
|
49
|
+
Open3.popen2e(command_str) do |stdin, stdout_and_stderr, wait_thr|
|
50
|
+
stdout_and_stderr.sync = true
|
51
|
+
stdout_and_stderr.each do |line|
|
52
|
+
puts line.strip if print_command_output
|
53
|
+
result << line
|
54
|
+
end
|
55
|
+
exit_status = wait_thr.value
|
56
|
+
end
|
57
|
+
|
58
|
+
if exit_status.exitstatus != 0
|
59
|
+
# 非0,执行失败
|
60
|
+
puts "error with \"#{command_str}\" !".red
|
61
|
+
end
|
62
|
+
|
63
|
+
if block_given?
|
64
|
+
yield exit_status.exitstatus, result, command_str
|
65
|
+
end
|
66
|
+
exit_status.exitstatus
|
67
|
+
end
|
68
|
+
|
69
|
+
# private
|
70
|
+
|
71
|
+
private_class_method def self.current_time_str(command_str)
|
72
|
+
current_time = Time.new
|
73
|
+
current_time.strftime('[%H:%M:%S]: ').blue + command_str.green
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Coo
|
4
|
+
module Helper
|
5
|
+
module FileHunter
|
6
|
+
|
7
|
+
# @return 终端工作 dir path
|
8
|
+
def self.work_directory_path
|
9
|
+
return Dir.pwd
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return 终端工作 dir name
|
13
|
+
def self.work_directory_name
|
14
|
+
return File.basename(Dir.pwd)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [文件名, 传入 __FILE__] file
|
18
|
+
# @return script path
|
19
|
+
def self.file_path(file)
|
20
|
+
return Pathname.new(file).realpath
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [文件名, 传入 __FILE__] file
|
24
|
+
# @return script name
|
25
|
+
def self.file_name(file)
|
26
|
+
return File.basename(Pathname.new(file).realpath)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [文件名, 传入 __FILE__] file
|
30
|
+
# @return script dir name
|
31
|
+
def self.file_directory_name(file)
|
32
|
+
return File.dirname(file)
|
33
|
+
end
|
34
|
+
|
35
|
+
# 获取文件内容
|
36
|
+
# @param [文件名] file_name
|
37
|
+
def self.get_file_content(file_name)
|
38
|
+
content = ''
|
39
|
+
if file_name.to_s.empty?
|
40
|
+
puts 'get_file_content file_name is wrong!'
|
41
|
+
else
|
42
|
+
if File.exist?(file_name) && !File.directory?(file_name)
|
43
|
+
target_file = File.new(file_name, 'r')
|
44
|
+
if target_file
|
45
|
+
content = target_file.read
|
46
|
+
target_file.close
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
content
|
51
|
+
end
|
52
|
+
|
53
|
+
# 根据文件名和内容创建对应文件
|
54
|
+
# @param [文件名] file_name
|
55
|
+
# @param [文件内容] content
|
56
|
+
def self.create_file(file_name, content)
|
57
|
+
is_success = false
|
58
|
+
if file_name.to_s.empty?
|
59
|
+
puts 'file_name is wrong!'
|
60
|
+
else
|
61
|
+
new_file = File.new(file_name, 'w')
|
62
|
+
if new_file
|
63
|
+
new_file.syswrite(content)
|
64
|
+
new_file.close
|
65
|
+
is_success = true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
is_success
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Coo
|
2
|
+
module Helper
|
3
|
+
module GitHelper
|
4
|
+
|
5
|
+
def self.parse_ssh(ssh_str)
|
6
|
+
if !ssh_str.to_s.start_with?('git@')
|
7
|
+
puts 'parse_ssh 参数错误'
|
8
|
+
return nil
|
9
|
+
end
|
10
|
+
|
11
|
+
center_str = ssh_str[('git@'.length)..-('.git'.length + 1)]
|
12
|
+
|
13
|
+
components = center_str.to_s.split(':',)
|
14
|
+
if components.length == 2
|
15
|
+
last = components.last.to_s
|
16
|
+
sub_com = last.split('/')
|
17
|
+
if sub_com.length == 2
|
18
|
+
return {'author' => sub_com[0], 'repo' => sub_com[1]}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_https(https_str)
|
26
|
+
if !https_str.to_s.start_with?('https://')
|
27
|
+
puts 'parse_https 参数错误'
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
center_str = https_str[('https://'.length)..-('.git'.length + 1)]
|
32
|
+
|
33
|
+
components = center_str.to_s.split('/')
|
34
|
+
if components.length == 3
|
35
|
+
return {'author' => components[1], 'repo' => components[2]}
|
36
|
+
end
|
37
|
+
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_url_get_web(str)
|
42
|
+
result = nil
|
43
|
+
if str.start_with?('https://')
|
44
|
+
result = self.parse_https(str)
|
45
|
+
elsif str.start_with?('git@')
|
46
|
+
result = self.parse_ssh(str)
|
47
|
+
end
|
48
|
+
|
49
|
+
if result.length != 2
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
author = result['author']
|
54
|
+
repo = result['repo']
|
55
|
+
|
56
|
+
web_url = ''
|
57
|
+
if str.include?('github.com')
|
58
|
+
# github
|
59
|
+
web_url = 'https://github.com/'.concat(author).concat('/').concat(repo)
|
60
|
+
elsif str.include?('coding.net')
|
61
|
+
# coding
|
62
|
+
web_url = 'https://coding.net/u/'.concat(author).concat('/p/').concat(repo).concat('/git')
|
63
|
+
elsif str.include?('picooc.com')
|
64
|
+
# gitlab picooc
|
65
|
+
web_url = 'https://git.picooc.com/'.concat(author).concat('/').concat(repo)
|
66
|
+
end
|
67
|
+
|
68
|
+
return web_url
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.get_remotes
|
72
|
+
remote_info = `git remote`.to_s.chomp
|
73
|
+
if remote_info.empty?
|
74
|
+
return nil
|
75
|
+
end
|
76
|
+
remote_info.split(/\n/)
|
77
|
+
end
|
78
|
+
|
79
|
+
# {'orgin':[{url => "xxx", web => "www"}, {}...], 'up...':[{}, {}...]}
|
80
|
+
def self.get_remote_urls_webs
|
81
|
+
remotes = self.get_remotes
|
82
|
+
if !remotes
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
urls_webs_hash = Hash.new
|
87
|
+
remotes.each do |remote|
|
88
|
+
url_info = `git remote get-url --all #{remote}`.to_s.chomp
|
89
|
+
if url_info.empty?
|
90
|
+
next
|
91
|
+
end
|
92
|
+
urls = url_info.split(/\n/)
|
93
|
+
|
94
|
+
one_remote = Array.new
|
95
|
+
|
96
|
+
urls.each do |url|
|
97
|
+
web = self.parse_url_get_web(url)
|
98
|
+
sub_hash = Hash.new
|
99
|
+
sub_hash["url"] = url
|
100
|
+
sub_hash["web"] = web
|
101
|
+
one_remote << sub_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
urls_webs_hash[remote] = one_remote
|
105
|
+
end
|
106
|
+
|
107
|
+
urls_webs_hash
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.get_remote_urls_webs_table
|
111
|
+
urls_webs_hash = get_remote_urls_webs
|
112
|
+
if !urls_webs_hash
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
|
116
|
+
table = Terminal::Table.new do |t|
|
117
|
+
# t.title = 'Remotes'
|
118
|
+
t.headings = [{:value => 'remote', :alignment => :center}, {:value => 'url', :alignment => :center}, {:value => 'webpage', :alignment => :center}]
|
119
|
+
|
120
|
+
first = true
|
121
|
+
urls_webs_hash.each_pair do |key , value|
|
122
|
+
if first
|
123
|
+
first = false
|
124
|
+
else
|
125
|
+
t.add_separator
|
126
|
+
end
|
127
|
+
value.each do |sub|
|
128
|
+
t.add_row [key ,sub['url'], sub['web']]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return table
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|