From 8d0e1b5474dc1f6f0bb07e627cd37fe69ebeb7a7 Mon Sep 17 00:00:00 2001 From: ialexeze Date: Fri, 26 Jun 2026 05:07:56 +0000 Subject: [PATCH 1/5] chore: update stale pkg/reconciler file headers in pkg/runners --- pkg/runners/configmaps.go | 2 +- pkg/runners/cronjobs.go | 2 +- pkg/runners/deployments.go | 2 +- pkg/runners/hpas.go | 2 +- pkg/runners/ingresses.go | 2 +- pkg/runners/jobs.go | 2 +- pkg/runners/namespaces.go | 2 +- pkg/runners/pdbs.go | 2 +- pkg/runners/pods.go | 2 +- pkg/runners/pvcs.go | 2 +- pkg/runners/pvs.go | 2 +- pkg/runners/replicasets.go | 2 +- pkg/runners/rolebindings.go | 2 +- pkg/runners/roles.go | 2 +- pkg/runners/secret_tls.go | 2 +- pkg/runners/secrets.go | 2 +- pkg/runners/secrets_once.go | 2 +- pkg/runners/serviceaccounts.go | 2 +- pkg/runners/services.go | 2 +- pkg/runners/statefulsets.go | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkg/runners/configmaps.go b/pkg/runners/configmaps.go index b01cc828..bf299f3e 100644 --- a/pkg/runners/configmaps.go +++ b/pkg/runners/configmaps.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_configmaps.go +// pkg/runners/configmaps.go package runners import ( diff --git a/pkg/runners/cronjobs.go b/pkg/runners/cronjobs.go index 459232ec..e65f2409 100644 --- a/pkg/runners/cronjobs.go +++ b/pkg/runners/cronjobs.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_cronjobs.go +// pkg/runners/cronjobs.go package runners import ( diff --git a/pkg/runners/deployments.go b/pkg/runners/deployments.go index 0a025e16..23f12118 100644 --- a/pkg/runners/deployments.go +++ b/pkg/runners/deployments.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_deployments.go +// pkg/runners/deployments.go package runners import ( diff --git a/pkg/runners/hpas.go b/pkg/runners/hpas.go index 0d1adc53..8f3b327d 100644 --- a/pkg/runners/hpas.go +++ b/pkg/runners/hpas.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_hpas.go +// pkg/runners/hpas.go package runners import ( diff --git a/pkg/runners/ingresses.go b/pkg/runners/ingresses.go index e10f777b..44ce28e9 100644 --- a/pkg/runners/ingresses.go +++ b/pkg/runners/ingresses.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_ingresses.go +// pkg/runners/ingresses.go package runners import ( diff --git a/pkg/runners/jobs.go b/pkg/runners/jobs.go index a23a2b51..3108946f 100644 --- a/pkg/runners/jobs.go +++ b/pkg/runners/jobs.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_jobs.go +// pkg/runners/jobs.go package runners import ( diff --git a/pkg/runners/namespaces.go b/pkg/runners/namespaces.go index dd5e509c..2e11947c 100644 --- a/pkg/runners/namespaces.go +++ b/pkg/runners/namespaces.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_namespace.go +// pkg/runners/namespaces.go package runners import ( diff --git a/pkg/runners/pdbs.go b/pkg/runners/pdbs.go index 68bdb64d..8f2350f2 100644 --- a/pkg/runners/pdbs.go +++ b/pkg/runners/pdbs.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_pdbs.go +// pkg/runners/pdbs.go package runners import ( diff --git a/pkg/runners/pods.go b/pkg/runners/pods.go index 9ba65ff6..3ab7163f 100644 --- a/pkg/runners/pods.go +++ b/pkg/runners/pods.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_pods.go +// pkg/runners/pods.go package runners import ( diff --git a/pkg/runners/pvcs.go b/pkg/runners/pvcs.go index 3920d95d..a53e8817 100644 --- a/pkg/runners/pvcs.go +++ b/pkg/runners/pvcs.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_pvcs.go +// pkg/runners/pvcs.go package runners import ( diff --git a/pkg/runners/pvs.go b/pkg/runners/pvs.go index 7ec759f6..a7b025c6 100644 --- a/pkg/runners/pvs.go +++ b/pkg/runners/pvs.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_pvs.go +// pkg/runners/pvs.go package runners import ( diff --git a/pkg/runners/replicasets.go b/pkg/runners/replicasets.go index cd2c1eb0..6f233308 100644 --- a/pkg/runners/replicasets.go +++ b/pkg/runners/replicasets.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_replicasets.go +// pkg/runners/replicasets.go package runners import ( diff --git a/pkg/runners/rolebindings.go b/pkg/runners/rolebindings.go index 124efd52..29c9e5b9 100644 --- a/pkg/runners/rolebindings.go +++ b/pkg/runners/rolebindings.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_rolebindings.go +// pkg/runners/rolebindings.go package runners import ( diff --git a/pkg/runners/roles.go b/pkg/runners/roles.go index e3a911cf..a8806703 100644 --- a/pkg/runners/roles.go +++ b/pkg/runners/roles.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_roles.go +// pkg/runners/roles.go package runners import ( diff --git a/pkg/runners/secret_tls.go b/pkg/runners/secret_tls.go index ed3329e4..4a6b1d68 100644 --- a/pkg/runners/secret_tls.go +++ b/pkg/runners/secret_tls.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_secrets_tls.go +// pkg/runners/secret_tls.go // // TLS certificate generation and secret rotation for Orkestra secrets. // diff --git a/pkg/runners/secrets.go b/pkg/runners/secrets.go index 1d294012..1c19db86 100644 --- a/pkg/runners/secrets.go +++ b/pkg/runners/secrets.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_secrets.go +// pkg/runners/secrets.go // // Adds to the previous version: // - orktypes.EvaluateWhen instead of evaluateConditions (fixes anyOf: being ignored) diff --git a/pkg/runners/secrets_once.go b/pkg/runners/secrets_once.go index a6ed8a3e..4dd41126 100644 --- a/pkg/runners/secrets_once.go +++ b/pkg/runners/secrets_once.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_secrets_once.go +// pkg/runners/secrets_once.go // // once: true on secrets — idempotent random secret generation. // diff --git a/pkg/runners/serviceaccounts.go b/pkg/runners/serviceaccounts.go index e334fe2b..b8b35e7e 100644 --- a/pkg/runners/serviceaccounts.go +++ b/pkg/runners/serviceaccounts.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_serviceaccounts.go +// pkg/runners/serviceaccounts.go package runners import ( diff --git a/pkg/runners/services.go b/pkg/runners/services.go index 882c9649..628bcdf1 100644 --- a/pkg/runners/services.go +++ b/pkg/runners/services.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_services.go +// pkg/runners/services.go package runners import ( diff --git a/pkg/runners/statefulsets.go b/pkg/runners/statefulsets.go index 7494f219..047db991 100644 --- a/pkg/runners/statefulsets.go +++ b/pkg/runners/statefulsets.go @@ -1,4 +1,4 @@ -// pkg/reconciler/run_statefulsets.go +// pkg/runners/statefulsets.go package runners import ( From d54c3129a82a865c32d09331eb23c7c47cf63664 Mon Sep 17 00:00:00 2001 From: ialexeze Date: Fri, 26 Jun 2026 05:25:38 +0000 Subject: [PATCH 2/5] fix(generate): update katalog scaffold to operatorBox.reconciler structure --- pkg/generate/katalog_generator.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/generate/katalog_generator.go b/pkg/generate/katalog_generator.go index 6cad2088..364bad92 100644 --- a/pkg/generate/katalog_generator.go +++ b/pkg/generate/katalog_generator.go @@ -278,24 +278,25 @@ spec: queue: maxDepth: 100 operatorBox: - default: {{ if .DefaultFalse }}false{{ else }}true{{ end }} + reconciler: + default: {{ if .DefaultFalse }}false{{ else }}true{{ end }} {{ if .ShowHooks }} - # hooks: - # # Package exporting the hook factory function. - # location: github.com/myorg/my-operator/hooks - # # Function signature: func() domain.AnyReconcileHooks - # # Return: domain.ReconcileHooks[*MyKind]{OnReconcile: ...} - # function: MyResourceHooks - # alias: myhook + # hooks: + # # Package exporting the hook factory function. + # location: github.com/myorg/my-operator/hooks + # # Function signature: func() domain.AnyReconcileHooks + # # Return: domain.ReconcileHooks[*MyKind]{OnReconcile: ...} + # function: MyResourceHooks + # alias: myhook {{ end -}} {{ if .ShowConstructor }} - # constructor: - # # Package exporting the reconciler constructor. - # location: github.com/myorg/my-operator/reconciler - # # Function signature: - # # func(*kubeclient.Kubeclient, cache.SharedIndexInformer, *event.Event) domain.Reconciler - # function: NewMyResourceReconciler - # alias: myrec + # constructor: + # # Package exporting the reconciler constructor. + # location: github.com/myorg/my-operator/reconciler + # # Function signature: + # # func(*kubeclient.Kubeclient, cache.SharedIndexInformer, *event.Event) domain.Reconciler + # function: NewMyResourceReconciler + # alias: myrec {{ end -}} {{ if not .IsTyped }} # Declarative template blocks — uncomment and fill in what you need. From 1e59a407fd924caa8f8096a14c7539072c4046c4 Mon Sep 17 00:00:00 2001 From: ialexeze Date: Fri, 26 Jun 2026 05:33:32 +0000 Subject: [PATCH 3/5] feat(cli): add ork create pattern command Scaffolds a new Orkestra pattern directory with katalog.yaml, simulate.yaml, e2e.yaml, and README.md in all modes. When any typed flag is set (--typed, --add-hook, --add-constructor), also writes a Makefile and Dockerfile suited for typed operator builds. Also fixes KatalogScaffold to create the output directory when the -o flag points to a non-existent path, and updates the katalog template to nest hooks/constructor under operatorBox.reconciler. --- cmd/cli/create_pattern.go | 110 ++++++++++++++++++ cmd/cli/files.go | 15 ++- pkg/generate/katalog_generator.go | 5 + pkg/generate/pattern_generator.go | 66 +++++++++++ .../templates/pattern_dockerfile.tmpl | 4 + pkg/generate/templates/pattern_e2e.tmpl | 17 +++ pkg/generate/templates/pattern_makefile.tmpl | 105 +++++++++++++++++ pkg/generate/templates/pattern_readme.tmpl | 87 ++++++++++++++ pkg/generate/templates/pattern_simulate.tmpl | 16 +++ 9 files changed, 419 insertions(+), 6 deletions(-) create mode 100644 cmd/cli/create_pattern.go create mode 100644 pkg/generate/pattern_generator.go create mode 100644 pkg/generate/templates/pattern_dockerfile.tmpl create mode 100644 pkg/generate/templates/pattern_e2e.tmpl create mode 100644 pkg/generate/templates/pattern_makefile.tmpl create mode 100644 pkg/generate/templates/pattern_readme.tmpl create mode 100644 pkg/generate/templates/pattern_simulate.tmpl diff --git a/cmd/cli/create_pattern.go b/cmd/cli/create_pattern.go new file mode 100644 index 00000000..ce1274cf --- /dev/null +++ b/cmd/cli/create_pattern.go @@ -0,0 +1,110 @@ +//go:build !runtime && !gateway + +package cli + +import ( + "fmt" + "path/filepath" + + "github.com/orkspace/orkestra/pkg/generate" + "github.com/spf13/cobra" +) + +var createPatternCmd = &cobra.Command{ + Use: "pattern", + Short: "Scaffold a new Orkestra pattern: katalog.yaml, simulate.yaml, e2e.yaml, README.md", + Long: `Creates the files needed to build, test, and publish an Orkestra pattern. + +Always written: + katalog.yaml — operator declaration + simulate.yaml — in-memory test scaffold (ork simulate) + e2e.yaml — real-cluster integration test scaffold (ork e2e) + README.md — actionable steps from edit to release + +Also written when --typed, --add-hook, or --add-constructor: + Makefile — registry, build, build-runtime, docker, push, release + Dockerfile — production container image (distroless, runtime binary only) + +Typed mode flags are forwarded to katalog generation: + --add-hook Include a hooks section + --add-constructor Include a constructor section + --typed Include both hooks and constructor (commented) + +Examples: + ork create pattern + ork create pattern --add-hook -o ./my-operator/ + ork create pattern --typed`, + RunE: func(cmd *cobra.Command, args []string) error { + addHook, _ := cmd.Flags().GetBool("add-hook") + addConstructor, _ := cmd.Flags().GetBool("add-constructor") + typed, _ := cmd.Flags().GetBool("typed") + outputDir, _ := cmd.Flags().GetString("output") + + if outputDir == "" { + outputDir = "." + } + + isTyped := typed || addHook || addConstructor + + katalogOpts := generate.KatalogScaffoldOptions{ + AddHook: addHook, + AddConstructor: addConstructor, + Typed: typed, + OutputFile: filepath.Join(outputDir, fileKatalog), + } + if err := katalogOpts.Validate(); err != nil { + return err + } + + fmt.Printf("generating pattern scaffold → %s/\n", outputDir) + + if _, err := generate.KatalogScaffold(katalogOpts); err != nil { + return fmt.Errorf("generating katalog.yaml: %w", err) + } + + if err := generate.WriteSimulateScaffold(filepath.Join(outputDir, fileSimulate)); err != nil { + return fmt.Errorf("generating simulate.yaml: %w", err) + } + + if err := generate.WriteE2EScaffold(filepath.Join(outputDir, fileE2e)); err != nil { + return fmt.Errorf("generating e2e.yaml: %w", err) + } + + if err := generate.WriteREADME(filepath.Join(outputDir, fileReadMe), isTyped); err != nil { + return fmt.Errorf("generating README.md: %w", err) + } + + if isTyped { + if err := generate.WriteMakefile(filepath.Join(outputDir, fileMakeFile)); err != nil { + return fmt.Errorf("generating Makefile: %w", err) + } + if err := generate.WriteDockerfile(filepath.Join(outputDir, fileDockerfile)); err != nil { + return fmt.Errorf("generating Dockerfile: %w", err) + } + } + + fmt.Printf("\nPattern scaffold written to %s/\n\n", outputDir) + fmt.Printf(" katalog.yaml — declare your CRD and resources\n") + fmt.Printf(" simulate.yaml — ork simulate\n") + fmt.Printf(" e2e.yaml — ork e2e\n") + fmt.Printf(" README.md — start here\n") + if isTyped { + fmt.Printf(" Makefile — make registry, make build, make release\n") + fmt.Printf(" Dockerfile — production container image\n") + } + fmt.Println() + return nil + }, +} + +func init() { + createCmd.AddCommand(createPatternCmd) + createPatternCmd.Flags().Bool("add-hook", false, + "Typed mode: include a hooks section in katalog.yaml (also writes Makefile + Dockerfile)") + createPatternCmd.Flags().Bool("add-constructor", false, + "Typed mode: include a constructor section in katalog.yaml (also writes Makefile + Dockerfile)") + createPatternCmd.Flags().Bool("typed", false, + "Typed mode: include both hooks and constructor commented (also writes Makefile + Dockerfile)") + createPatternCmd.Flags().StringP("output", "o", "", + "Output directory (default: current directory)") +} diff --git a/cmd/cli/files.go b/cmd/cli/files.go index 8a01d339..50c1d2b1 100644 --- a/cmd/cli/files.go +++ b/cmd/cli/files.go @@ -7,12 +7,15 @@ import ( ) const ( - fileKatalog = "katalog.yaml" - fileKomposer = "komposer.yaml" - fileE2e = "e2e.yaml" - fileSimulate = "simulate.yaml" - fileCrd = "crd.yaml" - fileCr = "cr.yaml" + fileKatalog = "katalog.yaml" + fileKomposer = "komposer.yaml" + fileE2e = "e2e.yaml" + fileSimulate = "simulate.yaml" + fileCrd = "crd.yaml" + fileCr = "cr.yaml" + fileReadMe = "README.md" + fileMakeFile = "Makefile" + fileDockerfile = "Dockerfile" ) // resolveKatalogPaths resolves the katalog file paths in the following order: diff --git a/pkg/generate/katalog_generator.go b/pkg/generate/katalog_generator.go index 364bad92..ec330441 100644 --- a/pkg/generate/katalog_generator.go +++ b/pkg/generate/katalog_generator.go @@ -26,6 +26,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "strings" "text/template" "time" @@ -186,6 +187,9 @@ func KatalogScaffold(opts KatalogScaffoldOptions) (string, error) { if dest == "" { dest = "katalog.yaml" } + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return "", fmt.Errorf("creating directory for %s: %w", dest, err) + } if err := os.WriteFile(dest, []byte(out), 0o644); err != nil { return "", fmt.Errorf("writing %s: %w", dest, err) } @@ -197,6 +201,7 @@ func KatalogScaffold(opts KatalogScaffoldOptions) (string, error) { // Template syntax appearing inside YAML comments is escaped via {{ "{{" }}. var katalogTmpl = template.Must(template.New("katalog-scaffold").Parse( `# Generated by "ork generate katalog{{ .FlagSuffix }}" on {{ .Timestamp }} +# https://orkestra.sh/docs/reference/schema/katalog/ # Edit every TODO placeholder before running "ork run". apiVersion: orkestra.orkspace.io/v1 kind: Katalog diff --git a/pkg/generate/pattern_generator.go b/pkg/generate/pattern_generator.go new file mode 100644 index 00000000..c20c47a2 --- /dev/null +++ b/pkg/generate/pattern_generator.go @@ -0,0 +1,66 @@ +// pkg/generate/pattern_generator.go +package generate + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "text/template" +) + +// WriteSimulateScaffold writes a simulate.yaml starter to dest. +func WriteSimulateScaffold(dest string) error { + return writeStaticTemplate("templates/pattern_simulate.tmpl", dest) +} + +// WriteE2EScaffold writes an e2e.yaml starter to dest. +func WriteE2EScaffold(dest string) error { + return writeStaticTemplate("templates/pattern_e2e.tmpl", dest) +} + +// WriteMakefile writes a clean Makefile (no example-pack workarounds) to dest. +func WriteMakefile(dest string) error { + return writeStaticTemplate("templates/pattern_makefile.tmpl", dest) +} + +// WriteDockerfile writes the production Dockerfile to dest. +func WriteDockerfile(dest string) error { + return writeStaticTemplate("templates/pattern_dockerfile.tmpl", dest) +} + +type patternReadmeData struct { + Typed bool +} + +// WriteREADME writes a README.md with actionable steps to dest. +// When typed is true, the steps include make registry, build, and release. +func WriteREADME(dest string, typed bool) error { + tmpl, err := template.ParseFS(templateFS, "templates/pattern_readme.tmpl") + if err != nil { + return fmt.Errorf("parsing readme template: %w", err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, patternReadmeData{Typed: typed}); err != nil { + return fmt.Errorf("rendering readme: %w", err) + } + + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return fmt.Errorf("creating directory for %q: %w", dest, err) + } + return os.WriteFile(dest, buf.Bytes(), 0o644) +} + +// writeStaticTemplate reads a template file from the embedded FS and writes it +// to dest as-is — no template execution, raw bytes only. +func writeStaticTemplate(name, dest string) error { + content, err := templateFS.ReadFile(name) + if err != nil { + return fmt.Errorf("reading embedded %q: %w", name, err) + } + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return fmt.Errorf("creating directory for %q: %w", dest, err) + } + return os.WriteFile(dest, content, 0o644) +} diff --git a/pkg/generate/templates/pattern_dockerfile.tmpl b/pkg/generate/templates/pattern_dockerfile.tmpl new file mode 100644 index 00000000..53083ae7 --- /dev/null +++ b/pkg/generate/templates/pattern_dockerfile.tmpl @@ -0,0 +1,4 @@ +FROM gcr.io/distroless/static-debian12:nonroot +COPY ork /usr/local/bin/ork +USER 65532:65532 +ENTRYPOINT ["/usr/local/bin/ork"] diff --git a/pkg/generate/templates/pattern_e2e.tmpl b/pkg/generate/templates/pattern_e2e.tmpl new file mode 100644 index 00000000..ee186bd1 --- /dev/null +++ b/pkg/generate/templates/pattern_e2e.tmpl @@ -0,0 +1,17 @@ +# https://orkestra.sh/docs/reference/schema/e2e/ +apiVersion: orkestra.orkspace.io/v1 +kind: E2E +metadata: + name: # TODO: operator name + description: "End-to-end test for " +spec: + katalog: ./katalog.yaml + cr: ./cr.yaml + # timeout: 120s + expect: + # TODO: add assertions + # - resource: deployments + # name: "{{ .metadata.name }}" + # namespace: "{{ .metadata.namespace }}" + # present: true + # ready: true diff --git a/pkg/generate/templates/pattern_makefile.tmpl b/pkg/generate/templates/pattern_makefile.tmpl new file mode 100644 index 00000000..20951d43 --- /dev/null +++ b/pkg/generate/templates/pattern_makefile.tmpl @@ -0,0 +1,105 @@ +# ── Typed Orkestra Operator ──────────────────────────────────────────────────── +BINARY_NAME ?= ork +DEV_OUTPUT_DIR ?= $(HOME)/.orkestra/bin +PROD_OUTPUT_DIR ?= $(HOME)/.orkestra/bin/runtime +KATALOG ?= katalog.yaml + +IMAGE_REPO ?= myorg/my-operator +IMAGE_TAG ?= latest +IMAGE ?= $(IMAGE_REPO):$(IMAGE_TAG) +BUILD_TAGS ?= + +GOOS ?= linux +GOARCH ?= amd64 +CGO_ENABLED = 0 + +ORK_LDFLAGS := -X github.com/orkspace/orkestra/pkg/version.Version=$(GIT_VERSION) \ + -X github.com/orkspace/orkestra/pkg/version.Commit=$(GIT_COMMIT) \ + -X github.com/orkspace/orkestra/pkg/version.Date=$(GIT_DATE) + +# ── registry ────────────────────────────────────────────────────────────────── +# Generates pkg/typeregistry/zz_generated_typeregistry.go. +# Re-run whenever you change apiTypes fields in katalog.yaml. +.PHONY: registry +registry: + ork generate registry --file $(KATALOG) + +# ── build (development) ─────────────────────────────────────────────────────── +# Builds the full CLI with all commands (validate, e2e, simulate, run, etc.) +.PHONY: build +build: + @mkdir -p $(DEV_OUTPUT_DIR) + go mod tidy + gofmt -w . + go build \ + -ldflags "$(ORK_LDFLAGS)" \ + -o $(DEV_OUTPUT_DIR)/$(BINARY_NAME) ./cmd/orkestra + @echo "✅ Development build: $(DEV_OUTPUT_DIR)/$(BINARY_NAME)" + +# ── build-runtime (production) ──────────────────────────────────────────────── +# Builds a production binary with ONLY the `run` command (no validate, e2e, etc.) +# This is what runs in your cluster — smaller attack surface, focused responsibility. +.PHONY: build-runtime +build-runtime: + @mkdir -p $(PROD_OUTPUT_DIR) + go mod tidy + gofmt -w . + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build \ + -tags "runtime" \ + -ldflags "$(ORK_LDFLAGS)" \ + -o $(PROD_OUTPUT_DIR)/$(BINARY_NAME) ./cmd/orkestra + @echo "✅ Production runtime build: $(PROD_OUTPUT_DIR)/$(BINARY_NAME)" + @echo " This binary only supports 'ork run' — perfect for production." + +# ── validate ────────────────────────────────────────────────────────────────── +.PHONY: validate +validate: + $(DEV_OUTPUT_DIR)/$(BINARY_NAME) validate -f $(KATALOG) + +# ── simulate ────────────────────────────────────────────────────────────────── +.PHONY: simulate +simulate: + $(DEV_OUTPUT_DIR)/$(BINARY_NAME) simulate + +# ── e2e ─────────────────────────────────────────────────────────────────────── +.PHONY: e2e +e2e: + $(DEV_OUTPUT_DIR)/$(BINARY_NAME) e2e + +# ── docker / push / release ─────────────────────────────────────────────────── +# Copies the production runtime binary into the current directory for docker build, +# then removes it. The Docker image contains ONLY the production binary (runtime tag). +.PHONY: docker +docker: build-runtime + @cp $(PROD_OUTPUT_DIR)/$(BINARY_NAME) ./$(BINARY_NAME) + docker build -t $(IMAGE) . + @rm -f ./$(BINARY_NAME) + @echo "✅ Docker image built: $(IMAGE)" + +.PHONY: push +push: + docker push $(IMAGE) + +.PHONY: release +release: docker push + +# ── clean ───────────────────────────────────────────────────────────────────── +.PHONY: clean +clean: + @rm -f $(DEV_OUTPUT_DIR)/$(BINARY_NAME) + @rm -rf $(PROD_OUTPUT_DIR) + @echo "✅ Removed all local builds" + +# ── help ────────────────────────────────────────────────────────────────────── +.PHONY: help +help: + @echo " registry generate type registry from Katalog" + @echo " build compile full development CLI (all commands)" + @echo " build-runtime compile production binary (only 'ork run')" + @echo " validate run katalog validation (using development binary)" + @echo " e2e run end‑to‑end tests (using development binary)" + @echo " simulate run simulation tests (using development binary)" + @echo " docker build-runtime + copy into Docker image" + @echo " push push Docker image to registry" + @echo " release docker + push" + @echo " clean remove all local builds" diff --git a/pkg/generate/templates/pattern_readme.tmpl b/pkg/generate/templates/pattern_readme.tmpl new file mode 100644 index 00000000..f9a1c417 --- /dev/null +++ b/pkg/generate/templates/pattern_readme.tmpl @@ -0,0 +1,87 @@ +# My Operator + +Generated by `ork create pattern`. + +## Step 1 — Edit katalog.yaml + +Fill in your CRD location and fields: + +- `spec.crds..crdFile` — path to your CRD file +- `spec.crds..crFiles` — path to your example CR +- `operatorBox.onCreate` — resources to create on each CR + +> **Before publishing or running in production:** replace `crdFile` and `crFiles` +> with `apiTypes` (group, version, kind, plural). `crdFile`/`crFiles` are +> development-only — the runtime reads them from disk; `apiTypes` references a +> CRD already installed in the cluster and is what belongs in a published Katalog. +{{if .Typed}} +## Step 2 — Generate type registry + +```bash +make registry +``` + +Re-run whenever you change `apiTypes` fields in `katalog.yaml`. + +## Step 3 — Write your hook or constructor + +Open the generated stub and implement the function body. The signature is generated — fill in the logic. + +## Step 4 — Build + +```bash +make build +``` + +## Step 5 — Validate + +```bash +make validate +``` +{{else}} +## Step 2 — Validate + +```bash +ork validate +``` +{{end}} +## Step {{if .Typed}}6{{else}}3{{end}} — Simulate + +```bash +ork simulate +``` + +Runs without a cluster. Edit `simulate.yaml` assertions first, or generate them from a live run: + +```bash +ork simulate init +``` + +## Step {{if .Typed}}7{{else}}4{{end}} — Run locally + +```bash +ork run --dev +``` + +`--dev` spins up a local kind cluster. Skip it if you already have one. + +## Step {{if .Typed}}8{{else}}5{{end}} — E2E test + +```bash +ork e2e +``` + +## Step {{if .Typed}}9{{else}}6{{end}} — Observe + +```bash +ork control +``` + +Open http://localhost:8081. Login with username `orkestra`, password `orkestra`. +{{if .Typed}} +## Step 10 — Release + +```bash +make release IMAGE=ghcr.io/myorg/my-operator:v0.1.0 +``` +{{end}} diff --git a/pkg/generate/templates/pattern_simulate.tmpl b/pkg/generate/templates/pattern_simulate.tmpl new file mode 100644 index 00000000..71d7072c --- /dev/null +++ b/pkg/generate/templates/pattern_simulate.tmpl @@ -0,0 +1,16 @@ +# https://orkestra.sh/docs/reference/schema/simulate/ +apiVersion: orkestra.orkspace.io/v1 +kind: Simulate +metadata: + name: # TODO: operator name +spec: + katalog: ./katalog.yaml + cr: ./cr.yaml + expect: + # TODO: add assertions — or generate them from a live run: + # ork simulate init + # + # - cycle: 1 + # verb: create + # resource: deployments + # name: "{{ .metadata.name }}" From 007d8db7f7aaddbcea48c559ce6e5266197a782b Mon Sep 17 00:00:00 2001 From: ialexeze Date: Fri, 26 Jun 2026 06:44:13 +0000 Subject: [PATCH 4/5] fix(cli): polish ork create pattern output and hide -f flag Use file name constants throughout, match push/generate-registry output style (successMark + dim), and hide the -f/--file persistent flag from all create subcommands via createCmd.PersistentFlags(). --- cmd/cli/create.go | 4 ++++ cmd/cli/create_pattern.go | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cmd/cli/create.go b/cmd/cli/create.go index c3c846b7..c515c6de 100644 --- a/cmd/cli/create.go +++ b/cmd/cli/create.go @@ -49,9 +49,13 @@ func init() { createClusterCmd.Flags().String("provider", "kind", "Cluster provider (only 'kind' is supported)") // Shadow global flags + createCmd.PersistentFlags().StringSlice("file", nil, "") createCmd.PersistentFlags().Bool("debug", false, "") createCmd.PersistentFlags().String("kubeconfig", "", "") createCmd.PersistentFlags().Bool("verbose", false, "") + + // Hide them from help output + createCmd.PersistentFlags().MarkHidden("file") createCmd.PersistentFlags().MarkHidden("debug") createCmd.PersistentFlags().MarkHidden("kubeconfig") createCmd.PersistentFlags().MarkHidden("verbose") diff --git a/cmd/cli/create_pattern.go b/cmd/cli/create_pattern.go index ce1274cf..49327ed3 100644 --- a/cmd/cli/create_pattern.go +++ b/cmd/cli/create_pattern.go @@ -59,38 +59,38 @@ Examples: fmt.Printf("generating pattern scaffold → %s/\n", outputDir) if _, err := generate.KatalogScaffold(katalogOpts); err != nil { - return fmt.Errorf("generating katalog.yaml: %w", err) + return fmt.Errorf("generating %s: %w", fileKatalog, err) } if err := generate.WriteSimulateScaffold(filepath.Join(outputDir, fileSimulate)); err != nil { - return fmt.Errorf("generating simulate.yaml: %w", err) + return fmt.Errorf("generating %s: %w", fileSimulate, err) } if err := generate.WriteE2EScaffold(filepath.Join(outputDir, fileE2e)); err != nil { - return fmt.Errorf("generating e2e.yaml: %w", err) + return fmt.Errorf("generating %s: %w", fileE2e, err) } if err := generate.WriteREADME(filepath.Join(outputDir, fileReadMe), isTyped); err != nil { - return fmt.Errorf("generating README.md: %w", err) + return fmt.Errorf("generating %s: %w", fileReadMe, err) } if isTyped { if err := generate.WriteMakefile(filepath.Join(outputDir, fileMakeFile)); err != nil { - return fmt.Errorf("generating Makefile: %w", err) + return fmt.Errorf("generating %s: %w", fileMakeFile, err) } if err := generate.WriteDockerfile(filepath.Join(outputDir, fileDockerfile)); err != nil { - return fmt.Errorf("generating Dockerfile: %w", err) + return fmt.Errorf("generating %s: %w", fileDockerfile, err) } } - fmt.Printf("\nPattern scaffold written to %s/\n\n", outputDir) - fmt.Printf(" katalog.yaml — declare your CRD and resources\n") - fmt.Printf(" simulate.yaml — ork simulate\n") - fmt.Printf(" e2e.yaml — ork e2e\n") - fmt.Printf(" README.md — start here\n") + fmt.Printf("\n→ pattern scaffold written to %s\n", bold(outputDir+"/")) + fmt.Printf(" %s %-16s %s\n", successMark(), fileKatalog, dim("declare your CRD(s) and resources")) + fmt.Printf(" %s %-16s %s\n", successMark(), fileSimulate, dim("ork simulate")) + fmt.Printf(" %s %-16s %s\n", successMark(), fileE2e, dim("ork e2e")) + fmt.Printf(" %s %-16s %s\n", successMark(), fileReadMe, dim("start here")) if isTyped { - fmt.Printf(" Makefile — make registry, make build, make release\n") - fmt.Printf(" Dockerfile — production container image\n") + fmt.Printf(" %s %-16s %s\n", successMark(), fileMakeFile, dim("make registry, make build, make release")) + fmt.Printf(" %s %-16s %s\n", successMark(), fileDockerfile, dim("production container image")) } fmt.Println() return nil From 2d1dcb9453b42e308f53628fd7b37bc9063c121b Mon Sep 17 00:00:00 2001 From: ialexeze Date: Fri, 26 Jun 2026 07:03:35 +0000 Subject: [PATCH 5/5] feat(cli): add values.yaml to typed scaffold and update docs Typed mode (--typed, --add-hook, --add-constructor) now also generates values.yaml with a runtime.image placeholder. The e2e.yaml scaffold includes valuesFiles: [./values.yaml] in typed mode so ork e2e can pass the operator image to the Orkestra Helm chart. README.md step order updated: release image -> set values.yaml -> e2e. CLI reference and concepts/patterns/scaffold.md updated to match. --- cmd/cli/create_pattern.go | 7 +- cmd/cli/files.go | 1 + documentation/concepts/patterns/index.md | 10 ++ documentation/concepts/patterns/scaffold.md | 141 ++++++++++++++++++++ documentation/reference/cli/create.md | 67 ++++++++++ pkg/generate/pattern_generator.go | 21 ++- pkg/generate/templates/pattern_e2e.tmpl | 18 ++- pkg/generate/templates/pattern_readme.tmpl | 31 +++-- pkg/generate/templates/pattern_values.tmpl | 4 + 9 files changed, 283 insertions(+), 17 deletions(-) create mode 100644 documentation/concepts/patterns/scaffold.md create mode 100644 pkg/generate/templates/pattern_values.tmpl diff --git a/cmd/cli/create_pattern.go b/cmd/cli/create_pattern.go index 49327ed3..2e921b5e 100644 --- a/cmd/cli/create_pattern.go +++ b/cmd/cli/create_pattern.go @@ -22,6 +22,7 @@ Always written: README.md — actionable steps from edit to release Also written when --typed, --add-hook, or --add-constructor: + values.yaml — runtime image (set before ork e2e) Makefile — registry, build, build-runtime, docker, push, release Dockerfile — production container image (distroless, runtime binary only) @@ -66,7 +67,7 @@ Examples: return fmt.Errorf("generating %s: %w", fileSimulate, err) } - if err := generate.WriteE2EScaffold(filepath.Join(outputDir, fileE2e)); err != nil { + if err := generate.WriteE2EScaffold(filepath.Join(outputDir, fileE2e), isTyped); err != nil { return fmt.Errorf("generating %s: %w", fileE2e, err) } @@ -75,6 +76,9 @@ Examples: } if isTyped { + if err := generate.WriteValuesYAML(filepath.Join(outputDir, fileValues)); err != nil { + return fmt.Errorf("generating %s: %w", fileValues, err) + } if err := generate.WriteMakefile(filepath.Join(outputDir, fileMakeFile)); err != nil { return fmt.Errorf("generating %s: %w", fileMakeFile, err) } @@ -89,6 +93,7 @@ Examples: fmt.Printf(" %s %-16s %s\n", successMark(), fileE2e, dim("ork e2e")) fmt.Printf(" %s %-16s %s\n", successMark(), fileReadMe, dim("start here")) if isTyped { + fmt.Printf(" %s %-16s %s\n", successMark(), fileValues, dim("set runtime.image before ork e2e")) fmt.Printf(" %s %-16s %s\n", successMark(), fileMakeFile, dim("make registry, make build, make release")) fmt.Printf(" %s %-16s %s\n", successMark(), fileDockerfile, dim("production container image")) } diff --git a/cmd/cli/files.go b/cmd/cli/files.go index 50c1d2b1..a8e96e46 100644 --- a/cmd/cli/files.go +++ b/cmd/cli/files.go @@ -16,6 +16,7 @@ const ( fileReadMe = "README.md" fileMakeFile = "Makefile" fileDockerfile = "Dockerfile" + fileValues = "values.yaml" ) // resolveKatalogPaths resolves the katalog file paths in the following order: diff --git a/documentation/concepts/patterns/index.md b/documentation/concepts/patterns/index.md index a4b01d08..403fd7c1 100644 --- a/documentation/concepts/patterns/index.md +++ b/documentation/concepts/patterns/index.md @@ -48,4 +48,14 @@ This is the same shift containers made for applications. Patterns make operator YAML files are documents. Patterns are something more specific: they encode a solution to a named problem that recurs in the operator world. The name reflects intent — not format. +--- + +## Pages + +| Page | What it covers | +|------|---------------| +| [Scaffolding a pattern](scaffold.md) | `ork create pattern` — generate the full file set to build, test, and publish | + +--- + → See the [Pattern kinds in the registry](../../orkestra-registry/index.md) — examples of Katalogs, Motifs, and Komposers in practice. diff --git a/documentation/concepts/patterns/scaffold.md b/documentation/concepts/patterns/scaffold.md new file mode 100644 index 00000000..513fb6db --- /dev/null +++ b/documentation/concepts/patterns/scaffold.md @@ -0,0 +1,141 @@ +# Scaffolding a Pattern + +`ork create pattern` generates the full file set needed to build, test, and publish an Orkestra pattern. It surfaces the same katalog scaffolding as `ork generate katalog` and adds the testing layer — `simulate.yaml` and `e2e.yaml` — so the operator suite is complete from the start. + +```bash +ork create pattern +ork create pattern -o ./my-operator/ +ork create pattern --add-hook -o ./my-operator/ +``` + +--- + +## What gets generated + +```text +my-operator/ + katalog.yaml — operator declaration + simulate.yaml — in-memory test (ork simulate) + e2e.yaml — real-cluster test (ork e2e) + README.md — actionable steps from edit to release +``` + +In typed mode (`--add-hook`, `--add-constructor`, `--typed`): + +```text + values.yaml — runtime image (set before ork e2e) + Makefile — registry, build, build-runtime, docker, push, release + Dockerfile — production container image (distroless, runtime binary only) +``` + +--- + +## Dynamic vs typed + +The typed flags are forwarded directly to katalog generation — they behave identically to `ork generate katalog`. + +| Flag | Katalog mode | Extra files | +|------|-------------|-------------| +| *(none)* | Dynamic — declarative templates only | — | +| `--add-hook` | Typed — commented `hooks` declaration | `values.yaml`, `Makefile`, `Dockerfile` | +| `--add-constructor` | Typed — commented `constructor` declaration | `values.yaml`, `Makefile`, `Dockerfile` | +| `--typed` | Typed — both sections commented, pick one | `values.yaml`, `Makefile`, `Dockerfile` | + +--- + +## From scaffold to running operator + +### Dynamic mode + +```bash +# 1. Generate +ork create pattern -o ./my-operator/ +cd my-operator/ + +# 2. Edit katalog.yaml — fill in spec.crds, declare resources +# 3. Validate +ork validate + +# 4. Simulate — no cluster needed +ork simulate + +# 5. Run locally +ork run --dev + +# 6. E2E test +ork e2e + +# 7. Observe +ork control +``` + +### Typed mode + +```bash +# 1. Generate +ork create pattern --add-hook -o ./my-operator/ +cd my-operator/ + +# 2. Edit katalog.yaml — fill in apiTypes +# 3. Generate type registry +make registry + +# 4. Write your hook function + +# 5. Release the image +make release IMAGE=ghcr.io/myorg/my-operator:v0.1.0 + +# 6. Set the image in values.yaml +# runtime.image.repository: ghcr.io/myorg/my-operator +# runtime.image.tag: v0.1.0 + +# 7. Validate +ork validate + +# 8. Simulate +ork simulate + +# 9. Run locally +ork run --dev + +# 10. E2E test — passes values.yaml to the Orkestra Helm chart +ork e2e + +# 11. Push +ork push +``` + +--- + +## The testing layer + +`simulate.yaml` and `e2e.yaml` are starters — they reference `./katalog.yaml` and `./cr.yaml` but have no assertions yet. + +Add assertions before running, or generate them from a live run: + +```bash +ork simulate init +``` + +See [Simulate](../simulate/index.md) and [E2E](../e2e/index.md) for the full test authoring guide. + +--- + +## Publishing + +Once your tests pass, push to the registry: + +```bash +ork push +``` + +In typed mode, release the image first, then update `values.yaml` so `ork e2e` can pass it to the Orkestra Helm chart, then push the katalog: + +```bash +make release IMAGE=ghcr.io/myorg/my-operator:v0.1.0 +# edit values.yaml: set runtime.image.repository and runtime.image.tag +ork e2e +ork push +``` + +→ See [`ork create pattern` CLI reference](../../reference/cli/create.md#ork-create-pattern) diff --git a/documentation/reference/cli/create.md b/documentation/reference/cli/create.md index 1ace4e09..c0dc890d 100644 --- a/documentation/reference/cli/create.md +++ b/documentation/reference/cli/create.md @@ -47,6 +47,73 @@ ork create cluster --name ork-e2e --- +### `ork create pattern` + +Scaffold a complete Orkestra pattern directory: `katalog.yaml`, `simulate.yaml`, `e2e.yaml`, and `README.md`. In typed mode, also writes `values.yaml`, `Makefile`, and `Dockerfile`. + +```bash +ork create pattern [flags] +``` + +#### Flags + +| Flag | Description | +|------|-------------| +| `--add-hook` | Typed mode: include a `hooks` section in `katalog.yaml` (also writes `values.yaml`, `Makefile`, `Dockerfile`) | +| `--add-constructor` | Typed mode: include a `constructor` section in `katalog.yaml` (also writes `values.yaml`, `Makefile`, `Dockerfile`) | +| `--typed` | Typed mode: include both sections commented (also writes `values.yaml`, `Makefile`, `Dockerfile`) | +| `-o, --output ` | Output directory (default: current directory) | + +#### Files written + +| File | Always | Typed mode only | +|------|--------|-----------------| +| `katalog.yaml` | Yes | — | +| `simulate.yaml` | Yes | — | +| `e2e.yaml` | Yes | — | +| `README.md` | Yes | — | +| `values.yaml` | — | Yes | +| `Makefile` | — | Yes | +| `Dockerfile` | — | Yes | + +#### Examples + +Scaffold a dynamic-mode pattern in the current directory: + +```bash +ork create pattern +``` + +Scaffold into a new directory: + +```bash +ork create pattern -o ./my-operator/ +``` + +Typed mode with hooks: + +```bash +ork create pattern --add-hook -o ./my-operator/ +``` + +Typed mode with both sections (choose one after generation): + +```bash +ork create pattern --typed +``` + +#### Behavior + +- `katalog.yaml` is generated by the same engine as `ork generate katalog` — the typed flags behave identically. +- `simulate.yaml` and `e2e.yaml` are minimal starters that reference `./katalog.yaml` and `./cr.yaml`. Edit assertions before running. +- `e2e.yaml` in typed mode includes `valuesFiles: [./values.yaml]` — the values file is how `ork e2e` passes the runtime image to the Orkestra Helm chart. +- `values.yaml` sets `runtime.image.repository` and `runtime.image.tag`. Update these after `make release` before running `ork e2e`. +- `README.md` contains numbered, actionable steps from edit to release. Typed mode adds the registry generation and build steps. +- `Makefile` targets: `registry`, `build`, `build-runtime`, `validate`, `simulate`, `e2e`, `docker`, `push`, `release`, `clean`. +- `Dockerfile` builds a production image using the runtime binary only (distroless base, no shell). + +--- + ## Notes - `ork create cluster` is intended for local development and CI environments, not production. diff --git a/pkg/generate/pattern_generator.go b/pkg/generate/pattern_generator.go index c20c47a2..1bf7746a 100644 --- a/pkg/generate/pattern_generator.go +++ b/pkg/generate/pattern_generator.go @@ -15,8 +15,25 @@ func WriteSimulateScaffold(dest string) error { } // WriteE2EScaffold writes an e2e.yaml starter to dest. -func WriteE2EScaffold(dest string) error { - return writeStaticTemplate("templates/pattern_e2e.tmpl", dest) +// When typed is true, a valuesFiles entry referencing values.yaml is included. +func WriteE2EScaffold(dest string, typed bool) error { + tmpl, err := template.ParseFS(templateFS, "templates/pattern_e2e.tmpl") + if err != nil { + return fmt.Errorf("parsing e2e template: %w", err) + } + var buf bytes.Buffer + if err := tmpl.Execute(&buf, patternReadmeData{Typed: typed}); err != nil { + return fmt.Errorf("rendering e2e: %w", err) + } + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return fmt.Errorf("creating directory for %q: %w", dest, err) + } + return os.WriteFile(dest, buf.Bytes(), 0o644) +} + +// WriteValuesYAML writes a values.yaml with the runtime image placeholder to dest. +func WriteValuesYAML(dest string) error { + return writeStaticTemplate("templates/pattern_values.tmpl", dest) } // WriteMakefile writes a clean Makefile (no example-pack workarounds) to dest. diff --git a/pkg/generate/templates/pattern_e2e.tmpl b/pkg/generate/templates/pattern_e2e.tmpl index ee186bd1..26ddb202 100644 --- a/pkg/generate/templates/pattern_e2e.tmpl +++ b/pkg/generate/templates/pattern_e2e.tmpl @@ -6,12 +6,20 @@ metadata: description: "End-to-end test for " spec: katalog: ./katalog.yaml + crd: ./crd.yaml cr: ./cr.yaml +{{- if .Typed}} + valuesFiles: + - ./values.yaml +{{- end}} # timeout: 120s expect: # TODO: add assertions - # - resource: deployments - # name: "{{ .metadata.name }}" - # namespace: "{{ .metadata.namespace }}" - # present: true - # ready: true + # - name: Deployment ready + # after: cr-applied + # timeout: 60s + # resources: + # - kind: Deployment + # name: "{{ "{{" }} .metadata.name {{ "}}" }}" + # namespace: "{{ "{{" }} .metadata.namespace {{ "}}" }}" + # ready: true diff --git a/pkg/generate/templates/pattern_readme.tmpl b/pkg/generate/templates/pattern_readme.tmpl index f9a1c417..2b71e3e9 100644 --- a/pkg/generate/templates/pattern_readme.tmpl +++ b/pkg/generate/templates/pattern_readme.tmpl @@ -27,13 +27,26 @@ Re-run whenever you change `apiTypes` fields in `katalog.yaml`. Open the generated stub and implement the function body. The signature is generated — fill in the logic. -## Step 4 — Build +## Step 4 — Release the image ```bash -make build +make release IMAGE=ghcr.io/myorg/my-operator:v0.1.0 ``` -## Step 5 — Validate +## Step 5 — Set the image in values.yaml + +Update `values.yaml` to match the image you just released: + +```yaml +runtime: + image: + repository: ghcr.io/myorg/my-operator + tag: v0.1.0 +``` + +`ork e2e` passes this file to the Orkestra Helm chart so it deploys your operator image. + +## Step 6 — Validate ```bash make validate @@ -45,7 +58,7 @@ make validate ork validate ``` {{end}} -## Step {{if .Typed}}6{{else}}3{{end}} — Simulate +## Step {{if .Typed}}7{{else}}3{{end}} — Simulate ```bash ork simulate @@ -57,7 +70,7 @@ Runs without a cluster. Edit `simulate.yaml` assertions first, or generate them ork simulate init ``` -## Step {{if .Typed}}7{{else}}4{{end}} — Run locally +## Step {{if .Typed}}8{{else}}4{{end}} — Run locally ```bash ork run --dev @@ -65,13 +78,13 @@ ork run --dev `--dev` spins up a local kind cluster. Skip it if you already have one. -## Step {{if .Typed}}8{{else}}5{{end}} — E2E test +## Step {{if .Typed}}9{{else}}5{{end}} — E2E test ```bash ork e2e ``` -## Step {{if .Typed}}9{{else}}6{{end}} — Observe +## Step {{if .Typed}}10{{else}}6{{end}} — Observe ```bash ork control @@ -79,9 +92,9 @@ ork control Open http://localhost:8081. Login with username `orkestra`, password `orkestra`. {{if .Typed}} -## Step 10 — Release +## Step 11 — Push ```bash -make release IMAGE=ghcr.io/myorg/my-operator:v0.1.0 +ork push ``` {{end}} diff --git a/pkg/generate/templates/pattern_values.tmpl b/pkg/generate/templates/pattern_values.tmpl new file mode 100644 index 00000000..190ae7a0 --- /dev/null +++ b/pkg/generate/templates/pattern_values.tmpl @@ -0,0 +1,4 @@ +runtime: + image: + repository: ghcr.io/myorg/my-operator + tag: latest