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.



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

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(',')
  @update_sources = argv.flag?('update-sources')
  @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)
  @validation_dir = argv.option('validation-dir', nil)
  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
42
43
# 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'],
    ['--update-sources', 'Make sure sources are up-to-date before a push'],
    ['--validation-dir', 'The directory to use for validation. If none is specified a temporary directory will be used.'],
  ].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.



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

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.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/cocoapods/command/repo/push.rb', line 113

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.



171
172
173
174
175
# File 'lib/cocoapods/command/repo/push.rb', line 171

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.



290
291
292
# File 'lib/cocoapods/command/repo/push.rb', line 290

def count
  podspec_files.count
end

#open_editorObject (private)

Open default editor to allow users to enter commit message



99
100
101
102
103
104
105
106
107
108
# File 'lib/cocoapods/command/repo/push.rb', line 99

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.



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/cocoapods/command/repo/push.rb', line 276

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.



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

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.



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

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



264
265
266
# File 'lib/cocoapods/command/repo/push.rb', line 264

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

#runObject



77
78
79
80
81
82
83
84
85
86
# File 'lib/cocoapods/command/repo/push.rb', line 77

def run
  open_editor if @commit_message && @message.nil?
  check_if_push_allowed
  update_sources if @update_sources
  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:



300
301
302
303
304
# File 'lib/cocoapods/command/repo/push.rb', line 300

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.



181
182
183
184
# File 'lib/cocoapods/command/repo/push.rb', line 181

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

#update_sourcesvoid (private)

This method returns an undefined value.

Update sources if present



190
191
192
193
194
195
196
197
198
# File 'lib/cocoapods/command/repo/push.rb', line 190

def update_sources
  return if @source_urls.nil?
  @source_urls.each do |source_url|
    source = config.sources_manager.source_with_name_or_url(source_url)
    dir = source.specs_dir
    UI.puts "Updating a source at #{dir} for #{source}"
    git!(%W(-C #{dir} pull))
  end
end

#validate!Object



67
68
69
70
71
72
73
74
75
# File 'lib/cocoapods/command/repo/push.rb', line 67

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.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/cocoapods/command/repo/push.rb', line 138

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
    validator.validation_dir = @validation_dir
    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