IDPROD-2142 part2: generate docs index.html page (#302)

Now generating a `docs/index.html` page that links to the documentation for all of the modules in the repo that have documentation enabled (`Stripe` & `StripeIdentity`). The index page reuses the same html template as the Jazzy theme we use to generate our docs. The descriptions that display for each module are populated by the summary in their podspec file. With the next deploy, this page will be visible at https://stripe.dev/stripe-ios.
This commit is contained in:
Mel 2021-08-02 14:58:10 -07:00
parent 987a7d0dcc
commit 702e8df394
10 changed files with 225 additions and 99 deletions

View File

@ -8,4 +8,4 @@ github_url: https://github.com/stripe/stripe-ios
github_file_prefix: https://github.com/stripe/stripe-ios/tree/master
skip_undocumented: true
hide_documentation_coverage: true
theme: fullwidth
theme: .jazzy/theme

View File

@ -31,9 +31,16 @@
{{> header}}
<p class="breadcrumbs">
<a class="breadcrumb" href="{{path_to_root}}index.html">{{module_name}} Reference</a>
{{#is_root_index}}
<a class="breadcrumb" href="{{path_to_root}}index.html">Stripe iOS SDKs</a>
{{/is_root_index}}
{{^is_root_index}}
<a class="breadcrumb" href="{{path_to_root}}../index.html">Stripe iOS SDKs</a>
<img class="carat" src="{{path_to_root}}img/carat.png" />
<a class="breadcrumb" href="{{path_to_root}}index.html">{{module_name}}</a>
<img class="carat" src="{{path_to_root}}img/carat.png" />
{{name}} {{kind}} Reference
{{/is_root_index}}
</p>
<div class="content-wrapper">

View File

@ -1,6 +1,11 @@
<header class="header">
<p class="header-col header-col--primary">
{{#is_root_index}}
<a class="header-link" href="{{path_to_root}}index.html">
{{/is_root_index}}
{{^is_root_index}}
<a class="header-link" href="{{path_to_root}}../index.html">
{{/is_root_index}}
{{docs_title}}
</a>
{{#doc_coverage}} ({{doc_coverage}}% documented){{/doc_coverage}}

View File

@ -0,0 +1,4 @@
{{#modules}}
<a href="{{path_to_root}}{{directory}}/index.html"><h2 class="heading">{{name}}</h2></a>
{{summary}}
{{/modules}}

View File

@ -3,6 +3,7 @@ source "https://rubygems.org"
gem "fastlane"
gem "cocoapods"
gem "jazzy", "~> 0.13.6"
gem "jazzy", "~> 0.13.7"
gem "mustache", "~> 1.1.1"
gem "xcode-install", "~> 2.6"

View File

@ -188,7 +188,7 @@ GEM
httpclient (2.8.3)
i18n (1.8.7)
concurrent-ruby (~> 1.0)
jazzy (0.13.6)
jazzy (0.13.7)
cocoapods (~> 1.5)
mustache (~> 1.1)
open4
@ -283,7 +283,8 @@ PLATFORMS
DEPENDENCIES
cocoapods
fastlane
jazzy (~> 0.13.6)
jazzy (~> 0.13.7)
mustache (~> 1.1.1)
xcode-install (~> 2.6)
BUNDLED WITH

View File

@ -5,12 +5,12 @@ Pod::Spec.new do |s|
# Instead, update the VERSION file and run ./ci_scripts/update_version.sh
s.version = '21.7.0'
s.summary = 'Stripe is a web-based API for accepting payments online.'
s.summary = 'Accept online payments using Stripe.'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.homepage = 'https://stripe.com/docs/mobile/ios'
s.authors = { 'Stripe' => 'support+github@stripe.com' }
s.source = { :git => 'https://github.com/stripe/stripe-ios.git', :tag => "#{s.version}" }
s.frameworks = 'Foundation', 'Security', 'WebKit', 'PassKit', 'Contacts', 'CoreLocation'
s.frameworks = 'Foundation', 'Security', 'WebKit', 'PassKit', 'Contacts', 'CoreLocation', 'UIKit'
s.requires_arc = true
s.platform = :ios
s.ios.deployment_target = '11.0'

View File

@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.homepage = 'https://stripe.com/docs/mobile/ios'
s.authors = { 'Stripe' => 'support+github@stripe.com' }
s.source = { :git => 'https://github.com/stripe/stripe-ios.git', :tag => "#{s.version}" }
s.frameworks = 'Foundation', 'Security', 'WebKit', 'PassKit', 'Contacts', 'CoreLocation'
s.frameworks = 'Foundation', 'UIKit'
s.requires_arc = true
s.platform = :ios
s.ios.deployment_target = '11.0'

View File

@ -5,12 +5,12 @@ Pod::Spec.new do |s|
# Instead, update the VERSION file and run ./ci_scripts/update_version.sh
s.version = '21.7.0'
s.summary = 'StripeIdentity is a web-based API that confirms the identity of global users.'
s.summary = 'Securely capture ID documents and selfies on iOS for use with Stripe\'s Identity API to confirm the identity of global users.'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.homepage = 'https://stripe.com/docs/mobile/ios'
s.homepage = 'https://stripe.com/identity'
s.authors = { 'Stripe' => 'support+github@stripe.com' }
s.source = { :git => 'https://github.com/stripe/stripe-ios.git', :tag => "#{s.version}" }
s.frameworks = 'Foundation', 'Security', 'WebKit', 'PassKit', 'Contacts', 'CoreLocation'
s.frameworks = 'Foundation', 'WebKit', 'UIKit'
s.requires_arc = true
s.platform = :ios
s.ios.deployment_target = '11.0'

View File

@ -1,13 +1,13 @@
#!/usr/bin/env ruby
require 'rubygems/dependency_installer.rb'
require 'cocoapods'
require 'jazzy'
require 'fileutils'
require 'mustache'
require 'optparse'
require 'pathname'
require 'yaml'
script_dir = __dir__
def info(string)
puts "[#{File.basename(__FILE__)}] [INFO] #{string}"
end
@ -16,116 +16,224 @@ def die(string)
abort "[#{File.basename(__FILE__)}] [ERROR] #{string}"
end
def install_jazzy(version = Gem::Requirement.default)
begin
installer = Gem::DependencyInstaller.new
installer.install("jazzy", version)
rescue
die "Executing \`gem install jazzy\` failed"
# Joins the given strings. If one or more arguments is nil or empty, an exception is raised.
def File.join_if_safe(arg1, *otherArgs)
args = [arg1] + otherArgs
# Check for empty or nil strings
args.each do |arg|
raise "Cannot join nil or empty string." if arg.nil? || arg.empty?
end
return File.join(args)
end
# MARK: - constants
$SCRIPT_DIR = __dir__
$ROOT_DIR = File.expand_path("..", $SCRIPT_DIR)
$JAZZY_CONFIG_FILE = File.join_if_safe($ROOT_DIR, ".jazzy.yaml")
$JAZZY_CONFIG = YAML.load_file($JAZZY_CONFIG_FILE)
# MARK: - build docs
# Note(mludowise): When `check_documentation.sh` locally, we want to save docs
# to a temp directory so we can check undocumented.json and grep for `@_spi`,
# without unintentially committing changes to the `/docs` folder.
docs_root_directory = File.expand_path("#{script_dir}/..", Dir.getwd)
OptionParser.new do |opts|
opts.on("--docs-root-dir DIRECTORY", "Generate docs to this directory instead of the repo's root directory.") do |dir|
docs_root_directory = File.expand_path(dir, Dir.getwd)
end
end.parse!
def get_docs_root_directory
docs_root_directory = $ROOT_DIR
# Verify jazzy is installed
begin
Gem::Specification.find_by_name('jazzy')
rescue
if ENV["CI"] != 'true'
die "Please install jazzy: https://github.com/realm/jazzy#installation"
end
OptionParser.new do |opts|
opts.on("--docs-root-dir DIRECTORY", "Generate docs to this directory instead of the repo's root directory.") do |dir|
docs_root_directory = File.expand_path(dir, Dir.getwd)
end
end.parse!
info "Installing jazzy..."
install_jazzy
end
# Verify jazzy is up to date
jazzy_version_local = Gem::Specification.find_by_name('jazzy').version
fetcher = Gem::SpecFetcher.fetcher
newer_jazzy_dependency = Gem::Dependency.new "jazzy", "> #{jazzy_version_local}"
newer_jazzy_remotes, = fetcher.search_for_dependency newer_jazzy_dependency
unless newer_jazzy_remotes.empty?
jazzy_version_remote = newer_jazzy_remotes.map { |n, _| n.version }.sort.last
info "Current jazzy version: #{jazzy_version_local}"
if ENV["CI"] != 'true'
die "Please update jazzy: \`gem update jazzy\`"
end
info "Updating jazzy to version #{jazzy_version_remote}..."
install_jazzy ">= #{jazzy_version_remote}"
return docs_root_directory
end
# Create temp podspec directory
# NOTE(mludowise|https://github.com/realm/jazzy/issues/1262):
# This won't be needed if jazzy ever allows for multiple development pods
make_dir_output = `#{script_dir}/make_temp_spec_repo.sh`
make_dir_status=$?.exitstatus
def make_temp_spec_repo
make_spec_repo_output = `#{$SCRIPT_DIR}/make_temp_spec_repo.sh`
make_spec_repo_status=$?.exitstatus
unless make_dir_status == 0
die temp_spec_dir
unless make_spec_repo_status == 0
die "Unable to create pod spec repo."
end
temp_spec_dir = make_spec_repo_output.lines.last.strip
info "Sucessfully created podspec repo at \`#{temp_spec_dir}\`"
return temp_spec_dir
end
temp_spec_dir = `#{script_dir}/make_temp_spec_repo.sh`.lines.last.strip
info "Sucessfully created podspec repo at \`#{temp_spec_dir}\`"
# Clean pod cache to always use latest local copy of pod dependencies
# NOTE(mludowise|https://github.com/realm/jazzy/issues/1262):
# This won't be needed if jazzy ever allows for multiple development pods
info "Cleaning pod cache..."
Dir.glob("#{script_dir}/../*.podspec").each do |file|
podspec = Pod::Specification.from_file(file)
cmd = Pod::Command::Cache::Clean.new(CLAide::ARGV.new([podspec.name, '--all']))
cmd.run
def clean_pod_cache
info "Cleaning pod cache..."
Dir.glob("#{$ROOT_DIR}/*.podspec").each do |file|
podspec = Pod::Specification.from_file(file)
cmd = Pod::Command::Cache::Clean.new(CLAide::ARGV.new([podspec.name, '--all']))
cmd.run
end
end
def docs_title(release_version)
return "Stripe iOS SDKs #{release_version}"
end
# Execute jazzy
modules = YAML.load_file("modules.yaml")['modules']
release_version = `cat "#{script_dir}/../VERSION"`.strip
jazzy_exit_code = 0
def build_module_docs(modules, release_version, docs_root_directory, temp_spec_dir)
jazzy_exit_code = 0
modules.each do |m|
docs_config = m['docs']
if docs_config.nil?
next
modules.each do |m|
# Note: If we don't check for empty string/nil, then jazzy will silently
# overwrite the entire git repo directory.
output = m['docs']['output'].to_s
if output.empty?
die "Missing required docs config \`output\`. Update modules.yaml."
end
# Prepend `docs_root_directory`
output = File.expand_path(output, docs_root_directory).to_s
info "Executing jazzy for #{m['podspec']}..."
`jazzy \
--config "#{$JAZZY_CONFIG_FILE}" \
--output "#{output}" \
--github-file-prefix "https://github.com/stripe/stripe-ios/tree/#{release_version}" \
--title "#{docs_title(release_version)}" \
--podspec "#{File.join_if_safe($ROOT_DIR, m['podspec'])}" \
--pod-sources "file://#{temp_spec_dir}"`
# Verify jazzy exit code
jazzy_exit_code=$?.exitstatus
if jazzy_exit_code != 0
die "Executing jazzy failed with status code: #{jazzy_exit_code}"
end
end
# Note: If we don't check for empty string/nil, then jazzy will silently
# overwrite the entire git repo directory.
output = docs_config['output'].to_s
if output.empty?
die "Missing required docs config \`output\`. Update modules.yaml."
end
# Prepend `docs_root_directory`
output = File.expand_path(output, docs_root_directory).to_s
info "Executing jazzy for #{m['podspec']}..."
`jazzy \
--config "#{script_dir}/../.jazzy.yaml" \
--output "#{output}" \
--github-file-prefix "https://github.com/stripe/stripe-ios/tree/#{release_version}" \
--podspec "#{script_dir}/../#{m['podspec']}" \
--pod-sources "file://#{temp_spec_dir}"`
# Verify jazzy exit code
jazzy_exit_code=$?.exitstatus
break if jazzy_exit_code != 0
end
# Cleanup temp podspec directory
FileUtils.rm_rf(temp_spec_dir)
# Creates html that lists all modules with docs enabled and links to their docs
# directory. The descriptions of the modules are from their podspec summaries.
def index_page_content(modules)
view = Mustache.new
view.template_file = File.join_if_safe($ROOT_DIR, $JAZZY_CONFIG['theme'], "templates", "index.mustache")
if jazzy_exit_code != 0
die "Executing jazzy failed with status code: #{jazzy_exit_code}"
view[:modules] = modules.map do |m|
# Get get module's docs output relative to `docs` folder
relative_path = Pathname.new(m['docs']['output']).relative_path_from(Pathname.new('docs'))
# Load podspec to get module name and summary
podspec = Pod::Specification.from_file(File.join_if_safe($ROOT_DIR, m['podspec']))
props={}
props[:name] = podspec.name
props[:summary] = podspec.summary
props[:directory] = relative_path.to_s
props
end
return view.render
end
# Builds the `/docs/index.html` page
def build_index_page(modules, release_version, docs_root_directory)
info "Building index page..."
# Reuse Jazzy theme so it's visually consistent with the reset of the docs
view = Mustache.new
view.template_name = "doc"
view.template_path = File.join_if_safe($ROOT_DIR, $JAZZY_CONFIG['theme'], "templates")
# Add properties expected by template
# Copied & modified from https://github.com/realm/jazzy
config = YAML.load_file(File.join_if_safe($ROOT_DIR, ".jazzy.yaml"))
view[:copyright] = (
date = DateTime.now.strftime('%Y-%m-%d')
year = date[0..3]
"&copy; #{year} <a class=\"link\" href=\"#{$JAZZY_CONFIG['author_url']}\"" \
"target=\"_blank\" rel=\"external\">#{$JAZZY_CONFIG['author']}</a>. " \
"All rights reserved. (Last updated: #{date})"
)
view[:jazzy_version] = Jazzy::VERSION
view[:objc_first] = false
view[:language_stub] = 'swift'
view[:disable_search] = false
view[:docs_title] = docs_title(release_version)
view[:module_version] = release_version
view[:github_url] = $JAZZY_CONFIG['github_url']
view[:name] = docs_title(release_version)
# Don't render search since it won't work for the index page
view[:disable_search] = true
# Custom template var for our theme to disable some html for the root index page
view[:is_root_index] = true
# Insert generated html
view[:overview] = index_page_content(modules)
# Write to docs/index.html
output_file = File.join_if_safe(docs_root_directory, "docs", "index.html")
File.open(output_file, 'w') { |file| file.write(view.render) }
end
# Jazzy compiles assets from the theme's assets directory and saves them to each
# module's docs directory. We're going to move one set of them to the docs/ root
# directory and symlink the rest. It's a bit hacky but it reduces duplicate
# css/js/images in our repo and moves it to an expected location that
# docs/index.html wants.
def fix_assets(modules, docs_root_directory)
docs_dir = File.expand_path('docs', docs_root_directory)
# Get list of assets used by theme (js, img, css, etc)
Dir.glob(File.join_if_safe($ROOT_DIR, $JAZZY_CONFIG['theme'], "assets", "*")).each do |asset_file|
asset_base_name = File.basename(asset_file)
# Delete old asset copies from /docs directory
FileUtils.rm_rf(File.join_if_safe(docs_dir, asset_base_name))
modules.each_with_index do |m, index|
module_docs_dir = File.join_if_safe(docs_root_directory, m['docs']['output'])
compiled_asset_path = File.join_if_safe(module_docs_dir, asset_base_name)
if index == 0
# Move the compiled asset from the module's docs folder into `/docs` so index.html can use it
FileUtils.mv(compiled_asset_path, File.join_if_safe(docs_dir, asset_base_name))
else
# Delete the compiled asset from the module's docs folder
FileUtils.rm_rf(compiled_asset_path)
end
# Symlink so each module's docs folder can use root `/docs` folder's assets
Dir.chdir(module_docs_dir){
File.symlink(File.join_if_safe(docs_dir, asset_base_name), compiled_asset_path)
}
end
end
end
# MARK: - main
temp_spec_dir = make_temp_spec_repo()
begin
clean_pod_cache()
docs_root_directory = get_docs_root_directory()
# Load modules from yaml and filter out any which don't have docs configured
modules = YAML.load_file(File.join_if_safe($ROOT_DIR, "modules.yaml"))['modules'].select { |m| !m['docs'].nil? }
release_version = `cat "#{$ROOT_DIR}/VERSION"`.strip
build_module_docs(modules, release_version, docs_root_directory, temp_spec_dir)
build_index_page(modules, release_version, docs_root_directory)
fix_assets(modules, docs_root_directory)
ensure
# Always cleanup temp podspec directory
info "Deleting podspec repo at `#{temp_spec_dir}`"
FileUtils.rm_rf(temp_spec_dir)
end