repo-mgr 0.1.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be8fb6163c79ae7f9956b0237f390c4d7764c23ef6f4ffa24c1531a3726187e0
4
- data.tar.gz: 60346b9d260718f3a03745b6c90b1fdb80c34ac62458cd999d67a2cadc7b840d
3
+ metadata.gz: a977e25ed86d9763543cd445087e3d4e94e6ecf830e707ffb8c9dfb9c5d19541
4
+ data.tar.gz: a7d2ea6036a1d3a4fc3fc2c6f575e7dda9eaa7bc2db1d7bc0b2fe7dcdb459891
5
5
  SHA512:
6
- metadata.gz: 8af9d6f12205d03e88d28ed848331708a9c6313ea377fd9c9144a2a2f88070de904784cc63659a42b9f3d110775caeeea636d90953927ed645039f135873eaf9
7
- data.tar.gz: 4688c21a24223d81a0b7fb1c9694a59e26cc7b125c4466aecf9a3bbc470c74619d6a2aa61f7469c9acc993c9f48ac4267fece461bc9738018bea819f5019214a
6
+ metadata.gz: 8044c481083ac23582a3d3133dbd225ab874188217766e71d0e82e3a76bf325776ef6241064347ee5fb39df5e63d89acfe7c086f592314062fe1c3a391645ae8
7
+ data.tar.gz: d58d378485e4a41741360d308652e56b4dc9a3abd4be396840d1131c6daf187fdce3bde7de00eb2242ac0207dfacd912c591783e6fe38eaf91e266f4e77892d0
data/README.md CHANGED
@@ -9,8 +9,14 @@ Features:
9
9
  * Create/update deb/rpm repositories.
10
10
  * Add/remove packages to these repositories and automatically sign packages using GPG.
11
11
  * Repository metadata/manifest signing using GPG.
12
+ * Publish to remote via git.
12
13
 
13
- To simplify things, aptly (which, kind of obviously, manages deb repositories) uses "stable" as distribution and "main" as component.
14
+ To simplify things:
15
+
16
+ * aptly (which, kind of obviously, manages deb repositories) uses "stable" as distribution and "main" as component.
17
+ * The git publisher uses the `main` branch for `sync` only.
18
+
19
+ The requred aptly version is 1.4.0+ which may be installed from [aptly's deb repository](https://www.aptly.info/download/). The version available in Ubuntu 20.04 (1.3.0) doesn't support gpg2.
14
20
 
15
21
  ## Install
16
22
 
@@ -22,20 +28,21 @@ gem install repo-mgr
22
28
  rake install
23
29
  ```
24
30
 
25
- As repo-mgr is a frontend for other tools, there's dependencies which must be installed separately.
31
+ As repo-mgr is a frontend for other tools, there are dependencies which must be installed separately. It is not compulsory to install all dependencies, only those needed for a particular use case. The purpose for each tool is explained by `check-depends`.
26
32
 
27
- To check which dependencies are required and their status:
33
+ To check which dependencies are required based on use case and their status:
28
34
 
29
35
  ```bash
30
36
  repo-mgr check-depends
31
- +------------+--------+
32
- | Binary | Status |
33
- +------------+--------+
34
- | aptly | ✔ |
35
- | dpkg-sig | ✔ |
36
- | createrepo | ✔ |
37
- | rpm | ✔ |
38
- +------------+--------+
37
+ +------------+--------+-----------------------+
38
+ | Binary | Status | Purpose |
39
+ +------------+--------+-----------------------+
40
+ | aptly | ✔ | Manage apt repository |
41
+ | dpkg-sig | ✔ | Sign deb packages |
42
+ | createrepo | ✔ | Manage rpm repository |
43
+ | rpm | ✔ | Sign rpm packages |
44
+ | git | ✔ | Use git publisher |
45
+ +------------+--------+-----------------------+
39
46
  ```
40
47
 
41
48
  For managing deb repositories:
@@ -48,6 +55,12 @@ For managing rpm repositories:
48
55
 
49
56
  ```bash
50
57
  sudo apt install createrepo rpm
58
+ ```
59
+
60
+ For using the git publisher:
61
+
62
+ ```bash
63
+ sudo apt install git
51
64
  ```
52
65
 
53
66
  n.b `createrepo` is not normally available for Debian and derrivates (including Ubuntu). This tool
@@ -56,6 +69,12 @@ sudo apt install createrepo rpm
56
69
 
57
70
  You can get our build of createrepo from our [deb repository](https://deb.staker.ltd/).
58
71
 
72
+ Pro tip: if the pkg signing fails because gpg can't open the output device, add this to your shell config:
73
+
74
+ ```bash
75
+ export GPG_TTY=$(tty)
76
+ ```
77
+
59
78
  ## How to use
60
79
 
61
80
  ```bash
@@ -65,11 +84,41 @@ repo-mgr help
65
84
  # create repo
66
85
  ## --path => a local directory where the repository is published - no remote support at the moment
67
86
  ## GPGKEYID is expected as log keyid i.e 16 hex chars
68
- repo-mgr upsert-repo --name foo --type deb --path path/to/foo --keyid GPGKEYID
87
+ ## --publisher - is optional i.e you can still manually publish a local repository
88
+ repo-mgr upsert-repo --name foo --type deb --path path/to/foo --keyid GPGKEYID --publisher git
69
89
 
70
90
  # sign package, add to repository, and update local repo (includes sign repo release manifest)
71
91
  # the local repo is exported to the path indicated in upsert-repo
92
+ # the git publisher also commits the changes as the path for upsert-repo is expected to be
93
+ # a git repository
72
94
  repo-mgr add-pkg --repo foo --path path/to/bar_0.0.1_amd64.deb
95
+
96
+ # publish the repository to a remote - for git publisher this means doing git push
97
+ repo-mgr sync --repo foo
98
+
99
+ # download repo from remote e.g when changing development machines
100
+ # repo-mgr upsert-repo (...) # needed only once if the machine is brand new
101
+ ## --url must point to the root of the remote repository
102
+ ## i.e where the pool and dists directories can be found for deb repos
103
+ ## --keyring is required for deb repositories; it is a file from
104
+ ## /usr/share/keyrings - the base path is automatically prepended
105
+ ## WARNING: the packages are not signed when imported via this method!
106
+ repo-mgr dl-repo --repo foo --type deb --url https://apt.example.com --keyring foo-keyring.gpg
107
+
108
+ ## --arch must be specified as each rpm repo arch is self-contained
109
+ ## the remote repository gpg key must be imported into the user's keyring and trusted
110
+ ## prior to starting the import process from remote rpm repo
111
+ repo-mgr dl-repo --repo foo --type rpm --url https://rpm.example.com --arch x86_64
112
+ ```
113
+
114
+ ## Migrating from v0.1
115
+
116
+ The package list is stored into a structure that's prone to lose the list upon re-running `upsert-repo` for `v0.1.x` of this gem. For this reason, the package list data structure has been redesigned within repo-mgr's config file.
117
+
118
+ So, to migrate from this earlier version, you must run, for every repo:
119
+
120
+ ```bash
121
+ repo-mgr rebuild-pkg-list --repo foo
73
122
  ```
74
123
 
75
- You then need to sync the repo to whatever desired target. For the time being, this isn't implemented as the main use case for this tool is publishing into a git repository which is served as GitHub page.
124
+ This rebuilds the data structure in the new config location.
@@ -14,14 +14,7 @@ module RepoMgr
14
14
  end
15
15
 
16
16
  def add_repo(name)
17
- return if aptly_repos.include? name
18
-
19
- cmd = "aptly -config=#{@aptly_config_file} repo create #{name}"
20
- out, status = Open3.capture2e cmd
21
-
22
- unless status.exitstatus.zero?
23
- Tools.error "aptly repo create failed with:\n#{out}"
24
- end
17
+ aptly "repo create #{name}" unless aptly_repos.include? name
25
18
 
26
19
  repo_config = @config.cfg[:repos][name]
27
20
 
@@ -34,6 +27,24 @@ module RepoMgr
34
27
  save_aptly_config
35
28
  end
36
29
 
30
+ def dl_repo(options)
31
+ name = options[:repo]
32
+ url = options[:url]
33
+ keyring = options[:keyring]
34
+
35
+ if keyring.nil?
36
+ Tools.error 'you must specify a keyring file for deb repo'
37
+ end
38
+
39
+ keyring = "/usr/share/keyrings/#{keyring}"
40
+
41
+ aptly "-keyring=#{keyring} mirror create #{name} #{url} stable main"
42
+ aptly "-keyring=#{keyring} mirror update #{name}", :output
43
+ aptly "repo import #{name} #{name} Name"
44
+ aptly "mirror drop #{name}"
45
+ aptly("repo search #{name}", :return).split.map { |e| "#{e}.deb" }
46
+ end
47
+
37
48
  def add_pkg(repo, pkg)
38
49
  sign_pkg repo, pkg
39
50
  repo_add repo, pkg
@@ -45,20 +56,20 @@ module RepoMgr
45
56
  repo_publish repo
46
57
  end
47
58
 
48
- def check_sig(pkg, allow_fail = false)
59
+ def check_sig(pkg, allow_fail: false)
49
60
  out, status = Open3.capture2e "dpkg-sig --verify #{pkg}"
50
61
 
51
62
  return out if status.exitstatus.zero? || allow_fail
52
63
 
53
64
  Tools.error "unable to check package signature for #{pkg} - "\
54
- "dpkg-sig returned:\n#{out}"
65
+ "dpkg-sig returned:\n#{out}"
55
66
  end
56
67
 
57
68
  def sign_pkg(repo, pkg)
58
- signature = check_sig pkg, true
69
+ signature = check_sig pkg, allow_fail: true
59
70
 
60
71
  unless signature[-6, 5] == 'NOSIG'
61
- return puts "-- dpkg-sig returned:\n#{signature.first}"
72
+ return puts "-- dpkg-sig returned:\n#{signature.split.first}"
62
73
  end
63
74
 
64
75
  if @config.cfg[:repos][repo].nil?
@@ -73,6 +84,16 @@ module RepoMgr
73
84
  Tools.error "unable to sign #{pkg} - dpkg-sig returned:\n#{out}"
74
85
  end
75
86
 
87
+ def rebuild_pkg_list(repo)
88
+ aptly("-with-packages repo search #{repo}", :return).split.map do |e|
89
+ "#{e}.deb"
90
+ end
91
+ end
92
+
93
+ def export(repo)
94
+ repo_publish repo
95
+ end
96
+
76
97
  private
77
98
 
78
99
  def init_aptly_config
@@ -102,8 +123,6 @@ module RepoMgr
102
123
  dependencyVerboseResolve: false,
103
124
  gpgDisableSign: false,
104
125
  gpgDisableVerify: false,
105
- # despite the binary being gpg, this must spell gpg2, otherwise aptly
106
- # defaults to gpg1 with less than impressive results
107
126
  gpgProvider: 'gpg2',
108
127
  downloadSourcePackages: false,
109
128
  skipLegacyPool: true,
@@ -117,36 +136,30 @@ module RepoMgr
117
136
  end
118
137
  # rubocop:enable Metrics/MethodLength
119
138
 
120
- def aptly_repos
121
- cmd = "aptly -raw -config=#{@aptly_config_file} repo list"
139
+ def aptly(cmd, output = nil)
140
+ cmd = "aptly -config=#{@aptly_config_file} #{cmd}"
122
141
  out, status = Open3.capture2e cmd
123
142
 
124
- unless status.exitstatus.zero?
125
- Tools.error "aptly repo list failed with:\n#{out}"
143
+ Tools.error "#{cmd} failed with:\n#{out}" unless status.exitstatus.zero?
144
+
145
+ case output
146
+ when :output
147
+ puts out
148
+ when :return
149
+ out
126
150
  end
151
+ end
127
152
 
128
- out.split("\n")
153
+ def aptly_repos
154
+ aptly('-raw repo list', :return).split
129
155
  end
130
156
 
131
157
  def aptly_published_repos
132
- cmd = "aptly -raw -config=#{@aptly_config_file} publish list"
133
- out, status = Open3.capture2e cmd
134
-
135
- unless status.exitstatus.zero?
136
- Tools.error "aptly publish list failed with:\n#{out}"
137
- end
138
-
139
- out.split("\n")
158
+ aptly('-raw publish list', :return).split("\n")
140
159
  end
141
160
 
142
161
  def aptly_publish_drop(repo)
143
- cmd = "aptly -config=#{@aptly_config_file} publish drop stable "\
144
- "filesystem:#{repo}:"
145
- out, status = Open3.capture2e cmd
146
-
147
- return if status.exitstatus.zero?
148
-
149
- Tools.error "aptly publish drop failed with:\n#{out}"
162
+ aptly "publish drop stable filesystem:#{repo}:"
150
163
  end
151
164
 
152
165
  def save_aptly_config
@@ -154,23 +167,12 @@ module RepoMgr
154
167
  end
155
168
 
156
169
  def repo_add(repo, pkg)
157
- cmd = "aptly -config=#{@aptly_config_file} repo add #{repo} #{pkg}"
158
- out, status = Open3.capture2e cmd
159
-
160
- return if status.exitstatus.zero?
161
-
162
- Tools.error "aptly repo add failed with:\n#{out}"
170
+ aptly "repo add #{repo} #{pkg}"
163
171
  end
164
172
 
165
173
  def repo_rm(repo, pkg)
166
174
  package = File.basename pkg, File.extname(pkg)
167
- cmd = "aptly -config=#{@aptly_config_file} repo remove "\
168
- "#{repo} #{package}"
169
- out, status = Open3.capture2e cmd
170
-
171
- return if status.exitstatus.zero?
172
-
173
- Tools.error "aptly repo remove failed with:\n#{out}"
175
+ aptly "repo remove #{repo} #{package}"
174
176
  end
175
177
 
176
178
  def repo_publish(repo)
@@ -179,14 +181,8 @@ module RepoMgr
179
181
  end
180
182
 
181
183
  keyid = @config.cfg[:repos][repo][:keyid]
182
- cmd = "aptly -config=#{@aptly_config_file} -distribution=stable "\
183
- "-gpg-key=#{keyid} publish repo #{repo} filesystem:#{repo}:"
184
-
185
- out, status = Open3.capture2e cmd
186
-
187
- return if status.exitstatus.zero?
188
-
189
- Tools.error "aptly publish repo failed with:\n#{out}"
184
+ aptly "-distribution=stable -gpg-key=#{keyid} publish repo #{repo} "\
185
+ "filesystem:#{repo}:"
190
186
  end
191
187
  end
192
188
  end
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: false
2
2
 
3
+ require 'zlib'
3
4
  require 'open3'
4
5
  require 'gpgme'
6
+ require 'digest'
7
+ require 'faraday'
8
+ require 'nokogiri'
9
+ require 'stringio'
5
10
  require 'fileutils'
6
11
 
7
12
  module RepoMgr
@@ -27,6 +32,30 @@ module RepoMgr
27
32
  sync_repo repo
28
33
  end
29
34
 
35
+ def dl_repo(options)
36
+ name = options[:repo]
37
+ url = options[:url]
38
+ arch = options[:arch]
39
+
40
+ Tools.error 'you must specify an arch name for rpm repo' if arch.nil?
41
+
42
+ tmpdir = "/tmp/#{name}/#{arch}"
43
+ dest_dir = "#{@config.cfg_dir}/rpms/#{name}/#{arch}"
44
+ make_dirs tmpdir, dest_dir
45
+
46
+ repomd = dl_repomd url, arch, tmpdir
47
+ pkgs = dl_primary url, arch, tmpdir, repomd
48
+
49
+ pkgs.each do |hash, file|
50
+ dl_pkg hash, url, arch, file, tmpdir
51
+ copy_pkg "#{tmpdir}/#{file}", dest_dir
52
+ end
53
+
54
+ sync_repo name
55
+
56
+ pkgs.values
57
+ end
58
+
30
59
  def remove_pkg(repo, pkg)
31
60
  name = File.basename pkg
32
61
  arch = extract_arch pkg
@@ -43,7 +72,7 @@ module RepoMgr
43
72
  return out if status.exitstatus.zero?
44
73
 
45
74
  Tools.error "unable to check package signature for #{pkg} - "\
46
- "rpm -K returned:\n#{out}"
75
+ "rpm -K returned:\n#{out}"
47
76
  end
48
77
 
49
78
  def sign_pkg(repo, pkg)
@@ -69,6 +98,16 @@ module RepoMgr
69
98
  Tools.error "unable to sign #{pkg} - rpm --addsign returned:\n#{out}"
70
99
  end
71
100
 
101
+ def rebuild_pkg_list(repo)
102
+ Dir["#{@config.cfg_dir}/rpms/#{repo}/**/*.rpm"].map do |e|
103
+ File.basename e
104
+ end
105
+ end
106
+
107
+ def export(repo)
108
+ sync_repo repo
109
+ end
110
+
72
111
  private
73
112
 
74
113
  def extract_arch(pkg)
@@ -104,7 +143,7 @@ module RepoMgr
104
143
  return if status.exitstatus.zero?
105
144
 
106
145
  Tools.error "unable to create repo for #{arch} - createrepo "\
107
- "returned:\n#{out}"
146
+ "returned:\n#{out}"
108
147
  end
109
148
 
110
149
  def sign_repo(repo)
@@ -121,6 +160,83 @@ module RepoMgr
121
160
 
122
161
  File.write 'repomd.xml.asc', signature
123
162
  end
163
+
164
+ def faraday_dl(url, tmpdir, file = File.basename(url))
165
+ puts "-- Download #{file}"
166
+ File.write "#{tmpdir}/#{file}", Faraday.get(url).body
167
+ end
168
+
169
+ def dl_repomd(url, arch, tmpdir)
170
+ faraday_dl "#{url}/#{arch}/repodata/repomd.xml", tmpdir
171
+ faraday_dl "#{url}/#{arch}/repodata/repomd.xml.asc", tmpdir
172
+
173
+ valid = false
174
+ crypto = GPGME::Crypto.new armor: true
175
+ sig = GPGME::Data.new File.read "#{tmpdir}/repomd.xml.asc"
176
+ data = File.read "#{tmpdir}/repomd.xml"
177
+ crypto.verify(sig, signed_text: data) do |sign|
178
+ valid = sign.valid?
179
+ end
180
+
181
+ unless valid == true
182
+ Tools.error "unable to check signature for #{tmpdir}/repomd.xml"
183
+ end
184
+
185
+ Nokogiri::XML data
186
+ end
187
+
188
+ def extract_pkgs(primary)
189
+ pkgs = {}
190
+
191
+ primary.css('package').each do |pkg|
192
+ pkg_hash = pkg.at('checksum[type=sha256]').text
193
+ pkgs[pkg_hash] = pkg.at('location')['href']
194
+ end
195
+
196
+ pkgs
197
+ end
198
+
199
+ def dl_primary(url, arch, tmpdir, repomd)
200
+ hash = repomd.at('data[type=primary]').at('checksum').text
201
+ primary = "#{url}/#{arch}/repodata/#{hash}-primary.xml.gz"
202
+
203
+ faraday_dl primary, tmpdir, 'primary.xml.gz'
204
+
205
+ primary_xml_gz = "#{tmpdir}/primary.xml.gz"
206
+ fl_hash = Digest::SHA256.hexdigest(File.read(primary_xml_gz))
207
+
208
+ unless fl_hash == hash
209
+ Tools.error "failed hash check for #{tmpdir}/primary.xml.gz"
210
+ end
211
+
212
+ primary_xml_gz = File.read("#{tmpdir}/primary.xml.gz")
213
+ primary = Zlib::GzipReader.new(StringIO.new(primary_xml_gz)).read
214
+ File.write("#{tmpdir}/primary.xml", primary)
215
+
216
+ extract_pkgs Nokogiri::XML(primary)
217
+ end
218
+
219
+ def dl_pkg(hash, url, arch, file, tmpdir)
220
+ unless File.exist? "#{tmpdir}/#{file}"
221
+ faraday_dl "#{url}/#{arch}/#{file}", tmpdir
222
+ end
223
+
224
+ fl_hash = Digest::SHA256.hexdigest(File.read("#{tmpdir}/#{file}"))
225
+
226
+ return if fl_hash == hash
227
+
228
+ Tools.error "failed hash check for #{tmpdir}/#{file}"
229
+ end
230
+
231
+ def copy_pkg(file, destdir)
232
+ puts "-- Copy to repo-mgr #{File.basename(file)}"
233
+ FileUtils.cp file, destdir
234
+ end
235
+
236
+ def make_dirs(tmpdir, dest_dir)
237
+ FileUtils.mkdir_p tmpdir
238
+ FileUtils.mkdir_p dest_dir
239
+ end
124
240
  end
125
241
  end
126
242
  end
data/lib/repo_mgr/cli.rb CHANGED
@@ -8,6 +8,7 @@ require 'terminal-table'
8
8
  require_relative 'tools'
9
9
  require_relative 'config'
10
10
  require_relative 'backends'
11
+ require_relative 'publishers'
11
12
 
12
13
  module RepoMgr
13
14
  # implements CLI interface
@@ -20,20 +21,27 @@ module RepoMgr
20
21
  %w[deb rpm]
21
22
  end
22
23
 
24
+ def self.publishers
25
+ %w[git]
26
+ end
27
+
23
28
  desc 'check-depends', 'Check dependencies'
24
29
 
25
30
  def check_depends
26
31
  rows = []
27
-
28
- %w[aptly dpkg-sig createrepo rpm].each do |bin_dep|
29
- rows << if Tools.which bin_dep
30
- [bin_dep, ''.green]
31
- else
32
- [bin_dep, ''.red]
33
- end
32
+ deps = {
33
+ 'aptly' => 'Manage apt repository',
34
+ 'dpkg-sig' => 'Sign deb packages',
35
+ 'createrepo' => 'Manage rpm repository',
36
+ 'rpm' => 'Sign rpm packages',
37
+ 'git' => 'Use git publisher'
38
+ }
39
+
40
+ deps.each do |bin_dep, purpose|
41
+ rows << which_depends(bin_dep, purpose)
34
42
  end
35
43
 
36
- puts Terminal::Table.new headings: %w[Binary Status], rows: rows
44
+ puts Terminal::Table.new headings: %w[Binary Status Purpose], rows: rows
37
45
  end
38
46
 
39
47
  desc 'upsert-repo', 'Create/Update new repository'
@@ -45,13 +53,14 @@ module RepoMgr
45
53
  desc: 'Directory path where to build the repository'
46
54
  option :keyid, type: :string, required: true, aliases: %w[-k],
47
55
  desc: 'GPG key id used to sign the repository metadata'
56
+ option :publisher, type: :string, aliases: %w[-r], enum: publishers,
57
+ desc: 'Publisher used to sync repo data to remote target'
48
58
 
49
59
  def upsert_repo
50
60
  FileUtils.mkdir_p options[:path]
51
61
 
52
62
  config = Config.new
53
- config.upsert_repo options[:name], options[:type], options[:path],
54
- options[:keyid]
63
+ config.upsert_repo options
55
64
 
56
65
  backend = Backends.load options[:type], config
57
66
  backend.add_repo options[:name]
@@ -66,12 +75,36 @@ module RepoMgr
66
75
  config = Config.new
67
76
 
68
77
  config.cfg[:repos].each do |name, repo|
69
- rows << [name, repo[:type], repo[:path], repo[:keyid]]
78
+ rows << [name, repo[:type], repo[:path], repo[:keyid], repo[:publisher]]
70
79
  end
71
80
 
72
81
  return puts '-- No repos have been created' if rows.count.zero?
73
82
 
74
- puts Terminal::Table.new headings: %w[Name Type Path KeyID], rows: rows
83
+ puts Terminal::Table.new(
84
+ headings: %w[Name Type Path KeyID Publisher], rows: rows
85
+ )
86
+ end
87
+
88
+ desc 'dl-repo', 'Download repository from remote endpoint'
89
+ option :repo, type: :string, required: true, aliases: %w[-r],
90
+ desc: 'The repository to download packages into'
91
+ option :type, type: :string, required: true, aliases: %w[-t],
92
+ enum: types, desc: 'Repository type'
93
+ option :url, type: :string, required: true, aliases: %w[-u],
94
+ desc: 'The URL for the remote repository'
95
+ option :keyring, type: :string, aliases: %w[-k],
96
+ desc: 'Name of keyring file if required to auth repository'
97
+ option :arch, type: :string, aliases: %w[-a],
98
+ desc: 'Pkg arch if multiple arch are supported by repo'
99
+
100
+ def dl_repo
101
+ backend, config = load_backend options[:type]
102
+
103
+ pkgs = backend.dl_repo options
104
+
105
+ pkgs.each do |pkg|
106
+ config.add_pkg options[:repo], pkg
107
+ end
75
108
  end
76
109
 
77
110
  desc 'add-pkg', 'Add package to repository'
@@ -85,8 +118,14 @@ module RepoMgr
85
118
  backend.add_pkg options[:repo], options[:path]
86
119
  config.add_pkg options[:repo], options[:path]
87
120
 
121
+ pub_type = config.cfg[:repos][options[:repo]][:publisher]
122
+ if pub_type
123
+ publisher = Publishers.load pub_type, config
124
+ publisher.save options[:repo], options[:path]
125
+ end
126
+
88
127
  puts "-- Added #{File.basename(options[:path])} to "\
89
- "#{options[:repo]} repository"
128
+ "#{options[:repo]} repository"
90
129
  end
91
130
 
92
131
  desc 'list-pkgs', 'List repository packages'
@@ -94,7 +133,7 @@ module RepoMgr
94
133
  desc: 'The repository to list the packages from'
95
134
 
96
135
  def list_pkgs
97
- packages = Config.new.cfg[:repos][options[:repo]][:packages]
136
+ packages = Config.new.cfg[:packages][options[:repo]]
98
137
 
99
138
  if packages.nil?
100
139
  Tools.error "#{options[:repo]} repo does not have any packages"
@@ -118,7 +157,7 @@ module RepoMgr
118
157
  config.remove_pkg options[:repo], options[:path]
119
158
 
120
159
  puts "-- Removed #{File.basename(options[:path])} from "\
121
- "#{options[:repo]} repository"
160
+ "#{options[:repo]} repository"
122
161
  end
123
162
 
124
163
  desc 'check-sig', 'Check package signature'
@@ -130,10 +169,61 @@ module RepoMgr
130
169
  puts backend.check_sig options[:path]
131
170
  end
132
171
 
172
+ desc 'rebuild-pkg-list', 'Rebuild package list from local pkg cache'
173
+ option :repo, type: :string, required: true, aliases: %w[-r],
174
+ desc: 'The repository to rebuild pkg list for'
175
+ def rebuild_pkg_list
176
+ config = Config.new
177
+ backend = Backends.load config.cfg[:repos][options[:repo]][:type], config
178
+ pkgs = backend.rebuild_pkg_list options[:repo]
179
+
180
+ pkgs.each do |pkg|
181
+ config.add_pkg options[:repo], pkg
182
+ end
183
+
184
+ puts "-- Rebuilt #{options[:repo]} repo pkg list"
185
+ end
186
+
187
+ desc 'sync', 'Sync local repo to remote target using repo publisher'
188
+ option :repo, type: :string, required: true, aliases: %w[-r],
189
+ desc: 'The repository to sync to remote target via publisher'
190
+ def sync
191
+ config = Config.new
192
+ pub_type = config.cfg[:repos][options[:repo]][:publisher]
193
+
194
+ unless pub_type
195
+ Tools.error "#{options[:repo]} repo does not have a publisher"
196
+ end
197
+
198
+ publisher = Publishers.load pub_type, config
199
+ publisher.sync options[:repo]
200
+
201
+ puts "-- Synchronised #{options[:repo]} using #{pub_type} publisher"
202
+ end
203
+
204
+ desc 'export', 'Export packages and metadata as local repository'
205
+ option :repo, type: :string, required: true, aliases: %w[-r],
206
+ desc: 'The repository to export'
207
+ def export
208
+ config = Config.new
209
+ backend = Backends.load config.cfg[:repos][options[:repo]][:type], config
210
+
211
+ backend.export options[:repo]
212
+
213
+ puts "-- Exported #{options[:repo]} repo"
214
+ end
215
+
133
216
  private
134
217
 
218
+ def which_depends(bin_dep, purpose)
219
+ return [bin_dep, '✔'.green, purpose] if Tools.which bin_dep
220
+
221
+ [bin_dep, '✘'.red, purpose]
222
+ end
223
+
135
224
  def load_backend(path)
136
225
  type = File.extname(path).strip.downcase[1..-1]
226
+ type = path if type.nil?
137
227
 
138
228
  unless CLI.types.include? type
139
229
  Tools.error "unsupported package type #{type}"
@@ -15,7 +15,9 @@ module RepoMgr
15
15
  FileUtils.mkdir_p @cfg_dir
16
16
 
17
17
  @cfg_file = "#{@cfg_dir}/repo-mgr.yml"
18
- File.write @cfg_file, { repos: {} }.to_yaml unless File.exist? @cfg_file
18
+ unless File.exist? @cfg_file
19
+ File.write @cfg_file, { repos: {}, packages: {} }.to_yaml
20
+ end
19
21
 
20
22
  @cfg = YAML.load_file @cfg_file
21
23
  end
@@ -24,17 +26,24 @@ module RepoMgr
24
26
  File.write @cfg_file, @cfg.to_yaml
25
27
  end
26
28
 
27
- def upsert_repo(name, type, path, keyid)
29
+ def upsert_repo(options)
30
+ name = options[:name]
31
+ type = options[:type]
32
+
28
33
  if @cfg[:repos][name] && @cfg[:repos][name][:type] != type
29
34
  Tools.error "unable to change type for #{name} repository"
30
35
  end
31
36
 
32
37
  @cfg[:repos][name] = {
33
38
  type: type,
34
- path: path,
35
- keyid: keyid
39
+ path: options[:path],
40
+ keyid: options[:keyid]
36
41
  }
37
42
 
43
+ if options[:publisher]
44
+ @cfg[:repos][name][:publisher] = options[:publisher]
45
+ end
46
+
38
47
  save
39
48
  end
40
49
 
@@ -43,14 +52,15 @@ module RepoMgr
43
52
  Tools.error "unable to add packages to #{repo} - repo does not exist"
44
53
  end
45
54
 
46
- @cfg[:repos][repo][:packages] ||= []
55
+ @cfg[:packages] ||= {}
56
+ @cfg[:packages][repo] ||= []
47
57
  pkg = File.basename path
48
58
 
49
- if @cfg[:repos][repo][:packages].include?(pkg)
59
+ if @cfg[:packages][repo].include?(pkg)
50
60
  Tools.error "you already have #{pkg} in your #{repo} repo"
51
61
  end
52
62
 
53
- @cfg[:repos][repo][:packages] << pkg
63
+ @cfg[:packages][repo] << pkg
54
64
 
55
65
  save
56
66
  end
@@ -58,12 +68,13 @@ module RepoMgr
58
68
  def remove_pkg(repo, path)
59
69
  if @cfg[:repos][repo].nil?
60
70
  Tools.error "unable to remove packages from #{repo} "\
61
- '- repo does not exist'
71
+ '- repo does not exist'
62
72
  end
63
73
 
64
- @cfg[:repos][repo][:packages] ||= []
74
+ @cfg[:packages] ||= {}
75
+ @cfg[:packages][repo] ||= []
65
76
  pkg = File.basename path
66
- @cfg[:repos][repo][:packages].delete pkg
77
+ @cfg[:packages][repo].delete pkg
67
78
 
68
79
  save
69
80
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'git'
4
+
5
+ module RepoMgr
6
+ module Publisher
7
+ # git publisher
8
+ class Git
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ # method invoked when the local deb/rpm repository is built
14
+ # for git, this requires a commit into the target git repository
15
+ # which is the target for deb/rpm repository export
16
+ def save(repo, pkg)
17
+ git = ::Git.open @config.cfg[:repos][repo][:path]
18
+ git.add(all: true)
19
+ git.commit "Add #{File.basename(pkg)}."
20
+ end
21
+
22
+ # method invoked when the local deb/rpm repository is published
23
+ # for git, this is pushing to a remote
24
+ def sync(repo)
25
+ git = ::Git.open @config.cfg[:repos][repo][:path]
26
+ git.push(git.remote('origin'), 'main')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative 'publishers/git'
4
+
5
+ module RepoMgr
6
+ # factory loader for RepoMgr::Publisher::Foo objects
7
+ class Publishers
8
+ def self.load(publisher, config)
9
+ @obj ||= {}
10
+
11
+ @obj[publisher] ||= Object.const_get(
12
+ "RepoMgr::Publisher::#{publisher.capitalize}"
13
+ ).new(config)
14
+
15
+ @obj[publisher]
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repo-mgr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ștefan Rusu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-17 00:00:00.000000000 Z
11
+ date: 2022-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colored
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.9.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.9.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: git
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.1
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: gpgme
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +66,20 @@ dependencies:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
68
  version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.13.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.13.3
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: terminal-table
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +109,7 @@ dependencies:
67
109
  - !ruby/object:Gem::Version
68
110
  version: '1.1'
69
111
  - !ruby/object:Gem::Dependency
70
- name: jeweler
112
+ name: juwelier
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
115
  - - ">="
@@ -140,6 +182,8 @@ files:
140
182
  - lib/repo_mgr/backends/rpm.rb
141
183
  - lib/repo_mgr/cli.rb
142
184
  - lib/repo_mgr/config.rb
185
+ - lib/repo_mgr/publishers.rb
186
+ - lib/repo_mgr/publishers/git.rb
143
187
  - lib/repo_mgr/tools.rb
144
188
  homepage: https://github.com/mr-staker/repo-mgr
145
189
  licenses:
@@ -160,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
204
  - !ruby/object:Gem::Version
161
205
  version: '0'
162
206
  requirements: []
163
- rubygems_version: 3.1.4
207
+ rubygems_version: 3.2.32
164
208
  signing_key:
165
209
  specification_version: 4
166
210
  summary: deb and rpm repository manager