xccleanup 0.3.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c11e52272b6c8b2d17d990175b113e26bc820921
4
+ data.tar.gz: 7a09e454448c8c52f7b9dabc00b81c6eb15649e1
5
+ SHA512:
6
+ metadata.gz: 35aa6833ea2c9f0829bf01a57072ce929a528f998f0cdd464a521f2c33cca67bad21d8e48ef2f1d151133e373f9d7646bf11823caf48a9261ebe517bc06ff1c4
7
+ data.tar.gz: 59daff81b5a4a7c1f378ecd315c216fc5d127a5fdfd2d4b0f8e1aa12703ef17aa87cabde6f420c5390764e19dc6ed6e2223b4033a57e8d2032c2a7bf8f523a2e
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xccleanup.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Toine Heuvelmans
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # xccleanup
2
+
3
+ A cleanup tool that assists you in cleaning up after Xcode.
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'xccleanup'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install xccleanup
21
+
22
+ ## Usage
23
+
24
+ Just run it. You'll be asked which steps you would like to run.
25
+
26
+ ## Development
27
+
28
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
29
+
30
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
31
+
32
+ ## Contributing
33
+
34
+ Bug reports and pull requests are welcome on GitHub at https://github.com/toineheuvelmans/xccleanup.
35
+
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
40
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "xccleanup"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ module Xccleanup
2
+ VERSION = "0.3.0"
3
+ end
data/lib/xccleanup.rb ADDED
@@ -0,0 +1,460 @@
1
+ require "xccleanup/version"
2
+
3
+ require 'fileutils'
4
+ require 'date'
5
+ require 'rubygems'
6
+
7
+ module Xccleanup
8
+
9
+ # Helper functions
10
+
11
+ $line_length = 80
12
+
13
+ def self.line(char)
14
+ puts char * $line_length
15
+ end
16
+
17
+ def self.center(text)
18
+ if text.length > 0
19
+ puts (' ' * (($line_length - text.length) / 2)) + text
20
+ end
21
+ end
22
+
23
+ def self.prompt(*args)
24
+ print(*args)
25
+ gets
26
+ end
27
+
28
+ def self.prompt_bool(message)
29
+ message = message + " (y/n) "
30
+ input = prompt message
31
+ input = input.strip!
32
+ return input.casecmp("y") == 0 || input.casecmp("yes") == 0
33
+ end
34
+
35
+ def self.get_folders_in_dir(dir)
36
+ if File.directory?(dir)
37
+ return Dir.entries(dir).select {|entry| File.directory? File.join(dir,entry) and !(entry[0] =='.')}.map {|entry| File.join(dir,entry)}
38
+ end
39
+ return []
40
+ end
41
+
42
+ def self.get_files_in_dir(dir)
43
+ if File.directory?(dir)
44
+ return Dir.entries(dir).select {|entry| File.file? File.join(dir,entry)}.map {|entry| File.join(dir,entry)}
45
+ end
46
+ return []
47
+ end
48
+
49
+ def self.get_byte_size(file_or_dir)
50
+ file_or_dir = file_or_dir
51
+ if File.file? file_or_dir
52
+ return File.stat(file_or_dir).size
53
+ else
54
+ cmd = "du -ks '#{file_or_dir}'"
55
+ return (`#{cmd}`).split("\t").first.to_i * 1024
56
+ end
57
+ end
58
+
59
+ def self.pbs(bytes) # pretty byte size
60
+ {
61
+ 'B' => 1024,
62
+ 'KB' => 1024 * 1024,
63
+ 'MB' => 1024 * 1024 * 1024,
64
+ 'GB' => 1024 * 1024 * 1024 * 1024,
65
+ 'TB' => 1024 * 1024 * 1024 * 1024 * 1024
66
+ }.each do |e, s|
67
+ return "#{(bytes.to_f / (s / 1024)).round(1)} #{e}" if bytes < s
68
+ end
69
+ end
70
+
71
+ #######################################################################
72
+ # Cleanup functions
73
+
74
+ def self.remove_derived_data(manually)
75
+ saved_bytes = 0
76
+
77
+ dd_dir = File.expand_path('~/Library/Developer/Xcode/DerivedData')
78
+ dd_folders = get_folders_in_dir(dd_dir).sort_by{ |d| File.mtime(d) }.reverse
79
+ if dd_folders.length > 1
80
+
81
+ recent_projects = 0
82
+ if manually
83
+ recent_projects = prompt "> KEEP how many most recent projects? "
84
+ recent_projects = recent_projects.to_i
85
+ end
86
+
87
+ kept = 0
88
+ dd_folders.each do |folder_path|
89
+ folder_name = folder_path.split('/').last
90
+ if folder_name != 'ModuleCache'
91
+ project_name = folder_name.rindex('-').nil? ? folder_name : folder_name[0,folder_name.rindex('-')]
92
+ project_size = get_byte_size(folder_path)
93
+ if kept < recent_projects
94
+ puts "- Keeping #{project_name} (#{pbs(project_size)})"
95
+ else
96
+ puts "- Removing #{project_name} (#{pbs(project_size)})"
97
+ FileUtils.rm_rf(folder_path)
98
+ saved_bytes += project_size
99
+ end
100
+ kept += 1
101
+ end
102
+ end
103
+ else
104
+ puts "Skipping, no cleanup needed."
105
+ end
106
+
107
+ return saved_bytes
108
+ end
109
+
110
+ def self.remove_module_cache(manually)
111
+ saved_bytes = 0
112
+
113
+ puts "Removing Module Cache..."
114
+
115
+ dd_dir = File.expand_path('~/Library/Developer/Xcode/DerivedData')
116
+ path = File.join(dd_dir,'ModuleCache')
117
+ saved_bytes = get_byte_size(path)
118
+
119
+ get_folders_in_dir(path).each do |folder|
120
+ FileUtils.rm_rf(folder)
121
+ end
122
+
123
+ return saved_bytes
124
+ end
125
+
126
+
127
+ def self.remove_device_support(manually)
128
+ saved_bytes = 0
129
+
130
+ ds_folder = File.expand_path('~/Library/Developer/Xcode/iOS DeviceSupport/')
131
+ ds_versions = get_folders_in_dir(ds_folder)
132
+
133
+ if ds_versions.length > 0
134
+ puts "Found versions:"
135
+ ds_versions.each do |version_folder|
136
+ version_size = get_byte_size(version_folder)
137
+ version_name = version_folder.split('/').last
138
+ puts "• #{version_name} (#{pbs(version_size)})"
139
+ end
140
+
141
+ min_version = Gem::Version.new('9999.9.9')
142
+ if manually
143
+ min_version = prompt "> Miminum version to KEEP? "
144
+ min_version = Gem::Version.new(min_version)
145
+ end
146
+
147
+ unless min_version.nil?
148
+ ds_versions.each do |version_folder|
149
+ version_name = version_folder.split('/').last
150
+ version_number = version_name.split(' ').first
151
+ version_number = Gem::Version.new(version_number)
152
+ unless version_number.nil?
153
+ if version_number < min_version
154
+ puts "- Removing #{version_name}"
155
+ saved_bytes += get_byte_size(version_folder)
156
+ FileUtils.rm_rf(version_folder)
157
+ else
158
+ puts "- Keeping #{version_name}"
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ return saved_bytes
166
+ end
167
+
168
+
169
+ def self.remove_old_archives(manually)
170
+ saved_bytes = 0
171
+
172
+ arch_folder = File.expand_path('~/Library/Developer/Xcode/Archives/')
173
+ arch_date_folders = get_folders_in_dir(arch_folder)
174
+
175
+ bundle_ids = {}
176
+
177
+ arch_date_folders.each do |arch_date_folder|
178
+ date_name = arch_date_folder.split('/').last
179
+
180
+ archives = get_folders_in_dir(arch_date_folder)
181
+
182
+
183
+ if archives.length == 0
184
+ puts "- Empty archives subfolder #{date_name}, removing..."
185
+ FileUtils.rm_rf(arch_date_folder)
186
+ else
187
+ archives.each do |archive|
188
+ plist_path = File.join(archive,'Info.plist')
189
+ if File.file?(plist_path)
190
+ plist = File.read(plist_path)
191
+
192
+ bundle_id_pos = plist.index('<key>CFBundleIdentifier</key>')
193
+ unless bundle_id_pos.nil?
194
+ bundle_id_pos = plist.index('<string>', bundle_id_pos)
195
+ unless bundle_id_pos.nil?
196
+ bundle_id_pos = bundle_id_pos + 8 # <string>
197
+ bundle_id_end = plist.index('</string>', bundle_id_pos)
198
+ unless bundle_id_end.nil?
199
+ bundle_id = plist[bundle_id_pos, bundle_id_end - bundle_id_pos]
200
+ if bundle_ids[bundle_id].nil?
201
+ bundle_ids[bundle_id] = {date_name => archive }
202
+ else
203
+ bundle_ids[bundle_id][date_name] = archive
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ skip_single_archives = false
214
+ if manually
215
+ skip_single_archives = prompt_bool("Skip all bundle id's for which only a single archive is present?")
216
+ end
217
+
218
+ bundle_ids.each do |bundle_id, dates|
219
+ if dates.length > 1
220
+ dates = dates.sort_by { |date, archive| Date.parse(date) }.reverse
221
+ puts "• #{dates.length} archives for \"#{bundle_id}\":"
222
+ arch_index = 1
223
+ dates.each do |date, archive|
224
+ archive_size = get_byte_size(archive)
225
+ puts " #{arch_index}: #{date} (#{pbs(archive_size)})"
226
+ arch_index += 1
227
+ end
228
+
229
+ recent = 0
230
+ if manually
231
+ recent = prompt("> KEEP how many most recent? ").to_i
232
+ end
233
+
234
+ if recent < dates.length
235
+ kept = 0
236
+ dates.each do |date, archive|
237
+ if kept >= recent
238
+ archive_size = get_byte_size(archive)
239
+ puts "- Removing #{date}"
240
+ FileUtils.rm_rf(archive)
241
+ saved_bytes += archive_size
242
+ else
243
+ puts "- Keeping #{date}"
244
+ end
245
+ kept += 1
246
+ end
247
+ end
248
+ elsif !skip_single_archives
249
+ date, archive = dates.first
250
+ archive_size = get_byte_size(archive)
251
+
252
+ remove = !manually
253
+ if manually
254
+ remove = prompt_bool("• 1 archive for \"#{bundle_id}\" (#{date}, #{pbs(archive_size)})\n> REMOVE it?")
255
+ else
256
+ puts "• Removing 1 archive for \"#{bundle_id}\" (#{date}, #{pbs(archive_size)})"
257
+ end
258
+
259
+ if remove
260
+ FileUtils.rm_rf(archive)
261
+ saved_bytes += archive_size
262
+ end
263
+ end
264
+ end
265
+
266
+ return saved_bytes
267
+ end
268
+
269
+
270
+ def self.remove_expired_provisioning_profiles(manually)
271
+ saved_bytes = 0
272
+
273
+ pp_folder = File.expand_path('~/Library/MobileDevice/Provisioning Profiles')
274
+ profiles = get_files_in_dir(pp_folder)
275
+
276
+ today = Date.today
277
+
278
+ profiles.each do |profile|
279
+ escaped_profile = profile.gsub(/ /, '\ ')
280
+ filename = profile.split('/').last
281
+ ext = filename.split('.').last
282
+ if ext == 'mobileprovision'
283
+ cmd = "security cms -D -i #{escaped_profile}"
284
+ plist = `#{cmd}`
285
+
286
+ exp_date_pos = plist.index('<key>ExpirationDate</key>')
287
+ unless exp_date_pos.nil?
288
+ exp_date_pos = plist.index('<date>', exp_date_pos)
289
+ unless exp_date_pos.nil?
290
+ exp_date_pos = exp_date_pos + 6 # <date>
291
+ exp_date_end = plist.index('</date>', exp_date_pos)
292
+ unless exp_date_end.nil?
293
+ exp_date_str = plist[exp_date_pos, exp_date_end - exp_date_pos]
294
+ exp_date = Date.parse(exp_date_str)
295
+ if today > exp_date
296
+ puts "- Removing #{filename} (#{exp_date})"
297
+ saved_bytes += get_byte_size(profile)
298
+ FileUtils.rm(profile)
299
+ else
300
+ puts "- Skipping #{filename} (#{exp_date})"
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+
308
+ return saved_bytes
309
+ end
310
+
311
+
312
+ def self.remove_simulator_devices(manually)
313
+ saved_bytes = 0
314
+
315
+ sd_dir = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
316
+
317
+ devices_output = `xcrun simctl list devices`
318
+ devices = devices_output.scan /\s\s\s\s(.*) \(([^)]+)\) (.*)/
319
+ devices.each do |device|
320
+ device_uuid = nil
321
+ device.each do |device_component|
322
+ device_uuid = /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/.match(device_component.downcase)
323
+
324
+ unless device_uuid.nil?
325
+ device_uuid = device_uuid[0].upcase
326
+ break
327
+ end
328
+ end
329
+
330
+ unavailable = false
331
+ device.each do |device_component|
332
+ if device_component.include? 'unavailable'
333
+ unavailable = true
334
+ break
335
+ end
336
+ end
337
+
338
+ path = File.join(sd_dir, device_uuid)
339
+ byte_size = get_byte_size(path)
340
+
341
+ if unavailable || !manually || prompt_bool("> REMOVE #{device[0]} (#{pbs(byte_size)})?")
342
+ if unavailable
343
+ puts "! Unavailable device found: #{device[0]} (#{pbs(byte_size)})"
344
+ end
345
+
346
+ puts "- Removing device #{device[0]}"
347
+ `xcrun simctl delete #{device_uuid}`
348
+ saved_bytes += byte_size
349
+ end
350
+ end
351
+
352
+ return saved_bytes
353
+ end
354
+
355
+
356
+
357
+ def self.remove_doc_sets(manually)
358
+ saved_bytes = 0
359
+
360
+ ds_dir = File.expand_path('~/Library/Developer/Shared/Documentation/DocSets')
361
+ ds_folders = get_folders_in_dir(ds_dir)
362
+
363
+ ds_folders.each do |ds_folder|
364
+ docset_size = get_byte_size(ds_folder)
365
+ docset_name = ds_folder.split('/').last
366
+ if docset_name.split('.').last == 'docset'
367
+ if !manually || prompt_bool("> REMOVE \"#{docset_name}\" (#{pbs(docset_size)})?")
368
+ FileUtils.rm_rf(ds_folder)
369
+ saved_bytes += docset_size
370
+ end
371
+ end
372
+ end
373
+
374
+ return saved_bytes
375
+ end
376
+
377
+
378
+ # Execution and Menu
379
+
380
+ def self.run_steps(steps, manually)
381
+ total_saved_bytes = 0
382
+
383
+ steps.each do |method|
384
+ puts "\n"
385
+ line('-')
386
+ method_name = "#{method.name}".gsub('_', ' ').capitalize
387
+ center(method_name.upcase)
388
+ line('-')
389
+ saved_bytes = method.call(manually)
390
+ line('.')
391
+ center("#{method_name} completed, #{pbs(saved_bytes)} removed.")
392
+ total_saved_bytes += saved_bytes
393
+ end
394
+
395
+ line('–')
396
+ emoji = '😞'
397
+ if total_saved_bytes > 0
398
+ emoji = '👍'
399
+ end
400
+ mb = 1024 * 1024
401
+ if total_saved_bytes > (100 * mb)
402
+ emoji = '💪'
403
+ end
404
+ if total_saved_bytes > (5000 * mb)
405
+ emoji = '🍾'
406
+ end
407
+ if total_saved_bytes > (10000 * mb)
408
+ emoji = '💥'
409
+ end
410
+ center("🎉 XCODE CLEANUP completed, #{pbs(total_saved_bytes)} removed. #{emoji}")
411
+ line('–')
412
+ end
413
+
414
+
415
+ def self.menu()
416
+ line('–')
417
+ center('🗑 XCODE CLEANUP 🗑')
418
+ line('–')
419
+ puts "This script can perform the following steps:\n"
420
+
421
+ steps = [method(:remove_derived_data),
422
+ method(:remove_module_cache),
423
+ method(:remove_device_support),
424
+ method(:remove_old_archives),
425
+ method(:remove_expired_provisioning_profiles),
426
+ method(:remove_simulator_devices),
427
+ method(:remove_doc_sets)]
428
+
429
+ step_index = 1
430
+ steps.each do |method|
431
+ method_name = "#{method.name}".gsub('_', ' ').capitalize
432
+ puts "#{step_index}: #{method_name}"
433
+ step_index += 1
434
+ end
435
+
436
+ puts "\nWhat would you like to do?\n"
437
+ puts "[m] 🖐 Run each stap and manually specify what should be removed"
438
+ puts "[n] 💥 Nuke'm, remove everything that can be removed"
439
+ puts "[1-#{steps.length}] Run a single step"
440
+
441
+ choice = prompt("(M/N/1-#{steps.length}): ").strip!
442
+
443
+ if choice.casecmp('M') == 0
444
+ puts "Running all steps manually"
445
+ run_steps(steps, true)
446
+ elsif choice.casecmp('N') == 0
447
+ if prompt_bool("Are you sure you would like to Nuke'm?")
448
+ run_steps(steps, false)
449
+ end
450
+ elsif choice.to_i > 0 && choice.to_i <= steps.length
451
+ step = steps[choice.to_i - 1]
452
+ method_name = "#{step.name}".gsub('_', ' ').capitalize
453
+ puts "Running step #{choice.to_i}: #{method_name}"
454
+ run_steps([step], true)
455
+ end
456
+ end
457
+
458
+ self.menu()
459
+
460
+ end
data/xccleanup.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xccleanup/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xccleanup"
8
+ spec.version = Xccleanup::VERSION
9
+ spec.authors = ["Toine Heuvelmans"]
10
+ spec.email = ["toine@algorithmic.me"]
11
+
12
+ spec.summary = "A cleanup tool that assists you in cleaning up after Xcode."
13
+ spec.homepage = "https://github.com/toineheuvelmans/xccleanup"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xccleanup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Toine Heuvelmans
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - toine@algorithmic.me
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - bin/console
54
+ - bin/setup
55
+ - lib/xccleanup.rb
56
+ - lib/xccleanup/version.rb
57
+ - xccleanup.gemspec
58
+ homepage: https://github.com/toineheuvelmans/xccleanup
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.5.1
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A cleanup tool that assists you in cleaning up after Xcode.
82
+ test_files: []