From 68fa5177e29e411886713230bec3a649c53a3c16 Mon Sep 17 00:00:00 2001 From: Ramiro Algozino Date: Thu, 2 Oct 2025 19:12:09 +0200 Subject: [PATCH 1/6] chore: add test for empty patches files Add test to validate that empty files don't produce an error when using the `path` option of the `patches` convenience. Add test to validate that using the deprecated patchesStrategicMerge still produces an error and no changes have been introduced in old features. --- api/krusty/multiplepatch_test.go | 106 +++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/api/krusty/multiplepatch_test.go b/api/krusty/multiplepatch_test.go index b2d24c5c7..5293ba31d 100644 --- a/api/krusty/multiplepatch_test.go +++ b/api/krusty/multiplepatch_test.go @@ -4,6 +4,7 @@ package krusty_test import ( + "strings" "testing" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" @@ -1766,3 +1767,108 @@ metadata: name: fluentd-sa-abc `) } + +// TestEmptyPatchFilesShouldBeIgnored verifies that empty patch files are ignored. +// Tests three cases: +// 1. Completely empty file +// 2. File with only comments +// 3. File with whitespace and comments +func TestEmptyPatchFilesShouldBeIgnored(t *testing.T) { + th := kusttest_test.MakeHarness(t) + + // Write a basic resource + th.WriteF("deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + template: + spec: + containers: + - name: nginx + image: nginx +`) + + // Create empty patch files of different types + th.WriteF("empty.yaml", ``) + th.WriteF("comments-only.yaml", ` +# This is a comment +# Another comment +`) + th.WriteF("whitespace.yaml", ` + +# Comments with whitespace + + # Indented comment + +`) + + // Reference empty patches in kustomization + th.WriteK(".", ` +resources: +- deployment.yaml +patches: +- path: empty.yaml +- path: comments-only.yaml +- path: whitespace.yaml +`) + + // Empty patches should be ignored, output should be unchanged + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + template: + spec: + containers: + - image: nginx + name: nginx +`) +} + +// TestEmptyPatchesStrategicMergeFails verifies that empty patch files are +// handled correctly with the deprecated patchesStrategicMerge field +func TestEmptyPatchesStrategicMergeFails(t *testing.T) { + th := kusttest_test.MakeHarness(t) + + // Create a basic resource + th.WriteF("resource.yaml", ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: dummy +data: + dummy: value +`) + + // Create an empty patch file + th.WriteF("empty-patch.yaml", ``) + + // Create a patch file with only comments + th.WriteF("comments-patch.yaml", ` +# This is just a comment +# Another comment +`) + + // Create kustomization using patchesStrategicMerge + th.WriteK(".", ` +resources: +- resource.yaml +patchesStrategicMerge: +- empty-patch.yaml +- comments-patch.yaml +`) + + // This fails with message + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected error for empty patchesStrategicMerge files but got none") + } + if !strings.Contains(err.Error(), "patch appears to be empty") { + t.Fatalf("unexpected error: %v", err) + } +} From 24ea1b951a8046a89a2704b76347f6fa4599122a Mon Sep 17 00:00:00 2001 From: Ramiro Algozino Date: Thu, 2 Oct 2025 19:19:27 +0200 Subject: [PATCH 2/6] fix(kustomize): call json6902 only when needed Call json6902 transformer only when there are patches to apply. Addresses #5956 --- api/internal/builtins/PatchTransformer.go | 5 ++++- plugin/builtin/patchtransformer/PatchTransformer.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/api/internal/builtins/PatchTransformer.go b/api/internal/builtins/PatchTransformer.go index 278d10093..442291d11 100644 --- a/api/internal/builtins/PatchTransformer.go +++ b/api/internal/builtins/PatchTransformer.go @@ -89,7 +89,10 @@ func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error { if p.smPatches != nil { return p.transformStrategicMerge(m) } - return p.transformJson6902(m) + if p.jsonPatches != nil { + return p.transformJson6902(m) + } + return nil } // transformStrategicMerge applies each loaded strategic merge patch diff --git a/plugin/builtin/patchtransformer/PatchTransformer.go b/plugin/builtin/patchtransformer/PatchTransformer.go index 77e5439b0..4a2be54bf 100644 --- a/plugin/builtin/patchtransformer/PatchTransformer.go +++ b/plugin/builtin/patchtransformer/PatchTransformer.go @@ -94,7 +94,10 @@ func (p *plugin) Transform(m resmap.ResMap) error { if p.smPatches != nil { return p.transformStrategicMerge(m) } - return p.transformJson6902(m) + if p.jsonPatches != nil { + return p.transformJson6902(m) + } + return nil } // transformStrategicMerge applies each loaded strategic merge patch From c79a356bb24fe367cfb3e5a1619d9943f285db25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 00:02:17 +0000 Subject: [PATCH 3/6] build(deps): bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/apidiff.yml | 2 +- .github/workflows/go.yml | 12 ++++++------ .github/workflows/release.yaml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/apidiff.yml b/.github/workflows/apidiff.yml index 21c60c353..f8d843222 100644 --- a/.github/workflows/apidiff.yml +++ b/.github/workflows/apidiff.yml @@ -13,7 +13,7 @@ jobs: if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) steps: - name: Clone the code - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup Go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 16edb3384..58b7cab50 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -17,7 +17,7 @@ jobs: outputs: doc: ${{ steps.filter.outputs.doc }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dorny/paths-filter@v3 id: filter with: @@ -32,7 +32,7 @@ jobs: runs-on: [ubuntu-latest] steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go 1.x @@ -52,7 +52,7 @@ jobs: runs-on: [ubuntu-latest] steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go 1.x @@ -72,7 +72,7 @@ jobs: runs-on: [ubuntu-latest] steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Go 1.x uses: actions/setup-go@v6 with: @@ -90,7 +90,7 @@ jobs: runs-on: [macos-latest] steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Go 1.x uses: actions/setup-go@v6 with: @@ -108,7 +108,7 @@ jobs: runs-on: [windows-latest] steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Go 1.x uses: actions/setup-go@v6 with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d2fb3d617..bf44d0581 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go 1.x From 77cf6d6b888f93585406ebe6b2e51f4f691b31a1 Mon Sep 17 00:00:00 2001 From: koba1t Date: Thu, 20 Nov 2025 01:40:27 +0900 Subject: [PATCH 4/6] CI: parallelizing tests --- .github/workflows/go.yml | 101 ++++++++++++++++++++++----------------- Makefile | 8 +++- hack/for-each-module.sh | 12 ++++- 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 58b7cab50..2b8c4471f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,14 +2,15 @@ name: Go on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] permissions: contents: read jobs: + ## TODO: conditional-changes checker is not working conditional-changes: runs-on: ubuntu-latest permissions: @@ -27,7 +28,7 @@ jobs: check-modules: name: check-synced-go-modules - needs: conditional-changes + # needs: conditional-changes # if: needs.conditional-changes.outputs.doc == 'false' runs-on: [ubuntu-latest] steps: @@ -39,6 +40,9 @@ jobs: uses: actions/setup-go@v6 with: go-version-file: go.work + cache: true + cache-dependency-path: | + **/go.sum id: go - name: sync go modules run: make workspace-sync @@ -47,7 +51,7 @@ jobs: lint: name: Lint - needs: conditional-changes + # needs: conditional-changes # if: needs.conditional-changes.outputs.doc == 'false' runs-on: [ubuntu-latest] steps: @@ -59,15 +63,19 @@ jobs: uses: actions/setup-go@v6 with: go-version-file: go.work + cache: true + cache-dependency-path: | + **/go.sum id: go - name: Lint run: make lint - name: Verify boilerplate run: make check-license - test-linux: + ## Test all modules without plugins and released modules + test-non-released-modules: name: Test Linux - needs: conditional-changes + # needs: conditional-changes # if: needs.conditional-changes.outputs.doc == 'false' runs-on: [ubuntu-latest] steps: @@ -77,17 +85,46 @@ jobs: uses: actions/setup-go@v6 with: go-version-file: go.work + cache: true + cache-dependency-path: | + **/go.sum id: go - - name: Test all modules - run: make test-unit-non-plugin + - name: Test all modules without plugins and released modules + run: make test-unit-non-plugin-and-non-released env: KUSTOMIZE_DOCKER_E2E: true - test-macos: - name: Test MacOS - needs: conditional-changes + test-modules: + name: Test ${{ matrix.os }} - ${{ matrix.module }} + # needs: conditional-changes # if: needs.conditional-changes.outputs.doc == 'false' - runs-on: [macos-latest] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + module: + - kyaml + - cmd/config + - api + - kustomize + include: + - module: kyaml + test-cmd: go test -race -v -cover ./... + - module: cmd/config + test-cmd: go test -v -cover ./... + - module: api + test-cmd: go test -v -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=2023-01-31T23:38:41Z -X sigs.k8s.io/kustomize/api/provenance.version=(test)" + - module: kustomize + test-cmd: go test -v -cover ./... + - os: ubuntu-latest + docker-e2e: true + - os: macos-latest + docker-e2e: false + - os: windows-latest + docker-e2e: false + env: + KUSTOMIZE_DOCKER_E2E: ${{ matrix.docker-e2e }} steps: - name: Check out code into the Go module directory uses: actions/checkout@v6 @@ -95,36 +132,12 @@ jobs: uses: actions/setup-go@v6 with: go-version-file: go.work + cache: true + cache-dependency-path: | + **/go.sum id: go - - name: Test all modules - run: make test-unit-non-plugin - env: - KUSTOMIZE_DOCKER_E2E: false # docker not installed on mac - - test-windows: - name: Test Windows - needs: conditional-changes - # if: needs.conditional-changes.outputs.doc == 'false' - runs-on: [windows-latest] - steps: - - name: Check out code into the Go module directory - uses: actions/checkout@v6 - - name: Set up Go 1.x - uses: actions/setup-go@v6 - with: - go-version-file: go.work - id: go - - name: Test kyaml - run: go test -cover ./... - working-directory: ./kyaml - - name: Test cmd/config - run: go test -cover ./... - working-directory: ./cmd/config - env: - KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet - - # TODO (#4001): replace specific modules above with this once Windows tests are passing. - #- name: Test all modules - # run: make test-unit-non-plugin - # env: - # KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet + - name: Test ${{ matrix.module }} + run: ${{ matrix.test-cmd }} + # TODO (#4001): replace specific modules above with this once Windows tests are passing. + if: ${{ !(matrix.os == 'windows-latest' && (matrix.module == 'api' || matrix.module == 'kustomize')) }} + working-directory: ./${{ matrix.module }} diff --git a/Makefile b/Makefile index 6030267ab..2b9dfd3c1 100644 --- a/Makefile +++ b/Makefile @@ -136,11 +136,15 @@ test-unit-all: \ test-unit-non-plugin \ test-unit-kustomize-plugins -# This target is used by our Github Actions CI to run unit tests for all non-plugin modules in multiple GOOS environments. .PHONY: test-unit-non-plugin test-unit-non-plugin: ./hack/for-each-module.sh "make test" "./plugin/*" 20 +# This target is used by our Github Actions CI to run unit tests for all non-plugin and non-released modules in multiple GOOS environments. +.PHONY: test-unit-non-plugin-and-non-released +test-unit-non-plugin-and-non-released: + ./hack/for-each-module.sh "make test" "./plugin/*|./kyaml/go.mod|./cmd/config/go.mod|./api/go.mod|./kustomize/go.mod" 16 + .PHONY: build-non-plugin-all build-non-plugin-all: ./hack/for-each-module.sh "make build" "./plugin/*" 20 @@ -183,7 +187,7 @@ test-examples-kustomize-against-latest-release: $(MYGOBIN)/mdrip workspace-sync: go work sync ./hack/doGoMod.sh tidy - + # --- Cleanup targets --- .PHONY: clean clean: clean-kustomize-external-go-plugin uninstall-tools diff --git a/hack/for-each-module.sh b/hack/for-each-module.sh index 5981a68e2..3f15289ff 100755 --- a/hack/for-each-module.sh +++ b/hack/for-each-module.sh @@ -22,8 +22,18 @@ seen=() KUSTOMIZE_ROOT=$(pwd) export KUSTOMIZE_ROOT +# Build find command with multiple -not -path options +find_cmd="find . -name go.mod -not -path \"./site/*\"" +if [[ -n "$skip_pattern" ]]; then + # Split skip_pattern by | and add -not -path for each + IFS='|' read -ra PATTERNS <<< "$skip_pattern" + for pattern in "${PATTERNS[@]}"; do + find_cmd+=" -not -path \"$pattern\"" + done +fi + # verify all modules pass validation -for i in $(find . -name go.mod -not -path "./site/*" -not -path "$skip_pattern"); do +for i in $(eval "$find_cmd"); do pushd . cd $(dirname "$i"); From e6621df4d518834af8d668ff2f6756fac812163a Mon Sep 17 00:00:00 2001 From: koba1t Date: Fri, 21 Nov 2025 00:17:21 +0900 Subject: [PATCH 5/6] add aggregation job for all matrix released module tests to define a branch protection rule --- .github/workflows/go.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2b8c4471f..26a78c1a2 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -140,4 +140,19 @@ jobs: run: ${{ matrix.test-cmd }} # TODO (#4001): replace specific modules above with this once Windows tests are passing. if: ${{ !(matrix.os == 'windows-latest' && (matrix.module == 'api' || matrix.module == 'kustomize')) }} - working-directory: ./${{ matrix.module }} + working-directory: ./${{ matrix.module }} + + # Aggregation matrix tests from test-modules for branch protection rules + test-modules-summary: + name: Test Summary + runs-on: ubuntu-latest + needs: test-modules + if: always() + steps: + - name: Check test results + run: | + if [[ "${{ needs.test-modules.result }}" != "success" ]]; then + echo "Some tests failed or were cancelled" + exit 1 + fi + echo "All tests passed successfully" From 6d7267f345d34ea6ed2bcc2281f4678d61687f43 Mon Sep 17 00:00:00 2001 From: Tsuzu <8574909+tsuzu@users.noreply.github.com> Date: Mon, 22 Dec 2025 00:11:48 +0900 Subject: [PATCH 6/6] Fix a failing test --- api/internal/loader/fileloader_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/internal/loader/fileloader_test.go b/api/internal/loader/fileloader_test.go index d84ecbfb1..17e8fbfe5 100644 --- a/api/internal/loader/fileloader_test.go +++ b/api/internal/loader/fileloader_test.go @@ -686,5 +686,5 @@ func TestLoaderHTTPMalformedURL(t *testing.T) { RestrictionRootOnly, MakeFakeFs([]testData{}), filesys.Separator) _, err := l1.Load(malformedURL) require.Error(err) - require.Equal("HTTP Error: status code 500 (Internal Server Error)", err.Error()) + require.Equal("HTTP Error: status code 400 (Bad Request)", err.Error()) }