frm 0.0.9 → 0.0.10

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/bin/frm CHANGED
@@ -24,13 +24,13 @@ FRM allows you to easily create debian repos on S3
24
24
 
25
25
  Usage: frm [options] package1 [package2] ..."
26
26
 
27
- options[:access_key] = nil
27
+ options[:access_key] = ENV['AWS_ACCESS_KEY_ID']
28
28
  opts.on( '-a', '--access-key ACCESS_KEY', 'S3 public access key' ) do |key|
29
29
  options[:access_key] = key
30
30
  end
31
31
 
32
32
 
33
- options[:secret_key] = nil
33
+ options[:secret_key] = ENV['AWS_SECRET_ACCESS_KEY']
34
34
  opts.on( '-s', '--secret-key SECRET_KEY', 'S3 private access key' ) do |key|
35
35
  options[:secret_key] = key
36
36
  end
@@ -74,7 +74,7 @@ optparse.parse!
74
74
 
75
75
 
76
76
  ARGV.each do |package|
77
- packages << File.join(Dir.pwd,package)
77
+ packages << package
78
78
  end
79
79
 
80
80
 
@@ -87,7 +87,8 @@ end
87
87
  print_and_exit "you need to specify at least one package to publish!!!" if packages.empty?
88
88
 
89
89
  packages.each do |package|
90
- print_and_exit "package '#{package}' does not exits!!!" unless File.exists?(package)
90
+ puts "package is #{package}"
91
+ print_and_exit "package '#{package}' does not exist!!!" unless File.exists?(package)
91
92
  print_and_exit "package '#{package}' does not appear to be a valid deb" unless system("dpkg --field #{package} > /dev/null 2>&1 ")
92
93
  end
93
94
 
@@ -95,16 +96,13 @@ end
95
96
  # if we have gotten this far it means that all required parameters have been set, so let's begin
96
97
 
97
98
 
98
- # chdir to the top level of the code
99
- Dir.chdir(File.dirname(File.dirname(__FILE__)))
100
-
101
99
  # require the necessary FRM libs
102
- require File.join Dir.pwd, 'lib', 'frm'
100
+ require_relative '../lib/frm'
103
101
 
104
102
  puts "creating release..."
105
- package_release = FRM::PackageRelease.new(packages,options[:release])
103
+ release = FRM::Release.new(options.merge(package_paths: packages))
106
104
 
107
105
  puts "pushing release..."
108
- release_pusher = FRM::ReleasePusher.new(package_release,options[:access_key],options[:secret_key],options[:bucket],options[:prefix],options[:public_repo])
106
+ release.push
109
107
 
110
108
  puts "looks like everything ran ok!"
data/frm.gemspec CHANGED
@@ -21,6 +21,6 @@ Gem::Specification.new do |s|
21
21
  s.require_paths = ["lib"]
22
22
 
23
23
  s.add_dependency('aws-sdk')
24
-
24
+ s.add_runtime_dependency "net-ntp", '~> 2.1.1'
25
25
  end
26
26
 
@@ -0,0 +1,180 @@
1
+ module FRM
2
+
3
+ class ArchRelease < Base
4
+ attr_reader :opts
5
+
6
+ def initialize(opts={})
7
+ super()
8
+ @opts = opts
9
+ @opts[:packages] = []
10
+ @opts[:package_paths] ||= []
11
+ @opts[:release] ||= run ". /etc/lsb-release && echo $DISTRIB_CODENAME"
12
+ @opts[:component] ||= 'main'
13
+ @opts[:origin] ||= 'FRM'
14
+ @opts[:lablel] ||= 'FRM'
15
+ @opts[:description] = "FRM apt repo"
16
+ @opts[:package_paths].each { |path| @opts[:packages] << Package.new(path) }
17
+ case run("uname -m")
18
+ when "x86_64"
19
+ @opts[:arch] ||= 'amd64'
20
+ else
21
+ @opts[:arch] ||= run "uname -m"
22
+ end
23
+ raise "you need to pass a remote_store object that responds to exists? and get"\
24
+ unless @opts[:remote_store] \
25
+ and @opts[:remote_store].respond_to? 'exists?' \
26
+ and @opts[:remote_store].respond_to? 'get'
27
+
28
+ @opts[:release_file_path] = "#{@opts[:component]}/binary-#{@opts[:arch]}/Release"
29
+ @opts[:package_file_path] = "#{@opts[:component]}/binary-#{@opts[:arch]}/Packages"
30
+ @opts[:gzipped_package_file_path] = "#{@opts[:component]}/binary-#{@opts[:arch]}/Packages.gz"
31
+
32
+ @opts[:package_file] = merge_package_files
33
+ @opts[:gzipped_package_file] = generate_gzip_pipe(@opts[:package_file]).read
34
+ @opts[:release_file] = release_file
35
+
36
+ @opts[:package_file_size] = @opts[:package_file].size
37
+ @opts[:gzipped_package_file_size] = @opts[:gzipped_package_file].size
38
+ @opts[:release_file_size] = @opts[:release_file].size
39
+
40
+ @opts[:package_file_md5sum] = compute_md5(@opts[:package_file])
41
+ @opts[:gzipped_package_file_md5sum] = compute_md5(@opts[:gzipped_package_file])
42
+ @opts[:release_file_md5sum] = compute_md5(@opts[:release_file])
43
+
44
+ @opts[:package_file_sha1] = compute_sha1(@opts[:package_file])
45
+ @opts[:gzipped_package_file_sha1] = compute_sha1(@opts[:gzipped_package_file])
46
+ @opts[:release_file_sha1] = compute_sha1(@opts[:release_file])
47
+
48
+ @opts[:package_file_sha256] = compute_sha2(@opts[:package_file])
49
+ @opts[:gzipped_package_file_sha256] = compute_sha2(@opts[:gzipped_package_file])
50
+ @opts[:release_file_sha256] = compute_sha2(@opts[:release_file])
51
+ end
52
+
53
+ def push
54
+ @opts[:packages].each do |p|
55
+ if @opts[:remote_store].exists?(p.info['Filename'])
56
+ STDERR.puts "package #{p.path} already exists"
57
+ unless @opts[:remote_store].etag(p.info['Filename']) == p.info['MD5sum']
58
+ raise <<EOE
59
+ trying to overwrite this package file: #{remote_path}
60
+ local md5 is #{package.info['MD5sum']}
61
+ remote md5 (etag) is #{@s3.etag(remote_path,@bucket)}
62
+ EOE
63
+ end
64
+ else
65
+ STDERR.puts "pushing package #{p.path}"
66
+ @opts[:remote_store].put(p.info['Filename'],p.content)
67
+ end
68
+ end
69
+ STDERR.puts "pushing arch release files"
70
+ @opts[:remote_store].put(File.join("dists/#{@opts[:release]}/",@opts[:release_file_path]),@opts[:release_file])
71
+ @opts[:remote_store].put(File.join("dists/#{@opts[:release]}/",@opts[:gzipped_package_file_path]),@opts[:gzipped_package_file])
72
+ @opts[:remote_store].put(File.join("dists/#{@opts[:release]}/",@opts[:package_file_path]),@opts[:package_file])
73
+ end
74
+
75
+ private
76
+
77
+ def release_file
78
+ return <<-EOF
79
+ Component: #{@opts[:component]}
80
+ Origin: #{@opts[:origin]}
81
+ Label: #{@opts[:lablel]}
82
+ Architecture: #{@opts[:arch]}
83
+ Description: #{@opts[:description]}
84
+ EOF
85
+ end
86
+
87
+ def previous_package_file
88
+ package_file_path = "dists/#{@opts[:release]}/#{@opts[:package_file_path]}"
89
+ return @opts[:remote_store].get(package_file_path) \
90
+ if @opts[:remote_store].exists? package_file_path
91
+ return ""
92
+ end
93
+
94
+
95
+ # given a package file as a string, parse out the next package
96
+ def parse_package_stub(package_file='')
97
+
98
+ # find the first package header
99
+ stub_start = package_file.index(/^Package: /)
100
+ return nil if stub_start.nil?
101
+
102
+ # find the end of the first package section
103
+ stub_end = package_file.index(/^$/,stub_start)
104
+ raise "could not parse #{package_file}" if stub_end.nil?
105
+
106
+ # extract out the next stub
107
+ stub = package_file.slice(stub_start,stub_end) + "\n"
108
+ raise "could not parse #{package_file}" if stub.nil?
109
+
110
+ # pull out the package line from the stub
111
+ package_line = stub[/^Package: .*$/]
112
+ raise "could not parse #{package_file}" if package_line.nil?
113
+
114
+ # pull out the name of the package
115
+ package = package_line.sub('Package: ','').strip
116
+ raise "could not read package name from #{stub}" if package.empty?
117
+
118
+ return stub, stub_end, package
119
+ end
120
+
121
+
122
+ # recursive function to merge two sets of packages
123
+ def merge_package_files(opts={})
124
+
125
+ # initial settings
126
+ opts[:previous_package_file] ||= previous_package_file
127
+ opts[:new_packages] ||= @opts[:packages].collect{|p| p.to_h}.sort{|a,b| a['Package'] <=> b['Package'] }
128
+ opts[:new_package_file] ||= ""
129
+
130
+ # if we are at the end of both the old package list and the new set of packages then finish
131
+ return opts[:new_package_file] \
132
+ if opts[:previous_package_file].strip.empty? \
133
+ and opts[:new_packages].empty?
134
+
135
+ # if we don't have any new packages to add then return the current list with the old appended
136
+ return (opts[:new_package_file] << opts[:previous_package_file]) \
137
+ if opts[:new_packages].empty?
138
+
139
+ # parse next package stub
140
+ stub, stub_end, next_package = parse_package_stub(opts[:previous_package_file])
141
+
142
+ # if we don't have and more old packages to add then return the
143
+ # current list with the new appended
144
+ return (opts[:new_package_file] << opts[:new_packages].collect{|p| FRM::Package.hash_to_stub(p)}.join("\n")) if stub.nil?
145
+
146
+ # compare the next previous existing pacakge to the next new package
147
+ case next_package <=> opts[:new_packages].first['Package']
148
+ when -1
149
+ opts[:new_package_file] << stub
150
+ opts[:previous_package_file].slice!(0..stub_end)
151
+ when 1
152
+ opts[:new_package_file] << FRM::Package.hash_to_stub(opts[:new_packages].shift)
153
+ when 0
154
+ # both packages have the same name
155
+ STDERR.puts "I see two version of this pakcage: #{next_package}"
156
+ previous_version_line = stub[/^Version: .*$/]
157
+ raise "could not get version from package stub: \n#{stub}" if previous_version_line.nil?
158
+
159
+ previous_version_raw = previous_version_line.sub('Version: ','').strip
160
+ raise "could not get version from package stub: \n#{stub}" if previous_version_raw.empty?
161
+ previous_version = Gem::Version.new(previous_version_raw)
162
+
163
+ newer_version_raw = opts[:new_packages].first['Version']
164
+ newer_version = Gem::Version.new(newer_version_raw)
165
+
166
+ raise "previous version #{previous_version_raw} is equal to #{newer_version_raw}. Exiting..." \
167
+ if previous_version == newer_version
168
+
169
+ raise "previous version #{previous_version_raw} is newer than #{newer_version_raw}. Exiting..." \
170
+ if previous_version > newer_version
171
+
172
+ opts[:new_package_file] << FRM::Package.hash_to_stub(opts[:new_packages].shift)
173
+ opts[:previous_package_file].slice!(0..stub_end)
174
+ end
175
+
176
+ return merge_package_files(opts)
177
+ end
178
+
179
+ end
180
+ end
data/lib/frm/base.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  module FRM
2
2
  class Base
3
+ def initialize
4
+ @retries = 3
5
+ end
3
6
 
4
7
  def compute_md5(string)
5
8
  Digest::MD5.hexdigest(string)
@@ -58,80 +61,24 @@ module FRM
58
61
  return unzipped_string
59
62
  end
60
63
 
61
- def parse_package_stub(read_buffer)
62
- package = {}
63
- stub = ""
64
- while line = read_buffer.gets
65
- return nil if line.strip.empty?
66
- raise "bad input" unless (match = line.match /^\w+\-?\w+?: /)
67
- stub << line
68
- key = match[0].delete ': ' # if match
69
- value = match.post_match.strip
70
- package[key] = value
71
- if key == 'Description'
72
- while (line = read_buffer.gets).strip != ""
73
- package['Description'] << line
74
- stub << line
75
- end
76
- package['Description'].rstrip!
77
- return package, stub
64
+ def handle_errors(&block)
65
+ retries = 0
66
+ begin
67
+ yield
68
+ rescue Object => error_object
69
+ if retries < @retries
70
+ sleep(2**retries * 1) #exponential backoff, 1s base
71
+ retries += 1
72
+ STDERR.puts "encountered error #{error_object.inspect}, retrying attempt #{retries}."
73
+ retry
74
+ else
75
+ logger.error "encountered error #{error_object.inspect}, aborting."
76
+ raise error_object
78
77
  end
79
78
  end
80
- nil
81
- end
82
-
83
- def merge_package_file(in_pipe,out_pipe,package_list)
84
- sorted_list = package_list.sort { |a,b| a['Package'] <=> b['Package'] }
85
- merge(in_pipe,out_pipe,sorted_list)
86
79
  end
87
-
88
- private
89
-
90
- def create_package_stub(package_hash)
91
- return_value = ''
92
- package_hash.each do |key,value|
93
- return_value << "#{key}: #{value}\n"
94
- end
95
- return_value << "\n"
96
- end
97
-
98
- def merge(in_pipe,out_pipe,package_list)
99
- return if out_pipe.closed?
100
- return if in_pipe.closed? and package_list.empty
101
80
 
102
- if package_list.empty?
103
- while line = in_pipe.gets
104
- out_pipe.puts line
105
- end
106
- in_pipe.close
107
- out_pipe.close
108
- return
109
- end
110
-
111
- if in_pipe.closed?
112
- package_list.each {|package_hash| out_pipe.write(create_package_stub(package_hash)) }
113
- out_pipe.close
114
- return
115
- end
116
-
117
- current_package, stub = parse_package_stub in_pipe
118
-
119
- if current_package['Package'] < package_list.first['Package']
120
- out_pipe.puts stub
121
- out_pipe.puts ""
122
- merge(in_pipe,out_pipe,package_list)
123
- return
124
- elsif current_package['Package'] > package_list.first['Package']
125
- while ( ! package_list.empty? ) and current_package['Package'] > package_list.first['Package']
126
- out_pipe.write create_package_stub(package_list.shift)
127
- end
128
- elsif current_package['Package'] == package_list.first['Package']
129
- out_pipe.write create_package_stub(package_list.shift)
130
- end
131
-
132
- merge(in_pipe,out_pipe,package_list)
133
- return nil
134
- end
81
+ private
135
82
 
136
83
  end
137
84
  end
data/lib/frm/package.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  module FRM
2
2
  class Package < Base
3
- attr_accessor :repo_filename
4
- attr_reader :path, :content, :md5, :sha1, :sha2 , :size, :release
5
- def initialize(path,release='natty')
6
- raise "you need to specify a path!!!" if path.nil?
3
+ attr_reader :path, :content, :info
4
+
5
+ def initialize(path)
7
6
  @path = path
8
- raise "Can not find file '#{path}'" unless File.exists?(path)
9
- @release = release
7
+
8
+ raise "you need to pass a path!!!" if path.nil?
9
+ raise "Can not find file: '#{path}'" unless File.exists?(path)
10
+ raise "this system does not have the dpkg binary" unless system 'which dpkg > /dev/null'
11
+ raise "file #{path} is not a deb" unless system "dpkg --field #{path} > /dev/null"
12
+
10
13
  begin
11
14
  @content = File.read(path)
12
15
  rescue Object => o
@@ -15,10 +18,37 @@ module FRM
15
18
  STDERR.puts o.backtrace
16
19
  raise "Could not open file '#{path}'. Exiting..."
17
20
  end
18
- @size = File.size(@path)
19
- @md5 = compute_md5(@content)
20
- @sha1 = compute_sha1(@content)
21
- @sha2 = compute_sha2(@content)
21
+
22
+ @info = get_package_info
22
23
  end
24
+
25
+ def to_stub
26
+ @info.collect{|k,v| "#{k}: #{v}"}.join("\n") + "\n\n"
27
+ end
28
+
29
+ def self.hash_to_stub(hash)
30
+ hash.collect{|k,v| "#{k}: #{v}"}.join("\n") + "\n\n"
31
+ end
32
+
33
+ def to_h
34
+ return @info
35
+ end
36
+
37
+ private
38
+
39
+ def get_package_info
40
+ h = {}
41
+ ['Package', 'Version', 'License' ,'Vendor' ,'Architecture' ,'Maintainer', 'Installed-Size' ,'Section', 'Priority', 'Homepage', 'Description' ].each do |value|
42
+ h[value] = run "dpkg --field #{@path} #{value}"
43
+ end
44
+
45
+ h['Filename'] = "pool/main/#{h['Package'][0]}/#{h['Package']}/#{File.basename @path}"
46
+ h['Size'] = File.size(@path)
47
+ h['MD5sum'] = compute_md5(@content)
48
+ h['SHA1'] = compute_sha1(@content)
49
+ h['SHA256'] = compute_sha2(@content)
50
+ return h
51
+ end
52
+
23
53
  end
24
54
  end
@@ -14,28 +14,44 @@ module FRM
14
14
 
15
15
 
16
16
  def push_release_files(package_release)
17
- # TODO: un-hardcode this
18
17
  release_path = @prefix + "/dists/#{@release}/Release"
19
- @s3.put(release_path,package_release.release_file,@bucket)
20
-
21
18
  in_release_path = @prefix + "/dists/#{@release}/InRelease"
22
- @s3.put(in_release_path,gpg_clearsign(package_release.release_file),@bucket)
23
-
24
19
  gpg_release_path = @prefix + "/dists/#{@release}/Release.gpg"
25
- @s3.put(gpg_release_path,gpg_detached(package_release.release_file),@bucket)
26
-
27
- release_file_path = @prefix + "/dists/#{@release}/" + package_release.component
28
- @s3.put(release_file_path + '/Release',package_release.short_release_file,@bucket)
29
- @s3.put(release_file_path + '/Packages',package_release.package_file,@bucket)
30
- @s3.put(release_file_path + '/Packages.gz',package_release.gzipped_package_file,@bucket)
31
20
 
21
+ unless @s3.exists?(release_path,@bucket)
22
+ STDERR.puts "pushing new release files..."
23
+ @s3.put(release_path,package_release.release_file,@bucket)
24
+ @s3.put(in_release_path,gpg_clearsign(package_release.release_file),@bucket)
25
+ @s3.put(gpg_release_path,gpg_detached(package_release.release_file),@bucket)
26
+
27
+
28
+ release_file_path = @prefix + "/dists/#{@release}/" + package_release.component
29
+ @s3.put(release_file_path + '/Release',package_release.short_release_file,@bucket)
30
+ @s3.put(release_file_path + '/Packages',package_release.package_file,@bucket)
31
+ @s3.put(release_file_path + '/Packages.gz',package_release.gzipped_package_file,@bucket)
32
+
33
+
34
+ i386_release_file_path = @prefix + "/dists/#{@release}/" + 'main/binary-i386'
35
+ @s3.put(i386_release_file_path + '/Release',package_release.i386_release_file,@bucket)
36
+ @s3.put(i386_release_file_path + '/Packages',package_release.i386_packages_file,@bucket)
37
+ @s3.put(i386_release_file_path + '/Packages.gz',package_release.gzipped_i386_packages_file,@bucket)
38
+
39
+ # push public key
40
+ @s3.put(@prefix + '/public.key',gpg_export_pubkey,@bucket)
41
+ else
42
+ STDERR.puts "updating releases file"
43
+ release_file = @s3.get(release_path,@bucket)
44
+ File.open('/tmp/in','w'){ |f|
45
+ f.write(release_file)
46
+ }
47
+ in_buffer = File.open('/tmp/in',"r")
48
+ out_buffer = File.open('/tmp/fd',"w")
49
+ STDERR.puts "package_release.packages is #{package_release.packages.inspect}"
50
+ merge_package_file(in_buffer,out_buffer,package_release.packages)
32
51
 
33
- i386_release_file_path = @prefix + "/dists/#{@release}/" + 'main/binary-i386'
34
- @s3.put(i386_release_file_path + '/Release',package_release.i386_release_file,@bucket)
35
- @s3.put(i386_release_file_path + '/Packages',package_release.i386_packages_file,@bucket)
36
- @s3.put(i386_release_file_path + '/Packages.gz',package_release.gzipped_i386_packages_file,@bucket)
37
- # push public key
38
- @s3.put(@prefix + '/public.key',gpg_export_pubkey,@bucket)
52
+ STDERR.puts "not yet implmented"
53
+ Kernel.exit 1
54
+ end
39
55
  end
40
56
 
41
57
 
@@ -44,8 +60,19 @@ module FRM
44
60
  end
45
61
 
46
62
  def push_package(package)
47
- remote_path = @prefix + '/' + package.repo_filename
48
- @s3.put(remote_path,package.content,@bucket)
63
+ remote_path = @prefix + '/' + package.info['Filename']
64
+ if @s3.exists?(remote_path,@bucket)
65
+ unless @s3.etag(remote_path,@bucket) == package.info['MD5sum']
66
+ error_message = <<EOE
67
+ trying to overwrite this package file: #{remote_path}
68
+ local md5 is #{package.info['MD5sum']}
69
+ remote md5 (etag) is #{@s3.etag(remote_path,@bucket)}
70
+ EOE
71
+ raise error_message
72
+ end
73
+ else
74
+ @s3.put(remote_path,package.content,@bucket)
75
+ end
49
76
  end
50
77
 
51
78
 
data/lib/frm/s3.rb CHANGED
@@ -3,76 +3,73 @@ require 'aws-sdk'
3
3
  module FRM
4
4
  class S3 < Base
5
5
 
6
- attr_reader :max_retries, :s3, :acl
7
-
8
- def initialize(access_key_id,secret_access_key,public_repo=false)
9
- @max_retries = 10
10
- AWS.config(:access_key_id => access_key_id,
11
- :secret_access_key => secret_access_key)
6
+ def initialize(opts={})
7
+ super()
8
+ @opts = opts
9
+ @opts[:public_repo] ||= false
10
+ @opts[:acl] = @opts[:public_repo] ? :public_read : :private
11
+ @opts[:aws_access_key] ||= ENV['AWS_ACCESS_KEY_ID']
12
+ @opts[:aws_secret_key] ||= ENV['AWS_SECRET_ACCESS_KEY']
13
+ raise "you either need to pass an aws_access_key option or set the AWS_ACCESS_KEY environment variable" \
14
+ if @opts[:aws_access_key].nil?
15
+ raise "you either need to pass an aws_secret_key option or set the AWS_SECRET_KEY environment variable" \
16
+ if @opts[:aws_secret_key].nil?
17
+ raise "you need to pass in a bucket param" unless @opts[:bucket]
18
+ raise "you need to pass in a prefix param" unless @opts[:prefix]
19
+ @opts[:prefix] << '/' unless @opts[:prefix][-1] == '/'
20
+ AWS.config(:access_key_id => @opts[:access_key_id],
21
+ :secret_access_key => @opts[:secret_access_key])
12
22
  @s3 = AWS::S3.new
13
- @acl = public_repo ? :public_read : :private
14
23
  end
15
-
16
24
 
17
- def key?(key,bucket)
18
- @s3.buckets[bucket].objects[key].exists?
25
+ def exists?(relative_path)
26
+ handle_errors do
27
+ return @s3.buckets[@opts[:bucket]].objects[full_path(relative_path)].exists?
28
+ end
19
29
  end
20
30
 
21
-
22
- def put(key,value,bucket)
23
- @max_retries.times do |i|
24
- begin
25
- @s3.buckets[bucket].objects[key].write(value,acl: @acl)
26
- return true
27
- rescue Object => o
28
- print_retry(__method__,o)
29
- end
30
- raise "could not put object!!!" if i == (@max_retries - 1)
31
+ def etag(relative_path)
32
+ quoted_etag = ""
33
+ handle_errors do
34
+ quoted_etag = @s3.buckets[@opts[:bucket]].objects[full_path(relative_path)].etag
31
35
  end
32
- raise "could not put object!!!"
36
+ # stupid method call is actually putting quotes in the string,
37
+ # so let's remove those:
38
+ return quoted_etag.gsub(/"/,'')
33
39
  end
34
-
35
-
36
- def get(key,bucket)
37
- @max_retries.times do |i|
38
- begin
39
- return @s3.buckets[bucket].objects[key].read
40
- rescue Object => o
41
- print_retry(__method__,o)
42
- end
43
- raise "could not get object!!!" if i == (@max_retries - 1)
40
+
41
+ def put(relative_path,value)
42
+ handle_errors do
43
+ @s3.buckets[@opts[:bucket]].objects[full_path(relative_path)].write(value,acl: @opts[:acl]) #
44
44
  end
45
- raise "could not get object!!!"
45
+ return true
46
46
  end
47
47
 
48
+ def get(relative_path)
49
+ handle_errors do
50
+ return @s3.buckets[@opts[:bucket]].objects[full_path(relative_path)].read
51
+ end
52
+ end
48
53
 
49
- def delete(key,bucket)
50
- begin
51
- @s3.buckets[bucket].objects[object].delete
52
- return true
53
- rescue Object => o
54
- print_retry(__method__,o)
54
+ def delete(relative_path)
55
+ handle_errors do
56
+ @s3.buckets[@opts[:bucket]].objects[full_path(relative_path)].delete
55
57
  end
58
+ return true
56
59
  end
57
60
 
58
-
59
- def move(old_key,new_key,bucket)
60
- begin
61
- @s3.buckets[bucket].objects[old_key].move_to(new_key)
62
- return true
63
- rescue Object => o
64
- print_retry(__method__,o)
61
+ def move(old_relative_path,new_relative_path)
62
+ handle_errors do
63
+ @s3.buckets[@opts[:bucket]].objects[full_path(old_relative_path)].move_to(new_relative_path)
65
64
  end
65
+ return true
66
66
  end
67
67
 
68
68
  protected
69
69
 
70
- def print_retry(action,error)
71
- STDERR.puts "coudld not #{action} object because of:"
72
- STDERR.puts error.inspect
73
- STDERR.puts ", retrying..."
74
- sleep 2
70
+ def full_path(relative_path="")
71
+ return @opts[:prefix] + relative_path
75
72
  end
76
-
73
+
77
74
  end
78
75
  end
data/lib/frm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Frm
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
data/lib/frm.rb CHANGED
@@ -1,14 +1,99 @@
1
-
2
1
  require 'zlib'
3
2
  require 'tempfile'
4
3
  require 'digest/md5'
5
4
  require 'digest/sha1'
6
5
  require 'digest/sha2'
6
+ require 'logger'
7
+ require 'net/ntp'
7
8
 
8
9
  require_relative 'frm/base'
9
10
  require_relative 'frm/s3'
10
11
  require_relative 'frm/package'
11
- require_relative 'frm/release'
12
+ require_relative 'frm/arch_release'
12
13
  require_relative 'frm/release_pusher'
13
14
  require_relative 'frm/deb'
14
15
 
16
+
17
+ module FRM
18
+ class Release < Base
19
+
20
+ def initialize(opts={})
21
+ super()
22
+ @opts = opts
23
+ @opts[:component] ||= 'main'
24
+ @opts[:origin] ||= 'FRM'
25
+ @opts[:label] ||= 'FRM'
26
+ @opts[:description] = "FRM apt repo"
27
+ @opts[:release] ||= run ". /etc/lsb-release && echo $DISTRIB_CODENAME"
28
+ handle_errors{@time = Net::NTP.get("us.pool.ntp.org").time.getutc}
29
+
30
+ case run("uname -m")
31
+ when "x86_64"
32
+ @opts[:arch] ||= 'amd64'
33
+ else
34
+ @opts[:arch] ||= run "uname -m"
35
+ end
36
+
37
+ @opts[:remote_store] = FRM::S3.new(opts)
38
+ @arch_releases = [ArchRelease.new(opts)]
39
+ @arch_releases << ArchRelease.new(arch: 'i386',remote_store: @opts[:remote_store]) \
40
+ if @opts[:arch] == 'amd64'
41
+
42
+ @release_file = release_file
43
+ @in_release_file = gpg_clearsign(release_file)
44
+ @release_gpg_file = gpg_detached(release_file)
45
+
46
+ @release_file_path = "dists/#{@opts[:release]}/Release"
47
+ @in_release_file_path = "dists/#{@opts[:release]}/InRelease"
48
+ @release_gpg_file_path = "dists/#{@opts[:release]}/Release.gpg"
49
+
50
+ end
51
+
52
+ def push
53
+ @arch_releases.each {|arch_release| arch_release.push}
54
+ @opts[:remote_store].put(@release_file_path,@release_file)
55
+ @opts[:remote_store].put(@in_release_file_path,@in_release_file)
56
+ @opts[:remote_store].put(@release_gpg_file_path,@release_gpg_file)
57
+
58
+ # push public key
59
+ @opts[:remote_store].put('public.key',gpg_export_pubkey)
60
+ end
61
+
62
+ def merge
63
+ end
64
+
65
+ private
66
+
67
+
68
+ # ubuntu precicse 64 wants i386 debs by default :\
69
+ def generate_i386_stubs()
70
+ end
71
+
72
+ def release_file()
73
+ partial_release_file = <<EOF
74
+ Origin: #{@opts[:origin]}
75
+ Label: #{@opts[:label]}
76
+ Codename: #{@opts[:release]}
77
+ Date: #{@time.getutc.strftime("%a, %d %b %Y %H:%M:%S UTC")}
78
+ Architectures: #{@arch_releases.collect{|r| r.opts[:arch]}.join(' ')}
79
+ Components: #{@opts[:component]}
80
+ Description: #{@opts[:description]}
81
+ EOF
82
+
83
+ %w{MD5Sum SHA1 SHA256}.each do |hash|
84
+ partial_release_file << "#{hash}:\n"
85
+ @arch_releases.each do |arch_release|
86
+ %w{package gzipped_package release}.each do |file|
87
+ partial_release_file << " #{arch_release.opts["#{file}_file_#{hash.downcase}".to_sym]} "
88
+ partial_release_file << arch_release.opts["#{file}_file_size".to_sym].to_s
89
+ partial_release_file << " "
90
+ partial_release_file << arch_release.opts["#{file}_file_path".to_sym]
91
+ partial_release_file << "\n"
92
+ end
93
+ end
94
+ end
95
+ return partial_release_file
96
+ end
97
+
98
+ end
99
+ end
data/test/deb.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'minitest/autorun'
4
- require 'mock-aws-s3'
4
+ #require 'mock-aws-s3'
5
5
 
6
6
  class TestReleasePusher < MiniTest::Unit::TestCase
7
7
  def setup
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-01 00:00:00.000000000 Z
12
+ date: 2013-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-ntp
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.1.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.1.1
30
46
  description: FRM makes it easy to build package repositories on S3
31
47
  email:
32
48
  - ermal14@gmail.com
@@ -43,10 +59,10 @@ files:
43
59
  - bin/frm
44
60
  - frm.gemspec
45
61
  - lib/frm.rb
62
+ - lib/frm/arch_release.rb
46
63
  - lib/frm/base.rb
47
64
  - lib/frm/deb.rb
48
65
  - lib/frm/package.rb
49
- - lib/frm/release.rb
50
66
  - lib/frm/release_pusher.rb
51
67
  - lib/frm/s3.rb
52
68
  - lib/frm/version.rb
data/lib/frm/release.rb DELETED
@@ -1,107 +0,0 @@
1
- module FRM
2
-
3
- class PackageRelease < Base
4
- attr_reader :packages, :standards_version, :priority, :package_file, :gzipped_package_file, :release_file, :short_release_file, :component, :release, :i386_release_file, :i386_packages_file, :gzipped_i386_packages_file
5
- def initialize(packages={},release='natty',component='main/binary-amd64')
6
- @release = release
7
- @component = component
8
- @standards_version = standards_version
9
- @priority = priority
10
- @packages = []
11
- packages.each { |package| @packages << Package.new(package,@release) }
12
- if component == 'main/binary-amd64'
13
- generate_i386_stubs
14
- end
15
- @i386_release_file = <<EOF
16
- Component: main
17
- Origin: apt.cloudscaling.com
18
- Label: apt repository #{@release}
19
- Architecture: i386
20
- Description: Cloudscaling APT repository
21
- EOF
22
- @i386_packages_file = ""
23
- @gzipped_i386_packages_file = generate_gzip_pipe(@i386_packages_file).read
24
- @package_file = generate_package_file
25
- @gzipped_package_file = generate_gzip_pipe(@package_file).read
26
- @short_release_file = generate_short_release_file
27
- @release_file = generate_release_file
28
- end
29
-
30
- private
31
-
32
-
33
- # ubuntu precicse 64 wants i386 debs by default :\
34
- def generate_i386_stubs()
35
- end
36
-
37
-
38
- def generate_release_file()
39
- partial_release_file = "Origin: apt.cloudscaling.com
40
- Label: apt repository #{@release}
41
- Codename: #{@release}
42
- Date: Thu, 22 Dec 2011 00:29:55 UTC
43
- Architectures: amd64 i386
44
- Components: main universe multiverse
45
- Description: Cloudscaling APT repository
46
- MD5Sum:
47
- #{compute_md5(@package_file)} #{@package_file.size} #{@component}/Packages
48
- #{compute_md5(@gzipped_package_file)} #{@gzipped_package_file.size} #{@component}/Packages.gz
49
- #{compute_md5(@short_release_file)} #{@short_release_file.size} #{@component}/Release
50
- #{compute_md5(@i386_packages_file)} #{@i386_packages_file.size} main/binary-i386/Packages
51
- #{compute_md5(@gzipped_i386_packages_file)} #{@gzipped_i386_packages_file.size} main/binary-i386/Packages.gz
52
- #{compute_md5(@i386_release_file)} #{@i386_release_file.size} main/binary-i386/Release
53
- SHA1:
54
- #{compute_sha1(@package_file)} #{@package_file.size} #{@component}/Packages
55
- #{compute_sha1(@gzipped_package_file)} #{@gzipped_package_file.size} #{@component}/Packages.gz
56
- #{compute_sha1(@short_release_file)} #{@short_release_file.size} #{@component}/Release
57
- #{compute_sha1(@i386_packages_file)} #{@i386_packages_file.size} main/binary-i386/Packages
58
- #{compute_sha1(@gzipped_i386_packages_file)} #{@gzipped_i386_packages_file.size} main/binary-i386/Packages.gz
59
- #{compute_sha1(@i386_release_file)} #{@i386_release_file.size} main/binary-i386/Release
60
- SHA256:
61
- #{compute_sha2(@package_file)} #{@package_file.size} #{@component}/Packages
62
- #{compute_sha2(@gzipped_package_file)} #{@gzipped_package_file.size} #{@component}/Packages.gz
63
- #{compute_sha2(@short_release_file)} #{@short_release_file.size} #{@component}/Release
64
- #{compute_sha2(@i386_packages_file)} #{@i386_packages_file.size} main/binary-i386/Packages
65
- #{compute_sha2(@gzipped_i386_packages_file)} #{@gzipped_i386_packages_file.size} main/binary-i386/Packages.gz
66
- #{compute_sha2(@i386_release_file)} #{@i386_release_file.size} main/binary-i386/Release
67
- "
68
- return partial_release_file
69
- end
70
-
71
-
72
- def generate_short_release_file
73
- "Component: main
74
- Origin: apt.cloudscaling.com
75
- Label: apt repository #{@release}
76
- Architecture: amd64
77
- Description: Cloudscaling APT repository
78
- "
79
- end
80
-
81
-
82
- def filename(package)
83
- filename = File.basename package.path
84
- shortname = run("dpkg --field #{package.path} Package")
85
- first_letter = shortname[0]
86
- package.repo_filename = "pool/main/#{first_letter}/#{shortname}/#{filename}"
87
- end
88
-
89
- def generate_package_file()
90
- package_file = ''
91
- @packages.each { |package| package_file << generate_package_stub(package) }
92
- return package_file
93
- end
94
-
95
- def generate_package_stub(package)
96
- package_stub = ''
97
- package_stub << run("dpkg --field #{package.path}") + "\n"
98
- package_stub << "Filename: #{filename(package)}\n"
99
- package_stub << "Size: #{package.size}\n"
100
- package_stub << "MD5sum: #{package.md5}\n"
101
- package_stub << "SHA1: #{package.sha1}\n"
102
- package_stub << "SHA256: #{package.sha2}\n"
103
- package_stub << "\n"
104
- return package_stub
105
- end
106
- end
107
- end