gemvault 0.1.5 → 0.2.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 +4 -4
- data/.containerignore +10 -0
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +30 -0
- data/CLAUDE.md +5 -4
- data/Dockerfile.test +11 -1
- data/README.md +3 -11
- data/Rakefile +9 -0
- data/issues.rec +540 -17
- data/lib/bundler/plugin/vault_source.rb +48 -73
- data/lib/bundler/plugin/vaulted_gem.rb +66 -0
- data/lib/gemvault/archive_entry.rb +6 -0
- data/lib/gemvault/cli/command.rb +9 -5
- data/lib/gemvault/cli/commands/extract.rb +11 -11
- data/lib/gemvault/cli/commands/new.rb +3 -2
- data/lib/gemvault/cli/commands/remove.rb +15 -5
- data/lib/gemvault/cli/commands/upgrade.rb +37 -13
- data/lib/gemvault/dbvault.rb +12 -9
- data/lib/gemvault/deprecation.rb +26 -24
- data/lib/gemvault/gem_entry.rb +20 -24
- data/lib/gemvault/gem_extraction.rb +19 -23
- data/lib/gemvault/gem_reference/any_version.rb +6 -2
- data/lib/gemvault/gem_reference/parser.rb +44 -0
- data/lib/gemvault/gem_reference/specific_version.rb +9 -25
- data/lib/gemvault/gem_reference.rb +13 -45
- data/lib/gemvault/manifest.rb +44 -42
- data/lib/gemvault/tarball.rb +53 -0
- data/lib/gemvault/tarvault.rb +47 -56
- data/lib/gemvault/vault.rb +17 -13
- data/lib/gemvault/vault_path.rb +33 -0
- data/lib/gemvault/vault_upgrade.rb +31 -24
- data/lib/gemvault/version.rb +1 -1
- data/lib/gemvault.rb +1 -0
- data/lib/rubygems/resolver/vault_set.rb +5 -7
- data/lib/rubygems/source/vault.rb +28 -32
- data/lib/rubygems_plugin.rb +8 -6
- metadata +7 -16
- data/lib/gemvault/tar_archive.rb +0 -66
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
require "
|
|
1
|
+
require "pathname"
|
|
2
|
+
require_relative "vaulted_gem"
|
|
2
3
|
require_relative "../../gemvault/vault"
|
|
4
|
+
require_relative "../../gemvault/gem_entry"
|
|
3
5
|
|
|
4
6
|
module Bundler
|
|
5
7
|
module Plugin
|
|
6
|
-
# Bundler plugin source that installs gems from a .gemv vault file.
|
|
8
|
+
# Bundler plugin source that installs gems from a .gemv vault file. A thin
|
|
9
|
+
# doorway over the Bundler Source API: it resolves the vault path, hands
|
|
10
|
+
# Bundler the gemspecs it can install, and delegates each gem's placement in
|
|
11
|
+
# the bundle to a VaultedGem.
|
|
7
12
|
class VaultSource
|
|
8
13
|
def initialize(opts)
|
|
9
14
|
super
|
|
10
|
-
@vault_path =
|
|
15
|
+
@vault_path = Pathname(@uri).expand_path(Bundler.root)
|
|
11
16
|
@allow_remote = false
|
|
12
17
|
end
|
|
13
18
|
|
|
@@ -15,7 +20,9 @@ module Bundler
|
|
|
15
20
|
validate_vault_exists!
|
|
16
21
|
|
|
17
22
|
Gemvault::Vault.open(@vault_path) do |vault|
|
|
18
|
-
vault.gem_entries
|
|
23
|
+
vault.gem_entries
|
|
24
|
+
.map { |entry| vault.spec_from_blob(entry) }
|
|
25
|
+
.filter_map { |spec| gemspec_file_for(spec) }
|
|
19
26
|
end
|
|
20
27
|
end
|
|
21
28
|
|
|
@@ -30,22 +37,35 @@ module Bundler
|
|
|
30
37
|
@allow_remote = true
|
|
31
38
|
end
|
|
32
39
|
|
|
40
|
+
# The inverse of remote!. Bundler's local-only resolve (Definition#check!,
|
|
41
|
+
# reached by `bundle check`) calls local_only! on every source. The base
|
|
42
|
+
# Bundler::Plugin::API::Source defines no such hook, so without this the
|
|
43
|
+
# command dies with NoMethodError; here it simply stops advertising gems
|
|
44
|
+
# that live only inside the archive.
|
|
45
|
+
def local_only!
|
|
46
|
+
@allow_remote = false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Also invoked on every source during resolution (Definition#prefer_local!,
|
|
50
|
+
# for `--prefer-local`). The vault reads from one local file already, so
|
|
51
|
+
# there is no remote to deprioritize.
|
|
52
|
+
def prefer_local!; end
|
|
53
|
+
|
|
33
54
|
def install(spec, opts = {})
|
|
34
|
-
|
|
35
|
-
return
|
|
55
|
+
gem = VaultedGem.new(spec)
|
|
56
|
+
return gem.adopt if gem.installed? && !opts[:force]
|
|
36
57
|
|
|
37
|
-
Bundler.ui.confirm "Installing #{version_message
|
|
38
|
-
|
|
39
|
-
spec.post_install_message
|
|
58
|
+
Bundler.ui.confirm "Installing #{gem.version_message} from vault #{@uri}"
|
|
59
|
+
extract(spec) { |gem_path| gem.install(gem_path:, build_args: opts[:build_args] || []) }
|
|
40
60
|
end
|
|
41
61
|
|
|
42
62
|
def options_to_lock
|
|
43
63
|
{}
|
|
44
64
|
end
|
|
45
65
|
|
|
46
|
-
# No source-level install_path to copy: VaultSource#install installs
|
|
47
|
-
#
|
|
48
|
-
#
|
|
66
|
+
# No source-level install_path to copy: VaultSource#install installs each
|
|
67
|
+
# gem into Bundler.bundle_path via RubyGemsGemInstaller, so the default
|
|
68
|
+
# Source#cache would dereference a non-existent directory.
|
|
49
69
|
def cache(spec, custom_path = nil); end
|
|
50
70
|
|
|
51
71
|
def to_s
|
|
@@ -54,80 +74,35 @@ module Bundler
|
|
|
54
74
|
|
|
55
75
|
private
|
|
56
76
|
|
|
57
|
-
def gemspec_file_for(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return anchor_gemspec(gem_dir, spec.full_name, spec.to_ruby) if File.directory?(gem_dir)
|
|
77
|
+
def gemspec_file_for(spec)
|
|
78
|
+
gem = VaultedGem.new(spec)
|
|
79
|
+
return gem.anchor(spec.to_ruby) if gem.installed?
|
|
61
80
|
return nil unless @allow_remote
|
|
62
81
|
|
|
63
|
-
write_tmp_gemspec(spec
|
|
82
|
+
write_tmp_gemspec(spec)
|
|
64
83
|
end
|
|
65
84
|
|
|
66
|
-
def write_tmp_gemspec(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
def write_tmp_gemspec(spec)
|
|
86
|
+
tmp_dir = Pathname(Bundler.tmp("vault_source"))
|
|
87
|
+
gemspec_dir = tmp_dir / "specifications"
|
|
88
|
+
gemspec_dir.mkpath
|
|
89
|
+
gemspec = gemspec_dir / "#{spec.full_name}.gemspec"
|
|
90
|
+
gemspec.write(spec.to_ruby)
|
|
91
|
+
gemspec.to_s
|
|
72
92
|
end
|
|
73
93
|
|
|
74
|
-
def
|
|
75
|
-
Bundler.ui.debug "Using #{version_message(spec)} from vault #{@uri}"
|
|
76
|
-
spec.full_gem_path = gem_dir
|
|
77
|
-
spec.loaded_from = File.join(gem_dir, "#{spec.full_name}.gemspec")
|
|
78
|
-
nil
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def install_into_bundle(spec, opts)
|
|
94
|
+
def extract(spec, &)
|
|
82
95
|
Gemvault::Vault.open(@vault_path) do |vault|
|
|
83
|
-
vault.with_gem_file(spec
|
|
84
|
-
installed_spec = build_installer(gem_path, opts).install
|
|
85
|
-
gem_dir = installed_spec.full_gem_path
|
|
86
|
-
spec.full_gem_path = gem_dir
|
|
87
|
-
spec.loaded_from = anchor_gemspec(gem_dir, spec.full_name, installed_spec.to_ruby)
|
|
88
|
-
end
|
|
96
|
+
vault.with_gem_file(entry_for(spec), &)
|
|
89
97
|
end
|
|
90
98
|
end
|
|
91
99
|
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
Bundler::RubyGemsGemInstaller.at(
|
|
96
|
-
gem_path,
|
|
97
|
-
install_dir: Bundler.bundle_path.to_s,
|
|
98
|
-
bin_dir: Bundler.system_bindir.to_s,
|
|
99
|
-
ignore_dependencies: true,
|
|
100
|
-
wrappers: true,
|
|
101
|
-
env_shebang: true,
|
|
102
|
-
build_args: opts[:build_args] || [],
|
|
103
|
-
)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def version_message(spec)
|
|
107
|
-
message = "#{spec.name} #{spec.version}"
|
|
108
|
-
message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY && !spec.platform.nil?
|
|
109
|
-
message
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def resolve_vault_path(uri)
|
|
113
|
-
File.expand_path(uri, Bundler.root.to_s)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Bundler computes full_gem_path as dirname(loaded_from) for plugin
|
|
117
|
-
# sources, so the gemspec must live inside the gem directory -- not
|
|
118
|
-
# in specifications/ -- for load paths to resolve correctly.
|
|
119
|
-
def anchor_gemspec(gem_dir, full_name, spec_ruby)
|
|
120
|
-
gemspec_path = File.join(gem_dir, "#{full_name}.gemspec")
|
|
121
|
-
File.write(gemspec_path, spec_ruby) unless File.exist?(gemspec_path)
|
|
122
|
-
gemspec_path
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def gem_dir_for(full_name)
|
|
126
|
-
File.join(Bundler.bundle_path, "gems", full_name)
|
|
100
|
+
def entry_for(spec)
|
|
101
|
+
Gemvault::GemEntry.from_spec(spec)
|
|
127
102
|
end
|
|
128
103
|
|
|
129
104
|
def validate_vault_exists!
|
|
130
|
-
return if
|
|
105
|
+
return if @vault_path.file?
|
|
131
106
|
|
|
132
107
|
raise Bundler::PathError,
|
|
133
108
|
"Could not find vault '#{@uri}' referenced in Gemfile " \
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module Bundler
|
|
4
|
+
module Plugin
|
|
5
|
+
# A gem sourced from a vault as it lives (or will live) inside the bundle.
|
|
6
|
+
# Owns where the gem unpacks, whether it is already installed, installing an
|
|
7
|
+
# extracted .gem file, and anchoring its gemspec inside the gem directory --
|
|
8
|
+
# Bundler derives full_gem_path from dirname(loaded_from), so the gemspec
|
|
9
|
+
# must sit beside the gem, not in specifications/.
|
|
10
|
+
class VaultedGem
|
|
11
|
+
def initialize(spec)
|
|
12
|
+
@spec = spec
|
|
13
|
+
@gem_dir = Bundler.bundle_path.join("gems", spec.full_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def installed?
|
|
17
|
+
@gem_dir.directory?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def version_message
|
|
21
|
+
message = "#{@spec.name} #{@spec.version}"
|
|
22
|
+
message += " (#{@spec.platform})" if @spec.platform != Gem::Platform::RUBY && !@spec.platform.nil?
|
|
23
|
+
message
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Point the Bundler spec at the already-unpacked gem.
|
|
27
|
+
def adopt
|
|
28
|
+
Bundler.ui.debug "Using #{version_message} from vault"
|
|
29
|
+
@spec.full_gem_path = @gem_dir.to_s
|
|
30
|
+
@spec.loaded_from = anchor(@spec.to_ruby)
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Install the extracted .gem file into the bundle, point the spec at it,
|
|
35
|
+
# and return its post-install message.
|
|
36
|
+
def install(gem_path:, build_args:)
|
|
37
|
+
installed = installer(gem_path:, build_args:).install
|
|
38
|
+
@spec.full_gem_path = installed.full_gem_path
|
|
39
|
+
@spec.loaded_from = anchor(installed.to_ruby, dir: installed.full_gem_path)
|
|
40
|
+
@spec.post_install_message
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def anchor(spec_ruby, dir: @gem_dir)
|
|
44
|
+
gemspec = Pathname(dir).join("#{@spec.full_name}.gemspec")
|
|
45
|
+
gemspec.write(spec_ruby) unless gemspec.exist?
|
|
46
|
+
gemspec.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def installer(gem_path:, build_args:)
|
|
52
|
+
require "bundler/rubygems_gem_installer"
|
|
53
|
+
|
|
54
|
+
Bundler::RubyGemsGemInstaller.at(
|
|
55
|
+
gem_path,
|
|
56
|
+
install_dir: Bundler.bundle_path.to_s,
|
|
57
|
+
bin_dir: Bundler.system_bindir.to_s,
|
|
58
|
+
ignore_dependencies: true,
|
|
59
|
+
wrappers: true,
|
|
60
|
+
env_shebang: true,
|
|
61
|
+
build_args:,
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/gemvault/cli/command.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "command_kit/command"
|
|
2
2
|
require_relative "../vault"
|
|
3
|
+
require_relative "../vault_path"
|
|
3
4
|
|
|
4
5
|
module Gemvault
|
|
5
6
|
class CLI
|
|
@@ -7,11 +8,14 @@ module Gemvault
|
|
|
7
8
|
class Command < CommandKit::Command
|
|
8
9
|
private
|
|
9
10
|
|
|
10
|
-
def with_vault(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
def with_vault(locator, create: false, &block)
|
|
12
|
+
begin
|
|
13
|
+
path = VaultPath.resolve(locator)
|
|
14
|
+
Gemvault::Vault.open(path, create:, &block)
|
|
15
|
+
rescue Gemvault::Vault::Error => e
|
|
16
|
+
print_error(e.message)
|
|
17
|
+
exit(1)
|
|
18
|
+
end
|
|
15
19
|
end
|
|
16
20
|
end
|
|
17
21
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require "
|
|
1
|
+
require "pathname"
|
|
2
2
|
require_relative "../command"
|
|
3
3
|
|
|
4
4
|
module Gemvault
|
|
@@ -25,21 +25,21 @@ module Gemvault
|
|
|
25
25
|
desc: "Output directory"
|
|
26
26
|
|
|
27
27
|
def run(vault, name, version = nil)
|
|
28
|
-
output_dir = options[:output]
|
|
28
|
+
output_dir = Pathname(options[:output])
|
|
29
29
|
|
|
30
30
|
with_vault(vault) do |v|
|
|
31
|
-
|
|
32
|
-
entries = matching_entries(v, name
|
|
31
|
+
output_dir.mkpath
|
|
32
|
+
entries = matching_entries(vault: v, name:, version:)
|
|
33
33
|
abort_missing_gem(name) if entries.empty?
|
|
34
|
-
extract_entries(v, entries
|
|
34
|
+
extract_entries(vault: v, entries:, output_dir:)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
|
-
def matching_entries(vault
|
|
41
|
-
entries = vault.gem_entries.select { |
|
|
42
|
-
version ? entries.select { |
|
|
40
|
+
def matching_entries(vault:, name:, version:)
|
|
41
|
+
entries = vault.gem_entries.select { |entry| entry.name == name }
|
|
42
|
+
version ? entries.select { |entry| entry.version == version } : entries
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def abort_missing_gem(name)
|
|
@@ -47,10 +47,10 @@ module Gemvault
|
|
|
47
47
|
exit(1)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def extract_entries(vault
|
|
50
|
+
def extract_entries(vault:, entries:, output_dir:)
|
|
51
51
|
entries.each do |entry|
|
|
52
|
-
data = vault.gem_data(entry
|
|
53
|
-
|
|
52
|
+
data = vault.gem_data(entry)
|
|
53
|
+
(output_dir / entry.filename).binwrite(data)
|
|
54
54
|
puts "Extracted #{entry.filename}"
|
|
55
55
|
end
|
|
56
56
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "pathname"
|
|
1
2
|
require_relative "../command"
|
|
2
3
|
|
|
3
4
|
module Gemvault
|
|
@@ -12,9 +13,9 @@ module Gemvault
|
|
|
12
13
|
desc: "Vault name (auto-appends .gemv)"
|
|
13
14
|
|
|
14
15
|
def run(name)
|
|
15
|
-
path = name.end_with?(".gemv") ? name : "#{name}.gemv"
|
|
16
|
+
path = Pathname(name.end_with?(".gemv") ? name : "#{name}.gemv")
|
|
16
17
|
|
|
17
|
-
if
|
|
18
|
+
if path.exist?
|
|
18
19
|
print_error("#{path} already exists")
|
|
19
20
|
exit(1)
|
|
20
21
|
end
|
|
@@ -26,15 +26,25 @@ module Gemvault
|
|
|
26
26
|
desc: "Gem version (overrides positional and NAME-VERSION forms)"
|
|
27
27
|
|
|
28
28
|
def run(vault, name, positional_version = nil)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
begin
|
|
30
|
+
version = options[:version] || positional_version
|
|
31
|
+
reference = Gemvault::GemReference.parse(name, version:)
|
|
32
|
+
remove_matching(vault:, reference:)
|
|
33
|
+
rescue Gemvault::GemReference::NonExactVersionError => e
|
|
34
|
+
print_error(e.message)
|
|
35
|
+
exit(1)
|
|
36
|
+
end
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
private
|
|
37
40
|
|
|
41
|
+
def remove_matching(vault:, reference:)
|
|
42
|
+
with_vault(vault) do |v|
|
|
43
|
+
removed = v.remove(reference)
|
|
44
|
+
report_removal(removed)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
38
48
|
def report_removal(count)
|
|
39
49
|
if count.zero?
|
|
40
50
|
print_error("No matching gem found")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative "../command"
|
|
2
|
+
require_relative "../../vault_path"
|
|
2
3
|
require_relative "../../vault_upgrade"
|
|
3
4
|
|
|
4
5
|
module Gemvault
|
|
@@ -17,29 +18,52 @@ module Gemvault
|
|
|
17
18
|
option :no_backup, desc: "Do not write a .bak copy before upgrading"
|
|
18
19
|
|
|
19
20
|
def run(vault)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
rescue Gemvault::Vault::Error => e
|
|
28
|
-
print_error(e.message)
|
|
29
|
-
exit(1)
|
|
21
|
+
begin
|
|
22
|
+
upgrade = build_upgrade(vault)
|
|
23
|
+
dispatch(vault:, upgrade:)
|
|
24
|
+
rescue Gemvault::Vault::Error => e
|
|
25
|
+
print_error(e.message)
|
|
26
|
+
exit(1)
|
|
27
|
+
end
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
private
|
|
33
31
|
|
|
34
|
-
def
|
|
32
|
+
def build_upgrade(vault)
|
|
33
|
+
path = VaultPath.resolve(vault)
|
|
34
|
+
Gemvault::VaultUpgrade.new(path, backup: backup?)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def backup?
|
|
38
|
+
!options[:no_backup]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def dispatch(vault:, upgrade:)
|
|
42
|
+
summary = upgrade.plan
|
|
43
|
+
case decision(summary)
|
|
44
|
+
in { no_op: true }
|
|
45
|
+
report_no_op(vault:, summary:)
|
|
46
|
+
in { dry_run: true }
|
|
47
|
+
report_dry_run(vault:, summary:)
|
|
48
|
+
else
|
|
49
|
+
upgrade.call
|
|
50
|
+
report_upgraded(vault:, summary:)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def decision(summary)
|
|
55
|
+
{ no_op: summary.no_op?, dry_run: options[:dry_run] }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def report_no_op(vault:, summary:)
|
|
35
59
|
puts "#{vault} is already current (format #{summary.to_version})"
|
|
36
60
|
end
|
|
37
61
|
|
|
38
|
-
def report_dry_run(vault
|
|
62
|
+
def report_dry_run(vault:, summary:)
|
|
39
63
|
puts "Would upgrade #{vault}: #{summary} (#{summary.gem_count} gems)"
|
|
40
64
|
end
|
|
41
65
|
|
|
42
|
-
def report_upgraded(vault
|
|
66
|
+
def report_upgraded(vault:, summary:)
|
|
43
67
|
puts "Upgraded #{vault}: #{summary} (#{summary.gem_count} gems)"
|
|
44
68
|
end
|
|
45
69
|
end
|
data/lib/gemvault/dbvault.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "sqlite3"
|
|
2
|
+
require "pathname"
|
|
2
3
|
require_relative "vault"
|
|
3
4
|
require_relative "vault_session"
|
|
4
5
|
require_relative "gem_extraction"
|
|
@@ -18,7 +19,7 @@ module Gemvault
|
|
|
18
19
|
attr_reader :path
|
|
19
20
|
|
|
20
21
|
def initialize(path)
|
|
21
|
-
@path =
|
|
22
|
+
@path = Pathname(path).expand_path
|
|
22
23
|
open_vault!
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -30,12 +31,12 @@ module Gemvault
|
|
|
30
31
|
raise Vault::ReadOnlyError, read_only_message
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
def gem_data(
|
|
34
|
+
def gem_data(entry)
|
|
34
35
|
row = @db.execute(
|
|
35
36
|
"SELECT data FROM gems WHERE name = ? AND version = ? AND platform = ?",
|
|
36
|
-
[name, version, platform],
|
|
37
|
+
[entry.name, entry.version, entry.platform],
|
|
37
38
|
).first
|
|
38
|
-
raise Vault::NotFoundError, "Gem not found: #{
|
|
39
|
+
raise Vault::NotFoundError, "Gem not found: #{entry}" unless row
|
|
39
40
|
|
|
40
41
|
row["data"]
|
|
41
42
|
end
|
|
@@ -60,28 +61,30 @@ module Gemvault
|
|
|
60
61
|
|
|
61
62
|
def format_version
|
|
62
63
|
row = @db.execute("SELECT value FROM metadata WHERE key = 'vault_version'").first
|
|
63
|
-
|
|
64
|
+
return FORMAT_VERSION unless row
|
|
65
|
+
|
|
66
|
+
row["value"].to_i
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
private
|
|
67
70
|
|
|
68
71
|
def open_vault!
|
|
69
|
-
raise Vault::NotFoundError, "Vault not found: #{@path}" unless
|
|
72
|
+
raise Vault::NotFoundError, "Vault not found: #{@path}" unless @path.exist?
|
|
70
73
|
|
|
71
74
|
validate_sqlite!
|
|
72
75
|
@db = new_database
|
|
73
|
-
Vault.assert_readable!(format_version, @path)
|
|
76
|
+
Vault.assert_readable!(version: format_version, path: @path)
|
|
74
77
|
Deprecation.warn_once(deprecation_message)
|
|
75
78
|
end
|
|
76
79
|
|
|
77
80
|
def new_database
|
|
78
|
-
db = SQLite3::Database.new(@path)
|
|
81
|
+
db = SQLite3::Database.new(@path.to_s)
|
|
79
82
|
db.results_as_hash = true
|
|
80
83
|
db
|
|
81
84
|
end
|
|
82
85
|
|
|
83
86
|
def validate_sqlite!
|
|
84
|
-
return if
|
|
87
|
+
return if @path.binread(Vault::SQLITE_MAGIC.bytesize) == Vault::SQLITE_MAGIC
|
|
85
88
|
|
|
86
89
|
raise Vault::Error, "Not a valid vault file (not SQLite): #{@path}"
|
|
87
90
|
end
|
data/lib/gemvault/deprecation.rb
CHANGED
|
@@ -3,44 +3,46 @@ module Gemvault
|
|
|
3
3
|
# (stderr in production). Silenceable per-block or via an environment opt-out
|
|
4
4
|
# so automated pipelines that consume a legacy vault are not spammed.
|
|
5
5
|
module Deprecation
|
|
6
|
+
extend self
|
|
7
|
+
|
|
6
8
|
ENV_KEY = "GEMVAULT_SILENCE_DEPRECATIONS".freeze
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
attr_writer :output
|
|
10
|
+
attr_writer :output
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
def output
|
|
13
|
+
@output ||= $stderr
|
|
14
|
+
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
def warn_once(message)
|
|
17
|
+
return if silenced?
|
|
18
|
+
return unless seen.add?(message)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
end
|
|
20
|
+
output.puts("gemvault: #{message}")
|
|
21
|
+
end
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
def silence
|
|
24
|
+
previous = @silenced
|
|
25
|
+
@silenced = true
|
|
26
|
+
begin
|
|
25
27
|
yield
|
|
26
28
|
ensure
|
|
27
29
|
@silenced = previous
|
|
28
30
|
end
|
|
31
|
+
end
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
def silenced?
|
|
34
|
+
@silenced || ENV.key?(ENV_KEY)
|
|
35
|
+
end
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
def reset!
|
|
38
|
+
@seen = Set.new
|
|
39
|
+
@silenced = false
|
|
40
|
+
end
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
private
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
+
def seen
|
|
45
|
+
@seen ||= Set.new
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
end
|
data/lib/gemvault/gem_entry.rb
CHANGED
|
@@ -1,38 +1,34 @@
|
|
|
1
1
|
module Gemvault
|
|
2
|
-
# Value object for one gem
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
# Value object for one gem's identity (name, version, platform) and the time
|
|
3
|
+
# it was stored in a vault. Equality, hashing, and to_h come from Data.
|
|
4
|
+
class GemEntry < Data.define(:name, :version, :platform, :created_at)
|
|
5
|
+
RUBY_PLATFORM_NAME = "ruby".freeze
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
##
|
|
8
|
+
# Builds an entry from a <tt>Gem::Specification</tt> (or any object with
|
|
9
|
+
# +name+, +version+, and +platform+), stringifying the version and platform.
|
|
10
|
+
def self.from_spec(spec, created_at: nil)
|
|
11
|
+
new(name: spec.name, version: spec.version.to_s, platform: spec.platform.to_s, created_at:)
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
def
|
|
14
|
-
|
|
14
|
+
def initialize(name:, version:, platform: RUBY_PLATFORM_NAME, created_at: nil)
|
|
15
|
+
super
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
-
"#{full_name}.gem"
|
|
19
|
-
end
|
|
18
|
+
def ruby_platform? = platform == RUBY_PLATFORM_NAME
|
|
20
19
|
|
|
21
|
-
def
|
|
22
|
-
|
|
20
|
+
def full_name
|
|
21
|
+
ruby_platform? ? "#{name}-#{version}" : "#{name}-#{version}-#{platform}"
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
def
|
|
26
|
-
other.is_a?(self.class) &&
|
|
27
|
-
name == other.name &&
|
|
28
|
-
version == other.version &&
|
|
29
|
-
platform == other.platform
|
|
30
|
-
end
|
|
24
|
+
def filename = "#{full_name}.gem"
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
def same_identity_as?(other)
|
|
27
|
+
name == other.name && version == other.version && platform == other.platform
|
|
28
|
+
end
|
|
33
29
|
|
|
34
|
-
def
|
|
35
|
-
|
|
30
|
+
def to_s
|
|
31
|
+
ruby_platform? ? "#{name}-#{version}" : "#{name}-#{version} (#{platform})"
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
34
|
end
|