akabei 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.
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