# RegenPreview.ps1

## Overview

`RegenPreview.ps1` is a PowerShell script that automates the process of building local generator packages and regenerating libraries for validation purposes. This script supports three modes:

- **Azure SDK Mode**: Regenerate Azure SDK for .NET libraries
- **OpenAI Mode**: Regenerate the OpenAI .NET library
- **Spector Mode**: Regenerate Azure spector test scenarios in azure-sdk-for-net

This script is designed to streamline the workflow for testing changes to the TypeSpec HTTP Client C# generator before submitting a pull request.

## Purpose

When making changes to the TypeSpec HTTP Client C# generator, it may be helpful to validate what the effects will be to generated libraries. This script automates the entire validation workflow by:

### Azure SDK Mode

1. Building local versions of the generator packages
2. Updating the Azure SDK for .NET repository to use these local packages
3. Regenerating all libraries (or selected libraries if `-Select` is specified)
4. Cleaning up all modifications after validation

### OpenAI Mode

1. Building local version of the unbranded generator package
2. Building local NuGet framework packages
3. Updating the OpenAI .NET repository's codegen configuration
4. Regenerating the OpenAI library using Invoke-CodeGen.ps1
5. Cleaning up all modifications after validation

### Spector Mode

1. Building local version of the unbranded generator package
2. Building local NuGet framework packages
3. Wiring the Azure generator in azure-sdk-for-net to use local packages
4. Running the Azure generator's Generate.ps1 to regenerate spector test scenarios
5. Cleaning up all modifications after validation

## Prerequisites

- **PowerShell 7.0 or later** (cross-platform: Windows, Linux, macOS)
- Node.js and npm installed
- .NET SDK installed
- Local clone of the `typespec` repository (unbranded generator)
- Local clone of the `azure-sdk-for-net` repository (for Azure SDK mode)
- Local clone of the `openai-dotnet` repository (for OpenAI mode)

## Usage

### Azure SDK Mode - Basic Usage (Regenerate All)

**Windows:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\path\to\azure-sdk-for-net"
```

**Linux/macOS:**

```powershell
./RegenPreview.ps1 -SdkLibraryRepoPath "/home/user/repos/azure-sdk-for-net"
```

This will:

- Clean and build the unbranded and Azure generators with local changes
- Regenerate **all** libraries automatically
- Restore all modified metadata files on success (ie. package.json, package-lock.json)

### OpenAI Mode - Basic Usage

**Windows:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\path\to\openai-dotnet"
```

**Linux/macOS:**

```powershell
./RegenPreview.ps1 -SdkLibraryRepoPath "/home/user/repos/openai-dotnet"
```

This will:

- Clean and build the unbranded generator with local changes
- Build and package NuGet framework packages
- Update root nuget.config with local NuGet source and packageSourceMapping
- Update codegen/package.json with local generator
- Regenerate codegen/package-lock.json with npm install
- Update OpenAI.Library.Plugin.csproj with local NuGet packages
- Regenerate the OpenAI library
- Restore all modified generator metadata files on success

**Note**: OpenAI mode is automatically detected when the repository path contains "openai-dotnet". The `-Select`, `-Azure`, `-Unbranded`, and `-Mgmt` parameters are not applicable in OpenAI mode.

### Parameters

#### `-SdkLibraryRepoPath` (Required)

The local file system path to your SDK repository (`azure-sdk-for-net` or `openai-dotnet`). When the path contains "openai-dotnet", the script automatically operates in OpenAI mode.

**Windows Example:**

```powershell
-SdkLibraryRepoPath "C:\repos\azure-sdk-for-net"
```

**Linux/macOS Example:**

```powershell
-SdkLibraryRepoPath "/home/user/repos/azure-sdk-for-net"
```

#### `-Select` (Optional)

**Azure SDK Mode only.** When specified, displays an interactive menu to select specific libraries to regenerate. If omitted, all libraries are regenerated automatically.

Can be combined with generator filter parameters (`-Azure`, `-Unbranded`, `-Mgmt`) to interactively select from a filtered subset.

Not applicable in OpenAI mode.

**Example:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Select
```

#### `-Azure` (Optional)

**Azure SDK Mode only.** When specified, only regenerates libraries using the Azure-branded generator (`@azure-typespec/http-client-csharp`).

Not applicable in OpenAI mode.

**Example:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Azure
```

#### `-Unbranded` (Optional)

**Azure SDK Mode only.** When specified, only regenerates libraries using the unbranded generator (`@typespec/http-client-csharp`).
Not applicable in OpenAI mode.

**Example:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Unbranded
```

#### `-Mgmt` (Optional)

**Azure SDK Mode only.** When specified, only regenerates libraries using the management plane generator (`@azure-typespec/http-client-csharp-mgmt`).

If no generator filter parameter is specified, all libraries are regenerated by default.

Not applicable in OpenAI mode.

**Example:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Mgmt
```

#### `-Spector` (Optional)

**Azure SDK Mode only.** When specified, regenerates the Azure spector test scenarios in azure-sdk-for-net instead of regenerating SDK libraries. This builds the local unbranded generator, wires it into the Azure generator, and runs the Azure generator's Generate.ps1 to regenerate spector test projects.

Mutually exclusive with `-Select`, `-Azure`, `-Unbranded`, and `-Mgmt`.

Not applicable in OpenAI mode.

**Example:**

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Spector
```

### Interactive Selection

When running with the `-Select` flag, the script presents an interactive menu listing all available libraries:

```
==================== LIBRARY SELECTION ====================
Found 28 libraries available for regeneration

Azure-branded libraries (@azure-typespec/http-client-csharp):
  [ 1] Azure.AI.VoiceLive                                  (ai)
  [ 2] Azure.Data.AppConfiguration                         (appconfiguration)
  ...

Unbranded libraries (@typespec/http-client-csharp):
  [12] Azure.AI.Projects                                   (ai)
  [13] Azure.AI.OpenAI                                     (openai)

Management plane libraries (@azure-typespec/http-client-csharp-mgmt):
  [14] Azure.ResourceManager.AgriculturePlatform           (agricultureplatform)
  [15] Azure.ResourceManager.ArizeAIObservabilityEval      (arizeaiobservabilityeval)
  ...

Enter library numbers to regenerate (comma-separated), 'all' for all libraries, or 'q' to quit:
Example: 1,3,5  or  1-4,7  or  all
```

**Selection Options:**

- Single library: `1`
- Multiple libraries: `1,3,5`
- Range of libraries: `1-4`
- Combination: `1-4,7,10`
- All libraries: `all`
- Quit: `q`

## How It Works

The script operates in two distinct modes based on the repository path provided.

### Azure SDK Mode

The script performs the following steps in sequence when the repository path points to `azure-sdk-for-net`:

### Step 0: Library Selection (Interactive Mode Only)

- Parses `Library_Inventory.md` in the azure-sdk-for-net repository
- Applies any generator filters if specified (`-Azure`, `-Unbranded`, `-Mgmt`)
- Displays available libraries grouped by generator type
- Prompts user to select which libraries to regenerate
- Skipped when `-Select` is not specified (non-interactive mode)

### Step 1: Build Unbranded Generator

- Runs `npm ci` to install dependencies
- Runs `npm run clean` to ensure a clean build
- Runs `npm run build` to build `@typespec/http-client-csharp`

### Step 2: Package Unbranded Generator

- Generates a local version string: `1.0.0-alpha.YYYYMMDD.hash`
- Updates `package.json` with the local version
- Runs `npm pack` to create a `.tgz` package
- Moves the package to the `debug` folder
- Restores original `package.json`

### Step 2.5: Build and Package NuGet Packages

- Generates NuGet packages for three generator framework projects:
  - `Microsoft.TypeSpec.Generator`
  - `Microsoft.TypeSpec.Generator.Input`
  - `Microsoft.TypeSpec.Generator.ClientModel`
- Uses Debug configuration with `--no-build` (reuses binaries from Step 1)
- Outputs `.nupkg` files to the `debug` folder
- In Azure SDK mode: Updates `Directory.Generation.Packages.props` and `NuGet.Config`
- In OpenAI mode: Proceeds to Step 3 (OpenAI regeneration)

---

### OpenAI Mode

When the repository path contains "openai-dotnet", the script switches to OpenAI mode and performs the following steps after Step 2.5:

#### Step 3: Update OpenAI Generator and Regenerate

**What the script does:**

- Updates `nuget.config` (at the root of the repo) with local NuGet package source
  - Adds the local debug folder as a package source
  - Updates `packageSourceMapping` to allow Generator packages from the local source (required for OpenAI's package source mapping configuration)
- Updates `codegen/package.json` with local unbranded generator dependency (`@typespec/http-client-csharp`)
  - Checks both `dependencies` and `devDependencies` sections for the package
  - Updates whichever section(s) contain the package to use `file:` protocol pointing to local `.tgz`
- Deletes `codegen/package-lock.json` to force regeneration with new dependencies
- Runs `npm install` in the codegen directory to regenerate the lock file with local dependencies
- Updates `codegen/generator/src/OpenAI.Library.Plugin.csproj` with local NuGet package version for `Microsoft.TypeSpec.Generator.ClientModel`
- Invokes `scripts/Invoke-CodeGen.ps1 -Clean` to regenerate the OpenAI library
- **On successful regeneration**, restores all modified artifacts:
  - `codegen/package.json`
  - `codegen/package-lock.json`
  - `nuget.config`
  - `codegen/generator/src/OpenAI.Library.Plugin.csproj`
- **On failure**, leaves all modified artifacts in place for debugging
- Displays success message and exits

**Note:** Filter parameters (`-Azure`, `-Unbranded`, `-Mgmt`, `-Select`) are not applicable in OpenAI mode and will cause an error if specified.

---

### Azure SDK Mode (continued)

In Azure SDK mode, the script continues with additional steps after Step 2.5:

### Step 3: Update and Build Azure Generator

- Updates the Azure generator's `package.json` to reference the local unbranded package using `file:` protocol
- Runs `npm run clean` to clear previous builds
- Runs `npm install --package-lock-only` to update the lock file with the new local dependency
- Runs `npm ci` for clean installation based on the updated lock file
- Builds the Azure generator with `npm run build`

### Step 4: Package Azure Generator

- Updates `package.json` with the local version (dependency already set in Step 3)
- Runs `npm pack` to create a `.tgz` package
- Moves the package to the `debug` folder
- Restores original `package.json` (after both Steps 3 and 4 complete)

### Step 5: Update eng Folder Artifacts

- Updates package.json artifacts in `azure-sdk-for-net/eng`:
  - `azure-typespec-http-client-csharp-emitter-package.json`
  - `azure-typespec-http-client-csharp-emitter-package-lock.json`
  - `http-client-csharp-emitter-package.json`
  - `http-client-csharp-emitter-package-lock.json`
- Uses a temporary directory to safely update the lock files
- These files control which generator versions are used during library regeneration

### Step 6: Update and Build Management Plane Generator

- Updates the management plane generator (`@azure-typespec/http-client-csharp-mgmt`) located in `azure-sdk-for-net/eng/packages/http-client-csharp-mgmt`
- Updates `package.json` to reference both:
  - Local Azure generator (`@azure-typespec/http-client-csharp`) using `file:` protocol
  - Local unbranded generator (`@typespec/http-client-csharp`) using `file:` protocol
- Runs `npm install` to install dependencies
- Runs `npm run clean` to ensure a clean build
- Runs `npm run build` to build the management plane generator
- Creates a local package with `npm pack`
- Updates eng folder emitter package artifacts:
  - `azure-typespec-http-client-csharp-mgmt-emitter-package.json`
  - `azure-typespec-http-client-csharp-mgmt-emitter-package-lock.json`
- Updates `Directory.Generation.Packages.props` in azure-sdk-for-net with `AzureGeneratorVersion` property
  - This version is used by management plane libraries that reference the `Azure.Generator` NuGet package
- This step is skipped if the management plane generator directory doesn't exist

### Step 7: Prepare Library List

- Confirms the list of libraries to regenerate
- When no `-Select` flag, loads all libraries from `Library_Inventory.md` including:
  - Data plane libraries using `@azure-typespec/http-client-csharp`
  - Data plane libraries using `@typespec/http-client-csharp`
  - Management plane libraries using `@azure-typespec/http-client-csharp-mgmt`
- In interactive mode (`-Select`), uses the previously selected libraries

### Step 8: Regenerate Libraries

- **Pre-Install tsp-client**: Runs `npm ci --prefix eng\common\tsp-client` once before parallel execution
  - Installs shared TypeSpec compiler tooling used by all libraries
  - Eliminates concurrent npm conflicts during parallel regeneration
- **Parallel Execution**: Uses PowerShell's `ForEach-Object -Parallel` to regenerate multiple libraries concurrently
- **Cross-Platform CPU Detection**:
  - Windows: Uses `Get-CimInstance` with `Win32_Processor`
  - Linux: Uses `nproc` command
  - macOS: Uses `sysctl -n hw.ncpu` command
- **Throttle Limit**: Automatically calculated as `(CPU cores - 2)` with a minimum of 1 and maximum of 8
- **Skip Redundant Installs**: Each parallel job uses `/p:SkipTspClientInstall=true` to skip re-installing tsp-client
- **Thread-Safe Progress**: Real-time updates showing `[completed/total] ✓/✗ LibraryName`
  - Success: Green with ✓
  - Failure: White with ✗ (red reserved for error details in final report)
- **Isolated Execution**: Each library regenerates in its own directory (no conflicts)
- Runs `dotnet build /t:GenerateCode /p:SkipTspClientInstall=true` in the library's project directory
- Automatically detects if the `.csproj` is in a `src` subdirectory
- Tracks success/failure for each library with colored output

### Step 9: Restore Artifacts (On Success Only)

If all libraries regenerate successfully, the script restores modified files:

- `eng/azure-typespec-http-client-csharp-emitter-package.json`
- `eng/azure-typespec-http-client-csharp-emitter-package-lock.json`
- `eng/http-client-csharp-emitter-package.json`
- `eng/http-client-csharp-emitter-package-lock.json`
- `eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json`
- `eng/azure-typespec-http-client-csharp-mgmt-emitter-package-lock.json`
- `eng/packages/http-client-csharp/package-lock.json`
- `eng/packages/http-client-csharp-mgmt/package.json`
- `eng/packages/http-client-csharp-mgmt/package-lock.json`
- `eng/centralpackagemanagement/Directory.Generation.Packages.props`
- `NuGet.Config`

**Note:** If any libraries fail, artifacts are NOT restored, allowing you to debug the issue with the modified configuration intact.

### Error Handling

If the script encounters an error during pre-requisite steps (Steps 1-6), it will:

- Display the error message and stack trace
- Attempt to restore `NuGet.Config` to prevent leaving the repository in an inconsistent state
- Exit with code 1

## Output

### Debug Folder

All packaged artifacts are stored in the `debug` folder at the root of the unbranded generator:

- `typespec-http-client-csharp-{version}.tgz` - Unbranded generator npm package
- `azure-typespec-http-client-csharp-{version}.tgz` - Azure generator npm package
- `Microsoft.TypeSpec.Generator.{version}.nupkg` - Core generator NuGet package
- `Microsoft.TypeSpec.Generator.Input.{version}.nupkg` - Input models NuGet package
- `Microsoft.TypeSpec.Generator.ClientModel.{version}.nupkg` - Client model NuGet package
- `regen-report.json` - Detailed JSON report of regeneration results

### Console Output

The script provides colored console output with:

- **Cyan**: Step headers and section dividers
- **Yellow**: Important information (versions, library counts, warnings)
- **Gray**: Detailed command execution and file operations
- **Green**: Success messages
- **Red**: Error messages and failed libraries
- **White**: Library names during regeneration

### Regeneration Report

After regeneration completes, a summary report is displayed:

```
==================== REGENERATION REPORT ====================
Total Libraries: 3
Passed: 2
Failed: 1
Execution Time: 00:02:45

PASSED LIBRARIES:
  ✓ Azure.AI.VoiceLive (ai)
  ✓ Azure.AI.Projects (cognitiveservices)

FAILED LIBRARIES:
  ✗ Azure.Messaging.EventGrid.Namespaces (eventgrid)
    Error: Generation failed with exit code 1
    Details: ...

=============================================================
Detailed report saved to: C:\...\debug\regen-report.json
```

## Common Scenarios

### Scenario 1: Test OpenAI Library Changes

```powershell
# You made changes to the unbranded generator that affect OpenAI
# Regenerate the OpenAI library to validate your changes
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\openai-dotnet"
```

### Scenario 2: Test a Small Change Quickly (Azure SDK)

```powershell
# You want to test Azure generator changes on specific libraries only
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Select

# Select just one or two libraries when prompted
# Selection: 1,5
```

### Scenario: Full Validation Before PR

```powershell
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net"
```

### Scenario 4: Regenerate Azure Spector Test Scenarios

```powershell
# Validate that local changes don't break Azure spector test scenarios
.\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Spector
```
