---
name: step-00-init
description: Initialize EF Core context - detect project, DbContext, and environment
next_step: null
---

# Step 0: Initialize EF Core Context

## YOUR TASK:

Detect EF Core project structure, identify DbContext (Core vs Extensions), and load environment configuration.

**This step runs first for ALL EF Core commands.**

---

## EXECUTION SEQUENCE:

### 0. Ensure dotnet ef Available (Platform-Aware)

> **CRITICAL:** On Windows (Git Bash, MSYS2), `dotnet ef` may not be on the shell PATH
> even though it's installed. This step fixes PATH before any EF Core command.

```bash
ensure_dotnet_ef() {
  # Quick check: already available?
  if dotnet ef --version &>/dev/null; then
    DOTNET_EF_VERSION=$(dotnet ef --version 2>/dev/null)
    echo "dotnet-ef: $DOTNET_EF_VERSION"
    return 0
  fi

  echo "dotnet-ef not on PATH, attempting platform-aware fix..."

  # Try all known .NET global tools locations
  # - $USERPROFILE/.dotnet/tools  → Windows via Git Bash (USERPROFILE=C:\Users\xxx)
  # - $HOME/.dotnet/tools         → Linux/macOS or Git Bash fallback
  # - $LOCALAPPDATA/Microsoft/dotnet/tools → Windows alternate location
  for TOOLS_DIR in \
    "$USERPROFILE/.dotnet/tools" \
    "$HOME/.dotnet/tools" \
    "$LOCALAPPDATA/Microsoft/dotnet/tools"; do
    if [ -n "$TOOLS_DIR" ] && [ -d "$TOOLS_DIR" ]; then
      export PATH="$TOOLS_DIR:$PATH"
      echo "  PATH += $TOOLS_DIR"
    fi
  done

  # Verify after PATH fix
  if dotnet ef --version &>/dev/null; then
    DOTNET_EF_VERSION=$(dotnet ef --version 2>/dev/null)
    echo "dotnet-ef: $DOTNET_EF_VERSION (found after PATH fix)"
    return 0
  fi

  echo "ERROR: dotnet-ef not available"
  echo "Install with: dotnet tool install --global dotnet-ef"
  echo ""
  echo "If installed but not found, check:"
  echo "  Windows: %USERPROFILE%\\.dotnet\\tools must be in PATH"
  echo "  Linux:   ~/.dotnet/tools must be in PATH"
  exit 1
}

ensure_dotnet_ef
```

**IMPORTANT — WSL pitfall:**
If the shell resolves `$HOME` to `/home/{user}` (WSL path) instead of `/c/Users/{user}` (Git Bash),
the .NET SDK may not be found even if `dotnet-ef` is. Use `$USERPROFILE` first on Windows.

---

### 1. Detect EF Core Project

```bash
detect_efcore_project() {
  # Find project with EF Core reference
  CSPROJ=$(find . -name "*.csproj" -exec grep -l "Microsoft.EntityFrameworkCore" {} \; | head -1)

  if [ -z "$CSPROJ" ]; then
    echo "ERROR: No EF Core project found"
    exit 1
  fi

  PROJECT_DIR=$(dirname "$CSPROJ")
  PROJECT_NAME=$(basename "$CSPROJ" .csproj)
  MIGRATIONS_DIR="$PROJECT_DIR/Persistence/Migrations"

  # Find startup and infrastructure projects
  STARTUP_PROJECT=$(find . -name "*.Api.csproj" -o -name "*Web.csproj" | head -1)
  INFRA_PROJECT=$(find . -name "*Infrastructure.csproj" | head -1)
  [ -z "$INFRA_PROJECT" ] && INFRA_PROJECT="$CSPROJ"

  echo "Project: $PROJECT_NAME"
  echo "Migrations: $MIGRATIONS_DIR"
}
```

### 2. Detect DbContext (Core vs Extensions)

```bash
detect_dbcontext() {
  # Priority 1: SmartStack.Domain exists → SmartStack source project (Core only)
  if find . -type d -name "SmartStack.Domain" | grep -q .; then
    DBCONTEXT="CoreDbContext"
    DBCONTEXT_TYPE="core"
    SCHEMA="core"
    RUN_BOTH=false
    echo "DbContext: CoreDbContext (SmartStack.Domain found)"
    return
  fi

  # Priority 2: Client project with SmartStack NuGet → BOTH contexts
  # Detect: csproj files that have a PackageReference to SmartStack (not ProjectReference)
  # This identifies client projects created via `ss init` that consume SmartStack as NuGet
  if find . -name "*.csproj" -exec grep -ql "PackageReference.*SmartStack" {} \; 2>/dev/null; then
    DBCONTEXT="CoreDbContext"
    DBCONTEXT_TYPE="both"
    SCHEMA="core"
    RUN_BOTH=true
    echo "DbContext: Both (Core from SmartStack NuGet + Extensions local)"
    echo "  Deploy order: CoreDbContext → ExtensionsDbContext"
    return
  fi

  # Priority 3: Scan for DbContext in code
  CORE_CTX=$(find . -name "*.cs" -exec grep -l "CoreDbContext" {} \; 2>/dev/null | head -1)
  EXT_CTX=$(find . -name "*.cs" -exec grep -l "ExtensionsDbContext" {} \; 2>/dev/null | head -1)

  if [ -n "$CORE_CTX" ] && [ -n "$EXT_CTX" ]; then
    DBCONTEXT="CoreDbContext"
    DBCONTEXT_TYPE="both"
    SCHEMA="core"
    RUN_BOTH=true
    echo "DbContext: Both (CoreDbContext + ExtensionsDbContext found)"
  elif [ -n "$CORE_CTX" ] && [ -z "$EXT_CTX" ]; then
    DBCONTEXT="CoreDbContext"
    DBCONTEXT_TYPE="core"
    SCHEMA="core"
    RUN_BOTH=false
  elif [ -z "$CORE_CTX" ] && [ -n "$EXT_CTX" ]; then
    DBCONTEXT="ExtensionsDbContext"
    DBCONTEXT_TYPE="extensions"
    SCHEMA="extensions"
    RUN_BOTH=false
  else
    # Priority 4: Ask user
    DBCONTEXT=""
    DBCONTEXT_TYPE=""
    SCHEMA=""
    RUN_BOTH=false
  fi
}
```

### 3. Ask User if DbContext Unknown

**If `{dbcontext}` is empty after detection:**

```yaml
AskUserQuestion:
  header: "DbContext"
  question: "Which DbContext to use?"
  options:
    - label: "CoreDbContext"
      description: "SmartStack entities (User, Role, Navigation...)"
    - label: "ExtensionsDbContext"
      description: "Client-specific entities"
    - label: "Both"
      description: "Run for both contexts"
  multiSelect: false
```

**Set variables based on response:**
```javascript
if (answer === "CoreDbContext") {
  DBCONTEXT = "CoreDbContext";
  DBCONTEXT_TYPE = "core";
  SCHEMA = "core";
} else if (answer === "ExtensionsDbContext") {
  DBCONTEXT = "ExtensionsDbContext";
  DBCONTEXT_TYPE = "extensions";
  SCHEMA = "extensions";
} else {
  // Both - will run commands twice
  RUN_BOTH = true;
}
```

### 4. Detect Environment

```bash
detect_environment() {
  API_DIR=$(find . -type d -name "*.Api" | head -1)
  [ -z "$API_DIR" ] && API_DIR="src/SmartStack.Api"

  # Priority: --env flag > appsettings.Local.json > error
  if [ -n "$ENV_FLAG" ]; then
    CONFIG_FILE="$API_DIR/appsettings.${ENV_FLAG}.json"
  elif [ -f "$API_DIR/appsettings.Local.json" ]; then
    CONFIG_FILE="$API_DIR/appsettings.Local.json"
    SELECTED_ENV="Local"
  else
    echo "ERROR: No appsettings.Local.json found"
    echo "Create one or use --env flag"
    exit 1
  fi

  echo "Environment: $SELECTED_ENV"
  echo "Config: $CONFIG_FILE"
}
```

### 5. Determine Base Branch (for squash/rebase)

```bash
determine_base_branch() {
  CURRENT_BRANCH=$(git branch --show-current)

  case "$CURRENT_BRANCH" in
    feature/*)
      BASE_BRANCH="develop"
      BRANCH_TYPE="feature"
      ;;
    release/*)
      BASE_BRANCH="main"
      BRANCH_TYPE="release"
      ;;
    hotfix/*)
      BASE_BRANCH="main"
      BRANCH_TYPE="hotfix"
      ;;
    develop)
      BASE_BRANCH="main"
      BRANCH_TYPE="develop"
      ;;
    main|master)
      echo "ERROR: Cannot run on main/master branch"
      exit 1
      ;;
    *)
      BASE_BRANCH="develop"
      BRANCH_TYPE="unknown"
      ;;
  esac

  echo "Branch: $CURRENT_BRANCH ($BRANCH_TYPE)"
  echo "Base: $BASE_BRANCH"
}
```

### 6. Block Production

```bash
block_production() {
  # Check for production indicators (case-insensitive)
  if grep -qi '"Production"' "$CONFIG_FILE" 2>/dev/null; then
    echo "BLOCKED: Production environment detected in $CONFIG_FILE"
    exit 1
  fi

  # Case-insensitive database name check
  DB_LOWER=$(echo "$DATABASE_NAME" | tr '[:upper:]' '[:lower:]')
  if [[ "$DB_LOWER" == *"prod"* ]] || [[ "$DB_LOWER" == *"production"* ]]; then
    echo "BLOCKED: Production database detected ($DATABASE_NAME)"
    exit 1
  fi

  # Environment name check (case-insensitive)
  ENV_LOWER=$(echo "$SELECTED_ENV" | tr '[:upper:]' '[:lower:]')
  if [[ "$ENV_LOWER" == "production" ]] || [[ "$ENV_LOWER" == "prod" ]]; then
    echo "BLOCKED: Production environment selected ($SELECTED_ENV)"
    exit 1
  fi
}
```

### 7. Dual Context Helper

```bash
# Helper for steps that need to run for both contexts when RUN_BOTH=true
# Usage: run_for_contexts "command_or_function"
# Executes with CoreDbContext first, then ExtensionsDbContext
# Maintains deploy order constraint: Core MUST run first

run_for_contexts() {
  if [ "$RUN_BOTH" = "true" ]; then
    echo "=== Running for CoreDbContext (core schema) ==="
    DBCONTEXT="CoreDbContext"
    DBCONTEXT_TYPE="core"
    SCHEMA="core"
    eval "$1"

    echo ""
    echo "=== Running for ExtensionsDbContext (extensions schema) ==="
    DBCONTEXT="ExtensionsDbContext"
    DBCONTEXT_TYPE="extensions"
    SCHEMA="extensions"
    eval "$1"
  else
    eval "$1"
  fi
}
```

### 8. Display Summary

```
EF CORE CONTEXT
├── Project:    {PROJECT_NAME}
├── DbContext:  {DBCONTEXT} ({DBCONTEXT_TYPE})
├── Run Both:   {RUN_BOTH} (Core → Extensions)
├── Schema:     {SCHEMA}
├── Migrations: {MIGRATIONS_DIR}
├── Environment:{SELECTED_ENV}
└── Branch:     {CURRENT_BRANCH} → {BASE_BRANCH}
```

---

## STATE OUTPUT:

After this step, these variables are available:

| Variable | Example Value |
|----------|---------------|
| `{project_name}` | SmartStack.Infrastructure |
| `{dbcontext}` | CoreDbContext |
| `{dbcontext_type}` | core |
| `{schema}` | core |
| `{migrations_dir}` | src/SmartStack.Infrastructure/Persistence/Migrations |
| `{infra_project}` | src/SmartStack.Infrastructure/SmartStack.Infrastructure.csproj |
| `{startup_project}` | src/SmartStack.Api/SmartStack.Api.csproj |
| `{base_branch}` | develop |
| `{branch_type}` | feature |
| `{current_branch}` | feature/multitenant |
| `{selected_env}` | Local |
| `{run_both}` | true |

---

## SUCCESS CRITERIA:

- EF Core project detected
- DbContext identified (or user prompted)
- Environment loaded
- No production environment
- All paths validated

## FAILURE MODES:

| Error | Resolution |
|-------|------------|
| No EF Core project | Check Microsoft.EntityFrameworkCore reference |
| No appsettings.Local.json | Create file or use --env flag |
| Production detected | Use different environment |
| Both DbContexts found | User will be prompted |

---

## NEXT STEP:

Route to command-specific step based on `{command}`:

| Command | Next Step |
|---------|-----------|
| `db-status` | `../db/step-status.md` |
| `db-deploy` | `../db/step-deploy.md` |
| `db-reset` | `../db/step-reset.md` |
| `db-seed` | `../db/step-seed.md` |
| `migration` | `../migration/step-01-check.md` |
| `squash` | `../squash/step-01-backup.md` |
| `rebase-snapshot` | `../rebase-snapshot/step-01-backup.md` |
