gemvault 0.2.5 → 0.2.7
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/CHANGELOG.md +61 -18
- data/CLAUDE.md +6 -3
- data/README.md +20 -6
- data/issues.rec +195 -4
- data/lib/gemvault/bundler_gemfile.rb +63 -0
- data/lib/gemvault/bundler_plugin_index.rb +58 -0
- data/lib/gemvault/bundler_plugin_root.rb +59 -0
- data/lib/gemvault/cli/commands/doctor.rb +90 -2
- data/lib/gemvault/legacy_tarvault.rb +114 -0
- data/lib/gemvault/manifest.rb +5 -37
- data/lib/gemvault/manifest_text.rb +149 -0
- data/lib/gemvault/tarball.rb +6 -0
- data/lib/gemvault/tarvault.rb +24 -14
- data/lib/gemvault/timestamp.rb +45 -0
- data/lib/gemvault/vault.rb +30 -5
- data/lib/gemvault/vault_upgrade.rb +5 -3
- data/lib/gemvault/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8478b689eb6f6b68e0c18872f4b2ca47034b1bd93ef9d0d16ecb92e8666de85
|
|
4
|
+
data.tar.gz: cb7f53a602a6b55a1a0593eda146635197e79dadb7366ca930a1e67b5b40d80d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: afa47c5b978c008d5fe46fb7399a3d1625505c8ad502d7e86e54c2f59a74b20b77077db8a31e60c70e294beeb54d51e8b808e005196c0b42f5e7e5cba0237574
|
|
7
|
+
data.tar.gz: b9a93c14eaeb496f1a46e78494b6129faeb8501333f678a85e8d1d07afb1a14465b32f7cc941eed294eae0bb7c319dcbbd5a0264305843394a4f1a1113a62530
|
data/CHANGELOG.md
CHANGED
|
@@ -9,15 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
- Tarball vault format ("Tarvault"): new vaults are portable tarballs with a
|
|
12
|
-
`manifest
|
|
13
|
-
on the read/write path (works on JRuby). The original SQLite
|
|
14
|
-
("Dbvault") is still read transparently.
|
|
12
|
+
plain-text `manifest` index and per-gem SHA256 integrity, with no sqlite3
|
|
13
|
+
dependency on the read/write path (works on JRuby). The original SQLite
|
|
14
|
+
format ("Dbvault") is still read transparently.
|
|
15
15
|
- Vaults carry an explicit on-disk **format version**, decoupled from the gem
|
|
16
16
|
version and validated on open; gemvault refuses a vault written by a newer
|
|
17
17
|
gemvault instead of misreading it.
|
|
18
18
|
- `gemvault upgrade` migrates a vault to the current format (e.g. SQLite → tar),
|
|
19
|
-
preserving every gem
|
|
20
|
-
`--
|
|
19
|
+
preserving every gem, writing a `.bak` backup by default, with `--dry-run` and
|
|
20
|
+
`--no-backup` flags. Timestamps survive a v1 upgrade; a v2 vault's are
|
|
21
|
+
restamped, its stored times living in an index this gemvault no longer reads
|
|
22
|
+
(see Changed). It is a no-op on an already-current vault.
|
|
21
23
|
|
|
22
24
|
- All CLI commands accept `vault://` and `file://` locators wherever they take
|
|
23
25
|
a vault path, e.g. `gemvault list vault:///path/to/myvault.gemv`; resolution
|
|
@@ -33,6 +35,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
35
|
plugin.)` — Bundler reinstalls the gem correctly but validates the ghost's
|
|
34
36
|
dead path — and nothing project-local can recover, because the wreck lives
|
|
35
37
|
in the ambient gem home (issue #23).
|
|
38
|
+
- `gemvault doctor` now works in a project whose Gemfile is inline
|
|
39
|
+
(`bundler/inline`). Such a script keeps its plugin index in
|
|
40
|
+
`<project>/.bundle/plugin`, a root bundler only consults mid-script, so
|
|
41
|
+
doctor's uninstall silently repaired nothing and the `bundle install` that
|
|
42
|
+
followed dumped bundler's entire usage screen. Doctor now reaches the
|
|
43
|
+
project's index the way `bundler/inline` does and, with no Gemfile to
|
|
44
|
+
reinstall from, says so and exits 0 — the inline gemfile reinstalls the
|
|
45
|
+
plugin the next time the script runs (issue #14). Run outside any project,
|
|
46
|
+
doctor no longer claims to have cleared a project index it never touched.
|
|
47
|
+
- `gemvault doctor` no longer strands a machine without the plugin. Its
|
|
48
|
+
closing `bundle install` does more than reinstall the plugin, and when that
|
|
49
|
+
extra work failed (a Ruby version pin, an unresolvable gem) the already-run
|
|
50
|
+
uninstall had cleared the plugin index for nothing — strictly worse than
|
|
51
|
+
the wreck doctor was asked to repair. The index is now snapshotted before
|
|
52
|
+
the uninstall and restored when the reinstall never happened, `bundle
|
|
53
|
+
install` runs as a child rather than replacing the process, and every
|
|
54
|
+
failure is one line on stderr and exit 1 instead of a backtrace
|
|
55
|
+
(issues #27, #24).
|
|
56
|
+
- The shim no longer spams `already initialized constant` warnings when
|
|
57
|
+
bundler evaluates two installed copies of it in one process — an upgrade
|
|
58
|
+
resolving a newer bundler-source-vault while the plugin index still names
|
|
59
|
+
the old one. The first copy wins, matching the guard plugins.rb already
|
|
60
|
+
applies to the vault source class (issue #26).
|
|
36
61
|
- `bundle plugin install bundler-source-vault` no longer dies with
|
|
37
62
|
`LoadError: cannot load such file -- bundler/plugin/vault_source`. The shim's
|
|
38
63
|
`plugins.rb` now derives the gem root from its own installed location (local
|
|
@@ -60,17 +85,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
60
85
|
evaluation, so `Gemvault::GemEntry` can no longer be defined twice from two
|
|
61
86
|
different gem roots (issue #13).
|
|
62
87
|
- `gemvault new` no longer raises `cannot load such file -- json` on a stock
|
|
63
|
-
distro ruby
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
runtime
|
|
88
|
+
distro ruby, where json is a default gem upstream but a separate package that
|
|
89
|
+
`dnf install ruby` leaves absent. json backed the tarball vault's manifest
|
|
90
|
+
when this was first fixed by declaring it a runtime dependency; the manifest
|
|
91
|
+
is now plain text and gemvault never loads json at runtime, so the failure is
|
|
92
|
+
retired outright rather than papered over (see Changed, issue #25).
|
|
67
93
|
- Reading a vault through the Bundler source no longer raises `cannot load such
|
|
68
94
|
file -- json` either. Loading gemvault off `$LOAD_PATH` skips activation, and
|
|
69
|
-
therefore skips its dependencies, so
|
|
70
|
-
|
|
71
|
-
dependencies the same way it resolves gemvault and puts their require
|
|
72
|
-
extension directories included — on `$LOAD_PATH
|
|
73
|
-
is skipped rather than fatal, which is what activation could not
|
|
95
|
+
therefore skips its dependencies, so a declared dependency (json, at the time)
|
|
96
|
+
never reached the plugin path. The shim now resolves gemvault's declared
|
|
97
|
+
runtime dependencies the same way it resolves gemvault and puts their require
|
|
98
|
+
paths — extension directories included — on `$LOAD_PATH`; a dependency it
|
|
99
|
+
cannot find is skipped rather than fatal, which is what activation could not
|
|
100
|
+
do. The manifest being plain text, nothing on this path requires json at all
|
|
101
|
+
(see Changed, issue #25).
|
|
74
102
|
- `bundle install` no longer fails with `Could not find 'command_kit' (~> 0.6)`
|
|
75
103
|
when gemvault is installed into the plugin root without its dependencies.
|
|
76
104
|
Loading the vault source no longer activates the gemvault gem, which would
|
|
@@ -78,6 +106,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
78
106
|
gemvault through `require_relative` alone (issue #13).
|
|
79
107
|
|
|
80
108
|
### Changed
|
|
109
|
+
- The manifest is now line-oriented text rather than JSON (vault format 3).
|
|
110
|
+
It was never document-shaped: a header plus one whitespace-separated line
|
|
111
|
+
per gem — name, version, platform, stored-at, SHA256, encrypted — with no
|
|
112
|
+
nesting and no free text, so `tar -xOf myvault.gemv manifest` gives you
|
|
113
|
+
something `grep` and `awk` read directly. Because every field comes from an
|
|
114
|
+
alphabet without whitespace, reading needs no parser beyond `split` plus
|
|
115
|
+
per-field validation: no recursion to exhaust the stack, no escape grammar,
|
|
116
|
+
and no library to load — which is what stops gemvault from ever activating
|
|
117
|
+
a json gem a project had locked (issue #25). Times are stored as
|
|
118
|
+
`2026-08-12T18:23:32Z`; a legacy vault's `2026-08-12 18:23:32` is converted
|
|
119
|
+
on upgrade. A format-2 vault (`manifest.json`) stays readable: it opens
|
|
120
|
+
read-only through the same path a legacy SQLite vault does, and
|
|
121
|
+
`gemvault upgrade` migrates it. That backend never parses the old manifest —
|
|
122
|
+
it derives the index from the stored gems, each of which carries its own
|
|
123
|
+
gemspec — so migrated gems keep their identity but are restamped, the old
|
|
124
|
+
index's times being unrecoverable.
|
|
81
125
|
- `sqlite3` is no longer a runtime dependency. Gemvault runs dependency-free on
|
|
82
126
|
the tarball path (including JRuby); `sqlite3` is loaded lazily only to read a
|
|
83
127
|
legacy SQLite vault, with a clear error if it is not installed.
|
|
@@ -89,10 +133,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
89
133
|
`spec:integration` rake task exists alongside a non-integration `spec:core`, the
|
|
90
134
|
unit job no longer needs podman, and the integration job installs podman
|
|
91
135
|
when the runner image lacks it.
|
|
92
|
-
- Reading a vault's
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
manifest fails fast rather than yielding a subtly broken vault.
|
|
136
|
+
- Reading a vault's manifest is now strict: every field is validated against
|
|
137
|
+
its own alphabet, so a truncated or hand-edited manifest fails fast rather
|
|
138
|
+
than yielding a subtly broken vault.
|
|
96
139
|
|
|
97
140
|
### Deprecated
|
|
98
141
|
- The SQLite vault format is deprecated and now **read-only**: existing SQLite
|
data/CLAUDE.md
CHANGED
|
@@ -27,7 +27,7 @@ Do NOT modify `.rubocop.yml` or use inline `# rubocop:disable` tags without expl
|
|
|
27
27
|
|
|
28
28
|
## Project Overview
|
|
29
29
|
|
|
30
|
-
Multi-gem portable archives. A single `.gemv` file is a tarball holding multiple `.gem` files plus a `manifest
|
|
30
|
+
Multi-gem portable archives. A single `.gemv` file is a tarball holding multiple `.gem` files plus a line-oriented `manifest` index; legacy SQLite vaults are read-only.
|
|
31
31
|
|
|
32
32
|
Two gems, one repo:
|
|
33
33
|
|
|
@@ -58,7 +58,10 @@ gem install --source file:///path/to/myvault.gemv foo
|
|
|
58
58
|
## Architecture
|
|
59
59
|
|
|
60
60
|
- `gemvault.gemspec` — main gem spec (name: `gemvault`)
|
|
61
|
-
- `lib/gemvault/vault.rb` — Vault facade choosing a backend by file format (Tarvault current
|
|
61
|
+
- `lib/gemvault/vault.rb` — Vault facade choosing a backend by file format (Tarvault current; Dbvault and LegacyTarvault read-only)
|
|
62
|
+
- `lib/gemvault/legacy_tarvault.rb` — read-only backend for a format-2 (manifest.json) vault; derives its index from the stored gems instead of parsing that manifest, so `gemvault upgrade` migrates one through the ordinary pipeline
|
|
63
|
+
- `lib/gemvault/manifest_text.rb` — the manifest's on-disk notation: a header plus one whitespace-separated line per gem, validated field by field on read
|
|
64
|
+
- `lib/gemvault/timestamp.rb` — the space-free notation a vault records times in, and conversion of a legacy vault's times into it
|
|
62
65
|
- `lib/gemvault/cli.rb` — CLI dispatcher (new/add/list/remove/extract)
|
|
63
66
|
- `lib/gemvault/ghost_specification.rb` — installation records whose gem directory is gone; swept by `gemvault doctor` (issue #23)
|
|
64
67
|
- `lib/bundler/plugin/vault_source.rb` — Bundler `Plugin::API::Source` implementation
|
|
@@ -165,6 +168,6 @@ container.
|
|
|
165
168
|
|
|
166
169
|
- `bundler` — NOT a dependency; the plugin always runs inside an existing Bundler process, and declaring it breaks gem activation under `bundle exec`'s restricted GEM_PATH
|
|
167
170
|
- `command_kit` (~> 0.6) — runtime (CLI)
|
|
168
|
-
- `json` — NOT a dependency
|
|
171
|
+
- `json` — NOT a dependency and never loaded at runtime. The manifest is a table, not a document, so `Gemvault::ManifestText` reads and writes it directly; nothing in `lib/` may `require "json"`. On rubies where require resolves through gem activation, a require inside a Bundler process activates the newest installed json and a project locking an older one dies in `check_for_activated_spec!` (issue #25).
|
|
169
172
|
- `sqlite3` (~> 2.0) — NOT a runtime dependency; loaded lazily only to read a legacy SQLite (Dbvault) vault. Declared in the Gemfile for development/test.
|
|
170
173
|
- `minitest`, `rspec`, `rake` — development
|
data/README.md
CHANGED
|
@@ -48,23 +48,33 @@ gemvault upgrade myvault.gemv # migrate to the current format
|
|
|
48
48
|
|
|
49
49
|
## Vault format versioning
|
|
50
50
|
|
|
51
|
-
Every vault records an on-disk **format version** — `1` for the original SQLite format, `2` for the current tarball
|
|
51
|
+
Every vault records an on-disk **format version** — `1` for the original SQLite format, `2` for a tarball with a JSON manifest, `3` for the current tarball with a plain-text manifest. This version lives inside the file and is **independent of the gemvault gem version**: it changes only when the storage layout changes, so upgrading the gem never invalidates your vaults.
|
|
52
52
|
|
|
53
53
|
gemvault reads any format up to the one it understands and **refuses a vault written by a newer gemvault** with a clear message (rather than silently misreading it).
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Formats `1` (SQLite) and `2` (JSON manifest) are **deprecated and read-only**: you can still list, extract from, and migrate an existing one, but `add`/`remove` are refused and print `gemvault upgrade`. Opening one shows a one-time deprecation notice (silence it with `GEMVAULT_SILENCE_DEPRECATIONS=1`). SQLite support will be removed in a future release (0.3–0.5). To migrate a vault to the current format:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
gemvault upgrade myvault.gemv # e.g. SQLite (v1)
|
|
58
|
+
gemvault upgrade myvault.gemv # e.g. SQLite (v1) or JSON manifest (v2) -> current (v3)
|
|
59
59
|
gemvault upgrade myvault.gemv --dry-run # show the plan, change nothing
|
|
60
60
|
gemvault upgrade myvault.gemv --no-backup # skip the default myvault.gemv.bak copy
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
`upgrade` preserves every gem
|
|
63
|
+
`upgrade` preserves every gem, writes `myvault.gemv.bak` by default, and is a no-op on an already-current vault. Timestamps survive a v1 upgrade; a v2 vault's are restamped, because its stored times live in the JSON index this gemvault deliberately no longer reads — the gems themselves are read straight out of the tarball.
|
|
64
64
|
|
|
65
65
|
## How It Works
|
|
66
66
|
|
|
67
|
-
The gemv file is a tarball containing your .gem files and a
|
|
67
|
+
The gemv file is a tarball containing your .gem files and a plain-text manifest. It has no dependencies other than the tar utilities that rubygems provides.
|
|
68
|
+
|
|
69
|
+
```console
|
|
70
|
+
$ tar -xOf myvault.gemv manifest
|
|
71
|
+
gemvault 3
|
|
72
|
+
created 2026-08-12T18:23:32Z
|
|
73
|
+
|
|
74
|
+
rails 7.1.3 ruby 2026-08-12T18:23:32Z 9f2c... 0
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
One header, then one line per gem: name, version, platform, when it was stored, its SHA256, and whether it is encrypted. Every field is drawn from an alphabet without whitespace, so reading the index needs no parser beyond `split` — and `grep`, `awk` and `cut` work on it directly.
|
|
68
78
|
|
|
69
79
|
When Bundler sees `type: :vault` in your Gemfile, it auto-installs the `bundler-source-vault` plugin from rubygems.org. The plugin implements the `Bundler::Plugin::API::Source` interface — it reads gemspecs from the vault, participates in dependency resolution, then extracts and installs gems from the vault's blob storage.
|
|
70
80
|
|
|
@@ -98,7 +108,11 @@ To recover, update the Gemfile to point at the new path and run:
|
|
|
98
108
|
gemvault doctor
|
|
99
109
|
```
|
|
100
110
|
|
|
101
|
-
`doctor` removes any ghost installation records, clears the broken entry from bundler's plugin index (`bundle plugin uninstall bundler-source-vault`) and then re-runs `bundle install`, which reinstalls the plugin against whatever the current Gemfile declares. Run it from your project directory. If a ghost record sits in a root-owned gem home, doctor says so on one line — re-run it with permissions for that gem home (e.g. `sudo gemvault doctor`).
|
|
111
|
+
`doctor` removes any ghost installation records, clears the broken entry from bundler's plugin index (`bundle plugin uninstall bundler-source-vault`) and then, when a Gemfile exists, re-runs `bundle install`, which reinstalls the plugin against whatever the current Gemfile declares. Run it from your project directory. If a ghost record sits in a root-owned gem home, doctor says so on one line — re-run it with permissions for that gem home (e.g. `sudo gemvault doctor`).
|
|
112
|
+
|
|
113
|
+
A project whose Gemfile is inline (`require "bundler/inline"`) works too. Such a script keeps its plugin index in `<project>/.bundle/plugin`, a root bundler only consults while the script runs; doctor reaches it the same way the script did. With no Gemfile on disk there is nothing to reinstall from, so doctor clears the entry, says so, and exits 0 — the inline gemfile reinstalls the plugin the next time the script runs. Run outside any project, doctor works against bundler's global index and points you back at the project directory for the reinstall.
|
|
114
|
+
|
|
115
|
+
The repair is transactional. `bundle install` does more than reinstall the plugin, and that extra work can fail on its own — a Ruby version pin, an unresolvable gem. When it does, doctor says so in one line and exits 1; and if the failure struck before the plugin was reinstalled, doctor puts the plugin index back the way it found it. A failed run never leaves the machine with less than it started with — fix the install error and re-run.
|
|
102
116
|
|
|
103
117
|
The published `bundler-source-vault` gem installed from rubygems.org is immune to this: it lives in a bundler-managed directory that does not move.
|
|
104
118
|
|
data/issues.rec
CHANGED
|
@@ -750,7 +750,42 @@ Id: 14
|
|
|
750
750
|
Updated: Wed, 29 Jul 2026 16:41:11 -0400
|
|
751
751
|
Name: gemvault doctor fails when bundler/inline is used
|
|
752
752
|
Description: Loudly fails with 2 errors, one of which is "can't find gemfile"
|
|
753
|
-
|
|
753
|
+
+
|
|
754
|
+
+ RESOLUTION: both errors reproduced in podman, and doctor turned out to be
|
|
755
|
+
+ broken twice over rather than once.
|
|
756
|
+
+
|
|
757
|
+
+ (1) "Plugin bundler-source-vault is not installed". bundler/inline sets
|
|
758
|
+
+ BUNDLE_GEMFILE to a bare "Gemfile" for the life of the script (bundler
|
|
759
|
+
+ inline.rb:53), so Bundler.root is the script's working directory and the plugin
|
|
760
|
+
+ index is written to <project>/.bundle/plugin. A later `gemvault doctor` sets no
|
|
761
|
+
+ such variable and finds no Gemfile on disk, so Bundler::Plugin.root --
|
|
762
|
+
+ local_root only when SharedHelpers.in_bundle?, global_root otherwise
|
|
763
|
+
+ (plugin.rb:162) -- falls back to ~/.bundle/plugin. The uninstall therefore
|
|
764
|
+
+ looked in the wrong root and repaired nothing, silently.
|
|
765
|
+
+
|
|
766
|
+
+ (2) "Could not locate Gemfile". doctor then exec'd `bundle install`, which with
|
|
767
|
+
+ no Gemfile prints its entire usage screen and exits 10 -- reading as gemvault
|
|
768
|
+
+ itself failing, after the repair it was asked for had already not happened.
|
|
769
|
+
+
|
|
770
|
+
+ doctor now runs the uninstall with BUNDLE_GEMFILE="Gemfile" when the project
|
|
771
|
+
+ owns a plugin root Bundler would ignore, which is bundler/inline's own trick and
|
|
772
|
+
+ needs no file to exist; verified against the real thing (plain uninstall says
|
|
773
|
+
+ "not installed", the same command with BUNDLE_GEMFILE set says "Uninstalled
|
|
774
|
+
+ plugin bundler-source-vault" and empties the index). When no Gemfile exists it
|
|
775
|
+
+ skips the reinstall and says so, exiting 0, because clearing the entry is the
|
|
776
|
+
+ repair and only the reinstall is unavailable -- an inline gemfile reinstalls the
|
|
777
|
+
+ plugin the next time the script runs.
|
|
778
|
+
+
|
|
779
|
+
+ Gemvault::BundlerGemfile mirrors SharedHelpers#find_gemfile and
|
|
780
|
+
+ Gemvault::BundlerPluginRoot mirrors Plugin.root, both reimplemented rather than
|
|
781
|
+
+ delegated because bundler is deliberately not a dependency and doctor runs
|
|
782
|
+
+ outside any Bundler process. Replicated first by
|
|
783
|
+
+ spec/integration/cli/commands/doctor_inline_spec.rb -- all 8 examples failed
|
|
784
|
+
+ against the old code -- with unit cover in spec/gemvault/bundler_gemfile_spec.rb,
|
|
785
|
+
+ spec/gemvault/bundler_plugin_root_spec.rb and the extended doctor_spec.rb. The
|
|
786
|
+
+ inline support now runs its script from the project directory, as a real user
|
|
787
|
+
+ does; that is what puts the plugin root where the report describes.
|
|
788
|
+
Status: closed
|
|
754
789
|
|
|
755
790
|
Id: 15
|
|
756
791
|
Updated: Wed, 29 Jul 2026 17:39:23 -0400
|
|
@@ -783,7 +818,11 @@ Id: 16
|
|
|
783
818
|
Updated: Wed, 29 Jul 2026 18:25:48 -0400
|
|
784
819
|
Name: Add jruby-ci workflow
|
|
785
820
|
Description: It should be already set up on the jruby-support branch. It just needs to be picked from it. DO NOT use the other code in that - it is very outdated
|
|
786
|
-
|
|
821
|
+
+
|
|
822
|
+
+ RESOLUTION: shipped in commit 635ba41 ("ci: add JRuby test job") -- the jruby
|
|
823
|
+
+ job in .github/workflows/ci.yml runs the minitest suite on jruby-10.1.0.0 and
|
|
824
|
+
+ is green on CI.
|
|
825
|
+
Status: closed
|
|
787
826
|
|
|
788
827
|
Id: 17
|
|
789
828
|
Updated: Thu, 30 Jul 2026 00:07:42 +0000
|
|
@@ -972,7 +1011,15 @@ Description: [dev@rstore]/workspace% bundle install
|
|
|
972
1011
|
+ from /usr/local/bin/bundle:25:in '<main>'
|
|
973
1012
|
+
|
|
974
1013
|
+ Though I will add that gemvault doctor followed by bundle install fixed the issue
|
|
975
|
-
|
|
1014
|
+
+
|
|
1015
|
+
+ RESOLUTION: the broken-plugin-path wreck, recovered exactly as reported --
|
|
1016
|
+
+ gemvault doctor then bundle install. The plugin index entry pointed into a
|
|
1017
|
+
+ reinstalled rbenv ruby's gem home; bundler does not revalidate stored plugin
|
|
1018
|
+
+ paths, skips loading the plugin, and crashes in SourceList#add_plugin_source,
|
|
1019
|
+
+ upstream behavior README documents under "A broken plugin path". doctor is
|
|
1020
|
+
+ the repair. Hinting at doctor from the failure site was considered and
|
|
1021
|
+
+ declined: the crash happens in bundler frames before any gemvault code loads.
|
|
1022
|
+
Status: closed
|
|
976
1023
|
|
|
977
1024
|
Id: 21
|
|
978
1025
|
Updated: Fri, 31 Jul 2026 13:54:09 -0400
|
|
@@ -1011,12 +1058,27 @@ Description: bundler-cache: true on the ruby/setup-ruby action will not work cor
|
|
|
1011
1058
|
+ run: | # confine gems to vendor/bundle so the cache never shadows vendor/vendored.gemv
|
|
1012
1059
|
+ bundle config set --local path vendor/bundle
|
|
1013
1060
|
+ bundle install --jobs 4 --retry 3
|
|
1061
|
+
Status: open
|
|
1062
|
+
|
|
1063
|
+
Id: 26
|
|
1014
1064
|
Updated: Mon, 10 Aug 2026 10:14:51 -0400
|
|
1015
1065
|
Name: Gemvault spams warnings on bundle install
|
|
1016
1066
|
Description: /workspace/.bundle/plugin/gems/bundler-source-vault-0.2.2/gemvault_load_path.rb:31: warning: already initialized constant BundlerSourceVault::GemvaultLoadPath::GEM
|
|
1017
1067
|
+ /workspace/.bundle/plugin/gems/bundler-source-vault-0.2.2/gemvault_load_path.rb:32: warning: already initialized constant BundlerSourceVault::GemvaultLoadPath::SHIM
|
|
1018
1068
|
+ /workspace/.bundle/plugin/gems/bundler-source-vault-0.2.2/gemvault_load_path.rb:33: warning: already initialized constant BundlerSourceVault::GemvaultLoadPath::VAULT_SOURCE
|
|
1019
|
-
|
|
1069
|
+
+
|
|
1070
|
+
+ RESOLUTION: bundler can evaluate two installed copies of the shim in one
|
|
1071
|
+
+ process -- an upgrade resolving a newer bundler-source-vault while the index
|
|
1072
|
+
+ still names the old one, the dual-version plugin root the failure report
|
|
1073
|
+
+ also recorded as issue #29 -- and require_relative cannot deduplicate
|
|
1074
|
+
+ across paths, so the second copy reopened GemvaultLoadPath and warned on
|
|
1075
|
+
+ every constant. gemvault_load_path.rb now returns before redefining
|
|
1076
|
+
+ anything when the module is already loaded, first copy winning as
|
|
1077
|
+
+ plugins.rb already arranges for Bundler::Plugin::VaultSource. Replicated by
|
|
1078
|
+
+ spec/shim_reload_spec.rb (two copies loaded in one process, red against the
|
|
1079
|
+
+ old shim), with the plain-install path guarded in
|
|
1080
|
+
+ spec/integration/bundle_install_spec.rb.
|
|
1081
|
+
Status: closed
|
|
1020
1082
|
|
|
1021
1083
|
Id: 23
|
|
1022
1084
|
Updated: Mon, 10 Aug 2026 17:40:28 -0400
|
|
@@ -1086,4 +1148,133 @@ Id: 24
|
|
|
1086
1148
|
Updated: Mon, 10 Aug 2026 23:16:35 +0000
|
|
1087
1149
|
Name: doctor prints a backtrace when the plugin uninstall step fails
|
|
1088
1150
|
Description: gemvault doctor's `bundle plugin uninstall` step runs with exception: true; a nonzero exit raises RuntimeError and a missing bundle raises Errno::ENOENT, and both escape Command#run to command_kit's ExceptionHandler, which prints error.full_message -- a full backtrace -- violating the one-line-and-exit-1 convention issue #15 established. Adjacent to issue #14 (doctor failing under bundler/inline). The ghost-specification sweep added for issue #23 reports its own failures as one line; the uninstall step should match.
|
|
1151
|
+
+
|
|
1152
|
+
+ RESOLUTION: fixed alongside issue #27's transactional rework. The uninstall
|
|
1153
|
+
+ step no longer runs with exception: true; a failing or missing bundle is one
|
|
1154
|
+
+ line on stderr ("doctor: bundle plugin uninstall bundler-source-vault
|
|
1155
|
+
+ failed") and exit 1, matching the convention issues #15 and #23 established,
|
|
1156
|
+
+ and the snapshotted index is left untouched.
|
|
1157
|
+
Status: closed
|
|
1158
|
+
|
|
1159
|
+
Id: 25
|
|
1160
|
+
Updated: Tue, 11 Aug 2026 20:05:27 -0400
|
|
1161
|
+
Name: Project with Gemfile + locked json version + gemvault throws error
|
|
1162
|
+
Description: Project with Gemfile + locked json version + gemvault throws "bundler: failed to load command: rails (/private/tmp/local/flipmine/flipmine/vendor/ruby/4.0.0/bin/rails)
|
|
1163
|
+
+ /Users/davidgillis/.rbenv/versions/4.0.1/lib/ruby/gems/4.0.0/gems/bundler-4.0.18/lib/bundler/runtime.rb:328:in 'Bundler::Runtime#check_for_activated_spec!': You have already activated json 2.21.2, but your Gemfile requires json 2.19.7. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)"
|
|
1164
|
+
+
|
|
1165
|
+
+ I believe this error to be coming from gemvault's dependency on JSON. It is clear that dependencies are cursed in this project, whether its the added load time for reinstalling them on every `bundle install`, or errors like this one. Rather than rely on JSON, I think it would be better to just marshal our own manifest, or use some other serialization tool if any that rubygems already provides.
|
|
1166
|
+
+
|
|
1167
|
+
+ RESOLUTION: gemvault no longer loads json, and the manifest is no longer a
|
|
1168
|
+
+ JSON document. Whether gemvault's require was the activator on the reported
|
|
1169
|
+
+ machine cannot be settled from here -- on the Fedora reproduction stack
|
|
1170
|
+
+ require resolves json from the stdlib path with no activation at all, and
|
|
1171
|
+
+ bundler loads json itself before the vault source runs -- but on rbenv-style
|
|
1172
|
+
+ stacks require does activate, gemvault held a live require in its
|
|
1173
|
+
+ vault-read path, and it can no longer be the component that detonates
|
|
1174
|
+
+ check_for_activated_spec!.
|
|
1175
|
+
+
|
|
1176
|
+
+ Marshal was considered as the reporter suggested and rejected: a .gemv is a
|
|
1177
|
+
+ file that arrives from elsewhere, the manifest is the root of trust (the
|
|
1178
|
+
+ SHA256 digests live inside it, so nothing verifies it first), and
|
|
1179
|
+
+ Marshal.load runs attacker-chosen code during load itself -- demonstrated
|
|
1180
|
+
+ here, a hash key's #hash fires inside load before the caller touches the
|
|
1181
|
+
+ result. `gemvault list` on an untrusted vault would become arbitrary code
|
|
1182
|
+
+ execution, and Marshal's versioned wire format would break the portability
|
|
1183
|
+
+ a .gemv exists for.
|
|
1184
|
+
+
|
|
1185
|
+
+ What replaced JSON is neither: the manifest was never document-shaped. It is
|
|
1186
|
+
+ a table -- a header plus fixed-arity records of scalars, no nesting, no free
|
|
1187
|
+
+ text -- and every field is drawn from an alphabet without whitespace once
|
|
1188
|
+
+ times are canonicalized (Gemvault::Timestamp; rubygems itself validates gem
|
|
1189
|
+
+ names against /\A[a-zA-Z0-9._-]+\z/). Gemvault::ManifestText writes and
|
|
1190
|
+
+ reads it as lines. There is no recursion to exhaust the stack, no escape
|
|
1191
|
+
+ grammar, no backtracking, and no library to load -- the same reasoning that
|
|
1192
|
+
+ keeps BundlerPluginIndex from requiring yaml. Reading validates every field
|
|
1193
|
+
+ and rejects everything else, because the file comes from elsewhere; writing
|
|
1194
|
+
+ trusts the value objects this library constructed. Precedent is rubygems'
|
|
1195
|
+
+ own compact index and Gemfile.lock, both line-oriented text.
|
|
1196
|
+
+
|
|
1197
|
+
+ Format version 3; vaults are inspectable with `tar -xOf v.gemv manifest`.
|
|
1198
|
+
+
|
|
1199
|
+
+ An existing format-2 vault keeps working. Gemvault::LegacyTarvault opens one
|
|
1200
|
+
+ read-only, exactly as Dbvault does for SQLite, so `gemvault upgrade` migrates
|
|
1201
|
+
+ it through the ordinary pipeline rather than leaving its gems stranded behind
|
|
1202
|
+
+ an error. That backend never parses manifest.json: it derives the index from
|
|
1203
|
+
+ the payload, since every stored .gem carries the gemspec the manifest's
|
|
1204
|
+
+ identity fields came from -- which keeps the old notation from re-entering
|
|
1205
|
+
+ the codebase and makes the reader indifferent to a damaged manifest. Two
|
|
1206
|
+
+ things the old index held are lost with it: each gem's stored time (entries
|
|
1207
|
+
+ are restamped on open, and the deprecation notice says so) and the per-gem
|
|
1208
|
+
+ digests (the new vault records fresh digests of exactly the bytes it copied).
|
|
1209
|
+
+ Covered end to end by spec/integration/cli/commands/legacy_tarvault_spec.rb
|
|
1210
|
+
+ against a committed format-2 fixture: list, extract, refused add, upgrade,
|
|
1211
|
+
+ and writing to the upgraded vault.
|
|
1212
|
+
+
|
|
1213
|
+
+ Settled after all: the Fedora image reproduces the rbenv-style stack once
|
|
1214
|
+
+ the unbundled stdlib json is cleared away -- require then resolves through
|
|
1215
|
+
+ gem activation, exactly the reported semantics.
|
|
1216
|
+
+ spec/integration/locked_json_spec.rb runs the reporter's flow (a project
|
|
1217
|
+
+ locking an older json than the machine's newest, gemvault ambient, bundle
|
|
1218
|
+
+ exec through the vault source) and fails against a gemvault that requires
|
|
1219
|
+
+ json on either the CLI path or the plugin path, the latter dying with the
|
|
1220
|
+
+ reported check_for_activated_spec! error verbatim.
|
|
1221
|
+
Status: closed
|
|
1222
|
+
|
|
1223
|
+
Id: 27
|
|
1224
|
+
Updated: Wed, 12 Aug 2026 16:05:55 +0000
|
|
1225
|
+
Name: gemvault doctor can leave the machine with no plugin at all
|
|
1226
|
+
Description: Reported from a container where bundle install could not succeed
|
|
1227
|
+
+ (ruby version pin mismatch): doctor removed ghost records, uninstalled the
|
|
1228
|
+
+ plugin, emptied .bundle/plugin/index, then exec'd bundle install -- which
|
|
1229
|
+
+ failed, leaving no plugin installed. Strictly worse than the broken-path
|
|
1230
|
+
+ state doctor was invoked to repair, and recoverable only by knowing to run
|
|
1231
|
+
+ `bundle plugin install bundler-source-vault` by hand. doctor repairs by
|
|
1232
|
+
+ mutation with no dry-run, no report of findings before acting, and no
|
|
1233
|
+
+ rollback when the reinstall it hands off to fails. Suggested in the report:
|
|
1234
|
+
+ report-then-fix (--dry-run, or report by default and repair under --fix);
|
|
1235
|
+
+ reinstall the plugin directly with `bundle plugin install` and verify the
|
|
1236
|
+
+ index rather than exec'ing into `bundle install`, so a failed dependency
|
|
1237
|
+
+ install cannot take the plugin down with it; or restore the previous index
|
|
1238
|
+
+ when the step after the uninstall fails.
|
|
1239
|
+
+
|
|
1240
|
+
+ RESOLUTION: the uninstall-reinstall pair is now transactional. doctor
|
|
1241
|
+
+ snapshots the plugin index bundler will consult (Gemvault::BundlerPluginIndex
|
|
1242
|
+
+ over BundlerPluginRoot#consulted) before clearing it, runs `bundle install`
|
|
1243
|
+
+ as a child instead of exec'ing into it, and on failure checks whether the
|
|
1244
|
+
+ plugin came back: reinstalled means the repair stands and the one-line error
|
|
1245
|
+
+ says only the bundle is unfinished; not reinstalled means the snapshot is
|
|
1246
|
+
+ restored, so the machine is left as found and re-running doctor once the
|
|
1247
|
+
+ install error is fixed recovers fully. Replicated first by
|
|
1248
|
+
+ spec/integration/cli/commands/doctor_reinstall_failure_spec.rb -- doctor run
|
|
1249
|
+
+ against a Gemfile still naming a renamed plugin path, an un-installable
|
|
1250
|
+
+ bundle in the reporter's shape -- which fails against the old code and
|
|
1251
|
+
+ proves the restored index heals on the next run. Unit cover in
|
|
1252
|
+
+ spec/gemvault/bundler_plugin_index_spec.rb and the reworked doctor_spec.rb.
|
|
1253
|
+
Status: closed
|
|
1254
|
+
|
|
1255
|
+
Id: 28
|
|
1256
|
+
Updated: Wed, 12 Aug 2026 16:05:55 +0000
|
|
1257
|
+
Name: plugins.rb dies with a bare LoadError when gemvault is installed nowhere
|
|
1258
|
+
Description: A plugin root holding only the shim -- the normal state wherever
|
|
1259
|
+
+ `gem install gemvault` has run, since Bundler skips installing a plugin
|
|
1260
|
+
+ dependency already present -- stops loading when the tree lands on a machine
|
|
1261
|
+
+ with no gemvault in any gem root: GemvaultLoadPath.entries returns [] and
|
|
1262
|
+
+ `require "bundler/plugin/vault_source"` raises a bare LoadError with no hint.
|
|
1263
|
+
+ Observed after a macOS working tree was copied into a Linux container (the
|
|
1264
|
+
+ shim alone in .bundle/plugin, gemvault absent from every gem root on the
|
|
1265
|
+
+ box); also reachable by `gem uninstall gemvault` on a single machine. The
|
|
1266
|
+
+ ambient-root search cannot cover gemvault being truly absent. Suggested:
|
|
1267
|
+
+ fail with an explicit "gemvault not found in any gem root" message naming
|
|
1268
|
+
+ the remedy (gem install gemvault, or bundle plugin uninstall + install to
|
|
1269
|
+
+ get a self-contained root), or vendor gemvault into the shim.
|
|
1270
|
+
Status: open
|
|
1271
|
+
|
|
1272
|
+
Id: 29
|
|
1273
|
+
Updated: Wed, 12 Aug 2026 16:05:55 +0000
|
|
1274
|
+
Name: Stale plugin versions are never reaped from the plugin root
|
|
1275
|
+
Description: bundler-source-vault-0.2.4 survived in the plugin root's gems/,
|
|
1276
|
+
+ specifications/ and cache/ across the 0.2.5 upgrade and an explicit `bundle
|
|
1277
|
+
+ plugin uninstall`; only 0.2.5 was ever referenced by the index. Possibly
|
|
1278
|
+
+ Bundler's doing rather than the plugin's, but doctor is well placed to sweep
|
|
1279
|
+
+ plugin-root versions the index no longer references.
|
|
1089
1280
|
Status: open
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module Gemvault
|
|
4
|
+
##
|
|
5
|
+
# The Gemfile Bundler would load from a directory.
|
|
6
|
+
#
|
|
7
|
+
# Mirrors Bundler::SharedHelpers#find_gemfile: <tt>BUNDLE_GEMFILE</tt> wins
|
|
8
|
+
# when set and non-empty, otherwise the search walks up looking for
|
|
9
|
+
# <tt>gems.rb</tt> then <tt>Gemfile</tt> in each directory. Reimplemented
|
|
10
|
+
# rather than delegated because bundler is deliberately not a gemvault
|
|
11
|
+
# dependency (see gemvault.gemspec) and +gemvault doctor+ runs as a plain
|
|
12
|
+
# CLI, outside any Bundler process.
|
|
13
|
+
#
|
|
14
|
+
# One difference is deliberate. Bundler returns <tt>BUNDLE_GEMFILE</tt>
|
|
15
|
+
# whatever it points at, existing or not -- bundler/inline relies on that,
|
|
16
|
+
# setting it to a bare "Gemfile" purely to make Bundler treat the working
|
|
17
|
+
# directory as the app root. The question here is whether +bundle install+
|
|
18
|
+
# could work, so a value that does not name a file is ignored and the
|
|
19
|
+
# walk-up search decides instead.
|
|
20
|
+
class BundlerGemfile
|
|
21
|
+
# Checked in this order within each directory, as Bundler checks them.
|
|
22
|
+
NAMES = ["gems.rb", "Gemfile"].freeze
|
|
23
|
+
|
|
24
|
+
def initialize(dir: Dir.pwd, env: ENV)
|
|
25
|
+
@dir = Pathname(dir)
|
|
26
|
+
@env = env
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# :call-seq:
|
|
30
|
+
# path -> Pathname or nil
|
|
31
|
+
#
|
|
32
|
+
# The Gemfile Bundler would load, or +nil+ when there is none.
|
|
33
|
+
def path
|
|
34
|
+
return @path if defined?(@path)
|
|
35
|
+
|
|
36
|
+
@path = from_env || search_up
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Whether Bundler would find a Gemfile at all.
|
|
40
|
+
def exist?
|
|
41
|
+
!path.nil?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def from_env
|
|
47
|
+
given = @env["BUNDLE_GEMFILE"].to_s
|
|
48
|
+
return nil if given.empty?
|
|
49
|
+
|
|
50
|
+
candidate = Pathname(given).expand_path(@dir)
|
|
51
|
+
candidate.file? ? candidate : nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def search_up
|
|
55
|
+
@dir.expand_path.ascend do |dir|
|
|
56
|
+
found = NAMES.map { |name| dir / name }.find(&:file?)
|
|
57
|
+
return found if found
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module Gemvault
|
|
4
|
+
##
|
|
5
|
+
# Bundler's plugin index file inside a plugin root.
|
|
6
|
+
#
|
|
7
|
+
# The uninstall step of +gemvault doctor+ clears entries from this file, and
|
|
8
|
+
# the reinstall that follows can fail for reasons unrelated to the plugin.
|
|
9
|
+
# Snapshot and restore make that pair transactional: a repair that cannot
|
|
10
|
+
# finish puts the index back instead of leaving the machine with no plugin
|
|
11
|
+
# registered at all (issue #27).
|
|
12
|
+
#
|
|
13
|
+
# The plugin_paths section is scanned textually rather than parsed as YAML:
|
|
14
|
+
# loading a YAML library into doctor's process invites the same
|
|
15
|
+
# stdlib-activation conflicts as issue #25, and bundler's own emitter writes
|
|
16
|
+
# one two-space-indented "name: path" line per plugin.
|
|
17
|
+
class BundlerPluginIndex
|
|
18
|
+
def initialize(root)
|
|
19
|
+
@root = Pathname(root)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def file
|
|
23
|
+
@root / "index"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Whether plugin_paths currently lists +plugin+.
|
|
27
|
+
def registered?(plugin)
|
|
28
|
+
plugin_paths.any? { |line| line.match?(/\A\s{2}#{Regexp.escape(plugin)}:/) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# :call-seq:
|
|
32
|
+
# snapshot -> String or nil
|
|
33
|
+
#
|
|
34
|
+
# The index content as it stands, +nil+ when no index exists.
|
|
35
|
+
def snapshot
|
|
36
|
+
file.file? ? file.read : nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Puts the index back the way +snapshot+ recorded it; restoring +nil+
|
|
40
|
+
# removes an index that did not exist at snapshot time.
|
|
41
|
+
def restore(snapshot)
|
|
42
|
+
return file.write(snapshot) if snapshot
|
|
43
|
+
|
|
44
|
+
file.delete if file.exist?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def plugin_paths
|
|
50
|
+
return [] unless file.file?
|
|
51
|
+
|
|
52
|
+
file.readlines
|
|
53
|
+
.drop_while { |line| !line.start_with?("plugin_paths:") }
|
|
54
|
+
.drop(1)
|
|
55
|
+
.take_while { |line| line.start_with?(" ") }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
require_relative "bundler_gemfile"
|
|
3
|
+
|
|
4
|
+
module Gemvault
|
|
5
|
+
##
|
|
6
|
+
# Where Bundler keeps plugin data for a directory.
|
|
7
|
+
#
|
|
8
|
+
# Bundler::Plugin.root answers the project-local <tt>.bundle/plugin</tt> when
|
|
9
|
+
# <tt>SharedHelpers.in_bundle?</tt> holds, and the global
|
|
10
|
+
# <tt>~/.bundle/plugin</tt> when it does not. Since +in_bundle?+ is just
|
|
11
|
+
# "was a Gemfile found", a project can own a plugin root that Bundler
|
|
12
|
+
# declines to look in.
|
|
13
|
+
class BundlerPluginRoot
|
|
14
|
+
LOCAL_DIR = ".bundle/plugin".freeze
|
|
15
|
+
|
|
16
|
+
def initialize(dir: Dir.pwd, gemfile: BundlerGemfile.new(dir: dir), env: ENV)
|
|
17
|
+
@dir = Pathname(dir)
|
|
18
|
+
@gemfile = gemfile
|
|
19
|
+
@env = env
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The project's own plugin root, whether or not Bundler would consult it.
|
|
23
|
+
def local
|
|
24
|
+
@dir.expand_path / LOCAL_DIR
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Where Bundler keeps plugins for a user outside any project, mirroring
|
|
28
|
+
# Bundler's user_bundle_path lookup for plugins: <tt>BUNDLE_USER_PLUGIN</tt>
|
|
29
|
+
# names the root directly, <tt>BUNDLE_USER_HOME</tt> relocates
|
|
30
|
+
# <tt>.bundle</tt>, and the home directory is the default.
|
|
31
|
+
def global
|
|
32
|
+
named = @env["BUNDLE_USER_PLUGIN"]
|
|
33
|
+
return Pathname(named) if named
|
|
34
|
+
|
|
35
|
+
Pathname(@env["BUNDLE_USER_HOME"] || File.join(Dir.home, ".bundle")) / "plugin"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The plugin root Bundler will consult here: beside the Gemfile when one
|
|
39
|
+
# was found, the project's own root when doctor points Bundler at it (see
|
|
40
|
+
# #unreachable?), the global root otherwise.
|
|
41
|
+
def consulted
|
|
42
|
+
return @gemfile.path.dirname / LOCAL_DIR if @gemfile.exist?
|
|
43
|
+
return local if unreachable?
|
|
44
|
+
|
|
45
|
+
global
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Whether this project has a plugin root that Bundler currently ignores.
|
|
49
|
+
#
|
|
50
|
+
# bundler/inline installs plugins into <tt><cwd>/.bundle/plugin</tt>, having
|
|
51
|
+
# set <tt>BUNDLE_GEMFILE</tt> to a bare "Gemfile" for the duration of the
|
|
52
|
+
# script. A later command in that same directory sets no such variable and
|
|
53
|
+
# finds no Gemfile on disk, so Bundler falls back to the global root and
|
|
54
|
+
# reports the plugin as not installed -- while the entry sits here.
|
|
55
|
+
def unreachable?
|
|
56
|
+
!@gemfile.exist? && local.directory?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|