Skip to content

Update transform with kustomize and multistage#211

Merged
aufi merged 31 commits intomigtools:mainfrom
aufi:transform-kustomize-multistage-2
Apr 10, 2026
Merged

Update transform with kustomize and multistage#211
aufi merged 31 commits intomigtools:mainfrom
aufi:transform-kustomize-multistage-2

Conversation

@aufi
Copy link
Copy Markdown
Contributor

@aufi aufi commented Mar 31, 2026

There is still some work on polishing this feature to make it really helpful and stable (mostly in follow-ups), but its basic version should work and PR is ready for review now.

Sorry for bigger amount of changes in this PR, but I already put some additional sub-features away.

Related to #188

Summary by CodeRabbit

  • New Features

    • Multi-stage Kustomize transform & apply workflows with stage selection, chaining, single/multi-stage execution, and --force overwrite
    • New CLI flags to select single, ranged, or list stage execution; explicit-flag detection drives behavior
    • Render manifests via kubectl kustomize and write per-stage or final outputs
  • Bug Fixes

    • Validation enforces mutual exclusivity of stage-selection flags
    • Check for kubectl client availability before running transforms
  • Documentation

    • Two new comprehensive guides on multi-stage Kustomize pipelines, usage, and migration
  • Tests

    • Added fixtures and unit tests for stage discovery, filtering, navigation, path helpers, writers, and file helpers
  • Chores

    • Output directory creation now uses stricter permissions (0700)

aufi and others added 14 commits March 26, 2026 21:29
Implements Phase 6-F1:
- Stage discovery scans transform directory for subdirectories matching
  pattern <number>_<pluginName> (e.g., 10_kubernetes, 20_openshift)
- Stages sorted by priority (numeric prefix) for deterministic ordering
- FilterStages supports multiple selectors: specific stage, from-stage,
  to-stage, or list of stages
- Helper functions for stage navigation: GetFirstStage, GetLastStage,
  GetPreviousStage, GetNextStage
- ValidateStageName and GenerateStageName utilities

Comprehensive test coverage for all discovery and filtering scenarios.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 2-B1 and B2:
- KustomizeWriter orchestrates writing all transform artifacts to
  Kustomize layout (resources/, patches/, kustomization.yaml)
- Groups resources by type and writes multi-doc YAML files
- Generates patches with target selectors
- Creates whiteout and ignored-patches reports in YAML format
- Implements dirty check integration to prevent overwriting user edits
- Writes metadata with SHA256 content hashes for change tracking
- Helper functions to parse ResourceGroup TypeKey and extract
  JSONPatch operation details

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 6-F2:
- Orchestrator coordinates single-stage and multi-stage transform execution
- RunSingleStage: Default mode creating one transform stage
- RunMultiStage: Executes pipeline with stage chaining
- Stage chaining: output of stage N becomes input for stage N+1
- Integrates with KustomizeWriter to write Kustomize layout
- Parses JSONPatch from existing Runner.Run() response
- loadStageOutput reads resources from completed stage

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 3-C1 and C2:
- KustomizeApplier orchestrates applying transformations via kubectl kustomize
- ApplySingleStage: Applies a specific stage to produce output YAML
- ApplyMultiStage: Applies multiple stages with selector support
- ApplyFinalStage: Applies the last stage in pipeline (typical use case)
- Integrates with stage discovery to find and process stages
- Validates kubectl availability before execution
- Writes combined output YAML for each stage

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 3-C3:
- ValidateStage: Checks stage directory structure, kustomization.yaml,
  resources, patches, and metadata
- ValidateAllStages: Validates all discovered stages
- ValidatePipeline: End-to-end pipeline validation with error reporting
- ValidateOutputDirectory: Ensures output directory is suitable
- ValidateStageChaining: Verifies stages can be chained correctly
- Integrates dirty check to warn about user modifications
- Validates kustomization.yaml by running kubectl kustomize build
- Comprehensive error and warning reporting

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 6-F3:
- Transform command flags:
  --stage: Run specific stage only
  --from-stage: Run from stage onwards
  --to-stage: Run up to and including stage
  --stages: Run specific stages (comma-separated)
  --stage-name: Name for output stage (default: '10_transform')
  --plugin-name: Plugin name for metadata (default: 'transform')
  --force: Force overwrite of existing stages with user modifications

- Apply command flags:
  --stage, --from-stage, --to-stage, --stages: Stage selectors
  --final-only: Apply only final stage (default: true)
  --validate: Run preflight validation (default: true)
  --force: Force overwrite of output directory

Flags support full multi-stage pipeline control and integrate with
stage discovery and filtering mechanisms.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 6-F4:
- AutoAssignPriorities: Generates priority map from discovered stages
- AutoAssignPrioritiesFromDir: Discovers stages and assigns priorities
- MergePriorities: Merges user-specified and auto-assigned priorities
- SuggestStageName: Suggests next available stage name for a plugin
- ParsePluginPrioritiesFromStrings: Parses priority strings ("plugin:10")
- GetPriorityAssignments: Returns all plugin priority assignments
- ValidatePriorityAssignments: Checks for priority conflicts
- RecommendPriorityOrder: Heuristic-based priority recommendations

Features:
- Fills significant gaps (> 5) in priority sequence
- Appends new stages with increment of 10
- Intelligent plugin categorization (kubernetes:10, openshift:20, etc.)
- Comprehensive test coverage for all functions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements task migtools#11:
- TestKustomizeWriterIntegration: End-to-end test of KustomizeWriter
  creating stage directory structure, resources, kustomization.yaml,
  and metadata
- TestStageDiscoveryIntegration: Tests stage discovery and filtering
  with multiple stages
- TestDirtyCheckIntegration: Tests dirty check workflow including
  metadata generation, modification detection, and EnsureCleanDirectory

Test fixtures:
- deployment.yaml: Sample Kubernetes Deployment
- service.yaml: Sample Kubernetes Service

All integration tests pass with -tags=integration flag.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements task migtools#12:

docs/kustomize-multistage.md:
- Complete feature overview and key concepts
- Stage directory structure and resource grouping
- CLI usage for transform and apply commands
- Priority assignment (auto, manual, recommended)
- Stage chaining workflow
- Migration guide from JSONPatch workflow
- Advanced features and API reference
- Troubleshooting guide and best practices

docs/TRANSFORM_README.md:
- Quick start guide for transform directory
- Explanation of directory contents
- Common workflows (review, customize, re-run, chain)
- Git best practices
- Troubleshooting common issues
- Advanced Kustomize customization examples

Documentation includes:
- Complete CLI flag reference
- Code examples in Go
- Command-line examples
- Directory structure diagrams
- Migration paths
- Best practices

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements tasks migtools#21 and migtools#23:

Transform command (cmd/transform/transform.go):
- Detects if new Kustomize workflow should be used based on flags
- runKustomizeWorkflow(): Uses Orchestrator for single/multi-stage execution
- runLegacyWorkflow(): Preserves existing JSONPatch workflow
- Supports --stage, --from-stage, --to-stage, --stages flags
- Validates mutually exclusive flag usage

Apply command (cmd/apply/apply.go):
- Detects if new Kustomize workflow should be used
- runKustomizeWorkflow(): Uses KustomizeApplier with kubectl kustomize
- runLegacyWorkflow(): Preserves existing apply workflow
- Supports --stage, --from-stage, --to-stage, --stages, --final-only flags
- Includes preflight validation with --validate flag
- Validates mutually exclusive flag usage

Both commands maintain backward compatibility by defaulting to legacy
workflow when multi-stage flags are not used.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements task migtools#24:

New subcommand: crane transform validate
- Validates all stages in transform directory
- Checks stage directory structure
- Validates kustomization.yaml syntax
- Verifies resource and patch file references
- Validates stage chaining correctness
- Checks dirty status of stages
- Provides clear success/warning/error output with emoji indicators

Features:
- --transform-dir flag to specify transform directory
- --verbose flag to show all warnings
- Validates each stage individually
- Validates stage chaining across pipeline
- User-friendly output with ✓, ⚠️, ✗ indicators
- Returns non-zero exit code on validation failure

Usage:
  crane transform validate
  crane transform validate --transform-dir transform
  crane transform validate --verbose

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Remove helper features while keeping core Kustomize and multi-stage functionality:

Removed files:
- priority.go, priority_test.go - auto-assignment not needed for MVP
- dirtycheck.go, dirtycheck_test.go, metadata.go - can add later
- validation.go, validate/ subcommand - can add later
- reports.go - whiteout/ignored patches reports not critical
- ordering.go, ordering_test.go - deterministic ordering not critical
- integration_test.go - depends on removed features

Simplified:
- writer.go: Removed metadata, dirty check, reports generation
  Now just writes resources, patches, and kustomization.yaml
- orchestrator.go: Updated to match simplified writer signature
- apply.go: Removed validation flags and checks
- transform.go: Removed validate subcommand reference

Core functionality preserved:
✓ Kustomize layout generation (resources/, patches/, kustomization.yaml)
✓ Multi-stage pipeline support
✓ Stage discovery and filtering
✓ Resource grouping by type
✓ Integration with crane transform and apply commands

All tests passing. Implementation is now simpler and focused on core features.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Kustomize-native multi-stage transform and apply pipeline with stage discovery/selection, orchestrator, Kustomize writer/applier, CLI flags for stage selection, and validation enforcing mutually exclusive selection modes.

Changes

Cohort / File(s) Summary
CLI Entrypoints & Flags
cmd/apply/apply.go, cmd/transform/transform.go
Stored *cobra.Command on Options, added multi-stage flags (Stage, FromStage, ToStage, Stages, StageName, PluginName, Force), mutual-exclusion validation, and runtime branching between Kustomize and legacy workflows.
Kustomize Applier
internal/apply/kustomize.go
New KustomizeApplier with ApplySingleStage/ApplyMultiStage/ApplyFinalStage, runs kubectl kustomize build, captures stdout/stderr, writes stage outputs to OutputDir, and adds ValidateKubectlAvailable().
Transform Orchestration
internal/transform/orchestrator.go, internal/transform/writer.go
New Orchestrator coordinating RunSingleStage/RunMultiStage, plugin loading/filtering, per-resource transforms, JSONPatch decoding, and delegating writes to KustomizeWriter, which emits per-stage Kustomize layout and enforces force semantics.
Stage Discovery & Selection
internal/transform/stages.go, internal/transform/stages_test.go
Added Stage model, DiscoverStages, StageSelector, selection semantics (single/list/range), navigation helpers, validation/generation functions, and unit tests.
File Path Helpers & Tests
internal/file/file_helper.go, internal/file/file_helper_test.go
Added PathOpts helpers to derive stage-specific paths (GetStageDir, GetResourcesDir, GetPatchesDir, GetKustomizationPath, metadata/report paths, etc.) and tests validating outputs.
Kustomize Workflow Integration in transform
cmd/transform/transform.go (additional)
New runKustomizeWorkflow() wiring orchestrator creation, parsing plugin priorities/optional flags, and executing single- or multi-stage orchestrator flows.
Apply Workflow Integration
cmd/apply/apply.go (additional)
Refactored run() to use KustomizeApplier, resolve dirs, create output dir with 0700, and choose ApplyMultiStage or ApplyFinalStage based on flags.
I/O Permission Tightening
cmd/runfn/util.go, various apply code
Output directory creation changed to mode 0700 (tighter permissions).
Docs
docs/TRANSFORM_README.md, docs/kustomize-multistage.md
Added detailed docs describing multi-stage layout, CLI usage, chaining/overwrite behavior, migration notes, reports, examples, and troubleshooting.
Test Fixtures
test-data/kustomize-transform/fixtures/deployment.yaml, .../service.yaml
Added sample Deployment and Service fixtures for transform tests.
Deps
go.mod
Updated module dependencies, promoted/added direct requires and bumped crane-lib pseudo-version.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as cmd/transform
    participant StageDiscovery as Stage Discovery
    participant Orchestrator
    participant PluginLoader as Plugin Loader
    participant Runner as cranelib.Runner
    participant Writer as KustomizeWriter
    participant FS as Filesystem

    User->>CLI: run transform (--stage/--from-stage/--to-stage/--stages)
    CLI->>CLI: shouldUseKustomizeWorkflow()
    CLI->>StageDiscovery: DiscoverStages(TransformDir)
    StageDiscovery->>FS: read transform dir
    FS-->>StageDiscovery: stage dirs
    StageDiscovery-->>CLI: sorted stages
    CLI->>Orchestrator: RunMultiStage(StageSelector)

    loop per selected stage
        Orchestrator->>PluginLoader: load plugins
        PluginLoader->>FS: read plugin files
        FS-->>PluginLoader: plugin data
        Orchestrator->>Orchestrator: determine stage input (export or prev stage)
        loop per input resource
            Orchestrator->>Runner: run(resource, plugins)
            Runner-->>Orchestrator: TransformArtifact
        end
        Orchestrator->>Writer: WriteStage(artifacts, force)
        Writer->>FS: create stage dirs & write resources/patches/kustomization
        FS-->>Writer: write complete
    end

    Orchestrator-->>CLI: success
    CLI-->>User: stages written
Loading
sequenceDiagram
    participant User
    participant CLI as cmd/apply
    participant Applier as KustomizeApplier
    participant StageDiscovery as Stage Discovery
    participant Kubectl as kubectl
    participant FS as Filesystem

    User->>CLI: run apply (--stage/--from-stage/--to-stage/--stages/--final-only)
    CLI->>CLI: shouldUseKustomizeWorkflow()
    CLI->>StageDiscovery: DiscoverStages(TransformDir)
    StageDiscovery->>FS: read transform dir
    StageDiscovery-->>CLI: stages
    CLI->>Applier: NewKustomizeApplier(TransformDir, OutputDir)

    alt Multi-Stage
        CLI->>Applier: ApplyMultiStage(selector)
        loop per filtered stage
            Applier->>Kubectl: kustomize build <stage>
            Kubectl-->>Applier: rendered YAML
            Applier->>FS: write <stage>.yaml to OutputDir
        end
    else Final-Only
        CLI->>Applier: ApplyFinalStage()
        Applier->>Kubectl: kustomize build <last-stage>
        Kubectl-->>Applier: rendered YAML
        Applier->>FS: write output.yaml to OutputDir
    end

    Applier-->>CLI: success
    CLI-->>User: manifests rendered
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Suggested reviewers

  • stillalearner
  • nachandr

Poem

🐰✨ A Kustomize Hop ✨
I hopped across stages, tidy and bright,
Plugins nudged manifests into flight.
Patches and resources found their place,
Files softly written — a clean little trace.
~Hoppy the Code Rabbit 🐇

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: refactoring transform/apply to use Kustomize and implementing multi-stage pipeline support.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 8, 2026

Test Coverage Report

Total: 28.1%

Per-package coverage

  • github.com/konveyor/crane — 0.0%
  • github.com/konveyor/crane/cmd/apply — 33.3%
  • github.com/konveyor/crane/cmd/convert — 0.0%
  • github.com/konveyor/crane/cmd/export — 93.1%
  • github.com/konveyor/crane/cmd/plugin-manager — 0.0%
  • github.com/konveyor/crane/cmd/plugin-manager/add — 0.0%
  • github.com/konveyor/crane/cmd/plugin-manager/list — 0.0%
  • github.com/konveyor/crane/cmd/plugin-manager/remove — 0.0%
  • github.com/konveyor/crane/cmd/runfn — 0.0%
  • github.com/konveyor/crane/cmd/skopeo-sync-gen — 0.0%
  • github.com/konveyor/crane/cmd/transfer-pvc — 9.2%
  • github.com/konveyor/crane/cmd/transform — 0.0%
  • github.com/konveyor/crane/cmd/transform/listplugins — 0.0%
  • github.com/konveyor/crane/cmd/transform/optionals — 0.0%
  • github.com/konveyor/crane/cmd/tunnel-api — 0.0%
  • github.com/konveyor/crane/cmd/version — 0.0%
  • github.com/konveyor/crane/e2e-tests/framework — 0.0%
  • github.com/konveyor/crane/e2e-tests/utils — 0.0%
  • github.com/konveyor/crane/internal/apply — 52.9%
  • github.com/konveyor/crane/internal/file — 89.2%
  • github.com/konveyor/crane/internal/flags — 0.0%
  • github.com/konveyor/crane/internal/plugin — 32.9%
  • github.com/konveyor/crane/internal/transform — 74.0%
Full function-level details
github.com/konveyor/crane/cmd/apply/apply.go:39:			Complete				100.0%
github.com/konveyor/crane/cmd/apply/apply.go:43:			Validate				100.0%
github.com/konveyor/crane/cmd/apply/apply.go:63:			Run					0.0%
github.com/konveyor/crane/cmd/apply/apply.go:67:			NewApplyCommand				0.0%
github.com/konveyor/crane/cmd/apply/apply.go:99:			addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/apply/apply.go:113:			run					0.0%
github.com/konveyor/crane/cmd/convert/convert.go:30:			NewConvertOptions			0.0%
github.com/konveyor/crane/cmd/convert/convert.go:63:			addFlagsForConvertOptions		0.0%
github.com/konveyor/crane/cmd/convert/convert.go:74:			Complete				0.0%
github.com/konveyor/crane/cmd/convert/convert.go:83:			Run					0.0%
github.com/konveyor/crane/cmd/convert/convert.go:87:			run					0.0%
github.com/konveyor/crane/cmd/convert/convert.go:112:			getClientFromContext			0.0%
github.com/konveyor/crane/cmd/convert/convert.go:134:			getRestConfigFromContext		0.0%
github.com/konveyor/crane/cmd/export/cluster.go:29:			NewClusterScopeHandler			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:35:			isClusterScopedResource			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:44:			filterRbacResources			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:102:			NewClusterScopedRbacHandler		100.0%
github.com/konveyor/crane/cmd/export/cluster.go:113:			exportedSANamespaces			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:126:			groupMatchesExportedSANamespaces	100.0%
github.com/konveyor/crane/cmd/export/cluster.go:143:			parseServiceAccountUserSubject		100.0%
github.com/konveyor/crane/cmd/export/cluster.go:154:			prepareForFiltering			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:187:			filteredResourcesOfKind			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:213:			accept					100.0%
github.com/konveyor/crane/cmd/export/cluster.go:223:			acceptClusterRoleBinding		100.0%
github.com/konveyor/crane/cmd/export/cluster.go:260:			acceptClusterRole			100.0%
github.com/konveyor/crane/cmd/export/cluster.go:284:			acceptSecurityContextConstraints	100.0%
github.com/konveyor/crane/cmd/export/cluster.go:336:			anyServiceAccountInNamespace		100.0%
github.com/konveyor/crane/cmd/export/crd.go:17:				normalizeGroupSet			85.7%
github.com/konveyor/crane/cmd/export/crd.go:29:				shouldSkipCRDGroup			100.0%
github.com/konveyor/crane/cmd/export/crd.go:50:				crdFailureAPIResourceName		100.0%
github.com/konveyor/crane/cmd/export/crd.go:58:				collectRelatedCRDs			89.7%
github.com/konveyor/crane/cmd/export/discover.go:39:			hasClusterScopedManifests		100.0%
github.com/konveyor/crane/cmd/export/discover.go:57:			prepareClusterResourceDir		100.0%
github.com/konveyor/crane/cmd/export/discover.go:73:			prepareFailuresDir			100.0%
github.com/konveyor/crane/cmd/export/discover.go:85:			writeResources				85.7%
github.com/konveyor/crane/cmd/export/discover.go:133:			writeErrors				83.3%
github.com/konveyor/crane/cmd/export/discover.go:174:			getFilePath				100.0%
github.com/konveyor/crane/cmd/export/discover.go:185:			discoverPreferredResources		100.0%
github.com/konveyor/crane/cmd/export/discover.go:214:			resourceToExtract			100.0%
github.com/konveyor/crane/cmd/export/discover.go:283:			isAdmittedResource			100.0%
github.com/konveyor/crane/cmd/export/discover.go:292:			getObjects				100.0%
github.com/konveyor/crane/cmd/export/discover.go:326:			iterateItemsByGet			93.8%
github.com/konveyor/crane/cmd/export/discover.go:352:			iterateItemsInList			92.3%
github.com/konveyor/crane/cmd/export/export.go:52:			Complete				90.0%
github.com/konveyor/crane/cmd/export/export.go:91:			Validate				100.0%
github.com/konveyor/crane/cmd/export/export.go:106:			validateExportNamespace			87.5%
github.com/konveyor/crane/cmd/export/export.go:122:			Run					0.0%
github.com/konveyor/crane/cmd/export/export.go:225:			NewExportCommand			50.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:42:		Complete				0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:47:		Validate				0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:90:		Run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:94:		NewAddCommand				0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:126:		addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:131:		run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/add/add.go:195:		downloadBinary				0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:46:		Complete				0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:51:		Validate				0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:56:		Run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:60:		NewListCommand				0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:90:		addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:98:		run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:167:		printInstalledInformation		0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:177:		groupInformationForPlugins		0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:196:		printInformation			0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:208:		printParamsInformation			0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:222:		getOptionalFields			0.0%
github.com/konveyor/crane/cmd/plugin-manager/list/list.go:247:		printTable				0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:33:	Complete				0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:38:	Validate				0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:43:	Run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:47:	NewPluginManagerCommand			0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:78:	addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/plugin-manager/plugin-manager.go:86:	run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/remove/remove.go:33:	Complete				0.0%
github.com/konveyor/crane/cmd/plugin-manager/remove/remove.go:38:	Validate				0.0%
github.com/konveyor/crane/cmd/plugin-manager/remove/remove.go:43:	Run					0.0%
github.com/konveyor/crane/cmd/plugin-manager/remove/remove.go:47:	NewRemoveCommand			0.0%
github.com/konveyor/crane/cmd/plugin-manager/remove/remove.go:77:	run					0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:33:			NewFnRunCommand				0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:49:			addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:59:			runE					0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:70:			preRunE					0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:112:			getFunctionImage			0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:124:			getContainerFunctions			0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:151:			getFunctionAnnotation			0.0%
github.com/konveyor/crane/cmd/runfn/runfn.go:170:			getFunctionConfig			0.0%
github.com/konveyor/crane/cmd/runfn/util.go:14:				WriteOutput				0.0%
github.com/konveyor/crane/cmd/runfn/util.go:31:				GetDestinationDir			0.0%
github.com/konveyor/crane/cmd/runfn/util.go:58:				ValidateFunctionImageURL		0.0%
github.com/konveyor/crane/cmd/runfn/util.go:80:				checkIfDirExists			0.0%
github.com/konveyor/crane/cmd/skopeo-sync-gen/skopeo-sync-gen.go:61:	Complete				0.0%
github.com/konveyor/crane/cmd/skopeo-sync-gen/skopeo-sync-gen.go:65:	Validate				0.0%
github.com/konveyor/crane/cmd/skopeo-sync-gen/skopeo-sync-gen.go:69:	NewSkopeoSyncGenCommand			0.0%
github.com/konveyor/crane/cmd/skopeo-sync-gen/skopeo-sync-gen.go:103:	shouldAddImageStream			0.0%
github.com/konveyor/crane/cmd/skopeo-sync-gen/skopeo-sync-gen.go:114:	Run					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:37:		NewRsyncLogStream			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:50:		Init					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:138:		writeProgressToFile			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:148:		Close					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:154:		Streams					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:190:		addDataSize				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:215:		String					100.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:219:		MarshalJSON				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:223:		AsString				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:271:		NewProgress				100.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:291:		Completed				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:296:		Status					38.5%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:321:		Merge					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:376:		newDataSize				72.7%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:399:		parseRsyncLogs				76.1%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:479:		waitForPodRunning			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/progress.go:513:		getFinalPodStatus			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:89:		Validate				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:117:		Validate				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:133:		NewTransferPVCCommand			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:169:		addFlagsToTransferPVCCommand		0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:189:		Complete				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:220:		Validate				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:246:		Run					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:250:		getClientFromContext			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:271:		getRestConfigFromContext		0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:278:		run					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:485:		getValidatedResourceName		0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:495:		getNodeNameForPVC			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:515:		getIDsForNamespace			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:541:		getRsyncClientPodSecurityContext	0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:553:		getRsyncServerPodSecurityContext	0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:565:		garbageCollect				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:602:		deleteResourcesIteratively		0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:628:		deleteResourcesForGVK			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:649:		followClientLogs			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:678:		waitForEndpoint				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:690:		createEndpoint				0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:735:		getRouteHostName			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:752:		buildDestinationPVC			0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:773:		ApplyTo					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:793:		ApplyTo					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:805:		ApplyTo					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:821:		String					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:825:		Set					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:835:		Type					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:841:		parseSourceDestinationMapping		100.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:863:		String					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:867:		Set					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:876:		Type					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:880:		String					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:884:		Set					0.0%
github.com/konveyor/crane/cmd/transfer-pvc/transfer-pvc.go:894:		Type					0.0%
github.com/konveyor/crane/cmd/transform/listplugins/listplugins.go:31:	Complete				0.0%
github.com/konveyor/crane/cmd/transform/listplugins/listplugins.go:36:	Validate				0.0%
github.com/konveyor/crane/cmd/transform/listplugins/listplugins.go:41:	Run					0.0%
github.com/konveyor/crane/cmd/transform/listplugins/listplugins.go:45:	NewListPluginsCommand			0.0%
github.com/konveyor/crane/cmd/transform/listplugins/listplugins.go:77:	run					0.0%
github.com/konveyor/crane/cmd/transform/optionals/optionals.go:31:	Complete				0.0%
github.com/konveyor/crane/cmd/transform/optionals/optionals.go:36:	Validate				0.0%
github.com/konveyor/crane/cmd/transform/optionals/optionals.go:41:	Run					0.0%
github.com/konveyor/crane/cmd/transform/optionals/optionals.go:45:	NewOptionalsCommand			0.0%
github.com/konveyor/crane/cmd/transform/optionals/optionals.go:77:	run					0.0%
github.com/konveyor/crane/cmd/transform/transform.go:50:		Complete				0.0%
github.com/konveyor/crane/cmd/transform/transform.go:54:		Validate				0.0%
github.com/konveyor/crane/cmd/transform/transform.go:74:		Run					0.0%
github.com/konveyor/crane/cmd/transform/transform.go:78:		NewTransformCommand			0.0%
github.com/konveyor/crane/cmd/transform/transform.go:112:		addFlagsForOptions			0.0%
github.com/konveyor/crane/cmd/transform/transform.go:136:		run					0.0%
github.com/konveyor/crane/cmd/transform/transform.go:203:		getPluginPrioritiesMap			0.0%
github.com/konveyor/crane/cmd/transform/transform.go:215:		optionalFlagsToLower			0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:35:		NewTunnelAPIOptions			0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:65:		addFlagsForTunnelAPIOptions		0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:77:		Complete				0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:100:		Validate				0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:116:		Run					0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:120:		getClientFromContext			0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:129:		getRestConfigFromContext		0.0%
github.com/konveyor/crane/cmd/tunnel-api/tunnel-api.go:136:		run					0.0%
github.com/konveyor/crane/cmd/version/version.go:20:			Complete				0.0%
github.com/konveyor/crane/cmd/version/version.go:25:			Validate				0.0%
github.com/konveyor/crane/cmd/version/version.go:30:			Run					0.0%
github.com/konveyor/crane/cmd/version/version.go:34:			NewVersionCommand			0.0%
github.com/konveyor/crane/cmd/version/version.go:60:			run					0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:27:		Deploy					0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:49:		Validate				0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:71:		Cleanup					0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:93:		buildCommand				0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:100:		envWithBinDir				0.0%
github.com/konveyor/crane/e2e-tests/framework/app.go:116:		withExtraVars				0.0%
github.com/konveyor/crane/e2e-tests/framework/client.go:14:		ListPVCs				0.0%
github.com/konveyor/crane/e2e-tests/framework/client.go:36:		NewClientSetForContext			0.0%
github.com/konveyor/crane/e2e-tests/framework/client.go:57:		GetClusterNodeIP			0.0%
github.com/konveyor/crane/e2e-tests/framework/client.go:85:		ResolveUsernameForContext		0.0%
github.com/konveyor/crane/e2e-tests/framework/crane.go:26:		Export					0.0%
github.com/konveyor/crane/e2e-tests/framework/crane.go:40:		Transform				0.0%
github.com/konveyor/crane/e2e-tests/framework/crane.go:54:		Apply					0.0%
github.com/konveyor/crane/e2e-tests/framework/crane.go:68:		TransferPVC				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:19:		Run					0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:25:		RunWithStdin				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:30:		executeWithStdin			0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:51:		normalizeKubectlArgs			0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:59:		CreateNamespace				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:78:		ApplyDir				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:94:		ApplyYAMLSpec				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:103:		ValidateApplyDir			0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:119:		ScaleDeployment				0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:177:		ScaleDeploymentIfPresent		0.0%
github.com/konveyor/crane/e2e-tests/framework/kubectl.go:198:		CanI					0.0%
github.com/konveyor/crane/e2e-tests/framework/logging.go:11:		logVerboseCommand			0.0%
github.com/konveyor/crane/e2e-tests/framework/logging.go:19:		logVerboseOutput			0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:11:		RunCranePipeline			0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:25:		RunCranePipelineWithChecks		0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:43:		PrepareSourceApp			0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:57:		ApplyOutputToTarget			0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:65:		ApplyOutputToTargetNonAdmin		0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:69:		applyOutputManifests			0.0%
github.com/konveyor/crane/e2e-tests/framework/pipeline.go:80:		checkAndLogStageFiles			0.0%
github.com/konveyor/crane/e2e-tests/framework/rbac.go:14:		GrantNamespaceAdminToUser		0.0%
github.com/konveyor/crane/e2e-tests/framework/rbac.go:41:		RevokeNamespaceAdminFromUser		0.0%
github.com/konveyor/crane/e2e-tests/framework/rbac.go:56:		SetupNamespaceAdminUser			0.0%
github.com/konveyor/crane/e2e-tests/framework/rbac.go:115:		SetupNamespaceAdminUsersForScenario	0.0%
github.com/konveyor/crane/e2e-tests/framework/scenario.go:29:		NewMigrationScenario			0.0%
github.com/konveyor/crane/e2e-tests/framework/scenario.go:80:		NewScenarioPaths			0.0%
github.com/konveyor/crane/e2e-tests/framework/scenario.go:97:		CleanupScenario				0.0%
github.com/konveyor/crane/e2e-tests/utils/utils.go:13:			CreateTempDir				0.0%
github.com/konveyor/crane/e2e-tests/utils/utils.go:18:			ListFilesRecursively			0.0%
github.com/konveyor/crane/e2e-tests/utils/utils.go:40:			ListFilesRecursivelyAsList		0.0%
github.com/konveyor/crane/e2e-tests/utils/utils.go:64:			HasFilesRecursively			0.0%
github.com/konveyor/crane/e2e-tests/utils/utils.go:74:			ReadTestdataFile			0.0%
github.com/konveyor/crane/internal/apply/kustomize.go:28:		ApplySingleStage			0.0%
github.com/konveyor/crane/internal/apply/kustomize.go:69:		ApplyMultiStage				0.0%
github.com/konveyor/crane/internal/apply/kustomize.go:109:		ApplyFinalStage				66.7%
github.com/konveyor/crane/internal/apply/kustomize.go:155:		runKustomizeBuild			87.5%
github.com/konveyor/crane/internal/apply/kustomize.go:172:		ValidateKubectlAvailable		75.0%
github.com/konveyor/crane/internal/apply/kustomize.go:182:		splitMultiDocYAMLToFiles		88.4%
github.com/konveyor/crane/internal/file/file_helper.go:22:		ReadFiles				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:32:		readFiles				84.0%
github.com/konveyor/crane/internal/file/file_helper.go:83:		GetWhiteOutFilePath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:87:		GetTransformPath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:91:		GetIgnoredPatchesPath			0.0%
github.com/konveyor/crane/internal/file/file_helper.go:95:		updateTransformDirPath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:99:		updateIgnoredPatchesDirPath		0.0%
github.com/konveyor/crane/internal/file/file_helper.go:106:		updatePath				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:113:		GetOutputFilePath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:123:		GetStageDir				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:129:		GetResourcesDir				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:135:		GetPatchesDir				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:141:		GetReportsDir				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:147:		GetWhiteoutsDir				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:153:		GetKustomizationPath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:159:		GetMetadataPath				100.0%
github.com/konveyor/crane/internal/file/file_helper.go:165:		GetResourceTypeFilePath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:171:		GetPatchFilePath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:177:		GetWhiteoutReportPath			100.0%
github.com/konveyor/crane/internal/file/file_helper.go:183:		GetIgnoredPatchReportPath		100.0%
github.com/konveyor/crane/internal/flags/global_flags.go:14:		ApplyFlags				0.0%
github.com/konveyor/crane/internal/flags/global_flags.go:21:		GetLogger				0.0%
github.com/konveyor/crane/internal/flags/global_flags.go:29:		initConfig				0.0%
github.com/konveyor/crane/internal/plugin/plugin_helper.go:21:		GetPlugins				0.0%
github.com/konveyor/crane/internal/plugin/plugin_helper.go:38:		getBinaryPlugins			0.0%
github.com/konveyor/crane/internal/plugin/plugin_helper.go:63:		IsExecAny				0.0%
github.com/konveyor/crane/internal/plugin/plugin_helper.go:67:		GetFilteredPlugins			0.0%
github.com/konveyor/crane/internal/plugin/plugin_helper.go:106:		isPluginInList				0.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:24:	BuildManifestMap			0.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:68:	GetYamlFromUrl				75.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:82:	YamlToManifest				72.7%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:104:	FilterPluginForOsArch			100.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:122:	GetDefaultSource			0.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:131:	LocateBinaryInPluginDir			0.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:143:	IsUrl					100.0%
github.com/konveyor/crane/internal/plugin/plugin_manager_helper.go:149:	getData					80.0%
github.com/konveyor/crane/internal/transform/orchestrator.go:36:	RunSingleStage				0.0%
github.com/konveyor/crane/internal/transform/orchestrator.go:110:	RunMultiStage				63.0%
github.com/konveyor/crane/internal/transform/orchestrator.go:173:	executeStage				66.7%
github.com/konveyor/crane/internal/transform/orchestrator.go:238:	loadStageOutput				29.0%
github.com/konveyor/crane/internal/transform/orchestrator.go:304:	filterPluginsByStage			85.7%
github.com/konveyor/crane/internal/transform/stages.go:22:		DiscoverStages				90.5%
github.com/konveyor/crane/internal/transform/stages.go:84:		FilterStages				94.1%
github.com/konveyor/crane/internal/transform/stages.go:153:		GetFirstStage				100.0%
github.com/konveyor/crane/internal/transform/stages.go:163:		GetLastStage				66.7%
github.com/konveyor/crane/internal/transform/stages.go:173:		GetPreviousStage			100.0%
github.com/konveyor/crane/internal/transform/stages.go:183:		GetNextStage				100.0%
github.com/konveyor/crane/internal/transform/stages.go:193:		ValidateStageName			100.0%
github.com/konveyor/crane/internal/transform/stages.go:202:		GenerateStageName			100.0%
github.com/konveyor/crane/internal/transform/writer.go:24:		NewKustomizeWriter			100.0%
github.com/konveyor/crane/internal/transform/writer.go:32:		WriteStage				75.0%
github.com/konveyor/crane/internal/transform/writer.go:152:		parseTypeKey				50.0%
github.com/konveyor/crane/internal/transform/writer.go:174:		capitalizeFirst				66.7%
github.com/konveyor/crane/internal/transform/writer.go:188:		filterValidRemoveOps			76.2%
github.com/konveyor/crane/internal/transform/writer.go:232:		pathExists				93.5%
github.com/konveyor/crane/internal/transform/writer.go:310:		checkStageDirectory			23.1%
github.com/konveyor/crane/main.go:21:					main					0.0%
total:									(statements)				28.1%

Posted by CI

@aufi aufi marked this pull request as ready for review April 8, 2026 09:37
@aufi
Copy link
Copy Markdown
Contributor Author

aufi commented Apr 8, 2026

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (7)
test-data/kustomize-transform/fixtures/service.yaml (1)

1-13: Consider renaming to follow the resource naming guideline.

The filename service.yaml does not follow the documented resource naming convention: Kind_group_version_namespace_name.yaml. For consistency, consider renaming to Service_v1_default_myapp-service.yaml.

However, if test fixtures intentionally use simple names for readability, this can be considered acceptable for test data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test-data/kustomize-transform/fixtures/service.yaml` around lines 1 - 13, The
file uses a generic fixture filename; rename it to follow the project's resource
naming convention by using the pattern Kind_group_version_namespace_name.yaml —
for this resource change service.yaml to Service_v1_default_myapp-service.yaml
(references: kind "Service", apiVersion "v1", metadata.name "myapp-service",
metadata.namespace "default") unless you intentionally want simpler test
filenames for readability.
docs/TRANSFORM_README.md (2)

9-19: Add language specifier to fenced code block.

The code block showing the directory structure should have a language specifier for consistent rendering. Since this shows a directory tree, use an empty language or a generic identifier.

📝 Proposed fix
-```
+```text
 transform/
 └── 10_transform/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/TRANSFORM_README.md` around lines 9 - 19, The fenced code block that
shows the directory tree (starting with the lines "transform/" and "└──
10_transform/") needs a language specifier for consistent rendering; update the
Markdown fenced block delimiter from ``` to ```text (or another generic
identifier) so the directory tree is rendered as plain text, keeping the
existing block contents unchanged.

70-75: Add language specifier to directory structure code block.

📝 Proposed fix
-```
+```text
 transform/
 ├── 10_kubernetes/     # Base Kubernetes transformations
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/TRANSFORM_README.md` around lines 70 - 75, The fenced code block showing
the directory structure (the block beginning with the line "transform/") is
missing a language specifier; update the opening fence from ``` to ```text so
the snippet is explicitly marked as plain text (i.e., change the fenced block
that contains "transform/" and the tree lines to start with ```text).
internal/file/file_helper_test.go (1)

314-350: Good multi-stage path validation with table-driven approach.

The test correctly validates the stage directory structure across multiple stage names. Consider adding edge case tests for boundary conditions such as empty stageName or stage names with special characters, if those scenarios should be handled gracefully.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/file/file_helper_test.go` around lines 314 - 350, Add table-driven
edge-case subtests to TestMultiStagePaths to validate behavior when stageName is
empty and when it contains special characters; use the existing opts
(file.PathOpts) and call its methods GetStageDir, GetResourcesDir, and
GetKustomizationPath for those inputs, asserting expected outputs (or explicit
error/empty behavior if that's the defined contract) so the test covers boundary
conditions alongside the current normal cases.
internal/transform/stages_test.go (1)

58-62: Consider documenting the nonexistent directory behavior.

DiscoverStages returns an empty list without error for nonexistent directories. While this may be intentional (treating missing directory as "no stages"), this could mask configuration errors. Consider whether this behavior is documented or if an error should be returned.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/transform/stages_test.go` around lines 58 - 62, The test shows
DiscoverStages returns an empty slice and no error for a missing directory;
decide and document that behavior: if intended, add a clear doc comment on the
DiscoverStages function explaining that a nonexistent path is treated as "no
stages" and will not produce an error (and optionally rename
TestDiscoverStagesNonexistentDir to reflect that expectation); if unintended,
change DiscoverStages to return an error for nonexistent directories and update
TestDiscoverStagesNonexistentDir to expect that error instead. Ensure references
to DiscoverStages and TestDiscoverStagesNonexistentDir are updated accordingly.
internal/file/file_helper.go (1)

6-6: Consider migrating from deprecated ioutil package.

The ioutil package is deprecated since Go 1.16. While this is existing code, consider updating to use os.ReadDir and os.ReadFile instead of ioutil.ReadDir and ioutil.ReadFile in future cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/file/file_helper.go` at line 6, Replace the deprecated import
"io/ioutil" and any usages of ioutil.ReadDir and ioutil.ReadFile in this file
with the modern equivalents from the os package (use os.ReadDir and
os.ReadFile), update the import block to remove "io/ioutil" and add "os", and
adjust any code handling the returned types (os.ReadDir returns []os.DirEntry)
accordingly (e.g., use DirEntry methods or convert to os.FileInfo if needed).
docs/kustomize-multistage.md (1)

15-33: Add language specifier to directory structure code blocks.

Several fenced code blocks showing directory structures lack language specifiers. For consistency, add text or leave empty with proper markdown.

📝 Proposed fix for lines 15 and similar blocks
-```
+```text
 transform/
 ├── 10_kubernetes/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/kustomize-multistage.md` around lines 15 - 33, The fenced code blocks
that show directory trees (e.g., the block starting with "transform/" and
containing "10_kubernetes/", "20_openshift/", "30_imagestream/") are missing a
language specifier; update those opening fences from ``` to ```text (or another
explicit specifier) for consistency and proper rendering, and apply the same
change to any other similar directory-structure blocks in the document.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/apply/apply.go`:
- Around line 51-69: The Validate() logic treats Flags.FinalOnly's default true
as an explicit selector, causing mutual-exclusion false-positives; change the
flag handling so FinalOnly is only considered when explicitly set (e.g., convert
Flags.FinalOnly to a pointer (*bool) or add an explicit Flags.FinalOnlySet
boolean that the flag parser sets), update Options.Validate() to increment
flagCount only when FinalOnly was explicitly provided (FinalOnlySet or FinalOnly
!= nil && *FinalOnly), and update shouldUseKustomizeWorkflow() and the other
checks mentioned (the blocks around the other mutual-exclusion checks) to use
the same explicit-set check so default state does not count as an opt-in. Ensure
you also update the flag registration/parsing code to set FinalOnlySet or to use
a pointer flag so the presence of the flag is detectable.
- Line 121: The --force flag is defined but ignored; modify the apply command to
respect o.Force by preventing accidental overwrites and by cleaning stale
outputs: when o.Force is false, refuse to create/overwrite the output directory
if it exists and is not empty (return an error); when o.Force is true, remove or
truncate the existing output directory before writing so stale files are deleted
(e.g., remove the directory contents or delete then recreate it) prior to the
write phase. Update the code path that currently creates the output dir in
cmd/apply (where cmd.Flags().BoolVar(&o.Force, ... ) is set) and the writer
logic in internal/apply/kustomize.go (the routines that call os.WriteFile) so
they check o.Force and avoid unconditional os.WriteFile overwrites or perform a
pre-write cleanup when Force==true.

In `@cmd/transform/transform.go`:
- Around line 125-132: The check that decides Kustomize vs legacy JSONPatch is
inspecting flag values (o.StageName, o.PluginName, o.Force) instead of whether
the user actually provided those flags, so default-valued flags incorrectly
trigger the Kustomize path; update the condition to use the FlagSet's Changed
method (e.g., cmd.Flags().Changed("stage-name"),
cmd.Flags().Changed("plugin-name"), cmd.Flags().Changed("force")) when deciding
the single-stage Kustomize flow (and apply the same change to the other similar
check at the other block around the 152-157 region), ensuring you reference the
existing variables o.StageName/o.PluginName/o.Force and the flag names exactly.

In `@docs/kustomize-multistage.md`:
- Around line 408-447: Update the API reference to match the real
implementation: remove undocumented/absent functions AutoAssignPriorities,
MergePriorities, IsDirectoryDirty, EnsureCleanDirectory, ValidateStage,
ValidatePipeline and replace name mismatches (use GenerateStageName instead of
SuggestStageName, and ValidateStageName instead of ValidateStage). In the
Transform examples, use the actual available helpers (DiscoverStages,
StageSelector, FilterStages) and show usage of GetFirstStage, GetLastStage,
GetPreviousStage and GetNextStage where stage navigation is demonstrated; in the
Apply examples, replace ApplyFinalStage/ValidateStage/ValidatePipeline with the
real applier methods ApplySingleStage and ApplyMultiStage and the
ValidateStageName function, and ensure each example matches the real function
signatures in the codebase.

In `@docs/TRANSFORM_README.md`:
- Around line 34-36: The README shows an incorrect mixed command `kubectl
kustomize build transform/10_transform/`; replace it with one correct form:
either use kubectl's built-in kustomize (`kubectl kustomize
transform/10_transform/`) or the standalone Kustomize build command (`kustomize
build transform/10_transform/`), updating the code block accordingly so it uses
a single valid tool invocation.

In `@internal/apply/kustomize.go`:
- Around line 143-166: The kubectl invocations use incorrect flags/subcommands:
in runKustomizeBuild remove the extra "build" argument so the command becomes
"kubectl kustomize DIR" (locate runKustomizeBuild to change exec.Command
invocation), and in ValidateKubectlAvailable remove the undocumented "--short"
flag so the command is "kubectl version --client" (locate
ValidateKubectlAvailable to update exec.Command). Ensure error messages and
Debugf log in runKustomizeBuild remain unchanged and continue to return stderr
on failure.

In `@internal/transform/orchestrator.go`:
- Around line 201-223: loadStageOutput currently reads only the resources
directory via opts.GetResourcesDir and file.ReadFiles, which ignores the stage's
patches wired through kustomization.yaml; change loadStageOutput to run a
kustomize build (e.g., invoke kubectl kustomize or the kustomize library)
against the stage's kustomization (stage directory or resourcesDir where
kustomization.yaml lives) and parse the rendered manifest into
[]unstructured.Unstructured before returning so previous-stage JSONPatch
operations are applied; preserve existing error wrapping and return types when
replacing the file.ReadFiles call.
- Around line 113-131: The first selected stage is always seeded from
o.ExportDir because the code checks if i == 0, but when doing a partial rerun
the first selected stage may have a predecessor in the full pipeline and should
instead load that predecessor's output via o.loadStageOutput. Change the
branching for seeding inputResources so that it only reads from o.ExportDir when
the selected stage is truly the pipeline's initial export stage; otherwise
compute the actual previous stage in the full pipeline for selectedStages[i] and
call o.loadStageOutput(prevStage) (do not rely on selectedStages[i-1] unless it
represents the immediate prior stage in the full pipeline). Use the
selectedStages slice and o.loadStageOutput to locate and load the correct
predecessor stage output (and preserve the existing error handling and logging
when loadStageOutput fails).

In `@internal/transform/stages.go`:
- Around line 116-137: The current range filter (iterating allStages and using
selector.FromStage/ToStage) doesn't detect missing stage names, so a misspelled
selector.ToStage (or FromStage) silently widens the range; fix by tracking
whether the requested FromStage and ToStage were actually seen: add booleans
like foundFrom and foundTo updated when stage.DirName matches
selector.FromStage/ToStage, and after the loop return an empty slice or an error
when selector.FromStage != "" and !foundFrom, or when selector.ToStage != "" and
!foundTo; keep the same logic in the loop (variables filtered, inRange,
allStages, Stage.DirName) but enforce that both specified endpoints must be
found before returning filtered results.

In `@internal/transform/writer.go`:
- Around line 32-37: The current logic only skips os.RemoveAll(stageDir) when
force is false but still proceeds to overwrite resource bundles, patches and
kustomization.yaml; change the flow so that before any writes (where you
currently use stageDir, write bundles/patches/kustomization) you check whether
stageDir exists and is non-empty and, if so and force==false, return an error
(or skip) instead of proceeding; if force==true keep the existing
RemoveAll(stageDir) path and allow writes. Apply the same existence/non-empty
guard to the other write locations referenced in the review (the blocks around
lines 78-80 and 105-120) so functions that create resource bundles, patch files,
and kustomization.yaml only run when either the directory is absent/empty or
force is true.

---

Nitpick comments:
In `@docs/kustomize-multistage.md`:
- Around line 15-33: The fenced code blocks that show directory trees (e.g., the
block starting with "transform/" and containing "10_kubernetes/",
"20_openshift/", "30_imagestream/") are missing a language specifier; update
those opening fences from ``` to ```text (or another explicit specifier) for
consistency and proper rendering, and apply the same change to any other similar
directory-structure blocks in the document.

In `@docs/TRANSFORM_README.md`:
- Around line 9-19: The fenced code block that shows the directory tree
(starting with the lines "transform/" and "└── 10_transform/") needs a language
specifier for consistent rendering; update the Markdown fenced block delimiter
from ``` to ```text (or another generic identifier) so the directory tree is
rendered as plain text, keeping the existing block contents unchanged.
- Around line 70-75: The fenced code block showing the directory structure (the
block beginning with the line "transform/") is missing a language specifier;
update the opening fence from ``` to ```text so the snippet is explicitly marked
as plain text (i.e., change the fenced block that contains "transform/" and the
tree lines to start with ```text).

In `@internal/file/file_helper_test.go`:
- Around line 314-350: Add table-driven edge-case subtests to
TestMultiStagePaths to validate behavior when stageName is empty and when it
contains special characters; use the existing opts (file.PathOpts) and call its
methods GetStageDir, GetResourcesDir, and GetKustomizationPath for those inputs,
asserting expected outputs (or explicit error/empty behavior if that's the
defined contract) so the test covers boundary conditions alongside the current
normal cases.

In `@internal/file/file_helper.go`:
- Line 6: Replace the deprecated import "io/ioutil" and any usages of
ioutil.ReadDir and ioutil.ReadFile in this file with the modern equivalents from
the os package (use os.ReadDir and os.ReadFile), update the import block to
remove "io/ioutil" and add "os", and adjust any code handling the returned types
(os.ReadDir returns []os.DirEntry) accordingly (e.g., use DirEntry methods or
convert to os.FileInfo if needed).

In `@internal/transform/stages_test.go`:
- Around line 58-62: The test shows DiscoverStages returns an empty slice and no
error for a missing directory; decide and document that behavior: if intended,
add a clear doc comment on the DiscoverStages function explaining that a
nonexistent path is treated as "no stages" and will not produce an error (and
optionally rename TestDiscoverStagesNonexistentDir to reflect that expectation);
if unintended, change DiscoverStages to return an error for nonexistent
directories and update TestDiscoverStagesNonexistentDir to expect that error
instead. Ensure references to DiscoverStages and
TestDiscoverStagesNonexistentDir are updated accordingly.

In `@test-data/kustomize-transform/fixtures/service.yaml`:
- Around line 1-13: The file uses a generic fixture filename; rename it to
follow the project's resource naming convention by using the pattern
Kind_group_version_namespace_name.yaml — for this resource change service.yaml
to Service_v1_default_myapp-service.yaml (references: kind "Service", apiVersion
"v1", metadata.name "myapp-service", metadata.namespace "default") unless you
intentionally want simpler test filenames for readability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51b7cff6-f438-4558-8c95-f9a0395fb01f

📥 Commits

Reviewing files that changed from the base of the PR and between 6b752a0 and 5d3311c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • cmd/apply/apply.go
  • cmd/transform/transform.go
  • docs/TRANSFORM_README.md
  • docs/kustomize-multistage.md
  • go.mod
  • internal/apply/kustomize.go
  • internal/file/file_helper.go
  • internal/file/file_helper_test.go
  • internal/transform/orchestrator.go
  • internal/transform/stages.go
  • internal/transform/stages_test.go
  • internal/transform/writer.go
  • test-data/kustomize-transform/fixtures/deployment.yaml
  • test-data/kustomize-transform/fixtures/service.yaml

Comment thread cmd/apply/apply.go
Comment thread cmd/apply/apply.go Outdated
Comment thread cmd/transform/transform.go
Comment thread docs/kustomize-multistage.md
Comment thread docs/TRANSFORM_README.md
Comment thread internal/apply/kustomize.go Outdated
Comment thread internal/transform/orchestrator.go
Comment thread internal/transform/stages.go
Comment thread internal/transform/writer.go Outdated
Signed-off-by: Marek Aufart <maufart@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
internal/transform/writer.go (1)

29-48: ⚠️ Potential issue | 🟠 Major

Refuse to write into a populated stage when --force is off.

Skipping RemoveAll(stageDir) is not enough here: the later WriteFile calls still replace resources/, patches/, and kustomization.yaml. A rerun can still clobber manual edits even when force == false.

Minimal fix
 func (w *KustomizeWriter) WriteStage(artifacts []cranelib.TransformArtifact, force bool) error {
 	stageDir := w.opts.GetStageDir(w.stageName)

-	// Remove existing stage directory if force is set
-	if force {
+	if !force {
+		entries, err := os.ReadDir(stageDir)
+		if err == nil && len(entries) > 0 {
+			return fmt.Errorf("stage directory %q already exists and is not empty; rerun with --force to overwrite", stageDir)
+		}
+		if err != nil && !os.IsNotExist(err) {
+			return fmt.Errorf("failed to inspect stage directory %q: %w", stageDir, err)
+		}
+	}
+
+	if force {
 		if err := os.RemoveAll(stageDir); err != nil && !os.IsNotExist(err) {
-			return fmt.Errorf("failed to remove existing stage directory: %w", err)
+			return fmt.Errorf("failed to remove existing stage directory %q: %w", stageDir, err)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/transform/writer.go` around lines 29 - 48, In
KustomizeWriter.WriteStage, when force is false you must refuse to proceed if
the target stage directory already exists and contains files; before creating
resources/ and patches/ check stageDir (from w.opts.GetStageDir) with os.Stat or
os.ReadDir and return a clear error instead of continuing so later WriteFile
calls won't clobber manual edits. Keep the existing RemoveAll(stageDir) behavior
when force is true, and only perform MkdirAll for resourcesDir/patchesDir after
confirming stageDir is absent or empty.
cmd/apply/apply.go (1)

50-68: ⚠️ Potential issue | 🟠 Major

This still doesn't track whether --final-only was explicitly set.

The flag is registered as a plain bool with a default, but Validate() and shouldUseKustomizeWorkflow() treat it like a tri-state. Without a separate FinalOnlySet/Changed("final-only") check, the selector logic still can't reliably distinguish the default from an explicit opt-in.

Run this read-only check to confirm there is no explicit-set tracking for --final-only in the current tree:

#!/bin/bash
set -euo pipefail

echo "=== final-only registration in cmd/apply/apply.go ==="
sed -n '109,140p' cmd/apply/apply.go

echo
echo "=== explicit-set tracking for final-only ==="
rg -n 'FinalOnlySet|Changed\("final-only"\)|IsSet\("final-only"\)' --type=go

echo
echo "=== all final-only usages ==="
rg -n '\bFinalOnly\b|final-only' --type=go

Expected result: the search finds default-value registration and value checks, but no mechanism that records whether the flag was actually provided by the user.

Also applies to: 119-120, 136-138

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/apply/apply.go` around lines 50 - 68, The Validate() logic incorrectly
treats Flags.FinalOnly as a tri-state without tracking whether the user
explicitly set --final-only; update the flag registration and checks so
explicit-set is recorded (e.g. store a separate boolean like FinalOnlySet or use
the flagset's Changed("final-only")/IsSet API) and change Options.Validate() and
shouldUseKustomizeWorkflow() to consult that explicit-set indicator rather than
the raw Flags.FinalOnly value; specifically, add and set FinalOnlySet when
parsing the flag (or switch to a pointer/Var registration that can distinguish
unset), then replace direct checks of Flags.FinalOnly in Validate() and
shouldUseKustomizeWorkflow() with checks that require FinalOnlySet (or
flagset.Changed) plus the value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/kustomize-multistage.md`:
- Around line 143-160: The docs mention a --validate flag that no longer exists;
fix by either restoring the flag in the CLI or removing the docs. To restore,
add a boolean flag registration and wiring in cmd/apply/apply.go (e.g., on
NewApplyCmd or ApplyCommand initialization) that sets an ApplyOptions.Validate
(or similar) field and is respected by the apply handler (where ApplyOptions are
used); ensure the flag defaults to true and is parsed into the code path that
calls the existing validation routine. Alternatively, if you choose to remove
docs, delete or replace the `--validate` examples and the "Validation" section
in docs/kustomize-multistage.md so the docs reflect the current CLI behavior.

In `@internal/transform/orchestrator.go`:
- Around line 41-44: Currently the code loads the full plugin list into the
variable plugins via plugin.GetFilteredPlugins(o.PluginDir, o.SkipPlugins,
o.Log) and never filters it per-stage; update the orchestration so that before
executing a stage you filter plugins down to only those matching the stage's
target (use stage.PluginName / stage.PluginNames as appropriate) and pass that
filtered list into the transform execution. Concretely: after obtaining plugins,
derive a per-stage slice (e.g., filtered := filterByPluginName(plugins,
stage.PluginName)) and use that filtered variable wherever the code currently
uses plugins to run transforms (the code paths around plugin.GetFilteredPlugins
and the later transform-running block referenced at lines ~151-153). Ensure the
filtering logic handles empty/blank stage.PluginName (meaning “all plugins”) and
preserves existing skip behavior.
- Around line 166-178: Update the error returns in the inputResources loop to
include the API resource identity and stage info: when runner.Run(resource,
plugins) errors, wrap the error with fmt.Errorf to include the stage name
"transform", the resource kind/name/namespace (e.g. fmt.Sprintf("%s/%s/%s",
resource.Kind, resource.Namespace, resource.Name) or the real accessors on the
resource struct) and the actual type received using %T; do the same for the
jsonpatch.DecodePatch error (include "transform decode patches", resource
kind/name/namespace, and %T for the response or resource) so both failures read
like "transform: failed to run transform for <kind>/<ns>/<name> (type %T): %w"
and "transform: failed to decode patches for <kind>/<ns>/<name> (type %T): %w".

---

Duplicate comments:
In `@cmd/apply/apply.go`:
- Around line 50-68: The Validate() logic incorrectly treats Flags.FinalOnly as
a tri-state without tracking whether the user explicitly set --final-only;
update the flag registration and checks so explicit-set is recorded (e.g. store
a separate boolean like FinalOnlySet or use the flagset's
Changed("final-only")/IsSet API) and change Options.Validate() and
shouldUseKustomizeWorkflow() to consult that explicit-set indicator rather than
the raw Flags.FinalOnly value; specifically, add and set FinalOnlySet when
parsing the flag (or switch to a pointer/Var registration that can distinguish
unset), then replace direct checks of Flags.FinalOnly in Validate() and
shouldUseKustomizeWorkflow() with checks that require FinalOnlySet (or
flagset.Changed) plus the value.

In `@internal/transform/writer.go`:
- Around line 29-48: In KustomizeWriter.WriteStage, when force is false you must
refuse to proceed if the target stage directory already exists and contains
files; before creating resources/ and patches/ check stageDir (from
w.opts.GetStageDir) with os.Stat or os.ReadDir and return a clear error instead
of continuing so later WriteFile calls won't clobber manual edits. Keep the
existing RemoveAll(stageDir) behavior when force is true, and only perform
MkdirAll for resourcesDir/patchesDir after confirming stageDir is absent or
empty.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 876faf2f-4e3b-46f5-bb59-81510a9ee3b6

📥 Commits

Reviewing files that changed from the base of the PR and between 5d3311c and 19aab83.

📒 Files selected for processing (8)
  • cmd/apply/apply.go
  • cmd/runfn/util.go
  • docs/TRANSFORM_README.md
  • docs/kustomize-multistage.md
  • internal/apply/kustomize.go
  • internal/transform/orchestrator.go
  • internal/transform/stages.go
  • internal/transform/writer.go
✅ Files skipped from review due to trivial changes (1)
  • cmd/runfn/util.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/transform/stages.go

Comment thread docs/kustomize-multistage.md Outdated
Comment thread internal/transform/orchestrator.go Outdated
Comment thread internal/transform/orchestrator.go
Signed-off-by: Marek Aufart <maufart@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
docs/kustomize-multistage.md (3)

15-33: Add language specifier to code block.

The code block showing the directory structure should have a language specifier for better rendering.

♻️ Suggested fix
-```
+```text
 transform/
 ├── 10_kubernetes/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/kustomize-multistage.md` around lines 15 - 33, Update the fenced code
block that renders the directory tree (starting with "transform/" and the ASCII
tree lines like "├── 10_kubernetes/") to include a language specifier by
replacing the opening triple backticks with ```text so the block becomes
```text, ensuring the directory tree is rendered with the intended fixed-width
formatting.

183-188: Add language specifier to code block.

♻️ Suggested fix
-```
+```text
 export/ → 10_kubernetes/ → 20_openshift/ → 30_imagestream/ → output/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/kustomize-multistage.md` around lines 183 - 188, The fenced code block
that shows the stage chain (the block containing "export/ → 10_kubernetes/ →
20_openshift/ → 30_imagestream/ → output/") should include a language specifier
to aid highlighting; update the opening fence from ``` to ```text so the block
reads as a text code block.

264-285: Add language specifiers to directory structure code blocks.

Both the "Old Workflow" and "New Workflow" directory structure blocks should have language specifiers.

♻️ Suggested fix
 ### Old Workflow
 
-```
+```text
 transform/
 ├── namespace/
 ### New Workflow
 
-```
+```text
 transform/
 └── 10_transform/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/kustomize-multistage.md` around lines 264 - 285, Update the two
directory-structure code fences under "Old Workflow" and "New Workflow" to
include a language specifier (e.g., add "text" after the opening triple
backticks) so the blocks are rendered consistently; locate the code fences that
start with the lines showing "transform/" in the docs/kustomize-multistage.md
content (the blocks under the headings "Old Workflow" and "New Workflow") and
add the language tag to each opening ``` line.
internal/transform/orchestrator.go (1)

229-241: Add timeout to kubectl command execution.

exec.Command("kubectl", "kustomize", stageDir) runs without a timeout. A stuck or slow kubectl process could hang the orchestrator indefinitely. Consider using exec.CommandContext with a deadline.

♻️ Suggested fix with timeout
+import (
+	"time"
+)

 // loadStageOutput loads resources from a completed stage's output
 func (o *Orchestrator) loadStageOutput(stage Stage) ([]unstructured.Unstructured, error) {
 	opts := file.PathOpts{
 		TransformDir: o.TransformDir,
 	}

 	stageDir := opts.GetStageDir(stage.DirName)

-	// Run kubectl kustomize to build the stage with patches applied
-	cmd := exec.Command("kubectl", "kustomize", stageDir)
+	// Run kubectl kustomize with timeout to prevent hanging
+	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+	defer cancel()
+	cmd := exec.CommandContext(ctx, "kubectl", "kustomize", stageDir)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/transform/orchestrator.go` around lines 229 - 241, The kubectl
kustomize invocation uses exec.Command without a timeout and can hang; wrap it
with a context deadline (use context.WithTimeout) and swap exec.Command to
exec.CommandContext(ctx, "kubectl", "kustomize", stageDir), defer cancel(), then
keep using the existing stdout/stderr buffers and cmd.Run(); on error, detect
context.DeadlineExceeded and return a clear timeout error (while still including
stderr.String()) so the orchestrator won't block indefinitely.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/apply/apply.go`:
- Around line 39-45: The Flags struct's FinalOnly pointer (FinalOnly) is never
bound to the cobra flag so it stays nil; either bind the flag to that field with
BoolVar/BoolVarP (or store the returned *bool from cmd.Flags().Bool) so
o.FinalOnly is set when the user passes --final-only, or remove the pointer and
make FinalOnly a plain bool and rely on cmd.Flags().Changed("final-only") in
shouldUseKustomizeWorkflow() to detect explicit use; update the registration
call that currently calls cmd.Flags().Bool("final-only", ...) accordingly to use
BoolVar (binding to Flags.FinalOnly or its address) or change the type and
adjust any callers.

In `@internal/transform/orchestrator.go`:
- Around line 247-253: The current split logic using strings.Split(output,
"\n---\n") (creating variable docs) misses edge cases like leading/trailing
document separators; replace this ad‑hoc split with a proper YAML multi‑document
decoder (e.g., yaml.v3 Decoder on strings.NewReader(output)) and iterate
Decode() until io.EOF to obtain each document, or alternatively normalize the
input by ensuring separators are surrounded by newlines before splitting; update
the code that creates docs and the for loop over doc to consume decoder results
(or the normalized split) and keep the existing TrimSpace/empty checks.
- Around line 125-145: The code currently calls o.loadStageOutput(*prevStage)
and surfaces opaque kubectl errors if the previous stage's output directory is
missing; add a defensive existence check for the previous stage's output
directory (derived from prevStage.DirName / the orchestrator's output root)
before invoking o.loadStageOutput, and if the directory does not exist return an
explicit error that names prevStage.DirName and instructs the user to run
preceding stages in order or use --stage for single-stage execution; update the
block around GetPreviousStage/stage to perform this check and only call
o.loadStageOutput when the directory exists.

---

Nitpick comments:
In `@docs/kustomize-multistage.md`:
- Around line 15-33: Update the fenced code block that renders the directory
tree (starting with "transform/" and the ASCII tree lines like "├──
10_kubernetes/") to include a language specifier by replacing the opening triple
backticks with ```text so the block becomes ```text, ensuring the directory tree
is rendered with the intended fixed-width formatting.
- Around line 183-188: The fenced code block that shows the stage chain (the
block containing "export/ → 10_kubernetes/ → 20_openshift/ → 30_imagestream/ →
output/") should include a language specifier to aid highlighting; update the
opening fence from ``` to ```text so the block reads as a text code block.
- Around line 264-285: Update the two directory-structure code fences under "Old
Workflow" and "New Workflow" to include a language specifier (e.g., add "text"
after the opening triple backticks) so the blocks are rendered consistently;
locate the code fences that start with the lines showing "transform/" in the
docs/kustomize-multistage.md content (the blocks under the headings "Old
Workflow" and "New Workflow") and add the language tag to each opening ``` line.

In `@internal/transform/orchestrator.go`:
- Around line 229-241: The kubectl kustomize invocation uses exec.Command
without a timeout and can hang; wrap it with a context deadline (use
context.WithTimeout) and swap exec.Command to exec.CommandContext(ctx,
"kubectl", "kustomize", stageDir), defer cancel(), then keep using the existing
stdout/stderr buffers and cmd.Run(); on error, detect context.DeadlineExceeded
and return a clear timeout error (while still including stderr.String()) so the
orchestrator won't block indefinitely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0bff1e87-d9ec-4f39-b1b7-89f31cf8cc0a

📥 Commits

Reviewing files that changed from the base of the PR and between 19aab83 and 82a4317.

📒 Files selected for processing (4)
  • cmd/apply/apply.go
  • cmd/transform/transform.go
  • docs/kustomize-multistage.md
  • internal/transform/orchestrator.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/transform/transform.go

Comment thread cmd/apply/apply.go
Comment thread internal/transform/orchestrator.go
Comment thread internal/transform/orchestrator.go Outdated
Signed-off-by: Marek Aufart <maufart@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
cmd/apply/apply.go (1)

137-144: ⚠️ Potential issue | 🟠 Major

final-only routing is still inconsistent and keeps default flow on Kustomize.

Line 143 (|| o.FinalOnly) makes shouldUseKustomizeWorkflow() true by default, so legacy flow is never selected. Also, Lines 190-192 always execute ApplyFinalStage(), so explicit --final-only=false is effectively ignored.

Suggested fix
 func (o *Options) shouldUseKustomizeWorkflow() bool {
-	// Use Kustomize workflow if any multi-stage flags are explicitly set by the user
-	// OR if final-only is true (including default case when no flags are provided)
-	return o.cmd.Flags().Changed("stage") || o.cmd.Flags().Changed("from-stage") ||
-		o.cmd.Flags().Changed("to-stage") || o.cmd.Flags().Changed("stages") ||
-		o.cmd.Flags().Changed("final-only") || o.FinalOnly
+	if o.cmd == nil {
+		return false
+	}
+	return o.cmd.Flags().Changed("stage") || o.cmd.Flags().Changed("from-stage") ||
+		o.cmd.Flags().Changed("to-stage") || o.cmd.Flags().Changed("stages") ||
+		o.cmd.Flags().Changed("final-only")
 }
@@
-	// Default or explicit --final-only: apply final stage
-	log.Info("Applying final stage...")
-	return applier.ApplyFinalStage()
+	// Explicitly disabled final-only means "not final-only": run all stages.
+	if o.cmd.Flags().Changed("final-only") && !o.FinalOnly {
+		log.Info("Applying all stages...")
+		return applier.ApplyMultiStage(internalTransform.StageSelector{})
+	}
+
+	log.Info("Applying final stage...")
+	return applier.ApplyFinalStage()
 }

Also applies to: 176-192

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/apply/apply.go` around lines 137 - 144, The Kustomize routing treats
FinalOnly as if it were explicitly requested because
shouldUseKustomizeWorkflow() uses o.FinalOnly directly; change that to only
treat final-only as set when the flag was explicitly changed (use
o.cmd.Flags().Changed("final-only") && o.FinalOnly) so default true doesn’t
force Kustomize; similarly, where ApplyFinalStage() is unconditionally called
(the block invoking ApplyFinalStage), replace checks that use o.FinalOnly
directly with an explicit check that the final-only flag was changed and true
(o.cmd.Flags().Changed("final-only") && o.FinalOnly) or derive the decision from
shouldUseKustomizeWorkflow() so that --final-only=false actually disables the
final-only path.
🧹 Nitpick comments (3)
internal/transform/writer.go (1)

134-153: Consider using strings.Index for cleaner parsing.

The manual loop to find the dot index works but is more verbose than necessary.

♻️ Simpler implementation
 func parseTypeKey(typeKey string) (kind, group string) {
-	// Check if typeKey contains a dot (indicating non-core resource)
-	dotIndex := -1
-	for i, ch := range typeKey {
-		if ch == '.' {
-			dotIndex = i
-			break
-		}
-	}
-
-	if dotIndex == -1 {
+	dotIndex := strings.Index(typeKey, ".")
+	if dotIndex < 0 {
 		// Core resource - capitalize first letter
 		return capitalizeFirst(typeKey), ""
 	}
 
 	// Non-core resource - split on first dot
 	kindPart := typeKey[:dotIndex]
 	groupPart := typeKey[dotIndex+1:]
 	return capitalizeFirst(kindPart), groupPart
 }

Note: This requires adding "strings" to imports if not already present.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/transform/writer.go` around lines 134 - 153, Replace the manual rune
loop in parseTypeKey with strings.Index to find the first '.'; use idx :=
strings.Index(typeKey, ".") and branch on idx == -1 for core resources (return
capitalizeFirst(typeKey), "") or split using kindPart := typeKey[:idx] and
groupPart := typeKey[idx+1:] for non-core resources, and add "strings" to the
imports; keep the existing calls to capitalizeFirst unchanged.
internal/transform/orchestrator.go (1)

54-92: Consider extracting common transform loop logic.

The transform loop in RunSingleStage (lines 54-92) and executeStage (lines 192-221) are nearly identical. Extracting this into a shared helper would reduce duplication and ensure consistent error handling.

♻️ Potential helper signature
// transformResources runs transforms on resources and returns artifacts
func (o *Orchestrator) transformResources(
	stageName string,
	resources []unstructured.Unstructured,
	plugins []cranelib.Plugin,
	pluginName string,
) ([]cranelib.TransformArtifact, error) {
	runner := cranelib.Runner{
		Log:              o.Log,
		PluginPriorities: o.PluginPriorities,
		OptionalFlags:    o.OptionalFlags,
	}

	var artifacts []cranelib.TransformArtifact
	for _, resource := range resources {
		// ... common transform logic ...
	}
	return artifacts, nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/transform/orchestrator.go` around lines 54 - 92, Extract the
duplicated transform loop in RunSingleStage and executeStage into a shared
method on Orchestrator (suggested name transformResources) that accepts
stageName string, resources []unstructured.Unstructured, plugins
[]cranelib.Plugin and pluginName string and returns
([]cranelib.TransformArtifact, error); move the Runner construction
(cranelib.Runner{Log: o.Log, PluginPriorities: o.PluginPriorities,
OptionalFlags: o.OptionalFlags}), the loop that calls runner.Run, JSON patch
decoding, error wrapping (include stageName, resource identity and type), and
construction of cranelib.TransformArtifact (Resource, HaveWhiteOut, Patches,
IgnoredOps, Target via cranelib.DeriveTargetFromResource, PluginName) into that
helper, then replace the loop bodies in RunSingleStage and executeStage to call
o.transformResources(...) and propagate its returned artifacts/error.
cmd/apply/apply.go (1)

150-163: Wrap path/IO errors with context for faster debugging.

At Lines 152, 157, and 162, raw errors are returned without indicating which input/path failed. Please include transform-dir/output-dir context in the error messages.

Suggested fix
 	transformDir, err := filepath.Abs(o.TransformDir)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to resolve --transform-dir %q: %w", o.TransformDir, err)
 	}
@@
 	outputDir, err := filepath.Abs(o.OutputDir)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to resolve --output-dir %q: %w", o.OutputDir, err)
 	}
@@
 	if err := os.MkdirAll(outputDir, 0700); err != nil {
-		return fmt.Errorf("failed to create output directory: %w", err)
+		return fmt.Errorf("failed to create output directory %q: %w", outputDir, err)
 	}

As per coding guidelines: "Prefer explicit error messages with context in Go code".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/apply/apply.go` around lines 150 - 163, The errors from filepath.Abs and
os.MkdirAll lack context; update the calls around transformDir and outputDir
(the filepath.Abs calls for o.TransformDir and o.OutputDir and the os.MkdirAll
call) to wrap returned errors with contextual messages indicating which path
failed (e.g., include "transform-dir: <value>" for transformDir and "output-dir:
<value>" for outputDir) using fmt.Errorf("%s: %w", ...) so callers can see which
path/operation caused the error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/apply/apply.go`:
- Around line 125-193: Tests are missing for the new stage-selection
routing/validation introduced around shouldUseKustomizeWorkflow and
runKustomizeWorkflow; add unit tests that exercise: (1) default routing when no
flags are set (assert shouldUseKustomizeWorkflow() returns true and
runKustomizeWorkflow invokes ApplyFinalStage), (2) selector exclusivity and
multi-flag selection (construct a StageSelector with Stage, FromStage, ToStage,
Stages and assert ApplyMultiStage is called with the expected selector), and (3)
explicit --final-only=false behavior (simulate the flag state where FinalOnly is
false and the "final-only" flag is explicitly set to false and assert
multi-stage vs final-stage routing). Use table-driven tests, mock or fake
apply.KustomizeApplier to observe calls to ApplyMultiStage and ApplyFinalStage,
and exercise shouldUseKustomizeWorkflow, runKustomizeWorkflow, and StageSelector
construction to cover validation/regression paths.

In `@internal/transform/orchestrator.go`:
- Around line 245-256: The kubectl kustomize invocation currently uses
exec.Command and cmd.Run() with no timeout; wrap the call in a context with
timeout (e.g., context.WithTimeout, configurable or a sensible default like
30s), use exec.CommandContext(ctx, "kubectl", "kustomize", stageDir), defer
cancel(), and handle ctx.Err() / context.DeadlineExceeded when returning the
error (include stderr.String() as before). Update the block that builds the
stage (where cmd := exec.Command("kubectl", "kustomize", stageDir),
stdout/stderr buffers, and the cmd.Run() call) to use the context-based command
and proper timeout handling, keeping the existing debug log and error message
(including stage.DirName and stderr).

In `@internal/transform/writer.go`:
- Around line 78-85: The patch serialization and write error messages lack the
resource identity; update the error handling around
kustomize.SerializePatchToYAML(artifact.Patches) and the os.WriteFile(patchPath,
...) call to include the artifact's resource identity (e.g., artifact.Name,
artifact.Namespace, artifact.Kind or artifact.ID) along with patchPath and the
wrapped error. Replace the current fmt.Errorf("failed to serialize patch: %w",
err) and fmt.Errorf("failed to write patch file: %w", err) with messages that
embed these identifying fields (for example: fmt.Errorf("failed to serialize
patch for %s/%s (%s): %w", artifact.Namespace, artifact.Name, artifact.Kind,
err)) so any failure clearly states which resource and file failed.

---

Duplicate comments:
In `@cmd/apply/apply.go`:
- Around line 137-144: The Kustomize routing treats FinalOnly as if it were
explicitly requested because shouldUseKustomizeWorkflow() uses o.FinalOnly
directly; change that to only treat final-only as set when the flag was
explicitly changed (use o.cmd.Flags().Changed("final-only") && o.FinalOnly) so
default true doesn’t force Kustomize; similarly, where ApplyFinalStage() is
unconditionally called (the block invoking ApplyFinalStage), replace checks that
use o.FinalOnly directly with an explicit check that the final-only flag was
changed and true (o.cmd.Flags().Changed("final-only") && o.FinalOnly) or derive
the decision from shouldUseKustomizeWorkflow() so that --final-only=false
actually disables the final-only path.

---

Nitpick comments:
In `@cmd/apply/apply.go`:
- Around line 150-163: The errors from filepath.Abs and os.MkdirAll lack
context; update the calls around transformDir and outputDir (the filepath.Abs
calls for o.TransformDir and o.OutputDir and the os.MkdirAll call) to wrap
returned errors with contextual messages indicating which path failed (e.g.,
include "transform-dir: <value>" for transformDir and "output-dir: <value>" for
outputDir) using fmt.Errorf("%s: %w", ...) so callers can see which
path/operation caused the error.

In `@internal/transform/orchestrator.go`:
- Around line 54-92: Extract the duplicated transform loop in RunSingleStage and
executeStage into a shared method on Orchestrator (suggested name
transformResources) that accepts stageName string, resources
[]unstructured.Unstructured, plugins []cranelib.Plugin and pluginName string and
returns ([]cranelib.TransformArtifact, error); move the Runner construction
(cranelib.Runner{Log: o.Log, PluginPriorities: o.PluginPriorities,
OptionalFlags: o.OptionalFlags}), the loop that calls runner.Run, JSON patch
decoding, error wrapping (include stageName, resource identity and type), and
construction of cranelib.TransformArtifact (Resource, HaveWhiteOut, Patches,
IgnoredOps, Target via cranelib.DeriveTargetFromResource, PluginName) into that
helper, then replace the loop bodies in RunSingleStage and executeStage to call
o.transformResources(...) and propagate its returned artifacts/error.

In `@internal/transform/writer.go`:
- Around line 134-153: Replace the manual rune loop in parseTypeKey with
strings.Index to find the first '.'; use idx := strings.Index(typeKey, ".") and
branch on idx == -1 for core resources (return capitalizeFirst(typeKey), "") or
split using kindPart := typeKey[:idx] and groupPart := typeKey[idx+1:] for
non-core resources, and add "strings" to the imports; keep the existing calls to
capitalizeFirst unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e323c62-5c4a-482a-a34e-7e9ac5da3aec

📥 Commits

Reviewing files that changed from the base of the PR and between 82a4317 and cb3073b.

📒 Files selected for processing (4)
  • cmd/apply/apply.go
  • go.mod
  • internal/transform/orchestrator.go
  • internal/transform/writer.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • go.mod

Comment thread cmd/apply/apply.go
Comment thread internal/transform/orchestrator.go
Comment thread internal/transform/writer.go Outdated
Signed-off-by: Marek Aufart <maufart@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cmd/apply/apply.go (1)

128-131: Create outputDir only when something is actually written.

ApplyMultiStage() and ApplyFinalStage() already call os.MkdirAll(k.OutputDir, 0700) before writing. Doing it here means a failed stage discovery/build still leaves an empty output directory behind. Letting the applier create it lazily keeps failed runs side-effect free.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/apply/apply.go` around lines 128 - 131, The current unconditional call to
os.MkdirAll(outputDir, 0700) creates an empty output directory even when the run
fails; remove or defer that call in Apply (the block in apply.go that uses
outputDir) so the directory is only created lazily by the applier when files are
actually written (as already done by ApplyMultiStage() and ApplyFinalStage());
specifically, delete the os.MkdirAll(outputDir, 0700) invocation here (or
replace it with a no-op) and rely on the existing MkdirAll calls in
ApplyMultiStage() / ApplyFinalStage() to create k.OutputDir only when writing
occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/apply/apply.go`:
- Around line 140-143: The branch that decides whether to call ApplyMultiStage()
vs ApplyFinalStage() incorrectly checks cobra flag change state via
o.cmd.Flags().Changed(...) instead of using the merged, config-backed option
values loaded into o.Flags in PreRun; update the conditional to inspect the
actual stage selector values on o.Flags (e.g., o.Flags.Stage, o.Flags.FromStage,
o.Flags.ToStage, o.Flags.Stages or whichever fields represent the selectors) and
call ApplyMultiStage() when any of those merged values are set, otherwise call
ApplyFinalStage(); after switching to value-based routing you can remove the
extra Options.cmd plumbing mentioned in the comment.

---

Nitpick comments:
In `@cmd/apply/apply.go`:
- Around line 128-131: The current unconditional call to os.MkdirAll(outputDir,
0700) creates an empty output directory even when the run fails; remove or defer
that call in Apply (the block in apply.go that uses outputDir) so the directory
is only created lazily by the applier when files are actually written (as
already done by ApplyMultiStage() and ApplyFinalStage()); specifically, delete
the os.MkdirAll(outputDir, 0700) invocation here (or replace it with a no-op)
and rely on the existing MkdirAll calls in ApplyMultiStage() / ApplyFinalStage()
to create k.OutputDir only when writing occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bbc2e8d6-87ab-437a-b3cf-b645a904574a

📥 Commits

Reviewing files that changed from the base of the PR and between cb3073b and fde2ec2.

📒 Files selected for processing (1)
  • cmd/apply/apply.go

Comment thread cmd/apply/apply.go Outdated
aufi added 6 commits April 8, 2026 18:11
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Comment thread docs/TRANSFORM_README.md
@@ -0,0 +1,373 @@
# Crane Transform Directory Structure
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs files structure will be updated/unified with #215

Signed-off-by: Marek Aufart <maufart@redhat.com>
Copy link
Copy Markdown
Contributor

@msajidmansoori12 msajidmansoori12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aufi Hi I noticed that in output dir manifests , the yaml indentation contains 4 spaces while in general the manifests we get from kubectl contains 2 spaces. Just a formatting difference not something that needs to be fixed right away just wanted to make a note of it.

apiVersion: apps/v1
kind: Deployment
metadata:
    annotations:
        deployment.kubernetes.io/revision: "1"
    labels:
        name: simple-nginx-nopv
    name: simple-nginx-nopv-deployment
    namespace: simple-nginxx-nopv
spec:
    progressDeadlineSeconds: 600
    replicas: 1
    revisionHistoryLimit: 10
    selector:
        matchLabels:
            app: simple-nginx-nopv
    strategy:
        rollingUpdate:
            maxSurge: 25%
            maxUnavailable: 25%
        type: RollingUpdate
    template:

Comment thread cmd/apply/apply.go
Comment thread internal/transform/orchestrator.go
Comment thread cmd/transform/transform.go Outdated
Comment thread internal/apply/kustomize.go
aufi added 4 commits April 9, 2026 16:41
orchestration unit test

Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
Signed-off-by: Marek Aufart <maufart@redhat.com>
// and returns them sorted by priority (numeric prefix)
func DiscoverStages(transformDir string) ([]Stage, error) {
// Pattern: <number>_<pluginName>
pattern := regexp.MustCompile(`^([0-9]+)_([a-zA-Z0-9_-]+)$`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current stage name validation regex only allows [a-zA-Z0-9_-]+ for the plugin portion, which excludes dots (.).

This prevents valid Kubernetes-style identifiers like route.openshift.io from being used in stage names (e.g., 30_route.openshift.io would fail validation).

Considering that many Kubernetes API groups and resources use dotted naming conventions, it would be beneficial to support . in plugin names.

Copy link
Copy Markdown
Contributor Author

@aufi aufi Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for comment. Existing plugin names usually describe their use-case (typically with CamelCase describing technology or something - https://github.com/migtools/crane-plugins/tree/main/plugins, so in this case maybe RouteToGatewayPlugin would work too).

I'd keep the regex validation without ., just might document it in some plugin development doc.

@aufi aufi requested a review from stillalearner April 10, 2026 06:50
Copy link
Copy Markdown
Contributor

@stillalearner stillalearner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Thanks @aufi , great PR

@aufi aufi merged commit aa106d6 into migtools:main Apr 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants