repo-mgr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1e6af886ad6d5adea7626716153f8da4e15edce28bf5300ba671e708f57e0b5a
4
+ data.tar.gz: 71f3930291d0c7f84bd1bd80507560ec0f0886bbd0f6c8972c7e1628886d7f61
5
+ SHA512:
6
+ metadata.gz: f1f43472590d082815bd16d718bac5d2dca91502789a957c7b21ee5ba87a2dbec59e80455c986bb9a208802e8adabf85e8b73d0cc7641cf8ef1dd44317a95f77
7
+ data.tar.gz: a65d9039b45515777e19a0b5c722b5b184781eafaae21d20e11f0e849c0a542d74988a9cd2fdb1f89584ee3ec15957a5180ba7da483bef6977d6a9080a2d742b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Ștefan Rusu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ ## About
2
+
3
+ deb and rpm repository management tool. Essentially, this is a frontend for a suite of tools provided by various distribution maintainers.
4
+
5
+ repo-mgr provides a unified and consistent way for managing various repositories (deb, rpm).
6
+
7
+ Features:
8
+
9
+ * Create/update deb/rpm repositories.
10
+ * Add/remove packages to these repositories and automatically sign packages using GPG.
11
+ * Repository metadata/manifest signing using GPG.
12
+
13
+ To simplify things, aptly (which, kind of obviously, manages deb repositories) uses "stable" as distribution and "main" as component.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ # RubyGems
19
+ gem install repo-mgr
20
+
21
+ # from source
22
+ rake install
23
+ ```
24
+
25
+ As repo-mgr is a frontend for other tools, there's dependencies which must be installed separately.
26
+
27
+ To check which dependencies are required and their status:
28
+
29
+ ```bash
30
+ repo-mgr check-depends
31
+ +------------+--------+
32
+ | Binary | Status |
33
+ +------------+--------+
34
+ | aptly | ✔ |
35
+ | dpkg-sig | ✔ |
36
+ | createrepo | ✔ |
37
+ | rpm | ✔ |
38
+ +------------+--------+
39
+ ```
40
+
41
+ For managing deb repositories:
42
+
43
+ ```bash
44
+ sudo apt install aptly dpkg-sig
45
+ ```
46
+
47
+ For managing rpm repositories:
48
+
49
+ ```bash
50
+ sudo apt install createrepo rpm
51
+ ```
52
+
53
+ n.b `createrepo` is not normally available for Debian and derrivates (including Ubuntu). This tool
54
+ has been used to bootstrap a deb repository which includes a `createrepo` build for Ubuntu 20.04,
55
+ therefore creating a dependency upon itself for setting up rpm repositories.
56
+
57
+ You can get our build of createrepo from our [deb repository](https://deb.staker.ltd/).
58
+
59
+ ## How to use
60
+
61
+ ```bash
62
+ # to get you started
63
+ repo-mgr help
64
+
65
+ # create repo
66
+ ## --path => a local directory where the repository is published - no remote support at the moment
67
+ ## 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
69
+
70
+ # sign package, add to repository, and update local repo (includes sign repo release manifest)
71
+ # the local repo is exported to the path indicated in upsert-repo
72
+ repo-mgr add-pkg --repo foo --path path/to/bar_0.0.1_amd64.deb
73
+ ```
74
+
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.
data/bin/repo-mgr ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ require_relative '../lib/repo_mgr'
5
+
6
+ RepoMgr::CLI.start(ARGV)
data/lib/repo_mgr.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative 'repo_mgr/cli'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative 'backends/deb'
4
+ require_relative 'backends/rpm'
5
+
6
+ module RepoMgr
7
+ # factory loader for RepoMgr::Backend::Foo objects
8
+ class Backends
9
+ def self.load(backend, config)
10
+ @obj ||= {}
11
+
12
+ @obj[backend] ||= Object.const_get(
13
+ "RepoMgr::Backend::#{backend.capitalize}"
14
+ ).new(config)
15
+
16
+ @obj[backend]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'json'
4
+ require 'open3'
5
+ require 'fileutils'
6
+
7
+ module RepoMgr
8
+ module Backend
9
+ # deb backend handler implemented on top of aptly
10
+ class Deb
11
+ def initialize(config)
12
+ @config = config
13
+ init_aptly_config
14
+ end
15
+
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
25
+
26
+ repo_config = @config.cfg[:repos][name]
27
+
28
+ @aptly_config['FileSystemPublishEndpoints'][name] = {
29
+ rootDir: repo_config[:path],
30
+ linkMethod: 'copy',
31
+ verifyMethod: 'md5'
32
+ }
33
+
34
+ save_aptly_config
35
+ end
36
+
37
+ def add_pkg(repo, pkg)
38
+ sign_pkg repo, pkg
39
+ repo_add repo, pkg
40
+ repo_publish repo
41
+ end
42
+
43
+ def remove_pkg(repo, pkg)
44
+ repo_rm repo, pkg
45
+ repo_publish repo
46
+ end
47
+
48
+ def check_sig(pkg)
49
+ out, status = Open3.capture2e "dpkg-sig --verify #{pkg}"
50
+
51
+ return out if status.exitstatus.zero?
52
+
53
+ Tools.error "unable to check package signature for #{pkg} - "\
54
+ "dpkg-sig returned:\n#{out}"
55
+ end
56
+
57
+ def sign_pkg(repo, pkg)
58
+ signature = check_sig(pkg)
59
+ unless signature.first[-6, 5] == 'NOSIG'
60
+ return puts "-- dpkg-sig returned:\n#{signature.first}"
61
+ end
62
+
63
+ if @config.cfg[:repos][repo].nil?
64
+ Tools.error "unable to find #{repo} repository"
65
+ end
66
+
67
+ keyid = @config.cfg[:repos][repo][:keyid]
68
+ out, status = Open3.capture2e "dpkg-sig -k #{keyid} -s builder #{pkg}"
69
+
70
+ return if status.exitstatus.zero?
71
+
72
+ Tools.error "unable to sign #{pkg} - dpkg-sig returned:\n#{out}"
73
+ end
74
+
75
+ private
76
+
77
+ def init_aptly_config
78
+ @aptly_root = "#{@config.cfg_dir}/aptly"
79
+ @aptly_config_file = "#{@config.cfg_dir}/aptly.json"
80
+
81
+ unless File.exist? @aptly_config_file
82
+ File.write @aptly_config_file, aptly_base_config.to_json
83
+ end
84
+
85
+ @aptly_config = JSON.parse File.read(@aptly_config_file)
86
+ end
87
+
88
+ # rubocop:disable Metrics/MethodLength
89
+ def aptly_base_config
90
+ FileUtils.mkdir_p @aptly_root
91
+
92
+ {
93
+ rootDir: @aptly_root,
94
+ downloadConcurrency: 4,
95
+ downloadSpeedLimit: 0,
96
+ architectures: [],
97
+ dependencyFollowSuggests: false,
98
+ dependencyFollowRecommends: false,
99
+ dependencyFollowAllVariants: false,
100
+ dependencyFollowSource: false,
101
+ dependencyVerboseResolve: false,
102
+ gpgDisableSign: false,
103
+ gpgDisableVerify: false,
104
+ # despite the binary being gpg, this must spell gpg2, otherwise aptly
105
+ # defaults to gpg1 with less than impressive results
106
+ gpgProvider: 'gpg2',
107
+ downloadSourcePackages: false,
108
+ skipLegacyPool: true,
109
+ ppaDistributorID: '',
110
+ ppaCodename: '',
111
+ skipContentsPublishing: false,
112
+ FileSystemPublishEndpoints: {},
113
+ S3PublishEndpoints: {},
114
+ SwiftPublishEndpoints: {}
115
+ }
116
+ end
117
+ # rubocop:enable Metrics/MethodLength
118
+
119
+ def aptly_repos
120
+ cmd = "aptly -raw -config=#{@aptly_config_file} repo list"
121
+ out, status = Open3.capture2e cmd
122
+
123
+ unless status.exitstatus.zero?
124
+ Tools.error "aptly repo list failed with:\n#{out}"
125
+ end
126
+
127
+ out.split("\n")
128
+ end
129
+
130
+ def aptly_published_repos
131
+ cmd = "aptly -raw -config=#{@aptly_config_file} publish list"
132
+ out, status = Open3.capture2e cmd
133
+
134
+ unless status.exitstatus.zero?
135
+ Tools.error "aptly publish list failed with:\n#{out}"
136
+ end
137
+
138
+ out.split("\n")
139
+ end
140
+
141
+ def aptly_publish_drop(repo)
142
+ cmd = "aptly -config=#{@aptly_config_file} publish drop stable "\
143
+ "filesystem:#{repo}:"
144
+ out, status = Open3.capture2e cmd
145
+
146
+ return if status.exitstatus.zero?
147
+
148
+ Tools.error "aptly publish drop failed with:\n#{out}"
149
+ end
150
+
151
+ def save_aptly_config
152
+ File.write @aptly_config_file, @aptly_config.to_json
153
+ end
154
+
155
+ def repo_add(repo, pkg)
156
+ cmd = "aptly -config=#{@aptly_config_file} repo add #{repo} #{pkg}"
157
+ out, status = Open3.capture2e cmd
158
+
159
+ return if status.exitstatus.zero?
160
+
161
+ Tools.error "aptly repo add failed with:\n#{out}"
162
+ end
163
+
164
+ def repo_rm(repo, pkg)
165
+ package = File.basename pkg, File.extname(pkg)
166
+ cmd = "aptly -config=#{@aptly_config_file} repo remove "\
167
+ "#{repo} #{package}"
168
+ out, status = Open3.capture2e cmd
169
+
170
+ return if status.exitstatus.zero?
171
+
172
+ Tools.error "aptly repo remove failed with:\n#{out}"
173
+ end
174
+
175
+ def repo_publish(repo)
176
+ if aptly_published_repos.include? "filesystem:#{repo}:. stable"
177
+ aptly_publish_drop(repo)
178
+ end
179
+
180
+ keyid = @config.cfg[:repos][repo][:keyid]
181
+ cmd = "aptly -config=#{@aptly_config_file} -distribution=stable "\
182
+ "-gpg-key=#{keyid} publish repo #{repo} filesystem:#{repo}:"
183
+
184
+ out, status = Open3.capture2e cmd
185
+
186
+ return if status.exitstatus.zero?
187
+
188
+ Tools.error "aptly publish repo failed with:\n#{out}"
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'open3'
4
+ require 'gpgme'
5
+ require 'fileutils'
6
+
7
+ module RepoMgr
8
+ module Backend
9
+ # rpm backend handler
10
+ class Rpm
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ # this is managed from RepoMgr::Config
16
+ def add_repo(_name); end
17
+
18
+ def add_pkg(repo, pkg)
19
+ sign_pkg repo, pkg
20
+
21
+ arch = extract_arch pkg
22
+ dest_dir = "#{@config.cfg_dir}/rpms/#{repo}/#{arch}"
23
+
24
+ FileUtils.mkdir_p dest_dir
25
+ FileUtils.cp pkg, dest_dir
26
+
27
+ sync_repo repo
28
+ end
29
+
30
+ def remove_pkg(repo, pkg)
31
+ name = File.basename pkg
32
+ arch = extract_arch pkg
33
+ dest_dir = "#{@config.cfg_dir}/rpms/#{repo}/#{arch}"
34
+
35
+ FileUtils.rm_f "#{dest_dir}/#{name}"
36
+
37
+ sync_repo repo
38
+ end
39
+
40
+ def check_sig(pkg)
41
+ out, status = Open3.capture2e "rpm -K #{pkg}"
42
+
43
+ return out if status.exitstatus.zero?
44
+
45
+ Tools.error "unable to check package signature for #{pkg} - "\
46
+ "rpm -K returned:\n#{out}"
47
+ end
48
+
49
+ def sign_pkg(repo, pkg)
50
+ keyid = @config.cfg[:repos][repo][:keyid]
51
+ gpg_name = GPGME::Key.find(keyid).first.uids.first.uid
52
+
53
+ # need to deal with the %_gpg_name nonsense as adding that via CLI is
54
+ # too bloody much for ARRRRRRRRRR PM - also who in their right mind
55
+ # would target a key by name / email rather than, you know, key ID
56
+
57
+ rpm_macros = "#{ENV['HOME']}/.rpmmacros"
58
+ File.write rpm_macros, "%_gpg_name #{gpg_name}"
59
+
60
+ # gpg-agent? nah - rpm is special
61
+ cmd = "rpm --addsign #{pkg}"
62
+
63
+ out, status = Open3.capture2e cmd
64
+
65
+ FileUtils.rm_f rpm_macros
66
+
67
+ return if status.exitstatus.zero?
68
+
69
+ Tools.error "unable to sign #{pkg} - rpm --addsign returned:\n#{out}"
70
+ end
71
+
72
+ private
73
+
74
+ def extract_arch(pkg)
75
+ pkg.split('.')[-2]
76
+ end
77
+
78
+ def sync_repo(repo)
79
+ repo_dir = @config.cfg[:repos][repo][:path]
80
+
81
+ Dir["#{@config.cfg_dir}/rpms/#{repo}/*"].each do |arch_dir|
82
+ arch = File.basename arch_dir
83
+ arch_dest = "#{repo_dir}/#{arch}"
84
+
85
+ FileUtils.rm_rf arch_dest
86
+ FileUtils.mkdir_p arch_dest
87
+
88
+ Dir["#{@config.cfg_dir}/rpms/#{repo}/#{arch}/*.rpm"].each do |rpm|
89
+ FileUtils.cp rpm, arch_dest
90
+ end
91
+
92
+ Dir.chdir arch_dest do
93
+ build_repo arch
94
+ Dir.chdir('repodata') { sign_repo repo }
95
+ end
96
+ end
97
+ end
98
+
99
+ def build_repo(arch)
100
+ cmd = 'createrepo --zck --verbose --update .'
101
+
102
+ out, status = Open3.capture2e cmd
103
+
104
+ return if status.exitstatus.zero?
105
+
106
+ Tools.error "unable to create repo for #{arch} - createrepo "\
107
+ "returned:\n#{out}"
108
+ end
109
+
110
+ def sign_repo(repo)
111
+ keyid = @config.cfg[:repos][repo][:keyid]
112
+
113
+ data = GPGME::Data.new File.read('repomd.xml')
114
+ opt = {
115
+ armor: true,
116
+ signer: keyid,
117
+ mode: GPGME::SIG_MODE_DETACH
118
+ }
119
+
120
+ signature = GPGME::Crypto.sign data, opt
121
+
122
+ File.write 'repomd.xml.asc', signature
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'thor'
4
+ require 'colored'
5
+ require 'fileutils'
6
+ require 'terminal-table'
7
+
8
+ require_relative 'tools'
9
+ require_relative 'config'
10
+ require_relative 'backends'
11
+
12
+ module RepoMgr
13
+ # implements CLI interface
14
+ class CLI < Thor
15
+ def self.exit_on_failure?
16
+ true
17
+ end
18
+
19
+ def self.types
20
+ %w[deb rpm]
21
+ end
22
+
23
+ desc 'check-depends', 'Check dependencies'
24
+
25
+ def check_depends
26
+ 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
34
+ end
35
+
36
+ puts Terminal::Table.new headings: %w[Binary Status], rows: rows
37
+ end
38
+
39
+ desc 'upsert-repo', 'Create/Update new repository'
40
+ option :name, type: :string, required: true, aliases: %w[-n],
41
+ desc: 'Name of repository to add'
42
+ option :type, type: :string, required: true, aliases: %w[-t],
43
+ enum: types, desc: 'Package type'
44
+ option :path, type: :string, required: true, aliases: %w[-p],
45
+ desc: 'Directory path where to build the repository'
46
+ option :keyid, type: :string, required: true, aliases: %w[-k],
47
+ desc: 'GPG key id used to sign the repository metadata'
48
+
49
+ def upsert_repo
50
+ FileUtils.mkdir_p options[:path]
51
+
52
+ config = Config.new
53
+ config.upsert_repo options[:name], options[:type], options[:path],
54
+ options[:keyid]
55
+
56
+ backend = Backends.load options[:type], config
57
+ backend.add_repo options[:name]
58
+
59
+ puts "-- Upserted #{options[:name]} repository"
60
+ end
61
+
62
+ desc 'list-repos', 'List existing repositories'
63
+
64
+ def list_repos
65
+ rows = []
66
+ config = Config.new
67
+
68
+ config.cfg[:repos].each do |name, repo|
69
+ rows << [name, repo[:type], repo[:path], repo[:keyid]]
70
+ end
71
+
72
+ return puts '-- No repos have been created' if rows.count.zero?
73
+
74
+ puts Terminal::Table.new headings: %w[Name Type Path KeyID], rows: rows
75
+ end
76
+
77
+ desc 'add-pkg', 'Add package to repository'
78
+ option :repo, type: :string, required: true, aliases: %w[-r],
79
+ desc: 'The repository to add the package to'
80
+ option :path, type: :string, required: true, aliases: %w[-p],
81
+ desc: 'Path to the package to add to a repo'
82
+
83
+ def add_pkg
84
+ backend, config = load_backend options[:path]
85
+ backend.add_pkg options[:repo], options[:path]
86
+ config.add_pkg options[:repo], options[:path]
87
+
88
+ puts "-- Added #{File.basename(options[:path])} to "\
89
+ "#{options[:repo]} repository"
90
+ end
91
+
92
+ desc 'list-pkgs', 'List repository packages'
93
+ option :repo, type: :string, required: true, aliases: %w[-r],
94
+ desc: 'The repository to list the packages from'
95
+
96
+ def list_pkgs
97
+ packages = Config.new.cfg[:repos][options[:repo]][:packages]
98
+
99
+ if packages.nil?
100
+ Tools.error "#{options[:repo]} repo does not have any packages"
101
+ end
102
+
103
+ rows = packages.sort.each_with_index.map { |e, i| [i + 1, e] }
104
+
105
+ puts Terminal::Table.new headings: ['#', "Packages in #{options[:repo]}"],
106
+ rows: rows
107
+ end
108
+
109
+ desc 'remove-pkg', 'Remove a package from a repository'
110
+ option :repo, type: :string, required: true, aliases: %w[-r],
111
+ desc: 'The repository to add the package to'
112
+ option :path, type: :string, required: true, aliases: %w[-p],
113
+ desc: 'Path to the package to add to a repo'
114
+
115
+ def remove_pkg
116
+ backend, config = load_backend options[:path]
117
+ backend.remove_pkg options[:repo], options[:path]
118
+ config.remove_pkg options[:repo], options[:path]
119
+
120
+ puts "-- Removed #{File.basename(options[:path])} from "\
121
+ "#{options[:repo]} repository"
122
+ end
123
+
124
+ desc 'check-sig', 'Check package signature'
125
+ option :path, type: :string, required: true, aliases: %w[-p],
126
+ desc: 'Path to the the package to check signature'
127
+
128
+ def check_sig
129
+ backend, _config = load_backend options[:path]
130
+ puts backend.check_sig options[:path]
131
+ end
132
+
133
+ private
134
+
135
+ def load_backend(path)
136
+ type = File.extname(path).strip.downcase[1..-1]
137
+
138
+ unless CLI.types.include? type
139
+ Tools.error "unsupported package type #{type}"
140
+ end
141
+
142
+ config = Config.new
143
+
144
+ [Backends.load(type, config), config]
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ require_relative 'tools'
7
+
8
+ module RepoMgr
9
+ # handles repo-mgr configuration
10
+ class Config
11
+ attr_reader :cfg, :cfg_dir
12
+
13
+ def initialize
14
+ @cfg_dir = "#{ENV['HOME']}/.repo-mgr"
15
+ FileUtils.mkdir_p @cfg_dir
16
+
17
+ @cfg_file = "#{@cfg_dir}/repo-mgr.yml"
18
+ File.write @cfg_file, { repos: {} }.to_yaml unless File.exist? @cfg_file
19
+
20
+ @cfg = YAML.load_file @cfg_file
21
+ end
22
+
23
+ def save
24
+ File.write @cfg_file, @cfg.to_yaml
25
+ end
26
+
27
+ def upsert_repo(name, type, path, keyid)
28
+ if @cfg[:repos][name] && @cfg[:repos][name][:type] != type
29
+ Tools.error "unable to change type for #{name} repository"
30
+ end
31
+
32
+ @cfg[:repos][name] = {
33
+ type: type,
34
+ path: path,
35
+ keyid: keyid
36
+ }
37
+
38
+ save
39
+ end
40
+
41
+ def add_pkg(repo, path)
42
+ if @cfg[:repos][repo].nil?
43
+ Tools.error "unable to add packages to #{repo} - repo does not exist"
44
+ end
45
+
46
+ @cfg[:repos][repo][:packages] ||= []
47
+ pkg = File.basename path
48
+
49
+ if @cfg[:repos][repo][:packages].include?(pkg)
50
+ Tools.error "you already have #{pkg} in your #{repo} repo"
51
+ end
52
+
53
+ @cfg[:repos][repo][:packages] << pkg
54
+
55
+ save
56
+ end
57
+
58
+ def remove_pkg(repo, path)
59
+ if @cfg[:repos][repo].nil?
60
+ Tools.error "unable to remove packages from #{repo} "\
61
+ '- repo does not exist'
62
+ end
63
+
64
+ @cfg[:repos][repo][:packages] ||= []
65
+ pkg = File.basename path
66
+ @cfg[:repos][repo][:packages].delete pkg
67
+
68
+ save
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'colored'
4
+
5
+ module RepoMgr
6
+ # Holds various tools
7
+ class Tools
8
+ def self.which(cmd)
9
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
10
+
11
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
12
+ exts.each do |ext|
13
+ exe = File.join(path, "#{cmd}#{ext}")
14
+ return exe if File.executable?(exe) && !File.directory?(exe)
15
+ end
16
+ end
17
+
18
+ nil
19
+ end
20
+
21
+ def self.error(msg)
22
+ warn "-- Error: #{msg}".red
23
+ Kernel.exit 1
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: repo-mgr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ștefan Rusu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colored
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gpgme
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: terminal-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 13.0.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 13.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: deb and rpm repository manager
126
+ email: saltwaterc@gmail.com
127
+ executables:
128
+ - repo-mgr
129
+ extensions: []
130
+ extra_rdoc_files:
131
+ - LICENSE
132
+ - README.md
133
+ files:
134
+ - LICENSE
135
+ - README.md
136
+ - bin/repo-mgr
137
+ - lib/repo_mgr.rb
138
+ - lib/repo_mgr/backends.rb
139
+ - lib/repo_mgr/backends/deb.rb
140
+ - lib/repo_mgr/backends/rpm.rb
141
+ - lib/repo_mgr/cli.rb
142
+ - lib/repo_mgr/config.rb
143
+ - lib/repo_mgr/tools.rb
144
+ homepage: https://github.com/mr-staker/repo-mgr
145
+ licenses:
146
+ - MIT
147
+ metadata: {}
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements: []
163
+ rubygems_version: 3.1.4
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: deb and rpm repository manager
167
+ test_files: []