gemkeeper 0.7.2 → 0.8.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -1
  3. data/README.md +11 -11
  4. data/lib/gemkeeper/bundler_mirror_configurator.rb +1 -1
  5. data/lib/gemkeeper/cli/commands/list.rb +2 -2
  6. data/lib/gemkeeper/cli/commands/server/start.rb +4 -4
  7. data/lib/gemkeeper/cli/commands/server/status.rb +3 -3
  8. data/lib/gemkeeper/cli/commands/server/stop.rb +3 -3
  9. data/lib/gemkeeper/cli/commands/sync.rb +1 -1
  10. data/lib/gemkeeper/compact_index_server/cache_meta.rb +34 -0
  11. data/lib/gemkeeper/compact_index_server/cache_store.rb +64 -0
  12. data/lib/gemkeeper/compact_index_server/gem_cache.rb +88 -0
  13. data/lib/gemkeeper/compact_index_server/gem_index.rb +78 -0
  14. data/lib/gemkeeper/compact_index_server/index_merger.rb +81 -0
  15. data/lib/gemkeeper/compact_index_server/response.rb +12 -0
  16. data/lib/gemkeeper/compact_index_server/response_builder.rb +63 -0
  17. data/lib/gemkeeper/compact_index_server/rubygems_client.rb +59 -0
  18. data/lib/gemkeeper/compact_index_server/spec_mapper.rb +38 -0
  19. data/lib/gemkeeper/compact_index_server/upload_handler.rb +36 -0
  20. data/lib/gemkeeper/compact_index_server/upstream_cache.rb +26 -0
  21. data/lib/gemkeeper/compact_index_server.rb +131 -0
  22. data/lib/gemkeeper/configuration.rb +1 -1
  23. data/lib/gemkeeper/gem_syncer.rb +53 -84
  24. data/lib/gemkeeper/gem_uploader.rb +26 -18
  25. data/lib/gemkeeper/rackup_process.rb +12 -7
  26. data/lib/gemkeeper/repo_fetcher.rb +80 -0
  27. data/lib/gemkeeper/server_manager.rb +1 -1
  28. data/lib/gemkeeper/version.rb +1 -1
  29. data/lib/gemkeeper.rb +2 -0
  30. data/specs/20260529-091429-replace-geminabox-compact-proxy/critique-consolidated-v-1.md +168 -0
  31. data/specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-claude.md +124 -0
  32. data/specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-codex.md +125 -0
  33. data/specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-copilot.md +261 -0
  34. data/specs/20260529-091429-replace-geminabox-compact-proxy/spec.md +360 -0
  35. data/specs/20260529-131354-sync-serve-cache-contract/critique-consolidated-v-1.md +95 -0
  36. data/specs/20260529-131354-sync-serve-cache-contract/critique-v-1-claude.md +47 -0
  37. data/specs/20260529-131354-sync-serve-cache-contract/critique-v-1-codex.md +112 -0
  38. data/specs/20260529-131354-sync-serve-cache-contract/critique-v-1-copilot.md +169 -0
  39. data/specs/20260529-131354-sync-serve-cache-contract/implementation-summary.md +59 -0
  40. data/specs/20260529-131354-sync-serve-cache-contract/spec.md +169 -0
  41. metadata +38 -28
@@ -0,0 +1,169 @@
1
+ # Spec 20260529-131354: Server-authoritative sync cache check
2
+
3
+ ## Overview
4
+
5
+ `gemkeeper sync` decides a gem is "already synced" by checking for a local build artifact, but the running server serves from a separate store populated only by HTTP upload. These two states can diverge, so a fresh or repointed server can never be repopulated by re-running `sync` — every gem is skipped and nothing is uploaded, leaving the server returning 404 for gems that exist on disk. This spec makes `sync`'s skip decision authoritative against the server's *private* store via a new read-only endpoint, and lets `sync` recover by re-uploading an already-built artifact without rebuilding.
6
+
7
+ ## Goals
8
+
9
+ - `sync` skips a gem only when the running server's private store actually serves that exact version.
10
+ - The presence check consults the private store only — never the upstream-proxied compact index — so it cannot be fooled by a public gem of the same name and never probes rubygems.org.
11
+ - A fresh, empty, or repointed server is fully repopulated by re-running `sync`, with no manual cache clearing.
12
+ - For pinned and `from_lockfile` versions, recovery never re-clones or rebuilds when the `.gem` artifact already exists locally. (`version: latest` keeps today's always-fetch behavior — see FR-1.6.)
13
+ - No regression to the shared Homebrew-service deployment, where `sync`'s `gems_path` legitimately differs from the server's.
14
+
15
+ ---
16
+
17
+ ## Feature 1: Server-authoritative sync skip and recovery
18
+
19
+ **Who & why:** A developer working offline runs `gemkeeper sync` to populate a local gem server, then `bundle install` against it. Today, if they restart the server against an empty store (or it was never uploaded to), `sync` silently skips every gem because the local `.gem` files still exist, and `bundle install` fails with 404s that give no hint the cache is stale. They need `sync` to detect that the *server* is missing a gem and fix it, so "run sync, then bundle" always works.
20
+
21
+ ### Functional Requirements
22
+
23
+ #### FR-1.1: Skip decision queries the server's private store, not the local filesystem
24
+ `GemSyncer` determines whether a gem version is already available by asking the running server's private store, replacing the current `cached?` check that tests for a local file at `gems_path/<name>-<version>.gem`. A gem is considered present only if the server's private store reports that exact `<name>` and `<version>`. The version comparison uses bare semver (no `v` prefix), consistent with existing cache-key normalization.
25
+ **Verify:** With a `.gem` present in the local `gems_path` but absent from the server's store, `sync` reports the gem as synced (not skipped) and the server subsequently serves it; with the gem present on the server, `sync` reports it skipped.
26
+
27
+ #### FR-1.2: Presence is read from the private-store endpoint, not `/info`
28
+ The server-presence check calls the dedicated private-store endpoint defined in FR-2.1 (`GET /gemkeeper/has/<name>/<version>`), not the Bundler-facing `/info/<name>`. A `200` means present; a `404` means not present. `/info/<name>` is deliberately not used because it falls back to the upstream rubygems.org index (`serve_upstream_info`), which would cause public-name-collision false positives, offline failures, and leakage of private gem names to rubygems.org.
29
+ **Verify:** A unit test stubs `GET /gemkeeper/has/mimir/1.0.5` returning `200` → present; stubs `404` → not present; confirms `sync`'s presence path issues no request to `/info` or to rubygems.org.
30
+
31
+ #### FR-1.3: Re-upload an existing artifact instead of rebuilding
32
+ When the server reports a gem version missing but the corresponding `.gem` already exists in the local `gems_path`, `sync` uploads that existing artifact directly. Before uploading, it verifies the artifact's embedded gemspec name and version match the requested gem; on mismatch or an unreadable artifact it falls through to the build path rather than uploading the wrong file. It does not clone, pull, or run `gem build` when a valid artifact is reused. Only when no valid local artifact exists does `sync` fall back to the full clone/checkout/build path.
33
+ **Verify:** With a valid `mimir-1.0.5.gem` in `gems_path` and absent from the server, running `sync mimir` uploads the gem without invoking the git or build steps (assert via stubbed `GitRepository`/`GemBuilder` that they are not called), and the server then serves `mimir 1.0.5`; with a corrupt `mimir-1.0.5.gem`, `sync` does not upload it and proceeds to rebuild.
34
+
35
+ #### FR-1.4: Idempotent re-sync against any server state
36
+ Running `sync` twice in a row is safe and convergent: the first run uploads whatever the server is missing; the second run finds everything present and skips it. If an upload targets a gem the server already has, the server's existing-gem response (`409`, already handled by `GemUploader#handle_response` as success/skip) is treated as success, not failure.
37
+ **Verify:** Run `sync` against an empty server, then immediately again; first run uploads N gems, second run skips all N with no errors and a zero failure count.
38
+
39
+ #### FR-1.5: Output distinguishes the three outcomes
40
+ `sync` output distinguishes (a) skipped because the server already has it, (b) uploaded an existing local artifact without rebuilding, and (c) built and uploaded from source. Outcomes (b) and (c) both count as `:synced` in the run summary; only (a) counts as `:skipped`, so the existing `:synced`/`:skipped` tally and `report_results` are unchanged. The distinction between (b) and (c) is conveyed in the per-gem output text using the existing `Output.skip`/`Output.step`/`Output.success` vocabulary, with wording that makes clear when a rebuild was avoided.
41
+ **Verify:** Output for a server-present gem shows a "skipped" line and increments `:skipped`; output for an artifact re-upload names that the cached artifact was uploaded without rebuilding and increments `:synced`; output for a fresh gem shows the build step and increments `:synced`.
42
+
43
+ #### FR-1.6: `version: latest` keeps today's always-fetch behavior
44
+ For `version: latest`, the version is only known after clone/checkout (`current_version` post-checkout), so `latest` is exempt from the "skip before fetching" and "re-upload without rebuild" guarantees. `latest` continues to fetch the repo and resolve the gemspec version as it does today (the existing `!gem_def.latest?` cache bypass). Once the version is resolved, the server-presence check still applies to decide whether the resolved version needs uploading.
45
+ **Verify:** A `version: latest` gem always fetches the repo; after resolving its gemspec version, if the server already has that version the gem is reported skipped, otherwise it is built/uploaded.
46
+
47
+ ### Architectural Requirements
48
+
49
+ #### AR-1.1: Presence check lives behind the uploader/server-client seam
50
+ The server-presence query belongs with the HTTP client that already owns server communication (`GemUploader`, `lib/gemkeeper/gem_uploader.rb`), which holds `server_url`, a Faraday connection, `reachable?`, and the `POST /upload` call. Add `has_version?(name, version)` there; `GemSyncer` calls it rather than holding HTTP concerns. The existing `list_gems` stub is left as-is (it has its own `gemkeeper list` contract). Reusing the existing Faraday connection (with its `:multipart`/`:url_encoded` middleware) for the GET is acceptable.
51
+ **Verify:** `GemSyncer` contains no direct HTTP/Faraday code; `has_version?` is covered by `test/gemkeeper/test_gem_uploader.rb`.
52
+
53
+ #### AR-1.2: Reuse existing version resolution and cache-key normalization
54
+ The presence check and artifact lookup use the version produced by the existing resolution path (`resolve_version`, `latest_version!`, `current_version`) and the bare-semver normalization already applied for cache keys and filenames (`checkout_tag`, the former `cached?`). Artifact filenames are derived one way, as `<name>-<version>.gem` (see the platform assumption in Assumptions & Risks).
55
+ **Verify:** A `from_lockfile` gem resolves its version, then the presence check and any upload use that bare version and the `<name>-<version>.gem` filename.
56
+
57
+ #### AR-1.3: Preserve both deployment modes
58
+ The design must not assume `sync`'s `gems_path` equals the server's `gems_path`. In the shared Homebrew-service mode they differ (project-local build dir vs. absolute service store), and the HTTP bridge between them is retained. The presence check is therefore strictly server-side over HTTP, never a local-path comparison against the server's store.
59
+ **Verify:** A test exercising a server whose store path differs from `sync`'s `gems_path` still skips/uploads correctly based solely on server responses.
60
+
61
+ #### AR-1.4: Presence-check status → outcome mapping
62
+ `has_version?` maps server responses as follows: `200` → present; `404` → not present; `400` (invalid name/version — should not occur for config-sourced names) → raise as a programming error, not silently "absent"; a connection failure or timeout → `ServerNotReachableError` with the existing "run 'gemkeeper server start'" guidance. Because the skip decision now depends on a server round-trip, an unreachable server surfaces this actionable error early (before any git work) rather than treating the gem as present or absent.
63
+ **Verify:** Server down → `sync` exits with the not-reachable error and guidance; `400` → raises a non-`ServerNotReachableError`; `404` → gem treated as not present and the run continues.
64
+
65
+ #### AR-1.5: `GemSyncer` flow defers source work until after the cheap checks
66
+ `GemSyncer#sync` is reordered so that, for pinned and `from_lockfile` versions, the server-presence check and the local-artifact check run before any repo or manifest resolution. Repo URL resolution (`resolve_repo`), `GitRepository` creation, and `clone_or_pull` happen only when a build is actually required. The upload-existing-artifact path and the build-then-upload path are separated (e.g. distinct private methods) rather than the current single `build_and_upload`.
67
+ **Verify:** For a pinned gem the server already has, `sync` makes no manifest/repo resolution and creates no `GitRepository` (assert via stubs); the build and upload-existing paths are independently testable.
68
+
69
+ ---
70
+
71
+ ## Feature 2: Private-store presence endpoint
72
+
73
+ **Who & why:** `sync` needs an authoritative yes/no on whether the server's *private* store holds a specific gem version, without the Bundler `/info/<name>` endpoint's upstream fallback that proxies rubygems.org. A public gem sharing a private gem's name, or an offline upstream probe, must never affect the answer.
74
+
75
+ ### Functional Requirements
76
+
77
+ #### FR-2.1: Read-only private-store presence endpoint
78
+ `CompactIndexServer` exposes `GET /gemkeeper/has/<name>/<version>` that consults the private `GemIndex` only and never invokes the upstream cache. It returns `200` when the index holds `<name>` with a version whose recorded number equals `<version>` (bare semver), and `404` otherwise. The response body is minimal (status is the signal).
79
+ **Verify:** With `mimir 1.0.5` uploaded, `GET /gemkeeper/has/mimir/1.0.5` → `200` and `GET /gemkeeper/has/mimir/9.9.9` → `404`; with the server offline and `mimir` absent, the same request returns `404` immediately with no upstream request and no 503.
80
+
81
+ #### FR-2.2: Name and version validation
82
+ The endpoint validates `<name>` with the existing `VALID_NAME` pattern and rejects a malformed name or empty version with `400`, consistent with the server's other parameterized routes.
83
+ **Verify:** `GET /gemkeeper/has/..%2Fetc/1.0.0` (or any name failing `VALID_NAME`) → `400`; a well-formed-but-absent gem → `404`.
84
+
85
+ ### Architectural Requirements
86
+
87
+ #### AR-2.1: Endpoint reads only the private index and follows existing server patterns
88
+ The endpoint is wired through the existing router and built with the existing response helpers (`ResponseBuilder`/the small `not_found`/`invalid_name` responders), reading exclusively from `GemIndex` (`@index`) and never touching `UpstreamCache`. It must not regress the server's quality gates (rubocop clean, rubycritic ≥ 90) — keep the routing addition consistent with the `RESOURCE_ROUTES` table approach so `CompactIndexServer` does not gain a new smell.
89
+ **Verify:** The endpoint handler references `@index` only (no `@cache`); rubocop and rubycritic remain green after the addition.
90
+
91
+ ---
92
+
93
+ ## Data Requirements
94
+
95
+ No new persisted data or schema changes. The private-store presence endpoint reads the in-memory `GemIndex` built from the on-disk store; the on-disk layout (`gems_path` for build artifacts, `gems_path/gems` for the served store) is unchanged.
96
+
97
+ ## Integration Points
98
+
99
+ - `lib/gemkeeper/gem_syncer.rb` — reordered `sync` flow; the `cached?` replacement (presence → re-upload → build/upload); split of `build_and_upload`.
100
+ - `lib/gemkeeper/gem_uploader.rb` — new `has_version?(name, version)`; reuses the existing Faraday connection and error mapping.
101
+ - `lib/gemkeeper/compact_index_server.rb` — new `GET /gemkeeper/has/<name>/<version>` route.
102
+ - `lib/gemkeeper/compact_index_server/gem_index.rb` — read path used by the endpoint (presence lookup against `@index`).
103
+ - `lib/gemkeeper/cli/commands/sync.rb` — unchanged wiring (`GemUploader.new(config.server_url)`); the uploader now also answers presence, and `report_results` is unchanged (re-upload counts as `:synced`).
104
+
105
+ ## Related Specs
106
+
107
+ | Spec | Relationship | Affected Requirements |
108
+ |------|-------------|---------------------|
109
+ | Spec 20260529-091429: Replace Geminabox with compact index server | **Modifies** — corrects the sync↔serve cache contract introduced when the in-process compact-index server replaced Geminabox, and adds a private-store presence endpoint to that server | FR-1.1, FR-2.1, AR-1.1, AR-1.3 |
110
+
111
+ ## Constraints
112
+
113
+ - Quality gates must stay green: `bundle exec rubocop` clean and `bundle exec rubycritic lib --no-browser` ≥ 90.
114
+ - Follow the established collaborator/value-object structure of the compact-index server refactor; no Metrics-cop suppression comments.
115
+ - Offline-first: for pinned and `from_lockfile` versions, the happy path and recovery path must not require network or git access when a valid local artifact exists (FR-1.3); the presence endpoint must never block on an upstream probe (FR-2.1).
116
+ - Tests: create `sync()` orchestration tests in `test/gemkeeper/test_gem_syncer.rb` (it currently tests only `resolve_repo`); extend `test/gemkeeper/test_gem_uploader.rb` (presence method) and `test/gemkeeper/test_compact_index_server.rb` (new endpoint); add the original-bug regression to `test/integration/test_server_lifecycle_integration.rb`.
117
+ - Add an `[Unreleased]` CHANGELOG entry under `Fixed`.
118
+
119
+ ## Out of Scope
120
+
121
+ - Flattening the `gems_path` vs `gems_path/gems` layout or eliminating the redundant flat build artifact. This is a tidiness improvement that would change the on-disk layout and require migrating existing Homebrew-service stores; deferred to a future spec.
122
+ - Precompiled, platform-specific gem variants (filenames like `<name>-<version>-<platform>.gem`). Internal gems are built from source with the default `ruby` platform (see Assumptions); supporting published per-platform binaries is a future spec.
123
+ - Removing or redesigning the HTTP `POST /upload` bridge. It is required for the shared-service mode and is retained as-is.
124
+ - Changing the Bundler-facing `/info`, `/names`, `/versions`, or `/gems` endpoints.
125
+ - Auto-refreshing the server's in-memory index from disk without an upload. The upload remains the mechanism that informs a running server of a new gem.
126
+ - Changing version-resolution semantics (`latest`, `from_lockfile`, tag normalization).
127
+
128
+ ## Assumptions & Risks
129
+
130
+ - **Platform assumption:** internal gems are built from source via `gem build` and are therefore the default `ruby` platform, producing a single `<name>-<version>.gem` (no platform suffix). This is confirmed by evidence — every gem currently built by `sync` lacks a platform suffix. Gems with C extensions still fall under this assumption (extensions compile at install time); only deliberately cross-compiled, published per-platform binaries would not, and those are out of scope.
131
+ - **Presence-implies-serveable invariant:** the private-store endpoint and `/gems/<file>` both read the same `GemIndex`, so a `200` from the presence endpoint implies the binary is serveable. If a future change makes them diverge, the skip decision would become unsafe.
132
+ - **`sync` requires a reachable server** (already true today, since it uploads). The presence check makes this dependency explicit and surfaces it earlier (AR-1.4).
133
+ - **Risk — artifact identity:** a stale or wrong local `.gem` could be uploaded; mitigated by the pre-upload name+version check (FR-1.3).
134
+
135
+ ## Spec Completeness Checklist
136
+
137
+ - [x] **Scope & acceptance criteria** — Goals + per-FR **Verify** lines + Out of Scope bound the change; the bug and its reproduction are in Overview/FR-1.1; `latest` is explicitly scoped (FR-1.6).
138
+ - [x] **Testing strategy** — Constraints list the files to create/extend (notably new `sync()` tests in `test_gem_syncer.rb`) and the original-bug regression; FR Verify lines define conditions.
139
+ - [x] **Existing patterns** — Reuses `GemUploader` HTTP seam, `Output` vocabulary, bare-semver normalization, the router/`RESOURCE_ROUTES` and `ResponseBuilder` patterns (AR-1.1, AR-1.2, AR-2.1, FR-1.5).
140
+ - [x] **Dependencies** — No new libraries; reuses the Faraday client and adds one read-only endpoint (FR-2.1, AR-1.1).
141
+ - [x] **Architecture & interfaces** — `has_version?` on the uploader; reordered `GemSyncer` flow with split build/upload (AR-1.1, AR-1.5); private-store endpoint reads `@index` only (AR-2.1); data model unchanged.
142
+ - [x] **Error handling & failure modes** — Status→outcome mapping incl. `400`/`404`/connection failure (AR-1.4); idempotent/`409` handling (FR-1.4); corrupt/mismatched artifact fallthrough (FR-1.3); offline endpoint returns `404` not `503` (FR-2.1).
143
+ - [x] **Security review** — Low (not N/A). The private-store endpoint removes the `/info` upstream fallback, so private gem names are no longer leaked to rubygems.org for the cache check; `<name>` is validated by `VALID_NAME` server-side (FR-2.2) and the client treats `400` as a programming error (AR-1.4). Traffic is loopback; gems are developer-controlled.
144
+ - [x] **Performance impact** — One extra loopback `GET /gemkeeper/has/...` per gem (no upstream probe); negligible. Re-upload avoids the far costlier clone/build for pinned/`from_lockfile` (FR-1.3).
145
+ - [x] **Rollout & migration** — No data migration; on-disk layout unchanged. New endpoint is additive; behavior change is limited to the skip decision; existing stores keep working.
146
+ - [x] **Assumptions & risks** — Platform, presence-implies-serveable, server-reachability, and artifact-identity captured in Assumptions & Risks.
147
+
148
+ ---
149
+
150
+ ## Change Log
151
+
152
+ ### Update from critique-consolidated-v-1.md
153
+
154
+ **Applied:**
155
+ - Replaced the `/info`-parsing cache check with a dedicated private-store endpoint `GET /gemkeeper/has/<name>/<version>` (new Feature 2; FR-1.2, FR-2.1, FR-2.2, AR-2.1) — resolves the upstream-fallback false-positive, offline-503, and private-name-leak findings (critique A, I).
156
+ - Scoped the "never re-clone" guarantee to pinned and `from_lockfile` versions; added FR-1.6 making `latest`'s always-fetch behavior explicit (critique B).
157
+ - Added the platform assumption (default `ruby` platform, `<name>-<version>.gem`); precompiled per-platform variants moved to Out of Scope (critique C).
158
+ - Required a pre-upload artifact identity check (name+version match; corrupt-artifact fallthrough) in FR-1.3 (critique D).
159
+ - Specified the `GemSyncer` flow change — defer repo/manifest resolution until after the presence and local-artifact checks, and split `build_and_upload` (AR-1.5) (critique E).
160
+ - Added the presence-check status→outcome mapping (AR-1.4) and decided third-outcome counting: re-upload counts as `:synced`, differentiated by output text only, leaving the CLI summary unchanged (FR-1.5) (critique F).
161
+ - Clarified `has_version?` is added and `list_gems` is left as-is; noted Faraday connection reuse is acceptable (AR-1.1) (critique G, H).
162
+ - Updated testing constraints to create `sync()` tests in `test_gem_syncer.rb` plus the original-bug regression and listed scenarios.
163
+ - Downgraded the security review from N/A to low-with-mitigations.
164
+
165
+ **Rejected:**
166
+ - None. (Flattening the `gems_path/gems` layout was raised as adjacent messiness but was already deliberately out of scope; left out of scope.)
167
+
168
+ **Reorganized:**
169
+ - Split the single feature into Feature 1 (client-side sync behavior) and Feature 2 (server-side private-store endpoint) so the new server surface has its own FRs/ARs.
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Brubaker Horst
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-28 00:00:00.000000000 Z
11
+ date: 2026-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: dry-cli
14
+ name: compact_index
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '0.15'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '0.15'
27
27
  - !ruby/object:Gem::Dependency
28
- name: faraday
28
+ name: dry-cli
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: faraday-multipart
42
+ name: faraday
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.0'
47
+ version: '2.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.0'
54
+ version: '2.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: geminabox
56
+ name: faraday-multipart
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '1.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: puma
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '2.0'
97
- - !ruby/object:Gem::Dependency
98
- name: rubygems-generate_index
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '1.0'
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '1.0'
111
97
  description: An opinionated wrapper around Gem in a Box to manage private gems in
112
98
  a development environment.
113
99
  email:
@@ -135,6 +121,18 @@ files:
135
121
  - lib/gemkeeper/cli/commands/sync.rb
136
122
  - lib/gemkeeper/cli/commands/version.rb
137
123
  - lib/gemkeeper/cli/lockfile_resolution.rb
124
+ - lib/gemkeeper/compact_index_server.rb
125
+ - lib/gemkeeper/compact_index_server/cache_meta.rb
126
+ - lib/gemkeeper/compact_index_server/cache_store.rb
127
+ - lib/gemkeeper/compact_index_server/gem_cache.rb
128
+ - lib/gemkeeper/compact_index_server/gem_index.rb
129
+ - lib/gemkeeper/compact_index_server/index_merger.rb
130
+ - lib/gemkeeper/compact_index_server/response.rb
131
+ - lib/gemkeeper/compact_index_server/response_builder.rb
132
+ - lib/gemkeeper/compact_index_server/rubygems_client.rb
133
+ - lib/gemkeeper/compact_index_server/spec_mapper.rb
134
+ - lib/gemkeeper/compact_index_server/upload_handler.rb
135
+ - lib/gemkeeper/compact_index_server/upstream_cache.rb
138
136
  - lib/gemkeeper/config_generator.rb
139
137
  - lib/gemkeeper/configuration.rb
140
138
  - lib/gemkeeper/errors.rb
@@ -150,6 +148,7 @@ files:
150
148
  - lib/gemkeeper/manifest_validator.rb
151
149
  - lib/gemkeeper/output.rb
152
150
  - lib/gemkeeper/rackup_process.rb
151
+ - lib/gemkeeper/repo_fetcher.rb
153
152
  - lib/gemkeeper/server_manager.rb
154
153
  - lib/gemkeeper/server_readiness_probe.rb
155
154
  - lib/gemkeeper/version.rb
@@ -157,6 +156,17 @@ files:
157
156
  - sig/gemkeeper.rbs
158
157
  - specs/20260518-154733-gemkeeper-contractor-support/implementation-summary.md
159
158
  - specs/20260518-154733-gemkeeper-contractor-support/spec.md
159
+ - specs/20260529-091429-replace-geminabox-compact-proxy/critique-consolidated-v-1.md
160
+ - specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-claude.md
161
+ - specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-codex.md
162
+ - specs/20260529-091429-replace-geminabox-compact-proxy/critique-v-1-copilot.md
163
+ - specs/20260529-091429-replace-geminabox-compact-proxy/spec.md
164
+ - specs/20260529-131354-sync-serve-cache-contract/critique-consolidated-v-1.md
165
+ - specs/20260529-131354-sync-serve-cache-contract/critique-v-1-claude.md
166
+ - specs/20260529-131354-sync-serve-cache-contract/critique-v-1-codex.md
167
+ - specs/20260529-131354-sync-serve-cache-contract/critique-v-1-copilot.md
168
+ - specs/20260529-131354-sync-serve-cache-contract/implementation-summary.md
169
+ - specs/20260529-131354-sync-serve-cache-contract/spec.md
160
170
  homepage: https://github.com/danhorst/gemkeeper
161
171
  licenses:
162
172
  - MIT