← back to Cli Printing Press
fix(cli): replace MarkFlagRequired with RunE validation, remove import guards, fix type fidelity (#130)
b5c911584e5e2882f00fd46c31a89eb881e23c7e · 2026-04-04 23:38:52 -0700 · Trevin Chow
* fix(cli): replace MarkFlagRequired with RunE validation, remove import guards, fix type fidelity
Three systemic generator fixes discovered during cal.com CLI polish:
1. MarkFlagRequired blocks --dry-run: Cobra enforces required flags before
RunE, preventing dry-run from reaching the handler. Moved validation to
RunE with a !flags.dryRun guard so verify can test all commands.
2. Import guards penalize type fidelity: Promoted command template emitted
var _ = strings.ReplaceAll and friends. Removed guards and unused imports
(io, strings) since promoted commands only generate GET endpoints.
3. goType() mapped object/array to string: Added goStructType() that returns
json.RawMessage for object/array fields in type structs. CLI flags remain
strings (correct for cobra). Types template conditionally imports
encoding/json only when non-scalar fields exist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Codex review — stdin guard, promoted types, Changed() over zero-val
Three fixes from Codex review:
1. P1: Body validation now skipped when --stdin is set, so JSON-from-stdin
workflow isn't blocked by required body field checks.
2. P1: Promoted template now uses goTypeForParam/cobraFlagFuncForParam
instead of goType/cobraFlagFunc, aligning ID param types correctly.
3. P2: Replaced zero-value comparison with cmd.Flags().Changed() so
--count=0 and --enabled=false are not rejected as "not set".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
A docs/plans/2026-04-04-005-fix-generator-dryrun-imports-types-plan.mdM internal/generator/generator.goM internal/generator/generator_test.goM internal/generator/templates/command_endpoint.go.tmplM internal/generator/templates/command_promoted.go.tmplM internal/generator/templates/types.go.tmpl
Diff
commit b5c911584e5e2882f00fd46c31a89eb881e23c7e
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sat Apr 4 23:38:52 2026 -0700
fix(cli): replace MarkFlagRequired with RunE validation, remove import guards, fix type fidelity (#130)
* fix(cli): replace MarkFlagRequired with RunE validation, remove import guards, fix type fidelity
Three systemic generator fixes discovered during cal.com CLI polish:
1. MarkFlagRequired blocks --dry-run: Cobra enforces required flags before
RunE, preventing dry-run from reaching the handler. Moved validation to
RunE with a !flags.dryRun guard so verify can test all commands.
2. Import guards penalize type fidelity: Promoted command template emitted
var _ = strings.ReplaceAll and friends. Removed guards and unused imports
(io, strings) since promoted commands only generate GET endpoints.
3. goType() mapped object/array to string: Added goStructType() that returns
json.RawMessage for object/array fields in type structs. CLI flags remain
strings (correct for cobra). Types template conditionally imports
encoding/json only when non-scalar fields exist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Codex review — stdin guard, promoted types, Changed() over zero-val
Three fixes from Codex review:
1. P1: Body validation now skipped when --stdin is set, so JSON-from-stdin
workflow isn't blocked by required body field checks.
2. P1: Promoted template now uses goTypeForParam/cobraFlagFuncForParam
instead of goType/cobraFlagFunc, aligning ID param types correctly.
3. P2: Replaced zero-value comparison with cmd.Flags().Changed() so
--count=0 and --enabled=false are not rejected as "not set".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
...-005-fix-generator-dryrun-imports-types-plan.md | 249 +++++++++++++++++++++
internal/generator/generator.go | 31 ++-
internal/generator/generator_test.go | 74 ++++++
.../generator/templates/command_endpoint.go.tmpl | 24 +-
.../generator/templates/command_promoted.go.tmpl | 22 +-
internal/generator/templates/types.go.tmpl | 5 +-
6 files changed, 381 insertions(+), 24 deletions(-)
diff --git a/docs/plans/2026-04-04-005-fix-generator-dryrun-imports-types-plan.md b/docs/plans/2026-04-04-005-fix-generator-dryrun-imports-types-plan.md
new file mode 100644
index 00000000..a18d692a
--- /dev/null
+++ b/docs/plans/2026-04-04-005-fix-generator-dryrun-imports-types-plan.md
@@ -0,0 +1,249 @@
+---
+title: "fix: Generator machinery — dry-run compatibility, import guards, type fidelity"
+type: fix
+status: active
+date: 2026-04-04
+---
+
+# fix: Generator machinery — dry-run compatibility, import guards, type fidelity
+
+## Overview
+
+Three systemic generator bugs cause every printed CLI to lose scorecard points and fail verify dry-run checks. These were discovered during the cal.com CLI polish and confirmed against the Steam Run 2 retro findings. All three are machine-level fixes in the Go binary templates.
+
+## Problem Frame
+
+When polishing the cal.com CLI (94/100 Grade A), three categories of issues required manual fixes to 20+ files. Every future printed CLI will hit the same problems because the generator templates produce the problematic patterns.
+
+1. **`MarkFlagRequired` blocks dry-run** — Cobra enforces required flags before RunE executes, so `--dry-run` never reaches the handler. Verify fails for every command with required params.
+2. **Import guards penalize type fidelity** — `var _ = strings.ReplaceAll` and friends in promoted command templates cause the scorer to deduct 1 point from type fidelity.
+3. **`goType()` maps object/array to string** — The type template calls `goType()` which has no case for `object` or `array`, so nested API response fields become `string` instead of `json.RawMessage`.
+
+## Requirements Trace
+
+- R1. Commands with required flags must still be runnable with `--dry-run`
+- R2. Generated promoted commands must not contain `var _` import guard lines
+- R3. Generated type structs must use `json.RawMessage` for object/array fields, not `string`
+- R4. Existing tests must continue to pass; new tests must cover each fix
+
+## Scope Boundaries
+
+- OAuth2 browser flow generation is out of scope (tracked separately)
+- Dogfood false-positive detection for nested sub-resources is out of scope (separate machine issue)
+- No changes to the scorecard — it already correctly rewards the right patterns
+
+## Context & Research
+
+### Relevant Code and Patterns
+
+- `internal/generator/templates/command_endpoint.go.tmpl` lines 281-290 — emits `MarkFlagRequired`
+- `internal/generator/templates/command_promoted.go.tmpl` lines 16-20 — emits `var _` guards
+- `internal/generator/templates/command_promoted.go.tmpl` lines 143-145 — emits `MarkFlagRequired` for promoted commands
+- `internal/generator/generator.go` `goType()` lines 775-788 — type mapping with no object/array case
+- `internal/generator/templates/types.go.tmpl` line 8 — calls `goType(.Type)` for every struct field
+- `internal/spec/spec.go` `Param.Required` field — drives the `MarkFlagRequired` decision
+- `internal/generator/generator_test.go` — 30+ existing tests for generated output
+
+### Institutional Learnings
+
+- Steam Run 2 retro (`docs/retros/2026-03-31-steam-run2-retro.md`) flagged the import guard issue as priority #5 with "Fix the generator" recommendation
+- Same retro flagged ID params as StringVar vs IntVar — adjacent but separate issue
+
+## Key Technical Decisions
+
+- **Move validation to RunE instead of removing required enforcement entirely**: Required params should still error when `--dry-run` is not set. The fix moves the check to RunE where `flags.dryRun` is accessible, not removes it.
+- **Remove import guards rather than making imports conditional**: Promoted commands only generate GET endpoints. The `io`, `os`, and `strings` imports are provably unused. Remove both the guards and the unused imports from the promoted template.
+- **Use `json.RawMessage` for non-scalar types, not typed structs**: Generating full Go structs for every nested object requires deep schema traversal. `json.RawMessage` preserves JSON fidelity without that complexity and is the standard Go pattern for "pass-through JSON."
+
+## Open Questions
+
+### Resolved During Planning
+
+- **Should dry-run provide default values for required params?** No — the template fix should only skip the validation error. The client's existing dry-run handler already prints the request without sending it. If a required param is empty, it shows as empty in the dry-run output, which is correct.
+- **Do endpoint commands (not just promoted) also have import guards?** No — `command_endpoint.go.tmpl` does not emit `var _` guards. Only `command_promoted.go.tmpl` does.
+
+### Deferred to Implementation
+
+- Whether `types.go.tmpl` needs an `import "encoding/json"` conditional or can always import it — depends on whether any spec produces zero object/array fields (unlikely but check during implementation)
+
+## Implementation Units
+
+- [ ] **Unit 1: Replace MarkFlagRequired with RunE validation in endpoint template**
+
+**Goal:** Commands with required flags accept `--dry-run` without error
+
+**Requirements:** R1
+
+**Dependencies:** None
+
+**Files:**
+- Modify: `internal/generator/templates/command_endpoint.go.tmpl`
+- Test: `internal/generator/generator_test.go`
+
+**Approach:**
+- Remove the `MarkFlagRequired` emission blocks (lines 281-283 for params, 288-290 for body)
+- Add a validation block at the start of RunE (after positional args check, before `flags.newClient()`):
+ - For each required param/body field, check if the value equals zero-value AND `!flags.dryRun`
+ - If so, return `fmt.Errorf("required flag \"%s\" not set", flagName)`
+- Use existing template helpers `zeroValForParam` and `flagName` to generate the checks
+
+**Patterns to follow:**
+- Existing positional args validation pattern at lines 41-44 of the same template
+- The `usageErr()` helper already exists in generated CLIs
+
+**Test scenarios:**
+- Happy path: Generated command with required param — source contains `fmt.Errorf("required flag"` in RunE, does NOT contain `MarkFlagRequired`
+- Happy path: Generated command with no required params — no validation block emitted
+- Edge case: Command with both required params and required body fields — both get RunE validation
+- Integration: Generated output compiles (`go vet`) with the new validation pattern
+
+**Verification:**
+- `go test ./internal/generator/ -run TestGenerate` passes
+- Generated test fixture has no `MarkFlagRequired` calls
+- Generated test fixture has RunE validation guarded by `!flags.dryRun`
+
+---
+
+- [ ] **Unit 2: Replace MarkFlagRequired with RunE validation in promoted template**
+
+**Goal:** Promoted commands with required flags accept `--dry-run` without error
+
+**Requirements:** R1
+
+**Dependencies:** Unit 1 (same pattern)
+
+**Files:**
+- Modify: `internal/generator/templates/command_promoted.go.tmpl`
+- Test: `internal/generator/generator_test.go`
+
+**Approach:**
+- Same pattern as Unit 1, applied to the promoted template
+- Remove `MarkFlagRequired` at lines 143-145
+- Add RunE validation after the promoted command's own positional check
+
+**Patterns to follow:**
+- Unit 1's approach — identical pattern, different template file
+
+**Test scenarios:**
+- Happy path: Generated promoted command with required params has RunE validation, not `MarkFlagRequired`
+- Integration: `TestGeneratedOutput_PromotedCommandCompiles` still passes
+
+**Verification:**
+- Existing promoted command tests pass
+- Generated promoted command source has no `MarkFlagRequired`
+
+---
+
+- [ ] **Unit 3: Remove import guards from promoted template**
+
+**Goal:** Promoted commands don't emit `var _` import guard lines
+
+**Requirements:** R2
+
+**Dependencies:** None (can be done in parallel with Units 1-2)
+
+**Files:**
+- Modify: `internal/generator/templates/command_promoted.go.tmpl`
+- Test: `internal/generator/generator_test.go`
+
+**Approach:**
+- Remove lines 16-20 (`var _ = strings.ReplaceAll`, `var _ = fmt.Sprintf`, etc.)
+- Remove unused imports from the template's import block: `io`, `strings` (promoted commands only use GET endpoints so these are provably unused)
+- Keep `encoding/json`, `fmt`, `os`, `github.com/spf13/cobra` — all used by promoted GET commands
+
+**Patterns to follow:**
+- `command_endpoint.go.tmpl` — imports only what it uses with conditional blocks
+
+**Test scenarios:**
+- Happy path: Generated promoted command source does NOT contain `var _ =` lines
+- Happy path: Generated promoted command source does NOT import `"io"` or `"strings"`
+- Integration: `TestGeneratedOutput_PromotedCommandCompiles` still passes — generated code compiles without the guards
+
+**Verification:**
+- No `var _` in generated promoted command output
+- `go vet` passes on generated output
+
+---
+
+- [ ] **Unit 4: Map object/array types to json.RawMessage in goType()**
+
+**Goal:** Generated type structs use `json.RawMessage` for nested objects and arrays
+
+**Requirements:** R3
+
+**Dependencies:** None (can be done in parallel with Units 1-3)
+
+**Files:**
+- Modify: `internal/generator/generator.go` (`goType()` function)
+- Modify: `internal/generator/templates/types.go.tmpl` (add conditional `encoding/json` import)
+- Test: `internal/generator/generator_test.go`
+
+**Approach:**
+- Add cases for `"object"` and `"array"` in `goType()` that return `"json.RawMessage"`
+- In `types.go.tmpl`, add `import "encoding/json"` — either unconditionally (since most specs have at least one object field) or behind a conditional that checks if any field uses `json.RawMessage`
+- The types template currently has no import block at all (line 4 is `package types`); add one
+
+**Patterns to follow:**
+- `goStoreType()` at line 798 already maps JSON columns to `json.RawMessage`
+- The store types template includes the `encoding/json` import
+
+**Test scenarios:**
+- Happy path: `goType("object")` returns `"json.RawMessage"`
+- Happy path: `goType("array")` returns `"json.RawMessage"`
+- Happy path: `goType("string")` still returns `"string"` (regression check)
+- Happy path: Generated types file includes `import "encoding/json"` when object fields exist
+- Edge case: Spec with only primitive fields — types file still compiles (import may be unused; verify whether this is possible or if every spec has at least one object field)
+
+**Verification:**
+- `go test ./internal/generator/ -run TestGenerate` passes
+- Generated types fixture uses `json.RawMessage` for object/array fields
+
+---
+
+- [ ] **Unit 5: Add targeted tests for all three fixes**
+
+**Goal:** Regression tests that prevent these issues from returning
+
+**Requirements:** R4
+
+**Dependencies:** Units 1-4
+
+**Files:**
+- Modify: `internal/generator/generator_test.go`
+
+**Approach:**
+- Add `TestGeneratedOutput_NoMarkFlagRequired` — generates from a spec with required params, asserts no `MarkFlagRequired` in output, asserts RunE validation present
+- Add `TestGeneratedOutput_PromotedNoImportGuards` — generates promoted command, asserts no `var _ =` in output
+- Add `TestGeneratedOutput_ObjectFieldsUseRawMessage` — generates types from a spec with object/array fields, asserts `json.RawMessage` in output, asserts no `string` for known object fields
+
+**Patterns to follow:**
+- Existing `TestGeneratedOutput_HasSelectFlag` pattern — generate, read output, assert string presence/absence
+
+**Test scenarios:**
+- Each test should both assert the fix IS present and assert the old pattern IS NOT present (belt and suspenders)
+
+**Verification:**
+- `go test ./internal/generator/ -count=1` passes with new tests
+- `go test ./... -count=1` all green
+
+## System-Wide Impact
+
+- **Interaction graph:** All three templates feed into the generator pipeline. Changes affect every future `printing-press generate` run.
+- **Error propagation:** RunE validation errors propagate the same way as `MarkFlagRequired` errors — through cobra's error handler. User-visible error messages will be identical.
+- **State lifecycle risks:** None — templates are stateless text generation.
+- **API surface parity:** The `import.go.tmpl` also has a `MarkFlagRequired` for `--input` (line 96). This is fine — import is not a verify-tested command and the required input file flag should always error without the flag.
+- **Unchanged invariants:** Non-dry-run behavior is identical. Required flags still error when missing. Only the dry-run path changes.
+
+## Risks & Dependencies
+
+| Risk | Mitigation |
+|------|------------|
+| Types import block breaks specs with zero object fields | Unlikely — every real API spec has object response types. Test with a minimal all-primitives fixture to verify. |
+| RunE validation error message differs from cobra's built-in | Use the same `"required flag \"%s\" not set"` format cobra uses internally. |
+| Promoted template removes an import that IS conditionally used | Verified: promoted commands only generate GET endpoints (generator.go line 1189 filters `Method != "GET"`). `io` and `strings` are provably unused. |
+
+## Sources & References
+
+- Steam Run 2 retro: `docs/retros/2026-03-31-steam-run2-retro.md` (items #5 and #4)
+- Cal.com CLI polish session (2026-04-04) — discovered all three issues during manual fix-up
+- Cobra required flags docs: enforcement happens in PreRunE, before RunE
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 97327afb..88725df5 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -78,6 +78,7 @@ func New(s *spec.APISpec, outputDir string) *Generator {
"snake": toSnake,
"pascal": toPascal,
"goType": goType,
+ "goStructType": goStructType,
"goTypeForParam": goTypeForParam,
"goStoreType": goStoreType,
"cobraFlagFunc": cobraFlagFunc,
@@ -103,10 +104,20 @@ func New(s *spec.APISpec, outputDir string) *Generator {
"oneline": oneline,
"flagName": flagName,
"safeTypeName": safeTypeName,
- "exampleLine": g.exampleLine,
- "currentYear": func() string { return strconv.Itoa(time.Now().Year()) },
- "modulePath": func() string { return naming.CLI(s.Name) },
- "kebab": toKebab,
+ "hasNonScalarType": func(types map[string]spec.TypeDef) bool {
+ for _, td := range types {
+ for _, f := range td.Fields {
+ if f.Type == "object" || f.Type == "array" {
+ return true
+ }
+ }
+ }
+ return false
+ },
+ "exampleLine": g.exampleLine,
+ "currentYear": func() string { return strconv.Itoa(time.Now().Year()) },
+ "modulePath": func() string { return naming.CLI(s.Name) },
+ "kebab": toKebab,
"humanName": func(s string) string {
// "steam-web" → "Steam Web", "notion" → "Notion"
return cases.Title(language.English).String(strings.ReplaceAll(s, "-", " "))
@@ -787,6 +798,18 @@ func goType(t string) string {
}
}
+// goStructType returns the Go type for a struct field definition.
+// Unlike goType (used for CLI flags which are always primitives),
+// this maps object/array types to json.RawMessage for type fidelity.
+func goStructType(t string) string {
+ switch t {
+ case "object", "array":
+ return "json.RawMessage"
+ default:
+ return goType(t)
+ }
+}
+
func goStoreType(sqlType string) string {
upper := strings.ToUpper(sqlType)
switch {
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index d5632865..d7661bda 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -1328,3 +1328,77 @@ func TestGenerate_ComposedAuthUsesBrowserTemplate(t *testing.T) {
runGoCommand(t, outputDir, "mod", "tidy")
runGoCommand(t, outputDir, "build", "./...")
}
+
+// --- Regression tests for machinery fixes (cal.com retro 2026-04-04) ---
+
+func TestGeneratedOutput_NoMarkFlagRequired(t *testing.T) {
+ t.Parallel()
+
+ outputDir := generatePetstore(t)
+
+ // Read all generated endpoint command files
+ cliDir := filepath.Join(outputDir, "internal", "cli")
+ entries, err := os.ReadDir(cliDir)
+ require.NoError(t, err)
+
+ foundRunEValidation := false
+ for _, e := range entries {
+ if e.IsDir() || !strings.HasSuffix(e.Name(), ".go") {
+ continue
+ }
+ data, err := os.ReadFile(filepath.Join(cliDir, e.Name()))
+ require.NoError(t, err)
+ content := string(data)
+
+ // No command should use MarkFlagRequired (except import.go which is not verify-tested)
+ if e.Name() != "import.go" && strings.Contains(content, "MarkFlagRequired") {
+ t.Errorf("%s still contains MarkFlagRequired", e.Name())
+ }
+
+ // Track whether we find the RunE-based validation
+ if strings.Contains(content, `!flags.dryRun`) && strings.Contains(content, `required flag`) {
+ foundRunEValidation = true
+ }
+ }
+
+ // The petstore spec has required params, so we should find RunE validation
+ assert.True(t, foundRunEValidation, "required params should use RunE validation with dryRun guard")
+}
+
+func TestGeneratedOutput_PromotedNoImportGuards(t *testing.T) {
+ t.Parallel()
+
+ outputDir := generatePetstore(t)
+
+ cliDir := filepath.Join(outputDir, "internal", "cli")
+ entries, err := os.ReadDir(cliDir)
+ require.NoError(t, err)
+
+ for _, e := range entries {
+ if !strings.HasPrefix(e.Name(), "promoted_") {
+ continue
+ }
+ data, err := os.ReadFile(filepath.Join(cliDir, e.Name()))
+ require.NoError(t, err)
+ content := string(data)
+
+ assert.NotContains(t, content, "var _ =", "promoted command %s should not contain import guards", e.Name())
+ assert.NotContains(t, content, "var _ json", "promoted command %s should not contain import guards", e.Name())
+ assert.NotContains(t, content, `"io"`, "promoted command %s should not import io", e.Name())
+ assert.NotContains(t, content, `"strings"`, "promoted command %s should not import strings", e.Name())
+ }
+}
+
+func TestGeneratedOutput_ObjectFieldsUseRawMessage(t *testing.T) {
+ t.Parallel()
+
+ outputDir := generatePetstore(t)
+
+ typesGo, err := os.ReadFile(filepath.Join(outputDir, "internal", "types", "types.go"))
+ require.NoError(t, err)
+ content := string(typesGo)
+
+ // The petstore spec has object-typed schema fields; they should be json.RawMessage
+ assert.Contains(t, content, "json.RawMessage", "types.go should use json.RawMessage for object/array fields")
+ assert.Contains(t, content, `import "encoding/json"`, "types.go should import encoding/json when RawMessage is used")
+}
diff --git a/internal/generator/templates/command_endpoint.go.tmpl b/internal/generator/templates/command_endpoint.go.tmpl
index 90b0fb29..ccb712ab 100644
--- a/internal/generator/templates/command_endpoint.go.tmpl
+++ b/internal/generator/templates/command_endpoint.go.tmpl
@@ -42,6 +42,24 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
if len(args) == 0 {
return cmd.Help()
}
+{{- end}}
+{{- range .Endpoint.Params}}
+{{- if and .Required (not .Positional)}}
+ if !cmd.Flags().Changed("{{flagName .Name}}") && !flags.dryRun {
+ return fmt.Errorf("required flag \"%s\" not set", "{{flagName .Name}}")
+ }
+{{- end}}
+{{- end}}
+{{- if or (eq .Endpoint.Method "POST") (eq .Endpoint.Method "PUT") (eq .Endpoint.Method "PATCH")}}
+ if !stdinBody {
+{{- range .Endpoint.Body}}
+{{- if .Required}}
+ if !cmd.Flags().Changed("{{flagName .Name}}") && !flags.dryRun {
+ return fmt.Errorf("required flag \"%s\" not set", "{{flagName .Name}}")
+ }
+{{- end}}
+{{- end}}
+ }
{{- end}}
c, err := flags.newClient()
if err != nil {
@@ -278,16 +296,10 @@ func new{{camel .FuncPrefix}}{{camel .EndpointName}}Cmd(flags *rootFlags) *cobra
{{- range .Endpoint.Params}}
{{- if not .Positional}}
cmd.Flags().{{cobraFlagFuncForParam .Name .Type}}(&flag{{camel .Name}}, "{{flagName .Name}}", {{defaultValForParam .}}, "{{oneline .Description}}")
-{{- if .Required}}
- _ = cmd.MarkFlagRequired("{{flagName .Name}}")
-{{- end}}
{{- end}}
{{- end}}
{{- range .Endpoint.Body}}
cmd.Flags().{{cobraFlagFunc .Type}}(&body{{camel .Name}}, "{{flagName .Name}}", {{defaultVal .}}, "{{oneline .Description}}")
-{{- if .Required}}
- _ = cmd.MarkFlagRequired("{{flagName .Name}}")
-{{- end}}
{{- end}}
{{- if .Endpoint.Pagination}}
cmd.Flags().BoolVar(&flagAll, "all", false, "Fetch all pages")
diff --git a/internal/generator/templates/command_promoted.go.tmpl b/internal/generator/templates/command_promoted.go.tmpl
index df403eb1..ee5fe358 100644
--- a/internal/generator/templates/command_promoted.go.tmpl
+++ b/internal/generator/templates/command_promoted.go.tmpl
@@ -6,23 +6,15 @@ package cli
import (
"encoding/json"
"fmt"
- "io"
"os"
- "strings"
"github.com/spf13/cobra"
)
-var _ = strings.ReplaceAll // ensure import
-var _ = fmt.Sprintf // ensure import
-var _ = io.ReadAll // ensure import
-var _ = os.Stdin // ensure import
-var _ json.RawMessage // ensure import
-
func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
{{- range .Endpoint.Params}}
{{- if not .Positional}}
- var flag{{camel .Name}} {{goType .Type}}
+ var flag{{camel .Name}} {{goTypeForParam .Name .Type}}
{{- end}}
{{- end}}
{{- if .Endpoint.Pagination}}
@@ -35,6 +27,13 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
Long: "Shortcut for '{{.ResourceName}} {{.EndpointName}}'. {{oneline .Endpoint.Description}}",
Example: " {{modulePath}} {{.PromotedName}}",
RunE: func(cmd *cobra.Command, args []string) error {
+{{- range .Endpoint.Params}}
+{{- if and .Required (not .Positional)}}
+ if !cmd.Flags().Changed("{{flagName .Name}}") && !flags.dryRun {
+ return fmt.Errorf("required flag \"%s\" not set", "{{flagName .Name}}")
+ }
+{{- end}}
+{{- end}}
c, err := flags.newClient()
if err != nil {
return err
@@ -139,10 +138,7 @@ func new{{camel .PromotedName}}PromotedCmd(flags *rootFlags) *cobra.Command {
{{- range .Endpoint.Params}}
{{- if not .Positional}}
- cmd.Flags().{{cobraFlagFunc .Type}}(&flag{{camel .Name}}, "{{flagName .Name}}", {{defaultVal .}}, "{{oneline .Description}}")
-{{- if .Required}}
- _ = cmd.MarkFlagRequired("{{flagName .Name}}")
-{{- end}}
+ cmd.Flags().{{cobraFlagFuncForParam .Name .Type}}(&flag{{camel .Name}}, "{{flagName .Name}}", {{defaultValForParam .}}, "{{oneline .Description}}")
{{- end}}
{{- end}}
{{- if .Endpoint.Pagination}}
diff --git a/internal/generator/templates/types.go.tmpl b/internal/generator/templates/types.go.tmpl
index 48c54399..7fc7033a 100644
--- a/internal/generator/templates/types.go.tmpl
+++ b/internal/generator/templates/types.go.tmpl
@@ -2,10 +2,13 @@
// Generated by CLI Printing Press (https://github.com/mvanhorn/cli-printing-press). DO NOT EDIT.
package types
+{{if hasNonScalarType .Types}}
+import "encoding/json"
+{{end}}
{{range $name, $typeDef := .Types}}
type {{safeTypeName $name}} struct {
{{- range $typeDef.Fields}}
- {{camel .Name}} {{goType .Type}} `json:"{{.Name}}"`
+ {{camel .Name}} {{goStructType .Type}} `json:"{{.Name}}"`
{{- end}}
}
{{end}}
← 0dac6232 feat(cli): add name collision detection and resolution to pu
·
back to Cli Printing Press
·
feat(skills): make printing-press-retro a public skill (#129 c2076e02 →