Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
155 commits
Select commit Hold shift + click to select a range
07ac89b
feat(render): expose ErrStaleRenderBackend for outdated render backends
jcogilvie Oct 18, 2026
b18b3ae
feat(diff): port renderer, types and testutils from crossplane-diff
jcogilvie Oct 18, 2026
8cd5e3f
build(nix): fetch Crossplane CRDs for diff tests
jcogilvie Oct 18, 2026
1ab035b
build(nix): format apps.nix and refresh vendor hash
jcogilvie Oct 18, 2026
a777efb
feat(diff): port ref, dockerrun and core client from crossplane-diff
jcogilvie Oct 18, 2026
e4fe74f
chore(diff): resync ported packages with crossplane-diff 9fa7d31
jcogilvie Oct 18, 2026
8cda557
feat(diff): port kubernetes client from crossplane-diff
jcogilvie Oct 18, 2026
1f914a1
feat(diff): port crossplane client from crossplane-diff
jcogilvie Oct 18, 2026
49bca29
feat(diff): port diffprocessor from crossplane-diff
jcogilvie Oct 18, 2026
ba1e321
refactor(diff): use render.ErrStaleRenderBackend from the cli
jcogilvie Oct 18, 2026
8f637f7
refactor(diff): use semver v3 for render version checks
jcogilvie Oct 19, 2026
b14c7d2
feat(diff): add client bundle constructor
jcogilvie Oct 19, 2026
47a3cde
refactor(project): export function loading for reuse by simulate
jcogilvie Oct 19, 2026
0c47131
fix(project): match default excludes when APIs path is the project root
jcogilvie Oct 19, 2026
61d2585
feat(project): export composition discovery
jcogilvie Oct 19, 2026
c5fdbd3
feat(diff): add project function overlay provider
jcogilvie Oct 19, 2026
3110a15
feat(project): add alpha project simulate command
jcogilvie Oct 19, 2026
e203e7e
feat(project): exit 3 on changes with project simulate --exit-code
jcogilvie Oct 19, 2026
5936cf0
feat(diff): port crossplane-diff integration tests behind a simulate …
jcogilvie Oct 19, 2026
52fd448
fix(diff): lint the ported integration tests
jcogilvie Oct 19, 2026
7fc39fa
fix(diff): skip integration environment gaps before the cluster starts
jcogilvie Oct 19, 2026
70a5abd
fix(diff): guard the concurrency test's cluster startup like the runner
jcogilvie Oct 19, 2026
d4f66b9
feat(project): add crossplane-diff flag parity to project simulate
jcogilvie Oct 19, 2026
f95734e
test(kube,diff): cover RESTConfig and client.New
jcogilvie Oct 19, 2026
b2056d3
test(diff): enable flag-parity integration cases
jcogilvie Oct 19, 2026
5e47c11
build(nix): provide envtest assets for integration tests
jcogilvie Oct 19, 2026
9b37e70
build(nix): run integration tests in the main test target
jcogilvie Oct 20, 2026
8631543
Revert "build(nix): run integration tests in the main test target"
jcogilvie Oct 20, 2026
cbd9abf
test(diff): put integration tests behind the integration build tag
jcogilvie Oct 20, 2026
51b00fa
build(nix): publish integration test coverage
jcogilvie Oct 20, 2026
7824b49
test(diff): run the XR integration cases through the engine harness
jcogilvie Oct 20, 2026
e08d585
feat(project): add --output-version to project simulate
jcogilvie Oct 20, 2026
38b87dc
feat(project)!: make project simulate's -f a repeatable --input
jcogilvie Oct 20, 2026
57f0ca8
feat(project): simulate Composition and composite resource files
jcogilvie Oct 20, 2026
9497f57
test(diff): run the integration cases through project simulate
jcogilvie Oct 20, 2026
9f6f3b1
fix(project): stop ignoring last-applied-configuration in project sim…
jcogilvie Oct 20, 2026
ab52696
fix(project): reject -f inputs that are not composite resources
jcogilvie Oct 20, 2026
72823e0
feat(project)!: exit 3 when project simulate finds changes
jcogilvie Oct 20, 2026
a74e96b
fix(diff): print errors once, from the command, not inline
jcogilvie Oct 20, 2026
0941a2e
refactor(diff): move object comparison out of the renderer into compare
jcogilvie Oct 20, 2026
83cfc83
feat(plan): carry composite grouping, errors and revision impact in p…
jcogilvie Oct 20, 2026
dee0633
refactor(simulate): move per-resource planning into internal/simulate
jcogilvie Oct 20, 2026
bfc400f
refactor(diff): processors return reports; the command renders them
jcogilvie Oct 21, 2026
d906461
refactor(diff): move the output model into a result package
jcogilvie Oct 21, 2026
b9668f4
refactor(diff): record filtered XRs in both modes; the renderer decides
jcogilvie Oct 21, 2026
2a8b154
feat(simulate): map diff reports to plan.Result
jcogilvie Oct 21, 2026
3811f1b
feat(plan): print revision impact and notes in plan text
jcogilvie Oct 21, 2026
37ba607
test(simulate): drop the unused ref helper
jcogilvie Oct 21, 2026
159cc8a
feat(project)!: default project simulate to -o plan
jcogilvie Oct 21, 2026
3a9d8b7
fix(plan): quote map keys containing dots in field paths
jcogilvie Oct 21, 2026
e60df12
fix(diff): key the function provider cache by pipeline
jcogilvie Oct 21, 2026
b1eb748
feat(diff): let changed functions trigger composition impact analysis
jcogilvie Oct 21, 2026
dc4bdec
feat(plan): name each composite's composition in comp results
jcogilvie Oct 21, 2026
388b773
test(simulate): assert integration JSON output on plan.Result
jcogilvie Oct 21, 2026
6cd6e8d
refactor(simulate)!: drop crossplane-diff's v1/v2 structured output
jcogilvie Oct 21, 2026
d2b10be
feat(diff): opt-in WithKeepEqualDiffs predicate
jcogilvie Oct 21, 2026
1d3665c
feat(simulate): plan engine diffs with provider diff servers
jcogilvie Oct 21, 2026
204acc6
refactor(simulate): share diff server lifecycle; load project provide…
jcogilvie Oct 21, 2026
24936f6
feat(simulate): annotate -o diff with provider plans
jcogilvie Oct 21, 2026
87f139b
feat(project): detect changed embedded functions in project simulate
jcogilvie Oct 21, 2026
36c2eb7
test(diff): keep engine tests free of CLI packages
jcogilvie Oct 21, 2026
bf9adc6
test(simulate): mark the unchanged-field check as reduced coverage
jcogilvie Oct 21, 2026
d2674c0
fix(simulate): tidy provider-plan review nits
jcogilvie Oct 21, 2026
dfaf72a
feat(plan)!: carry before and after objects in plan.Result
jcogilvie Oct 21, 2026
5f4887e
fix(plan): clean before and after in resource simulate
jcogilvie Oct 21, 2026
903db0e
feat(diff): opt-in per-XR timeout and after-XR hook
jcogilvie Oct 21, 2026
04483f9
feat(simulate): plan each composite's resources as the engine renders it
jcogilvie Oct 22, 2026
350c715
feat(simulate): start provider diff servers incrementally
jcogilvie Oct 22, 2026
081dec9
feat(project): add provider plan flags to project simulate
jcogilvie Oct 22, 2026
19809b8
test(simulate): fix and tighten XRPlanner tests
jcogilvie Oct 22, 2026
b7ab0f1
test(simulate): stop shadowing ensureCtx in XRPlanner tests
jcogilvie Oct 22, 2026
9fdf779
style(project): align simulate flag tags
jcogilvie Oct 22, 2026
de3f7b3
feat(project): plan composed resources with provider diff servers
jcogilvie Oct 22, 2026
c1758ee
feat(project): show provider plans in project simulate output
jcogilvie Oct 22, 2026
65975d8
test(diff): run project simulate against a fake provider diff server
jcogilvie Oct 22, 2026
15355f5
fix(project): mark replacements in project simulate -o diff
jcogilvie Oct 22, 2026
59ade08
docs(project): describe provider plans and exit codes in simulate help
jcogilvie Oct 22, 2026
2f9d79c
perf(diff): build cluster lookups lazily instead of listing at startup
jcogilvie Oct 22, 2026
104855d
test(diff): port lazy-lookup integration cases
jcogilvie Oct 22, 2026
dc71e43
fix(simulate): stop mutating the global scheme in NewKubeClient
jcogilvie Oct 22, 2026
1cbed0a
fix(diff): pause the per-XR budget while run-wide work runs
jcogilvie Oct 22, 2026
dd65518
fix(project): note when no diff server served any plan
jcogilvie Oct 22, 2026
43ebcd7
fix(simulate): don't cache ProviderConfig lookups that failed on a co…
jcogilvie Oct 22, 2026
d89e116
fix(project): reject a non-positive --timeout
jcogilvie Oct 22, 2026
d301f72
fix(simulate): degrade when a provider lookup fails in on-demand mode
jcogilvie Oct 22, 2026
a3e0497
fix(simulate): keep the first plan per diff key and document sequenti…
jcogilvie Oct 22, 2026
16994ca
fix(project): allow --cache-dir with planning, select servers in a te…
jcogilvie Oct 22, 2026
4f8b38d
fix(diff): test function start outside the XR budget, close the budge…
jcogilvie Oct 22, 2026
b55b80d
fix(simulate): count routed plans that fail, capitalize the warning, …
jcogilvie Oct 22, 2026
2ea5858
fix(simulate): key incremental server start by name for Development p…
jcogilvie Oct 22, 2026
daf21fa
feat(project): accept repeated and namespace-qualified --name in proj…
jcogilvie Oct 22, 2026
b06d6ca
feat(diff): render pinned XRs against their revision when it calls a …
jcogilvie Oct 22, 2026
4c98f33
feat(simulate): report pinned XRs rendered against their revision
jcogilvie Oct 22, 2026
e49fc9c
fix(simulate): keep cluster-only changes beside a provider plan's
jcogilvie Oct 22, 2026
d7d346a
fix(plan): tolerate empty replace paths and mark pinned composites in…
jcogilvie Oct 22, 2026
bc6ebae
fix(simulate): cache only definite CRD answers, show provider-only re…
jcogilvie Oct 22, 2026
0c6c114
test(simulate): cover dotted Secret keys and CRD fetch caching
jcogilvie Oct 22, 2026
24d4d1b
feat(plan)!: always report deferred, name error refs ref, mark every …
jcogilvie Oct 22, 2026
22305c5
feat(plan)!: report composite reasons as a typed camelCase enum
jcogilvie Oct 22, 2026
2d38e40
build(nix): keep envtest assets Linux-only and out of the unit-test c…
jcogilvie Oct 22, 2026
a87fb15
ci: cache Go modules in the integration-tests job
jcogilvie Oct 22, 2026
ce11095
fix(diff): resolve pinned XRs on changed functions before skipping a …
jcogilvie Oct 22, 2026
b56c115
fix(diff): drop the --resource flag name from the preflight error
jcogilvie Oct 22, 2026
15e05a4
build(nix): provide envtest binaries on macOS
jcogilvie Oct 22, 2026
42e0e3c
build(nix): inherit envtest hash for statix
jcogilvie Oct 22, 2026
2a59fac
fix(load): add a loader constructor that doesn't split paths on commas
jcogilvie Oct 22, 2026
b745376
fix(project): validate simulate's flags before building and keep both…
jcogilvie Oct 22, 2026
73d61ea
test(diff): report parse errors from the simulate harness as main does
jcogilvie Oct 22, 2026
894cc3b
test(simulate): fail plan assertions that would pass vacuously
jcogilvie Oct 22, 2026
1f323ae
test(simulate): unify plan expectations behind one builder
jcogilvie Oct 22, 2026
ec39f5e
test(simulate): merge the downstream and flat resource expectations
jcogilvie Oct 22, 2026
bdcd772
test(simulate): enforce spec and absent-field checks on downstream re…
jcogilvie Oct 22, 2026
aefefd0
test(simulate): call the unified plan expectation builder
jcogilvie Oct 22, 2026
65f353d
test(simulate): drop the pre-unification expectation names
jcogilvie Oct 22, 2026
e01e2ca
test(diff): give integration cases one plan expectation field
jcogilvie Oct 22, 2026
7456234
test(diff): run project simulate on project fixtures
jcogilvie Oct 22, 2026
df3be22
test(diff): simulate a project with an embedded function
jcogilvie Oct 22, 2026
317cea1
build(nix): keep the host PATH for integration tests
jcogilvie Oct 22, 2026
829de80
test(project): let tests replace simulate's diff server set
jcogilvie Oct 22, 2026
a01d955
test(diff): simulate a project with a provider dependency
jcogilvie Oct 22, 2026
669522b
test(diff): prove the project's function wins over the cluster's
jcogilvie Oct 22, 2026
8b52e61
test: address project table review nits
jcogilvie Oct 22, 2026
48f2d6e
test(diff): drop the -short skip from project cases
jcogilvie Oct 23, 2026
b351a05
docs(project): fix vale errors in simulate help
jcogilvie Oct 23, 2026
2365fb3
docs(project): say which errors exit 1 and 80 in simulate help
jcogilvie Oct 23, 2026
7e15b0c
style(project): realign simulate flag tags
jcogilvie Oct 23, 2026
862923b
Task 1: chore(lint): revert the exhaustive linter config
jcogilvie Oct 26, 2026
5e637cc
Task 1: chore(lint): drop exhaustive:enforce marker
jcogilvie Oct 26, 2026
ce4d3b4
Task 2: fix(project): kubeconfig namespace and the overwritten render…
jcogilvie Oct 26, 2026
d60ad45
Task 3: refactor(project): simplify simulate internals
jcogilvie Oct 26, 2026
545d275
Task 4: refactor(project): one xpkg client, one project parse, one de…
jcogilvie Oct 27, 2026
8c80fbc
Task 4: fix(project): compute embedded function digests only on request
jcogilvie Oct 27, 2026
d3108e2
Task 5: refactor(project): embed render.EngineFlags; function images …
jcogilvie Oct 27, 2026
8a9a37e
Task 5: fix(project): per-command --session-id help; review fixes
jcogilvie Oct 27, 2026
61841b8
Task 6: refactor(project): align simulate flags with the rest of the CLI
jcogilvie Oct 27, 2026
e896b8b
Task 6: fix(project): decode only v1 Secrets from --function-credentials
jcogilvie Oct 27, 2026
1bb124e
Task 7: refactor(plan)!: restore plan.Result; add ProjectResult
jcogilvie Oct 27, 2026
40b1d75
Task 7: fix(simulate): keep project JSON's summary-first key order
jcogilvie Oct 27, 2026
96aa440
Task 8: refactor(plan)!: use crossplane-runtime fieldpath for change …
jcogilvie Oct 27, 2026
9458b56
Task 8: fix(plan): keep quoted keys verbatim in change paths
jcogilvie Oct 27, 2026
5f5c307
Task 8: docs(plan): document keys that do not round-trip through fiel…
jcogilvie Oct 27, 2026
5a41443
Task 9: feat(simulate)!: one exit-code mechanism; mode-flag handling
jcogilvie Oct 27, 2026
2269251
Task 8: docs(plan): correct the fieldpath round-trip cases
jcogilvie Oct 27, 2026
4803b10
Task 9: fix(simulate): exit 1 on errored plans; warn on --skip-plan f…
jcogilvie Oct 27, 2026
5030390
Task 9: fix(main): match a silent ExitError through kong's join
jcogilvie Oct 27, 2026
6b09fa8
Task 9: docs(simulate): scope the exit-80 sentence to input-dependent…
jcogilvie Oct 27, 2026
ab190e9
Task 10: docs(project): one term per layer; trim help internals
jcogilvie Oct 27, 2026
7fd8880
Task 10: docs(simulate): restore the before/after contract; drop cros…
jcogilvie Oct 27, 2026
1fca507
Task 11: fix(project): keep -f resources that equal a pipeline input
jcogilvie Oct 27, 2026
7c2ce1c
Task 12: test(project): move simulate integration tests next to the c…
jcogilvie Oct 27, 2026
5dae6dd
docs(simulate): address final review of round 1
jcogilvie Oct 27, 2026
ba1ca4a
test(simulate): pin WithoutPipelineInputs and --providers precedence
jcogilvie Oct 27, 2026
8da11e9
test(project): rewrap the localCrossplaneBinary comment
jcogilvie Oct 27, 2026
2994422
refactor(simulate): name the field manager, lease dir and XR seed cro…
jcogilvie Oct 27, 2026
7b5cfb0
refactor(project): load --function-credentials with render.LoadCreden…
jcogilvie Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,45 @@ jobs:
file: result/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}

# The diff integration tests run the render engine's functions as Docker
# containers, so they cannot run in the Nix sandbox like the unit tests.
# The app runs outside the sandbox against the runner's Docker daemon.
integration-tests:
runs-on: ubuntu-24.04

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- name: Install Nix
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31

- name: Setup Cachix
uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17
with:
name: crossplane
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

- name: Cache Go modules and build cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-integration-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-integration-

- name: Run Integration Tests
run: nix run .#integration-test --option warn-dirty false

- name: Publish Integration Test Coverage
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
with:
flags: integration
file: coverage-integration.txt
token: ${{ secrets.CODECOV_TOKEN }}

# Build all artifacts
build-artifacts:
permissions:
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ Zone.Identifier
build/

# Nix build output symlinks
result
result-*
/result
/result-*

# Exclude the ko data dir
kodata/
Expand Down Expand Up @@ -45,3 +45,5 @@ AGENTS.md

# Ignore local dev environment variables
.env
/coverage-integration.txt
/coverage-merged.txt
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ run:
# runs our unit tests. Lint them anyway, so they don't rot.
build-tags:
- compilegate
# The diff integration tests need Docker and envtest, so they sit behind
# the integration tag and run in their own target, not the unit tests.
# Lint them anyway.
- integration

output:
formats:
Expand Down
82 changes: 82 additions & 0 deletions cmd/crossplane/common/exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2026 The Crossplane Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package common

import "errors"

// Exit statuses the simulate commands share.
const (
// ExitCodeSchemaValidation means only schema validation failed.
ExitCodeSchemaValidation = 2
// ExitCodeChanges means the simulation succeeded and found changes.
ExitCodeChanges = 3
// ExitCodeUsage is the status kong exits with for parse and Validate
// errors. Commands return it for invalid flags they can only detect in
// Run.
ExitCodeUsage = 80
)

// ExitError is an error that sets the command's exit status; it implements
// kong.ExitCoder. With a nil Err it reports a result that is not an error,
// such as changes found: main exits with Code without printing anything.
// Return it by value and unwrapped: main exits silently only when the
// returned error carries no message besides a silent ExitError, so one
// wrapped with a message or joined with another error, or a pointer, is
// printed as an error.
type ExitError struct {
// Err is the error to print, or nil to exit silently.
Err error

// Code is the status to exit with.
Code int
}

// Error returns Err's message, or an empty string when Err is nil.
func (e ExitError) Error() string {
if e.Err == nil {
return ""
}
return e.Err.Error()
}

// ExitCode implements kong.ExitCoder.
func (e ExitError) ExitCode() int {
return e.Code
}

// Unwrap returns Err.
func (e ExitError) Unwrap() error {
return e.Err
}

// Silent reports whether e exits without printing anything.
func (e ExitError) Silent() bool {
return e.Err == nil
}

// SilentExit returns the code of a silent ExitError value in err's chain
// when err has no message of its own, and false otherwise. kong's
// Context.Run joins the command's error with its AfterRun hooks' errors, so
// the check can't require err to be the ExitError itself; requiring an empty
// message still keeps a wrapping or joined error from being swallowed.
func SilentExit(err error) (int, bool) {
var e ExitError
if err == nil || err.Error() != "" || !errors.As(err, &e) || !e.Silent() {
return 0, false
}
return e.Code, true
}
53 changes: 53 additions & 0 deletions cmd/crossplane/common/exit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2026 The Crossplane Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package common

import (
"testing"

"github.com/google/go-cmp/cmp"

"github.com/crossplane/crossplane-runtime/v2/pkg/errors"
)

func TestSilentExit(t *testing.T) {
type want struct {
code int
silent bool
}
cases := map[string]struct {
reason string
err error
want want
}{
"Nil": {reason: "No error isn't a silent exit.", err: nil},
"Silent": {reason: "A silent ExitError value exits silently with its code.", err: ExitError{Code: ExitCodeChanges}, want: want{code: ExitCodeChanges, silent: true}},
"WithErr": {reason: "An ExitError with an error is printed.", err: ExitError{Code: 2, Err: errors.New("boom")}},
"Wrapped": {reason: "A wrapped silent ExitError is printed, so the wrapper's message isn't lost.", err: errors.Wrap(ExitError{Code: ExitCodeChanges}, "boom")},
"KongJoin": {reason: "kong's Run joins a silent ExitError with nil hook errors, which still exits silently.", err: errors.Join(ExitError{Code: ExitCodeChanges}, nil), want: want{code: ExitCodeChanges, silent: true}},
"Joined": {reason: "A silent ExitError joined with a real error is printed.", err: errors.Join(ExitError{Code: ExitCodeChanges}, errors.New("boom"))},
"Pointer": {reason: "A pointer isn't matched; ExitError must be returned by value.", err: &ExitError{Code: ExitCodeChanges}},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
code, silent := SilentExit(tc.err)
if diff := cmp.Diff(tc.want, want{code: code, silent: silent}, cmp.AllowUnexported(want{})); diff != "" {
t.Errorf("\n%s\nSilentExit(...): -want, +got:\n%s", tc.reason, diff)
}
})
}
}
67 changes: 55 additions & 12 deletions cmd/crossplane/common/load/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,15 @@ func NewLoader(input string) (Loader, error) {
}

func newLoader(input string) (Loader, error) {
return newSourceLoader(input, false)
}

// newSourceLoader returns the Loader for one input. With skipPipelineInputs,
// the loader doesn't return Composition pipeline step inputs as manifests of
// their own.
func newSourceLoader(input string, skipPipelineInputs bool) (Loader, error) {
if input == "-" {
return &StdinLoader{}, nil
return &StdinLoader{skipPipelineInputs: skipPipelineInputs}, nil
}

fi, err := os.Stat(input)
Expand All @@ -73,10 +80,10 @@ func newLoader(input string) (Loader, error) {
}

if fi.IsDir() {
return &FolderLoader{path: input}, nil
return &FolderLoader{path: input, skipPipelineInputs: skipPipelineInputs}, nil
}

return &FileLoader{path: input}, nil
return &FileLoader{path: input, skipPipelineInputs: skipPipelineInputs}, nil
}

// MultiLoader implements the Loader interface for reading from multiple other loaders.
Expand All @@ -101,7 +108,9 @@ func (m *MultiLoader) Load() ([]*unstructured.Unstructured, error) {
}

// StdinLoader implements the Loader interface for reading from stdin.
type StdinLoader struct{}
type StdinLoader struct {
skipPipelineInputs bool
}

// Load reads the contents from stdin.
func (s *StdinLoader) Load() ([]*unstructured.Unstructured, error) {
Expand All @@ -110,12 +119,13 @@ func (s *StdinLoader) Load() ([]*unstructured.Unstructured, error) {
return nil, errors.Wrap(err, "cannot load YAML stream from stdin")
}

return streamToUnstructured(stream)
return streamToUnstructured(stream, s.skipPipelineInputs)
}

// FileLoader implements the Loader interface for reading from a file and converting input to unstructured objects.
type FileLoader struct {
path string
path string
skipPipelineInputs bool
}

// Load reads the contents from a file.
Expand All @@ -125,12 +135,13 @@ func (f *FileLoader) Load() ([]*unstructured.Unstructured, error) {
return nil, errors.Wrap(err, "cannot read file")
}

return streamToUnstructured(stream)
return streamToUnstructured(stream, f.skipPipelineInputs)
}

// FolderLoader implements the Loader interface for reading from a folder.
type FolderLoader struct {
path string
path string
skipPipelineInputs bool
}

// Load reads the contents from all files in a folder.
Expand All @@ -157,7 +168,7 @@ func (f *FolderLoader) Load() ([]*unstructured.Unstructured, error) {
return nil, errors.Wrap(err, "cannot read folder")
}

return streamToUnstructured(stream)
return streamToUnstructured(stream, f.skipPipelineInputs)
}

func isYamlFile(info os.FileInfo) bool {
Expand Down Expand Up @@ -200,7 +211,10 @@ func YamlStream(r io.Reader) ([][]byte, error) {
return stream, nil
}

func streamToUnstructured(stream [][]byte) ([]*unstructured.Unstructured, error) {
// streamToUnstructured parses stream. Unless skipPipelineInputs is set, it also
// returns each Composition pipeline step input as a manifest of its own, so
// validate can check the inputs against their schemas.
func streamToUnstructured(stream [][]byte, skipPipelineInputs bool) ([]*unstructured.Unstructured, error) {
manifests := make([]*unstructured.Unstructured, 0, len(stream))

for _, y := range stream {
Expand All @@ -215,7 +229,7 @@ func streamToUnstructured(stream [][]byte) ([]*unstructured.Unstructured, error)
}

// extract pipeline input resources
if u.GetObjectKind().GroupVersionKind() == v1.CompositionGroupVersionKind {
if !skipPipelineInputs && u.GetObjectKind().GroupVersionKind() == v1.CompositionGroupVersionKind {
// Convert the unstructured resource to a Composition
var comp v1.Composition

Expand Down Expand Up @@ -259,6 +273,35 @@ type CompositeLoader struct {
// Sources can be files, directories, or "-" for stdin.
// If sources is empty, stdin is used by default.
func NewCompositeLoader(sources []string) (Loader, error) {
return newCompositeLoader(sources, NewLoader)
}

// ExactCompositeLoaderOption configures NewExactCompositeLoader.
type ExactCompositeLoaderOption func(*exactCompositeLoaderOptions)

type exactCompositeLoaderOptions struct {
skipPipelineInputs bool
}

// WithoutPipelineInputs stops the loader from returning each Composition
// pipeline step input as a manifest of its own. By default it returns them.
func WithoutPipelineInputs() ExactCompositeLoaderOption {
return func(o *exactCompositeLoaderOptions) { o.skipPipelineInputs = true }
}

// NewExactCompositeLoader is NewCompositeLoader, except each source is exactly
// one path or "-": commas are part of the path, not separators.
func NewExactCompositeLoader(sources []string, opts ...ExactCompositeLoaderOption) (Loader, error) {
o := &exactCompositeLoaderOptions{}
for _, fn := range opts {
fn(o)
}
return newCompositeLoader(sources, func(source string) (Loader, error) {
return newSourceLoader(source, o.skipPipelineInputs)
})
}

func newCompositeLoader(sources []string, newSource func(string) (Loader, error)) (Loader, error) {
if len(sources) == 0 {
// In unit tests, this will cause an error when Load() is called
// which is the expected behavior for NoSources test case
Expand All @@ -280,7 +323,7 @@ func NewCompositeLoader(sources []string) (Loader, error) {
stdinUsed = true
}

loader, err := NewLoader(source)
loader, err := newSource(source)
if err != nil {
return nil, errors.Wrapf(err, "cannot create loader for %q", source)
}
Expand Down
Loading
Loading