gemvault 0.1.1

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.
@@ -0,0 +1,124 @@
1
+ require "gemvault/vault"
2
+
3
+ ##
4
+ # A source backed by a .gemv vault file (SQLite archive of .gem blobs).
5
+ #
6
+ # Used by the gemvault RubyGems plugin to support:
7
+ #
8
+ # gem install --source myvault.gemv activesupport
9
+
10
+ class Gem::Source::Vault < Gem::Source
11
+ include Gem::UserInteraction
12
+
13
+ attr_reader :path
14
+
15
+ def initialize(path)
16
+ path = path.to_s
17
+ path = path.sub(%r{^file://}, "") if path.start_with?("file://")
18
+ @path = File.expand_path(path)
19
+ @uri = @path
20
+ @specs = nil
21
+ end
22
+
23
+ def load_specs(type)
24
+ verbose "Loading #{type} specs from vault at #{@path}"
25
+ ensure_specs_loaded
26
+
27
+ case type
28
+ when :released
29
+ @specs.keys.reject { |t| t.version.prerelease? }
30
+ when :prerelease
31
+ @specs.keys.select { |t| t.version.prerelease? }
32
+ when :latest
33
+ @specs.keys
34
+ .group_by { |tuple| [tuple.name, tuple.platform] }
35
+ .values
36
+ .map { |tuples| tuples.max_by(&:version) }
37
+ else
38
+ @specs.keys
39
+ end
40
+ end
41
+
42
+ def fetch_spec(name_tuple)
43
+ ensure_specs_loaded
44
+
45
+ spec = @specs[name_tuple]
46
+ raise Gem::Exception, "Unable to find '#{name_tuple}'" unless spec
47
+
48
+ spec
49
+ end
50
+
51
+ def download(spec, dir = Dir.pwd)
52
+ verbose "Extracting #{spec.file_name} from vault at #{@path}"
53
+ cache_dir = File.join(dir, "cache")
54
+ FileUtils.mkdir_p(cache_dir)
55
+
56
+ dest = File.join(cache_dir, spec.file_name)
57
+
58
+ Gemvault::Vault.open(@path) do |vault|
59
+ data = vault.gem_data(spec.name, spec.version.to_s, platform: spec.platform.to_s)
60
+ File.binwrite(dest, data)
61
+ end
62
+
63
+ dest
64
+ end
65
+
66
+ def dependency_resolver_set(prerelease = false)
67
+ require_relative "../resolver/vault_set"
68
+ set = Gem::Resolver::VaultSet.new(self)
69
+ set.prerelease = prerelease
70
+ set
71
+ end
72
+
73
+ def <=>(other)
74
+ case other
75
+ when Gem::Source::Installed,
76
+ Gem::Source::Lock then
77
+ -1
78
+ when Gem::Source::Vault
79
+ 0
80
+ when Gem::Source::Local
81
+ -1
82
+ when Gem::Source
83
+ 1
84
+ end
85
+ end
86
+
87
+ def ==(other)
88
+ other.is_a?(self.class) && @path == other.path
89
+ end
90
+
91
+ alias eql? ==
92
+
93
+ def hash
94
+ @path.hash
95
+ end
96
+
97
+ def to_s
98
+ "vault at #{@path}"
99
+ end
100
+
101
+ def pretty_print(q)
102
+ q.object_group(self) do
103
+ q.group 2, "[Vault:", "]" do
104
+ q.breakable
105
+ q.text @path
106
+ end
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def ensure_specs_loaded
113
+ return if @specs
114
+
115
+ @specs = {}
116
+ Gemvault::Vault.open(@path) do |vault|
117
+ vault.gem_entries.each do |entry|
118
+ spec = vault.spec_from_blob(entry.name, entry.version, entry.platform)
119
+ tuple = spec.name_tuple
120
+ @specs[tuple] = spec
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "rubygems/source/vault"
2
+ require "rubygems/source_list"
3
+
4
+ ##
5
+ # RubyGems plugin for .gemv vault sources.
6
+ #
7
+ # Enables: gem install --source myvault.gemv activesupport
8
+ #
9
+ # Three monkey-patches (via prepend) are needed because RubyGems has no
10
+ # source registration API:
11
+ #
12
+ # 1. accept_uri_http -- let .gemv paths bypass URI scheme validation
13
+ # 2. add_source_option -- skip trailing "/" append for .gemv paths
14
+ # 3. SourceList#<< -- route .gemv strings to Gem::Source::Vault
15
+
16
+ module Gemvault
17
+ module AcceptVaultURI
18
+ def accept_uri_http
19
+ Gem::OptionParser.accept Gem::URI::HTTP do |value|
20
+ next value if value.to_s.end_with?(".gemv")
21
+
22
+ begin
23
+ uri = Gem::URI.parse value
24
+ rescue Gem::URI::InvalidURIError
25
+ raise Gem::OptionParser::InvalidArgument, value
26
+ end
27
+
28
+ valid_uri_schemes = ["http", "https", "file", "s3"]
29
+ unless valid_uri_schemes.include?(uri.scheme)
30
+ msg = "Invalid uri scheme for #{value}\nPreface URLs with one of #{valid_uri_schemes.map { |s| "#{s}://" }}"
31
+ raise ArgumentError, msg
32
+ end
33
+
34
+ value
35
+ end
36
+ end
37
+ end
38
+
39
+ module AddVaultSourceOption
40
+ def add_source_option
41
+ accept_uri_http
42
+
43
+ add_option(:"Local/Remote", "-s", "--source URL", Gem::URI::HTTP,
44
+ "Append URL to list of remote gem sources") do |source, options|
45
+ source << "/" unless source.end_with?("/", ".gemv")
46
+
47
+ if options.delete :sources_cleared
48
+ Gem.sources = [source]
49
+ else
50
+ Gem.sources << source unless Gem.sources.include?(source)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ module VaultSourceList
57
+ def <<(obj)
58
+ if obj.is_a?(String) && obj.end_with?(".gemv")
59
+ src = Gem::Source::Vault.new(obj)
60
+ @sources << src unless @sources.include?(src)
61
+ return src
62
+ end
63
+ super
64
+ end
65
+ end
66
+ end
67
+
68
+ Gem::SourceList.prepend Gemvault::VaultSourceList
69
+
70
+ require "rubygems/local_remote_options"
71
+ Gem::LocalRemoteOptions.prepend Gemvault::AcceptVaultURI
72
+ Gem::LocalRemoteOptions.prepend Gemvault::AddVaultSourceOption
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemvault
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - David Gillis
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: command_kit
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.6'
40
+ - !ruby/object:Gem::Dependency
41
+ name: sqlite3
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ description: SQLite-backed .gemv archives for bundling and distributing multiple gems
55
+ as a single file
56
+ email:
57
+ - david@flipmine.com
58
+ executables:
59
+ - gemvault
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".rspec"
64
+ - ".rubocop.yml"
65
+ - ".ruby-version"
66
+ - ASSESSMENT.md
67
+ - CLAUDE.md
68
+ - Dockerfile.test
69
+ - LICENSE
70
+ - LICENSE.txt
71
+ - MACROPLAN.md
72
+ - README.md
73
+ - Rakefile
74
+ - docs/superpowers/plans/2026-04-16-container-integration-tests.md
75
+ - exe/gemvault
76
+ - lib/bundler/plugin/vault_source.rb
77
+ - lib/gemvault.rb
78
+ - lib/gemvault/cli.rb
79
+ - lib/gemvault/cli/command.rb
80
+ - lib/gemvault/cli/commands/add.rb
81
+ - lib/gemvault/cli/commands/extract.rb
82
+ - lib/gemvault/cli/commands/list.rb
83
+ - lib/gemvault/cli/commands/new.rb
84
+ - lib/gemvault/cli/commands/remove.rb
85
+ - lib/gemvault/gem_entry.rb
86
+ - lib/gemvault/vault.rb
87
+ - lib/gemvault/version.rb
88
+ - lib/rubygems/resolver/vault_set.rb
89
+ - lib/rubygems/source/vault.rb
90
+ - lib/rubygems_plugin.rb
91
+ homepage: https://github.com/gillisd/gemvault
92
+ licenses:
93
+ - MIT
94
+ metadata:
95
+ rubygems_mfa_required: 'true'
96
+ source_code_uri: https://github.com/gillisd/gemvault
97
+ bug_tracker_uri: https://github.com/gillisd/gemvault/issues
98
+ changelog_uri: https://github.com/gillisd/gemvault/blob/master/CHANGELOG.md
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 4.0.1
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 4.0.9
114
+ specification_version: 4
115
+ summary: Multi-gem portable archives backed by SQLite
116
+ test_files: []