Commit 2a21c651 authored by Fabio Pelosin's avatar Fabio Pelosin

Merge pull request #208 from CocoaPods/spec-create

Spec create
parents 4909373d 6c304ff7
...@@ -3,6 +3,8 @@ source "http://rubygems.org" ...@@ -3,6 +3,8 @@ source "http://rubygems.org"
gem "open4" gem "open4"
gem "colored" gem "colored"
gem "escape" gem "escape"
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"
...@@ -14,4 +16,5 @@ group :development do ...@@ -14,4 +16,5 @@ group :development do
gem "rb-fsevent" gem "rb-fsevent"
gem "vcr" gem "vcr"
gem "webmock" gem "webmock"
gem "awesome_print"
end end
...@@ -8,10 +8,19 @@ GEM ...@@ -8,10 +8,19 @@ GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
addressable (2.2.7) addressable (2.2.7)
awesome_print (1.0.2)
bacon (1.1.0) bacon (1.1.0)
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)
kicker (2.5.0) kicker (2.5.0)
rb-fsevent rb-fsevent
metaclass (0.0.1) metaclass (0.0.1)
...@@ -19,7 +28,16 @@ GEM ...@@ -19,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.1) rb-fsevent (0.9.1)
vcr (2.0.1) vcr (2.0.1)
...@@ -31,11 +49,14 @@ PLATFORMS ...@@ -31,11 +49,14 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
awesome_print
bacon bacon
colored colored
escape escape
json
kicker kicker
mocha-on-bacon mocha-on-bacon
octokit
open4 open4
rake rake
rb-fsevent rb-fsevent
......
# encoding: utf-8
require 'net/https'
require 'uri'
require 'octokit'
require 'json'
module Pod module Pod
class Command class Command
class Spec < Command class Spec < Command
def self.banner def self.banner
%{Managing PodSpec files: %{Managing PodSpec files:
$ pod spec create NAME $ pod spec create [NAME]
$ 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.
$ pod spec lint NAME.podspec $ pod spec lint [NAME.podspec]
Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults
to `*.podspec' in the current working dir.} to `*.podspec' in the current working dir.}
...@@ -20,7 +29,7 @@ module Pod ...@@ -20,7 +29,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
...@@ -28,76 +37,123 @@ module Pod ...@@ -28,76 +37,123 @@ module Pod
end end
def create def create
author = `git config --get user.name`.strip if repo_id = @name_or_url[/github.com\/([^\/]*\/[^\/]*).*/, 1]
email = `git config --get user.email`.strip data = github_data_for_template(repo_id)
spec = <<-SPEC.gsub(/^ /, '') puts semantic_versioning_notice(repo_id, data[:name]) if data[:tag] == 'HEAD'
# else
# Be sure to run `pod spec lint #{@name}.podspec' to ensure this is a data = default_data_for_template(@name_or_url)
# valid spec. end
# spec = spec_template(data)
# Remove all comments before submitting the spec. (Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
# puts "\nSpecification created at #{data[:name]}.podspec\n".green
Pod::Spec.new do |s| end
s.name = '#{@name}'
s.version = '1.0.0' def lint
s.summary = 'A short description of #{@name}.' name = @name_or_url
s.homepage = 'http://EXAMPLE/#{@name}' file = name ? Pathname.new(name) : Pathname.pwd.glob('*.podspec').first
spec = Specification.from_file(file)
puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate!
warnings = []
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[: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 << "The description should end with a dot" if spec.description && spec.description !~ /.*\./
warnings << "The summary should end with a dot" if spec.summary !~ /.*\./
unless warnings.empty?
puts "\n[!] The #{spec.name} specification raised the following warnings".yellow
warnings.each { |warn| puts ' - '+ warn }
end
puts
end
def suggest_tag_and_version(tags)
versions_tags = {}
tags.each do |tag|
clean_tag = tag.gsub(/^v(er)? ?/,'')
versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag)
end
version = versions_tags.keys.sort.last || '0.0.1'
tag = version == '0.0.1' ? 'HEAD' : versions_tags[version]
[tag, version.to_s]
end
def github_data_for_template(repo_id)
repo = Octokit.repo(repo_id)
user = Octokit.user(repo['owner']['login'])
tags = Octokit.tags(repo_id).map {|tag| tag.name}
tag, version = suggest_tag_and_version(tags)
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
def default_data_for_template(name)
data = {}
data[:name] = name
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
def spec_template(data)
return <<-SPEC
#
# 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 # 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 # the email addresses of the authors by using the SCM log. E.g. $ git log
# #
s.author = { '#{author}' => '#{email}', 'other author' => 'and email address' } 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. # If absolutely no email addresses are available, then you can use this form instead.
# #
# s.author = '#{author}', 'other author' # s.author = '#{data[:author_name]}', 'other author'
# Specify the location from where the source should be retreived. # Specify the location from where the source should be retreived.
# #
s.source = { :git => 'http://EXAMPLE/#{@name}.git', :tag => '1.0.0' } s.source = { :git => "#{data[:source_url]}", :tag => "#{data[:tag]}" }
# s.source = { :svn => 'http://EXAMPLE/#{@name}/tags/1.0.0' } # s.source = { :svn => 'http://EXAMPLE/#{data[:name]}/tags/1.0.0' }
# s.source = { :hg => 'http://EXAMPLE/#{@name}', :revision => '1.0.0' } # s.source = { :hg => 'http://EXAMPLE/#{data[: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. # Specify the license details. Only if no dedicated file is available include
# :type The type of the license. # the full text 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 = { s.license = {
:type => 'MIT', :type => 'MIT',
:file => 'LICENSE', :file => 'LICENSE',
# :range => 1..15,
# :text => 'Permission is hereby granted ...' # :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 # 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 # added to the Pods project. If the pattern is a directory then the
# path will automatically have '*.{h,m,mm,c,cpp}' appended. # path will automatically have '*.{h,m,mm,c,cpp}' appended.
...@@ -108,6 +164,16 @@ module Pod ...@@ -108,6 +164,16 @@ module Pod
# #
s.source_files = 'Classes', 'Classes/**/*.{h,m}' 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 # A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. # target bundle with a build phase script.
# #
...@@ -143,38 +209,49 @@ module Pod ...@@ -143,38 +209,49 @@ module Pod
# #
# s.requires_arc = true # 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 # If you need to specify any other build settings, add them to the
# xcconfig hash. # xcconfig hash.
# #
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } # s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
# Finally, specify any Pods that this Pod depends on. end
#
# s.dependency 'JSONKit', '~> 1.4'
end
SPEC SPEC
(Pathname.pwd + "#{@name}.podspec").open('w') { |f| f << spec }
end end
def lint def semantic_versioning_notice(repo_id, repo)
file = @name ? Pathname.new(@name) : Pathname.pwd.glob('*.podspec').first return <<-EOS
spec = Specification.from_file(file)
puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate! #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
warnings = [] I’ve recently added [#{repo}](https://github.com/CocoaPods/Specs/tree/master/#{repo}) to the [CocoaPods](https://github.com/CocoaPods/CocoaPods) package manager repo.
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[: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 start with https://github.com" if spec.source[:git] =~ /git:\/\/github\.com/
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.
unless warnings.empty? 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.
puts "\n[!] The #{spec.name} specification raised the following warnings".yellow
warnings.each { |warn| puts ' - '+ warn } [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).
end
puts In case you didn’t know this yet; you can tag the current HEAD as, for instance, version 1.0.0, like so:
```
$ git tag -a 1.0.0 -m "Tag release 1.0.0"
$ git push --tags
```
#{'――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
#{'[!] This repo does not appear to have semantic version tags.'.yellow}
After commiting the specification, consider opening a ticket with the template displayed above:
- link: https://github.com/#{repo_id}/issues/new
- title: Please add semantic version tags
EOS
end end
end end
end end
......
...@@ -54,11 +54,11 @@ describe "Pod::Command" do ...@@ -54,11 +54,11 @@ describe "Pod::Command" do
spec = Pod::Specification.from_file(path) spec = Pod::Specification.from_file(path)
spec.name.should == 'Bananas' spec.name.should == 'Bananas'
spec.license.should == { :type => "MIT", :file => "LICENSE" } spec.license.should == { :type => "MIT", :file => "LICENSE" }
spec.version.should == Pod::Version.new('1.0.0') spec.version.should == Pod::Version.new('0.0.1')
spec.summary.should == 'A short description of Bananas.' spec.summary.should == 'A short description of Bananas.'
spec.homepage.should == 'http://EXAMPLE/Bananas' spec.homepage.should == 'http://EXAMPLE/Bananas'
spec.authors.should == { `git config --get user.name`.strip => `git config --get user.email`.strip, "other author" => "and email address" } spec.authors.should == { `git config --get user.name`.strip => `git config --get user.email`.strip}
spec.source.should == { :git => 'http://EXAMPLE/Bananas.git', :tag => '1.0.0' } spec.source.should == { :git => 'http://EXAMPLE/Bananas.git', :tag => '0.0.1' }
spec.description.should == 'An optional longer description of Bananas.' spec.description.should == 'An optional longer description of Bananas.'
spec.source_files[:ios].should == ['Classes', 'Classes/**/*.{h,m}'] spec.source_files[:ios].should == ['Classes', 'Classes/**/*.{h,m}']
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