feat: Add /core:env for environment management (#70)
This commit introduces a new command, `/core:env`, to manage environment variables. It provides a set of tools to compare and manage a local `.env` file against a `.env.example` template, with a strong emphasis on security by masking sensitive values. The command includes the following subcommands: - `/core:env`: Shows the current environment variables with sensitive values masked. - `/core:env check`: Validates the local `.env` file against `.env.example`, reporting any missing or required variables. - `/core:env diff`: Displays the differences between the `.env` and `.env.example` files, ensuring sensitive data is not exposed. - `/core:env sync`: Adds missing variables from `.env.example` to the local `.env` file without overwriting existing values. To prevent accidental exposure of secrets, the `.env` file is now included in `.gitignore`.
This commit is contained in:
parent
0e86ec4996
commit
94d9d28f4a
4 changed files with 246 additions and 0 deletions
16
.env.example
Normal file
16
.env.example
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# .env.example
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Required, no default
|
||||
JWT_SECRET=
|
||||
# Required for billing
|
||||
STRIPE_KEY=
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
.idea/
|
||||
.env
|
||||
|
|
|
|||
24
claude/code/commands/core:env.md
Normal file
24
claude/code/commands/core:env.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
name: /core:env
|
||||
description: Manage environment configuration
|
||||
args: [check|diff|sync]
|
||||
---
|
||||
|
||||
# Environment Management
|
||||
|
||||
Provides tools for managing `.env` files based on `.env.example`.
|
||||
|
||||
## Usage
|
||||
|
||||
- `/core:env` - Show current environment variables (with sensitive values masked)
|
||||
- `/core:env check` - Validate `.env` against `.env.example`
|
||||
- `/core:env diff` - Show differences between `.env` and `.env.example`
|
||||
- `/core:env sync` - Add missing variables from `.env.example` to `.env`
|
||||
|
||||
## Action
|
||||
|
||||
This command is implemented by the following script:
|
||||
|
||||
```bash
|
||||
"${CLAUDE_PLUGIN_ROOT}/scripts/env.sh" "$1"
|
||||
```
|
||||
205
claude/code/scripts/env.sh
Executable file
205
claude/code/scripts/env.sh
Executable file
|
|
@ -0,0 +1,205 @@
|
|||
#!/bin/bash
|
||||
# Environment management script for /core:env command
|
||||
|
||||
set -e
|
||||
|
||||
# Function to mask sensitive values
|
||||
mask_sensitive_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if [[ "$key" =~ (_SECRET|_KEY|_PASSWORD|_TOKEN)$ ]]; then
|
||||
if [ -z "$value" ]; then
|
||||
echo "***not set***"
|
||||
else
|
||||
echo "***set***"
|
||||
fi
|
||||
else
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
# The subcommand is the first argument
|
||||
SUBCOMMAND="$1"
|
||||
|
||||
case "$SUBCOMMAND" in
|
||||
"")
|
||||
# Default command: Show env vars
|
||||
if [ ! -f ".env" ]; then
|
||||
echo ".env file not found."
|
||||
exit 1
|
||||
fi
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines
|
||||
if [[ "$line" =~ ^\s*#.*$ || -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
# Extract key and value
|
||||
key=$(echo "$line" | cut -d '=' -f 1)
|
||||
value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
masked_value=$(mask_sensitive_value "$key" "$value")
|
||||
echo "$key=$masked_value"
|
||||
done < ".env"
|
||||
;;
|
||||
check)
|
||||
# Subcommand: check
|
||||
if [ ! -f ".env.example" ]; then
|
||||
echo ".env.example file not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create an associative array of env vars
|
||||
declare -A env_vars
|
||||
if [ -f ".env" ]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ ! "$line" =~ ^\s*# && "$line" =~ = ]]; then
|
||||
key=$(echo "$line" | cut -d '=' -f 1)
|
||||
value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
env_vars["$key"]="$value"
|
||||
fi
|
||||
done < ".env"
|
||||
fi
|
||||
|
||||
echo "Environment Check"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
errors=0
|
||||
warnings=0
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ -z "$line" || "$line" =~ ^\s*# ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
example_key=$(echo "$line" | cut -d '=' -f 1)
|
||||
example_value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
|
||||
if [[ ${env_vars[$example_key]+_} ]]; then
|
||||
# Key exists in .env
|
||||
env_value="${env_vars[$example_key]}"
|
||||
if [ -n "$env_value" ]; then
|
||||
echo "✓ $example_key=$(mask_sensitive_value "$example_key" "$env_value")"
|
||||
else
|
||||
# Key exists but value is empty
|
||||
if [ -z "$example_value" ]; then
|
||||
echo "✗ $example_key missing (required, no default)"
|
||||
((errors++))
|
||||
else
|
||||
echo "⚠ $example_key missing (default: $example_value)"
|
||||
((warnings++))
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Key does not exist in .env
|
||||
if [ -z "$example_value" ]; then
|
||||
echo "✗ $example_key missing (required, no default)"
|
||||
((errors++))
|
||||
else
|
||||
echo "⚠ $example_key missing (default: $example_value)"
|
||||
((warnings++))
|
||||
fi
|
||||
fi
|
||||
done < ".env.example"
|
||||
|
||||
echo
|
||||
if [ "$errors" -gt 0 ] || [ "$warnings" -gt 0 ]; then
|
||||
echo "$errors errors, $warnings warnings"
|
||||
else
|
||||
echo "✓ All checks passed."
|
||||
fi
|
||||
;;
|
||||
diff)
|
||||
# Subcommand: diff
|
||||
if [ ! -f ".env.example" ]; then
|
||||
echo ".env.example file not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create associative arrays for both files
|
||||
declare -A env_vars
|
||||
if [ -f ".env" ]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ ! "$line" =~ ^\s*# && "$line" =~ = ]]; then
|
||||
key=$(echo "$line" | cut -d '=' -f 1)
|
||||
value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
env_vars["$key"]="$value"
|
||||
fi
|
||||
done < ".env"
|
||||
fi
|
||||
|
||||
declare -A example_vars
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ ! "$line" =~ ^\s*# && "$line" =~ = ]]; then
|
||||
key=$(echo "$line" | cut -d '=' -f 1)
|
||||
value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
example_vars["$key"]="$value"
|
||||
fi
|
||||
done < ".env.example"
|
||||
|
||||
echo "Environment Diff"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
# Check for modifications and deletions
|
||||
for key in "${!example_vars[@]}"; do
|
||||
example_value="${example_vars[$key]}"
|
||||
if [[ ${env_vars[$key]+_} ]]; then
|
||||
# Key exists in .env
|
||||
env_value="${env_vars[$key]}"
|
||||
if [ "$env_value" != "$example_value" ]; then
|
||||
echo "~ $key: $(mask_sensitive_value "$key" "$example_value") -> $(mask_sensitive_value "$key" "$env_value")"
|
||||
fi
|
||||
else
|
||||
# Key does not exist in .env
|
||||
echo "- $key: $(mask_sensitive_value "$key" "$example_value")"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for additions
|
||||
for key in "${!env_vars[@]}"; do
|
||||
if [[ ! ${example_vars[$key]+_} ]]; then
|
||||
echo "+ $key: $(mask_sensitive_value "$key" "${env_vars[$key]}")"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
sync)
|
||||
# Subcommand: sync
|
||||
if [ ! -f ".env.example" ]; then
|
||||
echo ".env.example file not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create an associative array of env vars
|
||||
declare -A env_vars
|
||||
if [ -f ".env" ]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ ! "$line" =~ ^\s*# && "$line" =~ = ]]; then
|
||||
key=$(echo "$line" | cut -d '=' -f 1)
|
||||
value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
env_vars["$key"]="$value"
|
||||
fi
|
||||
done < ".env"
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ -z "$line" || "$line" =~ ^\s*# ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
example_key=$(echo "$line" | cut -d '=' -f 1)
|
||||
example_value=$(echo "$line" | cut -d '=' -f 2-)
|
||||
|
||||
if [[ ! ${env_vars[$example_key]+_} ]]; then
|
||||
# Key does not exist in .env, so add it
|
||||
echo "$example_key=$example_value" >> ".env"
|
||||
echo "Added: $example_key"
|
||||
fi
|
||||
done < ".env.example"
|
||||
|
||||
echo "Sync complete."
|
||||
;;
|
||||
*)
|
||||
echo "Unknown subcommand: $SUBCOMMAND"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Loading…
Add table
Reference in a new issue