dpkg-s3 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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