open-build-service-api 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c5e147324f417fe00b366de7fbf147ba8226703afa23629694849d3432d1aea
4
- data.tar.gz: 96295d4f01c7f1988e1c491977d96f45a749abab67cb8559238cb3ef63afae1b
3
+ metadata.gz: 1f1e3cde838735b8f74df547f7409ad60ded20f5cdd25f6acd8e1328abcc14ad
4
+ data.tar.gz: 12e82fd311a6ffb074722f12d2edc25bbda0a0a6f89c8147155ed0856bfaa6bc
5
5
  SHA512:
6
- metadata.gz: ef3d73d904f39553846c60741c94aa52e3910140ba17ed352d686482e843aee016229aa20817bcb5f8155a889378e3783cd1e06787946c9d67a9e0339a288090
7
- data.tar.gz: 57d6e4b7f432b578a8deb7988ddacddd3a718da08390074fa773e6264cacb071adc1163f8481236c460212116c5605fd98c2ae9b4bd813d05994b071cd2d0ae8
6
+ metadata.gz: 1ff27e3f77889b45615a9042a1a8e1f340fe048fc8d64bcef26e1477d1f518089fdeaeb9dc34b18e5187c8d167bdd491c3ad20ee5a6916e2616b8dc16e1708f4
7
+ data.tar.gz: '019080bdede8c641971efc2f56ed07b4147f0e4eb546fdc8db09eba905688131f66e40c6a89d2a577383a4d87cdf5ab762ad293c24a57e3cc8fdd27ddebd3759'
@@ -1,3 +1,13 @@
1
+ ## Version: 0.3.0 (alpha)
2
+
3
+ - Added `#rebuild_failed!` method to `Package` model to rebuild only failed builds
4
+ - Added setter methods for package title and description
5
+ - Added `#dirty?` method to `Package` model to identify if a package needs to be saved
6
+ - Added `#repositories` method to `Project` model to list all build repositories
7
+ - Added `#binaries` method to `Package` model to list all built binaries
8
+ - Added `#run_service!` method to `Package` model to trigger remote services
9
+ - Added `#sources` method to `Package` model to retrieve sources of a package container
10
+
1
11
  ## Version: 0.2.0 (alpha)
2
12
 
3
13
  - Added `#public_key` method to `Project` model to retrieve a project's public key
@@ -3,7 +3,7 @@ module OpenBuildServiceAPI
3
3
  include Enumerable
4
4
  extend Forwardable
5
5
 
6
- def_delegators :@data, :each, :count
6
+ def_delegators :@data, :each, :count, :empty?
7
7
 
8
8
  def inspect
9
9
  count = 1
@@ -0,0 +1,18 @@
1
+ module OpenBuildServiceAPI
2
+ class BinariesCollection < AbstractCollection
3
+ attr_accessor :package
4
+
5
+ def initialize(params = {})
6
+ raise ArgumentError.new('The dataset needs to be provided as an Array.') if params[:data] && !params[:data].is_a?(Array)
7
+
8
+ @connection = params[:connection]
9
+ @package = params[:package]
10
+ @data = params[:data] ? params[:data] : []
11
+ end
12
+
13
+ def delete_all!
14
+ @connection.send_request(:post, "/build/#{CGI.escape(@package.project.name)}", cmd: :wipe, package: @package.name)
15
+ true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module OpenBuildServiceAPI
2
+ class SourcesCollection < AbstractCollection
3
+ attr_accessor :package
4
+
5
+ def initialize(params = {})
6
+ raise ArgumentError.new('The dataset needs to be provided as an Array.') if params[:data] && !params[:data].is_a?(Array)
7
+
8
+ @connection = params[:connection]
9
+ @package = params[:package]
10
+ @data = params[:data] ? params[:data] : []
11
+ end
12
+ end
13
+ end
@@ -40,9 +40,11 @@ module OpenBuildServiceAPI
40
40
  class PackageDeletionPermissionError < APIError; end
41
41
  class PackageAlreadyExistsError < APIError; end
42
42
  class PackageNotFoundError < APIError; end
43
+ class NoSourceServiceDefinedError < APIError; end
43
44
 
44
45
  # library specific exceptions
45
46
  class GeneralError < Exception; end
46
47
  class ConnectionError < GeneralError; end
47
48
  class InvalidHTTPMethodWithBody < GeneralError; end
49
+ class InvalidDownloadDirectoryPath < GeneralError; end
48
50
  end
@@ -0,0 +1,7 @@
1
+ module OpenBuildServiceAPI
2
+ module BinaryHelper
3
+ def self.binary_file?(file_name)
4
+ file_name.end_with?('.rpm') || file_name.end_with?('.deb') || file_name.end_with?('.qcow2')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,54 @@
1
+ module OpenBuildServiceAPI
2
+ class Binary
3
+ attr_reader :name, :size, :created_at, :repository, :architecture, :local_file_path, :local_path
4
+ attr_accessor :package
5
+
6
+ def initialize(params = {})
7
+ raise ArgumentError.new('repository needs to be an instance of OpenBuildServiceAPI::Repository') unless params[:repository].is_a?(Repository)
8
+
9
+ @name = params[:name]
10
+ @size = Filesize.from(params[:size])
11
+ @created_at = Time.at(params[:created_at])
12
+ @repository = params[:repository]
13
+ @architecture = params[:architecture]
14
+ @package = params[:package]
15
+ @connection = params[:connection]
16
+ end
17
+
18
+ def to_s
19
+ @name
20
+ end
21
+
22
+ def download(destination=nil)
23
+ delete_local_copy!
24
+
25
+ if destination
26
+ raise InvalidDownloadDirectoryPath.new("Path '#{destination}' is not a directory.") unless File.directory?(destination)
27
+
28
+ destination = destination[0..-2] if destination.end_with?('/')
29
+ @tmp_dir = false
30
+ else
31
+ destination = Dir.mktmpdir('obs-binary-download')
32
+ @tmp_dir = true
33
+ end
34
+
35
+ @local_file_path = "#{destination}/#{@name}"
36
+ @local_path = destination
37
+
38
+ response = @connection.send_request(:get, "/build/#{CGI.escape(@package.project.name)}/#{CGI.escape(@repository.to_s)}/" \
39
+ "#{CGI.escape(@architecture)}/#{CGI.escape(@package.name)}/#{CGI.escape(@name)}")
40
+ File.write(@local_file_path, response.body)
41
+ @local_file_path
42
+ end
43
+
44
+ def delete_local_copy!
45
+ return false unless @local_file_path
46
+ return false unless File.exists?(@local_file_path)
47
+
48
+ File.delete(@local_file_path)
49
+ FileUtils.rm_r(@local_path) if Dir.empty?(@local_path) && @tmp_dir
50
+
51
+ true
52
+ end
53
+ end
54
+ end
@@ -14,15 +14,43 @@ module OpenBuildServiceAPI
14
14
  end
15
15
 
16
16
  def title
17
+ return @title_updated if @title_updated
18
+
17
19
  title = meta.xpath('//package/title')
18
20
  title.empty? ? nil : title[0].text
19
21
  end
20
22
 
23
+ def title=(value)
24
+ @dirty = true
25
+ @title_updated = value
26
+ end
27
+
21
28
  def description
29
+ return @description_updated if @description_updated
30
+
22
31
  description = meta.xpath('//package/description')
23
32
  description.empty? ? nil : description[0].text
24
33
  end
25
34
 
35
+ def description=(value)
36
+ @dirty = true
37
+ @description_updated = value
38
+ end
39
+
40
+ def run_service!
41
+ begin
42
+ @connection.send_request(:post, "/source/#{CGI.escape(@project.name)}/#{CGI.escape(@name)}", cmd: :runservice)
43
+ rescue RequestError => err
44
+ if err.error_code == 'not_found' && err.error_summary =~ /no source service defined/
45
+ raise NoSourceServiceDefinedError.new("Package '#{@name}' in project '#{@project.name}' has no source service defined.")
46
+ end
47
+
48
+ raise
49
+ end
50
+
51
+ true
52
+ end
53
+
26
54
  def meta(opts = {})
27
55
  if !@cached_meta || @meta_reload
28
56
  @cached_meta = @connection.send_request(:get, "/source/#{CGI.escape(@project.name)}/#{CGI.escape(@name)}/_meta").body
@@ -36,6 +64,69 @@ module OpenBuildServiceAPI
36
64
  end
37
65
  end
38
66
 
67
+ def sources
68
+ return @cached_sources if @cached_sources && !@sources_reload
69
+ collection_data = []
70
+
71
+ response_xml = Nokogiri::XML(@connection.send_request(:get, "/source/#{CGI.escape(@project.name)}/#{CGI.escape(@name)}").body)
72
+ response_xml.xpath('//entry').each do |file|
73
+ collection_data << Source.new(
74
+ name: file.attr('name'),
75
+ md5_hash: file.attr('md5'),
76
+ size: file.attr('size'),
77
+ updated_at: file.attr('mtime').to_i,
78
+ package: self,
79
+ connection: @connection
80
+ )
81
+ end
82
+
83
+ @sources_reload = false
84
+ @cached_sources = SourcesCollection.new(data: collection_data, package: self, connection: @connection)
85
+ end
86
+
87
+ def binaries
88
+ collection_data = []
89
+
90
+ @project.repositories.each do |repository|
91
+ binary_data = { repository: repository, binaries: [] }
92
+
93
+ repository.architectures.each do |arch|
94
+ begin
95
+ response = @connection.send_request(:get, "/build/#{CGI.escape(@project.name)}/#{CGI.escape(repository.to_s)}/#{CGI.escape(arch)}/#{CGI.escape(@name)}")
96
+ response_xml = Nokogiri::XML(response.body)
97
+
98
+ response_xml.xpath('//binarylist/binary').each do |binary|
99
+ file_name = binary.attr('filename')
100
+ file_size = binary.attr('size')
101
+ created_at = binary.attr('mtime').to_i
102
+
103
+ if BinaryHelper.binary_file?(file_name)
104
+ binary_data[:binaries] << Binary.new(
105
+ name: file_name,
106
+ size: file_size,
107
+ architecture: arch,
108
+ created_at: created_at,
109
+ repository: repository,
110
+ package: self,
111
+ connection: @connection
112
+ )
113
+ end
114
+ end
115
+ rescue RequestError => err
116
+ # in case the repository does no longer exist, we can ignore it. there are no binaries anyways
117
+ next if err.error_code == '404' && err.error_summary =~ /has no repository/
118
+
119
+ # raise any other error
120
+ raise
121
+ end
122
+ end
123
+
124
+ collection_data << binary_data unless binary_data[:binaries].empty?
125
+ end
126
+
127
+ BinariesCollection.new(data: collection_data, connection: @connection, package: self)
128
+ end
129
+
39
130
  def inspect
40
131
  "#<#{self.class.name}:#{"0x00%x" % (object_id << 1)} @name=\"#{@name}\", @project=\"#{@project.name}\">"
41
132
  end
@@ -45,6 +136,39 @@ module OpenBuildServiceAPI
45
136
  true
46
137
  end
47
138
 
139
+ def rebuild_failed!
140
+ @connection.send_request(:post, "/build/#{CGI.escape(@project.name)}", cmd: :rebuild, package: @name, code: :failed)
141
+ true
142
+ end
143
+
144
+ def dirty?
145
+ !!@dirty
146
+ end
147
+
148
+ def save!
149
+ return false unless @dirty
150
+
151
+ @meta_reload = true
152
+ meta_xml = meta
153
+
154
+ meta_xml.xpath('//package/title')[0].content = @title_updated if @title_updated
155
+ meta_xml.xpath('//package/description')[0].content = @description_updated if @description_updated
156
+
157
+ # update meta definition
158
+ @connection.send_request(:put, "/source/#{CGI.escape(@project.name)}/#{CGI.escape(@name)}/_meta", request_body: meta_xml.to_xml)
159
+
160
+ # reset updated values
161
+ @dirty = false
162
+
163
+ @title_updated = nil
164
+ @description_updated = nil
165
+
166
+ # updated cached meta
167
+ @cached_meta = meta_xml.to_xml
168
+
169
+ true
170
+ end
171
+
48
172
  def delete!(message=nil)
49
173
  begin
50
174
  @connection.send_request(:delete, "/source/#{CGI.escape(@project.name)}/#{CGI.escape(@name)}", comment: message)
@@ -59,6 +183,7 @@ module OpenBuildServiceAPI
59
183
 
60
184
  def reload!
61
185
  @meta_reload = true
186
+ @sources_reload = true
62
187
  end
63
188
  end
64
189
  end
@@ -25,6 +25,29 @@ module OpenBuildServiceAPI
25
25
  end
26
26
  end
27
27
 
28
+ def repositories
29
+ collection_data = []
30
+
31
+ meta.xpath('//repository').each do |repository|
32
+ paths = []
33
+ archs = []
34
+
35
+ repository.children.each do |child|
36
+ if child.is_a?(Nokogiri::XML::Element)
37
+ if child.name == 'path'
38
+ paths << { project: child.attr('project'), repository: child.attr('repository') }
39
+ elsif child.name == 'arch'
40
+ archs << child.text
41
+ end
42
+ end
43
+ end
44
+
45
+ collection_data << Repository.new(name: repository.attr('name'), paths: paths, architectures: archs, connection: @connection, project: self)
46
+ end
47
+
48
+ collection_data
49
+ end
50
+
28
51
  def delete!(message=nil)
29
52
  begin
30
53
  @connection.send_request(:delete, "/source/#{CGI.escape(@name)}", comment: message)
@@ -0,0 +1,25 @@
1
+ module OpenBuildServiceAPI
2
+ class Repository
3
+ attr_accessor :name, :project
4
+
5
+ def initialize(params = {})
6
+ @name = params[:name]
7
+ @paths = params[:paths]
8
+ @architectures = params[:architectures]
9
+ @project = params[:project]
10
+ @connection = params[:connection]
11
+ end
12
+
13
+ def to_s
14
+ @name
15
+ end
16
+
17
+ def paths
18
+ @paths
19
+ end
20
+
21
+ def architectures
22
+ @architectures
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module OpenBuildServiceAPI
2
+ class Source
3
+ attr_reader :name, :md5_hash, :size, :updated_at
4
+ attr_accessor :package
5
+
6
+ def initialize(params = {})
7
+ @name = params[:name]
8
+ @md5_hash = params[:md5_hash]
9
+ @size = Filesize.from(params[:size])
10
+ @updated_at = Time.at(params[:updated_at])
11
+ @package = params[:package]
12
+ @connection = params[:connection]
13
+ end
14
+
15
+ def to_s
16
+ @name
17
+ end
18
+ end
19
+ end
@@ -1,8 +1,11 @@
1
1
  require 'cgi'
2
2
  require 'date'
3
+ require 'filesize'
4
+ require 'fileutils'
3
5
  require 'forwardable'
4
6
  require 'net/http'
5
7
  require 'nokogiri'
8
+ require 'tmpdir'
6
9
  require_relative 'version'
7
10
  require_relative 'exception'
8
11
  require_relative 'connection'
@@ -11,10 +14,18 @@ require_relative 'connection'
11
14
  require_relative 'api/about'
12
15
  require_relative 'api/projects'
13
16
 
17
+ # Helpers
18
+ require_relative 'helpers/binary_helper'
19
+
14
20
  # Collections
15
21
  require_relative 'collections/abstract_collection'
22
+ require_relative 'collections/binaries_collection'
16
23
  require_relative 'collections/packages_collection'
24
+ require_relative 'collections/sources_collection'
17
25
 
18
26
  # Models
27
+ require_relative 'models/binary'
19
28
  require_relative 'models/package'
20
29
  require_relative 'models/project'
30
+ require_relative 'models/repository'
31
+ require_relative 'models/source'
@@ -1,4 +1,4 @@
1
1
  module OpenBuildServiceAPI
2
- VERSION = '0.2.0'
3
- RELEASE_DATE = '2019-06-24'
2
+ VERSION = '0.3.0'
3
+ RELEASE_DATE = '2019-06-25'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: open-build-service-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Schnitzer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-24 00:00:00.000000000 Z
11
+ date: 2019-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: date
@@ -70,6 +70,46 @@ dependencies:
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: 1.10.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: filesize
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.2'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.2.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: fileutils
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.2'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.2.0
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.2'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.2.0
73
113
  - !ruby/object:Gem::Dependency
74
114
  name: byebug
75
115
  requirement: !ruby/object:Gem::Requirement
@@ -121,11 +161,17 @@ files:
121
161
  - lib/api/about.rb
122
162
  - lib/api/projects.rb
123
163
  - lib/collections/abstract_collection.rb
164
+ - lib/collections/binaries_collection.rb
124
165
  - lib/collections/packages_collection.rb
166
+ - lib/collections/sources_collection.rb
125
167
  - lib/connection.rb
126
168
  - lib/exception.rb
169
+ - lib/helpers/binary_helper.rb
170
+ - lib/models/binary.rb
127
171
  - lib/models/package.rb
128
172
  - lib/models/project.rb
173
+ - lib/models/repository.rb
174
+ - lib/models/source.rb
129
175
  - lib/open-build-service-api.rb
130
176
  - lib/version.rb
131
177
  homepage: https://github.com/mschnitzer/open-build-service-api