diff --git a/README.md b/README.md
index 3ab96df..c83991d 100644
--- a/README.md
+++ b/README.md
@@ -59,11 +59,35 @@ dotnet pack lib/csharp/Octopus/Octopus.Kubernetes.Monitor.MessageContracts -o ..
### Go SDK
You can use a local version of the Go SDK by using a replace directive in the Kubernete's monitor Go module file.
-```
-replace github.com/octopusdeploy/kubernetes-monitor-contracts => {PATH_TO_LOCAL_REPO}
+```go
+replace github.com/octopusdeploy/kubernetes-monitor-contracts/go => {PATH_TO_LOCAL_REPO}
// Example
-replace github.com/octopusdeploy/kubernetes-monitor-contracts => ../kubernetes-monitor-contracts/lib/go
+replace github.com/octopusdeploy/kubernetes-monitor-contracts/go => ../kubernetes-monitor-contracts/go
+```
+
+### `build-local` script
+Two scripts have been provided to automate generating and installing local packages. The scripts will do the following by default:
+1. Run `buf generate` to generate the gRPC SDKs from the Proto files - This will generate the SDKs for all of the languages
+2. Add a replace directive to the Kubernetes Monitor `go.mod` file assuming that the Kubernetes Project folder is at `../lobster-watcher`
+3. Update all the versions of any package references for to `Octopus.Kubernetes.Monitor.MessageContracts` to the local nuget package.
+
+**This script assumes that a local nuget package source has been configured already, the default location is `../LocalPackages`**
+
+#### Usage
+```bash
+# Bash
+./build-local.sh
+./build-local.sh --languages=go
+
+# For more detailed information about available parameters
+./build-local.sh --help
+
+# Powershell
+./build-local.ps1
+./build-local.ps1 --languages=go
+# For more detailed information about available parameters
+get-help .\build-local.ps1 -full
```
# Configuration
diff --git a/build-local.ps1 b/build-local.ps1
new file mode 100755
index 0000000..72517f2
--- /dev/null
+++ b/build-local.ps1
@@ -0,0 +1,186 @@
+#!/usr/bin/env pwsh
+
+<#
+.SYNOPSIS
+ Automates local development experience for Kubernetes Monitor Message Contracts.
+.DESCRIPTION
+ Generates SDKs, builds packages, and updates project references.
+.PARAMETER Languages
+ Comma-separated list of languages to generate SDKs for (default: go,csharp)
+.PARAMETER OutputLocation
+ Path where built packages should be moved to (default: ../LocalPackages)
+.PARAMETER OctopusLocation
+ Path to the Octopus Deploy project (default: ../OctopusDeploy)
+.PARAMETER K8sMonitorLocation
+ Path to the Kubernetes Monitor project (default: ../lobster-watcher)
+.PARAMETER OctopusProjects
+ Comma-separated list of specific Octopus projects to update
+#>
+
+param (
+ [string]$Languages = "go,csharp",
+ [string]$OutputLocation = "../LocalPackages",
+ [string]$OctopusLocation = "../OctopusDeploy",
+ [string]$K8sMonitorLocation = "../lobster-watcher",
+ [string]$OctopusProjects = ""
+)
+
+# Colors for prettier output
+$Green = [System.ConsoleColor]::Green
+$Yellow = [System.ConsoleColor]::Yellow
+$Red = [System.ConsoleColor]::Red
+$Cyan = [System.ConsoleColor]::Cyan
+$Blue = [System.ConsoleColor]::Blue
+$DefaultColor = [Console]::ForegroundColor
+
+# Create output directory if it doesn't exist
+if (-not (Test-Path $OutputLocation)) {
+ New-Item -ItemType Directory -Path $OutputLocation | Out-Null
+}
+
+# Get absolute path of current directory (repository root)
+$RepoPath = (Get-Location).Path
+
+# Split languages into array
+$LanguageArray = $Languages.Split(",")
+
+Write-Host "`n=== Generating SDKs ===" -ForegroundColor $Blue
+Write-Host "Running buf generate..." -ForegroundColor $Yellow
+try {
+ buf generate
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "ā Failed to generate SDKs" -ForegroundColor $Red
+ exit 1
+ }
+ Write-Host "ā
SDKs generated successfully" -ForegroundColor $Green
+} catch {
+ Write-Host "ā Failed to generate SDKs: $_" -ForegroundColor $Red
+ exit 1
+}
+
+# Process each language
+foreach ($lang in $LanguageArray) {
+ switch ($lang) {
+ "csharp" {
+ Write-Host "`n=== Processing C# SDK ===" -ForegroundColor $Blue
+
+ # Get the current version from csproj
+ $CsprojPath = "csharp/Octopus/Octopus.Kubernetes.Monitor.MessageContracts/Octopus.Kubernetes.Monitor.MessageContracts.csproj"
+ $CsprojContent = Get-Content $CsprojPath -Raw
+ if ($CsprojContent -match '(.*?)') {
+ $BaseVersion = $Matches[1]
+ } else {
+ $BaseVersion = "1.0.0"
+ }
+
+ # Get the current branch name
+ $BranchName = (git branch --show-current) -replace "/", "-"
+
+ # Generate a timestamp
+ $Timestamp = Get-Date -Format "yyyyMMddHHmmss"
+
+ # Create prerelease version string
+ $PrereleaseVersion = "${BaseVersion}-${BranchName}-${Timestamp}"
+
+ Write-Host "š¦ Packing C# SDK with version: $PrereleaseVersion" -ForegroundColor $Yellow
+
+ try {
+ dotnet pack $CsprojPath -c Debug -p:Version=$PrereleaseVersion -o $OutputLocation
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "ā Failed to pack C# SDK" -ForegroundColor $Red
+ exit 1
+ }
+ Write-Host "ā
C# SDK packaged to $OutputLocation" -ForegroundColor $Green
+ } catch {
+ Write-Host "ā Failed to pack C# SDK: $_" -ForegroundColor $Red
+ exit 1
+ }
+
+ # Update dependent projects in Octopus Deploy repository
+ if (Test-Path $OctopusLocation) {
+ $DependentProjects = @()
+
+ # Check if specific projects were specified
+ if ($OctopusProjects) {
+ Write-Host "šÆ Updating specified projects..." -ForegroundColor $Yellow
+ $ProjectArray = $OctopusProjects.Split(",")
+
+ # Find csproj files for specified projects
+ foreach ($projectName in $ProjectArray) {
+ $ProjectPaths = Get-ChildItem -Path $OctopusLocation -Recurse -Filter "${projectName}.csproj" -ErrorAction SilentlyContinue
+ if ($ProjectPaths) {
+ $DependentProjects += $ProjectPaths
+ } else {
+ Write-Host "ā ļø Project not found: $projectName" -ForegroundColor $Yellow
+ }
+ }
+ } else {
+ # Search for all projects that reference the package
+ Write-Host "š Searching for projects referencing Octopus.Kubernetes.Monitor.MessageContracts..." -ForegroundColor $Yellow
+ $AllProjects = Get-ChildItem -Path $OctopusLocation -Recurse -Filter "*.csproj" -ErrorAction SilentlyContinue
+ foreach ($project in $AllProjects) {
+ $content = Get-Content $project.FullName -Raw
+ if ($content -match 'Octopus\.Kubernetes\.Monitor\.MessageContracts') {
+ $DependentProjects += $project
+ }
+ }
+ }
+
+ # Update each project file
+ $UpdatedCount = 0
+ foreach ($project in $DependentProjects) {
+ Write-Host "š Updating $($project.FullName) to use version $PrereleaseVersion" -ForegroundColor $Yellow
+
+ $content = Get-Content $project.FullName -Raw
+ $updatedContent = $content -replace ' $($RepoPath.Replace('\', '/'))/go"
+
+ $GoModContent = Get-Content $GoModFile -Raw
+
+ # Check if the replace directive already exists
+ if ($GoModContent -match 'replace github\.com/OctopusDeploy/kubernetes-monitor-contracts/go =>' ) {
+ # Update existing directive
+ $GoModContent = $GoModContent -replace 'replace github\.com/OctopusDeploy/kubernetes-monitor-contracts/go =>.*', $ReplaceDirective
+ } else {
+ # Add new directive at the end of the file
+ $GoModContent = $GoModContent + "`n$ReplaceDirective`n"
+ }
+
+ Set-Content -Path $GoModFile -Value $GoModContent
+ Write-Host "ā
Added Go module replace directive to $GoModFile" -ForegroundColor $Green
+ }
+
+ default {
+ Write-Host "ā ļø Unsupported language: $lang" -ForegroundColor $Yellow
+ }
+ }
+}
+
+Write-Host "`n⨠Local development setup complete!" -ForegroundColor $Green
\ No newline at end of file
diff --git a/build-local.sh b/build-local.sh
new file mode 100755
index 0000000..a744bed
--- /dev/null
+++ b/build-local.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+# Colors for prettier output
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+RED='\033[0;31m'
+CYAN='\033[0;36m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Default values
+LANGUAGES="go,csharp"
+OUTPUT_LOCATION="../LocalPackages"
+OCTOPUS_DEPLOY_LOCATION="../OctopusDeploy"
+K8S_MONITOR_LOCATION="../lobster-watcher"
+OCTOPUS_PROJECTS=""
+
+# Parse command line arguments
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --languages=*)
+ LANGUAGES="${1#*=}"
+ shift
+ ;;
+ --output=*)
+ OUTPUT_LOCATION="${1#*=}"
+ shift
+ ;;
+ --octopus=*)
+ OCTOPUS_DEPLOY_LOCATION="${1#*=}"
+ shift
+ ;;
+ --k8s-monitor=*)
+ K8S_MONITOR_LOCATION="${1#*=}"
+ shift
+ ;;
+ --octopus-projects=*)
+ OCTOPUS_PROJECTS="${1#*=}"
+ shift
+ ;;
+ -h|--help)
+ echo -e "${CYAN}Usage: $0 [options]${NC}"
+ echo "Options:"
+ echo " --languages=LANGS Comma-separated list of languages (default: go,csharp)"
+ echo " --output=PATH Output location for built packages (default: ../LocalPackages)"
+ echo " --octopus=PATH Octopus Deploy project location (default: ../OctopusDeploy)"
+ echo " --k8s-monitor=PATH Kubernetes Monitor project location (default: ../lobster-watcher)"
+ echo " --octopus-projects=LIST Comma-separated list of specific Octopus projects to update"
+ echo " -h, --help Show this help message"
+ exit 0
+ ;;
+ *)
+ echo -e "${RED}Unknown option: $1${NC}"
+ echo "Use -h or --help for usage information"
+ exit 1
+ ;;
+ esac
+done
+
+# Convert languages string to array
+IFS=',' read -ra LANGUAGE_ARRAY <<< "$LANGUAGES"
+
+# Create output directory if it doesn't exist
+mkdir -p "$OUTPUT_LOCATION"
+
+# Get absolute path of current directory (repository root)
+REPO_PATH=$(pwd)
+
+echo -e "\n${BLUE}=== Generating SDKs ===${NC}"
+echo -e "${YELLOW}Running buf generate...${NC}"
+buf generate || { echo -e "${RED}ā Failed to generate SDKs${NC}"; exit 1; }
+echo -e "${GREEN}ā
SDKs generated successfully${NC}\n"
+
+# Process each language
+for lang in "${LANGUAGE_ARRAY[@]}"; do
+ case "$lang" in
+ "csharp")
+ echo -e "${BLUE}=== Processing C# SDK ===${NC}"
+
+ # Get the current version from csproj
+ CSPROJ_PATH="csharp/Octopus/Octopus.Kubernetes.Monitor.MessageContracts/Octopus.Kubernetes.Monitor.MessageContracts.csproj"
+ BASE_VERSION=$(grep -o '[^<]*' "$CSPROJ_PATH" | sed 's/\(.*\)<\/Version>/\1/')
+
+ if [ -z "$BASE_VERSION" ]; then
+ BASE_VERSION="1.0.0"
+ fi
+
+ # Get the current branch name
+ BRANCH_NAME=$(git branch --show-current | sed 's/\//-/g')
+
+ # Generate a timestamp
+ TIMESTAMP=$(date +"%Y%m%d%H%M%S")
+
+ # Create prerelease version string
+ PRERELEASE_VERSION="${BASE_VERSION}-${BRANCH_NAME}-${TIMESTAMP}"
+
+ echo -e "${YELLOW}š¦ Packing C# SDK with version: ${PRERELEASE_VERSION}${NC}"
+
+ # Pack with debug configuration and prerelease version
+ dotnet pack "$CSPROJ_PATH" -c Debug -p:Version="$PRERELEASE_VERSION" -o "$OUTPUT_LOCATION" || {
+ echo -e "${RED}ā Failed to pack C# SDK${NC}";
+ exit 1;
+ }
+ echo -e "${GREEN}ā
C# SDK packaged to $OUTPUT_LOCATION${NC}\n"
+
+ # Update dependent projects in Octopus Deploy repository
+ if [ -d "$OCTOPUS_DEPLOY_LOCATION" ]; then
+ DEPENDENT_PROJECTS=""
+
+ # Check if specific projects were specified
+ if [ -n "$OCTOPUS_PROJECTS" ]; then
+ echo -e "${YELLOW}šÆ Updating specified projects...${NC}"
+ # Convert projects string to array
+ IFS=',' read -ra PROJECT_ARRAY <<< "$OCTOPUS_PROJECTS"
+
+ # Find csproj files for specified projects
+ for project_name in "${PROJECT_ARRAY[@]}"; do
+ PROJECT_PATHS=$(find "$OCTOPUS_DEPLOY_LOCATION" -name "${project_name}.csproj" 2>/dev/null)
+ if [ -n "$PROJECT_PATHS" ]; then
+ # Add to the list of dependent projects if found
+ DEPENDENT_PROJECTS="${DEPENDENT_PROJECTS}${PROJECT_PATHS}"$'\n'
+ else
+ echo -e "${YELLOW}ā ļø Project not found: $project_name${NC}"
+ fi
+ done
+ else
+ # Search for all projects that reference the package
+ echo -e "${YELLOW}š Searching for projects referencing Octopus.Kubernetes.Monitor.MessageContracts...${NC}"
+ DEPENDENT_PROJECTS=$(find "$OCTOPUS_DEPLOY_LOCATION" -name "*.csproj" -exec grep -l "Octopus.Kubernetes.Monitor.MessageContracts" {} \;)
+ fi
+
+ # Trim trailing newline
+ DEPENDENT_PROJECTS=$(echo "$DEPENDENT_PROJECTS" | sed '/^$/d')
+
+ # Update each project file
+ for project in $DEPENDENT_PROJECTS; do
+ echo -e "${YELLOW}š Updating $project to use version $PRERELEASE_VERSION${NC}"
+
+ # Different sed syntax for macOS vs Linux
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ # macOS version
+ sed -i '' -E "s|.*|$REPLACE_DIRECTIVE|" "$GO_MOD_FILE" && rm -f "${GO_MOD_FILE}.bak"
+ else
+ # Add new directive at the end of the file
+ echo "" >> "$GO_MOD_FILE"
+ echo "$REPLACE_DIRECTIVE" >> "$GO_MOD_FILE"
+ fi
+ echo -e "${GREEN}ā
Added Go module replace directive to $GO_MOD_FILE${NC}\n"
+ ;;
+ *)
+ echo -e "${YELLOW}ā ļø Unsupported language: $lang${NC}\n"
+ ;;
+ esac
+done
+
+echo -e "${GREEN}⨠Local development setup complete!${NC}"
\ No newline at end of file