* Initial commit

* Fix CI trigger

* Rename workflow job

* Replace deprecated bundler flag

* Update README.md
This commit is contained in:
Tommaso Madonia 2020-09-14 16:59:53 +01:00 коммит произвёл GitHub
Родитель 584e844ba3
Коммит e558feec45
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 598 добавлений и 1 удалений

25
.github/workflows/tests.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
name: Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
unit_tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- name: Install dependencies
run: |
bundle config deployment true
bundle install
- name: Run tests
run: bundle exec rake tests

8
Gemfile Normal file
Просмотреть файл

@ -0,0 +1,8 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in cocoapods-azure-universal-packages.gemspec
gemspec
group :development do
gem 'cocoapods'
end

121
Gemfile.lock Normal file
Просмотреть файл

@ -0,0 +1,121 @@
PATH
remote: .
specs:
cocoapods-azure-universal-packages (0.0.1)
addressable (>= 2.6.0)
cocoapods
cocoapods-downloader
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.2)
activesupport (4.2.11.3)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.3)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
claide (1.0.3)
cocoapods (1.9.3)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.9.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.2.2, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.6.6)
nap (~> 1.0)
ruby-macho (~> 1.4)
xcodeproj (>= 1.14.0, < 2.0)
cocoapods-core (1.9.3)
activesupport (>= 4.0.2, < 6)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.4)
cocoapods-downloader (1.4.0)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.1.0)
cocoapods-trunk (1.5.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.1.7)
diff-lcs (1.4.4)
escape (0.0.4)
ethon (0.12.0)
ffi (>= 1.3.0)
ffi (1.13.1)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
json (2.3.1)
minitest (5.14.2)
mocha (1.11.2)
molinillo (0.6.6)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.6)
rake (13.0.1)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.2)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.3)
ruby-macho (1.4.0)
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
xcodeproj (1.18.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
PLATFORMS
ruby
DEPENDENCIES
cocoapods
cocoapods-azure-universal-packages!
mocha (~> 1.11)
rake (~> 13.0)
rspec (~> 3.9)
BUNDLED WITH
2.1.4

Просмотреть файл

@ -1,5 +1,71 @@
# CocoaPods Azure Universal Packages plugin
# Contributing
This project is a [CocoaPods](https://github.com/CocoaPods/CocoaPods) plugin that allows to dowload pods published as [Universal Packages](https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/universal-packages) in [Azure Artifacts](https://azure.microsoft.com/en-gb/services/devops/artifacts/) feeds.
## Getting started
Install the plugin by adding to your `Gemfile`
```Ruby
gem "cocoapods-azure-universal-packages"
```
Under the hood the plugin uses the [Azure CLI](https://aka.ms/azcli) to download the Universal Packages, you can install it running
```shell
brew update && brew install azure-cli
```
Finally, ensure that you are logged in using
```shell
az login
```
## How it works
The plugin replaces the default CocoaPods HTTP downloader when a pod source URL points to a universal package and it uses the Azure CLI to perform the download.
_Note:_ The plugin will install the Azure CLI [DevOps extension](https://github.com/Azure/azure-devops-cli-extension) automatically when running `pod install` or `pod update` for the first time.
## Usage
Add to your Podfile
```Ruby
plugin 'cocoapods-azure-universal-packages', {
:base_url => '{{BASE_URL}}'
}
```
replacing `{{BASE_URL}}` with the base URL of your Azure Artifacts feed (for example, `https://pkgs.dev.azure.com/`).
Then, in your podspec set the pod's source to
```Ruby
# For project scoped feeds:
spec.source = { :http => '{{BASE_URL}}/{{ORGANIZATION}}/{{PROJECT}}/_apis/packaging/feeds/{{FEED}}/upack/packages/{{PACKAGE}}/versions/{{VERSION}}' }
# For organization scoped feeds:
spec.source = { :http => '{{BASE_URL}}/{{ORGANIZATION}}/_apis/packaging/feeds/{{FEED}}/upack/packages/{{PACKAGE}}/versions/{{VERSION}}' }
```
where:
- `{{BASE_URL}}` is the base URL you chose above
- `{{ORGANIZATION}}` is the name of your feed's organization
- `{{PROJECT}}` is the name of your feed's project (you must specify this only if your feed is a project scoped feed)
- `{{PACKAGE}}` is the name of your universal package
- `{{VERSION}}` is the version of your universal package
### Plugin parameters
| Parameter | Description |
| --------- | ----------- |
| `base_url` | The base URL of the Azure Artifacts feed. Required unless `base_urls` is specified. |
| `base_urls` | An array of base URLs of possible Azure Artifacts feeds. Required unless `base_url` is specified. |
| `update_cli_extension` | Whether to update the Azure CLI DevOps extensions automatically. Default to `false`. |
## Run tests for this plugin
To run the tests, use
```shell
rake tests
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us

8
Rakefile Normal file
Просмотреть файл

@ -0,0 +1,8 @@
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:tests) do |t|
t.pattern = Dir.glob("spec/**/*_spec.rb")
t.rspec_opts = "--format documentation"
end
task :default => :tests

Просмотреть файл

@ -0,0 +1,26 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'cocoapods-azure-universal-packages/gem_version.rb'
Gem::Specification.new do |spec|
spec.name = 'cocoapods-azure-universal-packages'
spec.version = CocoapodsAzureUniversalPackages::VERSION
spec.authors = ['Microsoft Corporation']
spec.summary = %q{A CocoaPods plugin for downloading Universal Packages from Azure Artifacts feeds.}
spec.homepage = 'https://github.com/microsoft/cocoapods-azure-universal-packages'
spec.license = 'MIT'
spec.files = Dir["lib/**/*"] + %w(README.md LICENSE)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.add_runtime_dependency 'cocoapods'
spec.add_runtime_dependency 'cocoapods-downloader'
spec.add_runtime_dependency 'addressable', '>= 2.6.0'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.9'
spec.add_development_dependency 'mocha', '~> 1.11'
end

Просмотреть файл

@ -0,0 +1,3 @@
require 'cocoapods-azure-universal-packages/azure_universal_package_downloader'
require 'cocoapods-azure-universal-packages/gem_version'
require 'cocoapods-azure-universal-packages/pre_install'

Просмотреть файл

@ -0,0 +1,60 @@
require 'addressable'
require 'cocoapods'
require 'cocoapods-downloader'
module Pod
module Downloader
@azure_base_urls = []
class << self
attr_accessor :azure_base_urls
end
class Http
private
alias_method :aliased_download!, :download!
executable :az
def download!
aup_uri_template = Addressable::Template.new(
'{scheme}://{host}/{organization}{/project}/_apis/packaging/feeds/{feed}/upack/packages/{package}/versions/{version}'
)
uri = Addressable::URI.parse(url)
aup_uri_components = aup_uri_template.extract(uri)
if !aup_uri_components.nil? && Downloader.azure_base_urls.include?("#{aup_uri_components['scheme']}://#{aup_uri_components['host']}")
download_azure_universal_package!(aup_uri_components)
else
aliased_download!
end
end
def download_azure_universal_package!(params)
ui_sub_action("Downloading #{params['package']} (#{params['version']}) from Azure feed #{params['feed']} (#{[params['organization'], params['project']].compact.join('/')})") do
parameters = [
'artifacts',
'universal',
'download',
'--organization', "#{params['scheme']}://#{params['host']}/#{params['organization']}/",
'--feed', params['feed'],
'--name', params['package'],
'--version', params['version'],
'--path', target_path.to_s
]
# If it's a project scoped feed, we need to pass a few more arguments
parameters.push('--project', params['project'], '--scope', 'project') unless params['project'].nil?
# Fetch the Universal Package
az! parameters
end
end
end
end
end

Просмотреть файл

@ -0,0 +1,3 @@
module CocoapodsAzureUniversalPackages
VERSION = "0.0.1"
end

Просмотреть файл

@ -0,0 +1,29 @@
module CocoapodsAzureUniversalPackages
class << self
Pod::HooksManager.register('cocoapods-azure-universal-packages', :pre_install) do |context, options|
CocoapodsAzureUniversalPackages.pre_install(options)
end
def pre_install(options)
# Check if the Azure CLI is installed
raise Pod::Informative, 'Unable to locate the Azure CLI. To learn more refer to https://aka.ms/azcli' unless Pod::Executable.which('az')
# Install the azure-devops extension if necessary
Pod::Executable.execute_command('az', ['extension', 'add', '--yes', '--name', 'azure-devops'], false)
# Optionally, update the azure-devops extension
if options.fetch(:update_cli_extension, false)
Pod::Executable.execute_command('az', ['extension', 'update', '--name', 'azure-devops'], false)
end
# Now we can configure the downloader to use the Azure CLI for downloading pods from the given hosts
azure_base_urls = options[:base_url] || options[:base_urls]
raise Pod::Informative, 'You must configure at least one Azure base url' unless azure_base_urls
Pod::Downloader.azure_base_urls = ([] << azure_base_urls).flatten.map { |url| url.delete_suffix('/') }
raise Pod::Informative, 'You must configure at least one Azure base url' if Pod::Downloader.azure_base_urls.empty?
end
end
end

1
lib/cocoapods_plugin.rb Normal file
Просмотреть файл

@ -0,0 +1 @@
require 'cocoapods-azure-universal-packages'

Просмотреть файл

@ -0,0 +1,63 @@
require 'spec_helper'
describe Pod::Downloader::Http do
describe "#download!" do
before(:each) do
Pod::Downloader.azure_base_urls = ["https://pkgs.dev.azure.com"]
end
context 'when not downloading a pod from one of the configured base urls' do
it 'calls #aliased_download!' do
downloader = Pod::Downloader::Http.new("/tmp", "https://www.microsoft.com", {})
downloader.target_path.stubs(:mkpath)
downloader.expects(:aliased_download!)
downloader.download
end
end
context 'when downloading a pod from one of the configured base urls' do
it 'can download universal packages from organization feeds' do
downloader = Pod::Downloader::Http.new("/tmp", "https://pkgs.dev.azure.com/test_org/_apis/packaging/feeds/org_feed/upack/packages/test_package/versions/1.2.3", {})
parameters = [
'artifacts',
'universal',
'download',
'--organization', 'https://pkgs.dev.azure.com/test_org/',
'--feed', 'org_feed',
'--name', 'test_package',
'--version', '1.2.3',
'--path', '/tmp'
]
downloader.target_path.stubs(:mkpath)
downloader.expects(:execute_command).with('az', parameters, anything)
downloader.download
end
it 'can download universal packages from project feeds' do
downloader = Pod::Downloader::Http.new("/tmp", "https://pkgs.dev.azure.com/test_org/test_project/_apis/packaging/feeds/project_feed/upack/packages/test_package/versions/1.2.3", {})
parameters = [
'artifacts',
'universal',
'download',
'--organization', 'https://pkgs.dev.azure.com/test_org/',
'--feed', 'project_feed',
'--name', 'test_package',
'--version', '1.2.3',
'--path', '/tmp',
'--project', 'test_project',
'--scope', 'project'
]
downloader.target_path.stubs(:mkpath)
downloader.expects(:execute_command).with('az', parameters, anything)
downloader.download
end
end
end
end

Просмотреть файл

@ -0,0 +1,83 @@
require 'spec_helper'
describe CocoapodsAzureUniversalPackages do
describe ".pre_install" do
before(:each) do
Pod::Downloader.azure_base_urls = []
end
context 'when Azure CLI is not installed' do
let(:options) { {:base_url => "https://dev.azure.com/"} }
it 'raises an exception' do
Pod::Executable.expects(:which).with('az').returns(nil)
expect { CocoapodsAzureUniversalPackages.pre_install(options) }.to raise_error(Pod::Informative, '[!] Unable to locate the Azure CLI. To learn more refer to https://aka.ms/azcli'.red)
end
end
context 'when Azure CLI is installed' do
let(:base_options) { {:base_url => "https://dev.azure.com/"} }
before(:each) do
Pod::Executable.stubs(:which).with('az').returns('az')
end
it 'does not raise an exception' do
Pod::Executable.expects(:which).with('az').returns('az')
expect { CocoapodsAzureUniversalPackages.pre_install(base_options) }.to_not raise_error
end
it 'installs the Azure CLI DevOps extension' do
Pod::Executable.expects(:execute_command).with('az', ['extension', 'add', '--yes', '--name', 'azure-devops'], false).returns('')
CocoapodsAzureUniversalPackages.pre_install(base_options)
end
context "with 'update_cli_extension' set to true" do
let(:options) { base_options.merge(update_cli_extension: true) }
it 'updates the Azure CLI DevOps extension' do
Pod::Executable.expects(:execute_command).with('az', ['extension', 'add', '--yes', '--name', 'azure-devops'], false).returns('')
Pod::Executable.expects(:execute_command).with('az', ['extension', 'update', '--name', 'azure-devops'], false).returns('')
CocoapodsAzureUniversalPackages.pre_install(options)
end
end
%w[base_url base_urls].each do |url_option|
context "with a string specified in '#{url_option}'" do
it 'adds the url to the downloader' do
Pod::Downloader.expects(:azure_base_urls=).with(includes("https://dev.azure.com"))
Pod::Downloader.stubs(:azure_base_urls).returns(["https://dev.azure.com"])
CocoapodsAzureUniversalPackages.pre_install({url_option.to_sym => "https://dev.azure.com/"})
end
end
context "with an array specified in '#{url_option}'" do
it 'adds the url to the downloader' do
Pod::Downloader.expects(:azure_base_urls=).with(all_of(includes("https://dev.azure.com"), includes("https://pkgs.dev.azure.com")))
Pod::Downloader.stubs(:azure_base_urls).returns(["https://dev.azure.com", "https://pkgs.dev.azure.com"])
CocoapodsAzureUniversalPackages.pre_install({url_option.to_sym => ["https://dev.azure.com/", "https://pkgs.dev.azure.com/"]})
end
end
context "with an empty array specified in '#{url_option}'" do
it 'raises an exception' do
Pod::Downloader.expects(:azure_base_urls=).with(equals([]))
Pod::Downloader.stubs(:azure_base_urls).returns([])
expect { CocoapodsAzureUniversalPackages.pre_install({url_option.to_sym => []}) }.to raise_error(Pod::Informative, '[!] You must configure at least one Azure base url'.red)
end
end
end
context "without a base url argument" do
it 'raises an exception' do
Pod::Downloader.expects(:azure_base_urls=).never
expect { CocoapodsAzureUniversalPackages.pre_install({}) }.to raise_error(Pod::Informative, '[!] You must configure at least one Azure base url'.red)
end
end
end
end
end

101
spec/spec_helper.rb Normal file
Просмотреть файл

@ -0,0 +1,101 @@
require 'pathname'
ROOT = Pathname.new(File.expand_path('../../', __FILE__))
$:.unshift((ROOT + 'lib').to_s)
$:.unshift((ROOT + 'spec').to_s)
require 'bundler/setup'
require 'cocoapods'
RSpec.configure do |config|
config.mock_with :mocha
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
Mocha.configure { |configuration|
configuration.stubbing_non_existent_method = :prevent
}
require 'cocoapods_plugin'
#-----------------------------------------------------------------------------#
module Pod
# Disable the wrapping so the output is deterministic in the tests.
#
UI.disable_wrap = true
# Redirects the messages to an internal store.
#
module UI
@output = ''
@warnings = ''
class << self
attr_accessor :output
attr_accessor :warnings
def puts(message = '')
@output << "#{message}\n"
end
def warn(message = '', actions = [])
@warnings << "#{message}\n"
end
def print(message)
@output << message
end
end
end
end
#-----------------------------------------------------------------------------#
module Pod
module Downloader
# Disable command execution.
#
class Base
def execute_command(executable, command, raise_on_failure = false)
''
end
def ui_action(_)
yield
end
def ui_sub_action(_)
yield
end
def ui_message(_)
end
end
end
end
#-----------------------------------------------------------------------------#
module Pod
# Disable command execution.
#
module Executable
def self.execute_command(executable, command, raise_on_failure = true)
''
end
def self.which(program)
nil
end
end
end
#-----------------------------------------------------------------------------#