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.
Files changed (54) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +76 -0
  3. data/.rubocop.yml +5 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +19 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +30 -0
  8. data/LICENSE.txt +674 -0
  9. data/README.md +103 -0
  10. data/Rakefile +34 -0
  11. data/bin/suppository +36 -0
  12. data/fixtures/curl_7.22.0-3ubuntu4.11_amd64.deb +0 -0
  13. data/fixtures/vim_7.3.547-7_amd64.deb +0 -0
  14. data/lib/suppository/add_command.rb +116 -0
  15. data/lib/suppository/checksummed.rb +24 -0
  16. data/lib/suppository/cli.rb +26 -0
  17. data/lib/suppository/command_runner.rb +30 -0
  18. data/lib/suppository/create_command.rb +75 -0
  19. data/lib/suppository/dpkg_deb.rb +29 -0
  20. data/lib/suppository/dpkg_deb_line.rb +28 -0
  21. data/lib/suppository/exceptions.rb +20 -0
  22. data/lib/suppository/gzip.rb +14 -0
  23. data/lib/suppository/help.rb +22 -0
  24. data/lib/suppository/help_command.rb +13 -0
  25. data/lib/suppository/logger.rb +24 -0
  26. data/lib/suppository/master_deb.rb +62 -0
  27. data/lib/suppository/package.rb +23 -0
  28. data/lib/suppository/release.rb +57 -0
  29. data/lib/suppository/repository.rb +16 -0
  30. data/lib/suppository/tty.rb +43 -0
  31. data/lib/suppository/version.rb +3 -0
  32. data/lib/suppository/version_command.rb +12 -0
  33. data/lib/suppository.rb +5 -0
  34. data/spec/spec_helper.rb +55 -0
  35. data/spec/suppository/add_command_spec.rb +141 -0
  36. data/spec/suppository/cli_spec.rb +50 -0
  37. data/spec/suppository/command_runner_spec.rb +26 -0
  38. data/spec/suppository/create_command_spec.rb +80 -0
  39. data/spec/suppository/dpkg_deb_line_spec.rb +36 -0
  40. data/spec/suppository/dpkg_deb_spec.rb +65 -0
  41. data/spec/suppository/gzip_spec.rb.rb +22 -0
  42. data/spec/suppository/help_command_spec.rb +13 -0
  43. data/spec/suppository/help_spec.rb +12 -0
  44. data/spec/suppository/logger_spec.rb +64 -0
  45. data/spec/suppository/master_deb_spec.rb +84 -0
  46. data/spec/suppository/package_spec.rb +63 -0
  47. data/spec/suppository/release_spec.rb +70 -0
  48. data/spec/suppository/repository_spec.rb +53 -0
  49. data/spec/suppository/tty_spec.rb +92 -0
  50. data/spec/suppository/version_command_spec.rb +13 -0
  51. data/spec/suppository/version_spec.rb +13 -0
  52. data/spec/suppository_spec.rb +83 -0
  53. data/suppository.gemspec +34 -0
  54. 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
@@ -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,14 @@
1
+
2
+ require 'zlib'
3
+
4
+ module Suppository
5
+ module Gzip
6
+ def self.compress(file)
7
+ Zlib::GzipWriter.open("#{file}.gz") do |gz|
8
+ gz.mtime = File.mtime(file)
9
+ gz.orig_name = file
10
+ gz.write IO.read(file)
11
+ end
12
+ end
13
+ end
14
+ 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,13 @@
1
+
2
+ require 'suppository/help'
3
+
4
+ module Suppository
5
+ class HelpCommand
6
+ def initialize(_)
7
+ end
8
+
9
+ def run
10
+ puts Suppository.help
11
+ end
12
+ end
13
+ 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