From 5a49084337f51406b31b6ec97055dc42ada494e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:01:28 +0000 Subject: [PATCH 01/11] fix(deps): bump github.com/fluxcd/pkg/apis/meta from 1.31.0 to 1.32.0 Bumps [github.com/fluxcd/pkg/apis/meta](https://github.com/fluxcd/pkg) from 1.31.0 to 1.32.0. - [Commits](https://github.com/fluxcd/pkg/compare/apis/meta/v1.31.0...apis/meta/v1.32.0) --- updated-dependencies: - dependency-name: github.com/fluxcd/pkg/apis/meta dependency-version: 1.32.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 97b98bfe95..248ba4bfc9 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/fatih/color v1.19.0 github.com/fluxcd/helm-controller/api v1.6.5 github.com/fluxcd/kustomize-controller/api v1.9.5 - github.com/fluxcd/pkg/apis/meta v1.31.0 + github.com/fluxcd/pkg/apis/meta v1.32.0 github.com/fluxcd/source-controller/api v1.9.5 github.com/getsops/sops/v3 v3.13.3 github.com/gkampitakis/go-snaps v0.5.23 @@ -39,7 +39,7 @@ require ( helm.sh/helm/v4 v4.2.4 k8s.io/api v0.36.5 k8s.io/apiextensions-apiserver v0.36.5 - k8s.io/apimachinery v0.36.5 + k8s.io/apimachinery v0.37.0 k8s.io/cli-runtime v0.36.5 k8s.io/client-go v0.36.5 k8s.io/kubectl v0.36.5 @@ -1018,7 +1018,7 @@ require ( k8s.io/kubelet v0.36.2 // indirect k8s.io/kubernetes v1.36.0 // indirect k8s.io/metrics v0.36.5 // indirect - k8s.io/streaming v0.36.5 // indirect + k8s.io/streaming v0.37.0 // indirect k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect modernc.org/libc v1.73.4 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index 9710af4ed2..d0f45cc264 100644 --- a/go.sum +++ b/go.sum @@ -940,8 +940,8 @@ github.com/fluxcd/pkg/apis/acl v0.10.0 h1:KPfAmELNvtvaz8wixnm/MYXqa+MJf7ntVVMUU9 github.com/fluxcd/pkg/apis/acl v0.10.0/go.mod h1:a87i2A7AlFO5N2J8CxtzaUCCDmuLLWOHwkKu3eJF5fY= github.com/fluxcd/pkg/apis/kustomize v1.19.2 h1:/E8Nmn1XdAGUIqxI5vK60WemyBX3pIpfmP3mr/E3p9c= github.com/fluxcd/pkg/apis/kustomize v1.19.2/go.mod h1:HryXaJ1GpvagUOmh5uMkVtXVlfXWdKjc3/5IlLsjyzI= -github.com/fluxcd/pkg/apis/meta v1.31.0 h1:5niQvTirK0wTE0TfRjnUSdmu6GTSbAFzrdnovtZ9rJ8= -github.com/fluxcd/pkg/apis/meta v1.31.0/go.mod h1:Gx+YRq26a+mTbCjotSXC7/6kSSyo0zXQ8JnsEXf2vVk= +github.com/fluxcd/pkg/apis/meta v1.32.0 h1:jWuNuIziUM8NOrhZB7vovdFQ4wBYRNJoAn+XUoYlj1I= +github.com/fluxcd/pkg/apis/meta v1.32.0/go.mod h1:bZmU0RbSwFzsCg9sgjhbSWxgSbUy+3Oh/s7qUCZjwPk= github.com/fluxcd/pkg/oci v0.45.0 h1:lPtxqqzDOF3TAYx+Y91nULFiLzgmE8XDHt/uZwBqx44= github.com/fluxcd/pkg/oci v0.45.0/go.mod h1:i4kFlYDC84u6vtIE54eco/ArcLPXqTv+/Gt7ncSKmoE= github.com/fluxcd/pkg/ssa v0.77.0 h1:HaDg7XDQGfVTFY379GrnnhrON1CQLjrLmyGbbTw5Ohc= @@ -2031,8 +2031,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= -github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= +github.com/onsi/gomega v1.43.0 h1:VlG/1FxqNxhSO+lq/OHBNaaqwiBK/mO8JbVkX9Y+FeU= +github.com/onsi/gomega v1.43.0/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/open-policy-agent/gatekeeper/v3 v3.17.0 h1:8jiY9rgWRofEJeJLm4gyme97fwvqTbv9+QoerMGXCuU= github.com/open-policy-agent/gatekeeper/v3 v3.17.0/go.mod h1:LIk/6du1AV47s+aU1ovKNKzOCHIsEmEImUN/72DL3zw= github.com/open-policy-agent/opa v1.17.1 h1:wO0MOux/VCqY41aVAD6Toe1p3A7O7DlRZ1RHmYSpoS8= @@ -3506,8 +3506,8 @@ k8s.io/api v0.36.5 h1:vtL/ByHmw7suLt+SGVMPZnuj8QdfZ0kN1vvMvHRCY9c= k8s.io/api v0.36.5/go.mod h1:erfc3/3d6z30KO2Kf+BmOewE6CajqZTz1Hcd+FQqg60= k8s.io/apiextensions-apiserver v0.36.5 h1:pZcOcB7ur1ou+VeQSGChj3cvhrL2nA3gERMUaCHVJNQ= k8s.io/apiextensions-apiserver v0.36.5/go.mod h1:l1uFNtfzcEBqgrYEiOIYwu+6R1RCkuZIiEvfPE6RpNs= -k8s.io/apimachinery v0.36.5 h1:X5Xbg6G4om0ma6LOcGbmUmo+B5Nq7mmIpAWhk+tgCh8= -k8s.io/apimachinery v0.36.5/go.mod h1:oPTSicSaDHDdoGwOs5DFPo6YS2wdTnzFHIYMEglfJ7I= +k8s.io/apimachinery v0.37.0 h1:Np2AbDtf8x6RDHiD8T9LbKJ9gaegeVNa8yNm5FuGKm0= +k8s.io/apimachinery v0.37.0/go.mod h1:RN3nhprFSCxOi5Selxd7oMTXOe/c+ZbcE7Im+TS2zkE= k8s.io/apiserver v0.36.5 h1:KX599gn2a5fQXeoCrDNdpND+AJ9ymq3u4XXvAslwnlo= k8s.io/apiserver v0.36.5/go.mod h1:wxPt+sgzaPkk+WOgsrwsGB5eyRFXlRuPd2EoZSvh5k8= k8s.io/cli-runtime v0.36.5 h1:TwL178KVy8bRTe6h2nfvSwkcn/B2nDZpOqYqE8e+Eg0= @@ -3540,8 +3540,8 @@ k8s.io/kubernetes v1.36.0 h1:JKaAkgSzI4+ZvNWrNJg56jAkUaOiqgBaJstmi6ycyoU= k8s.io/kubernetes v1.36.0/go.mod h1:MLdeJ3qw2CWH9BFml5GvptxQVQckz54fJOZ/WuixpFE= k8s.io/metrics v0.36.5 h1:UE0VhdGrgpLvK6gmyyr5fX0o2zW/Z5OL7CPBLnoJr88= k8s.io/metrics v0.36.5/go.mod h1:eCjFrV/1gjxIvD8XVyKAIE8erzXZZPapOYAzUqWft7g= -k8s.io/streaming v0.36.5 h1:gcY9VwgT4J/idqwS9ZrzJtH8ouHvZuFEyDWofcaL+F4= -k8s.io/streaming v0.36.5/go.mod h1:tJ6S2bZa2HxIBauguBbCWSCYyd93Grfz1+z3tcOvlDE= +k8s.io/streaming v0.37.0 h1:iPBUZLZiKt5bV+lxJurASMOV07VuBhNpiwJt2//AWrM= +k8s.io/streaming v0.37.0/go.mod h1:APlJR26ZWRcVy5bIEj0QRrKUXROtBHPcxl2NT7EAzPU= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= modernc.org/cc/v4 v4.28.4 h1:Hd/4Es+MBj+/7hSdZaisNyu6bv3V0Dp2MdllyfqaH+c= From af756bfd79141feed0386f927b48c35a82097259 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:04:33 +0000 Subject: [PATCH 02/11] fix(deps): bump github.com/hetznercloud/hcloud-go/v2 (#7627) Bumps [github.com/hetznercloud/hcloud-go/v2](https://github.com/hetznercloud/hcloud-go) from 2.47.0 to 2.49.0. - [Release notes](https://github.com/hetznercloud/hcloud-go/releases) - [Changelog](https://github.com/hetznercloud/hcloud-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/hetznercloud/hcloud-go/compare/v2.47.0...v2.49.0) --- updated-dependencies: - dependency-name: github.com/hetznercloud/hcloud-go/v2 dependency-version: 2.49.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 97b98bfe95..80922fab3e 100644 --- a/go.mod +++ b/go.mod @@ -84,7 +84,7 @@ require ( github.com/googleapis/gax-go/v2 v2.26.2 github.com/gopacket/gopacket v1.7.3 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 - github.com/hetznercloud/hcloud-go/v2 v2.47.0 + github.com/hetznercloud/hcloud-go/v2 v2.49.0 github.com/invopop/jsonschema v0.14.0 github.com/kubescape/kubescape/v3 v3.0.48 github.com/kubescape/opa-utils v0.0.314 diff --git a/go.sum b/go.sum index 9710af4ed2..b877745530 100644 --- a/go.sum +++ b/go.sum @@ -1480,8 +1480,8 @@ github.com/hashicorp/vault/api v1.23.0 h1:gXgluBsSECfRWTSW9niY2jwg2e9mMJc4WoHNv4 github.com/hashicorp/vault/api v1.23.0/go.mod h1:zransKiB9ftp+kgY8ydjnvCU7Wk8i9L0DYWpXeMj9ko= github.com/henvic/httpretty v0.2.0 h1:U4pKgF9SV4uRrE/7PF85TVnCJxXA91zKFpYU0iFwFOw= github.com/henvic/httpretty v0.2.0/go.mod h1:4LSlqxtJoYd+gt1lsqh9omUUziIVwoVsxdW15hZHTcI= -github.com/hetznercloud/hcloud-go/v2 v2.47.0 h1:SI7C4cvdYReb2aHUEQ8KBMOqxNnmd4hOZti1SbPq3Qk= -github.com/hetznercloud/hcloud-go/v2 v2.47.0/go.mod h1:pdG7fFGlYsCAaJ9r0QOIF0O6wQcpbJxT2VT8aP6XlIc= +github.com/hetznercloud/hcloud-go/v2 v2.49.0 h1:QXONxfgXIF99PFJknkVw+LrQQB4PB5IbEjEDh4Hfmig= +github.com/hetznercloud/hcloud-go/v2 v2.49.0/go.mod h1:J9QH6j8pRH0K3+HlqgOlQ8abXagWTD/GpTkfra2et+g= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= From c55fbc8706d7afc8fa8769c468d3136bc270b494 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:36:10 +0000 Subject: [PATCH 03/11] fix(deps): bump devantler-tech/ksail/.github/actions/free-disk-space (#7632) Bumps [devantler-tech/ksail/.github/actions/free-disk-space](https://github.com/devantler-tech/ksail) from 7.202.4 to 7.202.15. - [Release notes](https://github.com/devantler-tech/ksail/releases) - [Commits](https://github.com/devantler-tech/ksail/compare/32f92acde3dcf290d21d50639a805f0cf42b7b30...3115397431e2bdf31d911930908a9002a93a4340) --- updated-dependencies: - dependency-name: devantler-tech/ksail/.github/actions/free-disk-space dependency-version: 7.202.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 94ebe5d1e3..6f4f07b97e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2205,7 +2205,7 @@ jobs: with: persist-credentials: false - name: ๐Ÿงน Free runner disk - uses: devantler-tech/ksail/.github/actions/free-disk-space@32f92acde3dcf290d21d50639a805f0cf42b7b30 + uses: devantler-tech/ksail/.github/actions/free-disk-space@3115397431e2bdf31d911930908a9002a93a4340 - name: โš™๏ธ Setup Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: From 0ffe48cf93758ef98a5f424fd771c3dc5676da37 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Thu, 8 Oct 2026 22:33:31 +0200 Subject: [PATCH 04/11] test(ci): accept any full commit pin for the binary restore action (#7634) The workflow contract compared the restore action's reference with one literal revision. The dependency bot moves that revision with every release, so each of its updates failed this test (#7630). The contract now requires what it is there for: this repository's action at a full commit, never a branch or a tag. Co-authored-by: Claude Opus 5.5 --- internal/ciharness/binary_artifact_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/ciharness/binary_artifact_test.go b/internal/ciharness/binary_artifact_test.go index 19b53b989f..103c1b8f5d 100644 --- a/internal/ciharness/binary_artifact_test.go +++ b/internal/ciharness/binary_artifact_test.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strings" "testing" @@ -13,6 +14,13 @@ import ( "github.com/stretchr/testify/require" ) +// The restore action is loaded from this repository at a full commit, never a branch or a tag. +// Which commit is not pinned here: the dependency bot moves it with every release, and a +// literal revision in this test failed each of those updates. +var restoreBinaryActionPin = regexp.MustCompile( + `^devantler-tech/ksail/\.github/actions/restore-ksail-binary@[0-9a-f]{40}$`, +) + func TestKSailBinaryArtifactProducerPublication(t *testing.T) { t.Parallel() workflow := readCIWorkflow(t, ".github/workflows/ci.yaml") @@ -55,9 +63,7 @@ func TestKSailBinaryArtifactDockerConsumer(t *testing.T) { consumer := workflow.Jobs["system-test-docker"] assert.Contains(t, consumer.Needs, "build-artifact") restore := findHarnessStep(t, consumer.Steps, "๐Ÿ“ฅ Restore KSail binary") - assert.Equal(t, - "devantler-tech/ksail/.github/actions/restore-ksail-binary@"+ - "f9172ab810fdcb94b47351d04d70708f6878e10a", restore.Uses) + assert.Regexp(t, restoreBinaryActionPin, restore.Uses) assert.Equal( t, "${{ needs.build-artifact.outputs.artifact-id }}", From ba832a72135b5e49131760929bc654ba0536f896 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:35:20 +0000 Subject: [PATCH 05/11] fix(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp (#7626) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp](https://github.com/open-telemetry/opentelemetry-go) from 0.21.0 to 0.22.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/log/v0.21.0...log/v0.22.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp dependency-version: 0.22.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 80922fab3e..aa4556d3ce 100644 --- a/go.mod +++ b/go.mod @@ -956,7 +956,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0 // indirect go.opentelemetry.io/otel v1.46.0 - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect diff --git a/go.sum b/go.sum index b877745530..5c4e0d2343 100644 --- a/go.sum +++ b/go.sum @@ -2736,8 +2736,8 @@ go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0 h1:deI9UQMoGFgrg5iLPgzueqFPHevDl+28YKfSpPTI6rY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0/go.mod h1:PFx9NgpNUKXdf7J4Q3agRxMs3Y07QhTCVipKmLsMKnU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 h1:fvNHGyo3CdRv/DQveXqhqBxnKTDyRaC5sMSQxilX/A0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0/go.mod h1:zyGrjRKL2B/6+Jc/m4/otPoZqV2MY9ZjC/aBraRO7zc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 h1:lYk7RmxdLK865qLwibroNGldHa1U7SWKYYvNjlK7PIo= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0/go.mod h1:6GvlND0H0xdUJanOtIAn0xfwLkauh1tmsYEEVSMDdqY= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 h1:SUplec5dp06reu1zaXmOXdvqH398taqrDXqUl99jxSc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0/go.mod h1:ho2g4N+ane+swq5I/VBkKWnRDY4kUINH3FuqyZqX/Ug= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 h1:RuynHbfU8JUEw7DyONgkVYg2SVtsoF28y0LGIr69jgA= @@ -2766,8 +2766,8 @@ go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eT go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= go.opentelemetry.io/otel/sdk/log v0.22.0 h1:PRL+s6P63XT4E/bheEflopPUpVxuvANqZwtt89yhoGk= go.opentelemetry.io/otel/sdk/log v0.22.0/go.mod h1:JNp0sBELrjCTcu5W3GzABVypeU6vDJjBS+X0JISuz+g= -go.opentelemetry.io/otel/sdk/log/logtest v0.21.0 h1:X+JBBgKlswCGYsmgL0CnoUUtlE//VB345c84jYAYkdQ= -go.opentelemetry.io/otel/sdk/log/logtest v0.21.0/go.mod h1:HD1575K8e6sIFBBDd5tZB3t9DlMytWXq9FuR+Y4rfjE= +go.opentelemetry.io/otel/sdk/log/logtest v0.22.0 h1:infPnfNrhCNgOUZRs3gWUg8vhoBUHihq02gwK05gzlg= +go.opentelemetry.io/otel/sdk/log/logtest v0.22.0/go.mod h1:gkQZA3z15Bv3KU9vigBTi8dFechSozRP7v94X4VZv+s= go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= From 94650d6d142a32f6d3f2b112b4b9093c9c3699a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 23:31:53 +0200 Subject: [PATCH 06/11] fix(deps): bump devantler-tech/.github/.github/workflows/validate-go-project.yaml from 7.2.11 to 7.8.5 (#7631) * fix(deps): bump devantler-tech/.github/.github/workflows/validate-go-project.yaml Bumps [devantler-tech/.github/.github/workflows/validate-go-project.yaml](https://github.com/devantler-tech/.github) from 7.2.11 to 7.8.5. - [Release notes](https://github.com/devantler-tech/.github/releases) - [Commits](https://github.com/devantler-tech/.github/compare/ef34177c48310d4e8d6605233cb7218b926dcc55...fca583ac795d56928c5b542944b6e0963ea1e270) --- updated-dependencies: - dependency-name: devantler-tech/.github/.github/workflows/validate-go-project.yaml dependency-version: 7.8.5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * test(ci): admit the audited Go validation workflow revision --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikolai Emil Damm --- .github/workflows/ci.yaml | 2 +- internal/ciharness/go_maintenance_caller_test.go | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6f4f07b97e..5db7efbd3b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -630,7 +630,7 @@ jobs: || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && needs.changes.outputs.govuln-allowlist == 'true') - uses: devantler-tech/.github/.github/workflows/validate-go-project.yaml@ef34177c48310d4e8d6605233cb7218b926dcc55 # v7.2.11 + uses: devantler-tech/.github/.github/workflows/validate-go-project.yaml@fca583ac795d56928c5b542944b6e0963ea1e270 # v7.8.5 # The canonical defaults make a default-branch run answer "did the check run" rather than # "did the diff touch a Go file". On main the branch's green IS the protection # signal, so a path filter that skips the suite or the scan leaves main green diff --git a/internal/ciharness/go_maintenance_caller_test.go b/internal/ciharness/go_maintenance_caller_test.go index 0f2b6f4266..6e44d022cb 100644 --- a/internal/ciharness/go_maintenance_caller_test.go +++ b/internal/ciharness/go_maintenance_caller_test.go @@ -82,13 +82,14 @@ func validateGoMaintenanceIdentity(workflow ciWorkflow, caller todosWorkflow) er job, found := workflow.Jobs["ci-go"] // Reviewed catalogue revisions preserve the read-only maintenance boundary. - // One adds test-mode caches and the latest only moves the signed-fix pins; the caller still - // disables signed-fix jobs. + // The latest adds opt-in disk measurements and capacity-based cleanup. This + // caller keeps their defaults and still explicitly disables signed-fix jobs. // Keep explicit reviewed identities: a syntactically valid SHA alone is insufficient. reviewed := job.Uses == prefix+"0600006235510307a04efebcac1ac1f363f5f862" || job.Uses == prefix+"498fb4b11f129928d3af9a90e9c5a46f1c4dbd77" || job.Uses == prefix+"2fa404276b0ce5c0527683b080e39045045c4e42" || - job.Uses == prefix+"ef34177c48310d4e8d6605233cb7218b926dcc55" + job.Uses == prefix+"ef34177c48310d4e8d6605233cb7218b926dcc55" || + job.Uses == prefix+"fca583ac795d56928c5b542944b6e0963ea1e270" if !found || !reviewed || len(job.Steps) != 0 || caller.Jobs["ci-go"].RunsOn != "" || !reflect.DeepEqual(job.Needs, []string{"changes"}) { return errGoMaintenanceIdentity @@ -130,6 +131,7 @@ func TestGoMaintenanceCallerAcceptsReviewedReleaseRevisions(t *testing.T) { "498fb4b11f129928d3af9a90e9c5a46f1c4dbd77", "2fa404276b0ce5c0527683b080e39045045c4e42", "ef34177c48310d4e8d6605233cb7218b926dcc55", + "fca583ac795d56928c5b542944b6e0963ea1e270", } { t.Run(revision, func(t *testing.T) { t.Parallel() From 2b64ca9f6489ab09b6b4329dc7ab966ed9f1a400 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:13:08 +0200 Subject: [PATCH 07/11] fix(deps): bump devantler-tech/ksail/.github/actions/restore-ksail-binary from 7.202.3 to 7.202.15 (#7630) * fix(deps): bump devantler-tech/ksail/.github/actions/restore-ksail-binary Bumps [devantler-tech/ksail/.github/actions/restore-ksail-binary](https://github.com/devantler-tech/ksail) from 7.202.3 to 7.202.15. - [Release notes](https://github.com/devantler-tech/ksail/releases) - [Commits](https://github.com/devantler-tech/ksail/compare/f9172ab810fdcb94b47351d04d70708f6878e10a...3115397431e2bdf31d911930908a9002a93a4340) --- updated-dependencies: - dependency-name: devantler-tech/ksail/.github/actions/restore-ksail-binary dependency-version: 7.202.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * test(ci): align the audited binary restore action pin --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikolai Emil Damm --- .github/workflows/ci.yaml | 2 +- internal/ciharness/binary_artifact_test.go | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5db7efbd3b..7f9d053197 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1838,7 +1838,7 @@ jobs: - name: ๐Ÿ“ฅ Restore KSail binary # Pin the identical published action until actionlint supports $/ references. - uses: devantler-tech/ksail/.github/actions/restore-ksail-binary@f9172ab810fdcb94b47351d04d70708f6878e10a + uses: devantler-tech/ksail/.github/actions/restore-ksail-binary@3115397431e2bdf31d911930908a9002a93a4340 with: artifact-id: ${{ needs.build-artifact.outputs.artifact-id }} binary-sha256: ${{ needs.build-artifact.outputs.binary-sha256 }} diff --git a/internal/ciharness/binary_artifact_test.go b/internal/ciharness/binary_artifact_test.go index 103c1b8f5d..ed6a199443 100644 --- a/internal/ciharness/binary_artifact_test.go +++ b/internal/ciharness/binary_artifact_test.go @@ -6,7 +6,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strings" "testing" @@ -14,13 +13,6 @@ import ( "github.com/stretchr/testify/require" ) -// The restore action is loaded from this repository at a full commit, never a branch or a tag. -// Which commit is not pinned here: the dependency bot moves it with every release, and a -// literal revision in this test failed each of those updates. -var restoreBinaryActionPin = regexp.MustCompile( - `^devantler-tech/ksail/\.github/actions/restore-ksail-binary@[0-9a-f]{40}$`, -) - func TestKSailBinaryArtifactProducerPublication(t *testing.T) { t.Parallel() workflow := readCIWorkflow(t, ".github/workflows/ci.yaml") @@ -63,7 +55,9 @@ func TestKSailBinaryArtifactDockerConsumer(t *testing.T) { consumer := workflow.Jobs["system-test-docker"] assert.Contains(t, consumer.Needs, "build-artifact") restore := findHarnessStep(t, consumer.Steps, "๐Ÿ“ฅ Restore KSail binary") - assert.Regexp(t, restoreBinaryActionPin, restore.Uses) + assert.Equal(t, + "devantler-tech/ksail/.github/actions/restore-ksail-binary@"+ + "3115397431e2bdf31d911930908a9002a93a4340", restore.Uses) assert.Equal( t, "${{ needs.build-artifact.outputs.artifact-id }}", From 343e0864f1f34cbe8193b825fddf0f469e06b1ce Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Fri, 9 Oct 2026 00:35:09 +0200 Subject: [PATCH 08/11] fix(talos): strip storage registration labels from autoscaler workers (#7636) --- .../talos/autoscaler_node_config_test.go | 14 ++- .../talos/autoscaler_secret_payload_test.go | 14 ++- .../cluster/talos/autoscaler_worker_config.go | 59 ++++++++++- .../talos/autoscaler_worker_config_test.go | 97 +++++++++++++++++++ 4 files changed, 176 insertions(+), 8 deletions(-) diff --git a/pkg/svc/provisioner/cluster/talos/autoscaler_node_config_test.go b/pkg/svc/provisioner/cluster/talos/autoscaler_node_config_test.go index 6709ecac69..d6b8d099f6 100644 --- a/pkg/svc/provisioner/cluster/talos/autoscaler_node_config_test.go +++ b/pkg/svc/provisioner/cluster/talos/autoscaler_node_config_test.go @@ -31,9 +31,10 @@ func autoscalerNodeFixture( t.Helper() configs, err := talosconfigmanager.NewDefaultConfigsWithPatches([]talosconfigmanager.Patch{{ - Path: "workers/longhorn.yaml", - Scope: talosconfigmanager.PatchScopeWorker, - Content: []byte("machine:\n nodeLabels:\n " + longhornDefaultDisk + ": \"true\"\n"), + Path: "workers/longhorn.yaml", + Scope: talosconfigmanager.PatchScopeWorker, + Content: []byte("machine:\n nodeLabels:\n " + longhornDefaultDisk + ": \"true\"\n" + + " kubelet:\n extraArgs:\n node-labels: \"" + longhornDefaultDisk + "=true,workload=compute\"\n"), }}) require.NoError(t, err) @@ -97,6 +98,11 @@ func TestAutoscalerNodeDesiredConfigKeepsAutoscalerShape(t *testing.T) { assert.Equal(t, "batch", machine.MachineNodeLabels["workload"]) assert.Equal(t, "batch:NoSchedule", machine.MachineNodeTaints["dedicated"]) assert.NotContains(t, machine.MachineNodeLabels, longhornDefaultDisk) + assert.Equal( + t, + []string{"workload=compute"}, + machine.MachineKubelet.KubeletExtraArgs.ToMap()["node-labels"], + ) diff, err := talosprovisioner.MachineConfigDiffForTest(running, desired) require.NoError(t, err) @@ -123,6 +129,8 @@ func TestStaticWorkerDesiredConfigKeepsStaticShape(t *testing.T) { labels := desired.RawV1Alpha1().MachineConfig.MachineNodeLabels assert.Equal(t, "true", labels[longhornDefaultDisk]) assert.NotContains(t, labels, talosprovisioner.LabelAutoscaled) + assert.Equal(t, []string{longhornDefaultDisk + "=true,workload=compute"}, + desired.RawV1Alpha1().MachineConfig.MachineKubelet.KubeletExtraArgs.ToMap()["node-labels"]) } // TestAutoscalerNodeOfUnconfiguredPoolIsRefused pins that a server whose pool is not in diff --git a/pkg/svc/provisioner/cluster/talos/autoscaler_secret_payload_test.go b/pkg/svc/provisioner/cluster/talos/autoscaler_secret_payload_test.go index 03e7064ece..6b11515ad7 100644 --- a/pkg/svc/provisioner/cluster/talos/autoscaler_secret_payload_test.go +++ b/pkg/svc/provisioner/cluster/talos/autoscaler_secret_payload_test.go @@ -29,7 +29,8 @@ func workerConfigsWithLonghornLabel(t *testing.T) *talosconfigmanager.Configs { require.NoError(t, os.MkdirAll(workersDir, 0o750)) require.NoError(t, os.WriteFile( filepath.Join(workersDir, "longhorn.yaml"), - []byte("machine:\n nodeLabels:\n "+longhornDefaultDiskLabel+": \"true\"\n"), + []byte("machine:\n nodeLabels:\n "+longhornDefaultDiskLabel+": \"true\"\n"+ + " kubelet:\n extraArgs:\n node-labels: \""+longhornDefaultDiskLabel+"=true,workload=compute\"\n"), 0o600, )) @@ -78,6 +79,16 @@ func bootedMachineLabels(t *testing.T, cloudInit string) map[string]string { return booted.RawV1Alpha1().MachineConfig.MachineNodeLabels } +func assertBootedKubeletLabels(t *testing.T, cloudInit string) { + t.Helper() + + booted, err := configloader.NewFromBytes(decodePoolCloudInit(t, cloudInit)) + require.NoError(t, err) + assert.Equal(t, []string{"workload=compute"}, + booted.RawV1Alpha1().MachineConfig.MachineKubelet.KubeletExtraArgs.ToMap()["node-labels"], + "an autoscaler node must register without the Longhorn default-disk label") +} + // The config a new autoscaler node boots from is the pool's cloudInit inside the Secret, not the // intermediate generated worker config. This builds that Secret the way cluster create and update // do, decodes each pool's cloudInit the way the autoscaler and Talos do, and checks the booted @@ -123,6 +134,7 @@ func TestAutoscalerSecretPayloadBootsEveryPoolWithTheAutoscalerShape(t *testing. "pool %s would boot without the autoscaler marker", name) assert.NotContains(t, labels, longhornDefaultDiskLabel, "pool %s would boot with the Longhorn default-disk label", name) + assertBootedKubeletLabels(t, nodeConfig.CloudInit) assert.Equal(t, "true", nodeConfig.Labels[talosprovisioner.LabelAutoscaled], "pool %s template would omit the autoscaler marker", name) assert.NotContains(t, nodeConfig.Labels, longhornDefaultDiskLabel, diff --git a/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config.go b/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config.go index 36b7fc2d7b..8196fc5c2e 100644 --- a/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config.go +++ b/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config.go @@ -12,6 +12,7 @@ import ( "io" "maps" "net/url" + "slices" "sort" "strings" @@ -20,6 +21,7 @@ import ( x509 "github.com/siderolabs/crypto/x509" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" + talosmeta "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -43,6 +45,7 @@ const ( // user_data, so that value must stay within this limit or Hetzner rejects every // scale-up with "invalid input in field 'user_data'". hetznerUserDataLimitBytes = 32768 + longhornDefaultDiskLabel = "node.longhorn.io/create-default-disk" ) // LabelAutoscaled is a Kubernetes node label stamped on every @@ -50,8 +53,9 @@ const ( // downstream workloads a discriminator to key node affinity off of (e.g. a soft // preference for baseline nodes so autoscaler nodes stay empty and scale down). // -// It is applied via the worker config's machine.nodeLabels (kubelet -// --node-labels) so it lands on the real Node object. The Hetzner cluster +// Talos applies the worker config's machine.nodeLabels to the real Node object +// through its node-labels controller, separately from kubelet registration labels. +// The Hetzner cluster // autoscaler deliberately does NOT push its per-pool nodeConfigs[].labels to the // kubelet โ€” those only seed the in-memory scheduling-simulation template โ€” so // stamping the label in the worker cloud-init is the canonical mechanism (see @@ -66,7 +70,8 @@ const labelValueTrue = "true" // GenerateAutoscalerWorkerConfig generates a stripped Talos worker config // suitable for autoscaler-provisioned compute-only nodes. It sets // machine.install.wipe to true, removes machine.disks (autoscaler nodes have -// no attached Hetzner Volumes), removes the Longhorn storage node label while +// no attached Hetzner Volumes), removes the Longhorn storage node label from both +// machine.nodeLabels and kubelet registration arguments while // preserving machine.kubelet.extraMounts for CSI consumer access, and stamps the // LabelAutoscaled marker node label so workloads can tell autoscaler nodes apart // from static baseline workers. @@ -127,7 +132,8 @@ func shapeAutoscalerWorker( maps.Copy(cfg.MachineConfig.MachineNodeLabels, poolLabels) - delete(cfg.MachineConfig.MachineNodeLabels, "node.longhorn.io/create-default-disk") + delete(cfg.MachineConfig.MachineNodeLabels, longhornDefaultDiskLabel) + stripKubeletStorageLabel(cfg.MachineConfig.MachineKubelet) cfg.MachineConfig.MachineNodeLabels[LabelAutoscaled] = labelValueTrue applyPoolTaints(cfg, poolTaints) @@ -141,6 +147,51 @@ func shapeAutoscalerWorker( return patched, nil } +// Kubelet registration labels are independent of machine.nodeLabels. Static workers +// may set the Longhorn label here so storage can schedule immediately on first boot. +// Remove it from every argument value when shaping compute-only autoscaler workers. +func stripKubeletStorageLabel(kubelet *v1alpha1.KubeletConfig) { + if kubelet == nil { + return + } + + values, ok := kubelet.KubeletExtraArgs.ToMap()["node-labels"] + if !ok { + return + } + + filtered := make([]string, 0, len(values)) + changed := false + + for _, value := range values { + labels := strings.Split(value, ",") + + kept := slices.DeleteFunc(labels, func(label string) bool { + key, _, _ := strings.Cut(label, "=") + + return key == longhornDefaultDiskLabel + }) + changed = changed || len(kept) != len(labels) + + if len(kept) > 0 { + filtered = append(filtered, strings.Join(kept, ",")) + } + } + + if !changed { + return + } + + switch len(filtered) { + case 0: + delete(kubelet.KubeletExtraArgs, "node-labels") + case 1: + kubelet.KubeletExtraArgs["node-labels"] = talosmeta.NewArgValue(filtered[0], nil) + default: + kubelet.KubeletExtraArgs["node-labels"] = talosmeta.NewArgValue("", filtered) + } +} + // applyPoolTaints writes the pool's taints into machine.nodeTaints. Talos encodes // each taint as a map entry keyed by the taint key, with the value formatted as // ":" (or "" when the effect is empty). Talos reconciles diff --git a/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config_test.go b/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config_test.go index 41f854fd4a..c7770a40ed 100644 --- a/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config_test.go +++ b/pkg/svc/provisioner/cluster/talos/autoscaler_worker_config_test.go @@ -20,6 +20,7 @@ import ( x509 "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/config/configloader" taloscontainer "github.com/siderolabs/talos/pkg/machinery/config/container" + talosmeta "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -224,6 +225,102 @@ func TestGenerateAutoscalerWorkerConfig_StampsAutoscaledMarker(t *testing.T) { ) } +type kubeletStorageLabelCase struct { + name string + labels string + want string + list bool +} + +func kubeletStorageLabelCases() []kubeletStorageLabelCase { + return []kubeletStorageLabelCase{ + {name: "only storage label", labels: "node.longhorn.io/create-default-disk=true"}, + { + name: "storage label first", + labels: "node.longhorn.io/create-default-disk=true,workload=batch", + want: "workload=batch", + }, + { + name: "storage label last", + labels: "workload=batch,node.longhorn.io/create-default-disk=false", + want: "workload=batch", + }, + { + name: "repeated storage label", + labels: longhornDefaultDiskLabel + "=true,workload=batch," + longhornDefaultDiskLabel + "=false", + want: "workload=batch", + }, + {name: "unrelated label", labels: "workload=batch", want: "workload=batch"}, + { + name: "similar key", + labels: "node.longhorn.io/create-default-disk-extra=true", + want: "node.longhorn.io/create-default-disk-extra=true", + }, + { + name: "list of arguments", + labels: "node.longhorn.io/create-default-disk=true,workload=batch", + want: "workload=batch", + list: true, + }, + { + name: "multiple remaining arguments", + labels: longhornDefaultDiskLabel + "=true,workload=batch,zone=west", + want: "workload=batch,zone=west", + list: true, + }, + { + name: "unrelated list", + labels: "workload=batch,zone=west", + want: "workload=batch,zone=west", + list: true, + }, + } +} + +func TestGenerateAutoscalerWorkerConfig_StripsKubeletStorageLabel(t *testing.T) { + t.Parallel() + + for _, testCase := range kubeletStorageLabelCases() { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + provider := newTestWorkerProvider() + + labelArg := talosmeta.NewArgValue(testCase.labels, nil) + if testCase.list { + labelArg = talosmeta.NewArgValue("", strings.Split(testCase.labels, ",")) + } + + provider.RawV1Alpha1().MachineConfig.MachineKubelet.KubeletExtraArgs = talosmeta.Args{ + "node-labels": labelArg, + "max-pods": talosmeta.NewArgValue("110", nil), + } + + cfg := generateAndParseAutoscalerConfig(t, provider, nil, nil) + args := cfg.MachineConfig.MachineKubelet.KubeletExtraArgs.ToMap() + assert.Equal(t, []string{"110"}, args["max-pods"]) + + if testCase.want == "" { + assert.NotContains(t, args, "node-labels") + } else { + wantValues := []string{testCase.want} + if testCase.list { + wantValues = strings.Split(testCase.want, ",") + } + + assert.Equal(t, wantValues, args["node-labels"]) + } + + assert.Equal( + t, + labelArg, + provider.RawV1Alpha1().MachineConfig.MachineKubelet.KubeletExtraArgs["node-labels"], + "the static worker's registration labels must stay unchanged", + ) + }) + } +} + func TestGenerateAutoscalerWorkerConfig_AppliesPoolLabelsAndTaints(t *testing.T) { t.Parallel() From 349f4a104cc9fe6d3b8db2e963b6203246ad8f4f Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Fri, 9 Oct 2026 01:49:47 +0200 Subject: [PATCH 09/11] fix: respect Flux ownership without Helm storage labels (#7639) * fix: respect Flux ownership without Helm storage labels * chore: apply golangci-lint fixes --------- Co-authored-by: ksail-bot[bot] <262010955+ksail-bot[bot]@users.noreply.github.com> --- .../docs/guides/cluster-provisioning.mdx | 2 + pkg/client/helm/client.go | 26 +- pkg/client/helm/flux_ownership.go | 119 ++++++++++ pkg/client/helm/flux_ownership_test.go | 222 ++++++++++++++++++ 4 files changed, 366 insertions(+), 3 deletions(-) create mode 100644 pkg/client/helm/flux_ownership.go create mode 100644 pkg/client/helm/flux_ownership_test.go diff --git a/docs/src/content/docs/guides/cluster-provisioning.mdx b/docs/src/content/docs/guides/cluster-provisioning.mdx index bb83cf026c..8438edbbb0 100644 --- a/docs/src/content/docs/guides/cluster-provisioning.mdx +++ b/docs/src/content/docs/guides/cluster-provisioning.mdx @@ -110,6 +110,8 @@ spec: For a KSail-managed Cluster Autoscaler on Talos with Hetzner, `cluster diff` also compares the installed Helm values with the values rendered by the current KSail version. A mismatch appears as `cluster.autoscaler.node.chartValues`; `cluster update` applies it even when `ksail.yaml` has not changed. KSail runs the same check for cert-manager when `spec.cluster.certManager` is `Enabled`, and reports a mismatch as `cluster.certManager.chartValues`. Both operations use the resolved cluster context. Releases managed by Flux or Argo CD are excluded from this check and remain under GitOps control. +KSail recognizes a Flux-owned release from its HelmRelease even when Helm's storage labels do not identify Flux. This discovery requires permission to list HelmReleases across namespaces; if that ownership read fails, KSail reports the failure rather than treating an unreadable release as unmanaged. Clusters without the Flux HelmRelease API continue to use Helm's storage labels. + If a release cannot be inspected, KSail prints a warning and continues with the other diff checks. Resolve that warning before treating the absence of a chart-values change as confirmation that the component's settings match. ## Version Upgrades diff --git a/pkg/client/helm/client.go b/pkg/client/helm/client.go index 7d35bf952b..e7f82c56ce 100644 --- a/pkg/client/helm/client.go +++ b/pkg/client/helm/client.go @@ -391,9 +391,11 @@ func (c *Client) ListReleases(ctx context.Context) ([]ReleaseInfo, error) { return result, nil } -// GetReleaseStorageLabels returns the Kubernetes object labels from the latest -// Helm release storage object (Secret or ConfigMap) for the given release name -// and namespace. The storage backend is determined by the HELM_DRIVER +// GetReleaseStorageLabels returns a matching Flux HelmRelease's ownership +// labels, or the latest Helm storage object's labels when Flux does not own +// the release. Flux need not label Helm storage, so inspect its declared owner +// before assuming that ordinary Helm labels mean KSail ownership. +// The storage backend is determined by the HELM_DRIVER // environment variable: "configmap"/"configmaps" queries ConfigMaps, "memory" // always returns ErrNoReleaseStorage (no Kubernetes objects), and the default // queries Secrets. Returns (nil, ErrNoReleaseStorage) when no matching storage @@ -402,6 +404,24 @@ func (c *Client) GetReleaseStorageLabels( ctx context.Context, releaseName, namespace string, ) (map[string]string, error) { + if releaseName == "" { + return nil, errReleaseNameRequired + } + + if namespace == "" { + namespace = c.settings.Namespace() + } + + err := ValidateKubernetesReleaseStorageDriver(configuredReleaseStorageDriver()) + if err != nil { + return nil, err + } + + labels, err := c.fluxReleaseOwnershipLabels(ctx, releaseName, namespace) + if !errors.Is(err, errFluxReleaseNotOwned) { + return labels, err + } + metadata, err := c.GetReleaseStorageMetadata(ctx, releaseName, namespace) if err != nil { return nil, err diff --git a/pkg/client/helm/flux_ownership.go b/pkg/client/helm/flux_ownership.go new file mode 100644 index 0000000000..d3e47ecfcf --- /dev/null +++ b/pkg/client/helm/flux_ownership.go @@ -0,0 +1,119 @@ +package helm + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + + fluxv2 "github.com/fluxcd/helm-controller/api/v2" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +var errFluxReleaseNotOwned = errors.New("no matching Flux HelmRelease") + +const fluxOwnershipPageSize = 200 + +// fluxReleaseOwnershipLabels recognizes the declared owner independently of +// Helm's storage labels. A missing Flux API is normal; unreadable ownership is +// not proof that it is safe to mutate a release. +func (c *Client) fluxReleaseOwnershipLabels( + ctx context.Context, releaseName, namespace string, +) (map[string]string, error) { + config, err := c.settings.RESTClientGetter().ToRESTConfig() + if err != nil { + return nil, fmt.Errorf("get REST config for Flux release ownership: %w", err) + } + + client, err := dynamic.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("create client for Flux release ownership: %w", err) + } + + resource := client.Resource(schema.GroupVersionResource{ + Group: "helm.toolkit.fluxcd.io", Version: "v2", Resource: "helmreleases", + }) + options := metav1.ListOptions{Limit: fluxOwnershipPageSize} + + for { + list, listErr := resource.List(ctx, options) + if apierrors.IsNotFound(listErr) && options.Continue == "" { + return nil, errFluxReleaseNotOwned + } + + if listErr != nil { + return nil, fmt.Errorf("read Flux release ownership: %w", listErr) + } + + labels, ownershipErr := fluxOwnershipLabels(list.Items, releaseName, namespace) + if !errors.Is(ownershipErr, errFluxReleaseNotOwned) { + return labels, ownershipErr + } + + options.Continue = list.GetContinue() + if options.Continue == "" { + return nil, errFluxReleaseNotOwned + } + } +} + +// fluxOwnershipLabels checks one page without treating malformed declarations +// as permission to change a release. +func fluxOwnershipLabels( + items []unstructured.Unstructured, releaseName, namespace string, +) (map[string]string, error) { + for _, item := range items { + var owner fluxv2.HelmRelease + + err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &owner) + if err != nil { + return nil, fmt.Errorf("decode Flux release ownership: %w", err) + } + + if fluxReleaseMatches(owner, releaseName, namespace) { + return map[string]string{ + "helm.toolkit.fluxcd.io/name": owner.Name, + "helm.toolkit.fluxcd.io/namespace": owner.Namespace, + }, nil + } + } + + return nil, errFluxReleaseNotOwned +} + +// fluxReleaseMatches binds ownership to this cluster and the target namespace, +// not the HelmRelease's namespace or its independently configured storage. +func fluxReleaseMatches(owner fluxv2.HelmRelease, name, namespace string) bool { + if owner.Spec.KubeConfig != nil { + return false + } + + declared := owner.GetReleaseName() + // Flux shortens names longer than Helm's 53-byte maximum to a 40-byte + // prefix and 12 hex characters of SHA-256, separated by a hyphen. + // https://github.com/fluxcd/helm-controller/blob/v1.5.5/internal/release/name.go + const helmNameLimit, prefixLength, digestLength = 53, 40, 12 + + if len(declared) > helmNameLimit { + digest := fmt.Sprintf("%x", sha256.Sum256([]byte(declared))) + declared = declared[:prefixLength] + "-" + digest[:digestLength] + } + + if declared == name && owner.GetReleaseNamespace() == namespace { + return true + } + + // Also protect a still-owned release while Flux reconciles a name change. + for _, snapshot := range owner.Status.History { + if snapshot.Name == name && snapshot.Namespace == namespace { + return true + } + } + + return false +} diff --git a/pkg/client/helm/flux_ownership_test.go b/pkg/client/helm/flux_ownership_test.go new file mode 100644 index 0000000000..a1413ca769 --- /dev/null +++ b/pkg/client/helm/flux_ownership_test.go @@ -0,0 +1,222 @@ +package helm_test + +import ( + "context" + "fmt" + "maps" + "net/http" + "net/http/httptest" + "path/filepath" + "testing" + + "github.com/devantler-tech/ksail/v7/pkg/client/helm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + helmcli "helm.sh/helm/v4/pkg/cli" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" +) + +type fluxOwnershipFixture struct { + item string + release string + namespace string + paginated bool + status int + wantOwner bool + wantError string +} + +func fluxOwnershipFixtures() map[string]fluxOwnershipFixture { + fixtures := map[string]fluxOwnershipFixture{ + "default name and namespace": { + item: `{"metadata":{"name":"cert-manager","namespace":"cert-manager"},"spec":{}}`, + wantOwner: true, + }, + "explicit release and target in another storage namespace": { + item: `{"metadata":{"name":"certificates","namespace":"flux-system"}, +"spec":{"releaseName":"cert-manager","targetNamespace":"cert-manager","storageNamespace":"helm-storage"}}`, + wantOwner: true, + }, + "composed default release name": { + item: `{"metadata":{"name":"manager","namespace":"flux-system"},"spec":{"targetNamespace":"cert"}}`, + namespace: "cert", + wantOwner: true, + }, + "shortened declared release name without history": { + item: `{"metadata":{"name":"controller","namespace":"cert-manager"}, +"spec":{"releaseName":"release-name-with-very-long-name-which-is-longer-than-53-characters"}}`, + release: "release-name-with-very-long-name-which-i-788ca0d0d7b0", wantOwner: true, + }, + "still-owned previous release name": { + item: `{"metadata":{"name":"new-name","namespace":"cert-manager"},"spec":{}, +"status":{"history":[{"name":"cert-manager","namespace":"cert-manager"}]}}`, + wantOwner: true, + }, + "still-owned previous target namespace": { + item: `{"metadata":{"name":"cert-manager","namespace":"flux-system"}, +"spec":{"targetNamespace":"new-target"}, +"status":{"history":[{"name":"cert-manager","namespace":"cert-manager"}]}}`, + wantOwner: true, + }, + "owner on a later list page": { + item: `{"metadata":{"name":"cert-manager","namespace":"cert-manager"},"spec":{}}`, + paginated: true, wantOwner: true, + }, + "wrong release": { + item: `{"metadata":{"name":"other","namespace":"cert-manager"},"spec":{}}`, + }, + "wrong target namespace": { + item: `{"metadata":{"name":"cert-manager","namespace":"other"},"spec":{}}`, + }, + "remote cluster release": { + item: `{"metadata":{"name":"cert-manager","namespace":"cert-manager"}, +"spec":{"kubeConfig":{"secretRef":{"name":"remote"}}}}`, + }, + "no HelmReleases": {}, + "Flux API absent": {status: http.StatusNotFound}, + } + + maps.Copy(fixtures, fluxOwnershipErrorFixtures()) + + return fixtures +} + +func fluxOwnershipErrorFixtures() map[string]fluxOwnershipFixture { + return map[string]fluxOwnershipFixture{ + "malformed ownership response": { + item: `{"metadata":{"name":"cert-manager","namespace":"cert-manager"},"spec":{"releaseName":42}}`, + wantError: "decode Flux release ownership", + }, + "ownership access denied": { + status: http.StatusForbidden, + wantError: "Flux release ownership", + }, + "ownership server failure": { + status: http.StatusInternalServerError, + wantError: "Flux release ownership", + }, + "access denied after a healthy list page": { + status: http.StatusForbidden, paginated: true, + wantError: "Flux release ownership", + }, + "API disappears after a healthy list page": { + status: http.StatusNotFound, paginated: true, + wantError: "Flux release ownership", + }, + "server failure after a healthy list page": { + status: http.StatusInternalServerError, paginated: true, + wantError: "Flux release ownership", + }, + } +} + +func TestReleaseLabelsRecognizeFluxWithoutStorageLabels(t *testing.T) { + t.Setenv("HELM_DRIVER", "secret") + + for name, test := range fluxOwnershipFixtures() { + t.Run(name, func(t *testing.T) { + t.Setenv("HELM_DRIVER", "secret") + assertFluxOwnership(t, test) + }) + } +} + +func assertFluxOwnership(t *testing.T, test fluxOwnershipFixture) { + t.Helper() + + releaseName, namespace := test.release, test.namespace + if releaseName == "" { + releaseName = "cert-manager" + } + + if namespace == "" { + namespace = "cert-manager" + } + + client := newFluxOwnershipClient(t, test, namespace) + + labels, err := client.GetReleaseStorageLabels(context.Background(), releaseName, namespace) + if test.wantError != "" { + require.ErrorContains(t, err, test.wantError) + assert.Nil(t, labels) + + return + } + + require.NoError(t, err) + + _, owned := labels["helm.toolkit.fluxcd.io/name"] + assert.Equal(t, test.wantOwner, owned) +} + +func newFluxOwnershipClient( + t *testing.T, + fixture fluxOwnershipFixture, + namespace string, +) *helm.Client { + t.Helper() + + server := httptest.NewServer(fluxOwnershipHandler(fixture, namespace)) + t.Cleanup(server.Close) + path := filepath.Join(t.TempDir(), "config") + require.NoError(t, clientcmd.WriteToFile(clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{"test": {Server: server.URL}}, + AuthInfos: map[string]*clientcmdapi.AuthInfo{"test": {}}, + Contexts: map[string]*clientcmdapi.Context{ + "test": {Cluster: "test", AuthInfo: "test"}, + }, + CurrentContext: "test", + }, path)) + + settings := helmcli.New() + settings.KubeConfig = path + + return helm.NewClientFromParts(nil, settings) +} + +func fluxOwnershipHandler(fixture fluxOwnershipFixture, namespace string) http.Handler { + return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + response.Header().Set("Content-Type", "application/json") + + switch request.URL.Path { + case "/apis/helm.toolkit.fluxcd.io/v2/helmreleases": + if fixture.paginated && request.URL.Query().Get("continue") == "" { + _, _ = fmt.Fprint( + response, + `{"apiVersion":"helm.toolkit.fluxcd.io/v2","kind":"HelmReleaseList", +"metadata":{"continue":"next-page"}, +"items":[{"metadata":{"name":"unrelated","namespace":"other"},"spec":{}}]}`, + ) + + return + } + + if fixture.status != 0 { + response.WriteHeader(fixture.status) + _, _ = fmt.Fprintf( + response, + `{"apiVersion":"v1","kind":"Status","status":"Failure","code":%d}`, + fixture.status, + ) + + return + } + + _, _ = fmt.Fprintf( + response, + `{"apiVersion":"helm.toolkit.fluxcd.io/v2","kind":"HelmReleaseList","items":[%s]}`, + fixture.item, + ) + case "/api/v1/namespaces/" + namespace + "/secrets": + _, _ = fmt.Fprint( + response, + `{"apiVersion":"v1","kind":"SecretList","items":[{"metadata":{ +"name":"sh.helm.release.v1.cert-manager.v1","namespace":"cert-manager","uid":"storage-uid", +"labels":{"name":"cert-manager","owner":"helm","version":"1"}}}]}`, + ) + default: + http.NotFound(response, request) + } + }) +} From 15322e40c4a494ee345e6873488208bfc4802488 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Fri, 9 Oct 2026 01:57:30 +0200 Subject: [PATCH 10/11] ci: reject retired repository links in active documentation (#7529) * ci: reject retired repository links in active documentation * chore(ci): label the pinned checkout release accurately * fix(ci): quote the retired link guard workspace setting * chore: Apply megalinter fixes --------- Co-authored-by: ksail-bot[bot] <262010955+ksail-bot[bot]@users.noreply.github.com> --- .github/retired-repo-links.json | 6 ++++ .github/workflows/ci.yaml | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 .github/retired-repo-links.json diff --git a/.github/retired-repo-links.json b/.github/retired-repo-links.json new file mode 100644 index 0000000000..19ae42c4ac --- /dev/null +++ b/.github/retired-repo-links.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "repositories": ["devantler-tech/reusable-workflows"], + "paths": ["README.md", "AGENTS.md"], + "exceptions": [] +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7f9d053197..ec8b25a456 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2288,12 +2288,64 @@ jobs: if-no-files-found: error retention-days: 14 + retired-repo-links: + name: Retired repository links + runs-on: ubuntu-latest + permissions: + contents: read + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check active documentation + id: links + uses: devantler-tech/.github/actions/validate-retired-repo-links@bd0035dd8f41fcf1459b878882b8897443f8dc59 # v4.9.4 + with: + enabled: "true" + - name: Require a completed scan + env: + VALIDATED: ${{ steps.links.outputs.validated }} + run: test "$VALIDATED" = true + - name: Check out the exact validator for refusal controls + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: devantler-tech/.github + ref: bd0035dd8f41fcf1459b878882b8897443f8dc59 # v4.9.4 + path: .retired-link-validator-source + persist-credentials: false + - name: Prove retired links and incomplete scans fail + env: + GOWORK: "off" + GOFLAGS: "" + GOTOOLCHAIN: local + run: | + set -euo pipefail + control=$(mktemp -d "$RUNNER_TEMP/retired-links.XXXXXX") + trap 'rm -rf "$control"' EXIT + mkdir "$control/.github" + cp .github/retired-repo-links.json "$control/.github/" + cp README.md AGENTS.md "$control/" + go -C .retired-link-validator-source/actions/validate-retired-repo-links build -mod=readonly -trimpath -o "$control/validator" . + printf '\nhttps://github.com/devantler-tech/reusable-workflows\n' >>"$control/README.md" + status=0 + "$control/validator" --root "$control" >"$control/negative.log" 2>&1 || status=$? + test "$status" = 1 || { cat "$control/negative.log"; exit 1; } + grep -Eq '^README.md:[0-9]+: link targets retired repository devantler-tech/reusable-workflows$' "$control/negative.log" + rm "$control/.github/retired-repo-links.json" + status=0 + "$control/validator" --root "$control" >"$control/missing.log" 2>&1 || status=$? + test "$status" = 2 || { cat "$control/missing.log"; exit 1; } + grep -q '^Invalid configuration:' "$control/missing.log" + echo 'PASS: clean scan, seeded retired link rejection, and missing configuration refusal' + require-checks-in-pr: name: ${{ github.event_name == 'workflow_dispatch' && inputs.run_local_recovery_trial && 'Local Recovery Trial - Checks' || 'CI - Required Checks' }} runs-on: ubuntu-latest timeout-minutes: 5 needs: [ + retired-repo-links, rate-limit-gate, changes, ci-go, @@ -2333,6 +2385,7 @@ jobs: - uses: devantler-tech/actions/aggregate-job-checks@1d726483facb9340655489913f609082677be8f5 # v13.10.6 with: job-results: >- + ${{ needs.retired-repo-links.result }} ${{ needs.rate-limit-gate.result }} ${{ needs.changes.result }} ${{ needs.ci-go.result }} From 4dfe77d7047844235647a91331c1cdd261409fd8 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Fri, 9 Oct 2026 03:40:44 +0200 Subject: [PATCH 11/11] fix(security): patch Go and HTTP/2 vulnerabilities (#7641) --- go.mod | 4 ++-- go.sum | 4 ++-- images/ksail-analysis-runner/Dockerfile | 6 +++--- images/ksail-analysis-runner/smoke.sh | 2 +- images/ksail-analysis-runner/smoke_test.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index aa4556d3ce..94f9989795 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/devantler-tech/ksail/v7 -go 1.26.8 +go 1.26.9 // Exclude the standalone google.golang.org/grpc/stats/opentelemetry module to avoid // ambiguous import errors. The opentelemetry stats package is included in the main @@ -984,7 +984,7 @@ require ( golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect golang.org/x/image v0.45.0 // indirect - golang.org/x/net v0.59.0 // indirect + golang.org/x/net v0.60.0 // indirect golang.org/x/sys v0.48.0 golang.org/x/telemetry v0.0.0-20260908163034-4bcc4b2ee518 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/go.sum b/go.sum index 5c4e0d2343..b1d18ae3ea 100644 --- a/go.sum +++ b/go.sum @@ -2972,8 +2972,8 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= -golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= +golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/images/ksail-analysis-runner/Dockerfile b/images/ksail-analysis-runner/Dockerfile index 8584cb70cc..82241c62bb 100644 --- a/images/ksail-analysis-runner/Dockerfile +++ b/images/ksail-analysis-runner/Dockerfile @@ -14,14 +14,14 @@ RUN printf 'APT::Snapshot "20261005T000000Z";\n' > /etc/apt/apt.conf.d/99ksail-a RUN set -eu; \ case "${TARGETARCH}" in \ - amd64) go_arch=amd64; go_sha=d0f743b33e8d8945e6b1f432edd15785c70507121d6e2a723b21285eddf8b57b; \ + amd64) go_arch=amd64; go_sha=42d158b4d8f7b61ac0a830567c940a86098fb7aac52e467a5ebec03ef5cc2f8d; \ node_arch=x64; node_sha=df450af89261115ef9f9e3830c3eeb2cc9213b63c720b1af623cb5dcbe2e02de ;; \ - arm64) go_arch=arm64; go_sha=211ffced9dcb9633a55eac6364816ec0ddd951389a740e88fa8b3337971bdda0; \ + arm64) go_arch=arm64; go_sha=4a97373d49fcacdcf3694fea368a500b00ee3e963974f3e7514132717632f052; \ node_arch=arm64; node_sha=a44aeb94849a299b22df10b9e622ec2f605c2183501bc40590705131de7c740f ;; \ *) echo "Unsupported architecture: ${TARGETARCH}" >&2; exit 1 ;; \ esac; \ curl --fail --silent --show-error --location --max-time 120 \ - "https://go.dev/dl/go1.26.8.linux-${go_arch}.tar.gz" --output /tmp/go.tar.gz; \ + "https://go.dev/dl/go1.26.9.linux-${go_arch}.tar.gz" --output /tmp/go.tar.gz; \ printf '%s /tmp/go.tar.gz\n' "${go_sha}" | sha256sum --check --strict; \ tar -xzf /tmp/go.tar.gz -C /usr/local; \ curl --fail --silent --show-error --location --max-time 120 \ diff --git a/images/ksail-analysis-runner/smoke.sh b/images/ksail-analysis-runner/smoke.sh index 0e1fa30920..9eb293d50f 100755 --- a/images/ksail-analysis-runner/smoke.sh +++ b/images/ksail-analysis-runner/smoke.sh @@ -32,7 +32,7 @@ if touch /usr/local/ksail-analysis-write-probe 2>/dev/null; then echo 'Root filesystem unexpectedly writable' >&2 exit 1 fi -test "$(go version)" = "go version go1.26.8 linux/$(go env GOARCH)" +test "$(go version)" = "go version go1.26.9 linux/$(go env GOARCH)" test "$(node --version)" = v22.23.3 test "$(go env CGO_ENABLED)" = 1 test "$(go env GOFLAGS)" = -tags=desktop diff --git a/images/ksail-analysis-runner/smoke_test.go b/images/ksail-analysis-runner/smoke_test.go index 19d3f92f61..9272851ad9 100644 --- a/images/ksail-analysis-runner/smoke_test.go +++ b/images/ksail-analysis-runner/smoke_test.go @@ -94,7 +94,7 @@ esac`, "pkg-config": "exit 0", "cp": "echo 'live smoke attempted to bootstrap the active runner' >&2; exit 99", "go": `case "$1" in -version) printf 'go version go1.26.8 linux/amd64\n' ;; +version) printf 'go version go1.26.9 linux/amd64\n' ;; env) case "$2" in GOARCH) printf 'amd64\n' ;; CGO_ENABLED) printf '1\n' ;;