ollama-client 0.2.2 → 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/CHANGELOG.md +8 -0
- data/README.md +7 -1
- data/docs/CLOUD.md +29 -0
- data/docs/CONSOLE_IMPROVEMENTS.md +256 -0
- 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 +43 -0
- data/docs/RUBYGEMS_OTP_SETUP.md +199 -0
- data/docs/SCHEMA_FIXES.md +147 -0
- data/docs/TEST_UPDATES.md +107 -0
- data/examples/README.md +92 -0
- data/examples/advanced_complex_schemas.rb +6 -3
- data/examples/advanced_multi_step_agent.rb +2 -1
- data/examples/chat_console.rb +12 -3
- data/examples/complete_workflow.rb +14 -4
- data/examples/dhan_console.rb +103 -8
- data/examples/dhanhq/agents/technical_analysis_agent.rb +6 -1
- data/examples/dhanhq/schemas/agent_schemas.rb +2 -2
- data/examples/dhanhq_agent.rb +23 -13
- data/examples/dhanhq_tools.rb +311 -246
- data/examples/multi_step_agent_with_external_data.rb +368 -0
- data/{test_dhanhq_tool_calling.rb → examples/test_dhanhq_tool_calling.rb} +99 -6
- data/lib/ollama/agent/executor.rb +30 -30
- data/lib/ollama/client.rb +73 -80
- data/lib/ollama/dto.rb +7 -7
- data/lib/ollama/options.rb +17 -9
- data/lib/ollama/response.rb +4 -6
- data/lib/ollama/tool/function/parameters.rb +1 -0
- data/lib/ollama/version.rb +1 -1
- metadata +24 -9
- /data/{FEATURES_ADDED.md → docs/FEATURES_ADDED.md} +0 -0
- /data/{HANDLERS_ANALYSIS.md → docs/HANDLERS_ANALYSIS.md} +0 -0
- /data/{PRODUCTION_FIXES.md → docs/PRODUCTION_FIXES.md} +0 -0
- /data/{TESTING.md → docs/TESTING.md} +0 -0
- /data/{test_tool_calling.rb → examples/test_tool_calling.rb} +0 -0
|
@@ -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
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Internal Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains internal development documentation for the ollama-client gem.
|
|
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
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
### Design Documentation
|
|
13
|
+
- **[HANDLERS_ANALYSIS.md](HANDLERS_ANALYSIS.md)** - Analysis of handler architecture decisions (why we didn't adopt ollama-ruby's handler pattern)
|
|
14
|
+
- **[FEATURES_ADDED.md](FEATURES_ADDED.md)** - Features integrated from ollama-ruby that align with our agent-first philosophy
|
|
15
|
+
- **[PRODUCTION_FIXES.md](PRODUCTION_FIXES.md)** - Production-ready fixes for hybrid agents (JSON parsing, retry policy, etc.)
|
|
16
|
+
- **[SCHEMA_FIXES.md](SCHEMA_FIXES.md)** - Schema validation fixes and best practices for numeric constraints
|
|
17
|
+
- **[CONSOLE_IMPROVEMENTS.md](CONSOLE_IMPROVEMENTS.md)** - Interactive console UX improvements (thinking indicators, formatted tool results)
|
|
18
|
+
|
|
19
|
+
### Testing Documentation
|
|
20
|
+
- **[TESTING.md](TESTING.md)** - Testing guide and examples
|
|
21
|
+
- **[TEST_UPDATES.md](TEST_UPDATES.md)** - Recent test updates for DhanHQ tool calling enhancements
|
|
22
|
+
|
|
23
|
+
### CI/Automation
|
|
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
|
|
26
|
+
|
|
27
|
+
## For Users
|
|
28
|
+
|
|
29
|
+
If you're looking for user-facing documentation, see:
|
|
30
|
+
- [Main README](../README.md) - Getting started, API reference, examples
|
|
31
|
+
- [CHANGELOG](../CHANGELOG.md) - Version history and changes
|
|
32
|
+
- [CONTRIBUTING](../CONTRIBUTING.md) - How to contribute
|
|
33
|
+
- [Examples](../examples/) - Working code examples
|
|
34
|
+
|
|
35
|
+
## For Contributors
|
|
36
|
+
|
|
37
|
+
These internal docs help maintainers understand:
|
|
38
|
+
- **Why** certain design decisions were made
|
|
39
|
+
- **What** features have been added and why
|
|
40
|
+
- **How** to test and maintain the codebase
|
|
41
|
+
- **Where** production fixes were applied
|
|
42
|
+
|
|
43
|
+
They are not intended for end users and can be safely ignored when using the gem.
|
|
@@ -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)
|