-
Notifications
You must be signed in to change notification settings - Fork 0
File Swapping
File swapping lets you temporarily replace project files with development-specific versions stored in your vault. Perfect for local overrides that shouldn't be committed.
Only change structure (move, delete, rename) when vault is in swapped-out state!
Unlike guarding (permanent move to vault), swapping is temporary:
NORMAL STATE AFTER "SWAP IN"
project/ project/
└── application.yml (original) ───► └── application.yml (from vault)
vault/swap/ vault/swap/
└── application.yml (dev version) └── application.yml.rsenv_original (backup)
Key difference from guarding:
- Guard: Permanent. File lives in vault, symlink in project.
- Swap: Temporary. Original backed up, vault version copied to project.
First, move your development override files to the vault:
rsenv swap init config/application.ymlThis:
- Moves
config/application.ymlfrom project tovault/swap/config/application.yml - Project file is removed (now only exists in vault)
Use swap in to put the vault version back into the project.
Replace project files with vault versions:
rsenv swap in config/application.ymlThis:
- Creates sentinel in vault:
application.yml.{hostname}.rsenv_active(copy of vault content) - Moves project file to vault backup:
vault/swap/application.yml.rsenv_original - Moves vault override to project (vault file is removed)
Restore original files:
rsenv swap out config/application.ymlThis:
- Moves project file back to vault (preserving your changes)
- Restores backup from vault:
vault/swap/application.yml.rsenv_original→ project - Removes sentinel from vault
rsenv swap statusOutput:
Swap status:
config/application.yml: IN (swapped on myhost)
config/database.yml: OUT (original in project)
Swap out all files in the current project's vault:
rsenv swap outBatch restore across all projects:
rsenv swap out --globalOr with a custom vault base directory:
rsenv swap out --global --vault-base ~/my-vaultsUseful before:
- Committing changes
- Switching git branches
- Sharing code with others
See which vaults have active swaps:
rsenv swap status --globalOutput:
Active Swaps:
myproject-a1b2c3d4:
config.yml [in (myhost)]
another-proj-d5e6f7g8:
settings.json [in (myhost)]
For shell scripts, use --silent to get exit code only:
# Project-level: 0=clean, 1=dirty, 2=unmanaged
rsenv swap status --silent
case $? in
0) echo "Clean — nothing swapped in" ;;
1) echo "Dirty — files swapped in" ;;
2) echo "Not an rsenv-managed project" ;;
esac
# Global: 0=clean, 1=dirty (across all vaults)
if rsenv swap status --global --silent; then
echo "All clean"
else
echo "Some files are swapped in"
fiUseful for:
- CI/CD pipelines to detect unwanted swaps
- Pre-commit hooks
- Shell prompts (project state indicator)
- Automated cleanup scripts
Remove files from swap management entirely:
rsenv swap delete config/application.ymlThis removes from the vault:
- The override file (
vault/swap/config/application.yml) - Any backup file (
config.yml.rsenv_original)
Project files are not touched.
Safety: Refuses to delete if the file is currently swapped in. Swap out first.
All-or-nothing: If any file fails validation, no files are deleted.
# vault/swap/config/application.yml (development version)
server:
port: 8080
database:
host: localhost
name: myapp_dev
logging:
level: DEBUG# project/config/application.yml (production version)
server:
port: 443
database:
host: prod-db.internal
name: myapp
logging:
level: WARN# Create dev version with mock services
rsenv swap init config/services.yml
# Edit vault version
vim $RSENV_VAULT/swap/config/services.yml
# Change: payment_api: https://api.stripe.com
# To: payment_api: http://localhost:8081/mock# Your local database password
rsenv swap init .env.local
# Swap in when developing
rsenv swap in .env.local
# Swap out before commits
rsenv swap out .env.localSwap state is tracked per hostname, preventing conflicts when the same vault is accessed from multiple machines (e.g., shared NFS home directories).
When you swap in, rsenv creates:
application.yml.myhost.rsenv_active
If someone else swaps in from a different host:
Error: File already swapped on host 'otherhost'
To swap in when another host has the file swapped:
# Option 1: Swap out on the other host first
# (on otherhost) rsenv swap out config/application.yml
# Option 2: Manually remove the sentinel from vault
rm $RSENV_VAULT/swap/config/application.yml.otherhost.rsenv_active
rsenv swap in config/application.ymlrsenv tracks swap state via an environment variable in your vault's dot.envrc:
export RSENV_SWAPPED=1-
swap in: Adds
RSENV_SWAPPED=1(idempotent - only added once) - swap out: Removes marker only when ALL files are swapped out
Detect if development overrides are active:
# In your .envrc or scripts
if [[ -n "$RSENV_SWAPPED" ]]; then
echo "Warning: Development overrides active"
fi# Fail CI if files are swapped
if [[ "$RSENV_SWAPPED" == "1" ]]; then
echo "Error: Cannot build with swapped files"
exit 1
fiDotfiles (.gitignore, .env, .hidden/, etc.) in swap directories are automatically neutralized in the vault to prevent them from having active effects.
Dotfiles in your vault could interfere with vault operations:
- A
.gitignorewould affect what git tracks in the vault - A
.envrccould load unexpected environment variables - Hidden directories might be invisible during maintenance
rsenv uses a dot. prefix to neutralize dotfiles:
| In Project | In Vault |
|---|---|
.gitignore |
dot.gitignore |
.envrc |
dot.envrc |
.hidden/config |
dot.hidden/config |
.a/.b/.c |
dot.a/dot.b/dot.c |
| Operation | Dotfiles in vault |
|---|---|
swap init |
Neutralized (dot.xxx) |
swap out |
Neutralized (dot.xxx) |
swap in |
Restored (.xxx in project) |
guard add |
Neutralized (dot.xxx) |
swap in refuses if a bare dotfile (e.g., .gitignore without dot.gitignore) exists in the vault. This prevents conflicts. Delete or rename the conflicting file first.
# Start work
cd ~/myproject
rsenv swap in config/application.yml
# Develop with local config...
# Before commit
rsenv swap out config/application.yml
git add -A
git commit -m "Feature complete"In your CI pipeline, ensure nothing is swapped:
# In CI script - check RSENV_SWAPPED marker
if [[ "$RSENV_SWAPPED" == "1" ]]; then
echo "Error: Files are swapped in"
exit 1
fi# Before switching branches
rsenv swap out --global
git checkout feature-branch
# After switching, swap back in if needed
rsenv swap in config/application.ymlvault/swap/
└── config/
└── application.yml # Your development version
Only change structure (move, delete, rename) when vault is in swapped-out state!
project/config/
└── application.yml # Vault's version (moved from vault)
vault/swap/config/
├── application.yml.rsenv_original # Backup of project's original
└── application.yml.myhost.rsenv_active # Sentinel (copy of vault content)
Note: All swap artifacts (.rsenv_original, .rsenv_active) live in the vault, not the project.
Never commit swapped-in files. Add a pre-commit hook:
#!/bin/bash
# .git/hooks/pre-commit
if rsenv swap status --quiet 2>/dev/null; then
echo "Error: Files are swapped in. Run 'rsenv swap out' first."
exit 1
fi# Add alias
alias swapout='rsenv swap out --global'
# Before any commit
swapout && git commitCreate a README in your swap directory:
cat > $RSENV_VAULT/swap/README.md << 'EOF'
# Swap Files
## config/application.yml
Local development config:
- Uses localhost database
- Debug logging enabled
- Mock payment API
## .env.local
Local credentials (not in git)
EOFWhen the original changes, update your swap version:
# See what changed
diff project/config/application.yml $RSENV_VAULT/swap/config/application.yml
# Update swap file
vim $RSENV_VAULT/swap/config/application.yml# Check status
rsenv swap status
# Remove the sentinel from vault and swap in
rm $RSENV_VAULT/swap/file.yml.otherhost.rsenv_active
rsenv swap in file.ymlThe original is backed up with .rsenv_original suffix:
# Find backup
ls -la *.rsenv_original
# Manual restore
mv file.yml.rsenv_original file.yml
rm file.yml.*.rsenv_active# Initialize it first
rsenv swap init path/to/file.yml# Swap out entire vault (default)
rsenv swap out
# Swap out specific files
rsenv swap out file1.yml file2.yml file3.yml
# Or swap out all vaults
rsenv swap out --global| Aspect | Guard | Swap |
|---|---|---|
| Purpose | Protect secrets | Development overrides |
| Duration | Permanent | Temporary |
| Mechanism | Symlink | File copy |
| Original location | Vault (always) | Project (when swapped out) |
| Git sees | Symlink | Real file |
| Use case | API keys, certs | Local configs, mock URLs |
- Core Concepts - Understanding vault categories
- Vault Management - File guarding (permanent)
- Configuration - rsenv settings
rsenv Documentation