Class: Pod::Command::Repo::Push

Inherits:
Pod::Command::Repo show all
Extended by:
Executable
Defined in:
lib/cocoapods/command/repo/push.rb

Private helpers collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Executable

capture_command, capture_command!, executable, execute_command, popen3, reader, which, which!

Methods inherited from Pod::Command::Repo

#dir

Methods inherited from Pod::Command

#ensure_master_spec_repo_exists!, ensure_not_root_or_allowed!, git_version, #installer_for_config, report_error, run, #verify_lockfile_exists!, verify_minimum_git_version!, #verify_podfile_exists!, verify_xcode_license_approved!

Methods included from Pod::Config::Mixin

#config

Constructor Details

#initialize(argv) ⇒ Push

Returns a new instance of Push.


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/cocoapods/command/repo/push.rb', line 43

def initialize(argv)
  @allow_warnings = argv.flag?('allow-warnings')
  @local_only = argv.flag?('local-only')
  @repo = argv.shift_argument
  @source = source_for_repo
  @source_urls = argv.option('sources', config.sources_manager.all.map(&:url).append(Pod::TrunkSource::TRUNK_REPO_URL).uniq.join(',')).split(',')
  @podspec = argv.shift_argument
  @use_frameworks = !argv.flag?('use-libraries')
  @use_modular_headers = argv.flag?('use-modular-headers', false)
  @private = argv.flag?('private', true)
  @message = argv.option('commit-message')
  @commit_message = argv.flag?('commit-message', false)
  @use_json = argv.flag?('use-json')
  @swift_version = argv.option('swift-version', nil)
  @skip_import_validation = argv.flag?('skip-import-validation', false)
  @skip_tests = argv.flag?('skip-tests', false)
  @allow_overwrite = argv.flag?('overwrite', true)
  super
end

Class Method Details

.optionsObject


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/cocoapods/command/repo/push.rb', line 23

def self.options
  [
    ['--allow-warnings', 'Allows pushing even if there are warnings'],
    ['--use-libraries', 'Linter uses static libraries to install the spec'],
    ['--use-modular-headers', 'Lint uses modular headers during installation'],
    ["--sources=#{Pod::TrunkSource::TRUNK_REPO_URL}", 'The sources from which to pull dependent pods ' \
     '(defaults to all available repos). Multiple sources must be comma-delimited'],
    ['--local-only', 'Does not perform the step of pushing REPO to its remote'],
    ['--no-private', 'Lint includes checks that apply only to public repos'],
    ['--skip-import-validation', 'Lint skips validating that the pod can be imported'],
    ['--skip-tests', 'Lint skips building and running tests during validation'],
    ['--commit-message="Fix bug in pod"', 'Add custom commit message. Opens default editor if no commit ' \
      'message is specified'],
    ['--use-json', 'Convert the podspec to JSON before pushing it to the repo'],
    ['--swift-version=VERSION', 'The `SWIFT_VERSION` that should be used when linting the spec. ' \
     'This takes precedence over the Swift versions specified by the spec or a `.swift-version` file'],
    ['--no-overwrite', 'Disallow pushing that would overwrite an existing spec'],
  ].concat(super)
end

Instance Method Details

#add_specs_to_repovoid (private)

Note:

The pre commit hook of the repo is skipped as the podspecs have already been linted.

This method returns an undefined value.

Commits the podspecs to the source, which should be a git repo.


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
# File 'lib/cocoapods/command/repo/push.rb', line 187

def add_specs_to_repo
  UI.puts "\nAdding the #{'spec'.pluralize(count)} to the `#{@repo}' repo\n".yellow
  podspec_files.each do |spec_file|
    spec = Pod::Specification.from_file(spec_file)
    output_path = @source.pod_path(spec.name) + spec.version.to_s
    message = if @message && !@message.empty?
                @message
              elsif output_path.exist?
                "[Fix] #{spec}"
              elsif output_path.dirname.directory?
                "[Update] #{spec}"
              else
                "[Add] #{spec}"
              end

    if output_path.exist? && !@allow_overwrite
      raise Informative, "#{spec} already exists and overwriting has been disabled."
    end

    FileUtils.mkdir_p(output_path)

    if @use_json
      json_file_name = "#{spec.name}.podspec.json"
      json_file = File.join(output_path, json_file_name)
      File.open(json_file, 'w') { |file| file.write(spec.to_pretty_json) }
    else
      FileUtils.cp(spec_file, output_path)
    end

    # only commit if modified
    if repo_git('status', '--porcelain').include?(spec.name)
      UI.puts " - #{message}"
      repo_git('add', spec.name)
      repo_git('commit', '--no-verify', '-m', message)
    else
      UI.puts " - [No change] #{spec}"
    end
  end
end

#check_if_push_allowedObject (private)

Temporary check to ensure that users do not push accidentally private specs to the master repo.


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/cocoapods/command/repo/push.rb', line 108

def check_if_push_allowed
  if @source.is_a?(CDNSource)
    raise Informative, 'Cannot push to a CDN source, as it is read-only.'
  end

  remotes, = Executable.capture_command('git', %w(remote --verbose), :capture => :merge, :chdir => repo_dir)
  master_repo_urls = [
    '[email protected]:CocoaPods/Specs.git',
    'https://github.com/CocoaPods/Specs.git',
  ]
  is_master_repo = master_repo_urls.any? do |url|
    remotes.include?(url)
  end

  if is_master_repo
    raise Informative, 'To push to the CocoaPods master repo use ' \
      "the `pod trunk push` command.\n\nIf you are using a fork of " \
      'the master repo for private purposes we recommend to migrate ' \
      'to a clean private repo. To disable this check remove the ' \
      'remote pointing to the CocoaPods master repo.'
  end
end

#check_repo_statusvoid (private)

TODO:

Add specs for staged and unstaged files.

TODO:

Gracefully handle the case where source is not under git source control.

This method returns an undefined value.

Checks that the repo is clean.

Raises:

  • If the repo is not clean.


165
166
167
168
169
# File 'lib/cocoapods/command/repo/push.rb', line 165

def check_repo_status
  porcelain_status, = Executable.capture_command('git', %w(status --porcelain), :capture => :merge, :chdir => repo_dir)
  clean = porcelain_status == ''
  raise Informative, "The repo `#{@repo}` at #{UI.path repo_dir} is not clean" unless clean
end

#countInteger (private)

Returns The number of the podspec files to push.

Returns:

  • (Integer)

    The number of the podspec files to push.


270
271
272
# File 'lib/cocoapods/command/repo/push.rb', line 270

def count
  podspec_files.count
end

#open_editorObject (private)

Open default editor to allow users to enter commit message


94
95
96
97
98
99
100
101
102
103
# File 'lib/cocoapods/command/repo/push.rb', line 94

def open_editor
  return if ENV['EDITOR'].nil?

  file = Tempfile.new('cocoapods')
  File.chmod(0777, file.path)
  file.close

  system("#{ENV['EDITOR']} #{file.path}")
  @message = File.read file.path
end

#podspec_filesArray<Pathname> (private)

Returns The path of the specifications to push.

Returns:

  • (Array<Pathname>)

    The path of the specifications to push.


256
257
258
259
260
261
262
263
264
265
266
# File 'lib/cocoapods/command/repo/push.rb', line 256

def podspec_files
  if @podspec
    path = Pathname(@podspec)
    raise Informative, "Couldn't find #{@podspec}" unless path.exist?
    [path]
  else
    files = Pathname.glob('*.podspec{,.json}')
    raise Informative, "Couldn't find any podspec files in current directory" if files.empty?
    files
  end
end

#push_repovoid (private)

This method returns an undefined value.

Pushes the git repo against the remote.


231
232
233
234
# File 'lib/cocoapods/command/repo/push.rb', line 231

def push_repo
  UI.puts "\nPushing the `#{@repo}' repo\n".yellow
  repo_git('push', 'origin', 'HEAD')
end

#repo_dirPathname (private)

Returns The directory of the repository.

Returns:

  • (Pathname)

    The directory of the repository.


250
251
252
# File 'lib/cocoapods/command/repo/push.rb', line 250

def repo_dir
  @source.specs_dir
end

#repo_git(*args) ⇒ Object (private)

Returns result of calling the git! with args in repo_dir.

Returns:

  • result of calling the git! with args in repo_dir


244
245
246
# File 'lib/cocoapods/command/repo/push.rb', line 244

def repo_git(*args)
  git!(['-C', repo_dir] + args)
end

#runObject


73
74
75
76
77
78
79
80
81
# File 'lib/cocoapods/command/repo/push.rb', line 73

def run
  open_editor if @commit_message && @message.nil?
  check_if_push_allowed
  validate_podspec_files
  check_repo_status
  update_repo
  add_specs_to_repo
  push_repo unless @local_only
end

#source_for_repoSource (private)

Note:

If URL is invalid or repo doesn't exist, validate! will throw the error

Returns source for @repo

Returns:


280
281
282
283
284
# File 'lib/cocoapods/command/repo/push.rb', line 280

def source_for_repo
  config.sources_manager.source_with_name_or_url(@repo) unless @repo.nil?
rescue
  nil
end

#update_repovoid (private)

This method returns an undefined value.

Updates the git repo against the remote.


175
176
177
178
# File 'lib/cocoapods/command/repo/push.rb', line 175

def update_repo
  UI.puts "Updating the `#{@repo}' repo\n".yellow
  git!(%W(-C #{repo_dir} pull))
end

#validate!Object


63
64
65
66
67
68
69
70
71
# File 'lib/cocoapods/command/repo/push.rb', line 63

def validate!
  super
  help! 'A spec-repo name or url is required.' unless @repo
  unless @source && @source.repo.directory?
    raise Informative,
          "Unable to find the `#{@repo}` repo. " \
          'If it has not yet been cloned, add it via `pod repo add`.'
  end
end

#validate_podspec_filesObject (private)

Performs a full lint against the podspecs.


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/cocoapods/command/repo/push.rb', line 133

def validate_podspec_files
  UI.puts "\nValidating #{'spec'.pluralize(count)}".yellow
  podspec_files.each do |podspec|
    validator = Validator.new(podspec, @source_urls)
    validator.allow_warnings = @allow_warnings
    validator.use_frameworks = @use_frameworks
    validator.use_modular_headers = @use_modular_headers
    validator.ignore_public_only_results = @private
    validator.swift_version = @swift_version
    validator.skip_import_validation = @skip_import_validation
    validator.skip_tests = @skip_tests
    begin
      validator.validate
    rescue => e
      raise Informative, "The `#{podspec}` specification does not validate." \
                         "\n\n#{e.message}"
    end
    raise Informative, "The `#{podspec}` specification does not validate." unless validator.validated?
  end
end