This repository has been archived by the owner on Mar 21, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
cocoadocs.rb
executable file
·539 lines (430 loc) · 16.9 KB
/
cocoadocs.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
#!/usr/bin/env ruby
require 'cocoapods-downloader'
require 'cocoapods-core'
require 'cocoapods'
require 'jazzy'
gem 'nap'
require 'rest'
require 'ostruct'
require 'yaml'
require 'json'
require "fileutils"
require "octokit"
require 'open-uri'
require 'net/http'
require "shellwords"
require "colored"
require 'tilt'
require "slim"
require "nokogiri"
require 'tempfile'
require 'mustache'
class CocoaDocs < Object
def help
puts "\n" +
" CocoaDocs command line \n" +
" \n" +
" ./cocoadocs.rb preview [spec name or podspec path] [branch] \n" +
" ./cocoadocs.rb cocoadocs doc [spec name or path] \n" +
" ./cocoadocs.rb cocoadocs days [days] \n" +
" ./cocoadocs.rb cocoadocs url [json podspec url] \n" +
" ./cocoadocs.rb cocoadocs all [starting_pod] \n" +
" \n" +
" Options: \n" +
" \n" +
" --verbose \n" +
" --skip-fetch-specs \n" +
" --skip-cloc \n" +
" --skip-source-download \n" +
" --dont-delete-source \n" +
" --create-website \"http://example.com/\" \n" +
" --specs-repo \"name/repo\" or \"http://u:p@server.com/git/specs.git\" \n" +
" --data-folder \"activity\" \n" +
" --upload-s3 \"bucketname\" \n" +
" --master \n"
end
$specs_repo = "CocoaPods/Specs"
$s3_bucket = "cocoadocs.org"
$website_home = "http://cocoadocs.org/"
$cocoadocs_specs_name = "cocoadocs_specs"
$verbose = false
$log_all_terminal_commands = false
# Download and document
$fetch_specs = true
$skip_source_download = false
$force_branch = nil
$overwrite_existing_source_files = true
$delete_source_after_docset_creation = true
$skip_downloading_readme = false
$skip_cloc = false
# Generate site site & json
$generate_website = false
$generate_docset_json = false
$generate_apple_json = false
# Upload html / docsets
$upload_docsets_to_s3 = false
$upload_redirects_for_docsets = false
$upload_stats = false
$upload_site_to_s3 = false
# Constrain all downloads and data into one subfolder
$active_folder_name = "activity"
$current_dir = File.dirname(File.expand_path(__FILE__))
$active_folder = File.join($current_dir, $active_folder_name)
# Include all the classes files
Dir[File.join($current_dir, "classes/*.rb")].each do |file|
require_relative(file)
end
# command line parsing
def initialize(args)
if ARGV.length > 0
setup_options ARGV
command = ARGV[0].gsub(/-/, '_').to_sym rescue :help
@params = ARGV[1..-1]
commands.include?(command.to_sym) ? send(command.to_sym) : help
else
help
end
end
# parse all docs and upload to s3
# ruby cocoadocs.rb all
def all
setup_for_cocoadocs
update_specs_repo
source = Pod::Source.new(File.join($active_folder, $cocoadocs_specs_name))
source.pod_sets.each do |spec_set|
if @params[0]
# Allow skipping x amount of pods by using another argument
if spec_set.name && spec_set.name > @params[0]
document_spec_at_path(spec_set.highest_version_spec_path)
end
else
document_spec_at_path(spec_set.highest_version_spec_path)
end
end
end
# parse the latest version of every pod and upload to s3
# ruby cocoadocs.rb all_latest
def all_latest
setup_for_cocoadocs
update_specs_repo
source = Pod::Source.new(File.join($active_folder, $cocoadocs_specs_name))
source.pod_sets.each do |set|
document_spec_with_name set.name
end
end
# just parse ARAnalytics and put the docset in the activity folder
# cocoadocs doc "ARAnalytics"
def doc
update_specs_repo
@params.each do |param|
next if param.start_with?('--')
name = param
if name.end_with? ".podspec.json"
document_spec_at_path(name)
else
document_spec_with_name(name)
end
end
end
def cocoadocs
case @params.shift
when 'doc', 'docs'
cocoadocs_doc
when 'day', 'days'
cocoadocs_day
when 'url'
cocoadocs_url
end
end
def setup_for_cocoadocs
$generate_website = true
$generate_docset_json = true
$generate_apple_json = true
$website_home = "http://cocoadocs.org/"
$upload_docsets_to_s3 = true
$upload_redirects_for_spec_index = true
$upload_site_to_s3 = true
$s3_bucket = "cocoadocs.org"
$upload_stats = true
end
def cocoadocs_day
setup_for_cocoadocs
update_specs_repo
updated_specs = specs_for_days_ago_diff @params.first
vputs "Looking at #{updated_specs.lines.count} specs"
updated_specs.lines.each_with_index do |spec_filepath, index|
spec_filepath.gsub!(/\n/, '')
spec_path = $active_folder + "/" + $cocoadocs_specs_name + "/" + spec_filepath.strip
p spec_path
next unless spec_filepath.end_with? ".podspec.json" and File.exists? spec_path
document_spec_at_path spec_path
end
end
def cocoadocs_doc
setup_for_cocoadocs
doc
end
def cocoadocs_url
$fetch_specs = false
setup_for_cocoadocs
url = @params.first
spec_name = url.split("/")[-1]
podspec_path = $active_folder + "/podspecs/" + spec_name
FileUtils.mkdir_p(File.dirname(podspec_path))
open(url) do|f|
File.open(podspec_path, 'w') { |tmp| tmp.write(f.read) }
@params = [podspec_path]
doc
end
end
def spec_with_name(name)
source = Pod::Source.new(File.join($active_folder, $cocoadocs_specs_name))
set = source.search(Pod::Dependency.new(name))
if set
set.specification.root
end
end
# tip: offline command
# bundle exec ./cocoadocs.rb preview ARAnalytics mybranch --verbose --skip-fetch --skip-readme-download --skip-source-download
def preview
name = ARGV[1]
branch = ARGV[2] || "master"
if branch.start_with? "--"
branch = "master"
end
update_specs_repo
$overwrite_existing_source_files = true
$delete_source_after_docset_creation = false
$force_branch = branch
document_spec_with_name(name)
command "open \"#{ $active_folder }/docsets/#{ name }/\""
puts "Preview: \"#{ $active_folder }/docsets/#{ name }/\""
end
private
def setup_options options
if options.find_index("--beta") != nil
$beta = true
end
if options.find_index("--master") != nil
$force_branch = "master"
end
if options.find_index("--verbose") != nil
$verbose = true
$log_all_terminal_commands = true
end
if options.find_index("--no-repo-update") != nil
$fetch_specs = false
end
if options.find_index("--skip-cloc") != nil
$skip_cloc = true
end
if options.find_index("--dont-delete-source") != nil
$delete_source_after_docset_creation = false
end
index = options.find_index "--create-website"
if index != nil
$generate_website = true
$generate_docset_json = true
$generate_apple_json = true
$website_home = options[index + 1]
end
index = options.find_index("--upload-s3")
if index != nil
$upload_docsets_to_s3 = true
$upload_redirects_for_spec_index = true
$upload_redirects_for_docsets = true
$s3_bucket = options[index + 1]
end
index = options.find_index("--skip-source-download")
if index != nil
$skip_source_download = true
end
index = options.find_index("--skip-readme-download")
if index != nil
$skip_downloading_readme = true
end
index = options.find_index "--specs-repo"
$specs_repo = options[index + 1] if index != nil
index = options.find_index "--data-folder"
$active_folder_name = options[index + 1] if index != nil
$active_folder = File.join($current_dir, $active_folder_name)
end
# Update or clone Cocoapods/Specs
def update_specs_repo
repo = File.join($active_folder, $cocoadocs_specs_name)
unless File.exists? repo
vputs "Creating Specs Repo for #{$specs_repo}"
unless repo.include? "://"
command "git clone git://github.com/#{$specs_repo}.git \"#{repo}\""
else
command "git clone \"#{$specs_repo}\" \"#{repo}\""
end
else
if $fetch_specs
vputs "Updating Specs Repo"
run_git_command_in_specs "pull origin master"
`pod repo update`
end
end
end
# returns an array from the diff log for the last x days
def specs_for_days_ago_diff days_ago
sha = run_git_command_in_specs 'rev-list -n1 --before="' + days_ago + ' day ago" master'
diff_log = run_git_command_in_specs "diff --name-status #{sha}"
cleanup_git_logs diff_log
end
# cleans up and removes modification notice to the diff
def cleanup_git_logs diff_log
diff_log.lines.map do |line|
line.slice!(0).strip!
line.gsub!(/\t/, '')
end.join
end
# We have to run commands from a different git root if we want to do anything in the Specs repo
def run_git_command_in_specs git_command
Dir.chdir(File.join($active_folder, $cocoadocs_specs_name)) do
vputs "git #{git_command}"
`git #{git_command}`
end
end
# generate the documentation for the pod
def document_spec(spec)
state = "failed"
begin
download_location = $active_folder + "/download/#{spec.name}/#{spec.version}/#{spec.name}"
docset_location = $active_folder + "/docsets/#{spec.name}/#{spec.version}/"
readme_location = $active_folder + "/readme/#{spec.name}/#{spec.version}/index.html"
changelog_location = $active_folder + "/changelog/#{spec.name}/#{spec.version}/index.html"
pod_root_location = $active_folder + "/docsets/#{spec.name}/"
templates_location = $active_folder + "/template/#{spec.name}/"
unless $skip_source_download
downloader = SourceDownloader.new ({ :spec => spec, :download_location => download_location, :overwrite => $overwrite_existing_source_files })
FileUtils.rm_r download_location if File.directory?(download_location)
downloader.download_pod_source_files
end
settings = CocoaDocsSettings.settings_at_location download_location
jazzy_config = CocoaDocsSettings.jazzy_config_at_location download_location
readme = ReadmeGenerator.new ({ :spec => spec, :readme_location => readme_location, :changelog_location => changelog_location, :settings => settings })
readme.create_readme
readme.create_changelog
cloc = ClocStatsGenerator.new(:spec => spec, :source_download_location => download_location)
cloc_results = cloc.generate
version_metadata = SpecMetadataGenerator.new(:spec => spec, :docset_path => docset_location)
versions = version_metadata.generate
fixer = DocsetFixer.new({ :docset_path => docset_location, :readme_path => readme_location, :changelog_path => changelog_location, :pod_root => pod_root_location, :spec => spec, :versions => versions })
documented = false
swift = cloc_results.find { |r| r[:language] == 'Swift' }
header = cloc_results.find { |r| r[:language] == 'C/C++ Header' }
if swift
puts "Using jazzy to document Swift pod"
download_spec_path = download_location + "/#{spec.name}.podspec.json"
File.open(download_spec_path, 'w') { |f| f.write spec.to_json }
podspec = Pod::Specification.from_file(download_spec_path)
if podspec.dependencies && podspec.dependencies.length
puts "Updating the global CocoaPods specs repos, as this spec has dependencies."
`bundle exec pod repo update`
end
config = Jazzy::Config.new.tap do |c|
c.config_file = Pathname(jazzy_config) if jazzy_config
begin
c.parse_config_file
rescue => e
puts "Setting up jazzy config failed: #{e.message.red} - continuing"
end
c.podspec = podspec
c.podspec_configured = true
# Either use the version submitted to trunk, or try with 3.0
trunk_swift_version = c.podspec.attributes_hash["pushed_with_swift_version"]
c.swift_version = (trunk_swift_version && trunk_swift_version.strip) || "3.0"
c.output = Pathname(docset_location)
c.min_acl = Jazzy::SourceDeclaration::AccessControlLevel.public
c.docset_icon = Pathname(__FILE__).parent + 'resources/docset_icon.png'
c.docset_path = "com.cocoadocs.#{spec.name.downcase}.#{spec.name}.docset"
c.readme_path = Pathname(readme_location)
c.source_directory = Pathname(download_location)
c.module_name = spec.module_name
c.clean = true
c.dash_url = "#{$website_home}docsets/#{spec.name}/#{spec.name}.xml"
end
begin
Jazzy::DocBuilder.build(Jazzy::Config.instance = config)
fixer.readme_path = docset_location + '/index.html'
fixer.fix_for_jazzy
documented = true
rescue => e
puts "Jazzy failed: #{e.message.red}"
vputs "\n#{e.backtrace.inspect.red}"
log_error(spec, e) if spec != nil
end
end
unless documented
appledoc_template = AppledocTemplateGenerator.new({ :spec => spec, :appledoc_templates_path => templates_location, :source_download_location => download_location, :versions => versions, :library_settings => settings })
appledoc_template.generate
generator = DocsetGenerator.new({ :spec => spec, :to => docset_location, :from => download_location, :readme_location => readme_location, :appledoc_templates_path => templates_location, :source_download_location => download_location, :library_settings => settings })
generator.create_docset
fixer.fix
end
version_metadata.save
fixer.add_index_redirect_to_latest_to_pod(version_metadata.latest_version)
fixer.add_docset_redirects(version_metadata.latest_version) if $upload_redirects_for_docsets
if version_metadata.latest_version?
# These are things that are specific to the pod
# not the version, so it only works if it's latest
tester = TestingIdealist.new(:spec => spec, :download_location => download_location)
testing_estimate = tester.testimate
SocialImageGenerator.new(:spec => spec, :output_folder => docset_location, :doc_percent => 80, :testing_estimate => testing_estimate, :language => cloc.get_top_cloc(cloc_results)[:language]).generate
end
$generator = WebsiteGenerator.new(:generate_json => $generate_docset_json, :spec => spec)
$generator.upload_docset if $upload_docsets_to_s3
if $delete_source_after_docset_creation
vputs "Deleting source files"
command "rm -rf \"#{download_location}\""
command "rm -rf \"#{docset_location}\"" if $upload_site_to_s3
end
state = "success"
rescue Exception => e
log_error(spec, e) if spec != nil
open('error_log.txt', 'a') { |f|
f.puts "\n\n\n --------------#{spec.defined_in_file}-------------"
f.puts e.message
f.puts "------"
f.puts e.backtrace.inspect
}
puts "--------------#{spec.defined_in_file}-------------".red
puts e.message.red
puts "------"
puts e.backtrace.inspect.red
end
HistoryLogger.new(:spec => spec, :download_location => docset_location, :source_download_location => download_location).append_state state
puts "* [docs] - " + $website_home + "docsets/" + spec.name + "/" + spec.version.to_s + "/"
end
def document_spec_at_path(spec_path)
vputs "Pod::Specification.from_file(#{spec_path})"
spec = Pod::Specification.from_file(spec_path)
document_spec(spec)
end
def document_spec_with_name(name)
spec = spec_with_name(name)
if spec
document_spec(spec)
else
puts "Could not find #{name}"
end
end
def commands
(public_methods - Object.public_methods).map(&:to_sym)
end
def log_error spec, e
error_path = "errors/#{spec.name}/#{spec.version}/error.json"
FileUtils.mkdir_p(File.dirname(error_path))
FileUtils.rm(error_path) if File.exists? error_path
open(error_path, 'a'){ |f|
report = {
"message" => e.message.encode("utf-8", "binary", :undef => :replace),
"trace" => e.backtrace
}
f.puts report.to_json.to_s
}
end
end
CocoaDocs.new(ARGV)