packo 0.0.1.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/bin/packo +78 -0
  2. data/bin/packo-base +24 -0
  3. data/bin/packo-build +24 -0
  4. data/bin/packo-db +24 -0
  5. data/bin/packo-env +24 -0
  6. data/bin/packo-files +24 -0
  7. data/bin/packo-profile +24 -0
  8. data/bin/packo-repository +24 -0
  9. data/bin/packo-select +24 -0
  10. data/lib/packo.rb +186 -0
  11. data/lib/packo/cli.rb +79 -0
  12. data/lib/packo/cli/base.rb +554 -0
  13. data/lib/packo/cli/build.rb +488 -0
  14. data/lib/packo/cli/database.rb +55 -0
  15. data/lib/packo/cli/database/definition.rb +65 -0
  16. data/lib/packo/cli/database/helpers.rb +20 -0
  17. data/lib/packo/cli/environment.rb +45 -0
  18. data/lib/packo/cli/files.rb +153 -0
  19. data/lib/packo/cli/profile.rb +31 -0
  20. data/lib/packo/cli/repository.rb +561 -0
  21. data/lib/packo/cli/repository/binary.rb +74 -0
  22. data/lib/packo/cli/repository/helpers.rb +20 -0
  23. data/lib/packo/cli/repository/repository.rb +48 -0
  24. data/lib/packo/cli/repository/source.rb +91 -0
  25. data/lib/packo/cli/repository/virtual.rb +53 -0
  26. data/lib/packo/cli/select.rb +61 -0
  27. data/lib/packo/do.rb +249 -0
  28. data/lib/packo/environment.rb +269 -0
  29. data/lib/packo/extensions.rb +70 -0
  30. data/lib/packo/flags.rb +64 -0
  31. data/lib/packo/host.rb +121 -0
  32. data/lib/packo/models.rb +171 -0
  33. data/lib/packo/models/installed_package.rb +190 -0
  34. data/lib/packo/models/installed_package/content.rb +50 -0
  35. data/lib/packo/models/installed_package/dependency.rb +39 -0
  36. data/lib/packo/models/repository.rb +209 -0
  37. data/lib/packo/models/repository/binary.rb +32 -0
  38. data/lib/packo/models/repository/binary/mirror.rb +35 -0
  39. data/lib/packo/models/repository/package.rb +73 -0
  40. data/lib/packo/models/repository/package/binary.rb +34 -0
  41. data/lib/packo/models/repository/package/binary/build.rb +36 -0
  42. data/lib/packo/models/repository/package/source.rb +36 -0
  43. data/lib/packo/models/repository/package/source/feature.rb +36 -0
  44. data/lib/packo/models/repository/package/source/flavor.rb +36 -0
  45. data/lib/packo/models/repository/package/virtual.rb +32 -0
  46. data/lib/packo/models/repository/source.rb +30 -0
  47. data/lib/packo/models/repository/virtual.rb +30 -0
  48. data/lib/packo/models/selector.rb +34 -0
  49. data/lib/packo/models/tag.rb +30 -0
  50. data/lib/packo/package.rb +239 -0
  51. data/lib/packo/package/blocker.rb +100 -0
  52. data/lib/packo/package/blockers.rb +40 -0
  53. data/lib/packo/package/dependencies.rb +40 -0
  54. data/lib/packo/package/dependency.rb +100 -0
  55. data/lib/packo/package/feature.rb +55 -0
  56. data/lib/packo/package/features.rb +105 -0
  57. data/lib/packo/package/flavor.rb +101 -0
  58. data/lib/packo/package/tags.rb +60 -0
  59. data/lib/packo/package/tags/expression.rb +130 -0
  60. data/lib/packo/package/tags/expression/group.rb +28 -0
  61. data/lib/packo/package/tags/expression/logic.rb +48 -0
  62. data/lib/packo/package/tags/expression/name.rb +28 -0
  63. data/lib/packo/profile.rb +174 -0
  64. data/lib/packo/rbuild.rb +22 -0
  65. data/lib/packo/rbuild/behaviors.rb +20 -0
  66. data/lib/packo/rbuild/behaviors/default.rb +27 -0
  67. data/lib/packo/rbuild/feature.rb +64 -0
  68. data/lib/packo/rbuild/features.rb +65 -0
  69. data/lib/packo/rbuild/flavor.rb +67 -0
  70. data/lib/packo/rbuild/module.rb +40 -0
  71. data/lib/packo/rbuild/modules.rb +27 -0
  72. data/lib/packo/rbuild/modules/building.rb +24 -0
  73. data/lib/packo/rbuild/modules/building/autotools.rb +315 -0
  74. data/lib/packo/rbuild/modules/building/cmake.rb +38 -0
  75. data/lib/packo/rbuild/modules/building/patch.rb +84 -0
  76. data/lib/packo/rbuild/modules/building/rake.rb +74 -0
  77. data/lib/packo/rbuild/modules/building/strip.rb +41 -0
  78. data/lib/packo/rbuild/modules/misc.rb +26 -0
  79. data/lib/packo/rbuild/modules/misc/fetcher.rb +177 -0
  80. data/lib/packo/rbuild/modules/misc/fetching.rb +22 -0
  81. data/lib/packo/rbuild/modules/misc/fetching/git.rb +57 -0
  82. data/lib/packo/rbuild/modules/misc/fetching/github.rb +31 -0
  83. data/lib/packo/rbuild/modules/misc/fetching/gnu.rb +59 -0
  84. data/lib/packo/rbuild/modules/misc/fetching/mercurial.rb +51 -0
  85. data/lib/packo/rbuild/modules/misc/fetching/sourceforge.rb +47 -0
  86. data/lib/packo/rbuild/modules/misc/fetching/subversion.rb +51 -0
  87. data/lib/packo/rbuild/modules/misc/fetching/wget.rb +57 -0
  88. data/lib/packo/rbuild/modules/misc/unpacker.rb +70 -0
  89. data/lib/packo/rbuild/modules/misc/unpacking.rb +23 -0
  90. data/lib/packo/rbuild/modules/misc/unpacking/lzma.rb +34 -0
  91. data/lib/packo/rbuild/modules/misc/unpacking/tar.rb +35 -0
  92. data/lib/packo/rbuild/modules/misc/unpacking/xz.rb +34 -0
  93. data/lib/packo/rbuild/modules/misc/unpacking/zip.rb +26 -0
  94. data/lib/packo/rbuild/modules/packaging.rb +20 -0
  95. data/lib/packo/rbuild/modules/packaging/pko.rb +78 -0
  96. data/lib/packo/rbuild/package.rb +281 -0
  97. data/lib/packo/rbuild/package/manifest.rb +169 -0
  98. data/lib/packo/rbuild/stages.rb +171 -0
  99. data/lib/packo/rbuild/stages/callbacks.rb +96 -0
  100. data/lib/packo/rbuild/stages/stage.rb +50 -0
  101. data/lib/packo/repository.rb +95 -0
  102. data/lib/packo/repository/binary.rb +116 -0
  103. data/lib/packo/repository/source.rb +77 -0
  104. data/lib/packo/repository/virtual.rb +66 -0
  105. data/lib/packo/system.rb +53 -0
  106. data/lib/packo/version.rb +26 -0
  107. metadata +276 -0
@@ -0,0 +1,55 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of packo.
5
+ #
6
+ # packo is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # packo is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'packo/system'
21
+ require 'packo/cli'
22
+
23
+ module Packo; module CLI
24
+
25
+ class Database < Thor
26
+ include Thor::Actions
27
+ include Database::Helpers
28
+
29
+ class_option :help, type: :boolean, desc: 'Show help usage'
30
+
31
+ desc 'export TYPE [DATA...] [OPTIONS]', 'Export a database'
32
+ map '-e' => :export
33
+ method_option :output, type: :string, aliases: '-o', desc: 'Output to a file instead of stdout'
34
+ def export (type, *data)
35
+ exported = Definition.new(type, *data).export
36
+
37
+ if options[:output]
38
+ file = File.new(options[:output])
39
+ file.write(exported)
40
+ file.close
41
+ else
42
+ puts exported
43
+ end
44
+ end
45
+
46
+ desc 'import FILE... [OPTIONS]', 'Import an exported database'
47
+ map '-i' => :import
48
+ def import (*files)
49
+ files.each {|file|
50
+ Definition.open(file).import
51
+ }
52
+ end
53
+ end
54
+
55
+ end; end
@@ -0,0 +1,65 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of packo.
5
+ #
6
+ # packo is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # packo is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'packo/models'
21
+
22
+ module Packo; module CLI; class Database < Thor; module Helpers
23
+
24
+ class Definition
25
+ def self.parse (data)
26
+ Definition.new(*JSON.parse(data))
27
+ end
28
+
29
+ def self.import
30
+
31
+ def self.open (path)
32
+ data = File.read(path)
33
+
34
+ Definition.parse(LZMA.decompress(data) rescue data)
35
+ end
36
+
37
+ attr_reader :type, :data
38
+
39
+ def initialize (type, *data)
40
+ @type = type
41
+ @data = data
42
+ end
43
+
44
+ def export
45
+
46
+ end
47
+
48
+ def import
49
+ query = ''
50
+
51
+ query << 'BEGIN;'
52
+
53
+ query << 'COMMIT;'
54
+
55
+ DataMapper.repository.adapter.execute(query)
56
+ end
57
+
58
+ private
59
+
60
+ def export
61
+
62
+ end
63
+ end
64
+
65
+ end; end; end; end
@@ -0,0 +1,20 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of packo.
5
+ #
6
+ # packo is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # packo is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'packo/cli/database/definition'
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
4
+ #
5
+ # This file is part of packo.
6
+ #
7
+ # packo is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # packo is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
19
+ #++
20
+
21
+ require 'packo'
22
+
23
+ module Packo; module CLI
24
+
25
+ class Environment < Thor
26
+ include Thor::Actions
27
+
28
+ class_option :help, type: :boolean, desc: 'Show help usage'
29
+
30
+ desc 'show', 'Show the current system environment'
31
+ def show
32
+ length = System.env!.map {|(name, value)| name.length}.max
33
+
34
+ System.env!.each {|(name, value)|
35
+ puts "#{name}#{' ' * (1 + length - name.length)}= #{value}" if value && !value.to_s.empty?
36
+ }
37
+ end
38
+
39
+ desc 'update', 'Update the environment'
40
+ def update
41
+
42
+ end
43
+ end
44
+
45
+ end; end
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
4
+ #
5
+ # This file is part of packo.
6
+ #
7
+ # packo is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # packo is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
19
+ #++
20
+
21
+ require 'packo'
22
+
23
+ module Packo; module CLI
24
+
25
+ class Files < Thor
26
+ include Thor::Actions
27
+
28
+ class_option :help, type: :boolean, desc: 'Show help usage'
29
+
30
+ desc 'package PACKAGE', 'Get a file list of a given package'
31
+ def package (name)
32
+ if name.end_with?('.pko')
33
+ require 'packo/rbuild'
34
+
35
+ path = "#{System.env[:TMP]}/.__packo_unpacked/#{File.basename(name)}"
36
+ RBuild::Modules::Packaging::PKO.unpack(File.realpath(name), path)
37
+
38
+ length = "#{path}/dist".length
39
+
40
+ Find.find("#{path}/dist") {|file|
41
+ type = nil
42
+ path = "/#{file[length, file.length]}".gsub(%r{/*/}, '/').sub(%r{/$}, '')
43
+ meta = nil
44
+
45
+ if File.directory? file
46
+ type = :dir
47
+ elsif File.symlink? file
48
+ type = :sym
49
+ meta = File.readlink file
50
+ elsif File.file? file
51
+ type = :obj
52
+ end
53
+
54
+ case type
55
+ when :dir; puts "--- #{path if path != '/'}/"
56
+ when :sym; puts ">>> #{path} -> #{meta}".cyan.bold
57
+ when :obj; puts ">>> #{path}".bold
58
+ end
59
+ }
60
+ else
61
+ require 'packo/models'
62
+
63
+ package = Models.search_installed(name).first
64
+ root = Pathname.new(package.destination || '/')
65
+
66
+ if !package
67
+ fatal "No package matches #{name}"
68
+ exit! 10
69
+ end
70
+
71
+ package.model.contents.each {|content| content.check!
72
+ case content.type
73
+ when :dir; puts "--- #{(root + content.path).cleanpath}"
74
+ when :sym; puts ">>> #{(root + content.path).cleanpath} -> #{content.meta}".cyan.bold
75
+ when :obj; puts ">>> #{(root + content.path).cleanpath}".bold
76
+ end
77
+ }
78
+ end
79
+ end
80
+
81
+ desc 'belongs FILE', 'Find out to what package a path belongs'
82
+ def belongs (file)
83
+ require 'packo/models'
84
+
85
+ path = Pathname.new(file).realpath.to_s
86
+ path[0] = ''
87
+
88
+ if content = Models::InstalledPackage::Content.first(path: path)
89
+ puts Package.wrap(content.installed_package).to_s
90
+ else
91
+ exit 1
92
+ end
93
+ end
94
+
95
+ desc 'check [PACKAGE...]', 'Check contents for the given packages'
96
+ def check (*names)
97
+ require 'packo/models'
98
+
99
+ packages = []
100
+
101
+ if names.empty?
102
+ packages << Models::InstalledPackage.all.map {|pkg|
103
+ Package.wrap(pkg)
104
+ }
105
+ else
106
+ names.each {|name|
107
+ packages << Models.search_installed(name)
108
+ }
109
+ end
110
+
111
+ packages.flatten.compact.each {|package|
112
+ print "[#{package.repository.black.bold}] " if package.repository
113
+ print "#{package.tags}/" unless package.tags.empty?
114
+ print package.name.bold
115
+ print "-#{package.version.to_s.red}"
116
+ print " (#{package.slot.to_s.blue.bold})" if package.slot
117
+ print " [#{package.features}]" unless package.features.empty?
118
+ print " {#{package.flavor}}" unless package.flavor.empty?
119
+ print "\n"
120
+
121
+ package.model.contents.each {|content|
122
+ path = (Pathname.new(package.model.destination || '/') + content.path[1, content.path.length]).cleanpath.to_s
123
+
124
+ case content.type
125
+ when :dir
126
+ if !(File.directory?(path) rescue false)
127
+ puts "#{'FAIL ' if System.env[:NO_COLORS]}--- #{path}#{'/' if path != '/'}".red
128
+ else
129
+ puts "#{'OK ' if System.env[:NO_COLORS]}--- #{path}#{'/' if path != '/'}".green
130
+ end
131
+
132
+ when :sym
133
+ if content.meta != (File.readlink(path) rescue nil)
134
+ puts "#{'FAIL ' if System.env[:NO_COLORS]}>>> #{path} -> #{content.meta}".red
135
+ else
136
+ puts "#{'OK ' if System.env[:NO_COLORS]}>>> #{path} -> #{content.meta}".green
137
+ end
138
+
139
+ when :obj
140
+ if content.meta != (Do.digest(path) rescue nil)
141
+ puts "#{'FAIL ' if System.env[:NO_COLORS]}>>> #{path}".red
142
+ else
143
+ puts "#{'OK ' if System.env[:NO_COLORS]}>>> #{path}".green
144
+ end
145
+ end
146
+ }
147
+
148
+ puts ''
149
+ }
150
+ end
151
+ end
152
+
153
+ end; end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
4
+ #
5
+ # This file is part of packo.
6
+ #
7
+ # packo is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # packo is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
19
+ #++
20
+
21
+ require 'packo'
22
+
23
+ module Packo; module CLI
24
+
25
+ class Profile < Thor
26
+ include Thor::Actions
27
+
28
+ class_option :help, type: :boolean, desc: 'Show help usage'
29
+ end
30
+
31
+ end; end
@@ -0,0 +1,561 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
4
+ #
5
+ # This file is part of packo.
6
+ #
7
+ # packo is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # packo is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with packo. If not, see <http://www.gnu.org/licenses/>.
19
+ #++
20
+
21
+ require 'open-uri'
22
+ require 'nokogiri'
23
+
24
+ require 'packo'
25
+ require 'packo/rbuild'
26
+ require 'packo/models'
27
+ require 'packo/cli/repository/helpers'
28
+
29
+ module Packo; module CLI
30
+
31
+ class Repository < Thor
32
+ include Thor::Actions
33
+
34
+ @@scm = ['git']
35
+
36
+ class_option :help, type: :boolean, desc: 'Show help usage'
37
+
38
+ desc 'add URI...', 'Add repositories'
39
+ map '-a' => :add
40
+ def add (*uris)
41
+ uris.each {|uri|
42
+ uri = URI.parse(uri)
43
+ kind = nil
44
+ type = nil
45
+ name = nil
46
+
47
+ if uri.scheme.nil? || uri.scheme == 'file'
48
+ kind = :file
49
+
50
+ if uri.to_s.end_with?('.rb')
51
+ uri = File.realpath(uri.path)
52
+
53
+ type = :virtual
54
+ name = File.basename(uri.to_s).sub('.rb', '')
55
+ else
56
+ if File.directory? uri.path
57
+ dom = Nokogiri::XML.parse(File.read("#{uri.path}/repository.xml"))
58
+ else
59
+ dom = Nokogiri::XML.parse(File.read(uri.path))
60
+ end
61
+
62
+ uri = File.realpath(uri.path)
63
+
64
+ type = dom.root['type'].to_sym
65
+ name = dom.root['name']
66
+ end
67
+ elsif ['http', 'https', 'ftp'].member?(uri.scheme)
68
+ kind = :fetched
69
+
70
+ if uri.to_s.end_with?('.rb')
71
+ type = :virtual
72
+ name = File.basename(uri.to_s).sub('.rb', '')
73
+ else
74
+ xml = open(uri).read
75
+ dom = Nokogiri::XML.parse(xml)
76
+
77
+ type = dom.root['type'].to_sym
78
+ name = dom.root['name']
79
+ end
80
+ elsif @@scm.member?(uri.scheme)
81
+ kind = :scm
82
+
83
+ FileUtils.rm_rf("#{System.env[:TMP]}/.__repo", secure: true)
84
+
85
+ _checkout(uri, "#{System.env[:TMP]}/.__repo")
86
+
87
+ dom = Nokogiri::XML.parse(File.read("#{System.env[:TMP]}/.__repo/repository.xml"))
88
+
89
+ type = dom.root['type'].to_sym
90
+ name = dom.root['name']
91
+ end
92
+
93
+ if !kind
94
+ CLI.fatal "I don't know what to do with #{uri}"
95
+ next
96
+ end
97
+
98
+ path = "#{System.env[:REPOSITORIES]}/#{type}/#{name}"
99
+
100
+ if Models::Repository.first(type: type, name: name)
101
+ CLI.fatal "#{type}/#{name} already exists, delete it first"
102
+ exit 10
103
+ end
104
+
105
+ case type
106
+ when :binary
107
+ path << '.xml'
108
+
109
+ FileUtils.mkpath(File.dirname(path))
110
+ File.write(path, open((kind == :file && (!uri.to_s.end_with?('.xml'))) ?
111
+ "#{uri}/repository.xml" :
112
+ uri
113
+ ).read)
114
+
115
+ when :source
116
+ FileUtils.rm_rf path, secure: true rescue nil
117
+ FileUtils.mkpath path rescue nil
118
+
119
+ case kind
120
+ when :fetched
121
+ _checkout(dom.xpath('//address').first.text, path)
122
+
123
+ when :scm
124
+ FileUtils.cp_r "#{System.env[:TMP]}/.__repo/.", path, preserve: true
125
+
126
+ else
127
+ _checkout(uri.to_s, path)
128
+ end
129
+
130
+ when :virtual
131
+ path << '.rb'
132
+
133
+ FileUtils.mkpath(File.dirname(path))
134
+ File.write(path, open((kind == :file && (!uri.to_s.end_with?('.rb'))) ?
135
+ "#{uri}/repository.rb" :
136
+ uri
137
+ ).read)
138
+ end
139
+
140
+ begin
141
+ Models.transaction {
142
+ _add type, name, uri, path
143
+ }
144
+
145
+ CLI.info "Added #{type}/#{name}"
146
+ rescue Exception => e
147
+ CLI.fatal 'Failed to add the cache'
148
+
149
+ Packo.debug e
150
+ end
151
+ }
152
+ end
153
+
154
+
155
+ desc 'delete REPOSITORY...', 'Delete installed repositories'
156
+ map '-d' => :delete, '-R' => :delete
157
+ def delete (*names)
158
+ names.each {|name|
159
+ repository = Packo::Repository.parse(name)
160
+
161
+ if repository.type && !Packo::Repository::Types.member?(repository.type)
162
+ CLI.fatal "#{repository.type} is not a valid repository type"
163
+ exit 20
164
+ end
165
+
166
+ conditions = Hash[name: repository.name]
167
+ conditions[:type] = repository.type if repository.type
168
+
169
+ repositories = Models::Repository.all(conditions)
170
+
171
+ if repositories.empty?
172
+ CLI.fatal "#{repository.type}/#{repository.name} doesn't exist"
173
+ exit 21
174
+ end
175
+
176
+ CLI.info "Deleting #{[repository.type, repository.name].join('/')}"
177
+
178
+ begin
179
+ repositories.each {|repository|
180
+ Models.transaction {
181
+ _delete(repository.type, repository.name)
182
+
183
+ FileUtils.rm_rf repository.path, secure: true
184
+ }
185
+ }
186
+ rescue Exception => e
187
+ CLI.fatal "Something went wrong while deleting #{name}"
188
+
189
+ Packo.debug e
190
+ end
191
+ }
192
+ end
193
+
194
+ desc 'update', 'Update installed repositories'
195
+ map '-u' => :update
196
+ method_option :force, type: :boolean, default: false, aliases: '-f', desc: 'Force the update'
197
+ def update
198
+ Models::Repository.all.each {|repository|
199
+ updated = false
200
+
201
+ type = repository.type
202
+ name = repository.name
203
+ uri = repository.uri.to_s
204
+ path = repository.path
205
+
206
+ Models.transaction {
207
+ case repository.type
208
+ when :binary
209
+ if (content = open(uri).read) != File.read(path) || options[:force]
210
+ _delete(:binary, name)
211
+ File.write(path, content)
212
+ _add(:binary, name, uri, path)
213
+
214
+ updated = true
215
+ end
216
+
217
+ when :source
218
+ if _update(path) || options[:force]
219
+ _delete(:source, name)
220
+ _add(:source, name, uri, path)
221
+
222
+ updated = true
223
+ end
224
+
225
+ when :virtual
226
+ end
227
+ }
228
+
229
+ if updated
230
+ CLI.info "Updated #{type}/#{name}"
231
+ else
232
+ CLI.info "#{type}/#{name} already up to date"
233
+ end
234
+ }
235
+ end
236
+
237
+ desc 'search [EXPRESSION] [OPTIONS]', 'Search packages with the given expression'
238
+ map '--search' => :search, '-Ss' => :search
239
+ method_option :exact, type: :boolean, default: false, aliases: '-e', desc: 'Search for the exact name'
240
+ method_option :full, type: :boolean, default: false, aliases: '-F', desc: 'Include the repository that owns the package'
241
+ method_option :type, type: :string, aliases: '-t', desc: 'The repository type'
242
+ method_option :repository, type: :string, aliases: '-r', desc: 'Set a specific repository'
243
+ def search (expression='')
244
+ Models.search(expression, options[:exact], options[:repository], options[:type]).group_by {|package|
245
+ "#{package.tags}/#{package.name}"
246
+ }.sort.each {|(name, packages)|
247
+ if options[:full]
248
+ packages.group_by {|package|
249
+ "#{package.repository.type}/#{package.repository.name}"
250
+ }.each {|name, packages|
251
+ print "#{"#{packages.first.tags}/" unless packages.first.tags.empty?}#{packages.first.name.bold}"
252
+
253
+ print ' ('
254
+ print packages.map {|package|
255
+ "#{package.version.to_s.red}" + (package.slot ? "%#{package.slot.to_s.blue.bold}" : '')
256
+ }.join(', ')
257
+ print ')'
258
+
259
+ print " <#{"#{package.repository.type}/#{package.repository.name}".black.bold} | #{package.repository.uri} | #{package.repository.path}>"
260
+ }
261
+ else
262
+ print "#{packages.first.tags}/#{packages.first.name.bold} ("
263
+
264
+ print packages.map {|package|
265
+ "#{package.version.to_s.red}" + (package.slot ? "%#{package.slot.to_s.blue.bold}" : '')
266
+ }.join(', ')
267
+
268
+ print ")"
269
+ end
270
+
271
+ print "\n"
272
+ }
273
+ end
274
+
275
+ desc 'info [EXPRESSION] [OPTIONS]', 'Search packages with the given expression and return detailed informations about them'
276
+ map '--info' => :info, '-I' => :info
277
+ method_option :exact, type: :boolean, default: false, aliases: '-e', desc: 'Search for the exact name'
278
+ method_option :type, type: :string, aliases: '-t', desc: 'The repository type'
279
+ method_option :repository, type: :string, aliases: '-r', desc: 'Set a specific repository'
280
+ def info (expression='')
281
+ Models.search(expression, options[:exact], options[:repository], options[:type]).group_by {|package|
282
+ package.name
283
+ }.sort.each {|(name, packages)|
284
+ packages.sort {|a, b|
285
+ a.version <=> b.version
286
+ }.each {|package|
287
+ print "<#{"source/#{package.repository.name}".black.bold}> "
288
+ print package.name.bold
289
+ print "-#{package.version.to_s.red}"
290
+ print " {#{package.revision.yellow.bold}}" if package.revision > 0
291
+ print " (#{package.slot.blue.bold})" if package.slot
292
+ print " [#{package.tags.join(' ').magenta}]" if !package.tags.empty?
293
+ print "\n"
294
+
295
+ puts " #{'Description'.green}: #{package.description}" if package.description
296
+ puts " #{'Homepage'.green}: #{package.homepage}" if package.homepage
297
+ puts " #{'License'.green}: #{package.license}" if package.license
298
+ puts " #{'Maintainer'.green}: #{package.model.maintainer}" if package.maintainer
299
+
300
+ case package.repository.type
301
+ when :binary
302
+ puts " #{'Features'.green}: #{package.features.to_a.select {|f| f.enabled?}.map {|f| f.name}.join(' ')}"
303
+
304
+ print " #{'Builds'.green}: "
305
+ package.model.data.builds.each {|build|
306
+ print 'With '
307
+
308
+ if !build.features.empty?
309
+ print build.features.bold
310
+ else
311
+ print 'nothing'
312
+ end
313
+
314
+ print " in #{build.flavor.bold} flavor" if build.flavor
315
+ print " (SHA1 #{build.digest})".black.bold if build.digest
316
+ print "\n "
317
+ }
318
+
319
+ when :source
320
+ length = (package.model.data.flavor.to_a + package.model.data.features.to_a).map {|f|
321
+ f.name.length
322
+ }.max
323
+
324
+ if package.model.data.flavor.length > 0
325
+ print " #{'Flavor'.green}: "
326
+
327
+ flavor = package.model.data.flavor
328
+
329
+ flavor.each {|element|
330
+ if element.enabled
331
+ print "#{element.name.white.bold}#{System.env[:NO_COLORS] ? '!' : ''}"
332
+ else
333
+ print element.name.black.bold
334
+ end
335
+
336
+ print "#{' ' * (4 + length - element.name.length + (System.env[:NO_COLORS] && !element.enabled ? 1 : 0))}#{element.description || '...'}"
337
+
338
+ print "\n "
339
+ }
340
+
341
+ print "\r" if package.model.data.features.length > 0
342
+ end
343
+
344
+ if package.model.data.features.length > 0
345
+ print " #{'Features'.green}: "
346
+
347
+ features = package.model.data.features
348
+
349
+ features.each {|feature|
350
+ if feature.enabled
351
+ print "#{feature.name.white.bold}#{System.env[:NO_COLORS] ? '!' : ''}"
352
+ else
353
+ print feature.name.black.bold
354
+ end
355
+
356
+ print "#{' ' * (4 + length - feature.name.length + (System.env[:NO_COLORS] && !feature.enabled ? 1 : 0))}#{feature.description || '...'}"
357
+
358
+ print "\n "
359
+ }
360
+ end
361
+ end
362
+
363
+ print "\n"
364
+ }
365
+ }
366
+ end
367
+
368
+ desc 'list [TYPE]', 'List installed repositories'
369
+ def list (type='all')
370
+ if Packo::Repository::Types.member?(type.to_sym)
371
+ CLI.info "Installed #{type} repositories:"
372
+
373
+ repositories = Models::Repository.all(type: type)
374
+ length = repositories.map {|repository| "#{repository.type}/#{repository.name}".length}.max
375
+
376
+ repositories.each {|repository|
377
+ puts " #{repository.type}/#{repository.name}#{' ' * (4 + length - "#{repository.type}/#{repository.name}".length)}#{repository.uri} (#{repository.path})"
378
+ }
379
+
380
+ puts ''
381
+ elsif type == 'all'
382
+ Packo::Repository::Types.each {|type|
383
+ list(type)
384
+ }
385
+ end
386
+ end
387
+
388
+ desc 'path REPOSITORY', 'Output the path of a given repository'
389
+ def path (name)
390
+ repository = Models::Repository.first(Packo::Repository.parse(name).to_hash)
391
+
392
+ exit if !repository
393
+
394
+ puts repository.path
395
+ end
396
+
397
+ desc 'uri REPOSITORY', 'Output the URI of a given package'
398
+ def uri (name)
399
+ repository = Models::Repository.first(Packo::Repository.parse(name).to_hash)
400
+
401
+ exit if !repository
402
+
403
+ puts repository.URI
404
+ end
405
+
406
+ desc 'rehash REPOSITORY...', 'Rehash the repository caches'
407
+ def rehash (*names)
408
+ repositories = []
409
+
410
+ if names.empty?
411
+ repositories << Models::Repository.all
412
+ else
413
+ names.each {|name|
414
+ repositories << Models::Repository.all(name: name)
415
+ }
416
+ end
417
+
418
+ repositories.flatten.compact.each {|repository|
419
+ type = repository.type
420
+ name = repository.name
421
+ uri = repository.uri
422
+ path = repository.path
423
+
424
+ CLI.info "Rehashing #{type}/#{name}"
425
+
426
+ Models.transaction {
427
+ _delete(type, name)
428
+
429
+ case type
430
+ when :binary
431
+ _add(:binary, name, uri, path)
432
+
433
+ when :source
434
+ _add(:source, name, uri, path)
435
+ end
436
+ }
437
+ }
438
+ end
439
+
440
+ desc 'generate REPOSITORY [OPTIONS]', 'Generate a binary repository from sources'
441
+ method_option :repository, type: :string, aliases: '-r', desc: 'Specify a source repository from where to get packages'
442
+ method_option :output, type: :string, default: System.env[:TMP], aliases: '-o', desc: 'Specify output directory'
443
+ def generate (repository)
444
+ dom = Nokogiri::XML.parse(File.read(repository)) {|config|
445
+ config.default_xml.noblanks
446
+ }
447
+
448
+ dom.xpath('//packages/package').each {|e|
449
+ CLI.info "Generating #{Packo::Package.new(tags: e['tags'].split(/\s+/), name: e['name'])}".bold if System.env[:VERBOSE]
450
+
451
+ e.xpath('.//build').each {|build|
452
+ package = Package.new(
453
+ tags: e['tags'],
454
+ name: e['name'],
455
+ version: build.parent['name'],
456
+ slot: (build.parent.parent.name == 'slot') ? build.parent.parent['name'] : nil,
457
+
458
+ repository: options[:repository]
459
+ )
460
+
461
+ package.flavor = (build.xpath('.//flavor').first.text rescue '')
462
+ package.features = (build.xpath('.//features').first.text rescue '')
463
+
464
+ next if File.exists?("#{options[:output]}/#{dom.root['name']}/#{package.tags.to_s(true)}/" +
465
+ "#{package.name}-#{package.version}#{"%#{package.slot}" if package.slot}" +
466
+ "#{"+#{package.flavor.to_s(:package)}" if !package.flavor.to_s(:package).empty?}" +
467
+ "#{"-#{package.features.to_s(:package)}" if !package.features.to_s(:package).empty?}" +
468
+ '.pko'
469
+ )
470
+
471
+ begin
472
+ pko = _build(package,
473
+ FLAVOR: package.flavor,
474
+ FEATURES: package.features
475
+ )
476
+
477
+ build.xpath('.//digest').each {|node| node.remove}
478
+ build.add_child dom.create_element('digest', Do.digest(pko))
479
+
480
+ FileUtils.mkpath "#{options[:output]}/#{dom.root['name']}/#{package.tags.to_s(true)}"
481
+ FileUtils.mv pko, "#{options[:output]}/#{dom.root['name']}/#{package.tags.to_s(true)}"
482
+ rescue Exception => e
483
+ Packo.debug e
484
+ end
485
+
486
+ File.write(repository, dom.to_xml(indent: 4))
487
+ }
488
+ }
489
+ end
490
+
491
+ private
492
+
493
+ def _build (package, env)
494
+ Do.cd {
495
+ FileUtils.rm_rf "#{System.env[:TMP]}/.__packo_build", secure: true rescue nil
496
+ FileUtils.mkpath "#{System.env[:TMP]}/.__packo_build" rescue nil
497
+
498
+ require 'packo/cli/build'
499
+
500
+ begin
501
+ System.env.sandbox(env) {
502
+ Packo::CLI::Build.start(['package', package.to_s(:whole), "--output=#{System.env[:TMP]}/.__packo_build", "--repository=#{package.repository}"])
503
+ }
504
+ rescue
505
+ end
506
+
507
+ Dir.glob("#{System.env[:TMP]}/.__packo_build/#{package.name}-#{package.version}*.pko").first
508
+ }
509
+ end
510
+
511
+ def _add (type, name, uri, path)
512
+ Helpers::Repository.wrap(Models::Repository.create(
513
+ type: type,
514
+ name: name,
515
+
516
+ uri: uri,
517
+ path: path
518
+ )).populate
519
+ end
520
+
521
+ def _delete (type, name)
522
+ Models::Repository.first(name: name, type: type).destroy
523
+ end
524
+
525
+ def _checkout (uri, path)
526
+ uri = URI.parse(uri.to_s) if !uri.is_a?(URI)
527
+
528
+ if !uri.scheme
529
+ if File.directory?("#{uri}/.git")
530
+ scm = 'git'
531
+ end
532
+ else
533
+ scm = uri.scheme
534
+ end
535
+
536
+ if !@@scm.member?(scm)
537
+ CLI.fatal "#{scm} is an unsupported SCM"
538
+ exit 40
539
+ end
540
+
541
+ case scm
542
+ when 'git'; Packo.sh 'git', 'clone', '--depth', '1', uri.to_s, path, silent: !System.env[:VERBOSE]
543
+ end
544
+ end
545
+
546
+ def _update (path)
547
+ result = false
548
+
549
+ old = Dir.pwd; Dir.chdir(path)
550
+
551
+ if !result && (`git reset --hard`) && (`git pull`.strip != 'Already up-to-date.' rescue nil)
552
+ result = true
553
+ end
554
+
555
+ Dir.chdir(old)
556
+
557
+ return result
558
+ end
559
+ end
560
+
561
+ end; end