php-composer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|