chore: snapshot current workspace changes

This commit is contained in:
S
2026-02-28 21:35:03 -05:00
parent 088e355962
commit 572e4c151c
5 changed files with 312 additions and 2 deletions

159
setup/bitwarden_to_env.sh Executable file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail
# Restore .env from a Bitwarden JSON export (vault export or single-item JSON).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "$PROJECT_ROOT/lib/common.sh"
ITEM_NAME="gitea-migration-env"
INPUT_FILE=""
OUTPUT_FILE="$PROJECT_ROOT/.env"
TEMPLATE_FILE="$PROJECT_ROOT/.env.example"
USE_BW_CLI=false
usage() {
cat >&2 <<EOF
Usage: $(basename "$0") [-f FILE | --bw] [-o FILE] [-n NAME]
Restore .env from a Bitwarden export.
Modes (pick one):
-f FILE Bitwarden JSON export file (full vault export or single-item)
--bw Fetch directly via Bitwarden CLI (requires 'bw' and unlocked vault)
Options:
-o FILE Output path (default: $OUTPUT_FILE)
-n NAME Item name to find in export (default: $ITEM_NAME)
-h Show this help
To export from Bitwarden:
Web vault: Tools → Export Vault → Format: .json
CLI: bw export --format json --output vault-export.json
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case $1 in
-f) INPUT_FILE="$2"; shift 2 ;;
--bw) USE_BW_CLI=true; shift ;;
-o) OUTPUT_FILE="$2"; shift 2 ;;
-n) ITEM_NAME="$2"; shift 2 ;;
-h) usage ;;
*) log_error "Unknown option: $1"; usage ;;
esac
done
if ! command -v jq &>/dev/null; then
log_error "jq is required but not installed"
exit 1
fi
# ── Retrieve the Bitwarden item ──────────────────────────────────────────────
item_json=""
if $USE_BW_CLI; then
if ! command -v bw &>/dev/null; then
log_error "Bitwarden CLI (bw) not found — install it or use -f with a JSON export"
exit 1
fi
log_info "Fetching '$ITEM_NAME' from Bitwarden CLI..."
if ! item_json=$(bw get item "$ITEM_NAME" 2>/dev/null); then
log_error "Failed to fetch '$ITEM_NAME'. Is your vault unlocked? (bw unlock)"
exit 1
fi
elif [[ -n "$INPUT_FILE" ]]; then
if [[ ! -f "$INPUT_FILE" ]]; then
log_error "File not found: $INPUT_FILE"
exit 1
fi
# Full vault export (has .items array) vs single-item JSON (has .fields directly)
if jq -e '.items' "$INPUT_FILE" >/dev/null 2>&1; then
if ! item_json=$(jq -e --arg name "$ITEM_NAME" \
'.items[] | select(.name == $name)' "$INPUT_FILE" 2>/dev/null); then
log_error "Item '$ITEM_NAME' not found in $INPUT_FILE"
log_info "Available items:"
jq -r '.items[].name' "$INPUT_FILE" >&2 || true
exit 1
fi
elif jq -e '.fields' "$INPUT_FILE" >/dev/null 2>&1; then
item_json=$(cat "$INPUT_FILE")
else
log_error "Unrecognised JSON format — expected a Bitwarden export or single-item JSON"
exit 1
fi
else
log_error "Specify -f FILE or --bw. Run with -h for help."
exit 1
fi
# ── Extract fields ───────────────────────────────────────────────────────────
fields_json=$(printf '%s' "$item_json" | jq -e '.fields // empty') || {
log_error "No custom fields found in the Bitwarden item"
exit 1
}
field_count=$(printf '%s' "$fields_json" | jq 'length')
log_info "Found $field_count fields in '$ITEM_NAME'"
# ── Reconstruct .env ─────────────────────────────────────────────────────────
if [[ -f "$TEMPLATE_FILE" ]]; then
log_info "Using .env.example as template (preserves comments and section structure)"
output=""
while IFS= read -r line || [[ -n "$line" ]]; do
# Pass through comments and blank lines unchanged
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
output+="$line"$'\n'
continue
fi
key="${line%%=*}"
# Look up this key in the Bitwarden fields
bw_value=$(printf '%s' "$fields_json" | \
jq -r --arg k "$key" '.[] | select(.name == $k) | .value // empty')
if [[ -n "$bw_value" ]]; then
# Quote values that contain shell-sensitive characters
if [[ "$bw_value" =~ [[:space:]\#\$\|\&\;\(\)\<\>] ]]; then
output+="${key}=\"${bw_value}\""$'\n'
else
output+="${key}=${bw_value}"$'\n'
fi
else
# No value in Bitwarden — keep the template line as-is (placeholder)
output+="$line"$'\n'
fi
done < "$TEMPLATE_FILE"
# Append any Bitwarden fields that aren't in the template (custom additions)
extra_header_written=false
while IFS= read -r key; do
if ! grep -q "^${key}=" "$TEMPLATE_FILE" 2>/dev/null; then
if ! $extra_header_written; then
output+=$'\n'"# --- Additional fields (not in .env.example) ---"$'\n'
extra_header_written=true
fi
value=$(printf '%s' "$fields_json" | \
jq -r --arg k "$key" '.[] | select(.name == $k) | .value')
output+="${key}=${value}"$'\n'
fi
done < <(printf '%s' "$fields_json" | jq -r '.[].name')
printf '%s' "$output" > "$OUTPUT_FILE"
else
log_warn "No .env.example found — writing flat key=value list (no comments)"
printf '%s' "$fields_json" | jq -r '.[] | "\(.name)=\(.value)"' > "$OUTPUT_FILE"
fi
log_success "Restored .env to $OUTPUT_FILE ($field_count variables)"
log_warn "Review the file before use — check for placeholder values that may need updating"

121
setup/env_to_bitwarden.sh Executable file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bash
set -euo pipefail
# Convert .env to Bitwarden JSON import format (secure note with custom fields).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "$PROJECT_ROOT/lib/common.sh"
ITEM_NAME="gitea-migration-env"
ENV_FILE="$PROJECT_ROOT/.env"
OUTPUT_FILE=""
usage() {
cat >&2 <<EOF
Usage: $(basename "$0") [-o FILE] [-n NAME]
Convert .env to Bitwarden-importable JSON (secure note with custom fields).
Options:
-o FILE Write to file instead of stdout
-n NAME Bitwarden item name (default: $ITEM_NAME)
-h Show this help
Import the output via:
Bitwarden Web Vault → Tools → Import Data → Format: Bitwarden (json) → Upload
EOF
exit 1
}
while getopts "o:n:h" opt; do
case $opt in
o) OUTPUT_FILE="$OPTARG" ;;
n) ITEM_NAME="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
if [[ ! -f "$ENV_FILE" ]]; then
log_error ".env not found at $ENV_FILE"
exit 1
fi
if ! command -v jq &>/dev/null; then
log_error "jq is required but not installed"
exit 1
fi
# Patterns for fields that should be hidden (type=1) in Bitwarden
sensitive_re="PASSWORD|TOKEN|SECRET|_KEY"
# Build a TSV of key\tvalue\ttype lines, then convert to JSON in one jq call.
# This avoids calling jq in a loop.
tsv=""
while IFS= read -r line || [[ -n "$line" ]]; do
# Skip comments and blank lines
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
key="${line%%=*}"
value="${line#*=}"
# Strip surrounding quotes
if [[ "$value" =~ ^\"(.*)\"$ ]]; then
value="${BASH_REMATCH[1]}"
elif [[ "$value" =~ ^\'(.*)\'$ ]]; then
value="${BASH_REMATCH[1]}"
fi
# Strip inline comments (space + #)
value="${value%%[[:space:]]#*}"
# Trim trailing whitespace
value="${value%"${value##*[![:space:]]}"}"
field_type=0
if [[ "$key" =~ $sensitive_re ]]; then
field_type=1
fi
# Append as null-delimited triple (safe for any value content)
tsv+="${key}"$'\x00'"${value}"$'\x00'"${field_type}"$'\x00'
done < "$ENV_FILE"
# Convert null-delimited triples into JSON fields array
fields_json=$(printf '%s' "$tsv" | \
jq -Rs '
split("\u0000") |
# drop trailing empty element from final delimiter
if .[-1] == "" then .[:-1] else . end |
[ range(0; length; 3) as $i |
{ name: .[$i], value: .[$i+1], type: (.[$i+2] | tonumber) }
]
')
field_count=$(printf '%s' "$fields_json" | jq 'length')
# Build the complete Bitwarden import envelope
import_json=$(jq -n \
--arg name "$ITEM_NAME" \
--argjson fields "$fields_json" \
'{
encrypted: false,
folders: [],
items: [
{
type: 2,
name: $name,
notes: "Gitea migration .env — exported by env_to_bitwarden.sh",
favorite: false,
secureNote: { type: 0 },
fields: $fields
}
]
}')
if [[ -n "$OUTPUT_FILE" ]]; then
printf '%s\n' "$import_json" > "$OUTPUT_FILE"
log_success "Wrote $field_count fields to $OUTPUT_FILE"
log_info "Import via: Bitwarden → Tools → Import Data → Bitwarden (json)"
else
printf '%s\n' "$import_json"
fi

View File

@@ -34,7 +34,7 @@ log_success "Homebrew found"
# --------------------------------------------------------------------------
# Brew packages
# --------------------------------------------------------------------------
BREW_PACKAGES=(jq curl gettext shellcheck gh)
BREW_PACKAGES=(jq curl gettext shellcheck gh bitwarden-cli)
for pkg in "${BREW_PACKAGES[@]}"; do
if brew list "$pkg" &>/dev/null; then