dpkg-s3 0.3.1 → 0.4.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.
data/lib/dpkg/s3/lock.rb CHANGED
@@ -1,67 +1,79 @@
1
- # -*- encoding : utf-8 -*-
2
- require "tempfile"
3
- require "socket"
4
- require "etc"
5
- require "securerandom"
1
+ # frozen_string_literal: true
6
2
 
7
- class Dpkg::S3::Lock
8
- attr_accessor :user
9
- attr_accessor :host
3
+ require 'tempfile'
4
+ require 'socket'
5
+ require 'etc'
6
+ require 'securerandom'
10
7
 
11
- def initialize
12
- @user = nil
13
- @host = nil
14
- end
15
-
16
- class << self
17
- def locked?(codename, component = nil, architecture = nil, cache_control = nil)
18
- Dpkg::S3::Utils.s3_exists?(lock_path(codename, component, architecture, cache_control))
19
- end
8
+ # Dpkg is the root module for all storage modules including S3
9
+ module Dpkg
10
+ # S3 storage module resposible of handling packages on S3 including upload, delete
11
+ module S3
12
+ # Lock is resposible of creating lock file on S3 to ensure when multiple instances of uploads will
13
+ # not conflict with each other
14
+ class Lock
15
+ attr_accessor :user, :host
20
16
 
21
- def wait_for_lock(codename, component = nil, architecture = nil, cache_control = nil, max_attempts=60, wait=10)
22
- attempts = 0
23
- while self.locked?(codename, component, architecture, cache_control) do
24
- attempts += 1
25
- throw "Unable to obtain a lock after #{max_attempts}, giving up." if attempts > max_attempts
26
- sleep(wait)
17
+ def initialize
18
+ @user = nil
19
+ @host = nil
27
20
  end
28
- end
29
21
 
30
- def lock(codename, component = nil, architecture = nil, cache_control = nil)
31
- lockfile = Tempfile.new("lockfile")
32
- lock_content = generate_lock_content
33
- lockfile.write lock_content
34
- lockfile.close
22
+ class << self
23
+ def locked?(codename, component = nil, architecture = nil, cache_control = nil)
24
+ Dpkg::S3::Utils.s3_exists?(lock_path(codename, component, architecture, cache_control))
25
+ end
35
26
 
36
- Dpkg::S3::Utils.s3_store(lockfile.path,
37
- lock_path(codename, component, architecture, cache_control),
38
- "text/plain",
39
- cache_control)
27
+ def wait_for_lock(codename, component = nil, architecture = nil, cache_control = nil)
28
+ max_attempts = 60
29
+ wait = 10
30
+ attempts = 0
31
+ while locked?(codename, component, architecture, cache_control)
32
+ attempts += 1
33
+ throw "Unable to obtain a lock after #{max_attempts}, giving up." if attempts > max_attempts
34
+ sleep(wait)
35
+ end
36
+ end
40
37
 
41
- return if lock_content == Dpkg::S3::Utils.s3_read(lock_path(codename, component, architecture, cache_control))
42
- throw "Failed to acquire lock, was overwritten by another deb-s3 process"
43
- end
38
+ def lock(codename, component = nil, architecture = nil, cache_control = nil)
39
+ lockfile = Tempfile.new('lockfile')
40
+ lock_content = generate_lock_content
41
+ lockfile.write lock_content
42
+ lockfile.close
44
43
 
45
- def unlock(codename, component = nil, architecture = nil, cache_control = nil)
46
- Dpkg::S3::Utils.s3_remove(lock_path(codename, component, architecture, cache_control))
47
- end
44
+ Dpkg::S3::Utils.s3_store(lockfile.path,
45
+ lock_path(codename, component, architecture, cache_control),
46
+ 'text/plain',
47
+ cache_control)
48
48
 
49
- def current(codename, component = nil, architecture = nil, cache_control = nil)
50
- lock_content = Dpkg::S3::Utils.s3_read(lock_path(codename, component, architecture, cache_control))
51
- lock_content = lock_content.split.first.split('@')
52
- lock = Dpkg::S3::Lock.new
53
- lock.user = lock_content[0]
54
- lock.host = lock_content[1] if lock_content.size > 1
55
- lock
56
- end
49
+ return if lock_content == Dpkg::S3::Utils.s3_read(lock_path(codename, component, architecture, cache_control))
57
50
 
58
- private
59
- def lock_path(codename, component = nil, architecture = nil, cache_control = nil)
60
- "dists/#{codename}/#{component}/binary-#{architecture}/lockfile"
61
- end
51
+ throw 'Failed to acquire lock, was overwritten by another deb-s3 process'
52
+ end
53
+
54
+ def unlock(codename, component = nil, architecture = nil, cache_control = nil)
55
+ Dpkg::S3::Utils.s3_remove(lock_path(codename, component, architecture, cache_control))
56
+ end
57
+
58
+ def current(codename, component = nil, architecture = nil, cache_control = nil)
59
+ lock_content = Dpkg::S3::Utils.s3_read(lock_path(codename, component, architecture, cache_control))
60
+ lock_content = lock_content.split.first.split('@')
61
+ lock = Dpkg::S3::Lock.new
62
+ lock.user = lock_content[0]
63
+ lock.host = lock_content[1] if lock_content.size > 1
64
+ lock
65
+ end
62
66
 
63
- def generate_lock_content
64
- "#{Etc.getlogin}@#{Socket.gethostname}\n#{SecureRandom.hex}"
67
+ private
68
+
69
+ def lock_path(codename, component = nil, architecture = nil, _cache_control = nil)
70
+ "dists/#{codename}/#{component}/binary-#{architecture}/lockfile"
71
+ end
72
+
73
+ def generate_lock_content
74
+ "#{Etc.getlogin}@#{Socket.gethostname}\n#{SecureRandom.hex}"
75
+ end
76
+ end
65
77
  end
66
78
  end
67
79
  end
@@ -1,144 +1,146 @@
1
- # -*- encoding : utf-8 -*-
2
- require "tempfile"
3
- require "zlib"
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+ require 'zlib'
4
5
  require 'dpkg/s3/utils'
5
6
  require 'dpkg/s3/package'
6
7
 
7
- class Dpkg::S3::Manifest
8
- include Dpkg::S3::Utils
9
-
10
- attr_accessor :codename
11
- attr_accessor :component
12
- attr_accessor :cache_control
13
- attr_accessor :architecture
14
- attr_accessor :fail_if_exists
15
- attr_accessor :skip_package_upload
16
-
17
- attr_accessor :files
18
-
19
- attr_reader :packages
20
- attr_reader :packages_to_be_upload
21
-
22
- def initialize
23
- @packages = []
24
- @packages_to_be_upload = []
25
- @component = nil
26
- @architecture = nil
27
- @files = {}
28
- @cache_control = ""
29
- @fail_if_exists = false
30
- @skip_package_upload = false
31
- end
32
-
33
- class << self
34
- def retrieve(codename, component, architecture, cache_control, fail_if_exists, skip_package_upload=false)
35
- m = if s = Dpkg::S3::Utils.s3_read("dists/#{codename}/#{component}/binary-#{architecture}/Packages")
36
- self.parse_packages(s)
37
- else
38
- self.new
8
+ # Dpkg is the root module for all storage modules including S3
9
+ module Dpkg
10
+ # S3 storage module resposible of handling packages on S3 including upload, delete
11
+ module S3
12
+ # Manifest is resposible of creating/retrieving and rebuilding the debian Package manifest with
13
+ # standard information required when publishing the packages to a S3 debian repository
14
+ class Manifest
15
+ include Dpkg::S3::Utils
16
+
17
+ attr_accessor :codename, :component, :cache_control, :architecture, :fail_if_exists, :skip_package_upload, :files
18
+
19
+ attr_reader :packages, :packages_to_be_upload
20
+
21
+ def initialize
22
+ @packages = []
23
+ @packages_to_be_upload = []
24
+ @component = nil
25
+ @architecture = nil
26
+ @files = {}
27
+ @cache_control = ''
28
+ @fail_if_exists = false
29
+ @skip_package_upload = false
39
30
  end
40
31
 
41
- m.codename = codename
42
- m.component = component
43
- m.architecture = architecture
44
- m.cache_control = cache_control
45
- m.fail_if_exists = fail_if_exists
46
- m.skip_package_upload = skip_package_upload
47
- m
48
- end
32
+ class << self
33
+ def retrieve(codename, component, architecture, cache_control, fail_if_exists, skip_upload: false)
34
+ m = if (s = Dpkg::S3::Utils.s3_read("dists/#{codename}/#{component}/binary-#{architecture}/Packages"))
35
+ parse_packages(s)
36
+ else
37
+ new
38
+ end
39
+
40
+ m.codename = codename
41
+ m.component = component
42
+ m.architecture = architecture
43
+ m.cache_control = cache_control
44
+ m.fail_if_exists = fail_if_exists
45
+ m.skip_package_upload = skip_upload
46
+ m
47
+ end
48
+
49
+ def parse_packages(str)
50
+ m = new
51
+ str.split("\n\n").each do |s|
52
+ next if s.chomp.empty?
49
53
 
50
- def parse_packages(str)
51
- m = self.new
52
- str.split("\n\n").each do |s|
53
- next if s.chomp.empty?
54
- m.packages << Dpkg::S3::Package.parse_string(s)
54
+ m.packages << Dpkg::S3::Package.parse_string(s)
55
+ end
56
+ m
57
+ end
55
58
  end
56
- m
57
- end
58
- end
59
59
 
60
- def add(pkg, preserve_versions, needs_uploading=true)
61
- if self.fail_if_exists
62
- packages.each { |p|
63
- next unless p.name == pkg.name && \
64
- p.full_version == pkg.full_version && \
65
- File.basename(p.url_filename(@codename)) != \
66
- File.basename(pkg.url_filename(@codename))
67
- raise AlreadyExistsError,
68
- "package #{pkg.name}_#{pkg.full_version} already exists " \
69
- "with different filename (#{p.url_filename(@codename)})"
70
- }
71
- end
72
- if preserve_versions
73
- packages.delete_if { |p| p.name == pkg.name && p.full_version == pkg.full_version }
74
- else
75
- packages.delete_if { |p| p.name == pkg.name }
76
- end
77
- packages << pkg
78
- packages_to_be_upload << pkg if needs_uploading
79
- pkg
80
- end
60
+ def add(pkg, preserve_versions, needs_uploading: true)
61
+ if fail_if_exists
62
+ packages.each do |p|
63
+ next unless p.name == pkg.name && \
64
+ p.full_version == pkg.full_version && \
65
+ File.basename(p.url_filename(@codename)) == \
66
+ File.basename(pkg.url_filename(@codename))
67
+
68
+ raise AlreadyExistsError,
69
+ "package #{pkg.name}_#{pkg.full_version} already exists " \
70
+ "with filename (#{p.url_filename(@codename)})"
71
+ end
72
+ end
73
+ if preserve_versions
74
+ packages.delete_if { |p| p.name == pkg.name && p.full_version == pkg.full_version }
75
+ else
76
+ packages.delete_if { |p| p.name == pkg.name }
77
+ end
78
+ packages << pkg
79
+ packages_to_be_upload << pkg if needs_uploading
80
+ pkg
81
+ end
81
82
 
82
- def delete_package(pkg, versions=nil)
83
- deleted = []
84
- new_packages = @packages.select { |p|
85
- # Include packages we didn't name
86
- if p.name != pkg
87
- p
88
- # Also include the packages not matching a specified version
89
- elsif (!versions.nil? and p.name == pkg and !versions.include?(p.version) and !versions.include?("#{p.version}-#{p.iteration}") and !versions.include?(p.full_version))
83
+ def delete_package(pkg, versions = nil)
84
+ new_packages = @packages.select do |p|
85
+ # Include packages we didn't name
86
+ # Also include the packages not matching a specified version
87
+ if p.name != pkg || (!versions.nil? && (p.name == pkg) && !versions.include?(p.version) &&
88
+ !versions.include?("#{p.version}-#{p.iteration}") && !versions.include?(p.full_version))
90
89
  p
90
+ end
91
91
  end
92
- }
93
- deleted = @packages - new_packages
94
- @packages = new_packages
95
- deleted
96
- end
92
+ deleted = @packages - new_packages
93
+ @packages = new_packages
94
+ deleted
95
+ end
97
96
 
98
- def generate
99
- @packages.collect { |pkg| pkg.generate(@codename) }.join("\n")
100
- end
97
+ def generate
98
+ @packages.collect { |pkg| pkg.generate(@codename) }.join("\n")
99
+ end
101
100
 
102
- def write_to_s3
103
- manifest = self.generate
101
+ def write_to_s3
102
+ manifest = generate
104
103
 
105
- unless self.skip_package_upload
106
- # store any packages that need to be stored
107
- @packages_to_be_upload.each do |pkg|
108
- yield pkg.url_filename(@codename) if block_given?
109
- s3_store(pkg.filename, pkg.url_filename(@codename), 'application/x-debian-package', self.cache_control, self.fail_if_exists)
110
- end
111
- end
104
+ unless skip_package_upload
105
+ # store any packages that need to be stored
106
+ @packages_to_be_upload.each do |pkg|
107
+ yield pkg.url_filename(@codename) if block_given?
108
+ s3_store(pkg.filename, pkg.url_filename(@codename), 'application/x-debian-package', cache_control,
109
+ fail_if_exists: fail_if_exists)
110
+ end
111
+ end
112
112
 
113
- # generate the Packages file
114
- pkgs_temp = Tempfile.new("Packages")
115
- pkgs_temp.write manifest
116
- pkgs_temp.close
117
- f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages"
118
- yield f if block_given?
119
- s3_store(pkgs_temp.path, f, 'text/plain; charset=utf-8', self.cache_control)
120
- @files["#{@component}/binary-#{@architecture}/Packages"] = hashfile(pkgs_temp.path)
121
- pkgs_temp.unlink
122
-
123
- # generate the Packages.gz file
124
- gztemp = Tempfile.new("Packages.gz")
125
- gztemp.close
126
- Zlib::GzipWriter.open(gztemp.path) { |gz| gz.write manifest }
127
- f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages.gz"
128
- yield f if block_given?
129
- s3_store(gztemp.path, f, 'application/x-gzip', self.cache_control)
130
- @files["#{@component}/binary-#{@architecture}/Packages.gz"] = hashfile(gztemp.path)
131
- gztemp.unlink
132
-
133
- nil
134
- end
113
+ # generate the Packages file
114
+ pkgs_temp = Tempfile.new('Packages')
115
+ pkgs_temp.write manifest
116
+ pkgs_temp.close
117
+ f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages"
118
+ yield f if block_given?
119
+ s3_store(pkgs_temp.path, f, 'text/plain; charset=utf-8', cache_control)
120
+ @files["#{@component}/binary-#{@architecture}/Packages"] = hashfile(pkgs_temp.path)
121
+ pkgs_temp.unlink
122
+
123
+ # generate the Packages.gz file
124
+ gztemp = Tempfile.new('Packages.gz')
125
+ gztemp.close
126
+ Zlib::GzipWriter.open(gztemp.path) { |gz| gz.write manifest }
127
+ f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages.gz"
128
+ yield f if block_given?
129
+ s3_store(gztemp.path, f, 'application/x-gzip', cache_control)
130
+ @files["#{@component}/binary-#{@architecture}/Packages.gz"] = hashfile(gztemp.path)
131
+ gztemp.unlink
132
+
133
+ nil
134
+ end
135
135
 
136
- def hashfile(path)
137
- {
138
- :size => File.size(path),
139
- :sha1 => Digest::SHA1.file(path).hexdigest,
140
- :sha256 => Digest::SHA2.file(path).hexdigest,
141
- :md5 => Digest::MD5.file(path).hexdigest
142
- }
136
+ def hashfile(path)
137
+ {
138
+ size: File.size(path),
139
+ sha1: Digest::SHA1.file(path).hexdigest,
140
+ sha256: Digest::SHA2.file(path).hexdigest,
141
+ md5: Digest::MD5.file(path).hexdigest
142
+ }
143
+ end
144
+ end
143
145
  end
144
146
  end
@@ -1,312 +1,297 @@
1
- # -*- encoding : utf-8 -*-
2
- require "digest/sha1"
3
- require "digest/sha2"
4
- require "digest/md5"
5
- require "socket"
6
- require "tmpdir"
7
- require "cgi"
1
+ # frozen_string_literal: true
8
2
 
9
- require 'dpkg/s3/utils'
10
-
11
- class Dpkg::S3::Package
12
- include Dpkg::S3::Utils
13
-
14
- attr_accessor :name
15
- attr_accessor :version
16
- attr_accessor :epoch
17
- attr_accessor :iteration
18
- attr_accessor :maintainer
19
- attr_accessor :vendor
20
- attr_accessor :url
21
- attr_accessor :category
22
- attr_accessor :license
23
- attr_accessor :architecture
24
- attr_accessor :description
25
-
26
- attr_accessor :dependencies
27
-
28
- # Any other attributes specific to this package.
29
- # This is where you'd put rpm, deb, or other specific attributes.
30
- attr_accessor :attributes
31
-
32
- # hashes
33
- attr_accessor :url_filename
34
- attr_accessor :sha1
35
- attr_accessor :sha256
36
- attr_accessor :md5
37
- attr_accessor :size
38
-
39
- attr_accessor :filename
40
-
41
- class << self
42
- include Dpkg::S3::Utils
43
-
44
- def parse_file(package)
45
- p = self.new
46
- p.extract_info(extract_control(package))
47
- p.apply_file_info(package)
48
- p.filename = package
49
- p
50
- end
3
+ require 'digest/sha1'
4
+ require 'digest/sha2'
5
+ require 'digest/md5'
6
+ require 'English'
7
+ require 'socket'
8
+ require 'tmpdir'
9
+ require 'cgi'
51
10
 
52
- def parse_string(s)
53
- p = self.new
54
- p.extract_info(s)
55
- p
56
- end
11
+ require 'dpkg/s3/utils'
57
12
 
58
- def extract_control(package)
59
- if system("which dpkg > /dev/null 2>&1")
60
- `dpkg -f #{package}`
61
- else
62
- # use ar to determine control file name (control.ext)
63
- package_files = `ar t #{package}`
64
- control_file = package_files.split("\n").select do |file|
65
- file.start_with?("control.")
66
- end.first
67
- if control_file === "control.tar.gz"
68
- compression = "z"
69
- else
70
- compression = "J"
13
+ # Dpkg is the root module for all storage modules including S3
14
+ module Dpkg
15
+ # S3 storage module resposible of handling packages on S3 including upload, delete
16
+ module S3
17
+ # Package class inlcudes methods for extracting the control file of a debian archive and extracting
18
+ # information required for publishing package
19
+ class Package
20
+ include Dpkg::S3::Utils
21
+
22
+ attr_accessor :name, :version, :epoch, :iteration, :maintainer, :vendor, :url, :category, :license, :architecture,
23
+ :description, :dependencies, :sha1, :sha256, :md5, :size, :filename, :url_filename
24
+
25
+ # Any other attributes specific to this package.
26
+ # This is where you'd put rpm, deb, or other specific attributes.
27
+ attr_accessor :attributes
28
+
29
+ class << self
30
+ include Dpkg::S3::Utils
31
+
32
+ def parse_file(package)
33
+ p = new
34
+ p.extract_info(extract_control(package))
35
+ p.apply_file_info(package)
36
+ p.filename = package
37
+ p
71
38
  end
72
39
 
73
- # ar fails to find the control.tar.gz tarball within the .deb
74
- # on Mac OS. Try using ar to list the control file, if found,
75
- # use ar to extract, otherwise attempt with tar which works on OS X.
76
- extract_control_tarball_cmd = "ar p #{package} #{control_file}"
77
-
78
- begin
79
- safesystem("ar t #{package} #{control_file} &> /dev/null")
80
- rescue SafeSystemError
81
- warn "Failed to find control data in .deb with ar, trying tar."
82
- extract_control_tarball_cmd = "tar #{compression}xf #{package} --to-stdout #{control_file}"
40
+ def parse_string(str)
41
+ p = new
42
+ p.extract_info(str)
43
+ p
83
44
  end
84
45
 
85
- Dir.mktmpdir do |path|
86
- safesystem("#{extract_control_tarball_cmd} | tar -#{compression}xf - -C #{path}")
87
- File.read(File.join(path, "control"), :encoding => "UTF-8")
46
+ def extract_control(package)
47
+ if system('which dpkg > /dev/null 2>&1')
48
+ `dpkg -f #{package}`
49
+ else
50
+ # use ar to determine control file name (control.ext)
51
+ package_files = `ar t #{package}`
52
+ control_file = package_files.split("\n").select do |file|
53
+ file.start_with?('control.')
54
+ end.first
55
+ compression = if control_file == 'control.tar.gz'
56
+ 'z'
57
+ else
58
+ 'J'
59
+ end
60
+
61
+ # ar fails to find the control.tar.gz tarball within the .deb
62
+ # on Mac OS. Try using ar to list the control file, if found,
63
+ # use ar to extract, otherwise attempt with tar which works on OS X.
64
+ extract_control_tarball_cmd = "ar p #{package} #{control_file}"
65
+
66
+ begin
67
+ safesystem("ar t #{package} #{control_file} &> /dev/null")
68
+ rescue SafeSystemError
69
+ warn 'Failed to find control data in .deb with ar, trying tar.'
70
+ extract_control_tarball_cmd = "tar #{compression}xf #{package} --to-stdout #{control_file}"
71
+ end
72
+
73
+ Dir.mktmpdir do |path|
74
+ safesystem("#{extract_control_tarball_cmd} | tar -#{compression}xf - -C #{path}")
75
+ File.read(File.join(path, 'control'), encoding: 'UTF-8')
76
+ end
77
+ end
88
78
  end
89
79
  end
90
- end
91
- end
92
-
93
- def initialize
94
- @attributes = {}
95
-
96
- # Reference
97
- # http://www.debian.org/doc/manuals/maint-guide/first.en.html
98
- # http://wiki.debian.org/DeveloperConfiguration
99
- # https://github.com/jordansissel/fpm/issues/37
100
- if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
101
- # Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
102
- @maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
103
- else
104
- # TODO(sissel): Maybe support using 'git config' for a default as well?
105
- # git config --get user.name, etc can be useful.
106
- #
107
- # Otherwise default to user@currenthost
108
- @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
109
- end
110
80
 
111
- @name = nil
112
- @architecture = "native"
113
- @description = "no description given"
114
- @version = nil
115
- @epoch = nil
116
- @iteration = nil
117
- @url = nil
118
- @category = "default"
119
- @license = "unknown"
120
- @vendor = "none"
121
- @sha1 = nil
122
- @sha256 = nil
123
- @md5 = nil
124
- @size = nil
125
- @filename = nil
126
- @url_filename = nil
127
-
128
- @dependencies = []
129
- end
81
+ def initialize
82
+ @attributes = {}
83
+
84
+ # Reference
85
+ # http://www.debian.org/doc/manuals/maint-guide/first.en.html
86
+ # http://wiki.debian.org/DeveloperConfiguration
87
+ # https://github.com/jordansissel/fpm/issues/37
88
+ @maintainer = if ENV.include?('DEBEMAIL') && ENV.include?('DEBFULLNAME')
89
+ # Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
90
+ "#{ENV['DEBFULLNAME']} <#{ENV['DEBEMAIL']}>"
91
+ else
92
+ # TODO(sissel): Maybe support using 'git config' for a default as well?
93
+ # git config --get user.name, etc can be useful.
94
+ #
95
+ # Otherwise default to user@currenthost
96
+ "<#{ENV['USER']}@#{Socket.gethostname}>"
97
+ end
98
+
99
+ @name = nil
100
+ @architecture = 'native'
101
+ @description = 'no description given'
102
+ @version = nil
103
+ @epoch = nil
104
+ @iteration = nil
105
+ @url = nil
106
+ @category = 'default'
107
+ @license = 'unknown'
108
+ @vendor = 'none'
109
+ @sha1 = nil
110
+ @sha256 = nil
111
+ @md5 = nil
112
+ @size = nil
113
+ @filename = nil
114
+ @url_filename = nil
115
+
116
+ @dependencies = []
117
+ end
130
118
 
131
- def full_version
132
- return nil if [epoch, version, iteration].all?(&:nil?)
133
- [[epoch, version].compact.join(":"), iteration].compact.join("-")
134
- end
119
+ def full_version
120
+ return nil if [epoch, version, iteration].all?(&:nil?)
135
121
 
136
- def filename=(f)
137
- @filename = f
138
- @filename
139
- end
122
+ [[epoch, version].compact.join(':'), iteration].compact.join('-')
123
+ end
140
124
 
141
- def url_filename(codename)
142
- @url_filename || "pool/#{codename}/#{self.name[0]}/#{self.name[0..1]}/#{File.basename(self.filename)}"
143
- end
125
+ def url_filename(codename) # rubocop:disable Lint/DuplicateMethods
126
+ @url_filename || "pool/#{codename}/#{name[0]}/#{name[0..1]}/#{File.basename(filename)}"
127
+ end
144
128
 
145
- def url_filename_encoded(codename)
146
- @url_filename || "pool/#{codename}/#{self.name[0]}/#{self.name[0..1]}/#{s3_escape(File.basename(self.filename))}"
147
- end
129
+ def url_filename_encoded(codename)
130
+ @url_filename || "pool/#{codename}/#{name[0]}/#{name[0..1]}/#{s3_escape(File.basename(filename))}"
131
+ end
148
132
 
149
- def generate(codename = nil)
150
- template("package.erb").result(binding)
151
- end
133
+ def generate(codename = nil)
134
+ template('package.erb').result(binding)
135
+ end
152
136
 
153
- # from fpm
154
- def parse_depends(data)
155
- return [] if data.nil? or data.empty?
156
- # parse dependencies. Debian dependencies come in one of two forms:
157
- # * name
158
- # * name (op version)
159
- # They are all on one line, separated by ", "
160
-
161
- dep_re = /^([^ ]+)(?: \(([>=<]+) ([^)]+)\))?$/
162
- return data.split(/, */).collect do |dep|
163
- m = dep_re.match(dep)
164
- if m
165
- name, op, version = m.captures
166
- # this is the proper form of dependency
167
- if op && version && op != "" && version != ""
168
- "#{name} (#{op} #{version})".strip
169
- else
170
- name.strip
137
+ # from fpm
138
+ def parse_depends(data)
139
+ return [] if data.nil? || data.empty?
140
+
141
+ # parse dependencies. Debian dependencies come in one of two forms:
142
+ # * name
143
+ # * name (op version)
144
+ # They are all on one line, separated by ", "
145
+
146
+ dep_re = /^([^ ]+)(?: \(([>=<]+) ([^)]+)\))?$/
147
+ data.split(/, */).collect do |dep|
148
+ m = dep_re.match(dep)
149
+ if m
150
+ name, op, version = m.captures
151
+ # this is the proper form of dependency
152
+ if op && version && op != '' && version != ''
153
+ "#{name} (#{op} #{version})".strip
154
+ else
155
+ name.strip
156
+ end
157
+ else
158
+ # Assume normal form dependency, "name op version".
159
+ dep
160
+ end
171
161
  end
172
- else
173
- # Assume normal form dependency, "name op version".
174
- dep
175
- end
176
- end
177
- end # def parse_depends
178
-
179
- # from fpm
180
- def fix_dependency(dep)
181
- # Deb dependencies are: NAME (OP VERSION), like "zsh (> 3.0)"
182
- # Convert anything that looks like 'NAME OP VERSION' to this format.
183
- if dep =~ /[\(,\|]/
184
- # Don't "fix" ones that could appear well formed already.
185
- else
186
- # Convert ones that appear to be 'name op version'
187
- name, op, version = dep.split(/ +/)
188
- if !version.nil?
189
- # Convert strings 'foo >= bar' to 'foo (>= bar)'
190
- dep = "#{name} (#{debianize_op(op)} #{version})"
191
162
  end
192
- end
193
-
194
- name_re = /^[^ \(]+/
195
- name = dep[name_re]
196
- if name =~ /[A-Z]/
197
- dep = dep.gsub(name_re) { |n| n.downcase }
198
- end
199
163
 
200
- if dep.include?("_")
201
- dep = dep.gsub("_", "-")
202
- end
164
+ # from fpm
165
+ def fix_dependency(dep)
166
+ # Deb dependencies are: NAME (OP VERSION), like "zsh (> 3.0)"
167
+ # Convert anything that looks like 'NAME OP VERSION' to this format.
168
+ if dep =~ /[(,|]/
169
+ # Don't "fix" ones that could appear well formed already.
170
+ else
171
+ # Convert ones that appear to be 'name op version'
172
+ name, op, version = dep.split(/ +/)
173
+ unless version.nil?
174
+ # Convert strings 'foo >= bar' to 'foo (>= bar)'
175
+ dep = "#{name} (#{debianize_op(op)} #{version})"
176
+ end
177
+ end
203
178
 
204
- # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
205
- if dep =~ /\(~>/
206
- name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
207
- nextversion = version.split(".").collect { |v| v.to_i }
208
- l = nextversion.length
209
- nextversion[l-2] += 1
210
- nextversion[l-1] = 0
211
- nextversion = nextversion.join(".")
212
- return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
213
- elsif (m = dep.match(/(\S+)\s+\(!= (.+)\)/))
214
- # Append this to conflicts
215
- self.conflicts += [dep.gsub(/!=/,"=")]
216
- return []
217
- elsif (m = dep.match(/(\S+)\s+\(= (.+)\)/)) and
218
- self.attributes[:deb_ignore_iteration_in_dependencies?]
219
- # Convert 'foo (= x)' to 'foo (>= x)' and 'foo (<< x+1)'
220
- # but only when flag --ignore-iteration-in-dependencies is passed.
221
- name, version = m[1..2]
222
- nextversion = version.split('.').collect { |v| v.to_i }
223
- nextversion[-1] += 1
224
- nextversion = nextversion.join(".")
225
- return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
226
- else
227
- # otherwise the dep is probably fine
228
- return dep.rstrip
229
- end
230
- end # def fix_dependency
179
+ name_re = /^[^ (]+/
180
+ name = dep[name_re]
181
+ dep = dep.gsub(name_re, &:downcase) if name =~ /[A-Z]/
182
+
183
+ dep = dep.gsub('_', '-') if dep.include?('_')
184
+
185
+ # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
186
+ if dep =~ /\(~>/
187
+ name, version = dep.gsub(/[()~>]/, '').split(/ +/)[0..1]
188
+ nextversion = version.split('.').collect(&:to_i)
189
+ l = nextversion.length
190
+ nextversion[l - 2] += 1
191
+ nextversion[l - 1] = 0
192
+ nextversion = nextversion.join('.')
193
+ ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
194
+ elsif (m = dep.match(/(\S+)\s+\(!= (.+)\)/))
195
+ # Append this to conflicts
196
+ self.conflicts += [dep.gsub(/!=/, '=')]
197
+ []
198
+ elsif (m = dep.match(/(\S+)\s+\(= (.+)\)/)) &&
199
+ attributes[:deb_ignore_iteration_in_dependencies?]
200
+ # Convert 'foo (= x)' to 'foo (>= x)' and 'foo (<< x+1)'
201
+ # but only when flag --ignore-iteration-in-dependencies is passed.
202
+ name, version = m[1..2]
203
+ nextversion = version.split('.').collect(&:to_i)
204
+ nextversion[-1] += 1
205
+ nextversion = nextversion.join('.')
206
+ ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
207
+ else
208
+ # otherwise the dep is probably fine
209
+ dep.rstrip
210
+ end
211
+ end
231
212
 
232
- # from fpm
233
- def extract_info(control)
234
- fields = parse_control(control)
213
+ # from fpm
214
+ def extract_info(control)
215
+ fields = parse_control(control)
216
+
217
+ # Parse 'epoch:version-iteration' in the version string
218
+ full_version = fields.delete('Version')
219
+ raise "Unsupported version string '#{full_version}'" if full_version !~ /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
220
+
221
+ self.epoch, self.version, self.iteration = $LAST_MATCH_INFO.captures
222
+
223
+ self.architecture = fields.delete('Architecture')
224
+ self.category = fields.delete('Section')
225
+ self.license = fields.delete('License') || license
226
+ self.maintainer = fields.delete('Maintainer')
227
+ self.name = fields.delete('Package')
228
+ self.url = fields.delete('Homepage')
229
+ self.vendor = fields.delete('Vendor') || vendor
230
+ attributes[:deb_priority] = fields.delete('Priority')
231
+ attributes[:deb_origin] = fields.delete('Origin')
232
+ attributes[:deb_installed_size] = fields.delete('Installed-Size')
233
+
234
+ # Packages manifest fields
235
+ filename = fields.delete('Filename')
236
+ self.url_filename = filename
237
+ self.sha1 = fields.delete('SHA1')
238
+ self.sha256 = fields.delete('SHA256')
239
+ self.md5 = fields.delete('MD5sum')
240
+ self.size = fields.delete('Size')
241
+ self.description = fields.delete('Description')
242
+
243
+ # self.config_files = config_files
244
+
245
+ self.dependencies += Array(parse_depends(fields.delete('Depends')))
246
+
247
+ attributes[:deb_recommends] = fields.delete('Recommends')
248
+ attributes[:deb_suggests] = fields.delete('Suggests')
249
+ attributes[:deb_enhances] = fields.delete('Enhances')
250
+ attributes[:deb_pre_depends] = fields.delete('Pre-Depends')
251
+
252
+ attributes[:deb_breaks] = fields.delete('Breaks')
253
+ attributes[:deb_conflicts] = fields.delete('Conflicts')
254
+ attributes[:deb_provides] = fields.delete('Provides')
255
+ attributes[:deb_replaces] = fields.delete('Replaces')
256
+
257
+ attributes[:deb_field] = fields.map do |k, v|
258
+ [k.sub(/\AX[BCS]{0,3}-/, ''), v]
259
+ end.to_h
260
+ end
235
261
 
236
- # Parse 'epoch:version-iteration' in the version string
237
- full_version = fields.delete('Version')
238
- if full_version !~ /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
239
- raise "Unsupported version string '#{full_version}'"
240
- end
241
- self.epoch, self.version, self.iteration = $~.captures
242
-
243
- self.architecture = fields.delete('Architecture')
244
- self.category = fields.delete('Section')
245
- self.license = fields.delete('License') || self.license
246
- self.maintainer = fields.delete('Maintainer')
247
- self.name = fields.delete('Package')
248
- self.url = fields.delete('Homepage')
249
- self.vendor = fields.delete('Vendor') || self.vendor
250
- self.attributes[:deb_priority] = fields.delete('Priority')
251
- self.attributes[:deb_origin] = fields.delete('Origin')
252
- self.attributes[:deb_installed_size] = fields.delete('Installed-Size')
253
-
254
- # Packages manifest fields
255
- filename = fields.delete('Filename')
256
- self.url_filename = filename && CGI.unescape(filename)
257
- self.sha1 = fields.delete('SHA1')
258
- self.sha256 = fields.delete('SHA256')
259
- self.md5 = fields.delete('MD5sum')
260
- self.size = fields.delete('Size')
261
- self.description = fields.delete('Description')
262
-
263
- #self.config_files = config_files
264
-
265
- self.dependencies += Array(parse_depends(fields.delete('Depends')))
266
-
267
- self.attributes[:deb_recommends] = fields.delete('Recommends')
268
- self.attributes[:deb_suggests] = fields.delete('Suggests')
269
- self.attributes[:deb_enhances] = fields.delete('Enhances')
270
- self.attributes[:deb_pre_depends] = fields.delete('Pre-Depends')
271
-
272
- self.attributes[:deb_breaks] = fields.delete('Breaks')
273
- self.attributes[:deb_conflicts] = fields.delete('Conflicts')
274
- self.attributes[:deb_provides] = fields.delete('Provides')
275
- self.attributes[:deb_replaces] = fields.delete('Replaces')
276
-
277
- self.attributes[:deb_field] = Hash[fields.map { |k, v|
278
- [k.sub(/\AX[BCS]{0,3}-/, ''), v]
279
- }]
280
- end # def extract_info
281
-
282
- def apply_file_info(file)
283
- self.size = File.size(file)
284
- self.sha1 = Digest::SHA1.file(file).hexdigest
285
- self.sha256 = Digest::SHA2.file(file).hexdigest
286
- self.md5 = Digest::MD5.file(file).hexdigest
287
- end
262
+ def apply_file_info(file)
263
+ self.size = File.size(file)
264
+ self.sha1 = Digest::SHA1.file(file).hexdigest
265
+ self.sha256 = Digest::SHA2.file(file).hexdigest
266
+ self.md5 = Digest::MD5.file(file).hexdigest
267
+ end
288
268
 
289
- def parse_control(control)
290
- field = nil
291
- value = ""
292
- {}.tap do |fields|
293
- control.each_line do |line|
294
- if line =~ /^(\s+)(\S.*)$/
295
- indent, rest = $1, $2
296
- # Continuation
297
- if indent.size == 1 && rest == "."
298
- value << "\n"
299
- rest = ""
300
- elsif value.size > 0
301
- value << "\n"
269
+ def parse_control(control)
270
+ field = nil
271
+ value = ''
272
+ {}.tap do |fields|
273
+ control.each_line do |line|
274
+ case line
275
+ when /^(\s+)(\S.*)$/
276
+ indent = Regexp.last_match(1)
277
+ rest = Regexp.last_match(2)
278
+ # Continuation
279
+ if indent.size == 1 && rest == '.'
280
+ value << "\n"
281
+ rest = ''
282
+ elsif value.size.positive?
283
+ value << "\n"
284
+ end
285
+ value << rest
286
+ when /^([-\w]+):(.*)$/
287
+ fields[field] = value if field
288
+ field = Regexp.last_match(1)
289
+ value = Regexp.last_match(2).strip
290
+ end
302
291
  end
303
- value << rest
304
- elsif line =~ /^([-\w]+):(.*)$/
305
292
  fields[field] = value if field
306
- field, value = $1, $2.strip
307
293
  end
308
294
  end
309
- fields[field] = value if field
310
295
  end
311
296
  end
312
297
  end