Repository: GoogleCloudPlatform/k8s-config-connector
Pull request #3823: Add conductor commands to generate Types, Mapper, CRD and fuzzer
Author: login: barney-s
Labels: name: lgtm
name: approved
Created: 2025-02-28T23:05:55Z | Merged: 2025-03-04T19:38:06Z

CL that is using this tooling: https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/3822/commits


Diff:
diff --git a/.gitignore b/.gitignore
index 02c9a449c0f..e462f037851 100644
--- a/.gitignore
+++ b/.gitignore
@@ -159,4 +159,7 @@ go.work.sum
 e2e.log
 testlogs/
 log
+
+# experiments/conductor
 experiments/conductor/logs
+experiments/conductor/branches.yaml
diff --git a/experiments/conductor/cmd/runner/crd_generator_commands.go b/experiments/conductor/cmd/runner/crd_generator_commands.go
new file mode 100644
index 00000000000..e00aaabba9b
--- /dev/null
+++ b/experiments/conductor/cmd/runner/crd_generator_commands.go
@@ -0,0 +1,333 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+/*
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Stage the changed files
+	scriptRelativePath := filepath.Join("apis", branch.Group, "v1alpha1", "generate.sh")
+	gitAdd(workDir, &out, scriptRelativePath)
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Stage the changed files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+*/
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev", "tools", "controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Generate mapper
+	cfg = CommandConfig{
+		Name: "Generate mapper",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-mapper",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Run goimports
+	cfg = CommandConfig{
+		Name: "Format generated code",
+		Cmd:  "go",
+		Args: []string{
+			"run", "-mod=readonly",
+			"golang.org/x/tools/cmd/goimports@latest",
+			"-w",
+			fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		},
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Stage the changed files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group))
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("Generated types and mapper for %s", branch.Kind))
+}
+
+func generateCRD(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Generate CRDs
+	cfg := CommandConfig{
+		Name:    "Generate CRDs",
+		Cmd:     filepath.Join(workDir, "dev", "tasks", "generate-crds"),
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Stage the changed files
+	gitAdd(workDir, &out, "config/crds/resources/")
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("Generated CRD for %s", branch.Kind))
+}
+
+func generateSpecStatus(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Run controllerbuilder to generate spec and status
+	log.Printf("Generating spec and status for %s", branch.Name)
+	stdinInput := fmt.Sprintf("// +kcc:proto=%s.%s\n", branch.ProtoSvc, branch.Proto)
+
+	cfg := CommandConfig{
+		Name:    "Spec/Status generation",
+		Cmd:     "controllerbuilder",
+		Args:    []string{"prompt", "--src-dir", workDir, "--proto-dir", filepath.Join(workDir, ".build", "third_party", "googleapis")},
+		WorkDir: workDir,
+		Stdin:   strings.NewReader(stdinInput),
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Stage the changed files
+	gitAdd(workDir, &out, fmt.Sprintf("apis/%s/v1alpha1/%s_types.go", branch.Group, strings.ToLower(branch.Resource)))
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("%s: Update types from generated", branch.Kind))
+}
+
+func generateFuzzer(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Generate fuzzer file
+	fuzzerDir := filepath.Join(workDir, "pkg", "controller", "direct", branch.Group)
+	if err := os.MkdirAll(fuzzerDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	fuzzerPath := filepath.Join(fuzzerDir, fmt.Sprintf("%s_fuzzer.go", strings.ToLower(branch.Resource)))
+	stdinInput := fmt.Sprintf(`// +tool:fuzz-gen
+// proto.message: %s
+`, branch.ProtoMsg)
+
+	var fuzzerOut strings.Builder
+	cfg := CommandConfig{
+		Name:    "Fuzzer generation",
+		Cmd:     "controllerbuilder",
+		Args:    []string{"prompt", "--src-dir", workDir, "--proto-dir", filepath.Join(workDir, ".build", "third_party", "googleapis")},
+		WorkDir: workDir,
+		Stdin:   strings.NewReader(stdinInput),
+	}
+	if err := executeCommand(cfg, &fuzzerOut, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	if err := os.WriteFile(fuzzerPath, []byte(fuzzerOut.String()), 0644); err != nil {
+		log.Fatal(err)
+	}
+
+	// Update register.go to import the new package
+	registerPath := filepath.Join(workDir, "pkg", "controller", "direct", "register", "register.go")
+	importLine := fmt.Sprintf(`_ "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/%s"`, branch.Group)
+	stdinInput = fmt.Sprintf("Add an unnamed (_) go import for %s to the imports in %s", importLine, registerPath)
+
+	cfg = CommandConfig{
+		Name:    "Import addition",
+		Cmd:     "codebot",
+		Args:    []string{"--prompt=/dev/stdin"},
+		WorkDir: workDir,
+		Stdin:   strings.NewReader(stdinInput),
+	}
+	if err := executeCommand(cfg, &out, nil); err != nil {
+		log.Fatal(err)
+	}
+
+	// Stage the changed files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("pkg/controller/direct/%s/%s_fuzzer.go", branch.Group, strings.ToLower(branch.Resource)),
+		"pkg/controller/direct/register/register.go")
+
+	// Commit the changes
+	gitCommit(workDir, &out, fmt.Sprintf("%s: Create fuzz test", branch.Kind))
+}
diff --git a/experiments/conductor/cmd/runner/mock_commands.go b/experiments/conductor/cmd/runner/mock_commands.go
index 6e5d93f2207..f9643b635da 100644
--- a/experiments/conductor/cmd/runner/mock_commands.go
+++ b/experiments/conductor/cmd/runner/mock_commands.go
@@ -100,12 +100,14 @@ func createScriptYaml(opts *RunnerOptions, branch Branch) {
 	if err := codebot.Run(); err != nil {
 		stop := time.Now()
 		diff := stop.Sub(start)
-		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, out.String())
+		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, formatCommandOutput(out.String()))
+		out.Reset()
 		log.Fatal(err)
 	}
 	stop := time.Now()
 	diff := stop.Sub(start)
-	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, out.String())
+	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, formatCommandOutput(out.String()))
+	out.Reset()
 
 	// Check to see if the script file was created
 	if _, err := os.Stat(scriptFullPath); errors.Is(err, os.ErrNotExist) {
@@ -182,13 +184,15 @@ func captureHttpLog(opts *RunnerOptions, branch Branch) {
 	test.Stdout = &out
 	test.Stderr = &out
 	if err := test.Run(); err != nil {
-		log.Printf("TEST GENERATE error: %q\n", out.String())
+		log.Printf("TEST GENERATE error: %q\n", formatCommandOutput(out.String()))
+		out.Reset()
 		// Currently ignoring error and just basing on if the _http.log was generated.
 		// log.Fatal(err)
 	}
 	stop := time.Now()
 	diff := stop.Sub(start)
-	log.Printf("TEST GENERATE (%v): %q\n", diff, out.String())
+	log.Printf("TEST GENERATE (%v): %q\n", diff, formatCommandOutput(out.String()))
+	out.Reset()
 
 	// Check to see if the script file was created
 	if _, err := os.Stat(logFullPath); errors.Is(err, os.ErrNotExist) {
@@ -254,7 +258,8 @@ func generateMockGo(opts *RunnerOptions, branch Branch) {
 		service_go.Stdout = &serviceOut
 		service_go.Stderr = &out
 		if err := service_go.Run(); err != nil {
-			log.Println(out.String())
+			log.Println(formatCommandOutput(out.String()))
+			out.Reset()
 			log.Printf("MOCK SERVICE GENERATE error: %q\n", err)
 			// Currently ignoring error and just basing on if the _http.log was generated.
 			// log.Fatal(err)
@@ -264,7 +269,8 @@ func generateMockGo(opts *RunnerOptions, branch Branch) {
 		if err := os.WriteFile(serviceFile, []byte(serviceOut.String()), 0755); err != nil {
 			log.Printf("WRITE MOCK SERVICE %s error: %q\n", serviceFile, err)
 		}
-		log.Printf("MOCK SERVICE GENERATE (%v): %q\n", diff, serviceOut.String())
+		log.Printf("MOCK SERVICE GENERATE (%v): %q\n", diff, formatCommandOutput(serviceOut.String()))
+		serviceOut.Reset()
 
 		// Check to see if the service go file was created
 		if _, err := os.Stat(serviceFile); errors.Is(err, os.ErrNotExist) {
@@ -295,7 +301,8 @@ func generateMockGo(opts *RunnerOptions, branch Branch) {
 		resource_go.Stdout = &resourceOut
 		resource_go.Stderr = &out
 		if err := resource_go.Run(); err != nil {
-			log.Println(out.String())
+			log.Println(formatCommandOutput(out.String()))
+			out.Reset()
 			log.Printf("MOCK RESOURCE GENERATE error: %q\n", err)
 			// Currently ignoring error and just basing on if the _http.log was generated.
 			// log.Fatal(err)
@@ -305,7 +312,8 @@ func generateMockGo(opts *RunnerOptions, branch Branch) {
 		if err := os.WriteFile(resourceFile, []byte(resourceOut.String()), 0755); err != nil {
 			log.Printf("WRITE MOCK RESOURCE %s error: %q\n", resourceFile, err)
 		}
-		log.Printf("MOCK RESOURCE GENERATE (%v): %q\n", diff, resourceOut.String())
+		log.Printf("MOCK RESOURCE GENERATE (%v): %q\n", diff, formatCommandOutput(resourceOut.String()))
+		resourceOut.Reset()
 
 		// Check to see if the service go file was created
 		if _, err := os.Stat(resourceFile); errors.Is(err, os.ErrNotExist) {
@@ -365,12 +373,14 @@ func addServiceToRoundTrip(opts *RunnerOptions, branch Branch) {
 	if err := codebot.Run(); err != nil {
 		stop := time.Now()
 		diff := stop.Sub(start)
-		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, out.String())
+		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, formatCommandOutput(out.String()))
+		out.Reset()
 		log.Fatal(err)
 	}
 	stop := time.Now()
 	diff := stop.Sub(start)
-	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, out.String())
+	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, formatCommandOutput(out.String()))
+	out.Reset()
 
 	// Add the new files to the current branch.
 	gitAdd(workDir, &out, "mock_http_roundtrip.go")
@@ -423,20 +433,21 @@ func addProtoToMakfile(opts *RunnerOptions, branch Branch) {
 	if err := codebot.Run(); err != nil {
 		stop := time.Now()
 		diff := stop.Sub(start)
-		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, out.String())
+		log.Printf("CODEBOT GENERATE ERROR (%v): %q\n", diff, formatCommandOutput(out.String()))
+		out.Reset()
 		log.Fatal(err)
 	}
 
 	stop := time.Now()
 	diff := stop.Sub(start)
-	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, out.String())
+	log.Printf("CODEBOT GENERATE (%v): %q\n", diff, formatCommandOutput(out.String()))
+	out.Reset()
 
 	// Add the new files to the current branch.
 	gitAdd(workDir, &out, "Makefile")
 
 	// Commit the change to the current branch.
 	gitCommit(workDir, &out, fmt.Sprintf("Adding proto to Makefile for %s", branch.Name))
-
 }
 
 func runMockgcpTests(opts *RunnerOptions, branch Branch) {
@@ -462,13 +473,15 @@ func runMockgcpTests(opts *RunnerOptions, branch Branch) {
 	test.Stdout = &out
 	test.Stderr = &out
 	if err := test.Run(); err != nil {
-		log.Printf("TEST RUN error: %q\n", out.String())
+		log.Printf("TEST RUN error: %q\n", formatCommandOutput(out.String()))
+		out.Reset()
 		// Currently ignoring error and just basing on if the _http.log was generated.
 		// log.Fatal(err)
 	}
 	stop := time.Now()
 	diff := stop.Sub(start)
-	log.Printf("TEST RUN (%v): %q\n", diff, out.String())
+	log.Printf("TEST RUN (%v): %q\n", diff, formatCommandOutput(out.String()))
+	out.Reset()
 
 	// Check to see if the script file was created
 	if _, err := os.Stat(logFullPath); errors.Is(err, os.ErrNotExist) {
diff --git a/experiments/conductor/cmd/runner/runner.go b/experiments/conductor/cmd/runner/runner.go
index 0af36bf7a9f..153d17e0467 100644
--- a/experiments/conductor/cmd/runner/runner.go
+++ b/experiments/conductor/cmd/runner/runner.go
@@ -34,6 +34,27 @@ const (
 # Run commands in multiple branches.
 conductor runner --branch-repo=/usr/local/google/home/wfender/go/src/github.com/cheftako/k8s-config-connector_branches
 `
+
+	// flag names.
+	branchConfigurationFlag = "branch-conf"
+	branchRepoFlag          = "branch-repo"
+	commandFlag             = "command"
+	loggingDirFlag          = "logging-dir"
+
+	// Command values
+	cmdHelp                = 0
+	cmdCheckRepo           = 1
+	cmdCreateGitBranch     = 2
+	cmdDeleteGitBranch     = 3
+	cmdCreateScriptYaml    = 4
+	cmdCaptureHttpLog      = 5
+	cmdGenerateMockGo      = 6
+	cmdAddServiceRoundTrip = 7
+	cmdAddProtoMakefile    = 8
+	cmdRunMockTests        = 9
+	cmdGenerateTypes       = 10
+	cmdGenerateCRD         = 11
+	cmdGenerateFuzzer      = 12
 )
 
 func BuildRunnerCmd() *cobra.Command {
@@ -61,14 +82,6 @@ func BuildRunnerCmd() *cobra.Command {
 	return cmd
 }
 
-const (
-	// flag names.
-	branchConfigurationFlag = "branch-conf"
-	branchRepoFlag          = "branch-repo"
-	commandFlag             = "command"
-	loggingDirFlag          = "logging-dir"
-)
-
 type RunnerOptions struct {
 	branchConfFile string
 	branchRepoDir  string
@@ -147,11 +160,11 @@ func RunRunner(ctx context.Context, opts *RunnerOptions) error {
 		splitMetadata(opts, branches)
 	case -1:
 		fixMetadata(opts, branches)
-	case 0:
+	case cmdHelp: // 0
 		printHelp()
-	case 1:
+	case cmdCheckRepo: // 1
 		checkRepoDir(opts, branches)
-	case 2:
+	case cmdCreateGitBranch: // 2
 		for idx, branch := range branches.Branches {
 			/*
 				if branch.Controller != "Unknown" {
@@ -162,43 +175,59 @@ func RunRunner(ctx context.Context, opts *RunnerOptions) error {
 			log.Printf("Create GitHub Branch: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			createGithubBranch(opts, branch)
 		}
-	case 3:
+	case cmdDeleteGitBranch: // 3
 		for idx, branch := range branches.Branches {
 			log.Printf("Delete GitHub Branch: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			deleteGithubBranch(opts, branch)
 		}
-	case 4:
+	case cmdCreateScriptYaml: // 4
 		for idx, branch := range branches.Branches {
 			log.Printf("Create Script YAML: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			createScriptYaml(opts, branch)
 		}
-	case 5:
+	case cmdCaptureHttpLog: // 5
 		for idx, branch := range branches.Branches {
 			log.Printf("Capture HTTP Log: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			captureHttpLog(opts, branch)
 		}
-	case 6:
+	case cmdGenerateMockGo: // 6
 		for idx, branch := range branches.Branches {
 			log.Printf("Generate mock Service and Resource go files: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			generateMockGo(opts, branch)
 		}
-	case 7:
+	case cmdAddServiceRoundTrip: // 7
 		for idx, branch := range branches.Branches {
 			log.Printf("Add service to mock_http_roundtrip.go: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			addServiceToRoundTrip(opts, branch)
 		}
-	case 8:
+	case cmdAddProtoMakefile: // 8
 		for idx, branch := range branches.Branches {
 			log.Printf("Add proto to makefile: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			addProtoToMakfile(opts, branch)
 		}
-	case 9:
+	case cmdRunMockTests: // 9
 		for idx, branch := range branches.Branches {
 			log.Printf("Run mockgcptests on generated mocks: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
 			runMockgcpTests(opts, branch)
 		}
+	case cmdGenerateTypes: // 10
+		for idx, branch := range branches.Branches {
+			log.Printf("Generate Types and Mapper: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
+			generateTypesAndMapper(opts, branch)
+		}
+	case cmdGenerateCRD: // 11
+		for idx, branch := range branches.Branches {
+			log.Printf("Generate CRD: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
+			generateCRD(opts, branch)
+			//generateSpecStatus(opts, branch)
+		}
+	case cmdGenerateFuzzer: // 12
+		for idx, branch := range branches.Branches {
+			log.Printf("Generate Fuzzer: %d name: %s, branch: %s\r\n", idx, branch.Name, branch.Local)
+			generateFuzzer(opts, branch)
+		}
 	default:
-		log.Fatalf("unrecognixed command: %d", opts.command)
+		log.Fatalf("unrecognized command: %d", opts.command)
 	}
 	return nil
 }
@@ -216,6 +245,9 @@ func printHelp() {
 	log.Println("\t7 - [Mock] Add service to mock_http_roundtrip.go in each github branch")
 	log.Println("\t8 - [Mock] Add proto to makefile in each github branch")
 	log.Println("\t9 - [Mock] Run mockgcptests on generated mocks in each github branch")
+	log.Println("\t10 - [CRD] Generate Types and Mapper for each branch")
+	log.Println("\t11 - [CRD] Generate CRD for each branch")
+	log.Println("\t12 - [Fuzzer] Generate fuzzer for each branch")
 }
 
 func checkRepoDir(opts *RunnerOptions, branches Branches) {
diff --git a/experiments/conductor/cmd/runner/utilities.go b/experiments/conductor/cmd/runner/utilities.go
index b031e891a0b..8cbf743ff99 100644
--- a/experiments/conductor/cmd/runner/utilities.go
+++ b/experiments/conductor/cmd/runner/utilities.go
@@ -15,6 +15,7 @@
 package runner
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"io"
@@ -23,6 +24,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"strings"
+	"time"
 )
 
 type exitBash func()
@@ -100,7 +102,8 @@ func checkoutBranch(branch Branch, workDir string, out *strings.Builder) {
 	if err := checkout.Run(); err != nil {
 		log.Fatal(err)
 	}
-	log.Printf("BRANCH CHECKOUT: %q\n", out.String())
+	log.Printf("BRANCH CHECKOUT: %q\n", formatCommandOutput(out.String()))
+	out.Reset()
 }
 
 func writeTemplateToFile(branch Branch, filePath string, template string) {
@@ -111,6 +114,12 @@ func writeTemplateToFile(branch Branch, filePath string, template string) {
 	tmp = strings.ReplaceAll(tmp, "<HTTP_HOST>", branch.HostName)
 	tmp = strings.ReplaceAll(tmp, "<PROTO_SERVICE>", branch.ProtoSvc)
 	tmp = strings.ReplaceAll(tmp, "<PROTO_MESSAGE>", branch.ProtoMsg)
+	tmp = strings.ReplaceAll(tmp, "<PROTO_PACKAGE>", branch.Package)
+	tmp = strings.ReplaceAll(tmp, "<CRD_GROUP>", fmt.Sprintf("%s.cnrm.cloud.google.com", branch.Group))
+	tmp = strings.ReplaceAll(tmp, "<CRD_VERSION>", "v1alpha1")
+	tmp = strings.ReplaceAll(tmp, "<CRD_KIND>", branch.Kind)
+	tmp = strings.ReplaceAll(tmp, "<PROTO_RESOURCE>", branch.Proto)
+
 	contents := strings.ReplaceAll(tmp, "<RESOURCE>", strings.ToLower(branch.Resource))
 	log.Printf("TEMPLATE %s %s", filePath, contents)
 
@@ -146,23 +155,27 @@ func gitAdd(workDir string, out *strings.Builder, files ...string) {
 	gitadd.Stdout = out
 	gitadd.Stderr = out
 	if err := gitadd.Run(); err != nil {
-		log.Printf("GIT add error: %q\n", out.String())
+		log.Printf("GIT add error: %q\n", formatCommandOutput(out.String()))
+		out.Reset()
 		log.Fatal(err)
 	}
-	log.Printf("BRANCH ADD: %q\n", out.String())
+	log.Printf("BRANCH ADD: %q\n", formatCommandOutput(out.String()))
+	out.Reset()
 }
 
 func gitCommit(workDir string, out *strings.Builder, msg string) {
 	log.Printf("COMMAND: git commit -m %q", msg)
-	gitcommit := exec.Command("git", "commit", "-m", fmt.Sprintf("%q", msg))
+	gitcommit := exec.Command("git", "commit", "-m", fmt.Sprintf("conductor: %q", msg))
 	gitcommit.Dir = workDir
 	gitcommit.Stdout = out
 	gitcommit.Stderr = out
 	if err := gitcommit.Run(); err != nil {
-		log.Printf("GIT commit error: %q\n", out.String())
+		log.Printf("GIT commit error: %q\n", formatCommandOutput(out.String()))
+		out.Reset()
 		log.Fatal(err)
 	}
-	log.Printf("BRANCH COMMIT: %q\n", out.String())
+	log.Printf("BRANCH COMMIT: %q\n", formatCommandOutput(out.String()))
+	out.Reset()
 }
 
 type closer func()
@@ -222,3 +235,96 @@ func setLoggingWriter(opts *RunnerOptions, branch Branch) closer {
 
 func noOp() {
 }
+
+func printCommandOutput(output string) {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+
+	// Split the string into lines and format each line
+	lines := strings.Split(formatted, "\n")
+	for _, line := range lines {
+		log.Printf("  > %s\n", line)
+	}
+}
+
+func formatCommandOutput(output string) string {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+	return formatted
+}
+
+// CommandConfig holds the configuration for executing a command
+type CommandConfig struct {
+	Name    string            // Name of the command for logging
+	Cmd     string            // The command to run
+	Args    []string          // Command arguments
+	WorkDir string            // Working directory
+	Stdin   io.Reader         // Optional stdin
+	Env     map[string]string // Optional environment variables
+	Timeout time.Duration     // Timeout duration (default 5m)
+}
+
+// Helper function to execute a command with timing and logging
+func executeCommand(cfg CommandConfig, out *strings.Builder, stderr *strings.Builder) error {
+	if cfg.Timeout == 0 {
+		cfg.Timeout = 5 * time.Minute
+	}
+
+	log.Printf("Starting command step: %s", cfg.Name)
+	log.Printf("[%s] working directory: %s", cfg.Name, cfg.WorkDir)
+	log.Printf("[%s] command: %s %s", cfg.Name, cfg.Cmd, strings.Join(cfg.Args, " "))
+	if cfg.Stdin != nil {
+		log.Printf("[%s] stdin: %s", cfg.Name, cfg.Stdin)
+	}
+	if len(cfg.Env) > 0 {
+		log.Printf("[%s] environment:", cfg.Name)
+		for k, v := range cfg.Env {
+			log.Printf("  %s=%s", k, v)
+		}
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), cfg.Timeout)
+	defer cancel()
+
+	cmd := exec.CommandContext(ctx, cfg.Cmd, cfg.Args...)
+	cmd.Dir = cfg.WorkDir
+	cmd.Stdout = out
+	if stderr != nil {
+		cmd.Stderr = stderr
+	} else {
+		cmd.Stderr = out
+	}
+	if cfg.Stdin != nil {
+		cmd.Stdin = cfg.Stdin
+	}
+
+	// Set up environment variables
+	if len(cfg.Env) > 0 {
+		cmd.Env = os.Environ() // Start with current environment
+		for k, v := range cfg.Env {
+			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
+		}
+	}
+
+	start := time.Now()
+
+	err := cmd.Run()
+	stop := time.Now()
+	diff := stop.Sub(start)
+	if err != nil {
+		log.Printf("[%s] ERROR (%v): \n", cfg.Name, diff)
+		log.Printf("[%s] err: %q\n", cfg.Name, err)
+	} else {
+		log.Printf("[%s] SUCCESS (%v): \n", cfg.Name, diff)
+	}
+	printCommandOutput(out.String())
+	if stderr != nil && stderr.Len() > 0 {
+		log.Printf("[%s] stderr output:\n", cfg.Name)
+		printCommandOutput(stderr.String())
+		stderr.Reset()
+	}
+	out.Reset()
+	return err
+}


Reviews:
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-28T23:37:26Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-28T23:55:27Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-28T23:56:11Z
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T00:06:54Z
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T00:09:07Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-01T00:17:13Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T06:40:13Z
user: login: cheftako
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:00:53Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:06:24Z
user: login: cheftako
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:06:40Z
user: login: cheftako
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:08:18Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:09:01Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T19:11:10Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T01:00:54Z
user: login: cheftako
body: Good start.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T05:50:10Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T06:02:47Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T06:02:59Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T06:03:29Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T06:09:34Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T06:10:16Z
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T08:19:13Z
user: login: barney-s
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T17:30:23Z
user: login: acpana
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T18:28:37Z

Review comments:
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: jingyih
body: Note that this will no longer work if a second resource is being generated under the same `--api-version`. This is a broader discussion, but I think we should have one git branch per `--api-version`, rather than per `--resource`.
created_at: 2025-02-28T23:33:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,49 +13,31 @@
 # limitations under the License.
 
 branches:
-  - name: accessapproval-approvalrequest
-    local: resource-accessapproval-approvalrequest
-    remote: resource-accessapproval-approvalrequest
-    command: ""
-    group: accessapproval
-    resource: approvalrequest
-    controller: Unknown
-    kind: AccessApprovalApprovalRequest
-    package: google.cloud.accessapproval.v1
-    proto: ApprovalRequest
-    proto-path: google/cloud/accessapproval/v1/accessapproval.proto
-    proto-service: ""
-    proto-msg: google.cloud.accessapproval.v1.ApprovalRequest
-    host-name: ""
-    notes:
-      - No gcloud command found
-  - name: ai-customjobs
-    local: resource-ai-customjobs
-    remote: resource-ai-customjobs
-    command: gcloud ai custom-jobs
-    group: ai
-    resource: customjobs
-    controller: Unknown
-    kind: ""
-    package: ""
-    proto: ""
-    proto-path: ""
-    proto-service: ""
-    proto-msg: ""
-    host-name: ""
-    notes: []
-  - name: compute-computebackendservice
-    local: resource-compute-computebackendservice
-    remote: resource-compute-computebackendservice
-    command: gcloud compute backend-services
-    group: compute
-    resource: computebackendservice
-    controller: terraform
-    kind: ""
-    package: ""
-    proto: BackendService
-    proto-path: google/cloud/compute/v1/compute.proto
-    proto-service: google.cloud.compute.v1.BackendServices
-    proto-msg: google.cloud.compute.v1.BackendService
-    host-name: compute.googleapis.com
-    notes: []
+    - name: notebooks-instance
+      local: resource-notebooks-instance
+      remote: resource-notebooks-instance
+      command: gcloud notebooks instances
+      group: notebooks
+      resource: instance
+      controller: Unknown
+      kind: NotebooksInstance
+      package: google.cloud.notebooks.v1
+      proto: Instance
+      proto-path: google/cloud/notebooks/v1/service.proto
+      proto-service: "google.cloud.notebooks.v1.NotebookService"
+      proto-msg: google.cloud.notebooks.v1.Instance
path: experiments/conductor/branches.yaml
user: login: jingyih
body: Not related to this PR, but is `proto` the same as `proto-msg`, with the only difference being that `proto-msg` is the fully qualified name (i.e., `package` + `proto`)?
created_at: 2025-02-28T23:33:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: i noticed it here: https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/3822/files#diff-dd75f3353e0a712412b061f3565c24dca0d5379d59d78f37235e8a28d9f638d6 

My question is do we run this script only once ?
Or do we run this script often.

Concern: If we make changes in the types.go for say Environment. Then i add instance it would run both. Would it revert the changes for environtment_types.go ? 

created_at: 2025-02-28T23:55:27Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: alternatively i will change the code to create generate_<type>.sh ?
created_at: 2025-02-28T23:56:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: jingyih
body: It will not revert `environtment_types.go` but it will regenerate `types.generated.go`. If we have one `generate.sh` per resource branch, they are going to fight with each other on `types.generated.go`.
created_at: 2025-03-01T00:06:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: jingyih
body: With the current tooling and code structure, we need to generate "CRD/mapper/types" per `--api-version`. i.e., one git branch per `--api-version`.
created_at: 2025-03-01T00:09:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: ok we may need to use llm to update existing file or create new one. 
created_at: 2025-03-01T00:17:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,198 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+const generatorScriptTemplate = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: done. works https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/3822/files#diff-dd75f3353e0a712412b061f3565c24dca0d5379d59d78f37235e8a28d9f638d6
created_at: 2025-03-03T06:40:12Z
author_association: COLLABORATOR
diff_hunk: @@ -13,49 +13,31 @@
 # limitations under the License.
 
 branches:
-  - name: accessapproval-approvalrequest
path: experiments/conductor/branches.yaml
user: login: cheftako
body: Probably should try avoiding stomping on each others branches.yaml. Maybe add it to the .gitignore?
created_at: 2025-03-03T19:00:53Z
author_association: COLLABORATOR
diff_hunk: @@ -13,49 +13,31 @@
 # limitations under the License.
 
 branches:
-  - name: accessapproval-approvalrequest
path: experiments/conductor/branches.yaml
user: login: barney-s
body: I added it to .gitignore, but it still shows up as diff (may be because the file exists in the repo).
created_at: 2025-03-03T19:06:24Z
author_association: COLLABORATOR
diff_hunk: @@ -13,49 +13,31 @@
 # limitations under the License.
 
 branches:
-  - name: accessapproval-approvalrequest
-    local: resource-accessapproval-approvalrequest
-    remote: resource-accessapproval-approvalrequest
-    command: ""
-    group: accessapproval
-    resource: approvalrequest
-    controller: Unknown
-    kind: AccessApprovalApprovalRequest
-    package: google.cloud.accessapproval.v1
-    proto: ApprovalRequest
-    proto-path: google/cloud/accessapproval/v1/accessapproval.proto
-    proto-service: ""
-    proto-msg: google.cloud.accessapproval.v1.ApprovalRequest
-    host-name: ""
-    notes:
-      - No gcloud command found
-  - name: ai-customjobs
-    local: resource-ai-customjobs
-    remote: resource-ai-customjobs
-    command: gcloud ai custom-jobs
-    group: ai
-    resource: customjobs
-    controller: Unknown
-    kind: ""
-    package: ""
-    proto: ""
-    proto-path: ""
-    proto-service: ""
-    proto-msg: ""
-    host-name: ""
-    notes: []
-  - name: compute-computebackendservice
-    local: resource-compute-computebackendservice
-    remote: resource-compute-computebackendservice
-    command: gcloud compute backend-services
-    group: compute
-    resource: computebackendservice
-    controller: terraform
-    kind: ""
-    package: ""
-    proto: BackendService
-    proto-path: google/cloud/compute/v1/compute.proto
-    proto-service: google.cloud.compute.v1.BackendServices
-    proto-msg: google.cloud.compute.v1.BackendService
-    host-name: compute.googleapis.com
-    notes: []
+    - name: notebooks-instance
+      local: resource-notebooks-instance
+      remote: resource-notebooks-instance
+      command: gcloud notebooks instances
+      group: notebooks
+      resource: instance
+      controller: Unknown
+      kind: NotebooksInstance
+      package: google.cloud.notebooks.v1
+      proto: Instance
+      proto-path: google/cloud/notebooks/v1/service.proto
+      proto-service: "google.cloud.notebooks.v1.NotebookService"
+      proto-msg: google.cloud.notebooks.v1.Instance
path: experiments/conductor/branches.yaml
user: login: cheftako
body: These are used to drive the automation. name and local are used for git operations and ids.
Interestingly proto-service, proto_path and proto-msg are used in templates.
I believe proto and remote are both essentially unused today.
created_at: 2025-03-03T19:06:40Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,227 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: Why are we generating a template for the bash command? Would see more consistent to exec the individual commands from with go.
created_at: 2025-03-03T19:08:18Z
author_association: COLLABORATOR
diff_hunk: @@ -13,49 +13,31 @@
 # limitations under the License.
 
 branches:
-  - name: accessapproval-approvalrequest
path: experiments/conductor/branches.yaml
user: login: barney-s
body: reverted the branches changes.
created_at: 2025-03-03T19:09:01Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,227 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: This file is checked into the repo. It is an artifact.

https://github.com/GoogleCloudPlatform/k8s-config-connector/blob/master/experiments/conductor/cmd/runner/scripts/crd_mapper_fuzzer/01-write-generator-script.sh#L43

Not sure if this is the standard working practice or just recently introduced. 
created_at: 2025-03-03T19:11:10Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,227 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: refactored the code to run the commands inline in go-code. 
created_at: 2025-03-04T01:00:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: filepath.Join is about getting the slashes correct. If your not concatenating directories/files, you don't need to call it.
created_at: 2025-03-04T05:35:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: This should be 
filepath.Join(workDir, "dev", "tools", "controllerbuilder")
created_at: 2025-03-04T05:35:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: Please make sure to log the error & out just in case before calling fatal.
created_at: 2025-03-04T05:39:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Generate mapper
+	cfg = CommandConfig{
+		Name: "Generate mapper",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-mapper",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: Ditto
created_at: 2025-03-04T05:39:42Z
author_association: COLLABORATOR
diff_hunk: @@ -222,3 +235,71 @@ func setLoggingWriter(opts *RunnerOptions, branch Branch) closer {
 
 func noOp() {
 }
+
+func printCommandOutput(output string) {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+
+	// Split the string into lines and format each line
+	lines := strings.Split(formatted, "\n")
+	for _, line := range lines {
+		log.Printf("  > %s\n", line)
+	}
+}
+
+func formatCommandOutput(output string) string {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+	return formatted
+}
+
+// CommandConfig holds the configuration for executing a command
+type CommandConfig struct {
+	Name    string        // Name of the command for logging
+	Cmd     string        // The command to run
+	Args    []string      // Command arguments
+	WorkDir string        // Working directory
+	Stdin   io.Reader     // Optional stdin
+	Timeout time.Duration // Timeout duration (default 5m)
+}
+
+// Helper function to execute a command with timing and logging
path: experiments/conductor/cmd/runner/utilities.go
user: login: cheftako
body: :D
created_at: 2025-03-04T05:47:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: cheftako
body: paths should be contructed using filepath.Join, at least for the slashes.
created_at: 2025-03-04T05:48:40Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: executeCommand logs it.
created_at: 2025-03-04T06:02:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Generate mapper
+	cfg = CommandConfig{
+		Name: "Generate mapper",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-mapper",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: executeCOmmand logs it.
created_at: 2025-03-04T06:02:59Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: of course. will change.
created_at: 2025-03-04T06:03:29Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: done.
created_at: 2025-03-04T06:09:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := fmt.Sprintf("apis/%s/v1alpha1/generate.sh", branch.Group)
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev/tools/controllerbuilder")
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: fixed. 
created_at: 2025-03-04T06:10:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := filepath.Join("apis", branch.Group, "v1alpha1", "generate.sh")
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev", "tools", "controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Generate mapper
+	cfg = CommandConfig{
+		Name: "Generate mapper",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-mapper",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Run goimports
+	cfg = CommandConfig{
+		Name: "Format generated code",
+		Cmd:  "go",
+		Args: []string{
+			"run", "-mod=readonly",
+			"golang.org/x/tools/cmd/goimports@latest",
+			"-w",
+			fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		},
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group))
+	gitCommit(workDir, &out, fmt.Sprintf("Generated types and mapper for %s", branch.Kind))
+}
+
+func generateCRD(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Generate CRDs
+	cfg := CommandConfig{
+		Name:    "Generate CRDs",
+		Cmd:     filepath.Join(workDir, "dev/tasks/generate-crds"),
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out, "config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("Generated CRD for %s", branch.Kind))
+}
+
+func generateSpecStatus(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Run controllerbuilder to generate spec and status
+	log.Printf("Generating spec and status for %s", branch.Name)
+	stdinInput := fmt.Sprintf("// +kcc:proto=%s.%s\n", branch.ProtoSvc, branch.Proto)
+
+	cfg := CommandConfig{
+		Name:    "Spec/Status generation",
+		Cmd:     "controllerbuilder",
+		Args:    []string{"prompt", "--src-dir", workDir, "--proto-dir", filepath.Join(workDir, ".build/third_party/googleapis/")},
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: jingyih
body: Do you have an example commit generated by this command? My experience is that this step currently does not work?
created_at: 2025-03-04T08:19:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,320 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runner
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// NOT USED
+const GENERATOR_SCRIPT_TEMPLATE = `#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd ${REPO_ROOT}/dev/tools/controllerbuilder
+
+go run . generate-types \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION> \
+    --resource <CRD_KIND>:<PROTO_RESOURCE>
+
+go run . generate-mapper \
+    --service <PROTO_PACKAGE> \
+    --api-version <CRD_GROUP>/<CRD_VERSION>
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+const UPDATE_GENERATE_SCRIPT_PROMPT = `
+Please update the apis/<SERVICE>/v1alpha1/generate.sh script for the <SERVICE> API to generate the CRD for the <CRD_KIND> resource.
+
+The generate.sh script is located at apis/<SERVICE>/v1alpha1/generate.sh.
+
+Add the parameter <TICK> --resource <CRD_KIND>:<PROTO_RESOURCE><TICK> to the <TICK>go run . generate-types --api-version <CRD_GROUP>/<CRD_VERSION>  <TICK> command.
+
+At the end of the script, ensure the following lines are present:
+
+cd ${REPO_ROOT}
+dev/tasks/generate-crds
+
+go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w pkg/controller/direct/<SERVICE>/
+`
+
+// NOT USED
+func generateCRDFromScripts(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Create the apis/<service>/<version> directory
+	serviceDir := filepath.Join(workDir, "apis", branch.Group, "v1alpha1")
+	if err := os.MkdirAll(serviceDir, 0755); err != nil {
+		log.Fatal(err)
+	}
+
+	// Create or update generate.sh
+	scriptPath := filepath.Join(serviceDir, "generate.sh")
+	// Check if generate.sh already exists.
+	if _, err := os.Stat(scriptPath); errors.Is(err, os.ErrNotExist) {
+		// File doesn't exist, use template approach
+		log.Printf("Creating new generate.sh at %s", scriptPath)
+
+		// Replace template markers with actual values and write to file
+		writeTemplateToFile(branch, scriptPath, GENERATOR_SCRIPT_TEMPLATE)
+	} else {
+		// File exists, use codebot to update it
+		log.Printf("Updating existing generate.sh at %s", scriptPath)
+
+		// Delete then write the prompt file.
+		promptPath := filepath.Join(workDir, "mockgcp", "crdgen_prompt.txt")
+		writeTemplateToFile(branch, promptPath, UPDATE_GENERATE_SCRIPT_PROMPT)
+
+		cfg := CommandConfig{
+			Name:    "CODEBOT GENERATE",
+			Cmd:     "codebot",
+			Args:    []string{"--ui-type=prompt", "--prompt=mockgcp/crdgen_prompt.txt"},
+			WorkDir: workDir,
+		}
+		if err := executeCommand(cfg, &out); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Add and commit changes
+	scriptRelativePath := filepath.Join("apis", branch.Group, "v1alpha1", "generate.sh")
+	gitAdd(workDir, &out, scriptRelativePath)
+	gitCommit(workDir, &out, fmt.Sprintf("add/update crd generation script for %s", branch.Group))
+
+	// Run the generator script
+	cfg := CommandConfig{
+		Name:    "Generator script",
+		Cmd:     scriptPath,
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		"config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("autogenerated types and CRDs using %s", scriptRelativePath))
+}
+
+func generateTypesAndMapper(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := opts.branchRepoDir
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Change to controllerbuilder directory
+	controllerBuilderDir := filepath.Join(workDir, "dev", "tools", "controllerbuilder")
+
+	// Generate types
+	cfg := CommandConfig{
+		Name: "Generate types",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-types",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+			"--resource", fmt.Sprintf("%s:%s", branch.Kind, branch.Proto),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Generate mapper
+	cfg = CommandConfig{
+		Name: "Generate mapper",
+		Cmd:  "go",
+		Args: []string{
+			"run", ".",
+			"generate-mapper",
+			"--service", branch.Package,
+			"--api-version", fmt.Sprintf("%s.cnrm.cloud.google.com/v1alpha1", branch.Group),
+		},
+		WorkDir: controllerBuilderDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Run goimports
+	cfg = CommandConfig{
+		Name: "Format generated code",
+		Cmd:  "go",
+		Args: []string{
+			"run", "-mod=readonly",
+			"golang.org/x/tools/cmd/goimports@latest",
+			"-w",
+			fmt.Sprintf("pkg/controller/direct/%s/", branch.Group),
+		},
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out,
+		fmt.Sprintf("apis/%s/v1alpha1/", branch.Group),
+		fmt.Sprintf("pkg/controller/direct/%s/", branch.Group))
+	gitCommit(workDir, &out, fmt.Sprintf("Generated types and mapper for %s", branch.Kind))
+}
+
+func generateCRD(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Generate CRDs
+	cfg := CommandConfig{
+		Name:    "Generate CRDs",
+		Cmd:     filepath.Join(workDir, "dev/tasks/generate-crds"),
+		WorkDir: workDir,
+	}
+	if err := executeCommand(cfg, &out); err != nil {
+		log.Fatal(err)
+	}
+
+	// Add and commit generated files
+	gitAdd(workDir, &out, "config/crds/resources/")
+	gitCommit(workDir, &out, fmt.Sprintf("Generated CRD for %s", branch.Kind))
+}
+
+func generateSpecStatus(opts *RunnerOptions, branch Branch) {
+	close := setLoggingWriter(opts, branch)
+	defer close()
+	workDir := filepath.Join(opts.branchRepoDir)
+
+	var out strings.Builder
+	checkoutBranch(branch, workDir, &out)
+
+	// Run controllerbuilder to generate spec and status
+	log.Printf("Generating spec and status for %s", branch.Name)
+	stdinInput := fmt.Sprintf("// +kcc:proto=%s.%s\n", branch.ProtoSvc, branch.Proto)
+
+	cfg := CommandConfig{
+		Name:    "Spec/Status generation",
+		Cmd:     "controllerbuilder",
+		Args:    []string{"prompt", "--src-dir", workDir, "--proto-dir", filepath.Join(workDir, ".build/third_party/googleapis/")},
path: experiments/conductor/cmd/runner/crd_generator_commands.go
user: login: barney-s
body: I have removed it now. We can add it back after investigation.
created_at: 2025-03-04T17:30:22Z
author_association: COLLABORATOR
diff_hunk: @@ -222,3 +235,96 @@ func setLoggingWriter(opts *RunnerOptions, branch Branch) closer {
 
 func noOp() {
 }
+
+func printCommandOutput(output string) {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+
+	// Split the string into lines and format each line
+	lines := strings.Split(formatted, "\n")
+	for _, line := range lines {
+		log.Printf("  > %s\n", line)
+	}
+}
+
+func formatCommandOutput(output string) string {
+	// Replace escaped newlines and tabs with their actual characters
+	formatted := strings.ReplaceAll(output, "\\n", "\n")
+	formatted = strings.ReplaceAll(formatted, "\\t", "\t")
+	return formatted
+}
+
+// CommandConfig holds the configuration for executing a command
+type CommandConfig struct {
+	Name    string            // Name of the command for logging
+	Cmd     string            // The command to run
+	Args    []string          // Command arguments
+	WorkDir string            // Working directory
+	Stdin   io.Reader         // Optional stdin
+	Env     map[string]string // Optional environment variables
+	Timeout time.Duration     // Timeout duration (default 5m)
path: experiments/conductor/cmd/runner/utilities.go
user: login: acpana
body: big fan! 
created_at: 2025-03-04T18:26:33Z
author_association: COLLABORATOR

PR comments:
user: login: cheftako
created_at: 2025-03-04T19:37:01Z
body: /lgtm
/approve
author_association: COLLABORATOR
user: login: google-oss-prow[bot]
created_at: 2025-03-04T19:37:14Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/3823#issuecomment-2698720409" title="Approved">cheftako</a>*

The full list of commands accepted by this bot can be found [here](https://oss.gprow.dev/command-help?repo=GoogleCloudPlatform%2Fk8s-config-connector).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/GoogleCloudPlatform/k8s-config-connector/blob/master/OWNERS)~~ [cheftako]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR