aptly 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/aptly.rb +102 -0
- data/lib/aptly/error.rb +9 -0
- data/lib/aptly/hash.rb +9 -0
- data/lib/aptly/mirror.rb +150 -0
- data/lib/aptly/mutex.rb +78 -0
- data/lib/aptly/publish.rb +147 -0
- data/lib/aptly/repo.rb +255 -0
- data/lib/aptly/snapshot.rb +270 -0
- data/lib/aptly/string.rb +6 -0
- data/lib/aptly/version.rb +3 -0
- data/spec/aptly.conf +3 -0
- data/spec/aptly/aptly_spec.rb +9 -0
- data/spec/aptly/error_spec.rb +11 -0
- data/spec/aptly/hash_spec.rb +13 -0
- data/spec/aptly/mirror_spec.rb +56 -0
- data/spec/aptly/mutex_spec.rb +25 -0
- data/spec/aptly/repo_spec.rb +148 -0
- data/spec/aptly/snapshot_spec.rb +19 -0
- data/spec/aptly/string_spec.rb +13 -0
- data/spec/bin/aptly +3 -0
- data/spec/pkgs/pkg1_1.0.1-1_amd64.deb +0 -0
- data/spec/pkgs/pkg2_1.0.2-2_amd64.deb +0 -0
- data/spec/setup.sh +21 -0
- data/spec/spec_helper.rb +22 -0
- metadata +138 -0
data/lib/aptly/repo.rb
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
module Aptly
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# Creates a new repository in aptly
|
5
|
+
#
|
6
|
+
# == Parameters:
|
7
|
+
# name::
|
8
|
+
# The name to use for the new repository
|
9
|
+
# dist::
|
10
|
+
# The distribution used during publishing
|
11
|
+
# comment::
|
12
|
+
# A comment describing the repository
|
13
|
+
# component::
|
14
|
+
# The component used during publishing
|
15
|
+
#
|
16
|
+
# == Returns:
|
17
|
+
# An Aptly::Repo object
|
18
|
+
#
|
19
|
+
def create_repo name, kwargs={}
|
20
|
+
dist = kwargs.arg :dist, ''
|
21
|
+
comment = kwargs.arg :comment, ''
|
22
|
+
component = kwargs.arg :component, 'main'
|
23
|
+
if list_repos.include? name
|
24
|
+
raise AptlyError.new("Repo '#{name}' already exists")
|
25
|
+
end
|
26
|
+
|
27
|
+
cmd = "aptly repo create"
|
28
|
+
cmd += " -comment=#{comment.quote}" if !comment.empty?
|
29
|
+
cmd += " -distribution=#{dist.quote}" if !dist.empty?
|
30
|
+
cmd += " #{name}"
|
31
|
+
|
32
|
+
runcmd cmd
|
33
|
+
return Repo.new name
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return a list of existing repositories
|
37
|
+
#
|
38
|
+
# == Returns
|
39
|
+
# An array of strings representing repository names
|
40
|
+
#
|
41
|
+
def list_repos
|
42
|
+
out = runcmd 'aptly repo list'
|
43
|
+
parse_list out.lines
|
44
|
+
end
|
45
|
+
|
46
|
+
# Retrieve information about a repository
|
47
|
+
#
|
48
|
+
# == Parameters:
|
49
|
+
# name::
|
50
|
+
# The name of the repository to retrieve information for
|
51
|
+
#
|
52
|
+
# == Returns:
|
53
|
+
# A hash of repository information
|
54
|
+
#
|
55
|
+
def repo_info name
|
56
|
+
out = runcmd "aptly repo show #{name.quote}"
|
57
|
+
parse_info out.lines
|
58
|
+
end
|
59
|
+
|
60
|
+
class Repo
|
61
|
+
attr_accessor :name, :dist, :component
|
62
|
+
attr_accessor :comment, :num_packages, :archlist
|
63
|
+
|
64
|
+
@name = ''
|
65
|
+
@dist = ''
|
66
|
+
@component = ''
|
67
|
+
@comment = ''
|
68
|
+
@num_packages = 0
|
69
|
+
|
70
|
+
# Instantiates an Aptly::Repo object
|
71
|
+
#
|
72
|
+
# == Parameters:
|
73
|
+
# name::
|
74
|
+
# The name of the repository
|
75
|
+
#
|
76
|
+
# == Returns:
|
77
|
+
# An Aptly::Repo object
|
78
|
+
#
|
79
|
+
def initialize name
|
80
|
+
if !Aptly::list_repos.include? name
|
81
|
+
raise AptlyError.new "Repo '#{name}' does not exist"
|
82
|
+
end
|
83
|
+
|
84
|
+
info = Aptly::repo_info name
|
85
|
+
@name = info['Name']
|
86
|
+
@comment = info['Comment']
|
87
|
+
@dist = info['Default Distribution']
|
88
|
+
@component = info['Default Component']
|
89
|
+
@num_packages = info['Number of packages'].to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
# Drops an existing aptly repository
|
93
|
+
def drop
|
94
|
+
Aptly::runcmd "aptly repo drop #{@name.quote}"
|
95
|
+
end
|
96
|
+
|
97
|
+
# List all packages contained in a repository
|
98
|
+
#
|
99
|
+
# == Returns:
|
100
|
+
# An array of packages
|
101
|
+
#
|
102
|
+
def list_packages
|
103
|
+
res = []
|
104
|
+
out = Aptly::runcmd "aptly repo show -with-packages #{@name.quote}"
|
105
|
+
Aptly::parse_indented_list out.lines
|
106
|
+
end
|
107
|
+
|
108
|
+
# Add debian packages to a repo
|
109
|
+
#
|
110
|
+
# == Parameters:
|
111
|
+
# path::
|
112
|
+
# The path to the file or directory source
|
113
|
+
# remove_files::
|
114
|
+
# When true, deletes source after import
|
115
|
+
#
|
116
|
+
def add path, kwargs={}
|
117
|
+
remove_files = kwargs.arg :remove_files, false
|
118
|
+
|
119
|
+
cmd = 'aptly repo add'
|
120
|
+
cmd += ' -remove-files' if remove_files
|
121
|
+
cmd += " #{@name.quote} #{path}"
|
122
|
+
|
123
|
+
Aptly::runcmd cmd
|
124
|
+
end
|
125
|
+
|
126
|
+
# Imports package resources from existing mirrors
|
127
|
+
#
|
128
|
+
# == Parameters:
|
129
|
+
# from_mirror::
|
130
|
+
# The name of the mirror to import from
|
131
|
+
# packages::
|
132
|
+
# A list of debian pkg_spec strings (e.g. "libc6 (>= 2.7-1)")
|
133
|
+
# deps::
|
134
|
+
# When true, follows package dependencies and adds them
|
135
|
+
#
|
136
|
+
def import from_mirror, kwargs={}
|
137
|
+
deps = kwargs.arg :deps, false
|
138
|
+
packages = kwargs.arg :packages, []
|
139
|
+
|
140
|
+
if packages.length == 0
|
141
|
+
raise AptlyError.new '1 or more packages are required'
|
142
|
+
end
|
143
|
+
|
144
|
+
cmd = 'aptly repo import'
|
145
|
+
cmd += ' -with-deps' if deps
|
146
|
+
cmd += " #{from_mirror.quote} #{@name.quote}"
|
147
|
+
packages.each {|p| cmd += " #{p.quote}"}
|
148
|
+
|
149
|
+
Aptly::runcmd cmd
|
150
|
+
end
|
151
|
+
|
152
|
+
# Copy package resources from one repository to another
|
153
|
+
#
|
154
|
+
# == Parameters:
|
155
|
+
# from_repo::
|
156
|
+
# The source repository name
|
157
|
+
# to_repo::
|
158
|
+
# The destination repository name
|
159
|
+
# pkg_spec::
|
160
|
+
# A debian pkg_spec string
|
161
|
+
# deps::
|
162
|
+
# When true, follow deps and copy them
|
163
|
+
#
|
164
|
+
def copy from_repo, to_repo, pkg_spec, kwargs={}
|
165
|
+
deps = kwargs.arg :deps, false
|
166
|
+
|
167
|
+
cmd = 'aptly repo copy'
|
168
|
+
cmd += ' -with-deps' if deps
|
169
|
+
cmd += " #{from_repo.quote} #{to_repo.quote} #{pkg_spec.quote}"
|
170
|
+
|
171
|
+
Aptly::runcmd cmd
|
172
|
+
end
|
173
|
+
private :copy
|
174
|
+
|
175
|
+
# Shortcut method to copy resources in from another repository
|
176
|
+
def copy_from from_repo, pkg_spec, kwargs={}
|
177
|
+
deps = kwargs.arg :deps, false
|
178
|
+
copy from_repo, @name, pkg_spec, :deps => deps
|
179
|
+
end
|
180
|
+
|
181
|
+
# Shortcut method to copy resources out to another repository
|
182
|
+
def copy_to to_repo, pkg_spec, kwargs={}
|
183
|
+
deps = kwargs.arg :deps, false
|
184
|
+
copy @name, to_repo, pkg_spec, :deps => deps
|
185
|
+
end
|
186
|
+
|
187
|
+
# Move package resources from one repository to another
|
188
|
+
#
|
189
|
+
# == Parameters:
|
190
|
+
# from_repo::
|
191
|
+
# The source repository name
|
192
|
+
# to_repo::
|
193
|
+
# The destination repository name
|
194
|
+
# pkg_spec::
|
195
|
+
# A debian pkg_spec string
|
196
|
+
# deps::
|
197
|
+
# When true, follow deps and move them too
|
198
|
+
#
|
199
|
+
def move from_repo, to_repo, pkg_spec, kwargs={}
|
200
|
+
deps = kwargs.arg :deps, false
|
201
|
+
|
202
|
+
cmd = 'aptly repo move'
|
203
|
+
cmd += ' -with-deps' if deps
|
204
|
+
cmd += " #{from_repo.quote} #{to_repo.quote} #{pkg_spec.quote}"
|
205
|
+
|
206
|
+
Aptly::runcmd cmd
|
207
|
+
end
|
208
|
+
private :move
|
209
|
+
|
210
|
+
# Shortcut method to move packages in from another repo
|
211
|
+
def move_from from_repo, pkg_spec, kwargs={}
|
212
|
+
deps = kwargs.arg :deps, false
|
213
|
+
move from_repo, @name, pkg_spec, :deps => deps
|
214
|
+
end
|
215
|
+
|
216
|
+
# Shortcut method to move packages out to another repository
|
217
|
+
def move_to to_repo, pkg_spec, kwargs={}
|
218
|
+
deps = kwargs.arg :deps, false
|
219
|
+
move @name, to_repo, pkg_spec, :deps => deps
|
220
|
+
end
|
221
|
+
|
222
|
+
# Remove packages selectively from a repository
|
223
|
+
#
|
224
|
+
# == Parameters:
|
225
|
+
# pkg_spec::
|
226
|
+
# A debian pkg_spec string to select packages by
|
227
|
+
#
|
228
|
+
def remove pkg_spec
|
229
|
+
Aptly::runcmd "aptly repo remove #{@name.quote} #{pkg_spec.quote}"
|
230
|
+
end
|
231
|
+
|
232
|
+
# Shortcut method to snapshot an Aptly::Repo object
|
233
|
+
def snapshot name
|
234
|
+
Aptly::create_repo_snapshot name, @name
|
235
|
+
end
|
236
|
+
|
237
|
+
# Shortcut method to publish a repo from an Aptly::Repo instance.
|
238
|
+
def publish args
|
239
|
+
Aptly::publish 'repo', @name, args
|
240
|
+
end
|
241
|
+
|
242
|
+
# save allows you to modify the repository distribution, comment, or
|
243
|
+
# component string by using the attr_accessor's, and then calling this
|
244
|
+
# method to persist them to aptly.
|
245
|
+
def save
|
246
|
+
cmd = "aptly repo edit"
|
247
|
+
cmd += " -distribution=#{@dist.quote}"
|
248
|
+
cmd += " -comment=#{@comment.quote}"
|
249
|
+
cmd += " -component=#{@component.quote}"
|
250
|
+
cmd += " #{@name.quote}"
|
251
|
+
|
252
|
+
Aptly::runcmd cmd
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
module Aptly
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# Create a new snapshot of a repo or mirror
|
5
|
+
#
|
6
|
+
# == Parameters:
|
7
|
+
# name::
|
8
|
+
# The name for the new snapshot
|
9
|
+
# type::
|
10
|
+
# The type of snapshot. "mirror" and "repo" are supported.
|
11
|
+
# resource_name::
|
12
|
+
# The name of the mirror or repo
|
13
|
+
#
|
14
|
+
# == Returns:
|
15
|
+
# An Aptly::Snapshot object
|
16
|
+
#
|
17
|
+
def create_snapshot name, type, kwargs={}
|
18
|
+
resource_name = kwargs.arg :resource_name, ''
|
19
|
+
|
20
|
+
if type != 'mirror' && type != 'repo' && type != 'empty'
|
21
|
+
raise AptlyError.new "Invalid snapshot type: #{type}"
|
22
|
+
end
|
23
|
+
|
24
|
+
if list_snapshots.include? name
|
25
|
+
raise AptlyError.new "Snapshot '#{name}' exists"
|
26
|
+
end
|
27
|
+
|
28
|
+
if type == 'mirror' && !list_mirrors.include?(resource_name)
|
29
|
+
raise AptlyError.new "Mirror '#{resource_name}' does not exist"
|
30
|
+
end
|
31
|
+
|
32
|
+
if type == 'repo' && !list_repos.include?(resource_name)
|
33
|
+
raise AptlyError.new "Repo '#{resource_name}' does not exist"
|
34
|
+
end
|
35
|
+
|
36
|
+
cmd = 'aptly snapshot create'
|
37
|
+
cmd += " #{name.quote}"
|
38
|
+
if type == 'empty'
|
39
|
+
cmd += ' empty'
|
40
|
+
else
|
41
|
+
cmd += " from #{type} #{resource_name.quote}"
|
42
|
+
end
|
43
|
+
|
44
|
+
runcmd cmd
|
45
|
+
Snapshot.new name
|
46
|
+
end
|
47
|
+
private :create_snapshot
|
48
|
+
|
49
|
+
# Shortcut method to create a snapshot from a mirror
|
50
|
+
def create_mirror_snapshot name, mirror_name
|
51
|
+
create_snapshot name, 'mirror', :resource_name => mirror_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Shortcut method to create a snapshot from a repo
|
55
|
+
def create_repo_snapshot name, repo_name
|
56
|
+
create_snapshot name, 'repo', :resource_name => repo_name
|
57
|
+
end
|
58
|
+
|
59
|
+
# Shortcut method to create an empty snapshot
|
60
|
+
def create_empty_snapshot name
|
61
|
+
create_snapshot name, 'empty'
|
62
|
+
end
|
63
|
+
|
64
|
+
# List existing snapshots
|
65
|
+
#
|
66
|
+
# == Returns:
|
67
|
+
# A list of snapshot names
|
68
|
+
#
|
69
|
+
def list_snapshots
|
70
|
+
out = runcmd 'aptly snapshot list'
|
71
|
+
parse_list out.lines
|
72
|
+
end
|
73
|
+
|
74
|
+
# Retrieves information about a snapshot
|
75
|
+
#
|
76
|
+
# == Parameters:
|
77
|
+
# name::
|
78
|
+
# The name of the snapshot to gather information about
|
79
|
+
#
|
80
|
+
# == Returns:
|
81
|
+
# A hash of snapshot information
|
82
|
+
#
|
83
|
+
def snapshot_info name
|
84
|
+
out = runcmd "aptly snapshot show #{name.quote}"
|
85
|
+
parse_info out.lines
|
86
|
+
end
|
87
|
+
|
88
|
+
# Merge snapshots into a single snapshot. This will create a new snapshot
|
89
|
+
# containing packages from all source snapshots. By default, packages with
|
90
|
+
# the same name-architecture pair are merged from right-over-left, meaning
|
91
|
+
# packages in the last source snapshot may overwrite the same packages in
|
92
|
+
# the first source snapshot if their name-architecture pairs match.
|
93
|
+
#
|
94
|
+
# == Parameters:
|
95
|
+
# dest::
|
96
|
+
# The destination snapshot name (will be created)
|
97
|
+
# sources::
|
98
|
+
# The names of source repositories. The order in which these are passed
|
99
|
+
# matters unless `-latest` is not passed.
|
100
|
+
# latest::
|
101
|
+
# When true, only the latest of each package will be copied into the
|
102
|
+
# new snapshot, following a "latest wins" approach.
|
103
|
+
#
|
104
|
+
# == Returns:
|
105
|
+
# An Aptly::Snapshot object for the new snapshot
|
106
|
+
#
|
107
|
+
def merge_snapshots dest, kwargs={}
|
108
|
+
sources = kwargs.arg :sources, []
|
109
|
+
latest = kwargs.arg :latest, false
|
110
|
+
|
111
|
+
if sources.length == 0
|
112
|
+
raise AptlyError.new '1 or more sources are required'
|
113
|
+
end
|
114
|
+
|
115
|
+
if list_snapshots.include? dest
|
116
|
+
raise AptlyError.new "Snapshot '#{dest}' exists"
|
117
|
+
end
|
118
|
+
|
119
|
+
cmd = 'aptly snapshot merge'
|
120
|
+
cmd += ' -latest' if latest
|
121
|
+
cmd += " #{dest.quote}"
|
122
|
+
cmd += " #{sources.join(' ')}"
|
123
|
+
|
124
|
+
runcmd cmd
|
125
|
+
Aptly::Snapshot.new dest
|
126
|
+
end
|
127
|
+
|
128
|
+
class Snapshot
|
129
|
+
attr_accessor :name, :created_at, :description, :num_packages
|
130
|
+
|
131
|
+
@name = ''
|
132
|
+
@created_at = ''
|
133
|
+
@description = ''
|
134
|
+
@num_packages = 0
|
135
|
+
|
136
|
+
# Instantiates a new Aptly::Snapshot instance
|
137
|
+
#
|
138
|
+
# == Parameters:
|
139
|
+
# name::
|
140
|
+
# The name of the snapshot
|
141
|
+
#
|
142
|
+
# == Returns:
|
143
|
+
# An Aptly::Snapshot instance
|
144
|
+
#
|
145
|
+
def initialize name
|
146
|
+
if !Aptly::list_snapshots.include? name
|
147
|
+
raise AptlyError.new("Snapshot '#{name}' does not exist")
|
148
|
+
end
|
149
|
+
|
150
|
+
info = Aptly::snapshot_info name
|
151
|
+
@name = info['Name']
|
152
|
+
@created_at = info['Created At']
|
153
|
+
@description = info['Description']
|
154
|
+
@num_packages = info['Number of packages'].to_i
|
155
|
+
end
|
156
|
+
|
157
|
+
# Drops an existing snapshot
|
158
|
+
#
|
159
|
+
# == Parameters:
|
160
|
+
# force::
|
161
|
+
# When true, drops a snapshot regardless of relationships
|
162
|
+
#
|
163
|
+
def drop kwargs={}
|
164
|
+
force = kwargs.arg :force, false
|
165
|
+
|
166
|
+
cmd = 'aptly snapshot drop'
|
167
|
+
cmd += ' -force' if force
|
168
|
+
cmd += " #{@name.quote}"
|
169
|
+
|
170
|
+
Aptly::runcmd cmd
|
171
|
+
end
|
172
|
+
|
173
|
+
# List all packages contained in a snapshot
|
174
|
+
#
|
175
|
+
# == Returns:
|
176
|
+
# An array of packages
|
177
|
+
#
|
178
|
+
def list_packages
|
179
|
+
res = []
|
180
|
+
out = Aptly::runcmd "aptly snapshot show -with-packages #{@name.quote}"
|
181
|
+
Aptly::parse_indented_list out.lines
|
182
|
+
end
|
183
|
+
|
184
|
+
# Pull packages from a snapshot into another, creating a new snapshot.
|
185
|
+
#
|
186
|
+
# == Parameters:
|
187
|
+
# name::
|
188
|
+
# The name of the snapshot to pull to
|
189
|
+
# source::
|
190
|
+
# The repository containing the packages to pull in
|
191
|
+
# dest::
|
192
|
+
# The name for the new snapshot which will be created
|
193
|
+
# packages::
|
194
|
+
# An array of package names to search
|
195
|
+
# deps::
|
196
|
+
# When true, process dependencies
|
197
|
+
# remove::
|
198
|
+
# When true, removes package versions not found in source
|
199
|
+
#
|
200
|
+
def pull name, source, dest, kwargs={}
|
201
|
+
packages = kwargs.arg :packages, []
|
202
|
+
deps = kwargs.arg :deps, true
|
203
|
+
remove = kwargs.arg :remove, true
|
204
|
+
|
205
|
+
if packages.length == 0
|
206
|
+
raise AptlyError.new "1 or more package names are required"
|
207
|
+
end
|
208
|
+
|
209
|
+
cmd = 'aptly snapshot pull'
|
210
|
+
cmd += ' -no-deps' if !deps
|
211
|
+
cmd += ' -no-remove' if !remove
|
212
|
+
cmd += " #{name.quote} #{source.quote} #{dest.quote}"
|
213
|
+
if !packages.empty?
|
214
|
+
packages.each {|p| cmd += " #{p.quote}"}
|
215
|
+
end
|
216
|
+
|
217
|
+
Aptly::runcmd cmd
|
218
|
+
Aptly::Snapshot.new dest
|
219
|
+
end
|
220
|
+
private :pull
|
221
|
+
|
222
|
+
# Shortcut method to pull packages to the current snapshot
|
223
|
+
def pull_from source, dest, kwargs={}
|
224
|
+
packages = kwargs.arg :packages, []
|
225
|
+
deps = kwargs.arg :deps, true
|
226
|
+
remove = kwargs.arg :remove, true
|
227
|
+
|
228
|
+
pull @name, source, dest, :packages => packages, :deps => deps, :remove => remove
|
229
|
+
end
|
230
|
+
|
231
|
+
# Shortcut method to push packages from the current snapshot
|
232
|
+
def push_to dest, source, kwargs={}
|
233
|
+
packages = kwargs.arg :packages, []
|
234
|
+
deps = kwargs.arg :deps, true
|
235
|
+
remove = kwargs.arg :remove, true
|
236
|
+
|
237
|
+
pull source, @name, dest, :packages => packages, :deps => deps, :remove => remove
|
238
|
+
end
|
239
|
+
|
240
|
+
# Verifies an existing snapshot is able to resolve dependencies. This method
|
241
|
+
# currently only returns true/false status.
|
242
|
+
#
|
243
|
+
# == Parameters:
|
244
|
+
# sources::
|
245
|
+
# Additional snapshot sources to be considered during verification
|
246
|
+
# follow_source::
|
247
|
+
# When true, verify all source packages as well
|
248
|
+
#
|
249
|
+
# == Returns:
|
250
|
+
# An array containing any missing deps. Empty list means all verified.
|
251
|
+
#
|
252
|
+
def verify kwargs={}
|
253
|
+
sources = kwargs.arg :sources, []
|
254
|
+
follow_source = kwargs.arg :follow_source, false
|
255
|
+
|
256
|
+
cmd = 'aptly snapshot verify'
|
257
|
+
cmd += ' -dep-follow-source' if follow_source
|
258
|
+
cmd += " #{@name.quote}"
|
259
|
+
cmd += " #{sources.join(' ')}" if !sources.empty?
|
260
|
+
|
261
|
+
out = Aptly::runcmd cmd
|
262
|
+
Aptly::parse_indented_list out
|
263
|
+
end
|
264
|
+
|
265
|
+
# Shortcut method to publish a snapshot from an Aptly::Snapshot instance.
|
266
|
+
def publish args
|
267
|
+
Aptly::publish 'snapshot', @name, args
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|