Commit 20613c18 authored by Fabio Pelosin's avatar Fabio Pelosin

GitHub support for pod spec create

- added semanting versioning notice
- warning if a spec description or summary does not ends with a dot
- requires octokit gem
- added awesome-print development gem
- removed appledoc options from spec template
- removed range attribute for license from the spec template
parent 0ea0f7ea
...@@ -4,6 +4,7 @@ gem "open4" ...@@ -4,6 +4,7 @@ gem "open4"
gem "colored" gem "colored"
gem "escape" gem "escape"
gem "json" gem "json"
gem "octokit"
group :development do group :development do
gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git" gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git"
......
...@@ -13,6 +13,13 @@ GEM ...@@ -13,6 +13,13 @@ GEM
colored (1.2) colored (1.2)
crack (0.3.1) crack (0.3.1)
escape (0.0.4) escape (0.0.4)
faraday (0.7.6)
addressable (~> 2.2)
multipart-post (~> 1.1)
rack (~> 1.1)
faraday_middleware (0.8.6)
faraday (>= 0.7.4, < 0.9)
hashie (1.2.0)
json (1.6.6) json (1.6.6)
kicker (2.5.0) kicker (2.5.0)
rb-fsevent rb-fsevent
...@@ -21,7 +28,16 @@ GEM ...@@ -21,7 +28,16 @@ GEM
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
mocha-on-bacon (0.2.0) mocha-on-bacon (0.2.0)
mocha (>= 0.9.8) mocha (>= 0.9.8)
multi_json (1.1.0)
multipart-post (1.1.5)
octokit (1.0.1)
addressable (~> 2.2)
faraday (~> 0.7)
faraday_middleware (~> 0.8)
hashie (~> 1.2)
multi_json (~> 1.0)
open4 (1.3.0) open4 (1.3.0)
rack (1.4.1)
rake (0.9.2.2) rake (0.9.2.2)
rb-fsevent (0.9.0) rb-fsevent (0.9.0)
vcr (2.0.0) vcr (2.0.0)
...@@ -40,6 +56,7 @@ DEPENDENCIES ...@@ -40,6 +56,7 @@ DEPENDENCIES
json json
kicker kicker
mocha-on-bacon mocha-on-bacon
octokit
open4 open4
rake rake
rb-fsevent rb-fsevent
......
require 'net/https' require 'net/https'
require 'uri' require 'uri'
require 'json' require 'octokit'
require 'awesome_print' require 'awesome_print'
require 'json'
module Pod module Pod
class Command class Command
...@@ -10,7 +11,7 @@ module Pod ...@@ -10,7 +11,7 @@ module Pod
%{Managing PodSpec files: %{Managing PodSpec files:
$ pod spec create [NAME] $ pod spec create [NAME]
$ pod spec create [https://github.com/user/repo] $ pod spec create [https://github.com/USER/REPO]
Creates a PodSpec, in the current working dir, called `NAME.podspec'. Creates a PodSpec, in the current working dir, called `NAME.podspec'.
If a GitHub url is passed the spec is prepopulated. If a GitHub url is passed the spec is prepopulated.
...@@ -27,7 +28,7 @@ module Pod ...@@ -27,7 +28,7 @@ module Pod
(args[0] == 'lint' && args.size <= 2) (args[0] == 'lint' && args.size <= 2)
super super
end end
@action, @name = args.first(2) @action, @name_or_url = args.first(2)
end end
def run def run
...@@ -35,23 +36,31 @@ module Pod ...@@ -35,23 +36,31 @@ module Pod
end end
def create def create
spec = is_git_hub ? github_spec_doc : spec_doc if repo_id = @name_or_url[/github.com\/(.*\/.*)/, 1]
(Pathname.pwd + "#{@name}.podspec").open('w') { |f| f << spec } data = github_data_for_template(repo_id)
puts semantic_versioning_notice(repo_id, data[:name]) if data[:tag] == 'HEAD'
else
data = default_data_for_template(@name_or_url)
end
spec = spec_template(data)
(Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
puts "\nSpecification created at #{data[:name]}.podspec\n".green
end end
def lint def lint
file = @name ? Pathname.new(@name) : Pathname.pwd.glob('*.podspec').first name = @name_or_url
file = name ? Pathname.new(name) : Pathname.pwd.glob('*.podspec').first
spec = Specification.from_file(file) spec = Specification.from_file(file)
puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate! puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate!
warnings = [] warnings = []
warnings << 'The name of the specificaiton should match the name of the podspec file' if spec.name + '.podspec' != @name warnings << 'The name of the specificaiton should match the name of the podspec file' if spec.name + '.podspec' != name
warnings << 'Missing license[:type]' unless spec.license && spec.license[:type] warnings << 'Missing license[:type]' unless spec.license && spec.license[:type]
warnings << 'Missing license[:file] or [:text]' unless spec.license && (spec.license[:file] || spec.license[:text]) warnings << 'Missing license[:file] or [:text]' unless spec.license && (spec.license[:file] || spec.license[:text])
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/ warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/ warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "The description should end with a dot" if spec.description && spec.description !~ /.*\./ warnings << "The description should end with a dot" if spec.description && spec.description !~ /.*\./
warnings << "The summary should end with a dot" if spec.summary !~ /.*\./ warnings << "The summary should end with a dot" if spec.summary !~ /.*\./
unless warnings.empty? unless warnings.empty?
puts "\n[!] The #{spec.name} specification raised the following warnings".yellow puts "\n[!] The #{spec.name} specification raised the following warnings".yellow
warnings.each { |warn| puts ' - '+ warn } warnings.each { |warn| puts ' - '+ warn }
...@@ -59,294 +68,189 @@ module Pod ...@@ -59,294 +68,189 @@ module Pod
puts puts
end end
private def suggest_tag_and_version(tags)
versions_tags = {}
def is_git_hub tags.each do |tag|
@name =~ /https:\/\/github.com\/.*\/.*/ clean_tag = tag.gsub(/^v(er)? ?/,'')
end versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag)
end
def find_github_info version = versions_tags.keys.sort.last || 'HEAD'
full, user, repo = *(@name.match /https:\/\/github.com\/(.*)\/(.*)/).to_a tag = version == 'HEAD' ? '0.0.1' : versions_tags[version]
user_data = fetch_request("users/#{user}") [tag, version.to_s]
repo_data = fetch_request("repos/#{user}/#{repo}")
tags_data = fetch_request("repos/#{user}/#{repo}/tags")
[user_data, repo_data, tags_data]
end end
def fetch_request(request_url) def github_data_for_template(repo_id)
uri = URI.parse("https://api.github.com/#{request_url}") repo = Octokit.repo(repo_id)
http = Net::HTTP.new(uri.host, uri.port) user = Octokit.user(repo['owner']['login'])
http.use_ssl = true tags = Octokit.tags(repo_id).map {|tag| tag.name}
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri) tag, version = suggest_tag_and_version(tags)
response = http.request(request)
JSON.parse(response.body) if response.is_a?(Net::HTTPSuccess) data = {}
data[:name] = repo['name']
data[:version] = version
data[:summary] = repo['description'].gsub(/["]/, '\"')
data[:homepage] = repo['homepage'] != "" ? repo['homepage'] : repo['html_url']
data[:author_name] = user['name'] || user['login']
data[:author_email] = user['email'] || 'email@address.com'
data[:source_url] = repo['clone_url']
data[:tag] = tag
data
end end
def find_suggested_tag(tags_data) def default_data_for_template(name)
tags = tags_data.map {|hash| hash["name"] } data = {}
versions = tags.reject {|t| t !~ /[0-9]+\.[0-9]+\.?[0-9]*/ } data[:name] = name
versions.sort.last || '0.0.1' data[:version] = '0.0.1'
data[:summary] = 'A short description of #{name}.'
data[:homepage] = "http://EXAMPLE/#{name}"
data[:author_name] = `git config --get user.name`.strip
data[:author_email] = `git config --get user.email`.strip
data[:source_url] = "http://EXAMPLE/#{name}.git"
data[:tag] = '0.0.1'
data
end end
def version_from_tag(tag) def spec_template(data)
tag.match /[0-9]+\.[0-9]+\.?[0-9]*/ return <<-SPEC
end #
# Be sure to run `pod spec lint #{data[:name]}.podspec' to ensure this is a
# valid spec.
#
# Remove all comments before submitting the spec.
#
Pod::Spec.new do |s|
# ――― REQUIRED VALUES ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
s.name = "#{data[:name]}"
s.version = "#{data[:version]}"
s.summary = "#{data[:summary]}"
s.homepage = "#{data[:homepage]}"
# Specify the authors of the library, with email addresses. You can often find
# the email addresses of the authors by using the SCM log. E.g. $ git log
#
s.author = { "#{data[:author_name]}" => "#{data[:author_email]}" }
# s.authors = { "#{data[:author_name]}" => "#{data[:author_email]}", "other author" => "and email address" }
#
# If absolutely no email addresses are available, then you can use this form instead.
#
# s.author = '#{data[:author_name]}', 'other author'
# Specify the location from where the source should be retreived.
#
s.source = { :git => "#{data[:source_url]}", :tag => "#{data[:tag]}" }
# s.source = { :svn => 'http://EXAMPLE/#{data[:name]}/tags/1.0.0' }
# s.source = { :hg => 'http://EXAMPLE/#{data[:name]}', :revision => '1.0.0' }
# Specify the license details. Only if no dedicated file is available include
# the full text of the license.
#
s.license = {
:type => 'MIT',
:file => 'LICENSE',
# :text => 'Permission is hereby granted ...'
}
# A list of file patterns which select the source files that should be
# added to the Pods project. If the pattern is a directory then the
# path will automatically have '*.{h,m,mm,c,cpp}' appended.
#
# Alternatively, you can use the FileList class for even more control
# over the selected files.
# (See http://rake.rubyforge.org/classes/Rake/FileList.html.)
#
s.source_files = 'Classes', 'Classes/**/*.{h,m}'
# ――― OPTIONAL VALUES ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
s.description = 'An optional longer description of #{data[:name]}.'
# If this Pod runs only on iOS or OS X, then specify that with one of
# these, or none if it runs on both platforms.
#
# s.platform = :ios
# s.platform = :osx
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.resource = "icon.png"
# s.resources = "Resources/*.png"
# A list of paths to remove after installing the Pod without the
# `--no-clean' option. These can be examples, docs, and any other type
# of files that are not needed to build the Pod.
#
# *NOTE*: Never remove license and README files.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.clean_path = "examples"
# s.clean_paths = "examples", "doc"
# Specify a list of frameworks that the application needs to link
# against for this Pod to work.
#
# s.framework = 'SomeFramework'
# s.frameworks = 'SomeFramework', 'AnotherFramework'
# Specify a list of libraries that the application needs to link
# against for this Pod to work.
#
# s.library = 'iconv'
# s.libraries = 'iconv', 'xml2'
# If this Pod uses ARC, specify it like so.
#
# s.requires_arc = true
# Finally, specify any Pods that this Pod depends on.
#
# s.dependency 'JSONKit', '~> 1.4'
# ――― EXTRA VALUES ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
# If you need to specify any other build settings, add them to the
# xcconfig hash.
#
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
def github_spec_doc end
user_data, repo_data, tags_data = find_github_info
tag = find_suggested_tag(tags_data)
version = version_from_tag(tag)
@name = repo_data["name"]
spec = <<-SPEC.gsub(/^ /, '')
#
# Be sure to run `pod spec lint #{@name}.podspec' to ensure this is a
# valid spec.
#
# Remove all comments before submitting the spec.
#
Pod::Spec.new do |s|
# ――― REQUIRED ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
s.name = '#{@name}'
s.version = '#{version}'
s.summary = '#{repo_data["description"].gsub(/[']/, '\\\\\'')}'
s.homepage = '#{repo_data["homepage"] != "" ? repo_data["homepage"] : repo_data["html_url"]}'
s.source = { :git => '#{repo_data["clone_url"]}', :tag => '#{tag}' }
s.author = { '#{user_data["name"]}' => '#{user_data["email"]}' }
# s.author = { '#{user_data["name"]}' => '#{user_data["email"]}', 'other author' => 'and email address' }
# Specify the license of the pod.
# :type The type of the license.
# :file The file containing the license of the pod.
# :range If a dedicated license file is not available specify a file
# that contains the license and the range of the lines
# containing the license.
# :text If the license is not available in any of the files it should be
# included here.
s.license = {
:type => 'MIT',
:file => 'LICENSE',
# :range => 1..15,
# :text => 'Permission is hereby granted ...'
}
# A list of file patterns which select the source files that should be
# added to the Pods project. If the pattern is a directory then the
# path will automatically have '*.{h,m,mm,c,cpp}' appended.
#
# Alternatively, you can use the FileList class for even more control
# over the selected files.
# (See http://rake.rubyforge.org/classes/Rake/FileList.html.)
#
s.source_files = 'Classes', 'Classes/**/*.{h,m}'
# ――― OPTIONAL ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
s.description = 'An optional longer description of #{@name}.'
# If this Pod runs only on iOS or OS X, then specify that with one of
# these, or none if it runs on both platforms.
#
# s.platform = :ios
# s.platform = :osx
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.resource = "icon.png"
# s.resources = "Resources/*.png"
# A list of paths to remove after installing the Pod without the
# `--no-clean' option. These can be examples, docs, and any other type
# of files that are not needed to build the Pod.
#
# *NOTE*: Never remove license and README files.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.clean_path = "examples"
# s.clean_paths = "examples", "doc"
# Specify a list of frameworks that the application needs to link
# against for this Pod to work.
#
# s.framework = 'SomeFramework'
# s.frameworks = 'SomeFramework', 'AnotherFramework'
# Specify a list of libraries that the application needs to link
# against for this Pod to work.
#
# s.library = 'iconv'
# s.libraries = 'iconv', 'xml2'
# If this Pod uses ARC, specify it like so.
#
# s.requires_arc = true
# Finally, specify any Pods that this Pod depends on.
#
# s.dependency 'JSONKit', '~> 1.4'
#
# ――― EXTRA ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
# If you need to specify any other build settings, add them to the
# xcconfig hash.
#
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
# If available specify the documentation homepage.
# :html The online link for the documentation.
# :appledoc Ammend the default appledoc options used
# by cocoapods if needed.
#
# s.documentation = {
# :html => 'http://EXAMPLE/#{@name}/documentation',
# :appledoc => [
# '--project-name', '#{@name}',
# '--project-company', 'Company Name',
# '--docset-copyright', copyright,
# '--ignore', 'Common',
# '--index-desc', 'readme.markdown',
# '--no-keep-undocumented-objects',
# '--no-keep-undocumented-members',
# ]
# }
end
SPEC SPEC
end end
def spec_doc def semantic_versioning_notice(repo_id, repo)
name = @name return <<-EOS
author = `git config --get user.name`.strip
email = `git config --get user.email`.strip #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
spec = <<-SPEC.gsub(/^ /, '')
# I’ve recently added [#{repo}](https://github.com/CocoaPods/Specs/tree/master/#{repo}) to the [CocoaPods](https://github.com/CocoaPods/CocoaPods) package manager repo.
# Be sure to run `pod spec lint #{name}.podspec' to ensure this is a
# valid spec. CocoaPods is a tool for managing dependencies for OSX and iOS Xcode projects and provides a central repository for iOS/OSX libraries. This makes adding libraries to a project and updating them extremely easy and it will help users to resolve dependencies of the libraries they use.
#
# Remove all comments before submitting the spec. However, #{repo} doesn't have any version tags. I’ve added the current HEAD as version 0.0.1, but a version tag will make dependency resolution much easier.
#
Pod::Spec.new do |s| [Semantic version](http://semver.org) tags (instead of plain commit hashes/revisions) allow for [resolution of cross-dependencies](https://github.com/CocoaPods/Specs/wiki/Cross-dependencies-resolution-example).
s.name = '#{name}'
s.version = '1.0.0' In case you didn’t know this yet; you can tag the current HEAD as, for instance, version 1.0.0, like so:
s.summary = 'A short description of #{name}.'
s.homepage = 'http://EXAMPLE/#{name}' ```
$ git tag -a 1.0.0 -m "Tag release 1.0.0"
# Specify the authors of the library, with email addresses. You can often find $ git push --tags
# the email addresses of the authors by using the SCM log. E.g. $ git log ```
#
s.author = { '#{author}' => '#{email}', 'other author' => 'and email address' } #{'――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
# If absolutely no email addresses are available, then you can use this form instead.
# #{'[!] This repo does not appear to have semantic version tags.'.yellow}
# s.author = '#{author}', 'other author'
After commiting the specification, consider opening a ticket with the template displayed above:
# Specify the location from where the source should be retreived. - link: https://github.com/#{repo_id}/issues/new
# - title: Please add semantic version tags
s.source = { :git => 'http://EXAMPLE/#{name}.git', :tag => '1.0.0' } EOS
# s.source = { :svn => 'http://EXAMPLE/#{name}/tags/1.0.0' }
# s.source = { :hg => 'http://EXAMPLE/#{name}', :revision => '1.0.0' }
s.description = 'An optional longer description of #{name}.'
# If available specify the documentation homepage.
# :html The online link for the documentation.
# :appledoc Ammend the default appledoc options used
# by cocoapods if needed.
#
s.documentation = {
# :html => 'http://EXAMPLE/#{name}/documentation',
# :appledoc => [
# '--project-name', '#{name}',
# '--project-company', 'Company Name',
# '--docset-copyright', copyright,
# '--ignore', 'Common',
# '--index-desc', 'readme.markdown',
# '--no-keep-undocumented-objects',
# '--no-keep-undocumented-members',
# ]
}
# Specify the license of the pod.
# :type The type of the license.
# :file The file containing the license of the pod.
# :range If a dedicated license file is not available specify a file
# that contains the license and the range of the lines
# containing the license.
# :text If the license is not available in any of the files it should be
# included here.
s.license = {
:type => 'MIT',
:file => 'LICENSE',
# :range => 1..15,
# :text => 'Permission is hereby granted ...'
}
# If this Pod runs only on iOS or OS X, then specify that with one of
# these, or none if it runs on both platforms.
#
# s.platform = :ios
# s.platform = :osx
# A list of file patterns which select the source files that should be
# added to the Pods project. If the pattern is a directory then the
# path will automatically have '*.{h,m,mm,c,cpp}' appended.
#
# Alternatively, you can use the FileList class for even more control
# over the selected files.
# (See http://rake.rubyforge.org/classes/Rake/FileList.html.)
#
s.source_files = 'Classes', 'Classes/**/*.{h,m}'
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.resource = "icon.png"
# s.resources = "Resources/*.png"
# A list of paths to remove after installing the Pod without the
# `--no-clean' option. These can be examples, docs, and any other type
# of files that are not needed to build the Pod.
#
# *NOTE*: Never remove license and README files.
#
# Also allows the use of the FileList class like `source_files does.
#
# s.clean_path = "examples"
# s.clean_paths = "examples", "doc"
# Specify a list of frameworks that the application needs to link
# against for this Pod to work.
#
# s.framework = 'SomeFramework'
# s.frameworks = 'SomeFramework', 'AnotherFramework'
# Specify a list of libraries that the application needs to link
# against for this Pod to work.
#
# s.library = 'iconv'
# s.libraries = 'iconv', 'xml2'
# If this Pod uses ARC, specify it like so.
#
# s.requires_arc = true
# If you need to specify any other build settings, add them to the
# xcconfig hash.
#
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
# Finally, specify any Pods that this Pod depends on.
#
# s.dependency 'JSONKit', '~> 1.4'
SPEC
end end
end end
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment