#!/bin/bash # === Bitwarden .env Sync (Advanced) === # Configuration TARGET_DIR="" SESSION_FILE="$HOME/.bw-session" DEBUG=false # Parse arguments for arg in "$@"; do case $arg in --debug) DEBUG=true ;; *) # If not a flag, treat as target directory if [[ -z "$TARGET_DIR" ]] && [[ "$arg" != --* ]]; then TARGET_DIR="$arg" fi ;; esac done # Set default target directory if not specified if [[ -z "$TARGET_DIR" ]]; then TARGET_DIR="$(pwd)" fi # Logging functions log_info() { echo "[INFO] $1" } log_debug() { if [[ "$DEBUG" == "true" ]]; then echo "[DEBUG] $1" fi } log_error() { echo "[ERROR] $1" >&2 } # Check dependencies check_dependencies() { log_debug "Checking dependencies..." if ! command -v bw &> /dev/null; then log_error "Bitwarden CLI (bw) is not installed" exit 1 fi log_debug "bw found at: $(which bw)" if ! command -v jq &> /dev/null; then log_error "jq is not installed" exit 1 fi log_debug "jq found at: $(which jq)" } # Authenticate with Bitwarden authenticate_bitwarden() { log_debug "Starting Bitwarden authentication..." # Check if we have a valid session if [[ -f "$SESSION_FILE" ]]; then log_debug "Session file found, loading session..." export BW_SESSION=$(cat "$SESSION_FILE") # Test if session is valid if bw status --session "$BW_SESSION" 2>/dev/null | jq -e '.status == "unlocked"' &>/dev/null; then log_debug "Session is valid and vault is unlocked" return 0 else log_debug "Session is invalid or vault is locked" rm -f "$SESSION_FILE" fi else log_debug "Session file does not exist" fi # Check login status STATUS=$(bw status | jq -r '.status') log_debug "Bitwarden status: $STATUS" case "$STATUS" in "unauthenticated") log_info "Not logged in to Bitwarden. Please login:" bw login if [[ $? -ne 0 ]]; then log_error "Login failed" exit 1 fi # After login, we need to unlock STATUS="locked" ;; esac # Handle locked vault if [[ "$STATUS" == "locked" ]]; then log_info "Vault is locked. Unlocking..." # Unlock and capture the session key SESSION_OUTPUT=$(bw unlock --raw) if [[ $? -ne 0 ]]; then log_error "Failed to unlock vault" exit 1 fi # Save session to file and export echo "$SESSION_OUTPUT" > "$SESSION_FILE" export BW_SESSION="$SESSION_OUTPUT" log_debug "Vault unlocked and session saved" elif [[ "$STATUS" == "unlocked" ]]; then # Vault is already unlocked but we don't have the session log_info "Vault is already unlocked but session not found. Please unlock again:" SESSION_OUTPUT=$(bw unlock --raw) if [[ $? -ne 0 ]]; then log_error "Failed to get session" exit 1 fi echo "$SESSION_OUTPUT" > "$SESSION_FILE" export BW_SESSION="$SESSION_OUTPUT" log_debug "Session retrieved and saved" fi # Final verification if ! bw status --session "$BW_SESSION" 2>/dev/null | jq -e '.status == "unlocked"' &>/dev/null; then log_error "Failed to authenticate with Bitwarden" exit 1 fi log_info "Successfully authenticated with Bitwarden" } # Sync environment variables sync_env_vars() { log_info "Starting .env sync to: $TARGET_DIR" # Ensure BW_SESSION is set if [[ -z "$BW_SESSION" ]]; then log_error "No Bitwarden session available" exit 1 fi # Sync with Bitwarden to get latest data log_info "Syncing with Bitwarden to get latest data..." if bw sync --session "$BW_SESSION" &>/dev/null; then log_debug "Successfully synced with Bitwarden" else log_error "Failed to sync with Bitwarden" exit 1 fi # Create .env file path ENV_FILE="$TARGET_DIR/.env" # Backup existing .env if it exists if [[ -f "$ENV_FILE" ]]; then cp "$ENV_FILE" "${ENV_FILE}.backup" log_debug "Backed up existing .env to .env.backup" fi # Search for items with .env in notes or custom fields # Adjust the search query based on how you store your env vars log_info "Fetching environment variables from Bitwarden..." # Example: Get a specific item by name (adjust based on your setup) # You might want to search by folder, collection, or specific naming convention ITEM_NAME="portfolio-env" # Adjust this to your item name ITEM=$(bw get item "$ITEM_NAME" --session "$BW_SESSION" 2>/dev/null) if [[ -z "$ITEM" ]]; then log_error "Could not find item '$ITEM_NAME' in Bitwarden" log_info "Make sure you have an item named '$ITEM_NAME' with your environment variables" exit 1 fi # Extract notes (assuming env vars are in notes field) NOTES=$(echo "$ITEM" | jq -r '.notes // empty') if [[ -z "$NOTES" ]]; then # Try custom fields if notes are empty log_debug "Notes empty, checking custom fields..." # Extract all custom fields and format as KEY=VALUE echo "$ITEM" | jq -r '.fields[]? | select(.type == 0) | "\(.name)=\(.value)"' > "$ENV_FILE" if [[ ! -s "$ENV_FILE" ]]; then log_error "No environment variables found in item '$ITEM_NAME'" exit 1 fi else # Write notes directly to .env file echo "$NOTES" > "$ENV_FILE" fi # Validate .env file if [[ -f "$ENV_FILE" ]] && [[ -s "$ENV_FILE" ]]; then log_info "Successfully created .env file at: $ENV_FILE" log_debug "Environment variables written:" if [[ "$DEBUG" == "true" ]]; then # Show keys only, not values for security grep -E '^[A-Z_]+=' "$ENV_FILE" | cut -d'=' -f1 | while read -r key; do echo " - $key" done fi else log_error "Failed to create .env file or file is empty" exit 1 fi } # Main execution main() { log_info "=== Bitwarden .env Sync (Advanced) ===" log_info "Target directory: $TARGET_DIR" log_debug "Session file: $SESSION_FILE" check_dependencies authenticate_bitwarden sync_env_vars log_info "=== Sync completed successfully ===" } # Run main function main