gemkeeper 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +249 -12
- data/exe/gemkeeper +7 -1
- data/lib/gemkeeper/cli/commands/setup.rb +137 -0
- data/lib/gemkeeper/cli/commands/sync.rb +115 -40
- data/lib/gemkeeper/cli.rb +1 -0
- data/lib/gemkeeper/configuration.rb +27 -2
- data/lib/gemkeeper/errors.rb +1 -0
- data/lib/gemkeeper/git_repository.rb +57 -18
- data/lib/gemkeeper/lockfile_parser.rb +58 -0
- data/lib/gemkeeper/manifest_reader.rb +45 -0
- data/lib/gemkeeper/output.rb +26 -0
- data/lib/gemkeeper/server_manager.rb +45 -44
- data/lib/gemkeeper/version.rb +1 -1
- data/lib/gemkeeper.rb +3 -0
- data/mise.toml +6 -0
- data/specs/20260518-154733-gemkeeper-contractor-support/implementation-summary.md +75 -0
- data/specs/20260518-154733-gemkeeper-contractor-support/spec.md +287 -0
- metadata +13 -12
- data/.env.example +0 -1
- data/.envrc +0 -3
- data/.rubocop.yml +0 -30
- data/AGENTS.md +0 -52
- data/CLAUDE.md +0 -1
- data/CODE_OF_CONDUCT.md +0 -132
- data/Makefile +0 -26
- data/Rakefile +0 -12
- data/gemkeeper.yml.example +0 -26
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Spec 20260518-154733: Gemkeeper Contractor Support
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Contractors need to install internal Ruby gems that are normally served from a private gem
|
|
5
|
+
server they cannot reach without VPN.
|
|
6
|
+
This spec defines the features needed to make gemkeeper a reliable, self-service solution for
|
|
7
|
+
this use case: a project setup command, lockfile-aware sync, correct server binding, and
|
|
8
|
+
accurate documentation of the full setup sequence.
|
|
9
|
+
The companion CLI tool that delivers the gem manifest is specified separately.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
- Let a contractor configure gemkeeper for a specific project without knowing the internal
|
|
13
|
+
gem inventory
|
|
14
|
+
- Make `gemkeeper sync` idempotent against a `Gemfile.lock` with no redundant builds
|
|
15
|
+
- Document the end-to-end contractor setup sequence in the gemkeeper README
|
|
16
|
+
- Harden the server and sync commands against the most common failure modes
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Feature 1: Project Setup Command
|
|
21
|
+
|
|
22
|
+
**Who & why:** A contractor has just installed gemkeeper and has a `Gemfile.lock` in front
|
|
23
|
+
of them.
|
|
24
|
+
They should not need to know the names of internal gems, their GitHub URLs, or how to
|
|
25
|
+
construct a `gemkeeper.yml` by hand.
|
|
26
|
+
`gemkeeper setup` reads the lockfile, cross-references the org gem manifest, and
|
|
27
|
+
produces a ready-to-use config.
|
|
28
|
+
|
|
29
|
+
### Functional Requirements
|
|
30
|
+
|
|
31
|
+
#### FR-1.1: Setup Command
|
|
32
|
+
`gemkeeper setup <path-to-gemfile-lock>` reads the `GEM` section of the lockfile,
|
|
33
|
+
cross-references the manifest at `~/.config/gemkeeper/manifest.yml` (override:
|
|
34
|
+
`--manifest <path>`), and writes a `gemkeeper.yml` in the current directory.
|
|
35
|
+
|
|
36
|
+
Each matched gem entry uses `version: "from_lockfile"` so subsequent sync calls read the
|
|
37
|
+
current lockfile version rather than requiring setup to be re-run on every lockfile change.
|
|
38
|
+
|
|
39
|
+
**Behavior when `gemkeeper.yml` already exists:** the command merges — it adds or updates
|
|
40
|
+
only entries for gems found in the manifest, leaving all other keys (port, repos_path,
|
|
41
|
+
gems_path, unrelated gem entries) untouched.
|
|
42
|
+
Pass `--force` to overwrite the file entirely.
|
|
43
|
+
|
|
44
|
+
**Error cases:**
|
|
45
|
+
- Manifest not found at default or `--manifest` path → fail with message directing the user
|
|
46
|
+
to install the org's gem manifest
|
|
47
|
+
- Gem appears in lockfile with a name matching an internal pattern but absent from manifest →
|
|
48
|
+
warn and skip, do not fail
|
|
49
|
+
|
|
50
|
+
**Verify:** Running `gemkeeper setup /path/to/Gemfile.lock` in a directory with no prior
|
|
51
|
+
config produces a `gemkeeper.yml` listing only the internal gems the lockfile references, each
|
|
52
|
+
with `version: "from_lockfile"`.
|
|
53
|
+
|
|
54
|
+
#### FR-1.2: Bundler Configuration Output
|
|
55
|
+
After setup completes, the command prints the `bundle config` command the developer
|
|
56
|
+
must run to redirect their project's internal gem source to the local Geminabox.
|
|
57
|
+
If the manifest includes a `source_url` field, that URL is used as the mirror key.
|
|
58
|
+
Otherwise, the command prints a generic placeholder the developer must fill in.
|
|
59
|
+
Example output:
|
|
60
|
+
```
|
|
61
|
+
To point Bundler at your local Geminabox, run:
|
|
62
|
+
bundle config set --local mirror.<private-gem-source-url> http://localhost:9292
|
|
63
|
+
```
|
|
64
|
+
The port in the command matches the configured port in `gemkeeper.yml` (default: 9292).
|
|
65
|
+
`git:` source declarations in the Gemfile are unaffected and require no mirror config.
|
|
66
|
+
|
|
67
|
+
**Verify:** After running the printed command, `bundle install` resolves internal gems from
|
|
68
|
+
the local Geminabox and public gems from rubygems.org without modifying any tracked file.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Feature 2: Lockfile-Aware Sync
|
|
73
|
+
|
|
74
|
+
**Who & why:** With `version: "from_lockfile"` in the config, sync must read the lockfile,
|
|
75
|
+
resolve the correct git checkout ref, skip versions already cached, and handle partial
|
|
76
|
+
failures gracefully.
|
|
77
|
+
A sync that rebuilds everything on every run, or that fails entirely on one bad gem, is not
|
|
78
|
+
usable in practice.
|
|
79
|
+
|
|
80
|
+
### Functional Requirements
|
|
81
|
+
|
|
82
|
+
#### FR-2.1: `from_lockfile` Version Resolution
|
|
83
|
+
When a gem entry specifies `version: "from_lockfile"`, `gemkeeper sync`:
|
|
84
|
+
1. Walks up from the current directory to find the nearest `Gemfile.lock`
|
|
85
|
+
2. Reads the gem's locked version from the `GEM` section
|
|
86
|
+
3. Attempts to check out git tag `v{version}`, then `{version}`, in that order
|
|
87
|
+
4. Fails with a named error if neither tag exists in the repo (naming the gem and version)
|
|
88
|
+
|
|
89
|
+
If no `Gemfile.lock` is found in the directory walk, sync fails with a message naming the
|
|
90
|
+
gem and the search path traversed.
|
|
91
|
+
|
|
92
|
+
`version: "latest"` and explicit version tags continue to work as before.
|
|
93
|
+
|
|
94
|
+
**Verify:** `gemkeeper sync` for a gem with `version: "from_lockfile"` checks out the git tag
|
|
95
|
+
corresponding to the version pinned in the nearest `Gemfile.lock`.
|
|
96
|
+
When no lockfile is found, the command exits non-zero with a descriptive message.
|
|
97
|
+
|
|
98
|
+
#### FR-2.2: Skip Already-Cached Versions
|
|
99
|
+
Before cloning, fetching, building, or uploading, `gemkeeper sync` checks whether the `.gem`
|
|
100
|
+
file for the resolved version is already present in the Geminabox cache at
|
|
101
|
+
`File.join(gems_path, "gems", "#{name}-#{version}.gem")`.
|
|
102
|
+
If present, the gem is skipped entirely — no clone, no build, no upload.
|
|
103
|
+
Re-running sync with no lockfile change is a no-op.
|
|
104
|
+
|
|
105
|
+
**Verify:** First sync builds and uploads gem X at version 1.2.3.
|
|
106
|
+
Second sync with no lockfile change produces no git, build, or upload activity.
|
|
107
|
+
Updating the lockfile to version 1.3.0 causes sync to build and upload only that version.
|
|
108
|
+
|
|
109
|
+
#### FR-2.3: Partial Failure Handling
|
|
110
|
+
If one gem's clone, build, or upload fails, sync continues with the remaining gems.
|
|
111
|
+
At the end of the run, sync reports all failures (gem name + error message) and exits
|
|
112
|
+
non-zero if any gem failed.
|
|
113
|
+
A fully successful sync exits zero.
|
|
114
|
+
**Verify:** A sync with one gem that fails to build still attempts all other configured gems
|
|
115
|
+
and exits non-zero with a summary of the failure.
|
|
116
|
+
|
|
117
|
+
#### FR-2.4: Git Authentication Failure Handling
|
|
118
|
+
When `git clone` or `git fetch` returns an authentication error, sync exits non-zero with a
|
|
119
|
+
message that includes the failed repo URL and a pointer to GitHub credential setup
|
|
120
|
+
documentation (`https://docs.github.com/en/authentication`).
|
|
121
|
+
The raw git error may be included but the failure type must be identified specifically.
|
|
122
|
+
Clone URLs must not use the embedded-token format (`https://token@github.com/...`); the
|
|
123
|
+
README must warn against this pattern.
|
|
124
|
+
**Verify:** Running sync against a repo with no configured GitHub credentials produces a
|
|
125
|
+
message containing "authentication" and the docs URL.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Feature 3: Server and Security Hardening
|
|
130
|
+
|
|
131
|
+
**Who & why:** Geminabox runs without authentication.
|
|
132
|
+
Binding to `0.0.0.0` would expose an unauthenticated gem push endpoint to LAN peers, allowing
|
|
133
|
+
a malicious gem to be silently installed by `bundle install`.
|
|
134
|
+
Ref validation prevents argument injection from crafted manifest entries.
|
|
135
|
+
|
|
136
|
+
### Functional Requirements
|
|
137
|
+
|
|
138
|
+
#### FR-3.1: Localhost-Only Server Binding
|
|
139
|
+
`gemkeeper server start` (and `server start --foreground`) must bind Geminabox to `127.0.0.1`
|
|
140
|
+
only.
|
|
141
|
+
**Verify:** After `gemkeeper server start`, `lsof -i :9292` shows the process bound to
|
|
142
|
+
`127.0.0.1`, not `0.0.0.0`.
|
|
143
|
+
|
|
144
|
+
### Architectural Requirements
|
|
145
|
+
|
|
146
|
+
#### AR-3.1: `--host 127.0.0.1` Passed to Rackup
|
|
147
|
+
The `start_server` and `start_server_foreground` methods in `ServerManager` must pass
|
|
148
|
+
`"--host", "127.0.0.1"` in the rackup command array.
|
|
149
|
+
This applies to both daemonized and foreground start.
|
|
150
|
+
|
|
151
|
+
#### AR-3.2: Manifest Ref Validation
|
|
152
|
+
Before passing any manifest-derived or lockfile-derived value to `git checkout`, gemkeeper
|
|
153
|
+
validates the value against `/\A[a-zA-Z0-9._\-]+\z/`.
|
|
154
|
+
Values that do not match are rejected with an error naming the offending entry.
|
|
155
|
+
The existing `run_git` method uses `Open3.capture3(*cmd)` array form and is already safe from
|
|
156
|
+
shell injection; this validation prevents argument injection (e.g., `--upload-pack=...`).
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Feature 4: Documentation
|
|
161
|
+
|
|
162
|
+
**Who & why:** A contractor following the gemkeeper README must be able to complete setup
|
|
163
|
+
independently.
|
|
164
|
+
The current README does not document the contractor workflow or the full sequence
|
|
165
|
+
of commands needed to get `bundle install` working.
|
|
166
|
+
|
|
167
|
+
### Functional Requirements
|
|
168
|
+
|
|
169
|
+
#### FR-4.1: Contractor Setup Sequence
|
|
170
|
+
The gemkeeper README must document the full setup sequence as a numbered checklist:
|
|
171
|
+
1. Install gemkeeper (`gem install gemkeeper`)
|
|
172
|
+
2. Install the org's gem manifest (mechanism provided by the org)
|
|
173
|
+
3. Run `gemkeeper setup <path/to/Gemfile.lock>` in the project directory
|
|
174
|
+
4. Run `gemkeeper sync` to build and cache internal gems
|
|
175
|
+
5. Start the local server (`gemkeeper server start`)
|
|
176
|
+
6. Configure Bundler with the command printed by step 3
|
|
177
|
+
|
|
178
|
+
The checklist must note that GitHub credentials must be configured before step 4 (link to
|
|
179
|
+
`https://docs.github.com/en/authentication`).
|
|
180
|
+
|
|
181
|
+
**Verify:** A new contractor with no prior gemkeeper knowledge can follow the checklist and
|
|
182
|
+
reach a passing `bundle install` without assistance.
|
|
183
|
+
|
|
184
|
+
#### FR-4.2: HTTPS Git URL Documentation
|
|
185
|
+
Config examples and the README must document HTTPS GitHub URLs
|
|
186
|
+
(`https://github.com/org/repo`) as the recommended format for contractors who have not
|
|
187
|
+
configured SSH keys.
|
|
188
|
+
SSH URLs (`git@github.com:org/repo.git`) must be noted as an alternative.
|
|
189
|
+
**Verify:** The README config examples use HTTPS URLs.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Architectural Requirements (Cross-Cutting)
|
|
194
|
+
|
|
195
|
+
#### AR-4.1: `from_lockfile` Schema in Configuration
|
|
196
|
+
`from_lockfile` is a new reserved string in `Configuration::GemDefinition`, validated
|
|
197
|
+
alongside `"latest"`.
|
|
198
|
+
The existing `latest?` predicate is joined by a `from_lockfile?` predicate.
|
|
199
|
+
`gemkeeper.yml` validation rejects any `version` value that is not `"latest"`,
|
|
200
|
+
`"from_lockfile"`, or a string matching `/\A[a-zA-Z0-9._\-]+\z/`.
|
|
201
|
+
|
|
202
|
+
#### AR-4.2: No Geminabox Patching
|
|
203
|
+
All sync logic, idempotency tracking, and version resolution lives in gemkeeper code.
|
|
204
|
+
Geminabox is used as a Rack dependency without modification.
|
|
205
|
+
`rubygems_proxy = true` is already set in the generated `config.ru` and requires no change.
|
|
206
|
+
|
|
207
|
+
#### AR-4.3: Bundler Mirror over Source Block
|
|
208
|
+
`gemkeeper setup` prints a `bundle config set --local mirror.X Y` instruction rather than
|
|
209
|
+
suggesting a Gemfile source block, because the mirror approach requires no change to the
|
|
210
|
+
committed `Gemfile` or `Gemfile.lock`.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Integration Points
|
|
215
|
+
|
|
216
|
+
- gemkeeper setup → `~/.config/gemkeeper/manifest.yml` (written by the org's manifest installer)
|
|
217
|
+
- gemkeeper sync → GitHub (HTTPS or SSH clone; no private gem server required)
|
|
218
|
+
- gemkeeper server → Geminabox (Rack app, localhost only)
|
|
219
|
+
- gemkeeper → Bundler (mirror config, local scope, printed by setup)
|
|
220
|
+
|
|
221
|
+
## Related Specs
|
|
222
|
+
|
|
223
|
+
The companion CLI tool that delivers `~/.config/gemkeeper/manifest.yml` is out of scope for this repo.
|
|
224
|
+
|
|
225
|
+
## Constraints
|
|
226
|
+
- Geminabox is used as a dependency, not modified
|
|
227
|
+
- Ruby >= 3.1.0 (existing gemspec requirement)
|
|
228
|
+
|
|
229
|
+
## Out of Scope
|
|
230
|
+
- Automating manifest updates on gem release
|
|
231
|
+
- Centrally hosted gem mirror
|
|
232
|
+
- Windows support
|
|
233
|
+
- Gems declared via `git:` source blocks (these already resolve via GitHub; no caching needed)
|
|
234
|
+
- CI/CD pipeline support (contractors and local dev only for this spec)
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Spec Completeness Checklist
|
|
239
|
+
|
|
240
|
+
- [x] **Scope & acceptance criteria** — FR-1.1 through FR-4.2 have Verify: lines; Out of
|
|
241
|
+
Scope names exclusions; `git:` source blocks explicitly excluded
|
|
242
|
+
- [x] **Testing strategy** — new commands (setup, FR-1.1; manifest-aware sync, FR-2.1–2.4)
|
|
243
|
+
follow the pattern in `test/` with unit tests per class and integration tests for CLI
|
|
244
|
+
commands; manifest loading tested with fixture YAML; lockfile parsing tested with fixture
|
|
245
|
+
lockfiles covering `GEM` and `GIT` sections; FR-3.1 server binding tested via `lsof` in
|
|
246
|
+
integration test; ref validation (AR-3.2) tested with crafted inputs
|
|
247
|
+
- [x] **Existing patterns** — spec extends `Configuration::GemDefinition` (AR-4.1),
|
|
248
|
+
`GemBuilder`/`GemUploader`/`GitRepository` (sync FRs), `ServerManager` (FR-3.1);
|
|
249
|
+
`run_git` array form confirmed safe from shell injection
|
|
250
|
+
- [x] **Dependencies** — no new runtime gems required; `bundler` gem used for lockfile
|
|
251
|
+
parsing if not already available (it is, as a development dependency); no other additions
|
|
252
|
+
- [x] **Architecture & interfaces** — `from_lockfile` schema defined in AR-4.1; lockfile
|
|
253
|
+
search and version-to-tag mapping defined in FR-2.1; idempotency check path defined in
|
|
254
|
+
FR-2.2; server binding defined in AR-3.1; manifest interface defined in Integration Points
|
|
255
|
+
- [x] **Error handling & failure modes** — FR-2.1 covers missing lockfile and missing tag;
|
|
256
|
+
FR-2.3 covers partial sync failure; FR-2.4 covers git auth failure; FR-1.1 covers missing
|
|
257
|
+
manifest; AR-3.2 covers invalid refs; known limitation: disk-present ≠ upload-confirmed
|
|
258
|
+
(see Assumptions & Risks #3)
|
|
259
|
+
- [x] **Security review** — `run_git` uses `Open3.capture3(*cmd)` array form (safe); AR-3.2
|
|
260
|
+
adds ref validation; AR-3.1 binds to localhost; FR-2.4 prohibits embedded-token URLs;
|
|
261
|
+
no credentials stored by gemkeeper
|
|
262
|
+
- [x] **Performance impact** — idempotency check (FR-2.2) skips entire clone/build/upload
|
|
263
|
+
pipeline when version is cached; Geminabox proxies public gems via `rubygems_proxy = true`;
|
|
264
|
+
no production system impact
|
|
265
|
+
- [N/A] **Rollout & migration** — additive; existing `gemkeeper.yml` files without
|
|
266
|
+
`from_lockfile` continue to work; server binding change is transparent to users
|
|
267
|
+
- [x] **Assumptions & risks** — see below
|
|
268
|
+
|
|
269
|
+
### Assumptions & Risks
|
|
270
|
+
|
|
271
|
+
1. **Assumption:** All contractors have HTTPS GitHub access to the internal gem repos listed
|
|
272
|
+
in the manifest.
|
|
273
|
+
The contractor setup checklist (FR-4.1) must state this as a prerequisite.
|
|
274
|
+
|
|
275
|
+
2. **Assumption:** Internal gem `gemspec` files can be evaluated locally without proprietary
|
|
276
|
+
build tooling.
|
|
277
|
+
If a gem requires a non-standard build step, `gemkeeper sync` will fail.
|
|
278
|
+
Mitigation: verify `gemkeeper sync` succeeds for each gem before adding it to the manifest.
|
|
279
|
+
|
|
280
|
+
3. **Known limitation:** The idempotency check (FR-2.2) confirms a `.gem` file exists on
|
|
281
|
+
disk, not that it was successfully uploaded to the running Geminabox instance.
|
|
282
|
+
A build whose upload failed will be silently skipped on re-run.
|
|
283
|
+
Mitigation: `gemkeeper list` can be used to verify what the running server holds.
|
|
284
|
+
|
|
285
|
+
4. **Risk:** Gems containing native extensions produce platform-specific `.gem` files.
|
|
286
|
+
A gem cache is not portable across machines with different CPU architectures.
|
|
287
|
+
Each developer must run `gemkeeper sync` on their own machine.
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gemkeeper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dan Brubaker Horst
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-05-19 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: dry-cli
|
|
@@ -88,25 +89,17 @@ executables:
|
|
|
88
89
|
extensions: []
|
|
89
90
|
extra_rdoc_files: []
|
|
90
91
|
files:
|
|
91
|
-
- ".env.example"
|
|
92
|
-
- ".envrc"
|
|
93
|
-
- ".rubocop.yml"
|
|
94
|
-
- AGENTS.md
|
|
95
92
|
- CHANGELOG.md
|
|
96
|
-
- CLAUDE.md
|
|
97
|
-
- CODE_OF_CONDUCT.md
|
|
98
93
|
- LICENSE
|
|
99
|
-
- Makefile
|
|
100
94
|
- README.md
|
|
101
|
-
- Rakefile
|
|
102
95
|
- exe/gemkeeper
|
|
103
|
-
- gemkeeper.yml.example
|
|
104
96
|
- lib/gemkeeper.rb
|
|
105
97
|
- lib/gemkeeper/cli.rb
|
|
106
98
|
- lib/gemkeeper/cli/commands/list.rb
|
|
107
99
|
- lib/gemkeeper/cli/commands/server/start.rb
|
|
108
100
|
- lib/gemkeeper/cli/commands/server/status.rb
|
|
109
101
|
- lib/gemkeeper/cli/commands/server/stop.rb
|
|
102
|
+
- lib/gemkeeper/cli/commands/setup.rb
|
|
110
103
|
- lib/gemkeeper/cli/commands/sync.rb
|
|
111
104
|
- lib/gemkeeper/cli/commands/version.rb
|
|
112
105
|
- lib/gemkeeper/configuration.rb
|
|
@@ -114,9 +107,15 @@ files:
|
|
|
114
107
|
- lib/gemkeeper/gem_builder.rb
|
|
115
108
|
- lib/gemkeeper/gem_uploader.rb
|
|
116
109
|
- lib/gemkeeper/git_repository.rb
|
|
110
|
+
- lib/gemkeeper/lockfile_parser.rb
|
|
111
|
+
- lib/gemkeeper/manifest_reader.rb
|
|
112
|
+
- lib/gemkeeper/output.rb
|
|
117
113
|
- lib/gemkeeper/server_manager.rb
|
|
118
114
|
- lib/gemkeeper/version.rb
|
|
115
|
+
- mise.toml
|
|
119
116
|
- sig/gemkeeper.rbs
|
|
117
|
+
- specs/20260518-154733-gemkeeper-contractor-support/implementation-summary.md
|
|
118
|
+
- specs/20260518-154733-gemkeeper-contractor-support/spec.md
|
|
120
119
|
homepage: https://github.com/danhorst/gemkeeper
|
|
121
120
|
licenses:
|
|
122
121
|
- MIT
|
|
@@ -125,6 +124,7 @@ metadata:
|
|
|
125
124
|
source_code_uri: https://github.com/danhorst/gemkeeper
|
|
126
125
|
changelog_uri: https://github.com/danhorst/gemkeeper/blob/main/CHANGELOG.md
|
|
127
126
|
rubygems_mfa_required: 'true'
|
|
127
|
+
post_install_message:
|
|
128
128
|
rdoc_options: []
|
|
129
129
|
require_paths:
|
|
130
130
|
- lib
|
|
@@ -139,7 +139,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
139
139
|
- !ruby/object:Gem::Version
|
|
140
140
|
version: '0'
|
|
141
141
|
requirements: []
|
|
142
|
-
rubygems_version: 3.
|
|
142
|
+
rubygems_version: 3.5.22
|
|
143
|
+
signing_key:
|
|
143
144
|
specification_version: 4
|
|
144
145
|
summary: Manage offline development with private gem dependencies
|
|
145
146
|
test_files: []
|
data/.env.example
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
RUBYGEMS_PROXY=true
|
data/.envrc
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
plugins:
|
|
2
|
-
- rubocop-performance
|
|
3
|
-
|
|
4
|
-
AllCops:
|
|
5
|
-
TargetRubyVersion: 3.1
|
|
6
|
-
NewCops: enable
|
|
7
|
-
SuggestExtensions: false
|
|
8
|
-
Exclude:
|
|
9
|
-
- cache/**/*
|
|
10
|
-
- vendor/**/*
|
|
11
|
-
|
|
12
|
-
Style/StringLiterals:
|
|
13
|
-
EnforcedStyle: double_quotes
|
|
14
|
-
|
|
15
|
-
Style/StringLiteralsInInterpolation:
|
|
16
|
-
EnforcedStyle: double_quotes
|
|
17
|
-
|
|
18
|
-
Style/Documentation:
|
|
19
|
-
Enabled: false
|
|
20
|
-
|
|
21
|
-
Metrics/MethodLength:
|
|
22
|
-
Max: 30
|
|
23
|
-
|
|
24
|
-
Metrics/AbcSize:
|
|
25
|
-
Max: 40
|
|
26
|
-
|
|
27
|
-
Metrics/ClassLength:
|
|
28
|
-
Max: 120
|
|
29
|
-
Exclude:
|
|
30
|
-
- test/**/*
|
data/AGENTS.md
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# Gemkeeper
|
|
2
|
-
|
|
3
|
-
A Ruby CLI tool for managing offline development with private gem dependencies.
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
Automate building internal gems from source and caching them in a local Geminabox server for offline Rails development when disconnected from VPN.
|
|
8
|
-
|
|
9
|
-
## Architecture
|
|
10
|
-
|
|
11
|
-
- Ruby gem with CLI executable (`exe/gemkeeper`)
|
|
12
|
-
- YAML config for gem repository definitions
|
|
13
|
-
- Git operations to clone/pull internal repos
|
|
14
|
-
- Build gems at specified versions/tags
|
|
15
|
-
- Upload to local Geminabox server
|
|
16
|
-
- Geminabox proxies public gems from RubyGems.org
|
|
17
|
-
|
|
18
|
-
## Config Example
|
|
19
|
-
|
|
20
|
-
```yaml
|
|
21
|
-
port: 9292
|
|
22
|
-
repos_path: ./cache/repos
|
|
23
|
-
gems_path: ./cache/gems
|
|
24
|
-
|
|
25
|
-
gems:
|
|
26
|
-
- repo: git@github.com:company/internal-gem-1.git
|
|
27
|
-
version: latest
|
|
28
|
-
- repo: git@github.com:company/internal-gem-2.git
|
|
29
|
-
version: v2.3.1
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## CLI Commands
|
|
33
|
-
|
|
34
|
-
- `gemkeeper version` - Print version
|
|
35
|
-
- `gemkeeper server start` - Start Geminabox server
|
|
36
|
-
- `gemkeeper server stop` - Stop Geminabox server
|
|
37
|
-
- `gemkeeper server status` - Check server status
|
|
38
|
-
- `gemkeeper sync` - Build and upload all configured gems
|
|
39
|
-
- `gemkeeper sync <gem-name>` - Sync specific gem
|
|
40
|
-
- `gemkeeper list` - Show locally uploaded gems
|
|
41
|
-
|
|
42
|
-
## Development
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
bundle install
|
|
46
|
-
bundle exec rake test # Run tests
|
|
47
|
-
bundle exec rubocop # Run linter
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Current Status
|
|
51
|
-
|
|
52
|
-
v1 complete - all core functionality implemented
|
data/CLAUDE.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
AGENTS.md
|
data/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
-
identity and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
|
18
|
-
community include:
|
|
19
|
-
|
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
-
and learning from the experience
|
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
-
community
|
|
27
|
-
|
|
28
|
-
Examples of unacceptable behavior include:
|
|
29
|
-
|
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
-
any kind
|
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
-
* Public or private harassment
|
|
34
|
-
* Publishing others' private information, such as a physical or email address,
|
|
35
|
-
without their explicit permission
|
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
-
professional setting
|
|
38
|
-
|
|
39
|
-
## Enforcement Responsibilities
|
|
40
|
-
|
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
-
or harmful.
|
|
45
|
-
|
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
-
decisions when appropriate.
|
|
50
|
-
|
|
51
|
-
## Scope
|
|
52
|
-
|
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
-
an individual is officially representing the community in public spaces.
|
|
55
|
-
Examples of representing our community include using an official email address,
|
|
56
|
-
posting via an official social media account, or acting as an appointed
|
|
57
|
-
representative at an online or offline event.
|
|
58
|
-
|
|
59
|
-
## Enforcement
|
|
60
|
-
|
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
-
reported to the community leaders responsible for enforcement at
|
|
63
|
-
[INSERT CONTACT METHOD].
|
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
-
|
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
-
reporter of any incident.
|
|
68
|
-
|
|
69
|
-
## Enforcement Guidelines
|
|
70
|
-
|
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
-
|
|
74
|
-
### 1. Correction
|
|
75
|
-
|
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
-
unprofessional or unwelcome in the community.
|
|
78
|
-
|
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
|
82
|
-
|
|
83
|
-
### 2. Warning
|
|
84
|
-
|
|
85
|
-
**Community Impact**: A violation through a single incident or series of
|
|
86
|
-
actions.
|
|
87
|
-
|
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
-
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
-
ban.
|
|
94
|
-
|
|
95
|
-
### 3. Temporary Ban
|
|
96
|
-
|
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
|
98
|
-
sustained inappropriate behavior.
|
|
99
|
-
|
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
-
communication with the community for a specified period of time. No public or
|
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
-
Violating these terms may lead to a permanent ban.
|
|
105
|
-
|
|
106
|
-
### 4. Permanent Ban
|
|
107
|
-
|
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
-
|
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
-
community.
|
|
114
|
-
|
|
115
|
-
## Attribution
|
|
116
|
-
|
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
-
version 2.1, available at
|
|
119
|
-
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
-
|
|
121
|
-
Community Impact Guidelines were inspired by
|
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
-
|
|
124
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
-
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
-
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
-
|
|
128
|
-
[homepage]: https://www.contributor-covenant.org
|
|
129
|
-
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
-
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
-
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
-
[translations]: https://www.contributor-covenant.org/translations
|
data/Makefile
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
.PHONY: all build test fmt clean
|
|
2
|
-
|
|
3
|
-
# Find all tool directories under cmd/
|
|
4
|
-
TOOLS := $(wildcard cmd/*)
|
|
5
|
-
BINARIES := $(notdir $(TOOLS))
|
|
6
|
-
|
|
7
|
-
all: build
|
|
8
|
-
|
|
9
|
-
build:
|
|
10
|
-
@for tool in $(BINARIES); do \
|
|
11
|
-
if [ -d "cmd/$$tool" ] && [ -f "cmd/$$tool/main.go" ]; then \
|
|
12
|
-
echo "Building $$tool..."; \
|
|
13
|
-
go build -o bin/$$tool ./cmd/$$tool; \
|
|
14
|
-
fi \
|
|
15
|
-
done
|
|
16
|
-
|
|
17
|
-
test:
|
|
18
|
-
go test .
|
|
19
|
-
|
|
20
|
-
fmt:
|
|
21
|
-
gofmt -w .
|
|
22
|
-
|
|
23
|
-
clean:
|
|
24
|
-
rm -f bin/*
|
|
25
|
-
@# Preserve agent-setup script
|
|
26
|
-
@git checkout bin/agent-setup 2>/dev/null || true
|
data/Rakefile
DELETED
data/gemkeeper.yml.example
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Gemkeeper configuration file
|
|
2
|
-
# Copy this to gemkeeper.yml and modify as needed
|
|
3
|
-
|
|
4
|
-
# Server port (default: 9292)
|
|
5
|
-
port: 9292
|
|
6
|
-
|
|
7
|
-
# Path for cloned repositories (default: ./cache/repos)
|
|
8
|
-
repos_path: ./cache/repos
|
|
9
|
-
|
|
10
|
-
# Path for Geminabox gem storage (default: ./cache/gems)
|
|
11
|
-
gems_path: ./cache/gems
|
|
12
|
-
|
|
13
|
-
# List of internal gems to sync
|
|
14
|
-
gems:
|
|
15
|
-
# Sync the latest version from the main/master branch
|
|
16
|
-
- repo: git@github.com:company/internal-gem.git
|
|
17
|
-
version: latest
|
|
18
|
-
|
|
19
|
-
# Sync a specific tag or branch
|
|
20
|
-
- repo: git@github.com:company/another-gem.git
|
|
21
|
-
version: v2.3.1
|
|
22
|
-
|
|
23
|
-
# Override the gem name if it differs from the repo name
|
|
24
|
-
- repo: git@github.com:company/ruby-mylib.git
|
|
25
|
-
name: mylib
|
|
26
|
-
version: latest
|