suppository 0.0.2
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.
- 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
|
+
[](https://travis-ci.org/TheBookPeople/suppository) [](https://codeclimate.com/github/TheBookPeople/suppository) [](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
|