suppository 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +76 -0
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +4 -0
- data/Guardfile +30 -0
- data/LICENSE.txt +674 -0
- data/README.md +103 -0
- data/Rakefile +34 -0
- data/bin/suppository +36 -0
- data/fixtures/curl_7.22.0-3ubuntu4.11_amd64.deb +0 -0
- data/fixtures/vim_7.3.547-7_amd64.deb +0 -0
- data/lib/suppository/add_command.rb +116 -0
- data/lib/suppository/checksummed.rb +24 -0
- data/lib/suppository/cli.rb +26 -0
- data/lib/suppository/command_runner.rb +30 -0
- data/lib/suppository/create_command.rb +75 -0
- data/lib/suppository/dpkg_deb.rb +29 -0
- data/lib/suppository/dpkg_deb_line.rb +28 -0
- data/lib/suppository/exceptions.rb +20 -0
- data/lib/suppository/gzip.rb +14 -0
- data/lib/suppository/help.rb +22 -0
- data/lib/suppository/help_command.rb +13 -0
- data/lib/suppository/logger.rb +24 -0
- data/lib/suppository/master_deb.rb +62 -0
- data/lib/suppository/package.rb +23 -0
- data/lib/suppository/release.rb +57 -0
- data/lib/suppository/repository.rb +16 -0
- data/lib/suppository/tty.rb +43 -0
- data/lib/suppository/version.rb +3 -0
- data/lib/suppository/version_command.rb +12 -0
- data/lib/suppository.rb +5 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/suppository/add_command_spec.rb +141 -0
- data/spec/suppository/cli_spec.rb +50 -0
- data/spec/suppository/command_runner_spec.rb +26 -0
- data/spec/suppository/create_command_spec.rb +80 -0
- data/spec/suppository/dpkg_deb_line_spec.rb +36 -0
- data/spec/suppository/dpkg_deb_spec.rb +65 -0
- data/spec/suppository/gzip_spec.rb.rb +22 -0
- data/spec/suppository/help_command_spec.rb +13 -0
- data/spec/suppository/help_spec.rb +12 -0
- data/spec/suppository/logger_spec.rb +64 -0
- data/spec/suppository/master_deb_spec.rb +84 -0
- data/spec/suppository/package_spec.rb +63 -0
- data/spec/suppository/release_spec.rb +70 -0
- data/spec/suppository/repository_spec.rb +53 -0
- data/spec/suppository/tty_spec.rb +92 -0
- data/spec/suppository/version_command_spec.rb +13 -0
- data/spec/suppository/version_spec.rb +13 -0
- data/spec/suppository_spec.rb +83 -0
- data/suppository.gemspec +34 -0
- metadata +286 -0
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Super Simple Apt Repository
|
2
|
+
[![Build Status](https://travis-ci.org/TheBookPeople/suppository.svg?branch=develop)](https://travis-ci.org/TheBookPeople/suppository) [![Code Climate](https://codeclimate.com/github/TheBookPeople/suppository/badges/gpa.svg)](https://codeclimate.com/github/TheBookPeople/suppository) [![Test Coverage](https://codeclimate.com/github/TheBookPeople/suppository/badges/coverage.svg)](https://codeclimate.com/github/TheBookPeople/suppository)
|
3
|
+
|
4
|
+
Based on the ideas from Super Simple Apt Repository https://github.com/lukepfarrar/suppository.
|
5
|
+
|
6
|
+
A RubyGem that can be used to manage a simple apt repository.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
$ gem install suppository
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
### Help
|
15
|
+
|
16
|
+
$ suppository help
|
17
|
+
|
18
|
+
### Version
|
19
|
+
|
20
|
+
$ suppository version
|
21
|
+
|
22
|
+
### Create new repository
|
23
|
+
|
24
|
+
$ suppository create REPOSITORY_PATH
|
25
|
+
|
26
|
+
### Add Deb to existing repository
|
27
|
+
|
28
|
+
$ suppository add REPOSITORY_PATH DIST_NAME COMPONENT_NAME DEB_FILE
|
29
|
+
|
30
|
+
## Build
|
31
|
+
|
32
|
+
### Prerequisites
|
33
|
+
|
34
|
+
Tested on Ruby 1.9.3, 2.0.0, 2.1.5 and 2.2.0
|
35
|
+
|
36
|
+
Bundler
|
37
|
+
|
38
|
+
RubyGems
|
39
|
+
|
40
|
+
#### OSX
|
41
|
+
|
42
|
+
If you are developing on a Mac the you will need to install dpkg and gpg for the tests to pass. The simplest way to install it is with
|
43
|
+
Homebrew (see http://brew.sh/ on how to install Homebrew)
|
44
|
+
|
45
|
+
$ brew install dpkg
|
46
|
+
$ brew install gpg
|
47
|
+
|
48
|
+
#### Ubuntu / Debian
|
49
|
+
|
50
|
+
dpkg will already be installed but you might need to install gpg.
|
51
|
+
|
52
|
+
|
53
|
+
### Run tests
|
54
|
+
The default rake task will run code quality checks and all the tests.
|
55
|
+
|
56
|
+
$ bundle install
|
57
|
+
$ bundle exec rake
|
58
|
+
|
59
|
+
If you want to automatically run the tests during development, you can use Guard. Guard will watch for file changes
|
60
|
+
and run the appropriate tests. See https://github.com/guard/guard for more information on guard
|
61
|
+
|
62
|
+
$ bundle exec guard
|
63
|
+
|
64
|
+
### Create Gem
|
65
|
+
|
66
|
+
$ bundle exec rake build
|
67
|
+
|
68
|
+
This will run all the tests and then create a gem file. NOTE: Only files tracked by Git will be included in the gem.
|
69
|
+
|
70
|
+
### Release
|
71
|
+
|
72
|
+
Check everything build and the tests pass
|
73
|
+
|
74
|
+
$ bundle exec build
|
75
|
+
|
76
|
+
Create release using GitFlow (http://danielkummer.github.io/git-flow-cheatsheet/)
|
77
|
+
|
78
|
+
$ git flow release start [version]
|
79
|
+
|
80
|
+
Update the version number and commit changes
|
81
|
+
|
82
|
+
$ vi lib/suppository/version.rb
|
83
|
+
|
84
|
+
Finish release
|
85
|
+
|
86
|
+
$ git flow release finish [version]
|
87
|
+
|
88
|
+
Push changes
|
89
|
+
|
90
|
+
$ git push
|
91
|
+
$ git checkout develop
|
92
|
+
$ git push
|
93
|
+
$ git push --tags
|
94
|
+
|
95
|
+
Travis will now build and deploy to RubyGems.org
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it ( https://github.com/TheBookPeople/suppository/fork )
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'rubocop/rake_task'
|
5
|
+
require 'suppository/version'
|
6
|
+
|
7
|
+
desc "Run Code quality checks and tests "
|
8
|
+
task :default => [:clean,:rubocop,:test]
|
9
|
+
|
10
|
+
desc "Run Code quality checks, tests and then create Gem File"
|
11
|
+
task :build => [:clean,:rubocop,:test,:gem]
|
12
|
+
|
13
|
+
CLEAN.include("suppository-#{Suppository::VERSION}.gem")
|
14
|
+
CLEAN.include('coverage')
|
15
|
+
|
16
|
+
task :test do
|
17
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
18
|
+
t.pattern = 'spec/**/*_spec.rb'
|
19
|
+
t.verbose = false
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::Task["spec"].execute
|
23
|
+
end
|
24
|
+
|
25
|
+
task :rubocop do
|
26
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
27
|
+
task.patterns = ['lib/**/*.rb']
|
28
|
+
task.fail_on_error = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
task :gem do
|
33
|
+
system "gem build suppository.gemspec"
|
34
|
+
end
|
data/bin/suppository
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# -*- mode: ruby -*-
|
4
|
+
require_relative '../lib/suppository/cli'
|
5
|
+
require_relative '../lib/suppository/logger'
|
6
|
+
require_relative '../lib/suppository/help'
|
7
|
+
|
8
|
+
std_trap = trap("INT") { exit! 130 } # no backtrace thanks
|
9
|
+
|
10
|
+
begin
|
11
|
+
trap("INT", std_trap) # restore default CTRL-C handler
|
12
|
+
Suppository::CLI.run(ARGV)
|
13
|
+
rescue UsageError
|
14
|
+
Suppository::Logger.log_error "Invalid usage"
|
15
|
+
abort Suppository.help
|
16
|
+
rescue SystemExit
|
17
|
+
puts "Kernel.exit" if ARGV.verbose?
|
18
|
+
raise
|
19
|
+
rescue Interrupt => e
|
20
|
+
puts # seemingly a newline is typical
|
21
|
+
exit 130
|
22
|
+
rescue RuntimeError, SystemCallError => e
|
23
|
+
raise if e.message.empty?
|
24
|
+
Suppository::Logger.log_error(e)
|
25
|
+
exit 1
|
26
|
+
rescue Exception => e
|
27
|
+
Suppository::Logger.log_error(e)
|
28
|
+
puts "#{Suppository::Tty.white}Please report this bug:"
|
29
|
+
puts " #{Suppository::Tty.em}https://github.com/TheBookPeople/suppository/issues#{Suppository::Tty.reset}"
|
30
|
+
puts e.backtrace
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
require 'suppository/master_deb'
|
3
|
+
require 'suppository/repository'
|
4
|
+
require 'suppository/exceptions'
|
5
|
+
require 'suppository/package'
|
6
|
+
require 'suppository/release'
|
7
|
+
require 'suppository/logger'
|
8
|
+
require 'suppository/checksummed'
|
9
|
+
require 'suppository/gzip'
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
module Suppository
|
13
|
+
class AddCommand
|
14
|
+
include Suppository::Logger
|
15
|
+
|
16
|
+
def initialize(args)
|
17
|
+
@unsigned = parse_params(args)
|
18
|
+
@repository = Suppository::Repository.new(args[0])
|
19
|
+
@dist = args[1]
|
20
|
+
@component = args[2]
|
21
|
+
@debs = Dir.glob(args[3])
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
assert_repository_exists
|
26
|
+
assert_dist_exists
|
27
|
+
assert_component_exists
|
28
|
+
|
29
|
+
@debs.each { |deb| add_deb Suppository::Checksummed.new(deb) }
|
30
|
+
|
31
|
+
Suppository::Release.new(@repository.path, @dist, @unsigned).create
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_params(args)
|
37
|
+
fail UsageError if args.nil? || args.length < 4 || args.length > 5
|
38
|
+
fail UsageError if args.length == 5 && args[4] != '--unsigned'
|
39
|
+
args.length == 5
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_deb(deb)
|
43
|
+
create_suppository_file(deb)
|
44
|
+
create_dist_file(suppository_file(deb), deb)
|
45
|
+
f = File.basename(deb.path)
|
46
|
+
message = "#{f} added to repository #{@repository.path}, #{@dist} #{@component}"
|
47
|
+
log_success message
|
48
|
+
end
|
49
|
+
|
50
|
+
def assert_repository_exists
|
51
|
+
message = "#{@repository.path} is not a valid repository.\n"
|
52
|
+
message << "You can create a new repository by running the following command\n\n"
|
53
|
+
message << " suppository create #{@repository.path}"
|
54
|
+
fail InvalidRepositoryError, message unless @repository.exist?
|
55
|
+
end
|
56
|
+
|
57
|
+
def assert_dist_exists
|
58
|
+
supported_dist = @repository.dists.join(', ')
|
59
|
+
message = "#{@dist} does not exist, try one of the following #{supported_dist}"
|
60
|
+
fail InvalidDistribution, message unless File.exist?("#{dist_path}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_component_exists
|
64
|
+
message = "#{@component} does not exist, try internal instead"
|
65
|
+
fail InvalidComponent, message unless File.exist?("#{component_path}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_suppository_file(deb)
|
69
|
+
FileUtils.copy_file(deb.path, suppository_file(deb), true)
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_dist_file(master_file, deb)
|
73
|
+
@repository.archs.each do |arch|
|
74
|
+
FileUtils.ln_s master_file, dist_file(arch, deb), force: true
|
75
|
+
update_packages master_file, arch
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_packages(master_file, arch)
|
80
|
+
deb = Suppository::MasterDeb.new(master_file)
|
81
|
+
file = package_file(arch)
|
82
|
+
package_info = Suppository::Package.new(internal_path(arch), deb).content
|
83
|
+
open(file, 'a') { |f| f.puts package_info }
|
84
|
+
Suppository::Gzip.compress file
|
85
|
+
end
|
86
|
+
|
87
|
+
def dist_file(arch, deb)
|
88
|
+
filename = Suppository::MasterDeb.new(suppository_file(deb)).filename
|
89
|
+
"#{component_path}/binary-#{arch}/#{filename}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def package_file(arch)
|
93
|
+
"#{component_path}/binary-#{arch}/Packages"
|
94
|
+
end
|
95
|
+
|
96
|
+
def internal_path(arch)
|
97
|
+
"dists/#{@dist}/#{@component}/binary-#{arch}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def suppository_file(deb)
|
101
|
+
"#{suppository}/#{deb.md5}_#{deb.sha1}_#{deb.sha2}.deb"
|
102
|
+
end
|
103
|
+
|
104
|
+
def dist_path
|
105
|
+
"#{@repository.path}/dists/#{@dist}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def component_path
|
109
|
+
"#{dist_path}/#{@component}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def suppository
|
113
|
+
@repository.suppository
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'digest'
|
3
|
+
|
4
|
+
module Suppository
|
5
|
+
class Checksummed
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = File.expand_path(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def md5
|
13
|
+
@md5 ||= Digest::MD5.file(@path).hexdigest
|
14
|
+
end
|
15
|
+
|
16
|
+
def sha1
|
17
|
+
@sha1 ||= Digest::SHA1.file(@path).hexdigest
|
18
|
+
end
|
19
|
+
|
20
|
+
def sha2
|
21
|
+
@sha2 ||= Digest::SHA2.file(@path).hexdigest
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
require 'suppository/exceptions'
|
3
|
+
|
4
|
+
module Suppository
|
5
|
+
class CLI
|
6
|
+
def self.run(args)
|
7
|
+
fail UsageError if args.empty?
|
8
|
+
cmd = args.delete_at(0)
|
9
|
+
|
10
|
+
begin
|
11
|
+
clazz(cmd).new(args).run
|
12
|
+
rescue LoadError
|
13
|
+
raise UsageError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.clazz(cmd)
|
18
|
+
require "suppository/#{cmd}_command"
|
19
|
+
clazz_name(cmd).split('::').inject(Object) { |a, e| a.const_get e }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.clazz_name(cmd)
|
23
|
+
"Suppository::#{cmd.capitalize}Command"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require 'suppository/exceptions'
|
3
|
+
|
4
|
+
module Suppository
|
5
|
+
class CommandRunner
|
6
|
+
def initialize(command, arguments = '')
|
7
|
+
@command = command
|
8
|
+
@arguments = arguments
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
assert_exists
|
13
|
+
run_command
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def assert_exists
|
19
|
+
`which "#{@command}"`
|
20
|
+
message = "'#{@command}' was not found."
|
21
|
+
fail(CommandMissingError, message) unless $CHILD_STATUS.success?
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_command
|
25
|
+
output = `#{@command} #{@arguments} 2>&1`
|
26
|
+
fail(CommandError, output) unless $CHILD_STATUS.success?
|
27
|
+
output
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
require 'fileutils'
|
3
|
+
require 'suppository/logger'
|
4
|
+
require 'suppository/repository'
|
5
|
+
require 'suppository/exceptions'
|
6
|
+
require 'suppository/gzip'
|
7
|
+
|
8
|
+
module Suppository
|
9
|
+
class CreateCommand
|
10
|
+
include Suppository::Logger
|
11
|
+
|
12
|
+
def initialize(args)
|
13
|
+
assert_arguments args
|
14
|
+
@repository = repository(args[0])
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
assert_not_created
|
19
|
+
create_repository
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def assert_arguments(args)
|
25
|
+
message = 'Create command needs one argument, the path to the new repository'
|
26
|
+
fail UsageError, message if args.nil? || args.length != 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def repository(path)
|
30
|
+
Suppository::Repository.new(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def assert_not_created
|
34
|
+
@repository.exist? ? fail("#{path} is already a repository") : ''
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_repository
|
38
|
+
FileUtils.mkdir_p "#{suppository}"
|
39
|
+
create_dists_folders
|
40
|
+
log_success "Created new Repository - #{path}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_dists_folders
|
44
|
+
@repository.dists.each do |dist|
|
45
|
+
create_archs_folders dist
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_archs_folders(dist)
|
50
|
+
@repository.archs.each do |arch|
|
51
|
+
create_folder(path, dist, arch)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_folder(path, dist, arch)
|
56
|
+
dir_path = "#{path}/dists/#{dist}/internal/binary-#{arch}"
|
57
|
+
FileUtils.mkdir_p dir_path
|
58
|
+
create_packages_file(dir_path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_packages_file(path)
|
62
|
+
packages_file = "#{path}/Packages"
|
63
|
+
FileUtils.touch packages_file
|
64
|
+
Suppository::Gzip.compress packages_file
|
65
|
+
end
|
66
|
+
|
67
|
+
def suppository
|
68
|
+
@repository.suppository
|
69
|
+
end
|
70
|
+
|
71
|
+
def path
|
72
|
+
@repository.path
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require 'suppository/dpkg_deb_line'
|
3
|
+
require 'suppository/command_runner'
|
4
|
+
|
5
|
+
module Suppository
|
6
|
+
class DpkgDeb
|
7
|
+
attr_reader :attibutes
|
8
|
+
|
9
|
+
def initialize(deb_path)
|
10
|
+
command = CommandRunner.new('dpkg-deb', "-f #{deb_path}")
|
11
|
+
@attibutes = parser(command.run)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def parser(output)
|
17
|
+
attibutes = {}
|
18
|
+
output.each_line do |line|
|
19
|
+
attibute = parser_line(line)
|
20
|
+
attibutes = attibutes.merge(attibute) { |_, first, second| first + second }
|
21
|
+
end
|
22
|
+
attibutes
|
23
|
+
end
|
24
|
+
|
25
|
+
def parser_line(output_line)
|
26
|
+
DpkgDebLine.new(output_line).attributes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Suppository
|
2
|
+
class DpkgDebLine
|
3
|
+
DESCRIPTION_FIELD = 'Description'
|
4
|
+
|
5
|
+
attr_reader :attributes
|
6
|
+
|
7
|
+
def initialize(line)
|
8
|
+
field = split_line(line)
|
9
|
+
if description?(line)
|
10
|
+
@attributes = { DESCRIPTION_FIELD => line }
|
11
|
+
elsif field
|
12
|
+
@attributes = { field['fieldname'] => field['fieldvalue'] }
|
13
|
+
else
|
14
|
+
fail "can't parse line - '#{line}'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def split_line(line)
|
21
|
+
/^(?<fieldname>[^:]+): (?<fieldvalue>.+)$/.match(line)
|
22
|
+
end
|
23
|
+
|
24
|
+
def description?(line)
|
25
|
+
/^ .+$/.match(line)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class UsageError < RuntimeError
|
2
|
+
end
|
3
|
+
|
4
|
+
class InvalidDistribution < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
class InvalidComponent < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
class CommandMissingError < RuntimeError
|
11
|
+
end
|
12
|
+
|
13
|
+
class InvalidMasterDeb < RuntimeError
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidRepositoryError < RuntimeError
|
17
|
+
end
|
18
|
+
|
19
|
+
class CommandError < RuntimeError
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
HELP = <<-EOS
|
2
|
+
Example usage:
|
3
|
+
|
4
|
+
suppository help
|
5
|
+
- Display this Help message
|
6
|
+
|
7
|
+
suppository version
|
8
|
+
- Display version
|
9
|
+
|
10
|
+
suppository create <REPOSITORY_PATH>
|
11
|
+
- Create new empty repository in REPOSITORY_PATH
|
12
|
+
|
13
|
+
suppository add <REPOSITORY_PATH> <DIST> <COMPONENT> <DEB_FILE> [--unsigned]
|
14
|
+
- Add DEB_FILE to DIST and COMPONENT of repository at REPOSITORY_PATH
|
15
|
+
|
16
|
+
EOS
|
17
|
+
|
18
|
+
module Suppository
|
19
|
+
def self.help
|
20
|
+
HELP
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'suppository/tty'
|
3
|
+
|
4
|
+
module Suppository
|
5
|
+
module Logger
|
6
|
+
def log_info(message)
|
7
|
+
puts message
|
8
|
+
end
|
9
|
+
|
10
|
+
def log_verbose(message)
|
11
|
+
puts "#{Tty.gray}#{message}#{Tty.reset}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_error(error)
|
15
|
+
$stderr.puts "#{Tty.red}Error#{Tty.reset}: #{error}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_success(message)
|
19
|
+
puts "#{Tty.green}==>#{Tty.white} #{message}#{Tty.reset}"
|
20
|
+
end
|
21
|
+
|
22
|
+
module_function :log_info, :log_error, :log_success, :log_verbose
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'suppository/exceptions'
|
2
|
+
require 'suppository/dpkg_deb'
|
3
|
+
|
4
|
+
module Suppository
|
5
|
+
class MasterDeb
|
6
|
+
attr_reader :md5sum, :sha256, :sha1, :dirname
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
|
11
|
+
assert_in_suppository
|
12
|
+
checksums = checksums_from_name
|
13
|
+
|
14
|
+
@md5sum = checksums['md5']
|
15
|
+
@sha1 = checksums['sha1']
|
16
|
+
@sha256 = checksums['sha256']
|
17
|
+
|
18
|
+
@dirname = File.dirname(path)
|
19
|
+
@attr = Suppository::DpkgDeb.new(path).attibutes
|
20
|
+
end
|
21
|
+
|
22
|
+
def filename
|
23
|
+
"#{@attr['Package']}_#{@attr['Version']}_#{@attr['Architecture']}.deb"
|
24
|
+
end
|
25
|
+
|
26
|
+
def full_attr
|
27
|
+
full_attrs = @attr
|
28
|
+
full_attrs['Size'] = size
|
29
|
+
full_attrs['MD5Sum'] = @md5sum
|
30
|
+
full_attrs['SHA1'] = @sha1
|
31
|
+
full_attrs['SHA256'] = @sha256
|
32
|
+
full_attrs
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
File.size(@path)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def assert_in_suppository
|
42
|
+
message = 'Master deb must be in the .suppository folder'
|
43
|
+
fail InvalidMasterDeb, message unless suppository_file?
|
44
|
+
end
|
45
|
+
|
46
|
+
def suppository_file?
|
47
|
+
File.dirname(@path).end_with?('.suppository')
|
48
|
+
end
|
49
|
+
|
50
|
+
def checksums_from_name
|
51
|
+
file_name = File.basename(@path)
|
52
|
+
matches = filename_regex.match(file_name)
|
53
|
+
message = 'Master deb must have the following name {md5}_{sha1}_{sha256}.deb'
|
54
|
+
fail InvalidMasterDeb, message unless matches
|
55
|
+
matches
|
56
|
+
end
|
57
|
+
|
58
|
+
def filename_regex
|
59
|
+
/^(?<md5>[a-f0-9]{32})_(?<sha1>[a-f0-9]{40})_(?<sha256>[a-f0-9]{64})\.deb$/
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Suppository
|
3
|
+
class Package
|
4
|
+
def initialize(parent_folder, deb)
|
5
|
+
@deb = deb
|
6
|
+
@parent_folder = parent_folder
|
7
|
+
end
|
8
|
+
|
9
|
+
def content
|
10
|
+
full_attrs = @deb.full_attr
|
11
|
+
full_attrs[:Filename] = filename
|
12
|
+
full_attrs.sort_by { |k, _v| k == 'Description' ? 1 : 0 }
|
13
|
+
.to_a.map { |kv_pair| kv_pair.join(': ') }
|
14
|
+
.join("\n") << "\n\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def filename
|
20
|
+
"#{@parent_folder}/#{@deb.filename}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|