ollama-client 0.2.3 → 0.2.4
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/docs/GEM_RELEASE_GUIDE.md +794 -0
- data/docs/GET_RUBYGEMS_SECRET.md +151 -0
- data/docs/QUICK_OTP_SETUP.md +80 -0
- data/docs/QUICK_RELEASE.md +106 -0
- data/docs/README.md +6 -0
- data/docs/RUBYGEMS_OTP_SETUP.md +199 -0
- data/lib/ollama/version.rb +1 -1
- metadata +11 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b126aae11a2fd7f0ff26e53e90e12222eb88e23fa6ecf622215c4a9317fe6ce
|
|
4
|
+
data.tar.gz: 2656544e1ce4bfa852dc687108bdde17084773c3a1e76c53ae9ed59d77604256
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5cbd78f768412b8e413e9222b4b0b2ef68e094280546742a0bd8191269072a6281ae4182d591e889ba3829c5b047b0e053c2410fa7734dc4cf1dfe320b527239
|
|
7
|
+
data.tar.gz: 2f1c2eafa75910646ed2300c8ad5ab312aefe56bb03d6e4797356e219543a57532058c2b2f3fc31b773caf0e7e0d26b3a71a6f9887b1a40955f5b818ffe6e227
|
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
# Automated Gem Release Guide
|
|
2
|
+
|
|
3
|
+
Complete guide to setting up automated Ruby gem releases using GitHub Actions and git tags.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Overview](#overview)
|
|
8
|
+
2. [Prerequisites](#prerequisites)
|
|
9
|
+
3. [Initial Setup](#initial-setup)
|
|
10
|
+
4. [GitHub Actions Workflow](#github-actions-workflow)
|
|
11
|
+
5. [RubyGems Authentication](#rubygems-authentication)
|
|
12
|
+
6. [Release Process](#release-process)
|
|
13
|
+
7. [Troubleshooting](#troubleshooting)
|
|
14
|
+
8. [Best Practices](#best-practices)
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
This guide shows how to automate gem releases so that:
|
|
19
|
+
- **Pushing a git tag** (e.g., `v1.2.3`) triggers an automated release
|
|
20
|
+
- **GitHub Actions** validates, builds, and publishes the gem
|
|
21
|
+
- **No manual `gem push`** required
|
|
22
|
+
|
|
23
|
+
### Benefits
|
|
24
|
+
|
|
25
|
+
✅ **Consistent releases** - Same process every time
|
|
26
|
+
✅ **Version validation** - Tag must match gem version
|
|
27
|
+
✅ **CI/CD integration** - Tests run before release
|
|
28
|
+
✅ **Security** - API keys stored in GitHub Secrets
|
|
29
|
+
✅ **Audit trail** - All releases tracked in GitHub
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
### 1. RubyGems Account
|
|
34
|
+
|
|
35
|
+
Create an account at https://rubygems.org if you don't have one.
|
|
36
|
+
|
|
37
|
+
### 2. Gem Ownership
|
|
38
|
+
|
|
39
|
+
You must own the gem name or it must be available:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Check if name is available
|
|
43
|
+
gem search ^your-gem-name$ --remote
|
|
44
|
+
|
|
45
|
+
# If gem exists, check ownership
|
|
46
|
+
gem owner your-gem-name
|
|
47
|
+
|
|
48
|
+
# Request ownership (if gem is abandoned)
|
|
49
|
+
# Visit: https://rubygems.org/gems/your-gem-name
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Gem Structure
|
|
53
|
+
|
|
54
|
+
Your gem should have:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
your-gem/
|
|
58
|
+
├── .github/
|
|
59
|
+
│ └── workflows/
|
|
60
|
+
│ └── release.yml # ← We'll create this
|
|
61
|
+
├── lib/
|
|
62
|
+
│ └── your_gem/
|
|
63
|
+
│ └── version.rb # ← Must have VERSION constant
|
|
64
|
+
├── your-gem.gemspec # ← Gem specification
|
|
65
|
+
├── Gemfile
|
|
66
|
+
└── README.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Initial Setup
|
|
70
|
+
|
|
71
|
+
### Step 1: Version File
|
|
72
|
+
|
|
73
|
+
Create `lib/your_gem/version.rb`:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# frozen_string_literal: true
|
|
77
|
+
|
|
78
|
+
module YourGem
|
|
79
|
+
VERSION = "0.1.0"
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Step 2: Gemspec File
|
|
84
|
+
|
|
85
|
+
Create `your-gem.gemspec`:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# frozen_string_literal: true
|
|
89
|
+
|
|
90
|
+
require_relative "lib/your_gem/version"
|
|
91
|
+
|
|
92
|
+
Gem::Specification.new do |spec|
|
|
93
|
+
spec.name = "your-gem"
|
|
94
|
+
spec.version = YourGem::VERSION
|
|
95
|
+
spec.authors = ["Your Name"]
|
|
96
|
+
spec.email = ["your.email@example.com"]
|
|
97
|
+
|
|
98
|
+
spec.summary = "Brief description"
|
|
99
|
+
spec.description = "Longer description"
|
|
100
|
+
spec.homepage = "https://github.com/yourusername/your-gem"
|
|
101
|
+
spec.license = "MIT"
|
|
102
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
103
|
+
|
|
104
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
105
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
106
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
107
|
+
|
|
108
|
+
# Specify which files should be added to the gem when it is released
|
|
109
|
+
spec.files = Dir.glob(%w[
|
|
110
|
+
lib/**/*.rb
|
|
111
|
+
lib/**/*.json
|
|
112
|
+
sig/**/*.rbs
|
|
113
|
+
*.md
|
|
114
|
+
LICENSE.txt
|
|
115
|
+
]).reject { |f| File.directory?(f) }
|
|
116
|
+
|
|
117
|
+
spec.bindir = "exe"
|
|
118
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
119
|
+
spec.require_paths = ["lib"]
|
|
120
|
+
|
|
121
|
+
# Dependencies
|
|
122
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
|
123
|
+
|
|
124
|
+
# Development dependencies
|
|
125
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
126
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
127
|
+
spec.add_development_dependency "rubocop", "~> 1.21"
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 3: Test Local Build
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Build the gem locally
|
|
135
|
+
gem build your-gem.gemspec
|
|
136
|
+
|
|
137
|
+
# Test installation
|
|
138
|
+
gem install ./your-gem-0.1.0.gem
|
|
139
|
+
|
|
140
|
+
# Clean up
|
|
141
|
+
rm your-gem-0.1.0.gem
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## GitHub Actions Workflow
|
|
145
|
+
|
|
146
|
+
### Create Workflow File
|
|
147
|
+
|
|
148
|
+
Create `.github/workflows/release.yml`:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
name: Release
|
|
152
|
+
|
|
153
|
+
on:
|
|
154
|
+
push:
|
|
155
|
+
tags:
|
|
156
|
+
- "v*"
|
|
157
|
+
|
|
158
|
+
jobs:
|
|
159
|
+
release:
|
|
160
|
+
runs-on: ubuntu-latest
|
|
161
|
+
permissions:
|
|
162
|
+
contents: read
|
|
163
|
+
steps:
|
|
164
|
+
- uses: actions/checkout@v4
|
|
165
|
+
with:
|
|
166
|
+
persist-credentials: false
|
|
167
|
+
|
|
168
|
+
- name: Set up Ruby
|
|
169
|
+
uses: ruby/setup-ruby@v1
|
|
170
|
+
with:
|
|
171
|
+
ruby-version: "3.3.4" # Use your Ruby version
|
|
172
|
+
bundler-cache: true
|
|
173
|
+
|
|
174
|
+
- name: Validate tag matches gem version
|
|
175
|
+
run: |
|
|
176
|
+
set -euo pipefail
|
|
177
|
+
tag="${GITHUB_REF#refs/tags/}"
|
|
178
|
+
tag_version="${tag#v}"
|
|
179
|
+
gem_version=$(ruby -e "require_relative 'lib/your_gem/version'; puts YourGem::VERSION")
|
|
180
|
+
if [ "$tag_version" != "$gem_version" ]; then
|
|
181
|
+
echo "❌ Tag version ($tag_version) does not match gem version ($gem_version)"
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
echo "✅ Tag v$tag_version matches gem version $gem_version"
|
|
185
|
+
|
|
186
|
+
- name: Build gem
|
|
187
|
+
run: gem build your-gem.gemspec
|
|
188
|
+
|
|
189
|
+
- name: Publish gem
|
|
190
|
+
env:
|
|
191
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
192
|
+
run: |
|
|
193
|
+
set -euo pipefail
|
|
194
|
+
gem_version=$(ruby -e "require_relative 'lib/your_gem/version'; puts YourGem::VERSION")
|
|
195
|
+
gem_file="your-gem-${gem_version}.gem"
|
|
196
|
+
if [ ! -f "$gem_file" ]; then
|
|
197
|
+
echo "❌ Gem file not found: $gem_file"
|
|
198
|
+
exit 1
|
|
199
|
+
fi
|
|
200
|
+
echo "📦 Publishing $gem_file to RubyGems..."
|
|
201
|
+
gem push "$gem_file"
|
|
202
|
+
echo "✅ Published successfully!"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Important:** Replace:
|
|
206
|
+
- `your_gem` with your gem's module name
|
|
207
|
+
- `YourGem` with your module constant
|
|
208
|
+
- `your-gem` with your gem's name
|
|
209
|
+
|
|
210
|
+
### Optional: Add Testing Before Release
|
|
211
|
+
|
|
212
|
+
Add a test job that runs before release:
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
name: Release
|
|
216
|
+
|
|
217
|
+
on:
|
|
218
|
+
push:
|
|
219
|
+
tags:
|
|
220
|
+
- "v*"
|
|
221
|
+
|
|
222
|
+
jobs:
|
|
223
|
+
# Run tests first
|
|
224
|
+
test:
|
|
225
|
+
runs-on: ubuntu-latest
|
|
226
|
+
steps:
|
|
227
|
+
- uses: actions/checkout@v4
|
|
228
|
+
- name: Set up Ruby
|
|
229
|
+
uses: ruby/setup-ruby@v1
|
|
230
|
+
with:
|
|
231
|
+
ruby-version: "3.3.4"
|
|
232
|
+
bundler-cache: true
|
|
233
|
+
- name: Run tests
|
|
234
|
+
run: bundle exec rake spec
|
|
235
|
+
- name: Run RuboCop
|
|
236
|
+
run: bundle exec rubocop
|
|
237
|
+
|
|
238
|
+
# Release only if tests pass
|
|
239
|
+
release:
|
|
240
|
+
needs: test
|
|
241
|
+
runs-on: ubuntu-latest
|
|
242
|
+
permissions:
|
|
243
|
+
contents: read
|
|
244
|
+
steps:
|
|
245
|
+
# ... (release steps as above)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## RubyGems Authentication
|
|
249
|
+
|
|
250
|
+
### Step 1: Get RubyGems API Key
|
|
251
|
+
|
|
252
|
+
1. **Login to RubyGems:**
|
|
253
|
+
- Visit: https://rubygems.org/sign_in
|
|
254
|
+
|
|
255
|
+
2. **Create API Key:**
|
|
256
|
+
- Go to: https://rubygems.org/profile/edit
|
|
257
|
+
- Scroll to "API Keys" section
|
|
258
|
+
- Click **"New API Key"**
|
|
259
|
+
|
|
260
|
+
3. **Configure Key:**
|
|
261
|
+
- **Name:** `GitHub Actions - your-gem` (descriptive name)
|
|
262
|
+
- **Scopes:** Select **"Push rubygems"** only (principle of least privilege)
|
|
263
|
+
- Click **"Create"**
|
|
264
|
+
|
|
265
|
+
4. **Copy Key:**
|
|
266
|
+
- Copy the generated key (you won't see it again!)
|
|
267
|
+
- Format: `rubygems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
|
268
|
+
|
|
269
|
+
### Step 2: Add to GitHub Secrets
|
|
270
|
+
|
|
271
|
+
1. **Navigate to Secrets:**
|
|
272
|
+
- Go to your GitHub repo
|
|
273
|
+
- Click **Settings** → **Secrets and variables** → **Actions**
|
|
274
|
+
- Or visit: `https://github.com/USERNAME/REPO/settings/secrets/actions`
|
|
275
|
+
|
|
276
|
+
2. **Create Secret:**
|
|
277
|
+
- Click **"New repository secret"**
|
|
278
|
+
- **Name:** `RUBYGEMS_API_KEY` (must match workflow file)
|
|
279
|
+
- **Value:** Paste your RubyGems API key
|
|
280
|
+
- Click **"Add secret"**
|
|
281
|
+
|
|
282
|
+
3. **Verify:**
|
|
283
|
+
- You should see `RUBYGEMS_API_KEY` in the list
|
|
284
|
+
- The value will be hidden (shows as `***`)
|
|
285
|
+
|
|
286
|
+
### Step 3: Test Authentication (Optional)
|
|
287
|
+
|
|
288
|
+
Test locally before using in CI:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Set up credentials file
|
|
292
|
+
mkdir -p ~/.gem
|
|
293
|
+
echo "---
|
|
294
|
+
:rubygems_api_key: your_api_key_here" > ~/.gem/credentials
|
|
295
|
+
chmod 600 ~/.gem/credentials
|
|
296
|
+
|
|
297
|
+
# Build and push manually to test
|
|
298
|
+
gem build your-gem.gemspec
|
|
299
|
+
gem push your-gem-0.1.0.gem
|
|
300
|
+
|
|
301
|
+
# Clean up
|
|
302
|
+
rm your-gem-0.1.0.gem
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Release Process
|
|
306
|
+
|
|
307
|
+
### Standard Release Workflow
|
|
308
|
+
|
|
309
|
+
#### 1. Update Version
|
|
310
|
+
|
|
311
|
+
Edit `lib/your_gem/version.rb`:
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
module YourGem
|
|
315
|
+
VERSION = "1.2.3" # Bump version
|
|
316
|
+
end
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### 2. Update Changelog
|
|
320
|
+
|
|
321
|
+
Edit `CHANGELOG.md`:
|
|
322
|
+
|
|
323
|
+
```markdown
|
|
324
|
+
## [1.2.3] - 2026-01-17
|
|
325
|
+
|
|
326
|
+
### Added
|
|
327
|
+
- New feature X
|
|
328
|
+
- New feature Y
|
|
329
|
+
|
|
330
|
+
### Fixed
|
|
331
|
+
- Bug fix A
|
|
332
|
+
- Bug fix B
|
|
333
|
+
|
|
334
|
+
### Changed
|
|
335
|
+
- Improvement C
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### 3. Commit Changes
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
git add lib/your_gem/version.rb CHANGELOG.md
|
|
342
|
+
git commit -m "Bump version to 1.2.3"
|
|
343
|
+
git push origin main
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### 4. Create and Push Tag
|
|
347
|
+
|
|
348
|
+
**Option A: Annotated Tag (Recommended)**
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Create annotated tag with message
|
|
352
|
+
git tag -a v1.2.3 -m "Release v1.2.3
|
|
353
|
+
|
|
354
|
+
- New feature X
|
|
355
|
+
- Bug fix A
|
|
356
|
+
- Improvement C"
|
|
357
|
+
|
|
358
|
+
# Push the tag
|
|
359
|
+
git push origin v1.2.3
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Option B: Lightweight Tag**
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Create simple tag
|
|
366
|
+
git tag v1.2.3
|
|
367
|
+
|
|
368
|
+
# Push it
|
|
369
|
+
git push origin v1.2.3
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### 5. Monitor Release
|
|
373
|
+
|
|
374
|
+
1. **Check GitHub Actions:**
|
|
375
|
+
- Go to: `https://github.com/USERNAME/REPO/actions`
|
|
376
|
+
- Watch the "Release" workflow run
|
|
377
|
+
- Should show: ✅ Validate → ✅ Build → ✅ Publish
|
|
378
|
+
|
|
379
|
+
2. **Verify on RubyGems:**
|
|
380
|
+
- Visit: `https://rubygems.org/gems/your-gem`
|
|
381
|
+
- Confirm version 1.2.3 is published
|
|
382
|
+
- Check download stats
|
|
383
|
+
|
|
384
|
+
3. **Test Installation:**
|
|
385
|
+
```bash
|
|
386
|
+
gem install your-gem
|
|
387
|
+
# Or with version
|
|
388
|
+
gem install your-gem -v 1.2.3
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Quick Release Script
|
|
392
|
+
|
|
393
|
+
Create `bin/release.sh`:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
#!/bin/bash
|
|
397
|
+
set -euo pipefail
|
|
398
|
+
|
|
399
|
+
# Usage: ./bin/release.sh 1.2.3
|
|
400
|
+
|
|
401
|
+
VERSION=$1
|
|
402
|
+
TAG="v${VERSION}"
|
|
403
|
+
|
|
404
|
+
echo "🚀 Releasing version ${VERSION}"
|
|
405
|
+
|
|
406
|
+
# Verify version in version.rb matches
|
|
407
|
+
CURRENT_VERSION=$(ruby -e "require_relative 'lib/your_gem/version'; puts YourGem::VERSION")
|
|
408
|
+
if [ "$CURRENT_VERSION" != "$VERSION" ]; then
|
|
409
|
+
echo "❌ Version mismatch!"
|
|
410
|
+
echo " lib/your_gem/version.rb: $CURRENT_VERSION"
|
|
411
|
+
echo " Requested: $VERSION"
|
|
412
|
+
exit 1
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
# Verify clean working directory
|
|
416
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
417
|
+
echo "❌ Working directory not clean. Commit or stash changes first."
|
|
418
|
+
exit 1
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
# Verify on main branch
|
|
422
|
+
BRANCH=$(git branch --show-current)
|
|
423
|
+
if [ "$BRANCH" != "main" ]; then
|
|
424
|
+
echo "⚠️ Not on main branch (currently on: $BRANCH)"
|
|
425
|
+
read -p "Continue anyway? [y/N] " -n 1 -r
|
|
426
|
+
echo
|
|
427
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
428
|
+
exit 1
|
|
429
|
+
fi
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# Create and push tag
|
|
433
|
+
echo "📝 Creating tag $TAG"
|
|
434
|
+
git tag -a "$TAG" -m "Release $TAG"
|
|
435
|
+
|
|
436
|
+
echo "⬆️ Pushing tag to origin"
|
|
437
|
+
git push origin "$TAG"
|
|
438
|
+
|
|
439
|
+
echo "✅ Tag pushed! GitHub Actions will publish the gem."
|
|
440
|
+
echo "📊 Monitor: https://github.com/USERNAME/REPO/actions"
|
|
441
|
+
echo "📦 Once published: https://rubygems.org/gems/your-gem"
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Make it executable:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
chmod +x bin/release.sh
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Use it:
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
# Update version in lib/your_gem/version.rb first
|
|
454
|
+
# Update CHANGELOG.md
|
|
455
|
+
# Commit changes
|
|
456
|
+
git add -A
|
|
457
|
+
git commit -m "Bump version to 1.2.3"
|
|
458
|
+
git push
|
|
459
|
+
|
|
460
|
+
# Run release script
|
|
461
|
+
./bin/release.sh 1.2.3
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## Troubleshooting
|
|
465
|
+
|
|
466
|
+
### Common Issues
|
|
467
|
+
|
|
468
|
+
#### 1. "Access Denied" Error
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
Pushing gem to https://rubygems.org...
|
|
472
|
+
Access Denied. Please sign up for an account
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Solution:**
|
|
476
|
+
- Verify `RUBYGEMS_API_KEY` secret is set in GitHub
|
|
477
|
+
- Check API key has "Push rubygems" scope
|
|
478
|
+
- Ensure you own the gem name
|
|
479
|
+
|
|
480
|
+
#### 2. "Gem name already taken"
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
There was a problem saving your gem: Name 'your-gem' is already taken
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Solution:**
|
|
487
|
+
```bash
|
|
488
|
+
# Check ownership
|
|
489
|
+
gem owner your-gem
|
|
490
|
+
|
|
491
|
+
# If you own it - check credentials
|
|
492
|
+
# If you don't - choose a different name or request ownership
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### 3. Version Mismatch Error
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
❌ Tag version (1.2.3) does not match gem version (1.2.2)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Solution:**
|
|
502
|
+
```bash
|
|
503
|
+
# Update version in lib/your_gem/version.rb
|
|
504
|
+
# Delete incorrect tag
|
|
505
|
+
git tag -d v1.2.3
|
|
506
|
+
git push origin :refs/tags/v1.2.3
|
|
507
|
+
|
|
508
|
+
# Create correct tag
|
|
509
|
+
git tag -a v1.2.3 -m "Release v1.2.3"
|
|
510
|
+
git push origin v1.2.3
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### 4. "Gem file not found"
|
|
514
|
+
|
|
515
|
+
```
|
|
516
|
+
❌ Gem file not found: your-gem-1.2.3.gem
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Solution:**
|
|
520
|
+
- Check gemspec file is named correctly
|
|
521
|
+
- Verify `spec.name` matches gem name
|
|
522
|
+
- Test build locally: `gem build your-gem.gemspec`
|
|
523
|
+
|
|
524
|
+
#### 5. Workflow Not Triggering
|
|
525
|
+
|
|
526
|
+
**Check:**
|
|
527
|
+
```bash
|
|
528
|
+
# Verify tag format
|
|
529
|
+
git tag -l # Should show v1.2.3 format
|
|
530
|
+
|
|
531
|
+
# Check workflow file location
|
|
532
|
+
ls -la .github/workflows/release.yml
|
|
533
|
+
|
|
534
|
+
# Verify tag trigger in workflow
|
|
535
|
+
grep "tags:" .github/workflows/release.yml
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Debug Failed Releases
|
|
539
|
+
|
|
540
|
+
#### View Workflow Logs
|
|
541
|
+
|
|
542
|
+
1. Go to: `https://github.com/USERNAME/REPO/actions`
|
|
543
|
+
2. Click on failed "Release" run
|
|
544
|
+
3. Expand failed step to see error
|
|
545
|
+
|
|
546
|
+
#### Re-run Failed Release
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
# Delete failed tag locally and remotely
|
|
550
|
+
git tag -d v1.2.3
|
|
551
|
+
git push origin :refs/tags/v1.2.3
|
|
552
|
+
|
|
553
|
+
# Fix the issue
|
|
554
|
+
|
|
555
|
+
# Re-create and push tag
|
|
556
|
+
git tag -a v1.2.3 -m "Release v1.2.3"
|
|
557
|
+
git push origin v1.2.3
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
#### Manual Release (Emergency)
|
|
561
|
+
|
|
562
|
+
If GitHub Actions fails, release manually:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
# Build gem
|
|
566
|
+
gem build your-gem.gemspec
|
|
567
|
+
|
|
568
|
+
# Push manually
|
|
569
|
+
gem push your-gem-1.2.3.gem
|
|
570
|
+
|
|
571
|
+
# Tag the commit after successful push
|
|
572
|
+
git tag -a v1.2.3 -m "Release v1.2.3 (manual)"
|
|
573
|
+
git push origin v1.2.3
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## Best Practices
|
|
577
|
+
|
|
578
|
+
### 1. Semantic Versioning
|
|
579
|
+
|
|
580
|
+
Follow [SemVer](https://semver.org/):
|
|
581
|
+
|
|
582
|
+
- **MAJOR** (1.0.0 → 2.0.0): Breaking changes
|
|
583
|
+
- **MINOR** (1.0.0 → 1.1.0): New features, backward compatible
|
|
584
|
+
- **PATCH** (1.0.0 → 1.0.1): Bug fixes, backward compatible
|
|
585
|
+
|
|
586
|
+
### 2. Changelog Maintenance
|
|
587
|
+
|
|
588
|
+
Keep `CHANGELOG.md` updated:
|
|
589
|
+
|
|
590
|
+
```markdown
|
|
591
|
+
# Changelog
|
|
592
|
+
|
|
593
|
+
All notable changes to this project will be documented in this file.
|
|
594
|
+
|
|
595
|
+
## [Unreleased]
|
|
596
|
+
|
|
597
|
+
### Added
|
|
598
|
+
- Features in development
|
|
599
|
+
|
|
600
|
+
## [1.2.3] - 2026-01-17
|
|
601
|
+
|
|
602
|
+
### Added
|
|
603
|
+
- New feature X
|
|
604
|
+
|
|
605
|
+
### Fixed
|
|
606
|
+
- Bug Y
|
|
607
|
+
|
|
608
|
+
### Changed
|
|
609
|
+
- Improvement Z
|
|
610
|
+
|
|
611
|
+
## [1.2.2] - 2026-01-10
|
|
612
|
+
...
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### 3. Pre-Release Checklist
|
|
616
|
+
|
|
617
|
+
Before tagging a release:
|
|
618
|
+
|
|
619
|
+
- [ ] Version bumped in `lib/your_gem/version.rb`
|
|
620
|
+
- [ ] `CHANGELOG.md` updated with changes
|
|
621
|
+
- [ ] All tests passing locally (`bundle exec rake spec`)
|
|
622
|
+
- [ ] RuboCop clean (`bundle exec rubocop`)
|
|
623
|
+
- [ ] Changes committed and pushed to main
|
|
624
|
+
- [ ] Local build works (`gem build your-gem.gemspec`)
|
|
625
|
+
|
|
626
|
+
### 4. Git Tag Conventions
|
|
627
|
+
|
|
628
|
+
Use consistent tag format:
|
|
629
|
+
|
|
630
|
+
✅ **Good:**
|
|
631
|
+
- `v1.2.3` (recommended)
|
|
632
|
+
- `1.2.3` (acceptable)
|
|
633
|
+
|
|
634
|
+
❌ **Bad:**
|
|
635
|
+
- `version-1.2.3`
|
|
636
|
+
- `release-1.2.3`
|
|
637
|
+
- `v1.2.3-final`
|
|
638
|
+
|
|
639
|
+
### 5. Security
|
|
640
|
+
|
|
641
|
+
**API Key Security:**
|
|
642
|
+
- ✅ Store in GitHub Secrets only
|
|
643
|
+
- ✅ Use "Push rubygems" scope only
|
|
644
|
+
- ✅ Rotate keys periodically (every 6-12 months)
|
|
645
|
+
- ✅ Delete keys for deprecated projects
|
|
646
|
+
- ❌ Never commit API keys to git
|
|
647
|
+
- ❌ Never share API keys in chat/email
|
|
648
|
+
|
|
649
|
+
**Gem File Security:**
|
|
650
|
+
- Include only necessary files in gem
|
|
651
|
+
- Use `spec.files` to explicitly list included files
|
|
652
|
+
- Exclude sensitive files (`.env`, credentials, etc.)
|
|
653
|
+
|
|
654
|
+
### 6. Release Notes
|
|
655
|
+
|
|
656
|
+
Create GitHub releases for major versions:
|
|
657
|
+
|
|
658
|
+
```bash
|
|
659
|
+
# Using gh CLI
|
|
660
|
+
gh release create v1.2.3 \
|
|
661
|
+
--title "v1.2.3 - Feature Name" \
|
|
662
|
+
--notes "## Highlights
|
|
663
|
+
|
|
664
|
+
- New feature X
|
|
665
|
+
- Bug fix Y
|
|
666
|
+
|
|
667
|
+
See [CHANGELOG.md](CHANGELOG.md) for details."
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### 7. Testing
|
|
671
|
+
|
|
672
|
+
Always test gems before public release:
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
# Build gem
|
|
676
|
+
gem build your-gem.gemspec
|
|
677
|
+
|
|
678
|
+
# Install locally
|
|
679
|
+
gem install ./your-gem-1.2.3.gem
|
|
680
|
+
|
|
681
|
+
# Test in a sample project
|
|
682
|
+
cd ~/tmp/test-project
|
|
683
|
+
bundle init
|
|
684
|
+
echo 'gem "your-gem", path: "/path/to/your-gem"' >> Gemfile
|
|
685
|
+
bundle install
|
|
686
|
+
|
|
687
|
+
# Test functionality
|
|
688
|
+
ruby -e "require 'your_gem'; puts YourGem::VERSION"
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
## Example: Complete Release Flow
|
|
692
|
+
|
|
693
|
+
Here's a complete example from start to finish:
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
# 1. Create new feature branch
|
|
697
|
+
git checkout -b feature/add-cool-feature
|
|
698
|
+
|
|
699
|
+
# 2. Implement feature
|
|
700
|
+
# ... code changes ...
|
|
701
|
+
|
|
702
|
+
# 3. Test locally
|
|
703
|
+
bundle exec rspec
|
|
704
|
+
bundle exec rubocop
|
|
705
|
+
|
|
706
|
+
# 4. Commit and push
|
|
707
|
+
git add -A
|
|
708
|
+
git commit -m "Add cool feature"
|
|
709
|
+
git push origin feature/add-cool-feature
|
|
710
|
+
|
|
711
|
+
# 5. Create PR and merge to main
|
|
712
|
+
gh pr create --title "Add cool feature"
|
|
713
|
+
# ... review and merge ...
|
|
714
|
+
|
|
715
|
+
# 6. Pull latest main
|
|
716
|
+
git checkout main
|
|
717
|
+
git pull origin main
|
|
718
|
+
|
|
719
|
+
# 7. Bump version
|
|
720
|
+
# Edit lib/your_gem/version.rb: VERSION = "1.3.0"
|
|
721
|
+
# Edit CHANGELOG.md: Add [1.3.0] section
|
|
722
|
+
|
|
723
|
+
# 8. Commit version bump
|
|
724
|
+
git add lib/your_gem/version.rb CHANGELOG.md
|
|
725
|
+
git commit -m "Bump version to 1.3.0"
|
|
726
|
+
git push origin main
|
|
727
|
+
|
|
728
|
+
# 9. Create and push tag
|
|
729
|
+
git tag -a v1.3.0 -m "Release v1.3.0
|
|
730
|
+
|
|
731
|
+
- Add cool feature
|
|
732
|
+
- Fix minor bugs"
|
|
733
|
+
git push origin v1.3.0
|
|
734
|
+
|
|
735
|
+
# 10. Monitor GitHub Actions
|
|
736
|
+
# Visit: https://github.com/USERNAME/REPO/actions
|
|
737
|
+
# Watch Release workflow complete
|
|
738
|
+
|
|
739
|
+
# 11. Verify on RubyGems
|
|
740
|
+
# Visit: https://rubygems.org/gems/your-gem
|
|
741
|
+
# Confirm v1.3.0 is live
|
|
742
|
+
|
|
743
|
+
# 12. Test installation
|
|
744
|
+
gem install your-gem
|
|
745
|
+
ruby -e "require 'your_gem'; puts YourGem::VERSION"
|
|
746
|
+
# Should output: 1.3.0
|
|
747
|
+
|
|
748
|
+
# 13. Announce release (optional)
|
|
749
|
+
# - Update README badges
|
|
750
|
+
# - Post on social media
|
|
751
|
+
# - Notify users/community
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
## Additional Resources
|
|
755
|
+
|
|
756
|
+
### Official Documentation
|
|
757
|
+
|
|
758
|
+
- **RubyGems Guides:** https://guides.rubygems.org/
|
|
759
|
+
- **GitHub Actions:** https://docs.github.com/en/actions
|
|
760
|
+
- **Semantic Versioning:** https://semver.org/
|
|
761
|
+
|
|
762
|
+
### Useful Tools
|
|
763
|
+
|
|
764
|
+
- **gh CLI:** https://cli.github.com/ - Manage GitHub from command line
|
|
765
|
+
- **gem-release:** https://github.com/svenfuchs/gem-release - Automate version bumping
|
|
766
|
+
- **bundler:** https://bundler.io/ - Dependency management
|
|
767
|
+
|
|
768
|
+
### Related Workflows
|
|
769
|
+
|
|
770
|
+
For more advanced setups, check:
|
|
771
|
+
- **Multi-platform testing:** Test on multiple Ruby versions
|
|
772
|
+
- **Dependency updates:** Automated dependabot PRs
|
|
773
|
+
- **Documentation:** Auto-generate and publish docs
|
|
774
|
+
- **Code coverage:** Track test coverage over time
|
|
775
|
+
|
|
776
|
+
## Conclusion
|
|
777
|
+
|
|
778
|
+
You now have a complete automated gem release pipeline that:
|
|
779
|
+
|
|
780
|
+
1. ✅ Validates version matches tag
|
|
781
|
+
2. ✅ Builds gem automatically
|
|
782
|
+
3. ✅ Publishes to RubyGems
|
|
783
|
+
4. ✅ Maintains security with GitHub Secrets
|
|
784
|
+
5. ✅ Provides audit trail via git tags
|
|
785
|
+
|
|
786
|
+
This workflow can be reused for all your Ruby gems with minimal modifications!
|
|
787
|
+
|
|
788
|
+
---
|
|
789
|
+
|
|
790
|
+
**Questions or Issues?**
|
|
791
|
+
|
|
792
|
+
- Check [Troubleshooting](#troubleshooting) section
|
|
793
|
+
- Review [GitHub Actions logs](https://github.com/USERNAME/REPO/actions)
|
|
794
|
+
- Consult [RubyGems Guides](https://guides.rubygems.org/)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# How to Get Your RubyGems OTP Secret
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
You need the **OTP secret key** from RubyGems, not the 6-digit codes from your authenticator app.
|
|
6
|
+
|
|
7
|
+
## What It Looks Like
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Example: JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP
|
|
11
|
+
|
|
12
|
+
✅ Valid format:
|
|
13
|
+
- All UPPERCASE letters A-Z
|
|
14
|
+
- Numbers 2-7 only
|
|
15
|
+
- Usually 16-32 characters
|
|
16
|
+
- No spaces, dashes, underscores, or special characters
|
|
17
|
+
|
|
18
|
+
❌ This is NOT it:
|
|
19
|
+
- 123456 (that's the OTP code, not the secret)
|
|
20
|
+
- abc123_secret (contains invalid characters)
|
|
21
|
+
- YOUR_SECRET_HERE (that's a placeholder!)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Step-by-Step: Get Your Secret
|
|
25
|
+
|
|
26
|
+
### Method 1: Find Existing Secret (If Saved)
|
|
27
|
+
|
|
28
|
+
When you first enabled MFA on RubyGems, it showed you a QR code and a secret key. If you saved that secret key, use it!
|
|
29
|
+
|
|
30
|
+
Check these places:
|
|
31
|
+
- Password manager (1Password, LastPass, Bitwarden, etc.)
|
|
32
|
+
- Notes app
|
|
33
|
+
- Screenshot of the QR code setup page
|
|
34
|
+
|
|
35
|
+
### Method 2: Regenerate MFA on RubyGems
|
|
36
|
+
|
|
37
|
+
If you can't find the original secret, regenerate it:
|
|
38
|
+
|
|
39
|
+
#### Step 1: Log in to RubyGems
|
|
40
|
+
Go to https://rubygems.org
|
|
41
|
+
|
|
42
|
+
#### Step 2: Go to MFA Settings
|
|
43
|
+
1. Click your profile picture/name (top right)
|
|
44
|
+
2. Click **Edit Profile**
|
|
45
|
+
3. Click **Multi-factor Authentication** (left sidebar)
|
|
46
|
+
|
|
47
|
+
#### Step 3: Regenerate MFA
|
|
48
|
+
1. Click **"Disable Multi-factor Authentication"**
|
|
49
|
+
2. Confirm disabling
|
|
50
|
+
3. Click **"Enable Multi-factor Authentication"** again
|
|
51
|
+
4. You'll see a QR code and the **secret key** text
|
|
52
|
+
|
|
53
|
+
#### Step 4: Save the Secret
|
|
54
|
+
```
|
|
55
|
+
IMPORTANT: Copy the secret key NOW!
|
|
56
|
+
Example: JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Step 5: Update Your Authenticator App
|
|
60
|
+
1. Remove the old RubyGems entry from your authenticator app
|
|
61
|
+
2. Scan the new QR code
|
|
62
|
+
3. Enter the 6-digit code to verify
|
|
63
|
+
|
|
64
|
+
⚠️ **Warning:** After regenerating, your old authenticator codes won't work!
|
|
65
|
+
|
|
66
|
+
## Test Your Secret
|
|
67
|
+
|
|
68
|
+
Once you have the secret, test it:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Replace JBSWY... with YOUR actual secret
|
|
72
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP').now"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Expected output:**
|
|
76
|
+
```
|
|
77
|
+
123456 ← A 6-digit number
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**This number should match** your authenticator app at the same time!
|
|
81
|
+
|
|
82
|
+
## Common Mistakes
|
|
83
|
+
|
|
84
|
+
### ❌ Using the 6-digit OTP code
|
|
85
|
+
```bash
|
|
86
|
+
# WRONG - This is the code, not the secret
|
|
87
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('123456').now"
|
|
88
|
+
# Error: Invalid Base32 Character
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### ❌ Using the placeholder text
|
|
92
|
+
```bash
|
|
93
|
+
# WRONG - This is the example placeholder
|
|
94
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('YOUR_SECRET_HERE').now"
|
|
95
|
+
# Error: Invalid Base32 Character - '_'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### ✅ Using the actual secret
|
|
99
|
+
```bash
|
|
100
|
+
# CORRECT - Your actual Base32 secret from RubyGems
|
|
101
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP').now"
|
|
102
|
+
# Output: 123456
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Where to Use It
|
|
106
|
+
|
|
107
|
+
Once you have your real secret:
|
|
108
|
+
|
|
109
|
+
### 1. Test Locally
|
|
110
|
+
```bash
|
|
111
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('YOUR_ACTUAL_SECRET').now"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. Add to GitHub Secrets
|
|
115
|
+
1. Go to: https://github.com/YOUR_USERNAME/YOUR_REPO/settings/secrets/actions
|
|
116
|
+
2. Click **New repository secret**
|
|
117
|
+
3. Name: `RUBYGEMS_OTP_SECRET`
|
|
118
|
+
4. Value: Your actual secret (e.g., `JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP`)
|
|
119
|
+
5. Click **Add secret**
|
|
120
|
+
|
|
121
|
+
## Still Having Issues?
|
|
122
|
+
|
|
123
|
+
### Error: "Invalid Base32 Character"
|
|
124
|
+
|
|
125
|
+
Your secret contains invalid characters. Check:
|
|
126
|
+
- No underscores (`_`)
|
|
127
|
+
- No lowercase letters
|
|
128
|
+
- No numbers 0, 1, 8, 9
|
|
129
|
+
- No spaces or special characters
|
|
130
|
+
|
|
131
|
+
Valid characters: `A-Z` and `2-7` only
|
|
132
|
+
|
|
133
|
+
### Error: "OTP code doesn't match"
|
|
134
|
+
|
|
135
|
+
1. Make sure your computer's time is synchronized
|
|
136
|
+
2. Try again (codes change every 30 seconds)
|
|
137
|
+
3. Verify the secret is correct
|
|
138
|
+
|
|
139
|
+
### Can't Find Secret Anywhere
|
|
140
|
+
|
|
141
|
+
You'll need to regenerate MFA (see Method 2 above).
|
|
142
|
+
|
|
143
|
+
## Security Note
|
|
144
|
+
|
|
145
|
+
🔒 **Keep your OTP secret secure!**
|
|
146
|
+
- Don't share it publicly
|
|
147
|
+
- Don't commit it to git
|
|
148
|
+
- Store it in a password manager
|
|
149
|
+
- Only add it to GitHub Secrets (which are encrypted)
|
|
150
|
+
|
|
151
|
+
The secret is like a password - anyone with it can generate your OTP codes!
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Quick OTP Setup for Automated Releases
|
|
2
|
+
|
|
3
|
+
## What You Need
|
|
4
|
+
|
|
5
|
+
Your GitHub repository needs **two secrets** for automated gem releases with MFA:
|
|
6
|
+
|
|
7
|
+
1. **RUBYGEMS_API_KEY** - Your RubyGems API key
|
|
8
|
+
2. **RUBYGEMS_OTP_SECRET** - Your RubyGems OTP secret
|
|
9
|
+
|
|
10
|
+
## Step 1: Get Your OTP Secret
|
|
11
|
+
|
|
12
|
+
1. Log in to https://rubygems.org
|
|
13
|
+
2. Go to **Edit Profile** → **Multi-factor Authentication**
|
|
14
|
+
3. Copy the **secret key** (long alphanumeric string like `JBSWY3DPEHPK3PXP`)
|
|
15
|
+
|
|
16
|
+
**⚠️ Save this securely!** If you don't see it, you may need to regenerate your MFA.
|
|
17
|
+
|
|
18
|
+
## Step 2: Add Secrets to GitHub
|
|
19
|
+
|
|
20
|
+
1. Go to your GitHub repo: https://github.com/shubhamtaywade82/ollama-client
|
|
21
|
+
2. Click **Settings** → **Secrets and variables** → **Actions**
|
|
22
|
+
3. Add two secrets:
|
|
23
|
+
|
|
24
|
+
| Secret Name | Value | Where to Get It |
|
|
25
|
+
| --------------------- | --------------- | ------------------------------------- |
|
|
26
|
+
| `RUBYGEMS_API_KEY` | Your API key | https://rubygems.org/profile/api_keys |
|
|
27
|
+
| `RUBYGEMS_OTP_SECRET` | Your OTP secret | RubyGems MFA settings (Step 1) |
|
|
28
|
+
|
|
29
|
+
## Step 3: Test It
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Bump version in lib/ollama/version.rb
|
|
33
|
+
# Then create and push tag:
|
|
34
|
+
git tag v0.2.4
|
|
35
|
+
git push origin v0.2.4
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The GitHub Action will automatically:
|
|
39
|
+
- Generate OTP code
|
|
40
|
+
- Build gem
|
|
41
|
+
- Push to RubyGems with OTP authentication
|
|
42
|
+
|
|
43
|
+
## Test OTP Locally (Optional)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
gem install rotp
|
|
47
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('YOUR_SECRET_HERE').now"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This should match the code in your authenticator app.
|
|
51
|
+
|
|
52
|
+
## Troubleshooting
|
|
53
|
+
|
|
54
|
+
### "Invalid OTP code"
|
|
55
|
+
- Verify `RUBYGEMS_OTP_SECRET` is correct
|
|
56
|
+
- Check it matches your authenticator app
|
|
57
|
+
|
|
58
|
+
### "unauthorized"
|
|
59
|
+
- Verify `RUBYGEMS_API_KEY` is correct
|
|
60
|
+
- Create new API key with push permissions
|
|
61
|
+
|
|
62
|
+
### Need more help?
|
|
63
|
+
See full guide: `docs/RUBYGEMS_OTP_SETUP.md`
|
|
64
|
+
|
|
65
|
+
## How It Works
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
- name: Install OTP generator
|
|
69
|
+
run: gem install rotp
|
|
70
|
+
|
|
71
|
+
- name: Publish gem with OTP
|
|
72
|
+
env:
|
|
73
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
74
|
+
RUBYGEMS_OTP_SECRET: ${{ secrets.RUBYGEMS_OTP_SECRET }}
|
|
75
|
+
run: |
|
|
76
|
+
otp_code=$(ruby -r rotp -e "puts ROTP::TOTP.new(ENV['RUBYGEMS_OTP_SECRET']).now")
|
|
77
|
+
gem push "ollama-client-${version}.gem" --otp "$otp_code"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
That's it! Your automated releases are now secured with MFA. 🔒
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Quick Release Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for releasing gems via GitHub Actions. See [GEM_RELEASE_GUIDE.md](GEM_RELEASE_GUIDE.md) for full details.
|
|
4
|
+
|
|
5
|
+
## One-Time Setup
|
|
6
|
+
|
|
7
|
+
### 1. RubyGems API Key
|
|
8
|
+
1. Visit: https://rubygems.org/profile/edit
|
|
9
|
+
2. Create API key with "Push rubygems" scope
|
|
10
|
+
3. Copy the key
|
|
11
|
+
|
|
12
|
+
### 2. GitHub Secret
|
|
13
|
+
1. Go to: `https://github.com/USERNAME/REPO/settings/secrets/actions`
|
|
14
|
+
2. Add secret: `RUBYGEMS_API_KEY`
|
|
15
|
+
3. Paste your RubyGems API key
|
|
16
|
+
|
|
17
|
+
### 3. Workflow File
|
|
18
|
+
Create `.github/workflows/release.yml`:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
name: Release
|
|
22
|
+
on:
|
|
23
|
+
push:
|
|
24
|
+
tags: ["v*"]
|
|
25
|
+
jobs:
|
|
26
|
+
release:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: ruby/setup-ruby@v1
|
|
31
|
+
with:
|
|
32
|
+
ruby-version: "3.3.4"
|
|
33
|
+
bundler-cache: true
|
|
34
|
+
- name: Validate version
|
|
35
|
+
run: |
|
|
36
|
+
tag_version="${GITHUB_REF#refs/tags/v}"
|
|
37
|
+
gem_version=$(ruby -e "require_relative 'lib/your_gem/version'; puts YourGem::VERSION")
|
|
38
|
+
[ "$tag_version" = "$gem_version" ] || exit 1
|
|
39
|
+
- name: Build and publish
|
|
40
|
+
env:
|
|
41
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
42
|
+
run: |
|
|
43
|
+
gem build your-gem.gemspec
|
|
44
|
+
gem push your-gem-*.gem
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Release Process
|
|
48
|
+
|
|
49
|
+
### Every Release
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 1. Update version
|
|
53
|
+
# Edit: lib/your_gem/version.rb
|
|
54
|
+
VERSION = "1.2.3"
|
|
55
|
+
|
|
56
|
+
# 2. Update changelog
|
|
57
|
+
# Edit: CHANGELOG.md
|
|
58
|
+
|
|
59
|
+
# 3. Commit
|
|
60
|
+
git add lib/your_gem/version.rb CHANGELOG.md
|
|
61
|
+
git commit -m "Bump version to 1.2.3"
|
|
62
|
+
git push
|
|
63
|
+
|
|
64
|
+
# 4. Tag and push
|
|
65
|
+
git tag -a v1.2.3 -m "Release v1.2.3"
|
|
66
|
+
git push origin v1.2.3
|
|
67
|
+
|
|
68
|
+
# 5. Monitor
|
|
69
|
+
# https://github.com/USERNAME/REPO/actions
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Quick Commands
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Create release script: bin/release.sh
|
|
76
|
+
#!/bin/bash
|
|
77
|
+
VERSION=$1
|
|
78
|
+
git tag -a v${VERSION} -m "Release v${VERSION}"
|
|
79
|
+
git push origin v${VERSION}
|
|
80
|
+
echo "✅ Releasing v${VERSION} - check GitHub Actions"
|
|
81
|
+
|
|
82
|
+
# Use it:
|
|
83
|
+
chmod +x bin/release.sh
|
|
84
|
+
./bin/release.sh 1.2.3
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Common Issues
|
|
88
|
+
|
|
89
|
+
**Access Denied?**
|
|
90
|
+
- Check `RUBYGEMS_API_KEY` secret exists
|
|
91
|
+
- Verify API key has "Push rubygems" scope
|
|
92
|
+
|
|
93
|
+
**Version Mismatch?**
|
|
94
|
+
- Update `lib/your_gem/version.rb`
|
|
95
|
+
- Delete tag: `git tag -d v1.2.3 && git push origin :refs/tags/v1.2.3`
|
|
96
|
+
|
|
97
|
+
**Workflow Not Running?**
|
|
98
|
+
- Verify `.github/workflows/release.yml` exists
|
|
99
|
+
- Check tag format: `v1.2.3` (with v prefix)
|
|
100
|
+
|
|
101
|
+
## Links
|
|
102
|
+
|
|
103
|
+
- **Full Guide:** [GEM_RELEASE_GUIDE.md](GEM_RELEASE_GUIDE.md)
|
|
104
|
+
- **Your Actions:** https://github.com/USERNAME/REPO/actions
|
|
105
|
+
- **RubyGems:** https://rubygems.org/profile/edit
|
|
106
|
+
- **GitHub Secrets:** https://github.com/USERNAME/REPO/settings/secrets/actions
|
data/docs/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
This directory contains internal development documentation for the ollama-client gem.
|
|
4
4
|
|
|
5
|
+
## Quick Links
|
|
6
|
+
|
|
7
|
+
- 🚀 **[Quick Release Reference](QUICK_RELEASE.md)** - Fast release checklist
|
|
8
|
+
- 📘 **[Complete Release Guide](GEM_RELEASE_GUIDE.md)** - Full automation setup (794 lines)
|
|
9
|
+
|
|
5
10
|
## Contents
|
|
6
11
|
|
|
7
12
|
### Design Documentation
|
|
@@ -17,6 +22,7 @@ This directory contains internal development documentation for the ollama-client
|
|
|
17
22
|
|
|
18
23
|
### CI/Automation
|
|
19
24
|
- **[CLOUD.md](CLOUD.md)** - Cloud agent guide for automated testing and fixes
|
|
25
|
+
- **[GEM_RELEASE_GUIDE.md](GEM_RELEASE_GUIDE.md)** - Complete guide for automated gem releases via GitHub Actions and git tags
|
|
20
26
|
|
|
21
27
|
## For Users
|
|
22
28
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# RubyGems OTP Setup for Automated Releases
|
|
2
|
+
|
|
3
|
+
This guide explains how to configure OTP (One-Time Password) authentication for automated gem releases with MFA enabled.
|
|
4
|
+
|
|
5
|
+
## Why This Is Needed
|
|
6
|
+
|
|
7
|
+
When `rubygems_mfa_required = "true"` is set in your gemspec, RubyGems requires MFA verification for all gem pushes, including automated CI/CD releases. This guide shows how to configure GitHub Actions to generate OTP codes automatically.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- RubyGems account with MFA enabled
|
|
12
|
+
- Admin access to your GitHub repository (to add secrets)
|
|
13
|
+
- Authenticator app (Google Authenticator, Authy, 1Password, etc.)
|
|
14
|
+
|
|
15
|
+
## Step 1: Get Your RubyGems OTP Secret
|
|
16
|
+
|
|
17
|
+
### Option A: From Existing MFA Setup
|
|
18
|
+
|
|
19
|
+
If you already have MFA enabled on RubyGems:
|
|
20
|
+
|
|
21
|
+
1. **Log in to RubyGems.org**
|
|
22
|
+
2. **Go to Edit Profile** → **Multi-factor Authentication**
|
|
23
|
+
3. **Click "Show QR Code"** or **"Regenerate Recovery Codes"**
|
|
24
|
+
4. When you see the QR code, look for the **secret key** (usually shown below the QR code)
|
|
25
|
+
5. Copy the secret key (it's a long alphanumeric string like `JBSWY3DPEHPK3PXP`)
|
|
26
|
+
|
|
27
|
+
### Option B: Enable MFA Fresh
|
|
28
|
+
|
|
29
|
+
If you're setting up MFA for the first time:
|
|
30
|
+
|
|
31
|
+
1. **Log in to RubyGems.org**
|
|
32
|
+
2. **Go to Edit Profile** → **Multi-factor Authentication**
|
|
33
|
+
3. **Click "Enable MFA"**
|
|
34
|
+
4. You'll see a QR code and a **secret key** below it
|
|
35
|
+
5. **Copy the secret key** before scanning the QR code
|
|
36
|
+
6. Scan the QR code with your authenticator app
|
|
37
|
+
7. Enter the 6-digit code to verify
|
|
38
|
+
|
|
39
|
+
**⚠️ IMPORTANT:** Save the secret key securely. You'll need it for GitHub Actions.
|
|
40
|
+
|
|
41
|
+
### What the Secret Looks Like
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Example: JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP
|
|
45
|
+
- All uppercase letters and numbers
|
|
46
|
+
- Typically 32 characters long
|
|
47
|
+
- Base32 encoded
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Step 2: Add Secrets to GitHub
|
|
51
|
+
|
|
52
|
+
### 2.1: Add API Key Secret
|
|
53
|
+
|
|
54
|
+
1. Go to your GitHub repository
|
|
55
|
+
2. Click **Settings** → **Secrets and variables** → **Actions**
|
|
56
|
+
3. Click **New repository secret**
|
|
57
|
+
4. Name: `RUBYGEMS_API_KEY`
|
|
58
|
+
5. Value: Your RubyGems API key (get from https://rubygems.org/profile/api_keys)
|
|
59
|
+
6. Click **Add secret**
|
|
60
|
+
|
|
61
|
+
### 2.2: Add OTP Secret
|
|
62
|
+
|
|
63
|
+
1. Still in **Secrets and variables** → **Actions**
|
|
64
|
+
2. Click **New repository secret**
|
|
65
|
+
3. Name: `RUBYGEMS_OTP_SECRET`
|
|
66
|
+
4. Value: Your OTP secret key (from Step 1)
|
|
67
|
+
5. Click **Add secret**
|
|
68
|
+
|
|
69
|
+
## Step 3: Verify GitHub Secrets
|
|
70
|
+
|
|
71
|
+
Your repository should now have two secrets:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
✅ RUBYGEMS_API_KEY
|
|
75
|
+
✅ RUBYGEMS_OTP_SECRET
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Step 4: Test the Setup
|
|
79
|
+
|
|
80
|
+
### Test Locally First
|
|
81
|
+
|
|
82
|
+
You can test OTP generation locally before pushing:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Install rotp gem
|
|
86
|
+
gem install rotp
|
|
87
|
+
|
|
88
|
+
# Generate an OTP code (replace with your actual secret)
|
|
89
|
+
ruby -r rotp -e "puts ROTP::TOTP.new('YOUR_SECRET_HERE').now"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This should output a 6-digit code that matches your authenticator app.
|
|
93
|
+
|
|
94
|
+
### Test in CI/CD
|
|
95
|
+
|
|
96
|
+
1. Bump your gem version in `lib/ollama/version.rb`
|
|
97
|
+
2. Commit and push
|
|
98
|
+
3. Create and push a matching tag:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git tag v0.2.4
|
|
102
|
+
git push origin v0.2.4
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
4. Check GitHub Actions to see the release workflow run
|
|
106
|
+
|
|
107
|
+
## How It Works
|
|
108
|
+
|
|
109
|
+
The GitHub Actions workflow:
|
|
110
|
+
|
|
111
|
+
1. **Installs `rotp` gem** - Ruby library for generating TOTP codes
|
|
112
|
+
2. **Generates OTP code** - Uses your secret to generate a 6-digit code
|
|
113
|
+
3. **Pushes gem** - Runs `gem push --otp <code>` with the generated code
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
- name: Install OTP generator
|
|
117
|
+
run: gem install rotp
|
|
118
|
+
|
|
119
|
+
- name: Publish gem with OTP
|
|
120
|
+
env:
|
|
121
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
122
|
+
RUBYGEMS_OTP_SECRET: ${{ secrets.RUBYGEMS_OTP_SECRET }}
|
|
123
|
+
run: |
|
|
124
|
+
otp_code=$(ruby -r rotp -e "puts ROTP::TOTP.new(ENV['RUBYGEMS_OTP_SECRET']).now")
|
|
125
|
+
gem push "ollama-client-${gem_version}.gem" --otp "$otp_code"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Troubleshooting
|
|
129
|
+
|
|
130
|
+
### "Invalid OTP code" Error
|
|
131
|
+
|
|
132
|
+
**Cause:** Time synchronization issue or wrong secret
|
|
133
|
+
|
|
134
|
+
**Solutions:**
|
|
135
|
+
1. Verify your secret is correct
|
|
136
|
+
2. Check server time is synchronized:
|
|
137
|
+
```bash
|
|
138
|
+
date
|
|
139
|
+
```
|
|
140
|
+
3. Try regenerating the secret on RubyGems
|
|
141
|
+
|
|
142
|
+
### "OTP code expired" Error
|
|
143
|
+
|
|
144
|
+
**Cause:** Code expired before being used (they last 30 seconds)
|
|
145
|
+
|
|
146
|
+
**Solution:** The workflow generates the code immediately before use, so this shouldn't happen. If it does, there may be a network delay. The workflow will need to retry.
|
|
147
|
+
|
|
148
|
+
### "unauthorized" Error
|
|
149
|
+
|
|
150
|
+
**Cause:** API key is invalid or doesn't have push permissions
|
|
151
|
+
|
|
152
|
+
**Solutions:**
|
|
153
|
+
1. Verify `RUBYGEMS_API_KEY` is correct
|
|
154
|
+
2. Create a new API key with push permissions
|
|
155
|
+
3. Update the secret in GitHub
|
|
156
|
+
|
|
157
|
+
### Missing Secrets
|
|
158
|
+
|
|
159
|
+
**Error:** `RUBYGEMS_OTP_SECRET not found`
|
|
160
|
+
|
|
161
|
+
**Solution:** Ensure both secrets are added to GitHub (Settings → Secrets and variables → Actions)
|
|
162
|
+
|
|
163
|
+
## Security Considerations
|
|
164
|
+
|
|
165
|
+
### ✅ Pros
|
|
166
|
+
- MFA protection on gem releases
|
|
167
|
+
- OTP secret encrypted in GitHub Secrets
|
|
168
|
+
- No manual intervention needed
|
|
169
|
+
- Audit trail in GitHub Actions
|
|
170
|
+
|
|
171
|
+
### ⚠️ Considerations
|
|
172
|
+
- OTP secret stored in GitHub increases attack surface
|
|
173
|
+
- If GitHub is compromised, attacker has both API key and OTP secret
|
|
174
|
+
- Alternative: Remove `rubygems_mfa_required` and rely on account-level MFA
|
|
175
|
+
|
|
176
|
+
### Best Practices
|
|
177
|
+
|
|
178
|
+
1. **Rotate API keys** regularly
|
|
179
|
+
2. **Use scoped API keys** (push-only if possible)
|
|
180
|
+
3. **Enable GitHub branch protection** on main branch
|
|
181
|
+
4. **Require PR reviews** for sensitive changes
|
|
182
|
+
5. **Monitor release logs** for unauthorized pushes
|
|
183
|
+
6. **Regenerate OTP secret** if you suspect compromise
|
|
184
|
+
|
|
185
|
+
## Alternative: Account-Level MFA Only
|
|
186
|
+
|
|
187
|
+
If you want simpler automation:
|
|
188
|
+
|
|
189
|
+
1. Remove `rubygems_mfa_required` from gemspec
|
|
190
|
+
2. Keep account-level MFA enabled on RubyGems
|
|
191
|
+
3. Use API key only (no OTP needed)
|
|
192
|
+
|
|
193
|
+
This is the recommended approach for most projects as it provides good security with less complexity.
|
|
194
|
+
|
|
195
|
+
## Reference
|
|
196
|
+
|
|
197
|
+
- [RubyGems MFA Documentation](https://guides.rubygems.org/setting-up-multifactor-authentication/)
|
|
198
|
+
- [ROTP Gem Documentation](https://github.com/mdp/rotp)
|
|
199
|
+
- [GitHub Encrypted Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
|
data/lib/ollama/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ollama-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shubham Taywade
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-01-18 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: bigdecimal
|
|
@@ -59,9 +60,14 @@ files:
|
|
|
59
60
|
- docs/CLOUD.md
|
|
60
61
|
- docs/CONSOLE_IMPROVEMENTS.md
|
|
61
62
|
- docs/FEATURES_ADDED.md
|
|
63
|
+
- docs/GEM_RELEASE_GUIDE.md
|
|
64
|
+
- docs/GET_RUBYGEMS_SECRET.md
|
|
62
65
|
- docs/HANDLERS_ANALYSIS.md
|
|
63
66
|
- docs/PRODUCTION_FIXES.md
|
|
67
|
+
- docs/QUICK_OTP_SETUP.md
|
|
68
|
+
- docs/QUICK_RELEASE.md
|
|
64
69
|
- docs/README.md
|
|
70
|
+
- docs/RUBYGEMS_OTP_SETUP.md
|
|
65
71
|
- docs/SCHEMA_FIXES.md
|
|
66
72
|
- docs/TESTING.md
|
|
67
73
|
- docs/TEST_UPDATES.md
|
|
@@ -141,6 +147,7 @@ metadata:
|
|
|
141
147
|
source_code_uri: https://github.com/shubhamtaywade82/ollama-client
|
|
142
148
|
changelog_uri: https://github.com/shubhamtaywade82/ollama-client/blob/main/CHANGELOG.md
|
|
143
149
|
rubygems_mfa_required: 'true'
|
|
150
|
+
post_install_message:
|
|
144
151
|
rdoc_options: []
|
|
145
152
|
require_paths:
|
|
146
153
|
- lib
|
|
@@ -155,7 +162,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
155
162
|
- !ruby/object:Gem::Version
|
|
156
163
|
version: '0'
|
|
157
164
|
requirements: []
|
|
158
|
-
rubygems_version:
|
|
165
|
+
rubygems_version: 3.5.11
|
|
166
|
+
signing_key:
|
|
159
167
|
specification_version: 4
|
|
160
168
|
summary: An agent-first Ruby client for Ollama (planner/executor + safe tool loops)
|
|
161
169
|
test_files: []
|