repo-mgr 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 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: []