php-composer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1006 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +1 -0
- data/lib/composer.rb +52 -0
- data/lib/composer/error.rb +8 -0
- data/lib/composer/json/json_file.rb +270 -0
- data/lib/composer/json/json_formatter.rb +159 -0
- data/lib/composer/json/json_validaton_error.rb +29 -0
- data/lib/composer/manager.rb +79 -0
- data/lib/composer/package/alias_package.rb +273 -0
- data/lib/composer/package/base_package.rb +130 -0
- data/lib/composer/package/complete_package.rb +55 -0
- data/lib/composer/package/dumper/hash_dumper.rb +169 -0
- data/lib/composer/package/link.rb +51 -0
- data/lib/composer/package/link_constraint/base_constraint.rb +36 -0
- data/lib/composer/package/link_constraint/empty_constraint.rb +35 -0
- data/lib/composer/package/link_constraint/multi_constraint.rb +67 -0
- data/lib/composer/package/link_constraint/specific_constraint.rb +41 -0
- data/lib/composer/package/link_constraint/version_constraint.rb +221 -0
- data/lib/composer/package/loader/hash_loader.rb +316 -0
- data/lib/composer/package/loader/json_loader.rb +47 -0
- data/lib/composer/package/loader/project_attributes_loader.rb +71 -0
- data/lib/composer/package/loader/project_root_package_loader.rb +28 -0
- data/lib/composer/package/package.rb +118 -0
- data/lib/composer/package/root_alias_package.rb +37 -0
- data/lib/composer/package/root_package.rb +37 -0
- data/lib/composer/package/version/version_parser.rb +583 -0
- data/lib/composer/package/version/version_selector.rb +106 -0
- data/lib/composer/provider.rb +94 -0
- data/lib/composer/repository/array_repository.rb +195 -0
- data/lib/composer/repository/filesystem_repository.rb +86 -0
- data/lib/composer/repository/writeable_array_repository.rb +60 -0
- data/lib/composer/version.rb +3 -0
- data/php-composer.gemspec +31 -0
- data/resources/composer-schema.json +421 -0
- metadata +188 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
#
|
2
|
+
# This file was ported to ruby from Composer php source code.
|
3
|
+
# Original Source: Composer\Package\Loader\JsonLoader.php
|
4
|
+
#
|
5
|
+
# (c) Nils Adermann <naderman@naderman.de>
|
6
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
7
|
+
#
|
8
|
+
# For the full copyright and license information, please view the LICENSE
|
9
|
+
# file that was distributed with this source code.
|
10
|
+
#
|
11
|
+
|
12
|
+
module Composer
|
13
|
+
module Package
|
14
|
+
module Loader
|
15
|
+
|
16
|
+
# Loads a package from a json string or JsonFile
|
17
|
+
# @author Ioannis Kappas <ikappas@devworks.gr>
|
18
|
+
# @php_author Konstantin Kudryashiv <ever.zet@gmail.com>
|
19
|
+
class JsonLoader
|
20
|
+
|
21
|
+
def initialize(loader)
|
22
|
+
@loader = loader
|
23
|
+
end
|
24
|
+
|
25
|
+
# Load a json string or file
|
26
|
+
# Param: string|JsonFile json A filename, json string or JsonFile instance to load the package from
|
27
|
+
# Returns: Composer::Package::Package
|
28
|
+
def load(json)
|
29
|
+
if json.instance_of?(Composer::Json::JsonFile)
|
30
|
+
config = json.read
|
31
|
+
elsif File.exist?(json)
|
32
|
+
config = Composer::Json::JsonFile.parse_json(
|
33
|
+
File.open(filepath, "r") { |f| f.read },
|
34
|
+
json
|
35
|
+
)
|
36
|
+
elsif json.class === "String"
|
37
|
+
config = Composer::Json::JsonFile.parse_json(
|
38
|
+
json
|
39
|
+
)
|
40
|
+
end
|
41
|
+
@loader.load(config)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'digest/crc32'
|
2
|
+
|
3
|
+
module Composer
|
4
|
+
module Package
|
5
|
+
module Loader
|
6
|
+
|
7
|
+
# Loads a package from project attributes
|
8
|
+
# @author Ioannis Kappas <ikappas@devworks.gr>
|
9
|
+
class ProjectAttributesLoader
|
10
|
+
|
11
|
+
def initialize(loader)
|
12
|
+
@loader = loader
|
13
|
+
end
|
14
|
+
|
15
|
+
# Load a json string or file
|
16
|
+
# Param: string|JsonFile json A filename, json string or JsonFile instance to load the package from
|
17
|
+
# Returns: Composer::Package::Package
|
18
|
+
def load(project, ref, type = 'library')
|
19
|
+
|
20
|
+
version = (ref.instance_of?(Gitlab::Git::Branch)) ? "dev-#{ref.name}" : ref.name
|
21
|
+
|
22
|
+
config = {
|
23
|
+
'name' => project.path_with_namespace.gsub(/\s/, '').downcase,
|
24
|
+
'description' => project.description,
|
25
|
+
'version' => version,
|
26
|
+
'uid' => Digest::CRC32.checksum(ref.name + ref.target),
|
27
|
+
'source' => {
|
28
|
+
'url' => project.url_to_repo,
|
29
|
+
'type' => 'git',
|
30
|
+
'reference' => ref.target
|
31
|
+
},
|
32
|
+
'dist' => {
|
33
|
+
'url' => [project.web_url, 'repository', 'archive.zip?ref=' + ref.name].join('/'),
|
34
|
+
'type' => 'zip'
|
35
|
+
},
|
36
|
+
'type' => type,
|
37
|
+
'homepage' => project.web_url
|
38
|
+
}
|
39
|
+
|
40
|
+
if time = get_time(project, ref)
|
41
|
+
log("Ref: #{ref.to_json} Time: #{time}")
|
42
|
+
config['time'] = time
|
43
|
+
end
|
44
|
+
|
45
|
+
if keywords = get_keywords(project)
|
46
|
+
config['keywords'] = keywords
|
47
|
+
end
|
48
|
+
|
49
|
+
@loader.load(config)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def get_time(project, ref)
|
55
|
+
commit = project.repository.commit(ref.target)
|
56
|
+
commit.committed_date.strftime('%Y-%m-%d %H:%M:%S')
|
57
|
+
rescue
|
58
|
+
# If there's a problem, just skip the "time" field
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_keywords(project)
|
62
|
+
project.tags.collect { |t| t['name'] }
|
63
|
+
end
|
64
|
+
|
65
|
+
def log(message)
|
66
|
+
Gitlab::AppLogger.error("ComposerService: #{message}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Composer
|
2
|
+
module Package
|
3
|
+
module Loader
|
4
|
+
|
5
|
+
# Loads a package from the project root package
|
6
|
+
# @author Ioannis Kappas <ikappas@devworks.gr>
|
7
|
+
class ProjectRootPackageLoader
|
8
|
+
|
9
|
+
def initialize(loader)
|
10
|
+
@loader = loader
|
11
|
+
end
|
12
|
+
|
13
|
+
# Load a project ref
|
14
|
+
# Param: string|JsonFile json A filename, json string or JsonFile instance to load the package from
|
15
|
+
# Returns: Composer::Package::Package
|
16
|
+
def load(project, ref)
|
17
|
+
|
18
|
+
config = Composer::Json::JsonFile.parse_json(
|
19
|
+
project.repository.blob_at(ref.target, 'composer.json')
|
20
|
+
)
|
21
|
+
|
22
|
+
@loader.load(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#
|
2
|
+
# This file was ported to ruby from Composer php source code file.
|
3
|
+
# Original Source: Composer\Package\Package.php
|
4
|
+
#
|
5
|
+
# (c) Nils Adermann <naderman@naderman.de>
|
6
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
7
|
+
#
|
8
|
+
# For the full copyright and license information, please view the LICENSE
|
9
|
+
# file that was distributed with this source code.
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'digest/crc32'
|
13
|
+
|
14
|
+
module Composer
|
15
|
+
module Package
|
16
|
+
|
17
|
+
# Core package definitions that are needed to resolve dependencies
|
18
|
+
# and install packages
|
19
|
+
class Package < Composer::Package::BasePackage
|
20
|
+
|
21
|
+
attr_reader :stability
|
22
|
+
|
23
|
+
attr_accessor :installation_source, :source_type,
|
24
|
+
:source_url, :source_reference, :source_mirrors, :dist_type,
|
25
|
+
:dist_url, :dist_reference, :dist_sha1_checksum,
|
26
|
+
:dist_mirrors, :release_date, :extra, :binaries, :requires,
|
27
|
+
:conflicts, :provides, :replaces, :dev_requires, :suggests,
|
28
|
+
:autoload, :dev_autoload, :include_paths, :archive_excludes,
|
29
|
+
:notification_url
|
30
|
+
|
31
|
+
# complete package attributes
|
32
|
+
|
33
|
+
|
34
|
+
# Creates a new in memory package.
|
35
|
+
# Param: string name The package's name
|
36
|
+
# Param: string version The package's version
|
37
|
+
# Param: string prettyVersion The package's non-normalized version
|
38
|
+
def initialize(name, version, pretty_version)
|
39
|
+
super(name)
|
40
|
+
|
41
|
+
# default values
|
42
|
+
@extra = {}
|
43
|
+
@binaries = []
|
44
|
+
@requires = {}
|
45
|
+
@conflicts = {}
|
46
|
+
@provides = {}
|
47
|
+
@replaces = {}
|
48
|
+
@dev_requires = {}
|
49
|
+
@suggests = {}
|
50
|
+
@autoload = {}
|
51
|
+
@dev_autoload = {}
|
52
|
+
@include_paths = []
|
53
|
+
@archive_excludes = []
|
54
|
+
|
55
|
+
# init package attributes
|
56
|
+
replace_version(version, pretty_version)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def attributes
|
61
|
+
dumper = Composer::Package::Dumper::HashDumper.new
|
62
|
+
dumper.dump(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set package type
|
66
|
+
# Param: string type
|
67
|
+
def type=(type)
|
68
|
+
@type = type
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get package type
|
72
|
+
# Return: string
|
73
|
+
def type
|
74
|
+
@type ? @type : 'library'
|
75
|
+
end
|
76
|
+
|
77
|
+
def target_dir=(target_dir)
|
78
|
+
@target_dir = target_dir
|
79
|
+
end
|
80
|
+
|
81
|
+
def target_dir
|
82
|
+
return unless @target_dir
|
83
|
+
regex = '(?:^|[\\\\/]+)\.\.?(?:[\\\\/]+|$)(?:\.\.?(?:[\\\\/]+|$))*'
|
84
|
+
@target_dir.gsub(/#{regex}/x, '/').gsub(/^\/+/, '')
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns package unique name, constructed from name, version and
|
88
|
+
# release type.
|
89
|
+
# Return: string
|
90
|
+
def unique_name
|
91
|
+
"#{name}-#{version}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def pretty_string
|
95
|
+
"#{pretty_name} #{pretty_version}"
|
96
|
+
end
|
97
|
+
|
98
|
+
# Determine if development package
|
99
|
+
# Return: true if development package; Otherwise false.
|
100
|
+
def is_dev
|
101
|
+
@dev
|
102
|
+
end
|
103
|
+
|
104
|
+
# Replaces current version and pretty version with passed values.
|
105
|
+
# It also sets stability.
|
106
|
+
# Param: string version The package's normalized version
|
107
|
+
# Param: string prettyVersion The package's non-normalized version
|
108
|
+
def replace_version(version, pretty_version)
|
109
|
+
@version = version
|
110
|
+
@pretty_version = pretty_version
|
111
|
+
|
112
|
+
@stability = Composer::Package::Version::VersionParser::parse_stability(version)
|
113
|
+
@dev = @stability === 'dev'
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# This file was ported to ruby from Composer php source code file.
|
3
|
+
# Original Source: Composer\Package\RootAliasPackage.php
|
4
|
+
#
|
5
|
+
# (c) Nils Adermann <naderman@naderman.de>
|
6
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
7
|
+
#
|
8
|
+
# For the full copyright and license information, please view the LICENSE
|
9
|
+
# file that was distributed with this source code.
|
10
|
+
#
|
11
|
+
|
12
|
+
module Composer
|
13
|
+
module Package
|
14
|
+
|
15
|
+
# The root package represents the project's composer.json
|
16
|
+
# and contains additional metadata
|
17
|
+
class RootAliasPackage < Composer::Package::CompletePackage
|
18
|
+
|
19
|
+
attr_accessor :minimum_stability, :prefer_stable, :stability_flags,
|
20
|
+
:references, :aliases
|
21
|
+
|
22
|
+
# Creates a new root package in memory package.
|
23
|
+
# Param: string name The package's name
|
24
|
+
# Param: string version The package's version
|
25
|
+
# Param: string prettyVersion The package's non-normalized version
|
26
|
+
def initialize(name, version, pretty_version)
|
27
|
+
super(name, version, pretty_version)
|
28
|
+
|
29
|
+
@minimum_stability = 'stable'
|
30
|
+
@stability_flags = []
|
31
|
+
@references = []
|
32
|
+
@aliases = []
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# This file was ported to ruby from Composer php source code file.
|
3
|
+
# Original Source: Composer\Package\RootPackage.php
|
4
|
+
#
|
5
|
+
# (c) Nils Adermann <naderman@naderman.de>
|
6
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
7
|
+
#
|
8
|
+
# For the full copyright and license information, please view the LICENSE
|
9
|
+
# file that was distributed with this source code.
|
10
|
+
#
|
11
|
+
|
12
|
+
module Composer
|
13
|
+
module Package
|
14
|
+
|
15
|
+
# The root package represents the project's composer.json
|
16
|
+
# and contains additional metadata
|
17
|
+
class RootPackage < Composer::Package::CompletePackage
|
18
|
+
|
19
|
+
attr_accessor :minimum_stability, :prefer_stable, :stability_flags,
|
20
|
+
:references, :aliases
|
21
|
+
|
22
|
+
# Creates a new root package in memory package.
|
23
|
+
# Param: string name The package's name
|
24
|
+
# Param: string version The package's version
|
25
|
+
# Param: string prettyVersion The package's non-normalized version
|
26
|
+
def initialize(name, version, pretty_version)
|
27
|
+
super(name, version, pretty_version)
|
28
|
+
|
29
|
+
@minimum_stability = 'stable'
|
30
|
+
@stability_flags = []
|
31
|
+
@references = []
|
32
|
+
@aliases = []
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,583 @@
|
|
1
|
+
#
|
2
|
+
# This file was ported to ruby from Composer php source code file.
|
3
|
+
# Original Source: Composer\Package\Version\VersionParser.php
|
4
|
+
#
|
5
|
+
# (c) Nils Adermann <naderman@naderman.de>
|
6
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
7
|
+
#
|
8
|
+
# For the full copyright and license information, please view the LICENSE
|
9
|
+
# file that was distributed with this source code.
|
10
|
+
#
|
11
|
+
|
12
|
+
module Composer
|
13
|
+
module Package
|
14
|
+
module Version
|
15
|
+
# Version Parser
|
16
|
+
#
|
17
|
+
# PHP Authors:
|
18
|
+
# Jordi Boggiano <j.boggiano@seld.be>
|
19
|
+
#
|
20
|
+
# Ruby Authors:
|
21
|
+
# Ioannis Kappas <ikappas@devworks.gr>
|
22
|
+
class VersionParser
|
23
|
+
MODIFIER_REGEX = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'.freeze()
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
# Returns the stability of a version
|
28
|
+
#
|
29
|
+
# Params:
|
30
|
+
# +version+:: string The version to parse for stability
|
31
|
+
#
|
32
|
+
# Returns:
|
33
|
+
# string The version's stability
|
34
|
+
def parse_stability(version)
|
35
|
+
raise ArgumentError, 'version must be specified' unless version
|
36
|
+
raise TypeError, 'version must be of type String' unless version.is_a?(String)
|
37
|
+
raise UnexpectedValueError, 'version string must not be empty' if version.empty?
|
38
|
+
|
39
|
+
version = version.gsub(/#.+$/i, '')
|
40
|
+
|
41
|
+
if version.start_with?('dev-') || version.end_with?('-dev')
|
42
|
+
return 'dev'
|
43
|
+
end
|
44
|
+
|
45
|
+
if matches = /#{MODIFIER_REGEX}$/i.match(version.downcase)
|
46
|
+
if matches[3]
|
47
|
+
return 'dev'
|
48
|
+
elsif matches[1]
|
49
|
+
if 'beta' === matches[1] || 'b' === matches[1]
|
50
|
+
return 'beta'
|
51
|
+
elsif 'alpha' === matches[1] || 'a' === matches[1]
|
52
|
+
return 'alpha'
|
53
|
+
elsif 'rc' === matches[1]
|
54
|
+
return 'RC'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
'stable'
|
60
|
+
end
|
61
|
+
|
62
|
+
# Normalize the specified stability
|
63
|
+
# Param: string stability
|
64
|
+
# Return: string
|
65
|
+
def normalize_stability(stability)
|
66
|
+
raise ArgumentError, 'stability must be specified' unless stability
|
67
|
+
raise TypeError, 'stability must be of type String' unless stability.is_a?(String)
|
68
|
+
stability = stability.downcase
|
69
|
+
stability === 'rc' ? 'RC' : stability
|
70
|
+
end
|
71
|
+
|
72
|
+
# Formats package version
|
73
|
+
# Param: Composer::Package::Package package
|
74
|
+
# Param: boolean truncate
|
75
|
+
# Return: string
|
76
|
+
def format_version(package, truncate = true)
|
77
|
+
if !package.is_dev || !['hg', 'git'].include?(package.source_type)
|
78
|
+
return package.pretty_version
|
79
|
+
end
|
80
|
+
|
81
|
+
# if source reference is a sha1 hash -- truncate
|
82
|
+
if truncate && package.source_reference.length === 40
|
83
|
+
return "#{package.pretty_version} #{package.source_reference[0..6]}"
|
84
|
+
end
|
85
|
+
|
86
|
+
"#{package.pretty_version} #{package.source_reference}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Normalizes a version string to be able to perform comparisons on it
|
91
|
+
#
|
92
|
+
# Params:
|
93
|
+
# +version+:: <tt>String</tt> The version string to normalize
|
94
|
+
# +full_version+:: <tt>String</tt> optional complete version string to
|
95
|
+
# give more context
|
96
|
+
#
|
97
|
+
# Throws:
|
98
|
+
# +InvalidVersionStringError+
|
99
|
+
#
|
100
|
+
# Returns:
|
101
|
+
# +String+
|
102
|
+
def normalize(version, full_version = nil)
|
103
|
+
raise ArgumentError, 'version must be specified' unless version
|
104
|
+
raise TypeError, 'version must be of type String' unless version.is_a?(String)
|
105
|
+
raise UnexpectedValueError, 'version string must not be empty' if version.empty?
|
106
|
+
|
107
|
+
version.strip!
|
108
|
+
if full_version == nil
|
109
|
+
full_version = version
|
110
|
+
end
|
111
|
+
|
112
|
+
# ignore aliases and just assume the alias is required
|
113
|
+
# instead of the source
|
114
|
+
if matches = /^([^,\s]+) +as +([^,\s]+)$/.match(version)
|
115
|
+
version = matches[1]
|
116
|
+
end
|
117
|
+
|
118
|
+
# ignore build metadata
|
119
|
+
if matches = /^([^,\s+]+)\+[^\s]+$/.match(version)
|
120
|
+
version = matches[1]
|
121
|
+
end
|
122
|
+
|
123
|
+
# match master-like branches
|
124
|
+
if matches = /^(?:dev-)?(?:master|trunk|default)$/i.match(version)
|
125
|
+
return '9999999-dev'
|
126
|
+
end
|
127
|
+
|
128
|
+
if 'dev-' === version[0...4].downcase
|
129
|
+
return "dev-#{version[4..version.size]}"
|
130
|
+
end
|
131
|
+
|
132
|
+
# match classical versioning
|
133
|
+
index = 0
|
134
|
+
if matches = /^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?#{MODIFIER_REGEX}$/i.match(version)
|
135
|
+
version = ''
|
136
|
+
matches.to_a[1..4].each do |c|
|
137
|
+
version += c ? c : '.0'
|
138
|
+
end
|
139
|
+
index = 5
|
140
|
+
elsif matches = /^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)#{MODIFIER_REGEX}$/i.match(version)
|
141
|
+
version = matches[1].gsub(/\D/, '-')
|
142
|
+
index = 2
|
143
|
+
elsif matches = /^v?(\d{4,})(\.\d+)?(\.\d+)?(\.\d+)?#{MODIFIER_REGEX}$/i.match(version)
|
144
|
+
version = ''
|
145
|
+
matches.to_a[1..4].each do |c|
|
146
|
+
version << (c.nil? ? '.0' : c)
|
147
|
+
end
|
148
|
+
index = 5
|
149
|
+
end
|
150
|
+
|
151
|
+
# add version modifiers if a version was matched
|
152
|
+
if index > 0
|
153
|
+
if matches[index]
|
154
|
+
if 'stable' === matches[index]
|
155
|
+
return version
|
156
|
+
end
|
157
|
+
stability = expand_stability(matches[index])
|
158
|
+
version = "#{version}-#{stability ? stability : matches[index]}#{matches[index + 1] ? matches[index + 1] : ''}"
|
159
|
+
end
|
160
|
+
|
161
|
+
if matches[index + 2]
|
162
|
+
version = "#{version}-dev"
|
163
|
+
end
|
164
|
+
|
165
|
+
return version
|
166
|
+
end
|
167
|
+
|
168
|
+
# match dev branches
|
169
|
+
if matches = /(.*?)[.-]?dev$/i.match(version)
|
170
|
+
begin
|
171
|
+
return normalize_branch(matches[1])
|
172
|
+
rescue
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
extra_message = ''
|
177
|
+
if matches = / +as +#{Regexp.escape(version)}$/.match(full_version)
|
178
|
+
extra_message = " in \"#{full_version}\", the alias must be an exact version"
|
179
|
+
elsif matches = /^#{Regexp.escape(version)} +as +/.match(full_version)
|
180
|
+
extra_message = " in \"#{full_version}\", the alias source must be an exact version, if it is a branch name you should prefix it with dev-"
|
181
|
+
end
|
182
|
+
|
183
|
+
raise UnexpectedValueError, "Invalid version string \"#{version}\"#{extra_message}"
|
184
|
+
end
|
185
|
+
|
186
|
+
# Normalizes a branch name to be able to perform comparisons on it
|
187
|
+
#
|
188
|
+
# Params:
|
189
|
+
# +name+:: string The branch name to normalize
|
190
|
+
#
|
191
|
+
# Returns:
|
192
|
+
# string The normalized branch name
|
193
|
+
def normalize_branch(name)
|
194
|
+
name.strip!
|
195
|
+
|
196
|
+
if ['master', 'trunk', 'default'].include?(name)
|
197
|
+
return normalize(name)
|
198
|
+
end
|
199
|
+
|
200
|
+
if matches = /^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$/i.match(name)
|
201
|
+
version = ''
|
202
|
+
|
203
|
+
# for i in 0..3
|
204
|
+
# # version << matches[i] ? matches[i].gsub('*', 'x').gsub('X', 'x') : '.x'
|
205
|
+
# end
|
206
|
+
matches.captures.each { |match| version << (match != nil ? match.gsub('*', 'x').gsub('X', 'x') : '.x') }
|
207
|
+
return "#{version.gsub('x', '9999999')}-dev"
|
208
|
+
end
|
209
|
+
|
210
|
+
"dev-#{name}"
|
211
|
+
end
|
212
|
+
|
213
|
+
# Params:
|
214
|
+
# +source+:: string source package name
|
215
|
+
# +source_version+:: string source package version (pretty version ideally)
|
216
|
+
# +description+:: string link description (e.g. requires, replaces, ..)
|
217
|
+
# +links+:: array An array of package name => constraint mappings
|
218
|
+
#
|
219
|
+
# Returns:
|
220
|
+
# Link[]
|
221
|
+
def parse_links(source, source_version, description, links)
|
222
|
+
res = {}
|
223
|
+
links.each do |target, constraint|
|
224
|
+
if 'self.version' === constraint
|
225
|
+
parsed_constraint = parse_constraints(source_version)
|
226
|
+
else
|
227
|
+
parsed_constraint = parse_constraints(constraint)
|
228
|
+
end
|
229
|
+
res[target.downcase] = Composer::Package::Link.new(
|
230
|
+
source,
|
231
|
+
target,
|
232
|
+
parsed_constraint,
|
233
|
+
description,
|
234
|
+
constraint
|
235
|
+
)
|
236
|
+
end
|
237
|
+
res
|
238
|
+
end
|
239
|
+
|
240
|
+
def parse_constraints(constraints)
|
241
|
+
raise ArgumentError, 'version must be specified' unless constraints
|
242
|
+
raise TypeError, 'version must be of type String' unless constraints.is_a?(String)
|
243
|
+
raise UnexpectedValueError, 'version string must not be empty' if constraints.empty?
|
244
|
+
|
245
|
+
pretty_constraint = constraints
|
246
|
+
|
247
|
+
stabilites = Composer::Package::BasePackage.stabilities.keys.join('|')
|
248
|
+
if match = /^([^,\s]*?)@(#{stabilites})$/i.match(constraints)
|
249
|
+
constraints = match[1].nil? || match[1].empty? ? '*' : match[1]
|
250
|
+
end
|
251
|
+
|
252
|
+
if match = /^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$/i.match(constraints)
|
253
|
+
constraints = match[1]
|
254
|
+
end
|
255
|
+
|
256
|
+
# or_constraints = preg_split('{\s*\|\|?\s*}', trim(constraints))
|
257
|
+
or_constraints = constraints.strip.split(/\s*\|\|?\s*/)
|
258
|
+
or_groups = []
|
259
|
+
or_constraints.each do |constraints|
|
260
|
+
|
261
|
+
# and_constraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', constraints)
|
262
|
+
and_constraints = constraints.split(/(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)/)
|
263
|
+
|
264
|
+
if and_constraints.length > 1
|
265
|
+
constraint_objects = []
|
266
|
+
and_constraints.each do |constraint|
|
267
|
+
constraint_objects << parse_constraint(constraint)
|
268
|
+
end
|
269
|
+
else
|
270
|
+
constraint_objects = parse_constraint(and_constraints[0])
|
271
|
+
end
|
272
|
+
|
273
|
+
if constraint_objects.length === 1
|
274
|
+
constraint = constraint_objects[0]
|
275
|
+
else
|
276
|
+
constraint = Composer::Package::LinkConstraint::MultiConstraint.new(constraint_objects)
|
277
|
+
end
|
278
|
+
|
279
|
+
or_groups << constraint
|
280
|
+
end
|
281
|
+
|
282
|
+
if or_groups.length === 1
|
283
|
+
constraint = or_groups[0]
|
284
|
+
else
|
285
|
+
constraint = Composer::Package::LinkConstraint::MultiConstraint.new(or_groups, false)
|
286
|
+
end
|
287
|
+
|
288
|
+
constraint.pretty_string = pretty_constraint
|
289
|
+
|
290
|
+
constraint
|
291
|
+
end
|
292
|
+
|
293
|
+
# Extract numeric prefix from alias, if it is in numeric format,
|
294
|
+
# suitable for version comparison
|
295
|
+
#
|
296
|
+
# Params:
|
297
|
+
# +branch+:: string Branch name (e.g. 2.1.x-dev)
|
298
|
+
#
|
299
|
+
# Returns:
|
300
|
+
# string|false Numeric prefix if present (e.g. 2.1.) or false
|
301
|
+
def parse_numeric_alias_prefix(branch)
|
302
|
+
if matches = /^(?<version>(\d+\.)*\d+)(?:\.x)?-dev$/i.match(branch)
|
303
|
+
return "#{matches['version']}."
|
304
|
+
end
|
305
|
+
false
|
306
|
+
end
|
307
|
+
|
308
|
+
# Parses a name/version pairs and returns an array of pairs + the
|
309
|
+
#
|
310
|
+
# Params:
|
311
|
+
# +pairs+:: array a set of package/version pairs separated by ":", "=" or " "
|
312
|
+
#
|
313
|
+
# Returns:
|
314
|
+
# array[] array of arrays containing a name and (if provided) a version
|
315
|
+
def parse_name_version_pairs(pairs)
|
316
|
+
pairs = pairs.values
|
317
|
+
result = []
|
318
|
+
|
319
|
+
for i in 0..(pairs.length - 1)
|
320
|
+
pair = pairs[i].strip!.gsub(/^([^=: ]+)[=: ](.*)$/, '$1 $2')
|
321
|
+
if nil === pair.index(' ') && pairs.key?(i + 1) && nil === pairs[i + 1].index('/')
|
322
|
+
pair = "#{pair} #{pairs[i + 1]}"
|
323
|
+
i = i + 1
|
324
|
+
end
|
325
|
+
|
326
|
+
if pair.index(' ')
|
327
|
+
name, version = pair.split(' ', 2)
|
328
|
+
result << { 'name' => name, 'version' => version }
|
329
|
+
else
|
330
|
+
result << { 'name' => pair }
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
result
|
336
|
+
end
|
337
|
+
|
338
|
+
# PRIVATE METHODS
|
339
|
+
private
|
340
|
+
|
341
|
+
def parse_constraint(constraint)
|
342
|
+
|
343
|
+
stabilites = Composer::Package::BasePackage.stabilities.keys.join('|')
|
344
|
+
if match = /^([^,\s]+?)@(#{stabilites})$/i.match(constraint)
|
345
|
+
constraint = match[1]
|
346
|
+
if match[2] != 'stable'
|
347
|
+
stability_modifier = match[2]
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
if /^[xX*](\.[xX*])*$/i.match(constraint)
|
352
|
+
return [
|
353
|
+
Composer::Package::LinkConstraint::EmptyConstraint.new
|
354
|
+
]
|
355
|
+
end
|
356
|
+
|
357
|
+
version_regex = '(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?' + MODIFIER_REGEX
|
358
|
+
|
359
|
+
# match tilde constraints
|
360
|
+
# like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
|
361
|
+
# version, to ensure that unstable instances of the current version are allowed.
|
362
|
+
# however, if a stability suffix is added to the constraint, then a >= match on the current version is
|
363
|
+
# used instead
|
364
|
+
if matches = /^~>?#{version_regex}$/i.match(constraint)
|
365
|
+
|
366
|
+
if constraint[0...2] === '~>'
|
367
|
+
raise UnexpectedValueError,
|
368
|
+
"Could not parse version constraint #{constraint}: \
|
369
|
+
Invalid operator \"~>\", you probably meant to use the \"~\" operator"
|
370
|
+
end
|
371
|
+
|
372
|
+
# Work out which position in the version we are operating at
|
373
|
+
if matches[4] && matches[4] != ''
|
374
|
+
position = 4
|
375
|
+
elsif matches[3] && matches[3] != ''
|
376
|
+
position = 3
|
377
|
+
elsif matches[2] && matches[2] != ''
|
378
|
+
position = 2
|
379
|
+
else
|
380
|
+
position = 1
|
381
|
+
end
|
382
|
+
|
383
|
+
# Calculate the stability suffix
|
384
|
+
stability_suffix = ''
|
385
|
+
if !matches[5].nil? && !matches[5].empty?
|
386
|
+
stability_suffix << "-#{expand_stability(matches[5])}"
|
387
|
+
if !matches[6].nil? && !matches[6].empty?
|
388
|
+
stability_suffix << matches[6]
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
if !matches[7].nil? && !matches[7].empty?
|
393
|
+
stability_suffix << '-dev'
|
394
|
+
end
|
395
|
+
|
396
|
+
stability_suffix = '-dev' if stability_suffix.empty?
|
397
|
+
|
398
|
+
low_version = manipulate_version_string(matches.to_a, position, 0) + stability_suffix
|
399
|
+
lower_bound = Composer::Package::LinkConstraint::VersionConstraint.new('>=', low_version)
|
400
|
+
|
401
|
+
# For upper bound, we increment the position of one more significance,
|
402
|
+
# but high_position = 0 would be illegal
|
403
|
+
high_position = [1, position - 1].max
|
404
|
+
high_version = manipulate_version_string(matches.to_a, high_position, 1) + '-dev'
|
405
|
+
upper_bound = Composer::Package::LinkConstraint::VersionConstraint.new('<', high_version)
|
406
|
+
|
407
|
+
return [
|
408
|
+
lower_bound,
|
409
|
+
upper_bound
|
410
|
+
]
|
411
|
+
end
|
412
|
+
|
413
|
+
# match caret constraints
|
414
|
+
if matches = /^\^#{version_regex}($)/i.match(constraint)
|
415
|
+
# Create comparison array
|
416
|
+
has_match = []
|
417
|
+
for i in 0..(matches.to_a.length - 1)
|
418
|
+
has_match[i] = !matches[i].nil? && !matches[i].empty?
|
419
|
+
end
|
420
|
+
|
421
|
+
# Work out which position in the version we are operating at
|
422
|
+
if matches[1] != '0' || !has_match[2]
|
423
|
+
position = 1
|
424
|
+
elsif matches[2] != '0' || !has_match[3]
|
425
|
+
position = 2
|
426
|
+
else
|
427
|
+
position = 3
|
428
|
+
end
|
429
|
+
|
430
|
+
# Calculate the stability suffix
|
431
|
+
stability_suffix = ''
|
432
|
+
if !has_match[5] && !has_match[7]
|
433
|
+
stability_suffix << '-dev'
|
434
|
+
end
|
435
|
+
|
436
|
+
low_pretty = "#{constraint}#{stability_suffix}"
|
437
|
+
low_version = normalize(low_pretty[1..low_pretty.length - 1])
|
438
|
+
lower_bound = Composer::Package::LinkConstraint::VersionConstraint.new('>=', low_version)
|
439
|
+
|
440
|
+
# For upper bound, we increment the position of one more significance,
|
441
|
+
# but high_position = 0 would be illegal
|
442
|
+
high_version = manipulate_version_string(matches.to_a, position, 1) + '-dev'
|
443
|
+
upper_bound = Composer::Package::LinkConstraint::VersionConstraint.new('<', high_version)
|
444
|
+
|
445
|
+
return [
|
446
|
+
lower_bound,
|
447
|
+
upper_bound
|
448
|
+
]
|
449
|
+
end
|
450
|
+
|
451
|
+
# match wildcard constraints
|
452
|
+
if matches = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[xX*]$/.match(constraint)
|
453
|
+
if matches[3] && matches[3] != ''
|
454
|
+
position = 3
|
455
|
+
elsif matches[2] && matches[2] != ''
|
456
|
+
position = 2
|
457
|
+
else
|
458
|
+
position = 1
|
459
|
+
end
|
460
|
+
|
461
|
+
low_version = manipulate_version_string(matches.to_a, position) + '-dev'
|
462
|
+
high_version = manipulate_version_string(matches.to_a, position, 1) + '-dev'
|
463
|
+
|
464
|
+
if low_version === "0.0.0.0-dev"
|
465
|
+
return [Composer::Package::LinkConstraint::VersionConstraint.new('<', high_version)]
|
466
|
+
end
|
467
|
+
|
468
|
+
return [
|
469
|
+
Composer::Package::LinkConstraint::VersionConstraint.new('>=', low_version),
|
470
|
+
Composer::Package::LinkConstraint::VersionConstraint.new('<', high_version),
|
471
|
+
]
|
472
|
+
end
|
473
|
+
|
474
|
+
# match hyphen constraints
|
475
|
+
if matches = /^(#{version_regex}) +- +(#{version_regex})($)/i.match(constraint)
|
476
|
+
|
477
|
+
match_from = matches[1]
|
478
|
+
match_to = matches[9]
|
479
|
+
|
480
|
+
# Create comparison array
|
481
|
+
has_match = []
|
482
|
+
for i in 0..(matches.to_a.length - 1)
|
483
|
+
has_match[i] = !matches[i].nil? && !matches[i].empty?
|
484
|
+
end
|
485
|
+
|
486
|
+
# Calculate the stability suffix
|
487
|
+
low_stability_suffix = ''
|
488
|
+
if !has_match[6] && !has_match[8]
|
489
|
+
low_stability_suffix = '-dev'
|
490
|
+
end
|
491
|
+
|
492
|
+
low_version = normalize(match_from)
|
493
|
+
lower_bound = Composer::Package::LinkConstraint::VersionConstraint.new('>=', low_version + low_stability_suffix)
|
494
|
+
|
495
|
+
# high_version = matches[10]
|
496
|
+
|
497
|
+
if (has_match[11] && has_match[12]) || has_match[14] || has_match[16]
|
498
|
+
high_version = normalize(match_to)
|
499
|
+
upper_bound = Composer::Package::LinkConstraint::VersionConstraint.new('<=', high_version)
|
500
|
+
else
|
501
|
+
high_match = ['', matches[10], matches[11], matches[12], matches[13]]
|
502
|
+
high_version = manipulate_version_string(high_match, (!has_match[11] ? 1 : 2), 1) + '-dev'
|
503
|
+
upper_bound = Composer::Package::LinkConstraint::VersionConstraint.new('<', high_version)
|
504
|
+
end
|
505
|
+
|
506
|
+
return [
|
507
|
+
lower_bound,
|
508
|
+
upper_bound
|
509
|
+
]
|
510
|
+
end
|
511
|
+
|
512
|
+
# match operators constraints
|
513
|
+
if matches = /^(<>|!=|>=?|<=?|==?)?\s*(.*)/.match(constraint)
|
514
|
+
begin
|
515
|
+
version = normalize(matches[2])
|
516
|
+
stability = VersionParser::parse_stability(version)
|
517
|
+
if !stability_modifier.nil? && !stability_modifier.empty? && (stability === 'stable')
|
518
|
+
version << "-#{stability_modifier}"
|
519
|
+
elsif matches[1] === '<'
|
520
|
+
unless /-#{MODIFIER_REGEX}$/.match(matches[2].downcase)
|
521
|
+
version << '-dev'
|
522
|
+
end
|
523
|
+
end
|
524
|
+
operator = matches[1].nil? ? '=' : matches[1]
|
525
|
+
return [Composer::Package::LinkConstraint::VersionConstraint.new(operator, version)]
|
526
|
+
rescue Exception => e
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
message = "Could not parse version constraint #{constraint}"
|
531
|
+
message << ": #{e.message}" if e
|
532
|
+
raise UnexpectedValueError, message
|
533
|
+
end
|
534
|
+
|
535
|
+
# Increment, decrement, or simply pad a version number.
|
536
|
+
# Support function for {@link parse_constraint()}
|
537
|
+
#
|
538
|
+
# Params:
|
539
|
+
# +matches+ Array with version parts in array indexes 1,2,3,4
|
540
|
+
# +position+ Integer 1,2,3,4 - which segment of the version to decrement
|
541
|
+
# +increment+ Integer
|
542
|
+
# +pad+ String The string to pad version parts after position
|
543
|
+
#
|
544
|
+
# Returns:
|
545
|
+
# string The new version
|
546
|
+
def manipulate_version_string(matches, position, increment = 0, pad = '0')
|
547
|
+
4.downto(1).each do |i|
|
548
|
+
if i > position
|
549
|
+
matches[i] = pad
|
550
|
+
elsif i == position && increment
|
551
|
+
matches[i] = matches[i].to_i + increment
|
552
|
+
# If matches[i] was 0, carry the decrement
|
553
|
+
if matches[i] < 0
|
554
|
+
matches[i] = pad
|
555
|
+
position -= 1
|
556
|
+
|
557
|
+
# Return nil on a carry overflow
|
558
|
+
return nil if i == 1
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
"#{matches[1]}.#{matches[2]}.#{matches[3]}.#{matches[4]}"
|
563
|
+
end
|
564
|
+
|
565
|
+
def expand_stability(stability)
|
566
|
+
stability = stability.downcase
|
567
|
+
case stability
|
568
|
+
when 'a'
|
569
|
+
'alpha'
|
570
|
+
when 'b'
|
571
|
+
'beta'
|
572
|
+
when 'p', 'pl'
|
573
|
+
'patch'
|
574
|
+
when 'rc'
|
575
|
+
'RC'
|
576
|
+
else
|
577
|
+
stability
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|