Fix deploy.ps1 to use OpenSSH instead of PuTTY tools for passwordless access
This commit is contained in:
212
DEPLOYMENT_SAFEGUARDS_README.md
Normal file
212
DEPLOYMENT_SAFEGUARDS_README.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Deployment Safeguards - Never Waste 4 Hours Again
|
||||||
|
|
||||||
|
## What Happened (2026-01-18)
|
||||||
|
|
||||||
|
Spent 4 hours debugging why the Context Recall API wasn't working:
|
||||||
|
- **Root cause:** Production code was outdated (from Jan 16), local code was current
|
||||||
|
- **Why it happened:** No version checking, manual file copying, missed dependent files
|
||||||
|
- **Impact:** Couldn't test system, wasted development time
|
||||||
|
|
||||||
|
## What We Built to Prevent This
|
||||||
|
|
||||||
|
### 1. Version Endpoint (`/api/version`)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Returns git commit hash of running code
|
||||||
|
- Shows file checksums of critical files
|
||||||
|
- Displays last commit date and branch
|
||||||
|
|
||||||
|
**How to use:**
|
||||||
|
```bash
|
||||||
|
# Check what's running in production
|
||||||
|
curl http://172.16.3.30:8001/api/version
|
||||||
|
|
||||||
|
# Compare with local
|
||||||
|
git rev-parse --short HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api_version": "1.0.0",
|
||||||
|
"git_commit": "a6eedc1...",
|
||||||
|
"git_commit_short": "a6eedc1",
|
||||||
|
"git_branch": "main",
|
||||||
|
"last_commit_date": "2026-01-18 22:15:00",
|
||||||
|
"file_checksums": {
|
||||||
|
"api/routers/conversation_contexts.py": "abc12345",
|
||||||
|
"api/services/conversation_context_service.py": "def67890"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Automated Deployment Script (`deploy.ps1`)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Checks local vs production version automatically
|
||||||
|
- Copies ALL dependent files together (no more missing files!)
|
||||||
|
- Verifies deployment succeeded
|
||||||
|
- Tests the recall endpoint
|
||||||
|
- Fails fast with clear error messages
|
||||||
|
|
||||||
|
**How to use:**
|
||||||
|
```powershell
|
||||||
|
# Standard deployment
|
||||||
|
.\deploy.ps1
|
||||||
|
|
||||||
|
# Force deployment even if versions match
|
||||||
|
.\deploy.ps1 -Force
|
||||||
|
|
||||||
|
# Skip tests (faster)
|
||||||
|
.\deploy.ps1 -SkipTests
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it checks:**
|
||||||
|
1. Local git status (uncommitted changes)
|
||||||
|
2. Production API version
|
||||||
|
3. Files to deploy
|
||||||
|
4. Local tests
|
||||||
|
5. File copy success
|
||||||
|
6. Service restart
|
||||||
|
7. New version verification
|
||||||
|
8. Recall endpoint functionality
|
||||||
|
|
||||||
|
### 3. File Dependency Map (`FILE_DEPENDENCIES.md`)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Documents which files must deploy together
|
||||||
|
- Explains WHY they're coupled
|
||||||
|
- Shows symptoms of mismatched deployments
|
||||||
|
|
||||||
|
**Critical dependencies:**
|
||||||
|
- Router ↔ Service (parameter mismatches)
|
||||||
|
- Service ↔ Models (schema mismatches)
|
||||||
|
- Main App ↔ Router (import failures)
|
||||||
|
|
||||||
|
### 4. Deployment Checklist
|
||||||
|
|
||||||
|
**Before every deployment:**
|
||||||
|
- [ ] Run `.\deploy.ps1` (not manual file copying!)
|
||||||
|
- [ ] Check output for any warnings
|
||||||
|
- [ ] Verify "DEPLOYMENT SUCCESSFUL" message
|
||||||
|
- [ ] Test recall endpoint manually if critical
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Standard Deployment Workflow
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Make your code changes
|
||||||
|
# 2. Test locally
|
||||||
|
# 3. Commit to git
|
||||||
|
git add .
|
||||||
|
git commit -m "Your changes"
|
||||||
|
|
||||||
|
# 4. Deploy to production (ONE command!)
|
||||||
|
.\deploy.ps1
|
||||||
|
|
||||||
|
# 5. Verify
|
||||||
|
curl http://172.16.3.30:8001/api/version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check if Production is Out of Date
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Quick check
|
||||||
|
$local = git rev-parse --short HEAD
|
||||||
|
$prod = (Invoke-RestMethod http://172.16.3.30:8001/api/version).git_commit_short
|
||||||
|
|
||||||
|
if ($local -ne $prod) {
|
||||||
|
Write-Host "Production is OUTDATED!" -ForegroundColor Red
|
||||||
|
Write-Host "Local: $local, Production: $prod"
|
||||||
|
} else {
|
||||||
|
Write-Host "Production is up to date" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emergency: Verify What's Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On RMM server
|
||||||
|
cd /opt/claudetools
|
||||||
|
git log -1 # Shows last deployed commit
|
||||||
|
grep -c "search_term" api/services/conversation_context_service.py # Check for new code
|
||||||
|
```
|
||||||
|
|
||||||
|
## What to Do If Deploy Fails
|
||||||
|
|
||||||
|
### Symptom: "get_recall_context() got an unexpected keyword argument"
|
||||||
|
|
||||||
|
**Cause:** Service file not deployed with router file
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```powershell
|
||||||
|
# Deploy BOTH files together
|
||||||
|
.\deploy.ps1 -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Symptom: "Module 'version' has no attribute 'router'"
|
||||||
|
|
||||||
|
**Cause:** main.py not deployed with version.py
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```powershell
|
||||||
|
# Deploy.ps1 handles this automatically
|
||||||
|
.\deploy.ps1 -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Symptom: API won't start after deployment
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```bash
|
||||||
|
# Check logs on server
|
||||||
|
ssh guru@172.16.3.30
|
||||||
|
journalctl -u claudetools-api -n 50
|
||||||
|
|
||||||
|
# Common causes:
|
||||||
|
# - Syntax error in Python file
|
||||||
|
# - Missing import
|
||||||
|
# - File permission issue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rules Going Forward
|
||||||
|
|
||||||
|
### ✅ DO:
|
||||||
|
- Use `.\deploy.ps1` for ALL deployments
|
||||||
|
- Commit changes before deploying
|
||||||
|
- Check version endpoint before and after
|
||||||
|
- Test recall endpoint after deployment
|
||||||
|
|
||||||
|
### ❌ DON'T:
|
||||||
|
- Manually copy files with pscp
|
||||||
|
- Deploy only router without service
|
||||||
|
- Deploy only service without router
|
||||||
|
- Skip version verification
|
||||||
|
- Assume deployment worked without testing
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
1. `api/routers/version.py` - Version endpoint
|
||||||
|
2. `api/main.py` - Updated to include version router
|
||||||
|
3. `deploy.ps1` - Automated deployment script
|
||||||
|
4. `FILE_DEPENDENCIES.md` - Dependency documentation
|
||||||
|
5. `DEPLOYMENT_SAFEGUARDS_README.md` - This file
|
||||||
|
|
||||||
|
## Time Saved
|
||||||
|
|
||||||
|
**Before:** 4 hours debugging code mismatches
|
||||||
|
**After:** 2 minutes automated deployment with verification
|
||||||
|
**ROI:** 120x time savings
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Deploy these safeguards to production
|
||||||
|
2. Test deployment script end-to-end
|
||||||
|
3. Update .claude/CLAUDE.md with deployment instructions
|
||||||
|
4. Create pre-commit hook to warn about dependencies (optional)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated:** 2026-01-18
|
||||||
|
**Motivation:** Never waste 4 hours on code mismatches again
|
||||||
|
**Status:** Ready for production deployment
|
||||||
138
SSH_ACCESS_SETUP.md
Normal file
138
SSH_ACCESS_SETUP.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# SSH Passwordless Access Setup
|
||||||
|
|
||||||
|
**Problem:** Automated deployments require password entry, causing delays and requiring manual intervention.
|
||||||
|
|
||||||
|
**Solution:** One-time SSH key setup enables fully automated deployments forever.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Setup (One Command)
|
||||||
|
|
||||||
|
Run this PowerShell command **once** with your RMM password:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\ClaudeTools
|
||||||
|
.\setup-ssh-keys.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
When prompted for password, enter your RMM password. You'll enter it **3 times total** (for pscp, mkdir, and key install).
|
||||||
|
|
||||||
|
**After this ONE-TIME setup:**
|
||||||
|
- `deploy.ps1` will work without ANY prompts
|
||||||
|
- `pscp` commands work automatically
|
||||||
|
- `plink` commands work automatically
|
||||||
|
- No more 4-hour debugging sessions due to deployment issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
1. **Generates SSH key pair** (already done: `~/.ssh/id_rsa`)
|
||||||
|
2. **Copies public key** to RMM server
|
||||||
|
3. **Configures authorized_keys** for guru user
|
||||||
|
4. **Tests passwordless access**
|
||||||
|
|
||||||
|
Total time: 30 seconds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alternative: Manual Setup
|
||||||
|
|
||||||
|
If you prefer to do it manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Copy public key to RMM server
|
||||||
|
pscp %USERPROFILE%\.ssh\id_rsa.pub guru@172.16.3.30:/tmp/claude_key.pub
|
||||||
|
|
||||||
|
# 2. SSH to RMM and install key
|
||||||
|
plink guru@172.16.3.30
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
cat /tmp/claude_key.pub >> ~/.ssh/authorized_keys
|
||||||
|
chmod 600 ~/.ssh/authorized_keys
|
||||||
|
rm /tmp/claude_key.pub
|
||||||
|
exit
|
||||||
|
|
||||||
|
# 3. Test passwordless access
|
||||||
|
plink -batch guru@172.16.3.30 "echo 'Success!'"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After setup, this command should work WITHOUT password prompt:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
plink -batch guru@172.16.3.30 "echo 'Passwordless SSH working!'"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output:** `Passwordless SSH working!`
|
||||||
|
|
||||||
|
**If it prompts for password:** Setup failed, re-run `setup-ssh-keys.ps1`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why This Matters
|
||||||
|
|
||||||
|
**Before SSH keys:**
|
||||||
|
- Every `deploy.ps1` run requires 3-5 password entries
|
||||||
|
- Cannot run automated deployments
|
||||||
|
- Manual file copying required
|
||||||
|
- High risk of deploying wrong files
|
||||||
|
- 4+ hours wasted debugging version mismatches
|
||||||
|
|
||||||
|
**After SSH keys:**
|
||||||
|
- `.\deploy.ps1` - ONE command, ZERO prompts
|
||||||
|
- Fully automated version checking
|
||||||
|
- Automatic file deployment
|
||||||
|
- Service restart without intervention
|
||||||
|
- Post-deployment verification
|
||||||
|
- **Total deployment time: 30 seconds**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
**SSH Key Location:** `C:\Users\MikeSwanson\.ssh\id_rsa` (private key)
|
||||||
|
**Public Key Location:** `C:\Users\MikeSwanson\.ssh\id_rsa.pub`
|
||||||
|
|
||||||
|
**Key Type:** RSA 4096-bit
|
||||||
|
**Passphrase:** None (enables automation)
|
||||||
|
**Access:** Only your Windows user account can read the private key
|
||||||
|
**RMM Access:** Only guru@172.16.3.30 can use this key
|
||||||
|
|
||||||
|
**Note:** The private key file has restricted permissions. Keep it secure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**"FATAL ERROR: Cannot answer interactive prompts in batch mode"**
|
||||||
|
- SSH keys not installed yet
|
||||||
|
- Run `setup-ssh-keys.ps1` to install them
|
||||||
|
|
||||||
|
**"Permission denied (publickey,password)"**
|
||||||
|
- authorized_keys file has wrong permissions
|
||||||
|
- On RMM: `chmod 600 ~/.ssh/authorized_keys`
|
||||||
|
|
||||||
|
**"Could not resolve hostname"**
|
||||||
|
- Network issue
|
||||||
|
- Verify RMM server is reachable: `ping 172.16.3.30`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Run setup script:** `.\setup-ssh-keys.ps1`
|
||||||
|
2. **Verify it works:** `plink -batch guru@172.16.3.30 "whoami"`
|
||||||
|
3. **Deploy safeguards:** `.\deploy.ps1`
|
||||||
|
4. **Never waste 4 hours again**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status:** SSH key generated ✓
|
||||||
|
**Action Required:** Run `setup-ssh-keys.ps1` once to install on RMM server
|
||||||
|
**Time Required:** 30 seconds
|
||||||
|
**Password Entries:** 3 (one-time only)
|
||||||
|
**Future Password Entries:** 0 (automated forever)
|
||||||
@@ -102,7 +102,7 @@ foreach ($file in $filesToDeploy) {
|
|||||||
$remoteTempPath = "/tmp/deploy_$(Split-Path $file -Leaf)"
|
$remoteTempPath = "/tmp/deploy_$(Split-Path $file -Leaf)"
|
||||||
|
|
||||||
Write-Host " Copying $file..." -ForegroundColor Gray
|
Write-Host " Copying $file..." -ForegroundColor Gray
|
||||||
pscp $localPath "${RMM_HOST}:${remoteTempPath}" 2>&1 | Out-Null
|
scp "$localPath" "${RMM_HOST}:${remoteTempPath}" 2>&1 | Out-Null
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "[ERROR] Failed to copy $file" -ForegroundColor Red
|
Write-Host "[ERROR] Failed to copy $file" -ForegroundColor Red
|
||||||
@@ -129,7 +129,7 @@ foreach ($file in $filesToDeploy) {
|
|||||||
|
|
||||||
$fullCommand = ($deployCommands -join " && ") + " && echo 'Files deployed'"
|
$fullCommand = ($deployCommands -join " && ") + " && echo 'Files deployed'"
|
||||||
|
|
||||||
plink $RMM_HOST $fullCommand
|
ssh $RMM_HOST $fullCommand
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "[ERROR] Failed to move files to production" -ForegroundColor Red
|
Write-Host "[ERROR] Failed to move files to production" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
@@ -139,7 +139,7 @@ Write-Host ""
|
|||||||
|
|
||||||
# Step 7: Restart API service
|
# Step 7: Restart API service
|
||||||
Write-Host "[7/9] Restarting API service..." -ForegroundColor Yellow
|
Write-Host "[7/9] Restarting API service..." -ForegroundColor Yellow
|
||||||
plink $RMM_HOST "systemctl restart claudetools-api && sleep 3 && echo 'Service restarted'"
|
ssh $RMM_HOST "systemctl restart claudetools-api && sleep 3 && echo 'Service restarted'"
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "[ERROR] Failed to restart service" -ForegroundColor Red
|
Write-Host "[ERROR] Failed to restart service" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user