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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +181 -0
- data/.ruby-version +1 -0
- data/ASSESSMENT.md +88 -0
- data/CLAUDE.md +76 -0
- data/Dockerfile.test +11 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/MACROPLAN.md +63 -0
- data/README.md +74 -0
- data/Rakefile +39 -0
- data/docs/superpowers/plans/2026-04-16-container-integration-tests.md +605 -0
- data/exe/gemvault +5 -0
- data/lib/bundler/plugin/vault_source.rb +117 -0
- data/lib/gemvault/cli/command.rb +17 -0
- data/lib/gemvault/cli/commands/add.rb +30 -0
- data/lib/gemvault/cli/commands/extract.rb +49 -0
- data/lib/gemvault/cli/commands/list.rb +26 -0
- data/lib/gemvault/cli/commands/new.rb +28 -0
- data/lib/gemvault/cli/commands/remove.rb +34 -0
- data/lib/gemvault/cli.rb +45 -0
- data/lib/gemvault/gem_entry.rb +37 -0
- data/lib/gemvault/vault.rb +178 -0
- data/lib/gemvault/version.rb +3 -0
- data/lib/gemvault.rb +10 -0
- data/lib/rubygems/resolver/vault_set.rb +39 -0
- data/lib/rubygems/source/vault.rb +124 -0
- data/lib/rubygems_plugin.rb +72 -0
- metadata +116 -0
|
@@ -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: []
|