aptly 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|