reuse_xcode_plugins 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e75195c8ee6a35fd9d64e0be344fe7e4c61f737
4
- data.tar.gz: f21de8a4ff1d826f9fc5f41e13ce04cc06dd2a91
3
+ metadata.gz: ee46e89f4b99a95715daaa6e93cb5822ee2ab5c1
4
+ data.tar.gz: 937e2172ded713da5b7765fa25ed4e99965aa1af
5
5
  SHA512:
6
- metadata.gz: 0cbd93594f982beb718bf8f6ec6d917b082f7c18c2ae41d49ce81a5770604e1fd769c0dc549d5b66d37f17db1f000780c524b1e6e952d1e28cb802bb33e67450
7
- data.tar.gz: 670537e822e9436ad411655d7e71f6a1b77e7beeb7c5cbad8e672465655c8fc20444f4c43d00b993ee3705935a314375c7ea4609896982da2c0120b159203d99
6
+ metadata.gz: f51efdf60f5b86ac0592f191812b4e8b82f39a3bd7d7d507715758abb72f58e6d4489daa9a58030704a5714fba2250e0551fb4bfcfb85a2c5b8243bd11a19450
7
+ data.tar.gz: bab0870aa549e8ba3fdd37f34c29f139f36c763e3cdab12f198b71bab9d670bf3d42668cd4b8042c9fa694fdf659843f52ed69a9032eb0713cbe6a27e684419e
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/reuse_xcode_plugins'
4
+
5
+ begin
6
+ if CLI.unsign_xcode?
7
+ XcodeUnsigner.unsign_xcode
8
+ elsif CLI.restore_xcode?
9
+ XcodeUnsigner.restore_xcode
10
+ elsif CLI.update_plugins?
11
+ PluginsUpdater.update_plugins
12
+ else
13
+ puts "请输入参数--unsign(取消签名),--restore(恢复签名),--update_plugins(更新所有插件UUID)"
14
+ end
15
+ rescue Interrupt
16
+ end
Binary file
Binary file
data/lib/bin/unsign ADDED
Binary file
data/lib/bundle.rb ADDED
@@ -0,0 +1,41 @@
1
+ require_relative 'cli'
2
+
3
+ class Bundle
4
+ attr_accessor :path
5
+
6
+ def initialize(path)
7
+ self.path = path.strip
8
+ end
9
+
10
+ def valid?
11
+ false
12
+ end
13
+
14
+ def info_path
15
+ "#{path}/Contents/Info.plist"
16
+ end
17
+
18
+ def bundle_identifier
19
+ defaults_read("CFBundleIdentifier")
20
+ end
21
+
22
+ def version
23
+ defaults_read('CFBundleShortVersionString')
24
+ end
25
+
26
+ def defaults_read(key)
27
+ plist_path = "#{path}/Contents/Info"
28
+ `defaults read "#{plist_path}" #{key}`.strip
29
+ end
30
+
31
+ def defaults_write(*args)
32
+ plist_path = "#{path}/Contents/Info"
33
+ command = "defaults write \"#{plist_path}\" #{args.join(' ')}"
34
+
35
+ if CLI.dry_run?
36
+ puts command
37
+ else
38
+ `#{command}`.strip
39
+ end
40
+ end
41
+ end
data/lib/cli.rb ADDED
@@ -0,0 +1,65 @@
1
+ module CLI
2
+ def self.dry_run?
3
+ ARGV.include?('-d') || ARGV.include?('--dry-run')
4
+ end
5
+
6
+ def self.unsign_xcode?
7
+ ARGV.include?('--unsign')
8
+ end
9
+
10
+ def self.restore_xcode?
11
+ ARGV.include?('--restore')
12
+ end
13
+
14
+ def self.update_plugins?
15
+ ARGV.include?('--update_plugins')
16
+ end
17
+
18
+ def self.no_colors?
19
+ ARGV.include?('--no-colors')
20
+ end
21
+
22
+ def self.non_interactive?
23
+ ARGV.include?('--non-interactive')
24
+ end
25
+
26
+ def self.codesign_exists?
27
+ `which codesign` && $CHILD_STATUS.exitstatus == 0
28
+ end
29
+
30
+ def self.chown_if_required(path)
31
+ return yield if File.owned?(path)
32
+
33
+ puts
34
+ puts "* 正在修改 #{path} 的所有者,之后会恢复".colorize(:light_blue)
35
+
36
+ previous_owner = File.stat(path).uid
37
+ system("sudo chown $(whoami) \"#{path}\"")
38
+
39
+ raise "不能修改 #{path} 的所有者" unless File.owned?(path)
40
+
41
+ result = yield
42
+ system("sudo chown #{previous_owner} \"#{path}\"")
43
+ puts "* 恢复 #{path} 的所有者".colorize(:light_blue)
44
+
45
+ result
46
+ end
47
+
48
+ {
49
+ title: :blue,
50
+ process: :light_blue,
51
+ warning: :yellow,
52
+ error: :red,
53
+ success: :green
54
+ }.each do |type, color|
55
+ if CLI.no_colors?
56
+ define_method type.to_sym do |str| puts str end
57
+ else
58
+ define_method type.to_sym do |str| puts str.colorize(color) end
59
+ end
60
+ end
61
+
62
+ def separator
63
+ puts
64
+ end
65
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'xcode'
2
+ require_relative 'xcode_plugin'
3
+
4
+ class PluginsUpdater
5
+ extend CLI
6
+
7
+ def self.update_plugins
8
+ xcodes = Xcode.find_xcodes
9
+
10
+ if xcodes.empty?
11
+ error "没有发现任何安装的 Xcode"
12
+ return
13
+ else
14
+ title '已找到'
15
+ puts xcodes.map { |xcode| "- #{xcode.detailed_description}" }
16
+ end
17
+
18
+ separator
19
+
20
+ plugins = XcodePlugin.find_plugins
21
+
22
+ if plugins.empty?
23
+ error "Didn't find any Xcode Plug-in installed on your system."
24
+ return
25
+ else
26
+ title '插件:'
27
+ puts plugins.map { |s| "- #{s}" }
28
+ end
29
+
30
+ separator
31
+ process '更新UUID...'
32
+
33
+ uuids = xcodes.collect(&:uuid)
34
+ uuids.each do |uuid|
35
+ plugins.each do |plugin|
36
+ if plugin.add_uuid(uuid) && !CLI.dry_run?
37
+ success "添加 #{uuid} 到 #{plugin}"
38
+ end
39
+ end
40
+ end
41
+
42
+ separator
43
+ success '完成! 🎉'
44
+
45
+ return if CLI.no_colors?
46
+
47
+ # if xcodes.any? { |xcode| xcode.version.to_f >= 8 }
48
+ # separator
49
+ # warning 'It seems that you have Xcode 8+ installed!'
50
+ # puts 'Some plugins might not work on recent versions of Xcode because of library validation.',
51
+ # "See #{'https://github.com/alcatraz/Alcatraz/issues/475'.underline}"
52
+
53
+ # separator
54
+ # puts "Run `#{'update_xcode_plugins --unsign'.bold}` to fix this."
55
+ # end
56
+ end
57
+ end
@@ -1,3 +1,3 @@
1
1
  module ReuseXcodePlugins
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,7 +1,8 @@
1
- require "reuse_xcode_plugins/version"
2
-
3
- module ReuseXcodePlugins
4
- def self.hello
5
- p "Hello 夜禹!"
6
- end
7
- end
1
+ require 'English'
2
+ require 'fileutils'
3
+ require_relative 'cli'
4
+ require 'colorize' unless CLI.no_colors?
5
+ require 'inquirer' unless CLI.non_interactive?
6
+ # require_relative 'version'
7
+ require_relative 'plugins_updater'
8
+ require_relative 'xcode_unsigner'
data/lib/xcode.rb ADDED
@@ -0,0 +1,127 @@
1
+ require_relative 'bundle'
2
+
3
+ class Xcode < Bundle
4
+ attr_accessor :signed
5
+
6
+ # Hardcoded paths in case mdfind is not working because Spotlight is disabled
7
+ DEFAULT_XCODE_PATHS = [
8
+ "/Applications/Xcode.app"
9
+ ]
10
+
11
+ XCODE_BUNDLE_IDENTIFIER = "com.apple.dt.Xcode"
12
+
13
+ def self.find_xcodes
14
+ output = `mdfind kMDItemCFBundleIdentifier = "#{XCODE_BUNDLE_IDENTIFIER}"`
15
+ paths = output.lines + DEFAULT_XCODE_PATHS
16
+
17
+ paths.map(&:strip).uniq.collect do |xcode_path|
18
+ Xcode.from_bundle(xcode_path)
19
+ end.compact.keep_if(&:valid?)
20
+ end
21
+
22
+ def self.from_bundle(path)
23
+ xcode = new(path)
24
+ xcode.valid? ? xcode : nil
25
+ end
26
+
27
+ def valid?
28
+ is_app = path.end_with?('.app')
29
+ has_info = File.exist?(info_path)
30
+ return false unless is_app && has_info
31
+
32
+ bundle_identifier == XCODE_BUNDLE_IDENTIFIER
33
+ end
34
+
35
+ def signed?
36
+ if signed.nil?
37
+ self.signed = `codesign -dv "#{path}" 2>/dev/null` &&
38
+ $CHILD_STATUS.exitstatus == 0
39
+ end
40
+
41
+ signed
42
+ end
43
+
44
+ def restorable?
45
+ binary_restorable? || xcodebuild_restorable?
46
+ end
47
+
48
+ def binary_restorable?
49
+ File.exist?("#{binary_path}.signed")
50
+ end
51
+
52
+ def xcodebuild_restorable?
53
+ File.exist?("#{xcodebuild_path}.signed")
54
+ end
55
+
56
+ def unsign_binary!
57
+ unsign!(binary_path)
58
+ end
59
+
60
+ def unsign_xcodebuild!
61
+ unsign!(xcodebuild_path)
62
+ end
63
+
64
+ def restore_binary!
65
+ restore!(binary_path)
66
+ end
67
+
68
+ def restore_xcodebuild!
69
+ restore!(xcodebuild_path)
70
+ end
71
+
72
+ def uuid
73
+ defaults_read('DVTPlugInCompatibilityUUID')
74
+ end
75
+
76
+ def to_s
77
+ unless signed.nil?
78
+ codesign_status = signed ? ' [Signed]' : ' [Unsigned]'
79
+ end
80
+
81
+ "Xcode (#{version})#{codesign_status}: #{path}"
82
+ end
83
+
84
+ def detailed_description
85
+ "Xcode (#{version}) [#{uuid}]: #{path}"
86
+ end
87
+
88
+ private
89
+
90
+ def binary_path
91
+ "#{path}/Contents/MacOS/Xcode"
92
+ end
93
+
94
+ def xcodebuild_path
95
+ "#{path}/Contents/Developer/usr/bin/xcodebuild"
96
+ end
97
+
98
+ def unsign_path
99
+ lib_path = File.expand_path(File.dirname(__FILE__))
100
+
101
+ "#{lib_path}/bin/unsign"
102
+ end
103
+
104
+ def unsign!(target)
105
+ unsigned_target = "#{target}.unsigned"
106
+ signed_target = "#{target}.signed"
107
+
108
+ CLI.chown_if_required(File.dirname(target)) do
109
+ `#{unsign_path} "#{target}"` &&
110
+ $CHILD_STATUS.exitstatus == 0
111
+ File.exist?(unsigned_target) &&
112
+ FileUtils.mv(target, signed_target) &&
113
+ File.exist?(signed_target) &&
114
+ FileUtils.mv(unsigned_target, target)
115
+ end
116
+ end
117
+
118
+ def restore!(target)
119
+ signed_target = "#{target}.signed"
120
+
121
+ CLI.chown_if_required(File.dirname(target)) do
122
+ File.exist?(signed_target) &&
123
+ File.exist?(target) &&
124
+ FileUtils.mv(signed_target, target)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'bundle'
2
+
3
+ class XcodePlugin < Bundle
4
+ def self.find_plugins
5
+ plugins_path = "#{Dir.home}/Library/Application Support/Developer/Shared/Xcode/Plug-ins/"
6
+
7
+ unless Dir.exist?(plugins_path)
8
+ puts "未找到 Plug-ins 目录"
9
+ return []
10
+ end
11
+
12
+ Dir.entries(plugins_path).collect do |plugin_path|
13
+ XcodePlugin.from_bundle("#{plugins_path}#{plugin_path}")
14
+ end.compact.keep_if(&:valid?)
15
+ end
16
+
17
+ def self.from_bundle(path)
18
+ plugin = new(path)
19
+ plugin.valid? ? plugin : nil
20
+ end
21
+
22
+ def valid?
23
+ not_hidden = !path.split('/').last.start_with?('.')
24
+ is_plugin = path.end_with?('.xcplugin')
25
+ has_info = File.exist?(info_path)
26
+
27
+ not_hidden && is_plugin && has_info
28
+ end
29
+
30
+ def has_uuid?(uuid)
31
+ defaults_read('DVTPlugInCompatibilityUUIDs').include?(uuid)
32
+ end
33
+
34
+ def add_uuid(uuid)
35
+ return false if has_uuid?(uuid)
36
+
37
+ defaults_write('DVTPlugInCompatibilityUUIDs', '-array-add', uuid)
38
+ true
39
+ end
40
+
41
+ def to_s
42
+ "#{path.split('/').last.sub(/\.xcplugin$/, '')} (#{version})"
43
+ end
44
+ end
@@ -0,0 +1,94 @@
1
+ require_relative 'xcode'
2
+
3
+ class XcodeUnsigner
4
+ extend CLI
5
+
6
+ def self.unsign_xcode
7
+ process '正在查找安装的 Xcode...'
8
+ xcodes = Xcode.find_xcodes
9
+ .select { |xcode| xcode.version.to_f >= 8 }
10
+ .select(&:signed?)
11
+
12
+ separator
13
+
14
+ if xcodes.empty?
15
+ error "没有发现任何有签名的 Xcode 8+."
16
+ return
17
+ end
18
+
19
+ notice
20
+ separator
21
+
22
+ selection = Ask.list "选择你想要去掉签名的 Xcode(按上下键改变箭头)", xcodes
23
+ return unless selection
24
+
25
+ xcode = xcodes[selection]
26
+
27
+ # unsign_xcodebuild = Ask.confirm "Unsign xcodebuild too?"
28
+ unsign_xcodebuild = true
29
+
30
+ separator
31
+
32
+ process '正在去除签名...'
33
+ if xcode.unsign_binary! &&
34
+ (!unsign_xcodebuild || (unsign_xcodebuild && xcode.unsign_xcodebuild!))
35
+ success '完成! 🎉'
36
+ else
37
+ error "未能取消 #{xcode.path} 的签名\n"\
38
+ '请联系mPaaS开发者'
39
+ end
40
+ end
41
+
42
+ def self.restore_xcode
43
+ process '正在查找安装的 Xcode...'
44
+ xcodes = Xcode.find_xcodes
45
+ .select { |xcode| xcode.version.to_f >= 8 }
46
+ .select(&:restorable?)
47
+
48
+ separator
49
+
50
+ if xcodes.empty?
51
+ error "没有发现任何可恢复签名的 Xcode 8+."
52
+ return
53
+ end
54
+
55
+ selection = Ask.list "选择你想要恢复签名的 Xcode(按上下键改变箭头)", xcodes
56
+ return unless selection
57
+
58
+ xcode = xcodes[selection]
59
+
60
+ separator
61
+
62
+ process '正在恢复签名...'
63
+
64
+ success = true
65
+
66
+ if xcode.binary_restorable? && !xcode.restore_binary!
67
+ error "未能恢复 #{xcode.path} 的签名\n"\
68
+ '请联系mPaaS开发者'
69
+ success = false
70
+ end
71
+
72
+ if xcode.xcodebuild_restorable? && !xcode.restore_xcodebuild!
73
+ error "未能恢复 xcodebuild 的签名\n"\
74
+ '请联系mPaaS开发者'
75
+ success = false
76
+ end
77
+
78
+ success '完成! 🎉' if success
79
+ end
80
+
81
+ def self.update_plugins
82
+
83
+ end
84
+
85
+ def self.notice
86
+ puts [
87
+ '取消Xcode的签名将会跳过插件的签名验证,从而允许加载插件'.colorize(:yellow),
88
+ '然而,未签名的Xcode会产生一定的安全风险,Apple和你的系统都会不信任未签名的Xcode'\
89
+ '请不要使用未签名的Xcode进行打包操作,正常开发没有问题'.colorize(:red),
90
+ "这个工具会产生签名文件的备份,以便于之后你可以用下面这个命令恢复\n",
91
+ '$ mpaas --restoresign'.colorize(:light_blue)
92
+ ]
93
+ end
94
+ end
@@ -7,11 +7,12 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "reuse_xcode_plugins"
8
8
  spec.version = ReuseXcodePlugins::VERSION
9
9
  spec.authors = ["夜禹"]
10
- spec.email = ["mingyu.ymy@alipay.com"]
10
+ spec.email = ["me@yemingyu.com"]
11
11
 
12
12
  spec.summary = %q{Write a short summary, because Rubygems requires one.}
13
13
  spec.description = %q{Write a longer description or delete this line.}
14
14
  spec.homepage = "https://yemingyu.com"
15
+ spec.license = "MIT"
15
16
 
16
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
18
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -26,9 +27,13 @@ Gem::Specification.new do |spec|
26
27
  f.match(%r{^(test|spec|features)/})
27
28
  end
28
29
  spec.bindir = "exe"
29
- spec.executables = %w(ymyTest)
30
+ spec.executables = ['reuse_xcode_plugins']
31
+ # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
32
  spec.require_paths = ["lib"]
31
33
 
32
34
  spec.add_development_dependency "bundler", "~> 1.13"
33
35
  spec.add_development_dependency "rake", "~> 10.0"
36
+
37
+ spec.add_runtime_dependency 'colorize', '~> 0.8.1'
38
+ spec.add_runtime_dependency 'inquirer', '~> 0.2.1'
34
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reuse_xcode_plugins
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "夜禹"
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,11 +38,39 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: inquirer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.2.1
41
69
  description: Write a longer description or delete this line.
42
70
  email:
43
- - mingyu.ymy@alipay.com
71
+ - me@yemingyu.com
44
72
  executables:
45
- - ymyTest
73
+ - reuse_xcode_plugins
46
74
  extensions: []
47
75
  extra_rdoc_files: []
48
76
  files:
@@ -52,12 +80,22 @@ files:
52
80
  - Rakefile
53
81
  - bin/console
54
82
  - bin/setup
55
- - exe/ymyTest
83
+ - exe/reuse_xcode_plugins
84
+ - lib/bin/System Preferences
85
+ - lib/bin/System Preferences.unsigned
86
+ - lib/bin/unsign
87
+ - lib/bundle.rb
88
+ - lib/cli.rb
89
+ - lib/plugins_updater.rb
56
90
  - lib/reuse_xcode_plugins.rb
57
91
  - lib/reuse_xcode_plugins/version.rb
92
+ - lib/xcode.rb
93
+ - lib/xcode_plugin.rb
94
+ - lib/xcode_unsigner.rb
58
95
  - reuse_xcode_plugins.gemspec
59
96
  homepage: https://yemingyu.com
60
- licenses: []
97
+ licenses:
98
+ - MIT
61
99
  metadata:
62
100
  allowed_push_host: https://rubygems.org
63
101
  post_install_message:
data/exe/ymyTest DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bash
2
- p "Hello World!"