akabei 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +55 -0
  8. data/Rakefile +14 -0
  9. data/akabei.gemspec +29 -0
  10. data/bin/akabei +9 -0
  11. data/lib/akabei.rb +5 -0
  12. data/lib/akabei/abs.rb +60 -0
  13. data/lib/akabei/archive_utils.rb +75 -0
  14. data/lib/akabei/attr_path.rb +20 -0
  15. data/lib/akabei/builder.rb +108 -0
  16. data/lib/akabei/chroot_tree.rb +81 -0
  17. data/lib/akabei/cli.rb +172 -0
  18. data/lib/akabei/error.rb +4 -0
  19. data/lib/akabei/package.rb +174 -0
  20. data/lib/akabei/package_entry.rb +110 -0
  21. data/lib/akabei/package_info.rb +68 -0
  22. data/lib/akabei/repository.rb +156 -0
  23. data/lib/akabei/signer.rb +75 -0
  24. data/lib/akabei/thor_handler.rb +14 -0
  25. data/lib/akabei/version.rb +3 -0
  26. data/spec/akabei/abs_spec.rb +95 -0
  27. data/spec/akabei/archive_utils_spec.rb +44 -0
  28. data/spec/akabei/builder_spec.rb +129 -0
  29. data/spec/akabei/chroot_tree_spec.rb +108 -0
  30. data/spec/akabei/cli_spec.rb +5 -0
  31. data/spec/akabei/package_entry_spec.rb +70 -0
  32. data/spec/akabei/package_info_spec.rb +17 -0
  33. data/spec/akabei/package_spec.rb +54 -0
  34. data/spec/akabei/repository_spec.rb +157 -0
  35. data/spec/akabei/signer_spec.rb +5 -0
  36. data/spec/data/input/abs.tar.gz +0 -0
  37. data/spec/data/input/htop-vi.tar.gz +0 -0
  38. data/spec/data/input/makepkg.x86_64.conf +140 -0
  39. data/spec/data/input/nkf-2.1.3-1-x86_64.pkg.tar.xz +0 -0
  40. data/spec/data/input/nkf.PKGINFO +22 -0
  41. data/spec/data/input/nkf.tar.gz +0 -0
  42. data/spec/data/input/pacman.x86_64.conf +99 -0
  43. data/spec/data/input/ruby.PKGINFO +38 -0
  44. data/spec/data/input/test.db +0 -0
  45. data/spec/data/input/test.files +0 -0
  46. data/spec/integration/build_spec.rb +105 -0
  47. data/spec/spec_helper.rb +110 -0
  48. metadata +225 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 573c7770c6f4880b4805deecf432dd1d893435fa
4
+ data.tar.gz: 5ba638d8e424de0d86b570ee3f4060d15100d08d
5
+ SHA512:
6
+ metadata.gz: a89b4bdce9f46c869a90e255f07d124bd3f27bdea251de7593a5d93e938532b07aab9aea16f882148e5e684ef0576b65ac0db5e125d551c7350094cb4cb200a9
7
+ data.tar.gz: b93ee03dffba2e814da2f38e6077b4fa4865776f658ada381af69df06afd7d1b165ddec623e34aab4a3755a0ef537b7c4aeafaf00fffa6145290f72c606758c4
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - ruby-head
6
+ before_install:
7
+ - sudo apt-get install -y libarchive-dev
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in akabei.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kohei Suzuki
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ # Akabei
2
+ [![Build Status](https://secure.travis-ci.org/eagletmt/akabei.png)](https://travis-ci.org/eagletmt/akabei)
3
+ [![Code Climate](https://codeclimate.com/github/eagletmt/akabei.png)](https://codeclimate.com/github/eagletmt/akabei)
4
+ [![Coverage Status](https://coveralls.io/repos/eagletmt/akabei/badge.png)](https://coveralls.io/r/eagletmt/akabei)
5
+
6
+ Custom repository manager for ArchLinux pacman.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'akabei'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install akabei
21
+
22
+ ## Usage
23
+ ### Build a package and store it to a repository
24
+ Basic usage: build foo x86_64 package for bar repository.
25
+
26
+ ```
27
+ % ls foo
28
+ PKGBUILD
29
+ % mkdir -p repo/x86_64
30
+ % akabei build foo --repo-dir repo/x86_64 --repo-name bar --arch x86_64
31
+ (snip)
32
+ % ls repo/x86_64
33
+ bar.abs.tar.gz bar.db bar.files foo-1.0.0-1-x86_64.pkg.tar.xz
34
+ ```
35
+
36
+ With full options:
37
+ ```
38
+ % [ -z $GPG_AGENT_INFO ] && eval `gpg-agent --daemon`
39
+ % akabei build foo --chroot-dir /var/cache/akabei/chroot-x86_64 --repo-dir repo/x86_64 --repo-name bar --arch x86_64 --package-key $GPGKEY --repo-key $GPGKEY --pacman-config pacman.x86_64.conf --makepkg-config makepkg.x86_64.conf --srcdest sources --logdest logs
40
+ (snip)
41
+ % ls repo/x86_64
42
+ bar.abs.tar.gz bar.db bar.db.sig bar.files bar.files.sig foo-1.0.0-1-x86_64.pkg.tar.xz foo-1.0.0-1-x86_64.pkg.tar.xz.sig
43
+ % ls sources
44
+ foo-1.0.0.tar.gz
45
+ % ls logs
46
+ foo-1.0.0-1-x86_64-build.log foo-1.0.0-1-x86_64-package.log
47
+ ```
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it ( https://github.com/eagletmt/akabei/fork )
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create new Pull Request
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :spec
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ namespace :spec do
8
+ task :set_arch do
9
+ ENV['AKABEI_ARCH_SPEC'] = '1'
10
+ end
11
+
12
+ desc 'Run RSpec examples including :arch. It requires sudo and devtools package.'
13
+ task :arch => %w[spec:set_arch spec]
14
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'akabei/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "akabei"
8
+ spec.version = Akabei::VERSION
9
+ spec.authors = ["Kohei Suzuki"]
10
+ spec.email = ["eagletmt@gmail.com"]
11
+ spec.summary = %q{Custom repository manager for ArchLinux pacman}
12
+ spec.description = %q{Custom repository manager for ArchLinux pacman}
13
+ spec.homepage = "https://github.com/eagletmt/akabei"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "gpgme"
22
+ spec.add_dependency "libarchive"
23
+ spec.add_dependency "thor"
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "coveralls"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec", "~> 3.0.0.beta1"
28
+ spec.add_development_dependency "simplecov"
29
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'akabei/cli'
3
+ require 'akabei/thor_handler'
4
+
5
+ exit_code = Akabei::ThorHandler.wrap do
6
+ Akabei::CLI.start(ARGV)
7
+ end
8
+
9
+ exit exit_code
@@ -0,0 +1,5 @@
1
+ require "akabei/version"
2
+
3
+ module Akabei
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,60 @@
1
+ require 'akabei/archive_utils'
2
+ require 'akabei/error'
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require 'tmpdir'
6
+
7
+ module Akabei
8
+ class Abs
9
+ def initialize(path, repo_name)
10
+ @path = Pathname.new(path)
11
+ @repo_name = repo_name
12
+ end
13
+
14
+ def add(dir, builder)
15
+ builder.with_source_package(dir) do |srcpkg|
16
+ Dir.mktmpdir do |tree|
17
+ tree = Pathname.new(tree)
18
+ root = tree.join(@repo_name)
19
+ root.mkpath
20
+ if @path.readable?
21
+ ArchiveUtils.extract_all(@path, tree)
22
+ end
23
+ pkgname = detect_pkgname(srcpkg)
24
+ FileUtils.rm_rf(root.join(pkgname).to_s)
25
+ ArchiveUtils.extract_all(srcpkg, root)
26
+ FileUtils.rm_f(@path.to_s)
27
+ ArchiveUtils.archive_all(tree, @path, Archive::COMPRESSION_GZIP, Archive::FORMAT_TAR)
28
+ end
29
+ end
30
+ end
31
+
32
+ def detect_pkgname(srcpkg)
33
+ ArchiveUtils.each_entry(srcpkg) do |entry|
34
+ if entry.directory?
35
+ if m = entry.pathname.match(%r{\A([^/]+)/\z})
36
+ return m[1]
37
+ end
38
+ end
39
+ end
40
+ raise Error.new("Cannot detect pkgname from #{srcpkg}")
41
+ end
42
+
43
+ def remove(package_name)
44
+ unless @path.readable?
45
+ raise Error.new("No such file: #{@path}")
46
+ end
47
+
48
+ Dir.mktmpdir do |tree|
49
+ tree = Pathname.new(tree)
50
+ ArchiveUtils.extract_all(@path, tree)
51
+ root = tree.join(@repo_name)
52
+ unless root.directory?
53
+ raise Error.new("No such repository: #{@repo_name}")
54
+ end
55
+ FileUtils.rm_rf(root.join(package_name))
56
+ ArchiveUtils.archive_all(tree, @path, Archive::COMPRESSION_GZIP, Archive::FORMAT_TAR)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,75 @@
1
+ require 'libarchive'
2
+
3
+ module Akabei
4
+ module ArchiveUtils
5
+ module_function
6
+
7
+ def each_entry(path, &block)
8
+ Archive.read_open_filename(path.to_s) do |archive|
9
+ while entry = archive.next_header
10
+ block.call(entry, archive)
11
+ end
12
+ end
13
+ end
14
+
15
+ def list_paths(path)
16
+ paths = []
17
+ each_entry(path) do |entry|
18
+ paths << entry.pathname
19
+ end
20
+ paths
21
+ end
22
+
23
+ BUFSIZ = 8192
24
+
25
+ def extract_all(src, dest)
26
+ each_entry(src) do |entry, archive|
27
+ path = dest.join(entry.pathname)
28
+ path.parent.mkpath
29
+ if entry.regular?
30
+ path.open('wb') do |f|
31
+ archive.read_data do |buf|
32
+ f.write(buf)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def archive_all(src, dest, comp, format)
40
+ Archive::Writer.open_filename(dest.to_s, comp, format) do |archive|
41
+ list_tree_paths(src).sort.each do |path|
42
+ archive.new_entry do |entry|
43
+ entry.pathname = path.relative_path_from(src).to_s
44
+ is_dir = path.directory?
45
+ if is_dir
46
+ entry.pathname += '/'
47
+ end
48
+ entry.copy_stat(path.to_s)
49
+ archive.write_header(entry)
50
+ unless is_dir
51
+ path.open do |f|
52
+ archive.write_data do
53
+ f.read(BUFSIZ)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def list_tree_paths(dir)
63
+ paths = []
64
+ q = dir.each_child.to_a
65
+ until q.empty?
66
+ path = q.shift
67
+ paths << path
68
+ if path.directory?
69
+ q += path.each_child.to_a
70
+ end
71
+ end
72
+ paths
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+
3
+ module Akabei
4
+ module AttrPath
5
+ def attr_path_writer(*attrs)
6
+ attrs.each do |attr|
7
+ define_method("#{attr}=") do |val|
8
+ unless val.nil?
9
+ instance_variable_set("@#{attr}", Pathname.new(val))
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ def attr_path_accessor(*attrs)
16
+ attr_reader *attrs
17
+ attr_path_writer *attrs
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,108 @@
1
+ require 'akabei/attr_path'
2
+ require 'akabei/error'
3
+ require 'akabei/package'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ module Akabei
8
+ class Builder
9
+ extend AttrPath
10
+ attr_path_accessor :srcdest, :pkgdest, :logdest
11
+ attr_accessor :signer
12
+
13
+ def build_package(dir, chroot_tree)
14
+ Dir.mktmpdir do |tmp_pkgdest|
15
+ wrap_dir(:srcdest) do
16
+ wrap_dir(:logdest) do
17
+ tmp_pkgdest = Pathname.new(tmp_pkgdest)
18
+ env = {
19
+ SRCDEST: srcdest.realpath,
20
+ PKGDEST: tmp_pkgdest.realpath,
21
+ LOGDEST: logdest.realpath,
22
+ }
23
+ chroot_tree.makechrootpkg(dir.to_s, env)
24
+ gather_packages(tmp_pkgdest)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def gather_packages(tmp_pkgdest)
31
+ tmp_pkgdest.each_child.map do |package_path|
32
+ dest = pkgdest.join(package_path.basename)
33
+ copy_and_sign_package(package_path, dest)
34
+ Package.new(dest)
35
+ end
36
+ end
37
+
38
+ def copy_and_sign_package(package_path, dest)
39
+ FileUtils.cp(package_path.to_s, dest.to_s)
40
+ if signer
41
+ signer.detach_sign(dest)
42
+ end
43
+ rescue => e
44
+ begin
45
+ dest.unlink
46
+ rescue Errno::ENOENT
47
+ end
48
+ raise e
49
+ end
50
+
51
+ def wrap_dir(attr, &block)
52
+ old_dir = instance_variable_get("@#{attr}")
53
+ if old_dir.nil?
54
+ Dir.mktmpdir do |tmp|
55
+ dir = Pathname.new(tmp)
56
+ instance_variable_set("@#{attr}", dir)
57
+ # Must be world-executable because it is referred from inside chroot.
58
+ dir.chmod(0755)
59
+ block.call
60
+ end
61
+ else
62
+ block.call
63
+ end
64
+ ensure
65
+ instance_variable_set("@#{attr}", old_dir)
66
+ end
67
+
68
+ def with_source_package(dir, &block)
69
+ dir = Pathname.new(dir)
70
+ Dir.mktmpdir do |tmp_srcpkgdest|
71
+ tmp_srcpkgdest = Pathname.new(tmp_srcpkgdest)
72
+ Dir.mktmpdir do |builddir|
73
+ builddir = Pathname.new(builddir)
74
+ wrap_dir(:srcdest) do
75
+ makepkg_source(dir, srcdest, tmp_srcpkgdest, builddir)
76
+ end
77
+ end
78
+
79
+ srcpkg = find_source_package(tmp_srcpkgdest)
80
+ # Remove symlink created by makepkg
81
+ dir.join(srcpkg.basename).unlink
82
+ block.call(srcpkg)
83
+ end
84
+ end
85
+
86
+ def makepkg_source(dir, srcdest, srcpkgdest, builddir)
87
+ env = {
88
+ 'SRCDEST' => srcdest.realpath.to_s,
89
+ 'SRCPKGDEST' => srcpkgdest.realpath.to_s,
90
+ 'BUILDDIR' => builddir.realpath.to_s,
91
+ }
92
+ unless system(env, 'makepkg', '--source', chdir: dir)
93
+ raise Error.new("makepkg --source failed: #{dir}")
94
+ end
95
+ end
96
+
97
+ def find_source_package(srcpkgdest)
98
+ children = srcpkgdest.each_child.to_a
99
+ if children.empty?
100
+ raise Error.new("makepkg --source generated nothing")
101
+ elsif children.size > 1
102
+ raise Error.new("makepkg --source generated multiple files???: #{children.map(&:to_s)}")
103
+ else
104
+ children.first
105
+ end
106
+ end
107
+ end
108
+ end