From cd947c1549efa9c26dadc197c8d4ad5218ec1155 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Thu, 28 May 2026 11:13:34 -0400 Subject: [PATCH 01/44] upgrade to latest dependencies (#3861) bumping knative.dev/serving 80528e7...847bb41: > 847bb41 Update net-gateway-api nightly (# 16616) Signed-off-by: Knative Automation --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 39733dd61a..09ef1f22b9 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( knative.dev/eventing v0.49.1-0.20260527033339-b0d6da89bcb7 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251 - knative.dev/serving v0.49.1-0.20260527071040-80528e7e3a3b + knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72 sigs.k8s.io/controller-runtime v0.23.3 ) diff --git a/go.sum b/go.sum index 6827521c03..0c40d2485c 100644 --- a/go.sum +++ b/go.sum @@ -1883,8 +1883,8 @@ knative.dev/networking v0.0.0-20260521020427-3cf3413b35b8 h1:EST6VC3OyHwY8ZruphB knative.dev/networking v0.0.0-20260521020427-3cf3413b35b8/go.mod h1:w2kcC3evVu4W9/4wpI03x3xylzk0isu9JqR7oRdVvJc= knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251 h1:BcWj5go8vZotFKrbv1NnoChA3x9Jy+qFTL1YVJ7QQ8s= knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= -knative.dev/serving v0.49.1-0.20260527071040-80528e7e3a3b h1:vpIwCsj6BRE8K2JMMt7n2CNbITQpojwWzFvr+cXVcB8= -knative.dev/serving v0.49.1-0.20260527071040-80528e7e3a3b/go.mod h1:nP38XTzAhFEKAD+JMwMuFI/qqUbKhnUBgbFWGnvxQFk= +knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72 h1:vQWBYiQj9woSXkcKjaR1rwNbIWTbUDMgBVUTIQ/1fYs= +knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72/go.mod h1:nP38XTzAhFEKAD+JMwMuFI/qqUbKhnUBgbFWGnvxQFk= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From f6d9270478e706d24c0c49e327c8f3f0db4e91eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Thu, 28 May 2026 21:07:33 +0200 Subject: [PATCH 02/44] fix: increase default pvc size (#3862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous 256Mi is too low. It worked mostly by accident: KinD uses hostpath storage so there is no size enforcement. On AWS OCP we probably use storage with minimum 1Gi size. Signed-off-by: Matej Vašek --- pkg/pipelines/tekton/pipelines_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pipelines/tekton/pipelines_provider.go b/pkg/pipelines/tekton/pipelines_provider.go index 15182ecc12..fbb700f16b 100644 --- a/pkg/pipelines/tekton/pipelines_provider.go +++ b/pkg/pipelines/tekton/pipelines_provider.go @@ -42,7 +42,7 @@ import ( const DefaultNamespace = "default" // DefaultPersistentVolumeClaimSize to allocate for the function. -var DefaultPersistentVolumeClaimSize = resource.MustParse("256Mi") +var DefaultPersistentVolumeClaimSize = resource.MustParse("750Mi") type PipelineDecorator interface { UpdateLabels(fn.Function, map[string]string) map[string]string From 5b32fefb51e10a3c479d07c5e3064dbe2fbff852 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Fri, 29 May 2026 10:59:07 -0400 Subject: [PATCH 03/44] upgrade to latest dependencies (#3865) bumping knative.dev/client/pkg 9584f25...751d4bf: > 751d4bf upgrade to latest dependencies (# 2225) bumping knative.dev/eventing b0d6da8...e2d6b95: > e2d6b95 [main] Upgrade to latest dependencies (# 9095) bumping knative.dev/pkg 71f6ad6...0dbbb5a: > 0dbbb5a Fix memory leak in expired matchers (# 3358) bumping knative.dev/serving 847bb41...f9dd97e: > f9dd97e Update net-kourier nightly (# 16623) > e25c1a2 test: add missing readiness and startup probe tests for init containers (# 16618) > 382ffef Update net-kourier nightly (# 16617) > 67040ac Update net-contour nightly (# 16614) bumping knative.dev/networking 3cf3413...08ed7ab: > 08ed7ab Fix idle connection leak in prober (# 1139) > aa0666d upgrade to latest dependencies (# 1140) Signed-off-by: Knative Automation --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 09ef1f22b9..9967137843 100644 --- a/go.mod +++ b/go.mod @@ -69,11 +69,11 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260527024141-9584f25b469c - knative.dev/eventing v0.49.1-0.20260527033339-b0d6da89bcb7 + knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1 + knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251 - knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72 + knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623 + knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311 sigs.k8s.io/controller-runtime v0.23.3 ) @@ -318,7 +318,7 @@ require ( k8s.io/apiserver v0.35.5 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - knative.dev/networking v0.0.0-20260521020427-3cf3413b35b8 // indirect + knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629 // indirect sigs.k8s.io/gateway-api v1.4.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.0 // indirect diff --git a/go.sum b/go.sum index 0c40d2485c..11a91bc689 100644 --- a/go.sum +++ b/go.sum @@ -1873,18 +1873,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260527024141-9584f25b469c h1:Wi/H5bGOISXs5Ll7XQ4xlFS2wYZvBVIb6ZFBGQJLpyM= -knative.dev/client/pkg v0.0.0-20260527024141-9584f25b469c/go.mod h1:zEKhuemns91ynMsu53ImHISd/N+HANe2RQJ3j0TnXrM= -knative.dev/eventing v0.49.1-0.20260527033339-b0d6da89bcb7 h1:1IXlwE0dGX7V5RjigXyMKorRPjeWp/e4lOAEx8lu/E0= -knative.dev/eventing v0.49.1-0.20260527033339-b0d6da89bcb7/go.mod h1:LyVkb/v8rhpNKvxNhk+IWOgZUDzAoX36GXvjbdmKFcc= +knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1 h1:9/w+qPWVqomxahH9+dfRmvrvouXeNlCdJrCcV7u2PbA= +knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1/go.mod h1:tTc6FP86e7Eqzim/9CsgdZloALRm1HJ63b0dq3T42No= +knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e h1:R1qo705sW597mxdbkul0Yxmp3BXZvWaiX4wJxOaWLJg= +knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e/go.mod h1:esrMPXP79jb7ZabpI9K3WqJ+SZuqADDycMFxRBCyDDY= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= -knative.dev/networking v0.0.0-20260521020427-3cf3413b35b8 h1:EST6VC3OyHwY8ZruphBF142QFT0xWiGLYTrIa/FU800= -knative.dev/networking v0.0.0-20260521020427-3cf3413b35b8/go.mod h1:w2kcC3evVu4W9/4wpI03x3xylzk0isu9JqR7oRdVvJc= -knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251 h1:BcWj5go8vZotFKrbv1NnoChA3x9Jy+qFTL1YVJ7QQ8s= -knative.dev/pkg v0.0.0-20260526162440-71f6ad65d251/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= -knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72 h1:vQWBYiQj9woSXkcKjaR1rwNbIWTbUDMgBVUTIQ/1fYs= -knative.dev/serving v0.49.1-0.20260528132932-847bb413ee72/go.mod h1:nP38XTzAhFEKAD+JMwMuFI/qqUbKhnUBgbFWGnvxQFk= +knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629 h1:VjS7Hvmpa19V1yaHmMqNGH6xrbo1xS7399VC8od6uk8= +knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629/go.mod h1:OxucqHsN/rXTOpRyuY1B1cKUPzk6aqHpu11LE7WBmkE= +knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623 h1:6tCZr0uzT5ZLDQHmYHTmvX/6WKHsLBAlmY2b2p+k6Bc= +knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= +knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311 h1:Q30SjBqKknWfaNvNaVqdnkpnKM1PPNqrvkK/PgRL+Hc= +knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311/go.mod h1:nP38XTzAhFEKAD+JMwMuFI/qqUbKhnUBgbFWGnvxQFk= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 55f32a3e50ddb042213df086de886384231fd938 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Sun, 31 May 2026 22:42:31 -0400 Subject: [PATCH 04/44] upgrade to latest dependencies (#3869) bumping knative.dev/client/pkg 751d4bf...13f0230: > 13f0230 upgrade to latest dependencies (# 2226) bumping knative.dev/pkg 0dbbb5a...91499a1: > 91499a1 Limit the webhook request body size to 3MiB (# 3362) bumping knative.dev/networking 08ed7ab...3057891: > 3057891 upgrade to latest dependencies (# 1143) bumping knative.dev/serving f9dd97e...1d4c5d9: > 1d4c5d9 Update net-gateway-api nightly (# 16624) > 6a87e65 Update net-istio nightly (# 16615) > a3bb5b7 upgrade to latest dependencies (# 16620) > e0b9fb4 Update net-contour nightly (# 16622) Signed-off-by: Knative Automation --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9967137843..2499699023 100644 --- a/go.mod +++ b/go.mod @@ -69,11 +69,11 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1 + knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8 knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623 - knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311 + knative.dev/pkg v0.0.0-20260529191007-91499a17111f + knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707 sigs.k8s.io/controller-runtime v0.23.3 ) @@ -318,7 +318,7 @@ require ( k8s.io/apiserver v0.35.5 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629 // indirect + knative.dev/networking v0.0.0-20260529020035-305789141b2b // indirect sigs.k8s.io/gateway-api v1.4.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.0 // indirect diff --git a/go.sum b/go.sum index 11a91bc689..bcd737d80f 100644 --- a/go.sum +++ b/go.sum @@ -1873,18 +1873,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1 h1:9/w+qPWVqomxahH9+dfRmvrvouXeNlCdJrCcV7u2PbA= -knative.dev/client/pkg v0.0.0-20260529023933-751d4bf165f1/go.mod h1:tTc6FP86e7Eqzim/9CsgdZloALRm1HJ63b0dq3T42No= +knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8 h1:lhAVj1K+VJ0yyhZ6xlX/1OextPGK5qwr9o8laP6ATlM= +knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8/go.mod h1:ARAILgl+iZm8VX3II5oVXH7tb1368GvDaP5JsaRGiig= knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e h1:R1qo705sW597mxdbkul0Yxmp3BXZvWaiX4wJxOaWLJg= knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e/go.mod h1:esrMPXP79jb7ZabpI9K3WqJ+SZuqADDycMFxRBCyDDY= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= -knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629 h1:VjS7Hvmpa19V1yaHmMqNGH6xrbo1xS7399VC8od6uk8= -knative.dev/networking v0.0.0-20260528195732-08ed7ab1d629/go.mod h1:OxucqHsN/rXTOpRyuY1B1cKUPzk6aqHpu11LE7WBmkE= -knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623 h1:6tCZr0uzT5ZLDQHmYHTmvX/6WKHsLBAlmY2b2p+k6Bc= -knative.dev/pkg v0.0.0-20260528184932-0dbbb5a7d623/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= -knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311 h1:Q30SjBqKknWfaNvNaVqdnkpnKM1PPNqrvkK/PgRL+Hc= -knative.dev/serving v0.49.1-0.20260529132106-f9dd97e12311/go.mod h1:nP38XTzAhFEKAD+JMwMuFI/qqUbKhnUBgbFWGnvxQFk= +knative.dev/networking v0.0.0-20260529020035-305789141b2b h1:bVEeZ66aokJjPNHA8gwcEw6bbU/Lxhbt3jQyy4QZPP8= +knative.dev/networking v0.0.0-20260529020035-305789141b2b/go.mod h1:upXhnYXpr11YRpKlKiGTB4hzDTn4kUJ7gqDaqMFJaoo= +knative.dev/pkg v0.0.0-20260529191007-91499a17111f h1:eNG7TLdIUZN/IQInFAJ3RPHZZtNsyJkZwFsOYZd1cRQ= +knative.dev/pkg v0.0.0-20260529191007-91499a17111f/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= +knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707 h1:L/a5EK+/c1QnHYfmHHNAE3Uvw39usxF5y7tjdKM6thk= +knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707/go.mod h1:JBk2I8KkkjLu3g4QesIyLsK2skAXGbqELL/Pq/J7TR0= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From b8d4db53c088c6cbda8b7c7f20aa9b34c9c01ca6 Mon Sep 17 00:00:00 2001 From: Stanislav Jakuschevskij Date: Tue, 2 Jun 2026 03:10:02 +0200 Subject: [PATCH 05/44] Refactor: Move CI workflow generation from `cmd/ci` to `pkg/ci/github`. (#3572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: move CI workflow to pkg/ci/github The CI workflow generation lived in cmd/ci/, tightly coupled to the command layer via viper flag reads and direct file I/O. This made it impossible to reuse or test the generator independently. Move workflow generation, printing, and writing into pkg/ci/github/ as a self-contained package. Introduce CI and PathWriter interfaces in pkg/functions/client.go so the client can orchestrate workflow generation without depending on the cmd layer. The cmd/config_ci.go command now resolves flags and delegates to client.GenerateCIWorkflow(). Also fix typos: --patform → --platform, functionl → function, proivdes → provides, precldes → precedes, sevices → services, suppot → support, Desribe → Describe, pipilines → pipelines, intance → instance. Issue #3256 Signed-off-by: Stanislav Jakuschevskij * refactor: address PR #3572 review feedback The CI interface on the client was too broad: it accepted any-typed config and required PathWriter and io.Writer to be threaded through the client as fields. The reviewer asked for a clean Generate(ctx, Function) signature and proper test separation between CLI and pkg layers. The client now holds a single CIGenerator interface with Generate(ctx, Function). All implementation details (writers, verbosity, workflow config) are owned by the concrete generator, injected at construction time via functional options. The PathWriter interface was replaced by a WorkflowWriter local to the github package, removing the reverse dependency on pkg/functions. To decouple CLI tests from generator internals, a ciGeneratorFactory was introduced following the existing ClientFactory/NewTestClient pattern. The factory takes the resolved WorkflowConfig and returns a CIGenerator, letting tests capture the config via a mock and verify that flags map correctly without inspecting generated YAML. Workflow structure tests were migrated from cmd/config_ci_test.go to pkg/ci/github/generator_test.go. The inadvertent whitespace change in printer.go (tab and missing colon on the Remote build line) was also reverted. Issue #3572 Signed-off-by: Stanislav Jakuschevskij --------- Signed-off-by: Stanislav Jakuschevskij --- cmd/build.go | 2 +- cmd/ci/config.go | 292 --------- cmd/ci/config_test.go | 47 -- cmd/ci/workflow.go | 225 ------- cmd/ci/workflow_test.go | 40 -- cmd/config.go | 16 +- cmd/config_ci.go | 282 +++++++-- cmd/config_ci_int_test.go | 68 +- cmd/config_ci_test.go | 635 +------------------ cmd/config_test.go | 5 +- cmd/root.go | 5 +- {cmd/ci => pkg/ci/github}/common.go | 24 +- pkg/ci/github/generator.go | 115 ++++ pkg/ci/github/generator_test.go | 718 ++++++++++++++++++++++ {cmd/ci => pkg/ci/github}/printer.go | 63 +- {cmd/ci => pkg/ci/github}/printer_test.go | 32 +- pkg/ci/github/workflow.go | 334 ++++++++++ pkg/ci/github/workflow_test.go | 29 + {cmd/ci => pkg/ci/github}/writer.go | 11 +- {cmd/ci => pkg/ci/github}/writer_test.go | 11 +- pkg/functions/client.go | 30 +- 21 files changed, 1641 insertions(+), 1343 deletions(-) delete mode 100644 cmd/ci/config.go delete mode 100644 cmd/ci/config_test.go delete mode 100644 cmd/ci/workflow.go delete mode 100644 cmd/ci/workflow_test.go rename {cmd/ci => pkg/ci/github}/common.go (50%) create mode 100644 pkg/ci/github/generator.go create mode 100644 pkg/ci/github/generator_test.go rename {cmd/ci => pkg/ci/github}/printer.go (53%) rename {cmd/ci => pkg/ci/github}/printer_test.go (63%) create mode 100644 pkg/ci/github/workflow.go create mode 100644 pkg/ci/github/workflow_test.go rename {cmd/ci => pkg/ci/github}/writer.go (95%) rename {cmd/ci => pkg/ci/github}/writer_test.go (85%) diff --git a/cmd/build.go b/cmd/build.go index 1c16cdce9b..f7f4e25822 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -502,7 +502,7 @@ func (c buildConfig) buildOptions() (oo []fn.BuildOption, err error) { if c.Platform != "" { parts := strings.Split(c.Platform, "/") if len(parts) != 2 { - return oo, fmt.Errorf("the value for --patform must be in the form [OS]/[Architecture]. eg \"linux/amd64\"") + return oo, fmt.Errorf("the value for --platform must be in the form [OS]/[Architecture]. eg \"linux/amd64\"") } oo = append(oo, fn.BuildWithPlatforms([]fn.Platform{{OS: parts[0], Architecture: parts[1]}})) } diff --git a/cmd/ci/config.go b/cmd/ci/config.go deleted file mode 100644 index 40a5dadea2..0000000000 --- a/cmd/ci/config.go +++ /dev/null @@ -1,292 +0,0 @@ -package ci - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/ory/viper" - "knative.dev/func/cmd/common" -) - -const ( - ConfigCIFeatureFlag = "FUNC_ENABLE_CI_CONFIG" - - PathFlag = "path" - - PlatformFlag = "platform" - DefaultPlatform = "github" - - DefaultGitHubWorkflowDir = ".github/workflows" - DefaultGitHubWorkflowFilename = "func-deploy.yaml" - - BranchFlag = "branch" - DefaultBranch = "main" - - WorkflowNameFlag = "workflow-name" - DefaultWorkflowName = "Func Deploy" - DefaultRemoteBuildWorkflowName = "Remote " + DefaultWorkflowName - - KubeconfigSecretNameFlag = "kubeconfig-secret-name" - DefaultKubeconfigSecretName = "KUBECONFIG" - - RegistryLoginUrlVariableNameFlag = "registry-login-url-variable-name" - DefaultRegistryLoginUrlVariableName = "REGISTRY_LOGIN_URL" - - RegistryUserVariableNameFlag = "registry-user-variable-name" - DefaultRegistryUserVariableName = "REGISTRY_USERNAME" - - RegistryPassSecretNameFlag = "registry-pass-secret-name" - DefaultRegistryPassSecretName = "REGISTRY_PASSWORD" - - RegistryUrlVariableNameFlag = "registry-url-variable-name" - DefaultRegistryUrlVariableName = "REGISTRY_URL" - - RegistryLoginFlag = "registry-login" - DefaultRegistryLogin = true - - WorkflowDispatchFlag = "workflow-dispatch" - DefaultWorkflowDispatch = false - - RemoteBuildFlag = "remote" - DefaultRemoteBuild = false - - SelfHostedRunnerFlag = "self-hosted-runner" - DefaultSelfHostedRunner = false - - TestStepFlag = "test-step" - DefaultTestStep = true - - ForceFlag = "force" - DefaultForce = false - - VerboseFlag = "verbose" - DefaultVerbose = false -) - -// CIConfig readonly configuration -type CIConfig struct { - githubWorkflowDir, - githubWorkflowFilename, - branch, - workflowName, - kubeconfigSecret, - registryLoginUrlVar, - registryUserVar, - registryPassSecret, - registryUrlVar string - registryLogin, - selfHostedRunner, - remoteBuild, - workflowDispatch, - testStep, - force, - verbose bool - fnRuntime, - fnRoot, - fnBuilder string -} - -func NewCIConfig( - fnLoader common.FunctionLoader, - currentBranch common.CurrentBranchFunc, - workingDir common.WorkDirFunc, - workflowNameExplicit bool, -) (CIConfig, error) { - if err := resolvePlatform(); err != nil { - return CIConfig{}, err - } - - path, err := resolvePath(workingDir) - if err != nil { - return CIConfig{}, err - } - - branch, err := resolveBranch(path, currentBranch) - if err != nil { - return CIConfig{}, err - } - - workflowName := resolveWorkflowName(workflowNameExplicit) - - f, err := fnLoader.Load(path) - if err != nil { - return CIConfig{}, err - } - - remoteBuild := viper.GetBool(RemoteBuildFlag) - fnBuilder, err := resolveBuilder(f.Runtime, remoteBuild) - if err != nil { - return CIConfig{}, err - } - - return CIConfig{ - githubWorkflowDir: DefaultGitHubWorkflowDir, - githubWorkflowFilename: DefaultGitHubWorkflowFilename, - branch: branch, - workflowName: workflowName, - kubeconfigSecret: viper.GetString(KubeconfigSecretNameFlag), - registryLoginUrlVar: viper.GetString(RegistryLoginUrlVariableNameFlag), - registryUserVar: viper.GetString(RegistryUserVariableNameFlag), - registryPassSecret: viper.GetString(RegistryPassSecretNameFlag), - registryUrlVar: viper.GetString(RegistryUrlVariableNameFlag), - registryLogin: viper.GetBool(RegistryLoginFlag), - selfHostedRunner: viper.GetBool(SelfHostedRunnerFlag), - remoteBuild: remoteBuild, - workflowDispatch: viper.GetBool(WorkflowDispatchFlag), - testStep: viper.GetBool(TestStepFlag), - force: viper.GetBool(ForceFlag), - verbose: viper.GetBool(VerboseFlag), - fnRuntime: f.Runtime, - fnRoot: f.Root, - fnBuilder: fnBuilder, - }, nil -} - -func resolvePlatform() error { - platform := viper.GetString(PlatformFlag) - if platform == "" { - return fmt.Errorf("platform must not be empty, supported: %s", DefaultPlatform) - } - if strings.ToLower(platform) != DefaultPlatform { - return fmt.Errorf("%s support is not implemented, supported: %s", platform, DefaultPlatform) - } - - return nil -} - -func resolvePath(workingDir common.WorkDirFunc) (string, error) { - path := viper.GetString(PathFlag) - if path != "" && path != "." { - return path, nil - } - - cwd, err := workingDir() - if err != nil { - return "", err - } - - return cwd, nil -} - -func resolveBranch(path string, currentBranch common.CurrentBranchFunc) (string, error) { - branch := viper.GetString(BranchFlag) - if branch != "" { - return branch, nil - } - - branch, err := currentBranch(path) - if err != nil { - return "", err - } - - return branch, nil -} - -func resolveWorkflowName(explicit bool) string { - workflowName := viper.GetString(WorkflowNameFlag) - if explicit { - return workflowName - } - - if viper.GetBool(RemoteBuildFlag) { - return DefaultRemoteBuildWorkflowName - } - - return DefaultWorkflowName -} - -func resolveBuilder(runtime string, remote bool) (string, error) { - switch runtime { - case "go": - if remote { - return "pack", nil - } - return "host", nil - - case "node", "typescript", "rust", "quarkus", "springboot": - return "pack", nil - - case "python": - if remote { - return "s2i", nil - } - return "host", nil - - default: - return "", fmt.Errorf("no builder support for runtime: %s", runtime) - } -} - -func (cc CIConfig) FnGitHubWorkflowFilepath() string { - fnGitHubWorkflowDir := filepath.Join(cc.fnRoot, cc.githubWorkflowDir) - return filepath.Join(fnGitHubWorkflowDir, cc.githubWorkflowFilename) -} - -func (cc CIConfig) OutputPath() string { - return filepath.Join(cc.githubWorkflowDir, cc.githubWorkflowFilename) -} - -func (cc CIConfig) Branch() string { - return cc.branch -} - -func (cc CIConfig) WorkflowName() string { - return cc.workflowName -} - -func (cc CIConfig) KubeconfigSecret() string { - return cc.kubeconfigSecret -} - -func (cc CIConfig) RegistryLoginUrlVar() string { - return cc.registryLoginUrlVar -} - -func (cc CIConfig) RegistryUserVar() string { - return cc.registryUserVar -} - -func (cc CIConfig) RegistryPassSecret() string { - return cc.registryPassSecret -} - -func (cc CIConfig) RegistryUrlVar() string { - return cc.registryUrlVar -} - -func (cc CIConfig) RegistryLogin() bool { - return cc.registryLogin -} - -func (cc CIConfig) SelfHostedRunner() bool { - return cc.selfHostedRunner -} - -func (cc CIConfig) RemoteBuild() bool { - return cc.remoteBuild -} - -func (cc CIConfig) WorkflowDispatch() bool { - return cc.workflowDispatch -} - -func (cc CIConfig) TestStep() bool { - return cc.testStep -} - -func (cc CIConfig) Force() bool { - return cc.force -} - -func (cc CIConfig) Verbose() bool { - return cc.verbose -} - -func (cc CIConfig) FnRuntime() string { - return cc.fnRuntime -} - -func (cc CIConfig) FnBuilder() string { - return cc.fnBuilder -} diff --git a/cmd/ci/config_test.go b/cmd/ci/config_test.go deleted file mode 100644 index 3694876522..0000000000 --- a/cmd/ci/config_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package ci - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -// TestResolveBuilder covers the branching logic that selects the correct -// build strategy for each runtime × local/remote combination. -func TestResolveBuilder(t *testing.T) { - tests := []struct { - name string - runtime string - remote bool - want string - wantErr bool - }{ - {name: "go local", runtime: "go", remote: false, want: "host"}, - {name: "go remote", runtime: "go", remote: true, want: "pack"}, - {name: "node local", runtime: "node", remote: false, want: "pack"}, - {name: "node remote", runtime: "node", remote: true, want: "pack"}, - {name: "typescript local", runtime: "typescript", remote: false, want: "pack"}, - {name: "typescript remote", runtime: "typescript", remote: true, want: "pack"}, - {name: "rust local", runtime: "rust", remote: false, want: "pack"}, - {name: "rust remote", runtime: "rust", remote: true, want: "pack"}, - {name: "quarkus local", runtime: "quarkus", remote: false, want: "pack"}, - {name: "quarkus remote", runtime: "quarkus", remote: true, want: "pack"}, - {name: "springboot local", runtime: "springboot", remote: false, want: "pack"}, - {name: "springboot remote", runtime: "springboot", remote: true, want: "pack"}, - {name: "python local", runtime: "python", remote: false, want: "host"}, - {name: "python remote", runtime: "python", remote: true, want: "s2i"}, - {name: "unknown runtime", runtime: "fortran", remote: false, wantErr: true}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got, err := resolveBuilder(tc.runtime, tc.remote) - if tc.wantErr { - assert.Assert(t, err != nil, "expected an error for runtime %q", tc.runtime) - return - } - assert.NilError(t, err) - assert.Equal(t, got, tc.want) - }) - } -} diff --git a/cmd/ci/workflow.go b/cmd/ci/workflow.go deleted file mode 100644 index fe4d546643..0000000000 --- a/cmd/ci/workflow.go +++ /dev/null @@ -1,225 +0,0 @@ -package ci - -import ( - "bytes" - "errors" - "fmt" - "io" - - "gopkg.in/yaml.v3" -) - -const defaultFuncCliVersion = "knative-v1.21.0" - -// ErrWorkflowExists is returned when a GitHub workflow file already exists and --force is not specified. -var ErrWorkflowExists = errors.New("existing GitHub workflow detected, overwrite using the --force option") - -type githubWorkflow struct { - Name string `yaml:"name"` - On workflowTriggers `yaml:"on"` - Jobs map[string]job `yaml:"jobs"` -} - -type workflowTriggers struct { - Push *pushTrigger `yaml:"push,omitempty"` - WorkflowDispatch *struct{} `yaml:"workflow_dispatch,omitempty"` -} - -type pushTrigger struct { - Branches []string `yaml:"branches,omitempty"` -} - -type job struct { - RunsOn string `yaml:"runs-on"` - Steps []step `yaml:"steps"` -} - -type step struct { - Name string `yaml:"name,omitempty"` - Env map[string]string `yaml:"env,omitempty"` - Uses string `yaml:"uses,omitempty"` - Run string `yaml:"run,omitempty"` - With map[string]string `yaml:"with,omitempty"` -} - -func NewGitHubWorkflow(conf CIConfig, messageWriter io.Writer) *githubWorkflow { - var steps []step - steps = createCheckoutStep(steps) - steps = createRuntimeTestStep(conf, messageWriter, steps) - steps = createK8ContextStep(conf, steps) - steps = createRegistryLoginStep(conf, steps) - steps = createFuncCLIInstallStep(steps) - - steps = createFuncDeployStep(conf, steps) - - return &githubWorkflow{ - Name: conf.WorkflowName(), - On: createPushTrigger(conf), - Jobs: map[string]job{ - "deploy": { - RunsOn: determineRunner(conf.SelfHostedRunner()), - Steps: steps, - }, - }, - } -} - -func createCheckoutStep(steps []step) []step { - checkoutCode := newStep("Checkout code"). - withUses("actions/checkout@v4") - - return append(steps, *checkoutCode) -} - -func createRuntimeTestStep(conf CIConfig, messageWriter io.Writer, steps []step) []step { - if !conf.TestStep() { - return steps - } - - testStep := newStep("Run tests") - - switch conf.FnRuntime() { - case "go": - testStep.withRun("go test ./...") - case "node", "typescript": - testStep.withRun("npm ci && npm test") - case "python": - testStep.withRun("pip install . && python -m pytest") - case "quarkus": - testStep.withRun("./mvnw test") - default: - // best-effort user message; errors are non-critical - _, _ = fmt.Fprintf(messageWriter, "WARNING: test step not supported for runtime %s\n", conf.FnRuntime()) - return steps - } - - return append(steps, *testStep) -} - -func createK8ContextStep(conf CIConfig, steps []step) []step { - setupK8Context := newStep("Setup Kubernetes context"). - withUses("azure/k8s-set-context@v4"). - withActionConfig("method", "kubeconfig"). - withActionConfig("kubeconfig", newSecret(conf.KubeconfigSecret())) - - return append(steps, *setupK8Context) -} - -func createRegistryLoginStep(conf CIConfig, steps []step) []step { - if !conf.RegistryLogin() { - return steps - } - - loginToContainerRegistry := newStep("Login to container registry"). - withUses("docker/login-action@v3"). - withActionConfig("registry", newVariable(conf.RegistryLoginUrlVar())). - withActionConfig("username", newVariable(conf.RegistryUserVar())). - withActionConfig("password", newSecret(conf.RegistryPassSecret())) - - return append(steps, *loginToContainerRegistry) -} - -func createFuncCLIInstallStep(steps []step) []step { - installFuncCli := newStep("Install func cli"). - withUses("functions-dev/action@main"). - withActionConfig("version", defaultFuncCliVersion). - withActionConfig("name", "func") - - return append(steps, *installFuncCli) -} - -func createFuncDeployStep(conf CIConfig, steps []step) []step { - deployFuncStep := newStep("Deploy function"). - withEnv("FUNC_VERBOSE", "true"). - withEnv("FUNC_BUILDER", conf.FnBuilder()) - - if conf.RemoteBuild() { - deployFuncStep.withEnv("FUNC_REMOTE", "true") - } - - registryUrl := newVariable(conf.RegistryUrlVar()) - if conf.RegistryLogin() { - registryUrl = newVariable(conf.RegistryLoginUrlVar()) + "/" + newVariable(conf.RegistryUserVar()) - } - deployFuncStep.withEnv("FUNC_REGISTRY", registryUrl). - withRun("func deploy") - - return append(steps, *deployFuncStep) -} - -func createPushTrigger(conf CIConfig) workflowTriggers { - result := workflowTriggers{ - Push: &pushTrigger{Branches: []string{conf.Branch()}}, - } - - if conf.WorkflowDispatch() { - result.WorkflowDispatch = &struct{}{} - } - - return result -} - -func newStep(name string) *step { - return &step{Name: name} -} - -func (s *step) withUses(u string) *step { - s.Uses = u - return s -} - -func (s *step) withRun(r string) *step { - s.Run = r - return s -} - -func (s *step) withActionConfig(key, value string) *step { - if s.With == nil { - s.With = make(map[string]string) - } - - s.With[key] = value - - return s -} - -func (s *step) withEnv(key, value string) *step { - if s.Env == nil { - s.Env = make(map[string]string) - } - - s.Env[key] = value - - return s -} - -func (gw *githubWorkflow) Export(path string, w WorkflowWriter, force bool, m io.Writer) error { - if !force && w.Exist(path) { - return ErrWorkflowExists - } - - if w.Exist(path) { - // best-effort user message; errors are non-critical - _, _ = fmt.Fprintf(m, "WARNING: --force flag is set, overwriting existing GitHub Workflow file\n") - } - - raw, err := gw.toYaml() - if err != nil { - return err - } - - return w.Write(path, raw) -} - -func (gw *githubWorkflow) toYaml() ([]byte, error) { - var buf bytes.Buffer - encoder := yaml.NewEncoder(&buf) - encoder.SetIndent(2) - - if err := encoder.Encode(gw); err != nil { - return nil, err - } - encoder.Close() - - return buf.Bytes(), nil -} diff --git a/cmd/ci/workflow_test.go b/cmd/ci/workflow_test.go deleted file mode 100644 index 8b5d3a551f..0000000000 --- a/cmd/ci/workflow_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package ci_test - -import ( - "bytes" - "strings" - "testing" - - "github.com/ory/viper" - "gotest.tools/v3/assert" - "knative.dev/func/cmd/ci" - "knative.dev/func/cmd/common" - fn "knative.dev/func/pkg/functions" -) - -func TestGitHubWorkflow_Export(t *testing.T) { - // GIVEN - viper.Set("platform", "github") - t.Cleanup(func() { viper.Reset() }) - loaderSaver := common.NewMockLoaderSaver() - loaderSaver.LoadFn = func(path string) (fn.Function, error) { - return fn.Function{Root: path, Runtime: "go"}, nil - } - bufferWriter := ci.NewBufferWriter() - - // WHEN - cfg, configErr := ci.NewCIConfig( - loaderSaver, - common.CurrentBranchStub("", nil), - common.WorkDirStub("", nil), - false, - ) - assert.NilError(t, configErr, "unexpected error when creating CIConfig") - - gw := ci.NewGitHubWorkflow(cfg, &bytes.Buffer{}) - exportErr := gw.Export(cfg.FnGitHubWorkflowFilepath(), bufferWriter, true, &bytes.Buffer{}) - - // THEN - assert.NilError(t, exportErr, "unexpected error when exporting GitHub Workflow") - assert.Assert(t, strings.Contains(bufferWriter.Buffer.String(), gw.Name)) -} diff --git a/cmd/config.go b/cmd/config.go index 2dccd67560..f5e1615728 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -8,17 +8,18 @@ import ( "github.com/ory/viper" "github.com/spf13/cobra" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" + "knative.dev/func/pkg/ci/github" "knative.dev/func/pkg/config" fn "knative.dev/func/pkg/functions" ) func NewConfigCmd( loaderSaver common.FunctionLoaderSaver, - writer ci.WorkflowWriter, + workflowWriter github.WorkflowWriter, currentBranch common.CurrentBranchFunc, workingDir common.WorkDirFunc, + newCIGenerator ciGeneratorFactory, newClient ClientFactory, ) *cobra.Command { cmd := &cobra.Command{ @@ -47,8 +48,15 @@ or from the directory specified with --path. cmd.AddCommand(NewConfigEnvsCmd(loaderSaver)) cmd.AddCommand(NewConfigVolumesCmd()) - if os.Getenv(ci.ConfigCIFeatureFlag) == "true" { - cmd.AddCommand(NewConfigCICmd(loaderSaver, writer, currentBranch, workingDir)) + if os.Getenv(ConfigCIFeatureFlag) == "true" { + cmd.AddCommand(NewConfigCICmd( + loaderSaver, + workflowWriter, + currentBranch, + workingDir, + newCIGenerator, + newClient, + )) } return cmd diff --git a/cmd/config_ci.go b/cmd/config_ci.go index 8d1a8b0b78..2c51dbd803 100644 --- a/cmd/config_ci.go +++ b/cmd/config_ci.go @@ -1,54 +1,86 @@ package cmd import ( + "fmt" "io" + "strings" "github.com/ory/viper" "github.com/spf13/cobra" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" + "knative.dev/func/pkg/ci/github" + fn "knative.dev/func/pkg/functions" +) + +// ciGeneratorFactory creates a CIGenerator from resolved CLI flag values. +// Using a factory allows tests to capture the resolved config and inject +// a mock generator without running the real implementation. +type ciGeneratorFactory func(github.WorkflowConfig, github.WorkflowWriter, io.Writer, bool) fn.CIGenerator + +const ( + ConfigCIFeatureFlag = "FUNC_ENABLE_CI_CONFIG" + pathFlag = "path" + platformFlag = "platform" + branchFlag = "branch" + workflowNameFlag = "workflow-name" + kubeconfigSecretNameFlag = "kubeconfig-secret-name" + registryLoginUrlVariableNameFlag = "registry-login-url-variable-name" + registryUserVariableNameFlag = "registry-user-variable-name" + registryPassSecretNameFlag = "registry-pass-secret-name" + registryUrlVariableNameFlag = "registry-url-variable-name" + registryLoginFlag = "registry-login" + workflowDispatchFlag = "workflow-dispatch" + remoteBuildFlag = "remote" + selfHostedRunnerFlag = "self-hosted-runner" + testStepFlag = "test-step" + forceFlag = "force" + verboseFlag = "verbose" ) func NewConfigCICmd( loaderSaver common.FunctionLoaderSaver, - writer ci.WorkflowWriter, + workflowWriter github.WorkflowWriter, currentBranch common.CurrentBranchFunc, workingDir common.WorkDirFunc, + newCIGenerator ciGeneratorFactory, + newClient ClientFactory, ) *cobra.Command { cmd := &cobra.Command{ Use: "ci", Short: "Generate a GitHub Workflow for function deployment", PreRunE: bindEnv( - ci.PathFlag, - ci.PlatformFlag, - ci.RegistryLoginFlag, - ci.WorkflowNameFlag, - ci.KubeconfigSecretNameFlag, - ci.RegistryLoginUrlVariableNameFlag, - ci.RegistryUserVariableNameFlag, - ci.RegistryPassSecretNameFlag, - ci.RegistryUrlVariableNameFlag, - ci.WorkflowDispatchFlag, - ci.RemoteBuildFlag, - ci.SelfHostedRunnerFlag, - ci.TestStepFlag, - ci.BranchFlag, - ci.ForceFlag, - ci.VerboseFlag, + pathFlag, + platformFlag, + registryLoginFlag, + workflowNameFlag, + kubeconfigSecretNameFlag, + registryLoginUrlVariableNameFlag, + registryUserVariableNameFlag, + registryPassSecretNameFlag, + registryUrlVariableNameFlag, + workflowDispatchFlag, + remoteBuildFlag, + selfHostedRunnerFlag, + testStepFlag, + branchFlag, + forceFlag, + verboseFlag, ), RunE: func(cmd *cobra.Command, args []string) (err error) { // Detect explicit config via CLI flag or env var workflowNameExplicit := - cmd.Flags().Changed(ci.WorkflowNameFlag) || viper.IsSet(ci.WorkflowNameFlag) + cmd.Flags().Changed(workflowNameFlag) || viper.IsSet(workflowNameFlag) return runConfigCIGitHub( + cmd, + workflowWriter, loaderSaver, - writer, currentBranch, workingDir, - cmd.OutOrStdout(), workflowNameExplicit, + newCIGenerator, + newClient, ) }, } @@ -56,120 +88,248 @@ func NewConfigCICmd( addPathFlag(cmd) cmd.Flags().String( - ci.PlatformFlag, - ci.DefaultPlatform, + platformFlag, + github.DefaultPlatform, "Pick a CI/CD platform for which a manifest will be generated. Currently only GitHub is supported.", ) cmd.Flags().String( - ci.BranchFlag, + branchFlag, "", "Use a custom branch name in the workflow", ) cmd.Flags().String( - ci.WorkflowNameFlag, - ci.DefaultWorkflowName, + workflowNameFlag, + github.DefaultWorkflowName, "Use a custom workflow name", ) cmd.Flags().String( - ci.KubeconfigSecretNameFlag, - ci.DefaultKubeconfigSecretName, + kubeconfigSecretNameFlag, + github.DefaultKubeconfigSecretName, "Use a custom secret name in the workflow, e.g. secret.YOUR_CUSTOM_KUBECONFIG", ) cmd.Flags().String( - ci.RegistryLoginUrlVariableNameFlag, - ci.DefaultRegistryLoginUrlVariableName, + registryLoginUrlVariableNameFlag, + github.DefaultRegistryLoginUrlVariableName, "Use a custom registry login url variable name in the workflow, e.g. vars.YOUR_REGISTRY_LOGIN_URL", ) cmd.Flags().String( - ci.RegistryUserVariableNameFlag, - ci.DefaultRegistryUserVariableName, + registryUserVariableNameFlag, + github.DefaultRegistryUserVariableName, "Use a custom registry user variable name in the workflow, e.g. vars.YOUR_REGISTRY_USER", ) cmd.Flags().String( - ci.RegistryPassSecretNameFlag, - ci.DefaultRegistryPassSecretName, + registryPassSecretNameFlag, + github.DefaultRegistryPassSecretName, "Use a custom registry pass secret name in the workflow, e.g. secret.YOUR_REGISTRY_PASSWORD", ) cmd.Flags().String( - ci.RegistryUrlVariableNameFlag, - ci.DefaultRegistryUrlVariableName, + registryUrlVariableNameFlag, + github.DefaultRegistryUrlVariableName, "Use a custom registry url variable name in the workflow, e.g. vars.YOUR_REGISTRY_URL", ) cmd.Flags().Bool( - ci.RegistryLoginFlag, - ci.DefaultRegistryLogin, + registryLoginFlag, + github.DefaultRegistryLogin, "Add a registry login step in the github workflow", ) cmd.Flags().Bool( - ci.WorkflowDispatchFlag, - ci.DefaultWorkflowDispatch, + workflowDispatchFlag, + github.DefaultWorkflowDispatch, "Add a workflow dispatch trigger for manual workflow execution", ) - _ = cmd.Flags().MarkHidden(ci.WorkflowDispatchFlag) + _ = cmd.Flags().MarkHidden(workflowDispatchFlag) cmd.Flags().Bool( - ci.RemoteBuildFlag, - ci.DefaultRemoteBuild, + remoteBuildFlag, + github.DefaultRemoteBuild, "Build the function on a Tekton-enabled cluster", ) cmd.Flags().Bool( - ci.SelfHostedRunnerFlag, - ci.DefaultSelfHostedRunner, + selfHostedRunnerFlag, + github.DefaultSelfHostedRunner, "Use a 'self-hosted' runner instead of the default 'ubuntu-latest' for local runner execution", ) cmd.Flags().Bool( - ci.TestStepFlag, - ci.DefaultTestStep, + testStepFlag, + github.DefaultTestStep, "Add a language-specific test step (supported: go, node, typescript, python, quarkus)", ) cmd.Flags().Bool( - ci.ForceFlag, - ci.DefaultForce, + forceFlag, + github.DefaultForce, "Use to overwrite an existing GitHub workflow", ) - addVerboseFlag(cmd, ci.DefaultVerbose) + addVerboseFlag(cmd, github.DefaultVerbose) return cmd } func runConfigCIGitHub( + cmd *cobra.Command, + workflowWriter github.WorkflowWriter, fnLoaderSaver common.FunctionLoaderSaver, - writer ci.WorkflowWriter, currentBranch common.CurrentBranchFunc, workingDir common.WorkDirFunc, - messageWriter io.Writer, workflowNameExplicit bool, + newCIGenerator ciGeneratorFactory, + newClient ClientFactory, ) error { - cfg, err := ci.NewCIConfig(fnLoaderSaver, currentBranch, workingDir, workflowNameExplicit) + cfg, f, err := newConfigAndLoadedFunc( + fnLoaderSaver, + currentBranch, + workingDir, + workflowNameExplicit, + ) if err != nil { return err } - githubWorkflow := ci.NewGitHubWorkflow(cfg, messageWriter) - if err := githubWorkflow.Export(cfg.FnGitHubWorkflowFilepath(), writer, cfg.Force(), messageWriter); err != nil { + verbose := viper.GetBool(verboseFlag) + ciGenerator := newCIGenerator(cfg, workflowWriter, cmd.OutOrStdout(), verbose) + + client, done := newClient( + ClientConfig{Verbose: verbose}, + fn.WithCIGenerator(ciGenerator), + ) + defer done() + + if err := client.GenerateCIWorkflow(cmd.Context(), f); err != nil { return err } - if cfg.Verbose() { - // best-effort user message; errors are non-critical - _ = ci.PrintConfiguration(messageWriter, cfg) - return nil + return nil +} + +func newConfigAndLoadedFunc( + fnLoader common.FunctionLoader, + currentBranch common.CurrentBranchFunc, + workingDir common.WorkDirFunc, + workflowNameExplicit bool, +) (github.WorkflowConfig, fn.Function, error) { + if err := resolvePlatform(); err != nil { + return github.WorkflowConfig{}, fn.Function{}, err + } + + path, err := resolvePath(workingDir) + if err != nil { + return github.WorkflowConfig{}, fn.Function{}, err + } + + branch, err := resolveBranch(path, currentBranch) + if err != nil { + return github.WorkflowConfig{}, fn.Function{}, err + } + + workflowName := resolveWorkflowName(workflowNameExplicit) + + f, err := fnLoader.Load(path) + if err != nil { + return github.WorkflowConfig{}, fn.Function{}, err + } + + return github.WorkflowConfig{ + GithubWorkflowDir: github.DefaultGitHubWorkflowDir, + GithubWorkflowFilename: github.DefaultGitHubWorkflowFilename, + Branch: branch, + WorkflowName: workflowName, + KubeconfigSecret: viper.GetString(kubeconfigSecretNameFlag), + RegistryLoginUrlVar: viper.GetString(registryLoginUrlVariableNameFlag), + RegistryUserVar: viper.GetString(registryUserVariableNameFlag), + RegistryPassSecret: viper.GetString(registryPassSecretNameFlag), + RegistryUrlVar: viper.GetString(registryUrlVariableNameFlag), + RegistryLogin: viper.GetBool(registryLoginFlag), + SelfHostedRunner: viper.GetBool(selfHostedRunnerFlag), + RemoteBuild: viper.GetBool(remoteBuildFlag), + WorkflowDispatch: viper.GetBool(workflowDispatchFlag), + TestStep: viper.GetBool(testStepFlag), + Force: viper.GetBool(forceFlag), + }, f, nil +} + +func resolvePlatform() error { + platform := viper.GetString(platformFlag) + if platform == "" { + return fmt.Errorf("platform must not be empty, supported: %s", github.DefaultPlatform) + } + if strings.ToLower(platform) != github.DefaultPlatform { + return fmt.Errorf("%s support is not implemented, supported: %s", platform, github.DefaultPlatform) } - // best-effort user message; errors are non-critical - _ = ci.PrintPostExportMessage(messageWriter, cfg) return nil } + +func resolvePath(workingDir common.WorkDirFunc) (string, error) { + path := viper.GetString(pathFlag) + if path != "" && path != "." { + return path, nil + } + + cwd, err := workingDir() + if err != nil { + return "", err + } + + return cwd, nil +} + +func resolveBranch(path string, currentBranch common.CurrentBranchFunc) (string, error) { + branch := viper.GetString(branchFlag) + if branch != "" { + return branch, nil + } + + branch, err := currentBranch(path) + if err != nil { + return "", err + } + + return branch, nil +} + +func resolveWorkflowName(explicit bool) string { + workflowName := viper.GetString(workflowNameFlag) + if explicit { + return workflowName + } + + if viper.GetBool(remoteBuildFlag) { + return github.DefaultRemoteBuildWorkflowName + } + + return github.DefaultWorkflowName +} + +// NewCIGeneratorFactory returns the default production factory that +// constructs a github.WorkflowGenerator from resolved flag values. +func NewCIGeneratorFactory() ciGeneratorFactory { + return func(cfg github.WorkflowConfig, ww github.WorkflowWriter, mw io.Writer, v bool) fn.CIGenerator { + return github.NewWorkflowGenerator( + github.WithWorkflowConfig(cfg), + github.WithWorkflowWriter(ww), + github.WithMessageWriter(mw), + github.WithVerbose(v), + ) + } +} + +// NewTestCIGeneratorFactory returns a test factory that captures the resolved +// WorkflowConfig and returns the given mock as the CIGenerator. +func NewTestCIGeneratorFactory(mock *github.WorkflowGeneratorMock) ciGeneratorFactory { + return func(cfg github.WorkflowConfig, _ github.WorkflowWriter, _ io.Writer, _ bool) fn.CIGenerator { + mock.Config = cfg + return mock + } +} diff --git a/cmd/config_ci_int_test.go b/cmd/config_ci_int_test.go index a097f637e1..e606eec6a9 100644 --- a/cmd/config_ci_int_test.go +++ b/cmd/config_ci_int_test.go @@ -2,6 +2,7 @@ package cmd_test import ( "errors" + "fmt" "os" "path/filepath" "slices" @@ -10,10 +11,11 @@ import ( "github.com/ory/viper" "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" fnCmd "knative.dev/func/cmd" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" cmdTest "knative.dev/func/cmd/testing" + "knative.dev/func/pkg/ci/github" fn "knative.dev/func/pkg/functions" fnTest "knative.dev/func/pkg/testing" ) @@ -70,7 +72,7 @@ func TestNewConfigCICmd_CreatesGitHubWorkflowDirectory(t *testing.T) { err := runConfigCiCmdIntegration(t, opts) assert.NilError(t, err) - _, statErr := os.Stat(filepath.Join(opts.withFunc.Root, ci.DefaultGitHubWorkflowDir)) + _, statErr := os.Stat(filepath.Join(opts.withFunc.Root, github.DefaultGitHubWorkflowDir)) assert.NilError(t, statErr) } @@ -81,7 +83,7 @@ func TestNewConfigCICmd_WritesWorkflowFileToFSWithCorrectYAMLStructure(t *testin raw := readWorkflowFile(t, opts.withFunc.Root) assert.NilError(t, err) - assertDefaultWorkflowWithBranch(t, raw, mainBranch) + assertDefaultWorkflow(t, raw) } func TestNewConfigCICmd_ForceFlagOverwritesExistingWorkflowOnFS(t *testing.T) { @@ -106,7 +108,7 @@ func TestNewConfigCICmd_ForceFlagOverwritesExistingWorkflowOnFS(t *testing.T) { err := runConfigCiCmdIntegration(t, opts) content := readWorkflowFile(t, opts.withFunc.Root) - assert.ErrorIs(t, err, ci.ErrWorkflowExists) + assert.ErrorIs(t, err, github.ErrWorkflowExists) assert.Assert(t, yamlContains(content, workflowName)) assert.Assert(t, !strings.Contains(content, changedWorkflowName)) }) @@ -131,6 +133,8 @@ func TestNewConfigCICmd_ForceFlagOverwritesExistingWorkflowOnFS(t *testing.T) { // START: Testing Framework // ------------------------ +const fnName = "github-ci-func" + type optsIntegration struct { withFunc *fn.Function args []string @@ -165,7 +169,7 @@ func runConfigCiCmdIntegration( // PRE-RUN PREP // all options for "func config ci" command - t.Setenv(ci.ConfigCIFeatureFlag, "true") + t.Setenv(fnCmd.ConfigCIFeatureFlag, "true") args := opts.args if len(opts.args) == 0 { @@ -176,9 +180,10 @@ func runConfigCiCmdIntegration( cmd := fnCmd.NewConfigCmd( common.DefaultLoaderSaver, - ci.DefaultWorkflowWriter, + github.DefaultWorkflowWriter, common.DefaultCurrentBranch, common.DefaultWorkDir, + fnCmd.NewCIGeneratorFactory(), fnCmd.NewClient, ) cmd.SetArgs(args) @@ -190,12 +195,61 @@ func runConfigCiCmdIntegration( func readWorkflowFile(t *testing.T, root string) string { t.Helper() - path := filepath.Join(root, ci.DefaultGitHubWorkflowDir, ci.DefaultGitHubWorkflowFilename) + path := filepath.Join(root, github.DefaultGitHubWorkflowDir, github.DefaultGitHubWorkflowFilename) result, err := os.ReadFile(path) assert.NilError(t, err) return string(result) } +func assertDefaultWorkflow(t *testing.T, actualGw string) { + t.Helper() + + assert.Assert(t, yamlContains(actualGw, "Func Deploy")) + assert.Assert(t, yamlContains(actualGw, "- main")) + + assert.Assert(t, yamlContains(actualGw, "ubuntu-latest")) + + assert.Assert(t, strings.Count(actualGw, "- name:") == 6) + + assert.Assert(t, yamlContains(actualGw, "Checkout code")) + assert.Assert(t, yamlContains(actualGw, "actions/checkout@v4")) + + assert.Assert(t, yamlContains(actualGw, "Run tests")) + assert.Assert(t, yamlContains(actualGw, "go test ./...")) + + assert.Assert(t, yamlContains(actualGw, "Setup Kubernetes context")) + assert.Assert(t, yamlContains(actualGw, "azure/k8s-set-context@v4")) + assert.Assert(t, yamlContains(actualGw, "method: kubeconfig")) + assert.Assert(t, yamlContains(actualGw, "kubeconfig: ${{ secrets.KUBECONFIG }}")) + + assert.Assert(t, yamlContains(actualGw, "Login to container registry")) + assert.Assert(t, yamlContains(actualGw, "docker/login-action@v3")) + assert.Assert(t, yamlContains(actualGw, "registry: ${{ vars.REGISTRY_LOGIN_URL }}")) + assert.Assert(t, yamlContains(actualGw, "username: ${{ vars.REGISTRY_USERNAME }}")) + assert.Assert(t, yamlContains(actualGw, "password: ${{ secrets.REGISTRY_PASSWORD }}")) + + assert.Assert(t, yamlContains(actualGw, "Install func cli")) + assert.Assert(t, yamlContains(actualGw, "functions-dev/action@main")) + assert.Assert(t, yamlContains(actualGw, "version: knative-v1.22.0")) + assert.Assert(t, yamlContains(actualGw, "name: func")) + + assert.Assert(t, yamlContains(actualGw, "Deploy function")) + assert.Assert(t, yamlContains(actualGw, `FUNC_VERBOSE: "true"`)) + assert.Assert(t, yamlContains(actualGw, "FUNC_REGISTRY: ${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }}")) + assert.Assert(t, yamlContains(actualGw, "func deploy")) +} + +func yamlContains(yaml, substr string) cmp.Comparison { + return func() cmp.Result { + if strings.Contains(yaml, substr) { + return cmp.ResultSuccess + } + return cmp.ResultFailure(fmt.Sprintf( + "missing '%s' in:\n\n%s", substr, yaml, + )) + } +} + // ---------------------- // END: Testing Framework diff --git a/cmd/config_ci_test.go b/cmd/config_ci_test.go index 3eb560f183..459f388294 100644 --- a/cmd/config_ci_test.go +++ b/cmd/config_ci_test.go @@ -1,7 +1,6 @@ package cmd_test import ( - "bytes" "fmt" "path/filepath" "strings" @@ -9,18 +8,12 @@ import ( "github.com/ory/viper" "gotest.tools/v3/assert" - "gotest.tools/v3/assert/cmp" fnCmd "knative.dev/func/cmd" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" + "knative.dev/func/pkg/ci/github" fn "knative.dev/func/pkg/functions" ) -// START: Broad Unit Tests -// ----------------------- -// Execution is tested starting from the entrypoint "func config ci" including -// all components working together. Infrastructure components like the -// filesystem are mocked. func TestNewConfigCICmd_RequiresFeatureFlag(t *testing.T) { opts := defaultOpts() opts.enableFeature = false @@ -28,6 +21,7 @@ func TestNewConfigCICmd_RequiresFeatureFlag(t *testing.T) { result := runConfigCiCmd(t, opts) assert.ErrorContains(t, result.executeErr, "unknown command \"ci\" for \"config\"") + assert.Equal(t, result.generatorWasInvoked, false) } func TestNewConfigCICmd_CISubcommandExist(t *testing.T) { @@ -38,39 +32,7 @@ func TestNewConfigCICmd_CISubcommandExist(t *testing.T) { result := runConfigCiCmd(t, opts) assert.NilError(t, result.executeErr) -} - -func TestNewConfigCICmd_WritesWorkflowFile(t *testing.T) { - result := runConfigCiCmd(t, defaultOpts()) - - assert.NilError(t, result.executeErr) - assert.Assert(t, result.gwYamlString != "") -} - -func TestNewConfigCICmd_WorkflowYAMLHasCorrectStructure(t *testing.T) { - result := runConfigCiCmd(t, defaultOpts()) - - assert.NilError(t, result.executeErr) - assertDefaultWorkflow(t, result.gwYamlString) -} - -func TestNewConfigCICmd_WorkflowYAMLHasCustomValues(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.args = append(opts.args, - "--self-hosted-runner", - "--kubeconfig-secret-name=DEV_CLUSTER_KUBECONFIG", - "--registry-login-url-variable-name=DEV_REGISTRY_LOGIN_URL", - "--registry-user-variable-name=DEV_REGISTRY_USER", - "--registry-pass-secret-name=DEV_REGISTRY_PASS", - ) - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assertCustomWorkflow(t, result.gwYamlString) + assert.Equal(t, result.generatorWasInvoked, true) } func TestNewConfigCICmd_WorkflowNameResolution(t *testing.T) { @@ -82,12 +44,12 @@ func TestNewConfigCICmd_WorkflowNameResolution(t *testing.T) { { name: "default workflow name when no flags", args: nil, - expectedWorkflowName: ci.DefaultWorkflowName, + expectedWorkflowName: github.DefaultWorkflowName, }, { name: "remote build uses remote default workflow name", args: []string{"--remote"}, - expectedWorkflowName: ci.DefaultRemoteBuildWorkflowName, + expectedWorkflowName: github.DefaultRemoteBuildWorkflowName, }, { name: "custom name is preserved without remote", @@ -101,8 +63,8 @@ func TestNewConfigCICmd_WorkflowNameResolution(t *testing.T) { }, { name: "custom name is preserved if its equal to default workflow name and remote is set", - args: []string{"--workflow-name=" + ci.DefaultWorkflowName, "--remote"}, - expectedWorkflowName: ci.DefaultWorkflowName, + args: []string{"--workflow-name=" + github.DefaultWorkflowName, "--remote"}, + expectedWorkflowName: github.DefaultWorkflowName, }, } @@ -117,7 +79,7 @@ func TestNewConfigCICmd_WorkflowNameResolution(t *testing.T) { // THEN assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, "name: "+tc.expectedWorkflowName)) + assert.Equal(t, result.workflowConfig.WorkflowName, tc.expectedWorkflowName) }) } } @@ -134,49 +96,7 @@ func TestNewConfigCICmd_WorkflowNameFromEnvVarPreserveWithRemote(t *testing.T) { // THEN assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, "name: "+customWorkflowName)) -} - -func TestNewConfigCICmd_WorkflowHasNoRegistryLogin(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.args = append(opts.args, "--registry-login=false") - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, !strings.Contains(result.gwYamlString, "docker/login-action@v3")) - assert.Assert(t, !strings.Contains(result.gwYamlString, "Login to container registry")) - assert.Assert(t, yamlContains(result.gwYamlString, "FUNC_REGISTRY: ${{ vars.REGISTRY_URL }}")) -} - -func TestNewConfigCICmd_RemoteBuildAndDeployWorkflow(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.args = append(opts.args, "--remote") - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, "Remote Func Deploy")) - assert.Assert(t, yamlContains(result.gwYamlString, `FUNC_REMOTE: "true"`)) -} - -func TestNewConfigCICmd_HasWorkflowDispatch(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.args = append(opts.args, "--workflow-dispatch") - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, "workflow_dispatch")) + assert.Equal(t, result.workflowConfig.WorkflowName, customWorkflowName) } func TestNewConfigCICmd_PathFlagResolution(t *testing.T) { @@ -188,25 +108,21 @@ func TestNewConfigCICmd_PathFlagResolution(t *testing.T) { testCases := []struct { name string pathArg string // empty means no --path flag - getwdReturn string expectedPath string }{ { name: "empty path uses cwd", pathArg: "", - getwdReturn: cwd, expectedPath: cwd, }, { name: "dot path uses cwd", pathArg: "--path=.", - getwdReturn: cwd, expectedPath: cwd, }, { name: "explicit func path used as-is", pathArg: "--path=" + explicitFuncPath, - getwdReturn: cwd, expectedPath: explicitFuncPath, }, } @@ -216,14 +132,14 @@ func TestNewConfigCICmd_PathFlagResolution(t *testing.T) { // GIVEN opts := defaultOpts() opts.args = append(opts.args, tc.pathArg) - opts.withFakeGetCwdReturn.dir = tc.getwdReturn + opts.withFakeGetCwdReturn.dir = cwd // WHEN result := runConfigCiCmd(t, opts) // THEN assert.NilError(t, result.executeErr) - assert.Assert(t, strings.Contains(result.actualPath, tc.expectedPath)) + assert.Assert(t, strings.Contains(result.fnRoot, tc.expectedPath)) }) } } @@ -274,7 +190,7 @@ func TestNewConfigCICmd_BranchFlagResolution(t *testing.T) { // THEN assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, "- "+tc.expectedBranch)) + assert.Equal(t, result.workflowConfig.Branch, tc.expectedBranch) }) } } @@ -335,12 +251,12 @@ func TestNewConfigCICmd_PlatformFlagErrors(t *testing.T) { { name: "empty platform value", platformArg: "--platform=", - expectedErr: fmt.Sprintf("platform must not be empty, supported: %s", ci.DefaultPlatform), + expectedErr: fmt.Sprintf("platform must not be empty, supported: %s", github.DefaultPlatform), }, { name: "unsupported platform value", platformArg: "--platform=unsupported", - expectedErr: fmt.Sprintf("unsupported support is not implemented, supported: %s", ci.DefaultPlatform), + expectedErr: fmt.Sprintf("unsupported support is not implemented, supported: %s", github.DefaultPlatform), }, } @@ -359,420 +275,16 @@ func TestNewConfigCICmd_PlatformFlagErrors(t *testing.T) { } } -func TestNewConfigCICmd_ForceFlagOverwritesExistingWorkflow(t *testing.T) { - workflowName := "Func Deploy" - changedWorkflowName := "Sales Service Deployment" - sharedWriter := ci.NewBufferWriter() - - t.Run("initial workflow creation succeeds", func(t *testing.T) { - opts := defaultOpts() - opts.withWriter = sharedWriter - - result := runConfigCiCmd(t, opts) - - assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, workflowName)) - assert.Assert(t, !strings.Contains(result.stdOut, forceWarning)) - }) - - t.Run("overwrite without force flag fails", func(t *testing.T) { - opts := defaultOpts() - opts.withWriter = sharedWriter - opts.args = append(opts.args, "--workflow-name="+changedWorkflowName) - - result := runConfigCiCmd(t, opts) - - assert.ErrorIs(t, result.executeErr, ci.ErrWorkflowExists) - assert.Assert(t, yamlContains(result.gwYamlString, workflowName)) - assert.Assert(t, !strings.Contains(result.gwYamlString, changedWorkflowName)) - assert.Assert(t, !strings.Contains(result.stdOut, forceWarning)) - }) - - t.Run("overwrite with force flag succeeds and a warning message is printed to stdout", func(t *testing.T) { - opts := defaultOpts() - opts.withWriter = sharedWriter - opts.args = append(opts.args, "--workflow-name="+changedWorkflowName, "--force") - - result := runConfigCiCmd(t, opts) - - assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, changedWorkflowName)) - assert.Assert(t, !strings.Contains(result.gwYamlString, workflowName)) - assert.Assert(t, strings.Contains(result.stdOut, forceWarning)) - }) -} - -func TestNewConfigCICmd_VerboseFlagPrintsWorkflowDetails(t *testing.T) { - t.Run("verbose flag prints default Github Workflow configuration", func(t *testing.T) { - opts := defaultOpts() - opts.args = append(opts.args, "--verbose") - expectedMessage := fmt.Sprintf(ci.MainLayoutPlainText, - defaultOutputPath, - ci.DefaultWorkflowName, - issueBranch, - "host", - "disabled", - "ubuntu-latest", - "enabled", - "enabled", - "disabled", - "disabled", - ) + fmt.Sprintf(ci.RequireManyPlainText, - "secrets."+ci.DefaultKubeconfigSecretName, - "secrets."+ci.DefaultRegistryPassSecretName, - "vars."+ci.DefaultRegistryLoginUrlVariableName, - "vars."+ci.DefaultRegistryUserVariableName, - "vars."+ci.DefaultRegistryUrlVariableName, - ) - - result := runConfigCiCmd(t, opts) - - assertMessage(t, result, expectedMessage) - }) - - t.Run("verbose flag prints custom Github Workflow configuration", func(t *testing.T) { - opts := defaultOpts() - opts.args = append(opts.args, - "--verbose", - "--self-hosted-runner", - "--workflow-name=Deploy Checkout Service", - "--remote", - "--test-step=false", - "--workflow-dispatch", - "--force", - "--kubeconfig-secret-name=DEV_CLUSTER_KUBECONFIG", - "--registry-pass-secret-name=DEV_REGISTRY_PASS", - "--registry-login-url-variable-name=DEV_REGISTRY_LOGIN_URL", - "--registry-user-variable-name=DEV_REGISTRY_USER", - "--registry-url-variable-name=DEV_REGISTRY_URL", - ) - expectedMessage := fmt.Sprintf(ci.MainLayoutPlainText, - defaultOutputPath, - customWorkflowName, - issueBranch, - "pack", - "enabled", - "self-hosted", - "disabled", - "enabled", - "enabled", - "enabled", - ) + fmt.Sprintf(ci.RequireManyPlainText, - "secrets.DEV_CLUSTER_KUBECONFIG", - "secrets.DEV_REGISTRY_PASS", - "vars.DEV_REGISTRY_LOGIN_URL", - "vars.DEV_REGISTRY_USER", - "vars.DEV_REGISTRY_URL", - ) - - result := runConfigCiCmd(t, opts) - - assertMessage(t, result, expectedMessage) - }) - - t.Run("verbose flag prints custom Github Workflow configuration without registry login", func(t *testing.T) { - opts := defaultOpts() - opts.args = append(opts.args, - "--verbose", - "--registry-login=false", - ) - expectedMessage := fmt.Sprintf(ci.MainLayoutPlainText, - defaultOutputPath, - ci.DefaultWorkflowName, - issueBranch, - "host", - "disabled", - "ubuntu-latest", - "enabled", - "disabled", - "disabled", - "disabled", - ) + fmt.Sprintf(ci.RequireOnePlainText, - "secrets."+ci.DefaultKubeconfigSecretName, - ) - - result := runConfigCiCmd(t, opts) - - assertMessage(t, result, expectedMessage) - }) -} - -func TestNewConfigCICmd_PostExportMessageShown(t *testing.T) { - t.Run("a message is shown with all secrets and variables for k8 and registry which needs creation", func(t *testing.T) { - opts := defaultOpts() - expectedMessage := fmt.Sprintf(ci.PostExportManyPlainText, - defaultOutputPath, - "secrets."+ci.DefaultKubeconfigSecretName, - "secrets."+ci.DefaultRegistryPassSecretName, - "vars."+ci.DefaultRegistryLoginUrlVariableName, - "vars."+ci.DefaultRegistryUserVariableName, - "vars."+ci.DefaultRegistryUrlVariableName, - ) - - result := runConfigCiCmd(t, opts) - - assertMessage(t, result, expectedMessage) - }) - - t.Run("a message is shown with a secret for k8 which needs creation", func(t *testing.T) { - opts := defaultOpts() - opts.args = append(opts.args, "--registry-login=false") - expectedMessage := fmt.Sprintf(ci.PostExportOnePlainText, - defaultOutputPath, - "secrets."+ci.DefaultKubeconfigSecretName, - ) - - result := runConfigCiCmd(t, opts) - - assertMessage(t, result, expectedMessage) - }) -} - -func TestNewConfigCICmd_VerboseAndPostExportMessageAreMutuallyExclusive(t *testing.T) { - t.Run("verbose flag shows configuration, not post-export message", func(t *testing.T) { - opts := defaultOpts() - opts.args = append(opts.args, "--verbose") - - result := runConfigCiCmd(t, opts) - - assert.NilError(t, result.executeErr) - assert.Assert(t, strings.Contains(result.stdOut, "GitHub Workflow Configuration")) - assert.Assert(t, !strings.Contains(result.stdOut, "GitHub Workflow created at:")) - }) - - t.Run("without verbose flag shows post-export message, not configuration", func(t *testing.T) { - opts := defaultOpts() - - result := runConfigCiCmd(t, opts) - - assert.NilError(t, result.executeErr) - assert.Assert(t, strings.Contains(result.stdOut, "GitHub Workflow created at:")) - assert.Assert(t, !strings.Contains(result.stdOut, "GitHub Workflow Configuration")) - }) -} - -func TestNewConfigCICmd_TestStepPerRuntime(t *testing.T) { - testCases := []struct { - name string - runtime string - expectedRun string - }{ - { - name: "go runtime adds go test step", - runtime: "go", - expectedRun: "go test ./...", - }, - { - name: "nodejs runtime adds npm test step", - runtime: "node", - expectedRun: "npm ci && npm test", - }, - { - name: "typescript runtime adds npm test step", - runtime: "typescript", - expectedRun: "npm ci && npm test", - }, - { - name: "python runtime adds python -m pytest step", - runtime: "python", - expectedRun: "pip install . && python -m pytest", - }, - { - name: "quarkus runtime adds mvnw test step", - runtime: "quarkus", - expectedRun: "./mvnw test", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.runtime = tc.runtime - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, yamlContains(result.gwYamlString, runTestStepName)) - assert.Assert(t, yamlContains(result.gwYamlString, tc.expectedRun)) - }) - } -} - -func TestNewConfigCICmd_TestStepSkipped(t *testing.T) { - t.Run("unsupported runtime skips test step and prints warning", func(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.runtime = "rust" - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, !strings.Contains(result.gwYamlString, runTestStepName)) - assert.Assert(t, strings.Contains(result.stdOut, "WARNING: test step not supported for runtime rust")) - }) - - t.Run("test step disabled via flag", func(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.args = append(opts.args, "--test-step=false") - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, !strings.Contains(result.gwYamlString, runTestStepName)) - assert.Assert(t, strings.Count(result.gwYamlString, "- name:") == 5) - }) -} - -func TestNewConfigCICmd_BuilderForRuntime(t *testing.T) { - testCases := []struct { - name, - runtime, - builder, - args string - }{ - { - name: "go function and local build", - args: "", - runtime: "go", - builder: "host", - }, - { - name: "go function and remote build", - args: "--remote", - runtime: "go", - builder: "pack", - }, - { - name: "python function and local build", - args: "", - runtime: "python", - builder: "host", - }, - { - name: "python function and remote build", - args: "--remote", - runtime: "python", - builder: "s2i", - }, - { - name: "node function and local build", - args: "", - runtime: "node", - builder: "pack", - }, - { - name: "node function and remote build", - args: "--remote", - runtime: "node", - builder: "pack", - }, - { - name: "typescript function and local build", - args: "", - runtime: "typescript", - builder: "pack", - }, - { - name: "typescript function and remote build", - args: "--remote", - runtime: "typescript", - builder: "pack", - }, - { - name: "rust function and local build", - args: "", - runtime: "rust", - builder: "pack", - }, - { - name: "rust function and remote build", - args: "--remote", - runtime: "rust", - builder: "pack", - }, - { - name: "quarkus function and local build", - args: "", - runtime: "quarkus", - builder: "pack", - }, - { - name: "quarkus function and remote build", - args: "--remote", - runtime: "quarkus", - builder: "pack", - }, - { - name: "springboot function and local build", - args: "", - runtime: "springboot", - builder: "pack", - }, - { - name: "springboot function and remote build", - args: "--remote", - runtime: "springboot", - builder: "pack", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.runtime = tc.runtime - opts.args = append(opts.args, tc.args) - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.NilError(t, result.executeErr) - assert.Assert(t, strings.Contains(result.gwYamlString, "FUNC_BUILDER: "+tc.builder)) - }) - } -} - -func TestNewConfigCICmd_BuilderForRuntimeError(t *testing.T) { - // GIVEN - opts := defaultOpts() - opts.runtime = "zig" - expectedErr := fmt.Errorf("no builder support for runtime: %s", opts.runtime) - - // WHEN - result := runConfigCiCmd(t, opts) - - // THEN - assert.Error(t, result.executeErr, expectedErr.Error()) -} - -// --------------------- -// END: Broad Unit Tests - // START: Testing Framework // ------------------------ const ( mainBranch = "main" issueBranch = "issue-778-current-branch" - fnName = "github-ci-func" - forceWarning = "WARNING: --force flag is set, overwriting existing GitHub Workflow file" customWorkflowName = "Deploy Checkout Service" - runTestStepName = "Run tests" ) -var defaultOutputPath = filepath.Join(ci.DefaultGitHubWorkflowDir, ci.DefaultGitHubWorkflowFilename) - type opts struct { enableFeature bool - runtime string withFakeGitCliReturn struct { output string err error @@ -781,11 +293,10 @@ type opts struct { dir string err error } - withWriter *ci.BufferWriter - args []string + args []string } -// defaultOpts returns test options for broad unit tests with sensible defaults: +// defaultOpts returns test options for unit tests with sensible defaults: // - enableFeature: true // - withFakeGitCliReturn: {output: issueBranch, err: nil} // - withFakeGetCwdReturn: {dir: "", err: nil} @@ -793,7 +304,6 @@ type opts struct { func defaultOpts() opts { return opts{ enableFeature: true, - runtime: "go", withFakeGitCliReturn: struct { output string err error @@ -808,16 +318,15 @@ func defaultOpts() opts { dir: "", err: nil, }, - withWriter: nil, - args: []string{"ci"}, + args: []string{"ci"}, } } type result struct { - executeErr error - gwYamlString, - actualPath, - stdOut string + executeErr error + generatorWasInvoked bool + workflowConfig github.WorkflowConfig + fnRoot string } func runConfigCiCmd( @@ -829,17 +338,12 @@ func runConfigCiCmd( // PRE-RUN PREP // all options for "func config ci" command if opts.enableFeature { - t.Setenv(ci.ConfigCIFeatureFlag, "true") + t.Setenv(fnCmd.ConfigCIFeatureFlag, "true") } loaderSaver := common.NewMockLoaderSaver() loaderSaver.LoadFn = func(path string) (fn.Function, error) { - return fn.Function{Root: path, Runtime: opts.runtime}, nil - } - - writer := opts.withWriter - if writer == nil { - writer = ci.NewBufferWriter() + return fn.Function{Root: path, Runtime: "go"}, nil } currentBranch := common.CurrentBranchStub( @@ -852,19 +356,19 @@ func runConfigCiCmd( opts.withFakeGetCwdReturn.err, ) - messageBufferWriter := &bytes.Buffer{} + ciGeneratorFake := github.WorkflowGeneratorMock{} viper.Reset() cmd := fnCmd.NewConfigCmd( loaderSaver, - writer, + github.NewBufferWriter(), currentBranch, workingDir, + fnCmd.NewTestCIGeneratorFactory(&ciGeneratorFake), fnCmd.NewClient, ) cmd.SetArgs(opts.args) - cmd.SetOut(messageBufferWriter) // RUN err := cmd.Execute() @@ -872,92 +376,11 @@ func runConfigCiCmd( // POST-RUN GATHER return result{ err, - writer.Buffer.String(), - writer.Path, - messageBufferWriter.String(), + ciGeneratorFake.WasInvoked, + ciGeneratorFake.Config, + ciGeneratorFake.FnRoot, } } -// assertDefaultWorkflow asserts all the GitHub workflow value for correct values -// including the default values which can be changed: -// - runs-on: ubuntu-latest -// - kubeconfig: ${{ secrets.KUBECONFIG }} -// - registry: ${{ vars.REGISTRY_LOGIN_URL }}") -// - username: ${{ vars.REGISTRY_USERNAME }} -// - password: ${{ secrets.REGISTRY_PASSWORD }} -// - run: func deploy --registry=${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }} -v -func assertDefaultWorkflow(t *testing.T, actualGw string) { - t.Helper() - - assertDefaultWorkflowWithBranch(t, actualGw, issueBranch) -} - -func assertDefaultWorkflowWithBranch(t *testing.T, actualGw, branch string) { - t.Helper() - - assert.Assert(t, yamlContains(actualGw, "Func Deploy")) - assert.Assert(t, yamlContains(actualGw, "- "+branch)) - - assert.Assert(t, yamlContains(actualGw, "ubuntu-latest")) - - assert.Assert(t, strings.Count(actualGw, "- name:") == 6) - - assert.Assert(t, yamlContains(actualGw, "Checkout code")) - assert.Assert(t, yamlContains(actualGw, "actions/checkout@v4")) - - assert.Assert(t, yamlContains(actualGw, "Run tests")) - assert.Assert(t, yamlContains(actualGw, "go test ./...")) - - assert.Assert(t, yamlContains(actualGw, "Setup Kubernetes context")) - assert.Assert(t, yamlContains(actualGw, "azure/k8s-set-context@v4")) - assert.Assert(t, yamlContains(actualGw, "method: kubeconfig")) - assert.Assert(t, yamlContains(actualGw, "kubeconfig: ${{ secrets.KUBECONFIG }}")) - - assert.Assert(t, yamlContains(actualGw, "Login to container registry")) - assert.Assert(t, yamlContains(actualGw, "docker/login-action@v3")) - assert.Assert(t, yamlContains(actualGw, "registry: ${{ vars.REGISTRY_LOGIN_URL }}")) - assert.Assert(t, yamlContains(actualGw, "username: ${{ vars.REGISTRY_USERNAME }}")) - assert.Assert(t, yamlContains(actualGw, "password: ${{ secrets.REGISTRY_PASSWORD }}")) - - assert.Assert(t, yamlContains(actualGw, "Install func cli")) - assert.Assert(t, yamlContains(actualGw, "functions-dev/action@main")) - assert.Assert(t, yamlContains(actualGw, "version: knative-v1.21.0")) - assert.Assert(t, yamlContains(actualGw, "name: func")) - - assert.Assert(t, yamlContains(actualGw, "Deploy function")) - assert.Assert(t, yamlContains(actualGw, `FUNC_VERBOSE: "true"`)) - assert.Assert(t, yamlContains(actualGw, "FUNC_REGISTRY: ${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }}")) - assert.Assert(t, yamlContains(actualGw, "func deploy")) -} - -func yamlContains(yaml, substr string) cmp.Comparison { - return func() cmp.Result { - if strings.Contains(yaml, substr) { - return cmp.ResultSuccess - } - return cmp.ResultFailure(fmt.Sprintf( - "missing '%s' in:\n\n%s", substr, yaml, - )) - } -} - -func assertCustomWorkflow(t *testing.T, actualGw string) { - t.Helper() - - assert.Assert(t, yamlContains(actualGw, "self-hosted")) - assert.Assert(t, yamlContains(actualGw, "DEV_CLUSTER_KUBECONFIG")) - assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_LOGIN_URL")) - assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_USER")) - assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_PASS")) -} - -func assertMessage(t *testing.T, res result, expectedMessage string) { - t.Helper() - - assert.NilError(t, res.executeErr) - assert.Assert(t, strings.Contains(res.stdOut, expectedMessage), - "\nexpected:\n%s\n\ngot:\n%s", expectedMessage, res.stdOut) -} - // ---------------------- // END: Testing Framework diff --git a/cmd/config_test.go b/cmd/config_test.go index 5317567613..0ce09cdb68 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -11,8 +11,8 @@ import ( "github.com/ory/viper" "github.com/spf13/cobra" fnCmd "knative.dev/func/cmd" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" + "knative.dev/func/pkg/ci/github" fn "knative.dev/func/pkg/functions" ) @@ -52,9 +52,10 @@ func TestListEnvs(t *testing.T) { func setupConfigEnvCmd(mock common.FunctionLoaderSaver, args ...string) *cobra.Command { cmd := fnCmd.NewConfigCmd( mock, - ci.NewBufferWriter(), + github.NewBufferWriter(), common.CurrentBranchStub("", nil), common.WorkDirStub("", nil), + fnCmd.NewTestCIGeneratorFactory(&github.WorkflowGeneratorMock{}), fnCmd.NewClient, ) cmd.SetArgs(append([]string{"envs"}, args...)) diff --git a/cmd/root.go b/cmd/root.go index df89eb7479..ec1e2e680f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,9 +14,9 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "knative.dev/client/pkg/util" - "knative.dev/func/cmd/ci" "knative.dev/func/cmd/common" "knative.dev/func/cmd/templates" + "knative.dev/func/pkg/ci/github" "knative.dev/func/pkg/config" fn "knative.dev/func/pkg/functions" "knative.dev/func/pkg/k8s" @@ -104,9 +104,10 @@ Learn more about Knative at: https://knative.dev`, cfg.Name), Commands: []*cobra.Command{ NewConfigCmd( common.DefaultLoaderSaver, - ci.DefaultWorkflowWriter, + github.DefaultWorkflowWriter, common.DefaultCurrentBranch, common.DefaultWorkDir, + NewCIGeneratorFactory(), newClient, ), NewClusterCmd(), diff --git a/cmd/ci/common.go b/pkg/ci/github/common.go similarity index 50% rename from cmd/ci/common.go rename to pkg/ci/github/common.go index 6b03a95778..5fd105c86c 100644 --- a/cmd/ci/common.go +++ b/pkg/ci/github/common.go @@ -1,7 +1,29 @@ -package ci +package github import "fmt" +func determineBuilder(runtime string, remote bool) (string, error) { + switch runtime { + case "go": + if remote { + return "pack", nil + } + return "host", nil + + case "node", "typescript", "rust", "quarkus", "springboot": + return "pack", nil + + case "python": + if remote { + return "s2i", nil + } + return "host", nil + + default: + return "", fmt.Errorf("no builder support for runtime: %s", runtime) + } +} + func determineRunner(selfHosted bool) string { if selfHosted { return "self-hosted" diff --git a/pkg/ci/github/generator.go b/pkg/ci/github/generator.go new file mode 100644 index 0000000000..6d5ed696d2 --- /dev/null +++ b/pkg/ci/github/generator.go @@ -0,0 +1,115 @@ +package github + +import ( + "context" + "fmt" + "io" + "os" + + fn "knative.dev/func/pkg/functions" +) + +type workflowGenerator struct { + verbose bool + workflowWriter WorkflowWriter + messageWriter io.Writer + cfg WorkflowConfig +} + +// Option configures a workflowGenerator. +type Option func(*workflowGenerator) + +// WithWorkflowConfig overrides the default workflow configuration. +// Empty string fields are backfilled with defaults after all options +// are applied. Boolean fields are used as-is since their zero value +// (false) is indistinguishable from an explicit false. +func WithWorkflowConfig(wc WorkflowConfig) Option { + return func(wg *workflowGenerator) { + wg.cfg = wc + } +} + +// WithVerbose enables detailed configuration output after generation. +func WithVerbose(v bool) Option { + return func(wg *workflowGenerator) { + wg.verbose = v + } +} + +// WithWorkflowWriter sets the writer used to persist the workflow file. +func WithWorkflowWriter(ww WorkflowWriter) Option { + return func(wg *workflowGenerator) { + wg.workflowWriter = ww + } +} + +// WithMessageWriter sets the writer used for user-facing messages. +func WithMessageWriter(mw io.Writer) Option { + return func(wg *workflowGenerator) { + wg.messageWriter = mw + } +} + +// NewWorkflowGenerator creates a workflow generator with sensible +// defaults: writes to disk via DefaultWorkflowWriter, prints to +// os.Stdout, and uses a full default WorkflowConfig. All defaults +// can be overridden via options. If WithWorkflowConfig is used, +// the provided config replaces the defaults and any empty string +// fields are backfilled with defaults. +func NewWorkflowGenerator(options ...Option) *workflowGenerator { + wg := &workflowGenerator{ + cfg: defaultWorkflowConfig(), + workflowWriter: DefaultWorkflowWriter, + messageWriter: os.Stdout, + } + + for _, o := range options { + o(wg) + } + + wg.cfg = setEmptyFieldsToDefaults(wg.cfg) + + return wg +} + +// Generate creates a GitHub Actions workflow file for the given function. +func (g *workflowGenerator) Generate(ctx context.Context, f fn.Function) error { + if f.Root == "" { + return fmt.Errorf("function root path can not be empty") + } + + githubWorkflow, err := newGitHubWorkflow(g.cfg, f.Runtime, g.messageWriter) + if err != nil { + return err + } + + if err := githubWorkflow.Export(g.cfg.fnGitHubWorkflowFilepath(f.Root), g.workflowWriter, g.cfg.Force, g.messageWriter); err != nil { + return err + } + + if g.verbose { + // best-effort user message; errors are non-critical + _ = PrintConfiguration(g.cfg, f.Runtime, g.messageWriter) + return nil + } + + // best-effort user message; errors are non-critical + _ = PrintPostExportMessage(g.cfg, g.messageWriter) + return nil +} + +// WorkflowGeneratorMock is a test double that records calls to Generate +// for assertion in tests. +type WorkflowGeneratorMock struct { + WasInvoked bool // true after Generate is called + Config WorkflowConfig // captured config from factory + FnRoot string // captured function root from Generate +} + +// Generate implements fn.CIGenerator by recording the call. +func (gm *WorkflowGeneratorMock) Generate(_ context.Context, f fn.Function) error { + gm.WasInvoked = true + gm.FnRoot = f.Root + + return nil +} diff --git a/pkg/ci/github/generator_test.go b/pkg/ci/github/generator_test.go new file mode 100644 index 0000000000..fb458431f7 --- /dev/null +++ b/pkg/ci/github/generator_test.go @@ -0,0 +1,718 @@ +package github_test + +import ( + "bytes" + "fmt" + "path/filepath" + "strings" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" + "knative.dev/func/pkg/ci/github" + fn "knative.dev/func/pkg/functions" +) + +// START: Broad Unit Tests +// ----------------------- +func TestCIGenerator_WritesWorkflowFile(t *testing.T) { + opts := defaultOpts() + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assert.Assert(t, result.gwYamlString != "") +} + +func TestCIGenerator_WorkflowYAMLHasCorrectStructure(t *testing.T) { + opts := defaultOpts() + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assertDefaultWorkflow(t, result.gwYamlString) +} + +func TestCIGenerator_WorkflowYAMLHasCustomValues(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.cfg.SelfHostedRunner = true + opts.cfg.KubeconfigSecret = "DEV_CLUSTER_KUBECONFIG" + opts.cfg.RegistryLoginUrlVar = "DEV_REGISTRY_LOGIN_URL" + opts.cfg.RegistryUserVar = "DEV_REGISTRY_USER" + opts.cfg.RegistryPassSecret = "DEV_REGISTRY_PASS" + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assertCustomWorkflow(t, result.gwYamlString) +} + +func TestCIGenerator_WorkflowHasNoRegistryLogin(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.cfg.RegistryLogin = false + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, !strings.Contains(result.gwYamlString, "docker/login-action@v3")) + assert.Assert(t, !strings.Contains(result.gwYamlString, "Login to container registry")) + assert.Assert(t, yamlContains(result.gwYamlString, "FUNC_REGISTRY: ${{ vars.REGISTRY_URL }}")) +} + +func TestCIGenerator_RemoteBuildAndDeployWorkflow(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.cfg.WorkflowName = "Remote Func Deploy" + opts.cfg.RemoteBuild = true + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, yamlContains(result.gwYamlString, "Remote Func Deploy")) + assert.Assert(t, yamlContains(result.gwYamlString, `FUNC_REMOTE: "true"`)) +} + +func TestCIGenerator_HasWorkflowDispatch(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.cfg.WorkflowDispatch = true + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, yamlContains(result.gwYamlString, "workflow_dispatch")) +} + +func TestCIGenerator_FunctionPath(t *testing.T) { + t.Run("non-empty function path is accepted", func(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.goFn.Root = filepath.Join("current-working-directory") // os-agnostic test path + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, strings.Contains(result.actualPath, opts.goFn.Root)) + }) + + t.Run("empty function path returns an error", func(t *testing.T) { + // GIVEN + expectedErr := fmt.Errorf("function root path can not be empty") + opts := defaultOpts() + opts.goFn.Root = "" // os-agnostic test path + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.Error(t, result.executeErr, expectedErr.Error()) + }) +} + +func TestCIGenerator_WorkflowConfig(t *testing.T) { + t.Run("applies all defaults when no config is provided", func(t *testing.T) { + opts := defaultOpts() + opts.cfg = nil + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assertDefaultWorkflow(t, result.gwYamlString) + }) + + t.Run("fills empty strings but cannot default boolean fields to true", func(t *testing.T) { + opts := defaultOpts() + opts.cfg = &github.WorkflowConfig{} + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assertSemiDefaultWorkflow(t, result.gwYamlString) + }) +} + +func TestCIGenerator_ForceFlagOverwritesExistingWorkflow(t *testing.T) { + workflowName := "Func Deploy" + changedWorkflowName := "Sales Service Deployment" + sharedWriter := github.NewBufferWriter() + + t.Run("initial workflow creation succeeds", func(t *testing.T) { + opts := defaultOpts() + opts.withWriter = sharedWriter + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assert.Assert(t, yamlContains(result.gwYamlString, workflowName)) + assert.Assert(t, !strings.Contains(result.stdOut, forceWarning)) + }) + + t.Run("overwrite without force flag fails", func(t *testing.T) { + opts := defaultOpts() + opts.withWriter = sharedWriter + opts.cfg.WorkflowName = changedWorkflowName + + result := runGenerateWorkflow(t, opts) + + assert.ErrorIs(t, result.executeErr, github.ErrWorkflowExists) + assert.Assert(t, yamlContains(result.gwYamlString, workflowName)) + assert.Assert(t, !strings.Contains(result.gwYamlString, changedWorkflowName)) + assert.Assert(t, !strings.Contains(result.stdOut, forceWarning)) + }) + + t.Run("overwrite with force flag succeeds and a warning message is printed to stdout", func(t *testing.T) { + opts := defaultOpts() + opts.withWriter = sharedWriter + opts.cfg.WorkflowName = changedWorkflowName + opts.cfg.Force = true + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assert.Assert(t, yamlContains(result.gwYamlString, changedWorkflowName)) + assert.Assert(t, !strings.Contains(result.gwYamlString, workflowName)) + assert.Assert(t, strings.Contains(result.stdOut, forceWarning)) + }) +} + +func TestCIGenerator_VerbosePrintsWorkflowDetails(t *testing.T) { + t.Run("default configuration", func(t *testing.T) { + opts := defaultOpts() + opts.verbose = true + + expectedMessage := fmt.Sprintf(github.MainLayoutPlainText, + defaultOutputPath, + github.DefaultWorkflowName, + github.DefaultBranch, + "host", + "disabled", + "ubuntu-latest", + "enabled", + "enabled", + "disabled", + "disabled", + ) + fmt.Sprintf(github.RequireManyPlainText, + "secrets."+github.DefaultKubeconfigSecretName, + "secrets."+github.DefaultRegistryPassSecretName, + "vars."+github.DefaultRegistryLoginUrlVariableName, + "vars."+github.DefaultRegistryUserVariableName, + "vars."+github.DefaultRegistryUrlVariableName, + ) + + result := runGenerateWorkflow(t, opts) + + assertMessage(t, result, expectedMessage) + }) + + t.Run("custom configuration", func(t *testing.T) { + opts := defaultOpts() + opts.verbose = true + opts.cfg.SelfHostedRunner = true + opts.cfg.WorkflowName = "Deploy Checkout Service" + opts.cfg.RemoteBuild = true + opts.cfg.TestStep = false + opts.cfg.WorkflowDispatch = true + opts.cfg.Force = true + opts.cfg.KubeconfigSecret = "DEV_CLUSTER_KUBECONFIG" + opts.cfg.RegistryPassSecret = "DEV_REGISTRY_PASS" + opts.cfg.RegistryLoginUrlVar = "DEV_REGISTRY_LOGIN_URL" + opts.cfg.RegistryUserVar = "DEV_REGISTRY_USER" + opts.cfg.RegistryUrlVar = "DEV_REGISTRY_URL" + + expectedMessage := fmt.Sprintf(github.MainLayoutPlainText, + defaultOutputPath, + "Deploy Checkout Service", + github.DefaultBranch, + "pack", + "enabled", + "self-hosted", + "disabled", + "enabled", + "enabled", + "enabled", + ) + fmt.Sprintf(github.RequireManyPlainText, + "secrets.DEV_CLUSTER_KUBECONFIG", + "secrets.DEV_REGISTRY_PASS", + "vars.DEV_REGISTRY_LOGIN_URL", + "vars.DEV_REGISTRY_USER", + "vars.DEV_REGISTRY_URL", + ) + + result := runGenerateWorkflow(t, opts) + + assertMessage(t, result, expectedMessage) + }) + + t.Run("without registry login", func(t *testing.T) { + opts := defaultOpts() + opts.verbose = true + opts.cfg.RegistryLogin = false + + expectedMessage := fmt.Sprintf(github.MainLayoutPlainText, + defaultOutputPath, + github.DefaultWorkflowName, + github.DefaultBranch, + "host", + "disabled", + "ubuntu-latest", + "enabled", + "disabled", + "disabled", + "disabled", + ) + fmt.Sprintf(github.RequireOnePlainText, + "secrets."+github.DefaultKubeconfigSecretName, + ) + + result := runGenerateWorkflow(t, opts) + + assertMessage(t, result, expectedMessage) + }) +} + +func TestCIGenerator_PostExportMessageShown(t *testing.T) { + t.Run("shows all secrets and variables for k8s and registry", func(t *testing.T) { + opts := defaultOpts() + + expectedMessage := fmt.Sprintf(github.PostExportManyPlainText, + defaultOutputPath, + "secrets."+github.DefaultKubeconfigSecretName, + "secrets."+github.DefaultRegistryPassSecretName, + "vars."+github.DefaultRegistryLoginUrlVariableName, + "vars."+github.DefaultRegistryUserVariableName, + "vars."+github.DefaultRegistryUrlVariableName, + ) + + result := runGenerateWorkflow(t, opts) + + assertMessage(t, result, expectedMessage) + }) + + t.Run("shows only k8s secret when registry login is disabled", func(t *testing.T) { + opts := defaultOpts() + opts.cfg.RegistryLogin = false + + expectedMessage := fmt.Sprintf(github.PostExportOnePlainText, + defaultOutputPath, + "secrets."+github.DefaultKubeconfigSecretName, + ) + + result := runGenerateWorkflow(t, opts) + + assertMessage(t, result, expectedMessage) + }) +} + +func TestCIGenerator_VerboseAndPostExportMessageAreMutuallyExclusive(t *testing.T) { + t.Run("verbose shows configuration, not post-export message", func(t *testing.T) { + opts := defaultOpts() + opts.verbose = true + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assert.Assert(t, strings.Contains(result.stdOut, "GitHub Workflow Configuration")) + assert.Assert(t, !strings.Contains(result.stdOut, "GitHub Workflow created at:")) + }) + + t.Run("non-verbose shows post-export message, not configuration", func(t *testing.T) { + opts := defaultOpts() + + result := runGenerateWorkflow(t, opts) + + assert.NilError(t, result.executeErr) + assert.Assert(t, strings.Contains(result.stdOut, "GitHub Workflow created at:")) + assert.Assert(t, !strings.Contains(result.stdOut, "GitHub Workflow Configuration")) + }) +} + +func TestCIGenerator_TestStepPerRuntime(t *testing.T) { + testCases := []struct { + name string + runtime string + expectedRun string + }{ + { + name: "go runtime adds go test step", + runtime: "go", + expectedRun: "go test ./...", + }, + { + name: "nodejs runtime adds npm test step", + runtime: "node", + expectedRun: "npm ci && npm test", + }, + { + name: "typescript runtime adds npm test step", + runtime: "typescript", + expectedRun: "npm ci && npm test", + }, + { + name: "python runtime adds python -m pytest step", + runtime: "python", + expectedRun: "pip install . && python -m pytest", + }, + { + name: "quarkus runtime adds mvnw test step", + runtime: "quarkus", + expectedRun: "./mvnw test", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.goFn.Runtime = tc.runtime + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, yamlContains(result.gwYamlString, "Run tests")) + assert.Assert(t, yamlContains(result.gwYamlString, tc.expectedRun)) + }) + } +} + +func TestCIGenerator_TestStepSkipped(t *testing.T) { + t.Run("unsupported runtime skips test step and prints warning", func(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.goFn.Runtime = "rust" + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, !strings.Contains(result.gwYamlString, "Run tests")) + assert.Assert(t, strings.Contains(result.stdOut, "WARNING: test step not supported for runtime rust")) + }) + + t.Run("test step disabled via config", func(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.cfg.TestStep = false + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, !strings.Contains(result.gwYamlString, "Run tests")) + assert.Assert(t, strings.Count(result.gwYamlString, "- name:") == 5) + }) +} + +func TestCIGenerator_BuilderForRuntime(t *testing.T) { + testCases := []struct { + name string + runtime string + builder string + remote bool + }{ + { + name: "go function and local build", + runtime: "go", + builder: "host", + }, + { + name: "go function and remote build", + runtime: "go", + builder: "pack", + remote: true, + }, + { + name: "python function and local build", + runtime: "python", + builder: "host", + }, + { + name: "python function and remote build", + runtime: "python", + builder: "s2i", + remote: true, + }, + { + name: "node function and local build", + runtime: "node", + builder: "pack", + }, + { + name: "node function and remote build", + runtime: "node", + builder: "pack", + remote: true, + }, + { + name: "typescript function and local build", + runtime: "typescript", + builder: "pack", + }, + { + name: "typescript function and remote build", + runtime: "typescript", + builder: "pack", + remote: true, + }, + { + name: "rust function and local build", + runtime: "rust", + builder: "pack", + }, + { + name: "rust function and remote build", + runtime: "rust", + builder: "pack", + remote: true, + }, + { + name: "quarkus function and local build", + runtime: "quarkus", + builder: "pack", + }, + { + name: "quarkus function and remote build", + runtime: "quarkus", + builder: "pack", + remote: true, + }, + { + name: "springboot function and local build", + runtime: "springboot", + builder: "pack", + }, + { + name: "springboot function and remote build", + runtime: "springboot", + builder: "pack", + remote: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.goFn.Runtime = tc.runtime + opts.cfg.RemoteBuild = tc.remote + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.NilError(t, result.executeErr) + assert.Assert(t, strings.Contains(result.gwYamlString, "FUNC_BUILDER: "+tc.builder)) + }) + } +} + +func TestCIGenerator_BuilderForRuntimeError(t *testing.T) { + // GIVEN + opts := defaultOpts() + opts.goFn.Runtime = "zig" + + // WHEN + result := runGenerateWorkflow(t, opts) + + // THEN + assert.Error(t, result.executeErr, "no builder support for runtime: zig") +} + +// --------------------- +// END: Broad Unit Tests + +// START: Testing Framework +// ------------------------ +const ( + forceWarning = "WARNING: --force flag is set, overwriting existing GitHub Workflow file" +) + +var defaultOutputPath = filepath.Join(github.DefaultGitHubWorkflowDir, github.DefaultGitHubWorkflowFilename) + +type result struct { + actualPath string + executeErr error + gwYamlString string + stdOut string +} + +type opts struct { + cfg *github.WorkflowConfig + goFn fn.Function + withWriter *github.BufferWriter + verbose bool +} + +func defaultOpts() opts { + cfg := &github.WorkflowConfig{ + GithubWorkflowDir: github.DefaultGitHubWorkflowDir, + GithubWorkflowFilename: github.DefaultGitHubWorkflowFilename, + Branch: github.DefaultBranch, + WorkflowName: github.DefaultWorkflowName, + KubeconfigSecret: github.DefaultKubeconfigSecretName, + RegistryLoginUrlVar: github.DefaultRegistryLoginUrlVariableName, + RegistryUserVar: github.DefaultRegistryUserVariableName, + RegistryPassSecret: github.DefaultRegistryPassSecretName, + RegistryUrlVar: github.DefaultRegistryUrlVariableName, + RegistryLogin: github.DefaultRegistryLogin, + SelfHostedRunner: github.DefaultSelfHostedRunner, + RemoteBuild: github.DefaultRemoteBuild, + WorkflowDispatch: github.DefaultWorkflowDispatch, + TestStep: github.DefaultTestStep, + Force: github.DefaultForce, + } + goFn := fn.Function{Root: "path/to/func", Runtime: "go"} + return opts{ + cfg: cfg, + goFn: goFn, + } +} + +func runGenerateWorkflow(t *testing.T, opts opts) result { + t.Helper() + + writer := opts.withWriter + if writer == nil { + writer = github.NewBufferWriter() + } + + messageBufferWriter := &bytes.Buffer{} + + generatorOptions := []github.Option{ + github.WithWorkflowWriter(writer), + github.WithMessageWriter(messageBufferWriter), + github.WithVerbose(opts.verbose), + } + + if opts.cfg != nil { + generatorOptions = append(generatorOptions, github.WithWorkflowConfig(*opts.cfg)) + } + + generator := github.NewWorkflowGenerator(generatorOptions...) + err := generator.Generate(t.Context(), opts.goFn) + + return result{ + writer.Path, + err, + writer.Buffer.String(), + messageBufferWriter.String(), + } +} + +// assertDefaultWorkflow verifies the generated YAML contains the full +// default workflow structure: all six steps, default branch, runner, +// secrets, variables, and deploy command. +func assertDefaultWorkflow(t *testing.T, actualGw string) { + t.Helper() + + assert.Assert(t, yamlContains(actualGw, "Func Deploy")) + assert.Assert(t, yamlContains(actualGw, "- main")) + + assert.Assert(t, yamlContains(actualGw, "ubuntu-latest")) + + assert.Assert(t, strings.Count(actualGw, "- name:") == 6) + + assert.Assert(t, yamlContains(actualGw, "Checkout code")) + assert.Assert(t, yamlContains(actualGw, "actions/checkout@v4")) + + assert.Assert(t, yamlContains(actualGw, "Run tests")) + assert.Assert(t, yamlContains(actualGw, "go test ./...")) + + assert.Assert(t, yamlContains(actualGw, "Setup Kubernetes context")) + assert.Assert(t, yamlContains(actualGw, "azure/k8s-set-context@v4")) + assert.Assert(t, yamlContains(actualGw, "method: kubeconfig")) + assert.Assert(t, yamlContains(actualGw, "kubeconfig: ${{ secrets.KUBECONFIG }}")) + + assert.Assert(t, yamlContains(actualGw, "Login to container registry")) + assert.Assert(t, yamlContains(actualGw, "docker/login-action@v3")) + assert.Assert(t, yamlContains(actualGw, "registry: ${{ vars.REGISTRY_LOGIN_URL }}")) + assert.Assert(t, yamlContains(actualGw, "username: ${{ vars.REGISTRY_USERNAME }}")) + assert.Assert(t, yamlContains(actualGw, "password: ${{ secrets.REGISTRY_PASSWORD }}")) + + assert.Assert(t, yamlContains(actualGw, "Install func cli")) + assert.Assert(t, yamlContains(actualGw, "functions-dev/action@main")) + assert.Assert(t, yamlContains(actualGw, "version: knative-v1.22.0")) + assert.Assert(t, yamlContains(actualGw, "name: func")) + + assert.Assert(t, yamlContains(actualGw, "Deploy function")) + assert.Assert(t, yamlContains(actualGw, `FUNC_VERBOSE: "true"`)) + assert.Assert(t, yamlContains(actualGw, "FUNC_REGISTRY: ${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }}")) + assert.Assert(t, yamlContains(actualGw, "func deploy")) +} + +// assertSemiDefaultWorkflow verifies the workflow generated from an empty +// WorkflowConfig{}. String fields are backfilled with defaults, but boolean +// fields stay at zero (false), so RegistryLogin and TestStep are off — +// resulting in four steps instead of six and no registry login action. +func assertSemiDefaultWorkflow(t *testing.T, actualGw string) { + t.Helper() + + assert.Assert(t, yamlContains(actualGw, "Func Deploy")) + assert.Assert(t, yamlContains(actualGw, "- main")) + + assert.Assert(t, yamlContains(actualGw, "ubuntu-latest")) + + assert.Assert(t, strings.Count(actualGw, "- name:") == 4) + + assert.Assert(t, yamlContains(actualGw, "Checkout code")) + assert.Assert(t, yamlContains(actualGw, "actions/checkout@v4")) + + assert.Assert(t, yamlContains(actualGw, "Setup Kubernetes context")) + assert.Assert(t, yamlContains(actualGw, "azure/k8s-set-context@v4")) + assert.Assert(t, yamlContains(actualGw, "method: kubeconfig")) + assert.Assert(t, yamlContains(actualGw, "kubeconfig: ${{ secrets.KUBECONFIG }}")) + + assert.Assert(t, yamlContains(actualGw, "Install func cli")) + assert.Assert(t, yamlContains(actualGw, "functions-dev/action@main")) + assert.Assert(t, yamlContains(actualGw, "version: knative-v1.22.0")) + assert.Assert(t, yamlContains(actualGw, "name: func")) + + assert.Assert(t, yamlContains(actualGw, "Deploy function")) + assert.Assert(t, yamlContains(actualGw, `FUNC_VERBOSE: "true"`)) + assert.Assert(t, yamlContains(actualGw, "FUNC_REGISTRY: ${{ vars.REGISTRY_URL }}")) + assert.Assert(t, yamlContains(actualGw, "func deploy")) +} + +func yamlContains(yaml, substr string) cmp.Comparison { + return func() cmp.Result { + if strings.Contains(yaml, substr) { + return cmp.ResultSuccess + } + return cmp.ResultFailure(fmt.Sprintf( + "missing '%s' in:\n\n%s", substr, yaml, + )) + } +} + +func assertCustomWorkflow(t *testing.T, actualGw string) { + t.Helper() + + assert.Assert(t, yamlContains(actualGw, "self-hosted")) + assert.Assert(t, yamlContains(actualGw, "DEV_CLUSTER_KUBECONFIG")) + assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_LOGIN_URL")) + assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_USER")) + assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_PASS")) +} + +func assertMessage(t *testing.T, res result, expectedMessage string) { + t.Helper() + + assert.NilError(t, res.executeErr) + assert.Assert(t, strings.Contains(res.stdOut, expectedMessage), + "\nexpected:\n%s\n\ngot:\n%s", expectedMessage, res.stdOut) +} + +// ---------------------- +// END: Testing Framework diff --git a/cmd/ci/printer.go b/pkg/ci/github/printer.go similarity index 53% rename from cmd/ci/printer.go rename to pkg/ci/github/printer.go index 6470717b45..962801d373 100644 --- a/cmd/ci/printer.go +++ b/pkg/ci/github/printer.go @@ -1,4 +1,4 @@ -package ci +package github import ( "fmt" @@ -49,29 +49,34 @@ Create the following Secret on github.com: %s ` ) -func PrintConfiguration(w io.Writer, conf CIConfig) error { +func PrintConfiguration(cfg WorkflowConfig, runtime string, w io.Writer) error { + builder, err := determineBuilder(runtime, cfg.RemoteBuild) + if err != nil { + return err + } + if _, err := fmt.Fprintf(w, MainLayoutPlainText, - conf.OutputPath(), - conf.WorkflowName(), - conf.Branch(), - conf.FnBuilder(), - enabledOrDisabled(conf.RemoteBuild()), - determineRunner(conf.SelfHostedRunner()), - enabledOrDisabled(conf.TestStep()), - enabledOrDisabled(conf.RegistryLogin()), - enabledOrDisabled(conf.WorkflowDispatch()), - enabledOrDisabled(conf.Force()), + cfg.outputPath(), + cfg.WorkflowName, + cfg.Branch, + builder, + enabledOrDisabled(cfg.RemoteBuild), + determineRunner(cfg.SelfHostedRunner), + enabledOrDisabled(cfg.TestStep), + enabledOrDisabled(cfg.RegistryLogin), + enabledOrDisabled(cfg.WorkflowDispatch), + enabledOrDisabled(cfg.Force), ); err != nil { return err } - if conf.RegistryLogin() { + if cfg.RegistryLogin { if _, err := fmt.Fprintf(w, RequireManyPlainText, - secretsPrefix(conf.KubeconfigSecret()), - secretsPrefix(conf.RegistryPassSecret()), - varsPrefix(conf.RegistryLoginUrlVar()), - varsPrefix(conf.RegistryUserVar()), - varsPrefix(conf.RegistryUrlVar()), + secretsPrefix(cfg.KubeconfigSecret), + secretsPrefix(cfg.RegistryPassSecret), + varsPrefix(cfg.RegistryLoginUrlVar), + varsPrefix(cfg.RegistryUserVar), + varsPrefix(cfg.RegistryUrlVar), ); err != nil { return err } @@ -81,7 +86,7 @@ func PrintConfiguration(w io.Writer, conf CIConfig) error { if _, err := fmt.Fprintf(w, RequireOnePlainText, - secretsPrefix(conf.KubeconfigSecret()), + secretsPrefix(cfg.KubeconfigSecret), ); err != nil { return err } @@ -89,22 +94,22 @@ func PrintConfiguration(w io.Writer, conf CIConfig) error { return nil } -func PrintPostExportMessage(w io.Writer, conf CIConfig) error { - if conf.RegistryLogin() { +func PrintPostExportMessage(opts WorkflowConfig, w io.Writer) error { + if opts.RegistryLogin { _, err := fmt.Fprintf(w, PostExportManyPlainText, - conf.OutputPath(), - secretsPrefix(conf.KubeconfigSecret()), - secretsPrefix(conf.RegistryPassSecret()), - varsPrefix(conf.RegistryLoginUrlVar()), - varsPrefix(conf.RegistryUserVar()), - varsPrefix(conf.RegistryUrlVar()), + opts.outputPath(), + secretsPrefix(opts.KubeconfigSecret), + secretsPrefix(opts.RegistryPassSecret), + varsPrefix(opts.RegistryLoginUrlVar), + varsPrefix(opts.RegistryUserVar), + varsPrefix(opts.RegistryUrlVar), ) return err } _, err := fmt.Fprintf(w, PostExportOnePlainText, - conf.OutputPath(), - secretsPrefix(conf.KubeconfigSecret()), + opts.outputPath(), + secretsPrefix(opts.KubeconfigSecret), ) return err } diff --git a/cmd/ci/printer_test.go b/pkg/ci/github/printer_test.go similarity index 63% rename from cmd/ci/printer_test.go rename to pkg/ci/github/printer_test.go index 423f41ba90..7b718b78fa 100644 --- a/cmd/ci/printer_test.go +++ b/pkg/ci/github/printer_test.go @@ -1,7 +1,9 @@ -package ci +package github import ( + "bytes" "errors" + "fmt" "testing" "gotest.tools/v3/assert" @@ -26,47 +28,55 @@ func (fw *failWriter) Write(p []byte) (int, error) { func TestPrintConfigurationFail(t *testing.T) { t.Run("main layout write fails", func(t *testing.T) { w := &failWriter{failOnCall: 1, err: errWrite} - conf := CIConfig{fnRuntime: "go"} - err := PrintConfiguration(w, conf) + err := PrintConfiguration(WorkflowConfig{}, "go", w) assert.Error(t, err, errWrite.Error()) }) t.Run("registry secrets write fails", func(t *testing.T) { w := &failWriter{failOnCall: 2, err: errWrite} - conf := CIConfig{registryLogin: true, fnRuntime: "go"} + opts := WorkflowConfig{RegistryLogin: true} - err := PrintConfiguration(w, conf) + err := PrintConfiguration(opts, "go", w) assert.Error(t, err, errWrite.Error()) }) t.Run("single secret write fails", func(t *testing.T) { w := &failWriter{failOnCall: 2, err: errWrite} - conf := CIConfig{registryLogin: false, fnRuntime: "go"} + opts := WorkflowConfig{RegistryLogin: false} - err := PrintConfiguration(w, conf) + err := PrintConfiguration(opts, "go", w) assert.Error(t, err, errWrite.Error()) }) + + t.Run("unsupported runtime fails", func(t *testing.T) { + runtime := "ruby" + expectedErr := fmt.Errorf("no builder support for runtime: %s", runtime) + + err := PrintConfiguration(WorkflowConfig{}, runtime, &bytes.Buffer{}) + + assert.Error(t, err, expectedErr.Error()) + }) } func TestPrintPostExportMessageFail(t *testing.T) { t.Run("with registry login write fails", func(t *testing.T) { w := &failWriter{failOnCall: 1, err: errWrite} - conf := CIConfig{registryLogin: true} + opts := WorkflowConfig{RegistryLogin: true} - err := PrintPostExportMessage(w, conf) + err := PrintPostExportMessage(opts, w) assert.Error(t, err, errWrite.Error()) }) t.Run("without registry login write fails", func(t *testing.T) { w := &failWriter{failOnCall: 1, err: errWrite} - conf := CIConfig{registryLogin: false} + opts := WorkflowConfig{RegistryLogin: false} - err := PrintPostExportMessage(w, conf) + err := PrintPostExportMessage(opts, w) assert.Error(t, err, errWrite.Error()) }) diff --git a/pkg/ci/github/workflow.go b/pkg/ci/github/workflow.go new file mode 100644 index 0000000000..85403760e2 --- /dev/null +++ b/pkg/ci/github/workflow.go @@ -0,0 +1,334 @@ +package github + +import ( + "bytes" + "errors" + "fmt" + "io" + "path/filepath" + + "gopkg.in/yaml.v3" +) + +// ErrWorkflowExists is returned when a GitHub workflow file already exists and --force is not specified. +var ErrWorkflowExists = errors.New("existing GitHub workflow detected, overwrite using the --force option") + +const ( + defaultFuncCliVersion = "knative-v1.22.0" + DefaultPlatform = "github" + DefaultGitHubWorkflowDir = ".github/workflows" + DefaultGitHubWorkflowFilename = "func-deploy.yaml" + DefaultBranch = "main" + DefaultWorkflowName = "Func Deploy" + DefaultRemoteBuildWorkflowName = "Remote " + DefaultWorkflowName + DefaultKubeconfigSecretName = "KUBECONFIG" + DefaultRegistryLoginUrlVariableName = "REGISTRY_LOGIN_URL" + DefaultRegistryUserVariableName = "REGISTRY_USERNAME" + DefaultRegistryPassSecretName = "REGISTRY_PASSWORD" + DefaultRegistryUrlVariableName = "REGISTRY_URL" + DefaultRegistryLogin = true + DefaultWorkflowDispatch = false + DefaultRemoteBuild = false + DefaultSelfHostedRunner = false + DefaultTestStep = true + DefaultForce = false + DefaultVerbose = false +) + +// WorkflowConfig holds the settings for generating a GitHub Actions workflow. +type WorkflowConfig struct { + GithubWorkflowDir, + GithubWorkflowFilename, + Branch, + WorkflowName, + KubeconfigSecret, + RegistryLoginUrlVar, + RegistryUserVar, + RegistryPassSecret, + RegistryUrlVar string + RegistryLogin, + SelfHostedRunner, + RemoteBuild, + WorkflowDispatch, + TestStep, + Force bool +} + +func (o WorkflowConfig) fnGitHubWorkflowFilepath(fnRoot string) string { + fnGitHubWorkflowDir := filepath.Join(fnRoot, o.GithubWorkflowDir) + return filepath.Join(fnGitHubWorkflowDir, o.GithubWorkflowFilename) +} + +func (o WorkflowConfig) outputPath() string { + return filepath.Join(o.GithubWorkflowDir, o.GithubWorkflowFilename) +} + +func defaultWorkflowConfig() WorkflowConfig { + return WorkflowConfig{ + GithubWorkflowDir: DefaultGitHubWorkflowDir, + GithubWorkflowFilename: DefaultGitHubWorkflowFilename, + Branch: DefaultBranch, + WorkflowName: DefaultWorkflowName, + KubeconfigSecret: DefaultKubeconfigSecretName, + RegistryLoginUrlVar: DefaultRegistryLoginUrlVariableName, + RegistryUserVar: DefaultRegistryUserVariableName, + RegistryPassSecret: DefaultRegistryPassSecretName, + RegistryUrlVar: DefaultRegistryUrlVariableName, + RegistryLogin: DefaultRegistryLogin, + SelfHostedRunner: DefaultSelfHostedRunner, + RemoteBuild: DefaultRemoteBuild, + WorkflowDispatch: DefaultWorkflowDispatch, + TestStep: DefaultTestStep, + Force: DefaultForce, + } +} + +func setEmptyFieldsToDefaults(defaults WorkflowConfig) WorkflowConfig { + if defaults.GithubWorkflowDir == "" { + defaults.GithubWorkflowDir = DefaultGitHubWorkflowDir + } + if defaults.GithubWorkflowFilename == "" { + defaults.GithubWorkflowFilename = DefaultGitHubWorkflowFilename + } + if defaults.Branch == "" { + defaults.Branch = DefaultBranch + } + if defaults.WorkflowName == "" { + defaults.WorkflowName = DefaultWorkflowName + } + if defaults.KubeconfigSecret == "" { + defaults.KubeconfigSecret = DefaultKubeconfigSecretName + } + if defaults.RegistryLoginUrlVar == "" { + defaults.RegistryLoginUrlVar = DefaultRegistryLoginUrlVariableName + } + if defaults.RegistryUserVar == "" { + defaults.RegistryUserVar = DefaultRegistryUserVariableName + } + if defaults.RegistryPassSecret == "" { + defaults.RegistryPassSecret = DefaultRegistryPassSecretName + } + if defaults.RegistryUrlVar == "" { + defaults.RegistryUrlVar = DefaultRegistryUrlVariableName + } + + return defaults +} + +type workflow struct { + Name string `yaml:"name"` + On workflowTriggers `yaml:"on"` + Jobs map[string]job `yaml:"jobs"` +} + +type workflowTriggers struct { + Push *pushTrigger `yaml:"push,omitempty"` + WorkflowDispatch *struct{} `yaml:"workflow_dispatch,omitempty"` +} + +type pushTrigger struct { + Branches []string `yaml:"branches,omitempty"` +} + +type job struct { + RunsOn string `yaml:"runs-on"` + Steps []step `yaml:"steps"` +} + +type step struct { + Name string `yaml:"name,omitempty"` + Env map[string]string `yaml:"env,omitempty"` + Uses string `yaml:"uses,omitempty"` + Run string `yaml:"run,omitempty"` + With map[string]string `yaml:"with,omitempty"` +} + +func newGitHubWorkflow(cfg WorkflowConfig, runtime string, messageWriter io.Writer) (*workflow, error) { + var steps []step + steps = createCheckoutStep(steps) + steps = createRuntimeTestStep(cfg, runtime, messageWriter, steps) + steps = createK8ContextStep(cfg, steps) + steps = createRegistryLoginStep(cfg, steps) + steps = createFuncCLIInstallStep(steps) + + steps, err := createFuncDeployStep(cfg, runtime, steps) + if err != nil { + return nil, err + } + + return &workflow{ + Name: cfg.WorkflowName, + On: createPushTrigger(cfg), + Jobs: map[string]job{ + "deploy": { + RunsOn: determineRunner(cfg.SelfHostedRunner), + Steps: steps, + }, + }, + }, nil +} + +func createCheckoutStep(steps []step) []step { + checkoutCode := newStep("Checkout code"). + withUses("actions/checkout@v4") + + return append(steps, *checkoutCode) +} + +func createRuntimeTestStep(opts WorkflowConfig, runtime string, messageWriter io.Writer, steps []step) []step { + if !opts.TestStep { + return steps + } + + testStep := newStep("Run tests") + + switch runtime { + case "go": + testStep.withRun("go test ./...") + case "node", "typescript": + testStep.withRun("npm ci && npm test") + case "python": + testStep.withRun("pip install . && python -m pytest") + case "quarkus": + testStep.withRun("./mvnw test") + default: + // best-effort user message; errors are non-critical + _, _ = fmt.Fprintf(messageWriter, "WARNING: test step not supported for runtime %s\n", runtime) + return steps + } + + return append(steps, *testStep) +} + +func createK8ContextStep(opts WorkflowConfig, steps []step) []step { + setupK8Context := newStep("Setup Kubernetes context"). + withUses("azure/k8s-set-context@v4"). + withActionConfig("method", "kubeconfig"). + withActionConfig("kubeconfig", newSecret(opts.KubeconfigSecret)) + + return append(steps, *setupK8Context) +} + +func createRegistryLoginStep(opts WorkflowConfig, steps []step) []step { + if !opts.RegistryLogin { + return steps + } + + loginToContainerRegistry := newStep("Login to container registry"). + withUses("docker/login-action@v3"). + withActionConfig("registry", newVariable(opts.RegistryLoginUrlVar)). + withActionConfig("username", newVariable(opts.RegistryUserVar)). + withActionConfig("password", newSecret(opts.RegistryPassSecret)) + + return append(steps, *loginToContainerRegistry) +} + +func createFuncCLIInstallStep(steps []step) []step { + installFuncCli := newStep("Install func cli"). + withUses("functions-dev/action@main"). + withActionConfig("version", defaultFuncCliVersion). + withActionConfig("name", "func") + + return append(steps, *installFuncCli) +} + +func createFuncDeployStep(opts WorkflowConfig, runtime string, steps []step) ([]step, error) { + deployFuncStep := newStep("Deploy function"). + withEnv("FUNC_VERBOSE", "true") + + builder, err := determineBuilder(runtime, opts.RemoteBuild) + if err != nil { + return nil, err + } + deployFuncStep.withEnv("FUNC_BUILDER", builder) + + if opts.RemoteBuild { + deployFuncStep.withEnv("FUNC_REMOTE", "true") + } + + registryUrl := newVariable(opts.RegistryUrlVar) + if opts.RegistryLogin { + registryUrl = newVariable(opts.RegistryLoginUrlVar) + "/" + newVariable(opts.RegistryUserVar) + } + deployFuncStep.withEnv("FUNC_REGISTRY", registryUrl). + withRun("func deploy") + + return append(steps, *deployFuncStep), nil +} + +func createPushTrigger(opts WorkflowConfig) workflowTriggers { + result := workflowTriggers{ + Push: &pushTrigger{Branches: []string{opts.Branch}}, + } + + if opts.WorkflowDispatch { + result.WorkflowDispatch = &struct{}{} + } + + return result +} + +func newStep(name string) *step { + return &step{Name: name} +} + +func (s *step) withUses(u string) *step { + s.Uses = u + return s +} + +func (s *step) withRun(r string) *step { + s.Run = r + return s +} + +func (s *step) withActionConfig(key, value string) *step { + if s.With == nil { + s.With = make(map[string]string) + } + + s.With[key] = value + + return s +} + +func (s *step) withEnv(key, value string) *step { + if s.Env == nil { + s.Env = make(map[string]string) + } + + s.Env[key] = value + + return s +} + +func (gw *workflow) Export(path string, w WorkflowWriter, force bool, m io.Writer) error { + if !force && w.Exist(path) { + return ErrWorkflowExists + } + + if w.Exist(path) { + // best-effort user message; errors are non-critical + _, _ = fmt.Fprintf(m, "WARNING: --force flag is set, overwriting existing GitHub Workflow file\n") + } + + raw, err := gw.toYaml() + if err != nil { + return err + } + + return w.Write(path, raw) +} + +func (gw *workflow) toYaml() ([]byte, error) { + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + + if err := encoder.Encode(gw); err != nil { + return nil, err + } + encoder.Close() + + return buf.Bytes(), nil +} diff --git a/pkg/ci/github/workflow_test.go b/pkg/ci/github/workflow_test.go new file mode 100644 index 0000000000..9c1fca1708 --- /dev/null +++ b/pkg/ci/github/workflow_test.go @@ -0,0 +1,29 @@ +package github + +import ( + "bytes" + "strings" + "testing" + + "github.com/ory/viper" + "gotest.tools/v3/assert" +) + +func TestGitHubWorkflow_Export(t *testing.T) { + // GIVEN + viper.Set("platform", "github") + t.Cleanup(func() { viper.Reset() }) + bufferWriter := NewBufferWriter() + + // WHEN + opts := WorkflowConfig{} + + gw, workflowErr := newGitHubWorkflow(WorkflowConfig{}, "go", &bytes.Buffer{}) + assert.NilError(t, workflowErr, "unexpected error when creating GitHub Workflow") + + exportErr := gw.Export(opts.fnGitHubWorkflowFilepath("path/to/functions"), bufferWriter, true, &bytes.Buffer{}) + + // THEN + assert.NilError(t, exportErr, "unexpected error when exporting GitHub Workflow") + assert.Assert(t, strings.Contains(bufferWriter.Buffer.String(), gw.Name)) +} diff --git a/cmd/ci/writer.go b/pkg/ci/github/writer.go similarity index 95% rename from cmd/ci/writer.go rename to pkg/ci/github/writer.go index f15be50b1e..565bac54a2 100644 --- a/cmd/ci/writer.go +++ b/pkg/ci/github/writer.go @@ -1,4 +1,4 @@ -package ci +package github import ( "bytes" @@ -11,15 +11,16 @@ const ( filePerm = 0644 // u: rw-, g: r--, o: r-- ) -// DefaultWorkflowWriter is the default implementation for writing workflow files to disk. -var DefaultWorkflowWriter = &fileWriter{} - -// WorkflowWriter defines the interface for writing workflow files. +// WorkflowWriter defines the interface for writing workflow files to a given path. type WorkflowWriter interface { Exist(path string) bool Write(path string, raw []byte) error } +// DefaultWorkflowWriter is the default implementation for writing workflow files to disk. +var DefaultWorkflowWriter = &fileWriter{} + +// fileWriter implements WorkflowWriter type fileWriter struct{} // Write writes raw bytes to the specified path, creating directories as needed. diff --git a/cmd/ci/writer_test.go b/pkg/ci/github/writer_test.go similarity index 85% rename from cmd/ci/writer_test.go rename to pkg/ci/github/writer_test.go index 5a64f13004..7d77eab734 100644 --- a/cmd/ci/writer_test.go +++ b/pkg/ci/github/writer_test.go @@ -1,16 +1,15 @@ -package ci_test +package github import ( "testing" "gotest.tools/v3/assert" - "knative.dev/func/cmd/ci" ) // TestBufferWriter_WriteAndExist exercises the in-memory BufferWriter test double: // Exist is false on an empty buffer and true after Write. func TestBufferWriter_WriteAndExist(t *testing.T) { - bw := ci.NewBufferWriter() + bw := NewBufferWriter() assert.Assert(t, !bw.Exist("any/path"), "empty buffer should report Exist=false") @@ -25,7 +24,7 @@ func TestBufferWriter_WriteAndExist(t *testing.T) { // TestBufferWriter_Write_StoresPath checks that Write records the path that was // passed to it. func TestBufferWriter_Write_StoresPath(t *testing.T) { - bw := ci.NewBufferWriter() + bw := NewBufferWriter() path := ".github/workflows/func-deploy.yaml" assert.NilError(t, bw.Write(path, []byte("data"))) @@ -33,9 +32,9 @@ func TestBufferWriter_Write_StoresPath(t *testing.T) { } // TestBufferWriter_Write_ResetsBuffer ensures that consecutive Write calls do -// not accumulate content — each call starts with a fresh buffer. +// not accumulate content, each call starts with a fresh buffer. func TestBufferWriter_Write_ResetsBuffer(t *testing.T) { - bw := ci.NewBufferWriter() + bw := NewBufferWriter() assert.NilError(t, bw.Write("p", []byte("first"))) assert.NilError(t, bw.Write("p", []byte("second"))) diff --git a/pkg/functions/client.go b/pkg/functions/client.go index 893cc9af68..a7f45c4254 100644 --- a/pkg/functions/client.go +++ b/pkg/functions/client.go @@ -43,7 +43,7 @@ var ( // use of this set is left up to the discretion of the builders // themselves. In the event the builder receives build options which // specify a set of platforms to use in leau of the default (see the - // BuildWithPlatforms functionl option), the builder should return + // BuildWithPlatforms function option), the builder should return // an error if the request can not proceed. DefaultPlatforms = []Platform{ {OS: "linux", Architecture: "amd64"}, @@ -83,6 +83,7 @@ type Client struct { mcpServer MCPServer // MCP Server startTimeout time.Duration // default start timeout for all runs syncer FunctionSyncer // Syncs Function CR after deploy + ciGenerator CIGenerator // CI workflow generator } // Scaffolder wraps a function with a service scaffolding (entrypoint) @@ -230,6 +231,11 @@ type MCPServer interface { Start(context.Context) error } +// CIGenerator creates CI/CD workflow files for a given function. +type CIGenerator interface { + Generate(context.Context, Function) error +} + // New client for function management. func New(options ...Option) *Client { // Instantiate client with static defaults. @@ -246,6 +252,7 @@ func New(options ...Option) *Client { mcpServer: &noopMCPServer{}, transport: http.DefaultTransport, startTimeout: DefaultStartTimeout, + ciGenerator: &noopCIGenerator{}, } c.runner = newDefaultRunner(c, os.Stdout, os.Stderr) for _, o := range options { @@ -352,7 +359,7 @@ func WithDescribers(describers ...Describer) Option { } } -// WithDNSProvider proivdes a DNS provider implementation for registering the +// WithDNSProvider provides a DNS provider implementation for registering the // effective DNS name which is either explicitly set via WithName or is derived // from the root path. func WithDNSProvider(provider DNSProvider) Option { @@ -371,7 +378,7 @@ func WithRepositoriesPath(path string) Option { } // WithRepository sets a specific URL to a Git repository from which to pull -// templates. This setting's existence precldes the use of either the inbuilt +// templates. This setting's existence precludes the use of either the inbuilt // templates or any repositories from the extensible repositories path. func WithRepository(uri string) Option { return func(c *Client) { @@ -444,6 +451,13 @@ func WithStartTimeout(t time.Duration) Option { } } +// WithCIGenerator sets the CI workflow generator implementation. +func WithCIGenerator(cig CIGenerator) Option { + return func(c *Client) { + c.ciGenerator = cig + } +} + // ACCESSORS // --------- @@ -582,7 +596,6 @@ func (c *Client) New(ctx context.Context, cfg Function) (string, Function, error // Push the produced function image fmt.Fprintf(os.Stderr, "Pushing container image to registry\n") - if f, _, err = c.Push(ctx, f); err != nil { return route, f, err } @@ -1270,6 +1283,11 @@ func (c *Client) StartMCPServer(ctx context.Context) error { return c.mcpServer.Start(ctx) } +// GenerateCIWorkflow delegates to the configured CIGenerator. +func (c *Client) GenerateCIWorkflow(ctx context.Context, f Function) error { + return c.ciGenerator.Generate(ctx, f) +} + // ensureRunDataDir creates a .func directory at the given path, and // registers it as ignored in a .gitignore file. func ensureRunDataDir(root string) error { @@ -1636,3 +1654,7 @@ func (n *noopDNSProvider) Provide(_ Function) error { return nil } type noopMCPServer struct{} func (n *noopMCPServer) Start(_ context.Context) error { return nil } + +type noopCIGenerator struct{} + +func (n *noopCIGenerator) Generate(_ context.Context, _ Function) error { return nil } From 3ecc9e30edc73165219403d34722182a785a1396 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:01:06 +0200 Subject: [PATCH 06/44] gitlab test (#3874) --- pkg/pipelines/tekton/gitlab_int_test.go | 40 ++++++++++++------- .../tekton/pipelines_pac_provider.go | 10 +++++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/pkg/pipelines/tekton/gitlab_int_test.go b/pkg/pipelines/tekton/gitlab_int_test.go index da99814db6..b13e56ddaa 100644 --- a/pkg/pipelines/tekton/gitlab_int_test.go +++ b/pkg/pipelines/tekton/gitlab_int_test.go @@ -76,13 +76,14 @@ func TestInt_Gitlab(t *testing.T) { funcImg := fmt.Sprintf("registry.default.svc.cluster.local:5000/fn-%s", uuid.NewUUID()) f := fn.Function{ - Root: projDir, - Name: glabEnv.ProjectName, - Runtime: "test-runtime", - Template: "test-template", - Image: funcImg, - Created: time.Now(), - Invoke: "none", + Root: projDir, + Name: glabEnv.ProjectName, + Runtime: "test-runtime", + Template: "test-template", + RegistryInsecure: true, + Image: funcImg, + Created: time.Now(), + Invoke: "none", Build: fn.BuildSpec{ Git: fn.Git{ URL: strings.TrimSuffix(glabEnv.HTTPProjectURL, ".git"), @@ -161,7 +162,10 @@ func TestInt_Gitlab(t *testing.T) { } select { - case <-buildDoneCh: + case buildErr := <-buildDoneCh: + if buildErr != nil { + t.Fatalf("build failed: %v", buildErr) + } t.Log("build done on time") case <-time.After(time.Minute * 10): t.Error("build has not been done in time (10 minute timeout)") @@ -661,7 +665,7 @@ func usingNamespace(t *testing.T) string { return name } -func awaitBuildCompletion(t *testing.T, name, ns string) <-chan struct{} { +func awaitBuildCompletion(t *testing.T, name, ns string) <-chan error { clis, err := tekton.NewTektonClients() if err != nil { @@ -678,7 +682,7 @@ func awaitBuildCompletion(t *testing.T, name, ns string) <-chan struct{} { t.Fatal(err) } - ch := make(chan struct{}, 1) + ch := make(chan error, 1) go func() { defer w.Stop() @@ -687,11 +691,19 @@ func awaitBuildCompletion(t *testing.T, name, ns string) <-chan struct{} { if !ok { continue } - // No longer need to check name prefix since we're filtering by label for _, condition := range taskRun.Status.Conditions { - if condition.Type == apis.ConditionSucceeded && condition.IsTrue() { - ch <- struct{}{} - break + if condition.Type != apis.ConditionSucceeded { + continue + } + if condition.IsTrue() { + ch <- nil + return + } + if condition.IsFalse() { + ch <- fmt.Errorf("TaskRun %s/%s: %s — %s", + taskRun.Namespace, taskRun.Name, + condition.Reason, condition.Message) + return } } } diff --git a/pkg/pipelines/tekton/pipelines_pac_provider.go b/pkg/pipelines/tekton/pipelines_pac_provider.go index 9c007f274c..87e3bd2fd6 100644 --- a/pkg/pipelines/tekton/pipelines_pac_provider.go +++ b/pkg/pipelines/tekton/pipelines_pac_provider.go @@ -25,6 +25,16 @@ import ( // Parameter `metadata` is type `any` to not bring `pkg/pipelines` package dependency to `pkg/functions`, // this specific implementation expects the parameter to be a type `pipelines.PacMetada`. func (pp *PipelinesProvider) ConfigurePAC(ctx context.Context, f fn.Function, metadata any) error { + // Derive Registry from the image when not explicitly set, so that + // downstream insecure-registry detection and template params work. + if f.Registry == "" { + if img := f.Deploy.Image; img != "" { + f.Registry, _ = docker.GetRegistry(img) + } else if f.Image != "" { + f.Registry, _ = docker.GetRegistry(f.Image) + } + } + // Use the new target namespace if specified, otherwise use the // function's currently deployed namespace (if any) namespace := f.Namespace From e8131d975243232f92c2737501927d1d7ade00ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Wed, 3 Jun 2026 18:48:05 +0200 Subject: [PATCH 07/44] fix: reduce GitLab test instance memory footprint (~400-500MB) (#3876) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable unused services (monitoring, registry, pages, KAS), reduce sidekiq concurrency, tune PostgreSQL, disable unneeded Rails features, and update gitaly config to remove dead ruby_max_rss setting and fix deprecated RPC names. Signed-off-by: Matej Vašek --- hack/gitlab.sh | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/hack/gitlab.sh b/hack/gitlab.sh index 322c775a0b..e40ae80ca9 100755 --- a/hack/gitlab.sh +++ b/hack/gitlab.sh @@ -87,19 +87,41 @@ spec: nginx['listen_addresses'] = ["0.0.0.0", "[::]"] gitlab_rails['gitlab_shell_ssh_port'] = 30022 gitlab_rails['gitlab_email_enabled'] = false + gitlab_rails['gravatar_enabled'] = false + gitlab_rails['usage_ping_enabled'] = false + gitlab_rails['gitlab_default_projects_features_builds'] = false + gitlab_rails['gitlab_default_projects_features_container_registry'] = false + gitlab_rails['gitlab_default_projects_features_snippets'] = false + gitlab_rails['gitlab_default_projects_features_wiki'] = false + postgresql['shared_buffers'] = '128MB' + postgresql['max_worker_processes'] = 2 puma['worker_processes'] = 0 + sidekiq['concurrency'] = 10 prometheus_monitoring['enable'] = false + alertmanager['enable'] = false + gitlab_exporter['enable'] = false + gitlab_kas['enable'] = false + node_exporter['enable'] = false + postgres_exporter['enable'] = false + prometheus['enable'] = false + puma['exporter_enabled'] = false + redis_exporter['enable'] = false + sidekiq['metrics_enabled'] = false + registry['enable'] = false + gitlab_pages['enable'] = false gitlab_rails['env'] = { 'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000' } gitaly['configuration'] = { - ruby_max_rss: 200_000_000, concurrency: [ { rpc: "/gitaly.SmartHTTPService/PostReceivePack", max_per_repo: 1 }, { - rpc: "/gitaly.SSHService/SSHUploadPack", + rpc: "/gitaly.SmartHTTPService/PostUploadPackWithSidechannel", + max_per_repo: 1 + }, { + rpc: "/gitaly.SSHService/SSHUploadPackWithSidechannel", max_per_repo: 1 } ] From a14d397fe21942d0521520e655407a7b7957a0f5 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:00:05 +0200 Subject: [PATCH 08/44] enable jj for make check (#3873) --- Makefile | 23 +++++++---------------- hack/vcs.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 16 deletions(-) create mode 100755 hack/vcs.sh diff --git a/Makefile b/Makefile index e35743e2b0..b528032788 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,9 @@ export GOFLAGS MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +# List tracked source files, excluding generated and vendored. +LS_SOURCES := ./hack/vcs.sh ls-sources + # Disable output buffering (stream) MAKEFLAGS += --output-sync=none @@ -92,10 +95,7 @@ check-lint: $(BIN_GOLANGCI_LINT) ## Run golangci-lint .PHONY: check-goimports check-goimports: $(BIN_GOIMPORTS) ## Check Go import formatting @echo "Checking Go import formatting..." - @git ls-files | \ - git check-attr --stdin linguist-generated | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - git check-attr --stdin linguist-vendored | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - grep -Ev '(vendor/|third_party/|\.git)' | \ + @$(LS_SOURCES) | \ grep '\.go$$' | \ while IFS= read -r file; do [ -f "$$file" ] && echo "$$file"; done | \ xargs $(BIN_GOIMPORTS) -l | grep . && \ @@ -104,10 +104,7 @@ check-goimports: $(BIN_GOIMPORTS) ## Check Go import formatting .PHONY: check-misspell check-misspell: $(BIN_MISSPELL) ## Check for common misspellings @echo "Checking for misspellings..." - @git ls-files | \ - git check-attr --stdin linguist-generated | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - git check-attr --stdin linguist-vendored | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - grep -Ev '(vendor/|third_party/|\.git)' | \ + @$(LS_SOURCES) | \ grep -v '\.svg$$' | \ while IFS= read -r file; do [ -f "$$file" ] && echo "$$file"; done | \ xargs $(BIN_MISSPELL) -i importas -error @@ -115,10 +112,7 @@ check-misspell: $(BIN_MISSPELL) ## Check for common misspellings .PHONY: check-whitespace check-whitespace: ## Check for trailing whitespace @echo "Checking for trailing whitespace..." - @git ls-files | \ - git check-attr --stdin linguist-generated | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - git check-attr --stdin linguist-vendored | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - grep -Ev '(vendor/|third_party/|\.git)' | \ + @$(LS_SOURCES) | \ grep -v '\.svg$$' | \ while IFS= read -r file; do [ -f "$$file" ] && echo "$$file"; done | \ xargs grep -nE " +$$" 2>&1 | grep -vi "binary file" && \ @@ -127,10 +121,7 @@ check-whitespace: ## Check for trailing whitespace .PHONY: check-eof check-eof: ## Check files end with newlines @echo "Checking for missing EOF newlines..." - @git ls-files | \ - git check-attr --stdin linguist-generated | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - git check-attr --stdin linguist-vendored | grep -Ev ': (set|true)$$' | cut -d: -f1 | \ - grep -Ev '(vendor/|third_party/|\.git)' | \ + @$(LS_SOURCES) | \ grep -Ev '\.(ai|svg|tar|tgz|zip)$$' | \ while IFS= read -r file; do \ if [ -f "$$file" ] && [ -n "$$(tail -c 1 "$$file" 2>/dev/null)" ]; then \ diff --git a/hack/vcs.sh b/hack/vcs.sh new file mode 100755 index 0000000000..ed3a6c5b08 --- /dev/null +++ b/hack/vcs.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# VCS abstraction layer for git and jj. +# Usage: +# hack/vcs.sh ls-sources # list tracked source files, excluding generated/vendored +# +# Set FUNC_VCS=git|jj to override auto-detection (e.g. FUNC_VCS=jj make check). + +set -euo pipefail + +# --- VCS detection --- + +VCS="" +if [ -n "${FUNC_VCS:-}" ]; then + VCS="$FUNC_VCS" +elif git rev-parse --is-inside-work-tree &>/dev/null; then + VCS=git +elif [ -d .jj ]; then + VCS=jj +fi + +# --- Subcommands --- + +cmd_ls_sources() { + if [ "$VCS" = git ]; then + git ls-files \ + | git check-attr --stdin linguist-generated | grep -Ev ': (set|true)$' | cut -d: -f1 \ + | git check-attr --stdin linguist-vendored | grep -Ev ': (set|true)$' | cut -d: -f1 \ + | grep -Ev '(vendor/|third_party/|\.git)' + elif [ "$VCS" = jj ]; then + # jj has no .gitattributes support (https://github.com/jj-vcs/jj/issues/53), + # so we replicate the linguist-generated filters from .gitattributes here. + jj file list \ + | grep -Ev '(zz_filesystem_generated\.go$|zz_close_guarding_client_generated\.go$)' \ + | grep -Ev '(vendor/|third_party/|\.git|\.jj)' + else + find . -type f | sed 's|^\./||' \ + | grep -Ev '(zz_filesystem_generated\.go$|zz_close_guarding_client_generated\.go$)' \ + | grep -Ev '(vendor/|third_party/|\.git|\.jj)' + fi +} + +# --- Dispatch --- + +case "${1:-}" in + ls-sources) cmd_ls_sources ;; + *) + echo "Usage: $0 {ls-sources}" >&2 + exit 1 + ;; +esac From bf19b170a33774fca313fb3386471896c4c4b932 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Wed, 3 Jun 2026 22:47:05 -0400 Subject: [PATCH 09/44] upgrade to latest dependencies (#3870) Signed-off-by: Knative Automation --- go.mod | 44 +++++++++--------- go.sum | 90 ++++++++++++++++++------------------ hack/component-versions.json | 6 +-- hack/component-versions.sh | 6 +-- 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index 2499699023..75326eb6a1 100644 --- a/go.mod +++ b/go.mod @@ -69,11 +69,11 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8 - knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e + knative.dev/client/pkg v0.0.0-20260603153307-b36797799628 + knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260529191007-91499a17111f - knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707 + knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 + knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8 sigs.k8s.io/controller-runtime v0.23.3 ) @@ -188,7 +188,7 @@ require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect @@ -283,19 +283,19 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.65.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + 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.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.66.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.podman.io/storage v1.62.1-0.20260310180906-9819c3739308 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -306,9 +306,9 @@ require ( golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/grpc v1.80.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -318,7 +318,7 @@ require ( k8s.io/apiserver v0.35.5 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - knative.dev/networking v0.0.0-20260529020035-305789141b2b // indirect + knative.dev/networking v0.0.0-20260602144506-c8765a725c2b // indirect sigs.k8s.io/gateway-api v1.4.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.0 // indirect diff --git a/go.sum b/go.sum index bcd737d80f..a1b647c6bb 100644 --- a/go.sum +++ b/go.sum @@ -614,8 +614,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -1156,46 +1156,48 @@ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbE go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.1/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= +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= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0/go.mod h1:qZF+/lBs71APw8mlnEZcqZHMzqrYrsFiJOv83lX1OGo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s= +go.opentelemetry.io/otel/exporters/prometheus v0.66.0 h1:vkrK8PAznv2NKt2r+kdu252ccGzkEqLc2aSXbQIALYQ= +go.opentelemetry.io/otel/exporters/prometheus v0.66.0/go.mod h1:V/UB6D3vMF/UBOL5igAsAYnk1nG/bzYYTzvsB16cy7o= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 h1:bl2S7Ubua0Nms+D/gAmznQTd4dxxMA93aKbcpKqiTCs= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0/go.mod h1:L0hRV50XdVIODHUfWEqGRCXQvj2rV82STVo12FMFBU0= go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -1723,10 +1725,10 @@ google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1758,8 +1760,8 @@ google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1873,18 +1875,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8 h1:lhAVj1K+VJ0yyhZ6xlX/1OextPGK5qwr9o8laP6ATlM= -knative.dev/client/pkg v0.0.0-20260529150307-13f0230a3ca8/go.mod h1:ARAILgl+iZm8VX3II5oVXH7tb1368GvDaP5JsaRGiig= -knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e h1:R1qo705sW597mxdbkul0Yxmp3BXZvWaiX4wJxOaWLJg= -knative.dev/eventing v0.49.1-0.20260529032731-e2d6b95a971e/go.mod h1:esrMPXP79jb7ZabpI9K3WqJ+SZuqADDycMFxRBCyDDY= +knative.dev/client/pkg v0.0.0-20260603153307-b36797799628 h1:0fB3L3CV59NWGm0IrJjfPj6UgR+l8MHVmwgAU56t0K4= +knative.dev/client/pkg v0.0.0-20260603153307-b36797799628/go.mod h1:KdhDvCgj5Mtj9cJvQ5D2kdAuRr3BUgYc3UzZo2BuuHQ= +knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 h1:AlTrf8YFMDG/85cST9XKPG0OVB1mboYr734SY7zkcqU= +knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= -knative.dev/networking v0.0.0-20260529020035-305789141b2b h1:bVEeZ66aokJjPNHA8gwcEw6bbU/Lxhbt3jQyy4QZPP8= -knative.dev/networking v0.0.0-20260529020035-305789141b2b/go.mod h1:upXhnYXpr11YRpKlKiGTB4hzDTn4kUJ7gqDaqMFJaoo= -knative.dev/pkg v0.0.0-20260529191007-91499a17111f h1:eNG7TLdIUZN/IQInFAJ3RPHZZtNsyJkZwFsOYZd1cRQ= -knative.dev/pkg v0.0.0-20260529191007-91499a17111f/go.mod h1:3/ACpFr1GwpmELPWoBIIlQc8YMZ9KqdhsE4xNbEDbbo= -knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707 h1:L/a5EK+/c1QnHYfmHHNAE3Uvw39usxF5y7tjdKM6thk= -knative.dev/serving v0.49.1-0.20260531185038-1d4c5d97c707/go.mod h1:JBk2I8KkkjLu3g4QesIyLsK2skAXGbqELL/Pq/J7TR0= +knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= +knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= +knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 h1:Lf5I3oaFc0bErDP7yUbOxT+6GaGnVwuAiCAnDAn5F48= +knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622/go.mod h1:A6IJjMX0nATYJ4eJAcqXGLiMTg1AXsA9QtPd7nI9Tk0= +knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8 h1:wJbAh+ODH1rKCBzID4+p6UgCOVSi/O5AwubIp72o5DQ= +knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/hack/component-versions.json b/hack/component-versions.json index 0e55ee8f40..e2a7f65524 100644 --- a/hack/component-versions.json +++ b/hack/component-versions.json @@ -1,11 +1,11 @@ { "Contour": { - "version": "v1.22.0", + "version": "v1.22.1", "owner": "knative-extensions", "repo": "net-contour" }, "Eventing": { - "version": "v1.22.0", + "version": "v1.22.1", "owner": "knative", "repo": "eventing" }, @@ -25,7 +25,7 @@ "version": "v0.35.2" }, "Serving": { - "version": "v1.22.0", + "version": "v1.22.1", "owner": "knative", "repo": "serving" }, diff --git a/hack/component-versions.sh b/hack/component-versions.sh index 041b371f61..5eaf39aef7 100644 --- a/hack/component-versions.sh +++ b/hack/component-versions.sh @@ -11,9 +11,9 @@ set_versions() { kind_node_version=v1.34.0@sha256:7416a61b42b1662ca6ca89f02028ac133a309a2a30ba309614e8ec94d976dc5a # find source-of-truth in component-versions.json to add/modify components - knative_serving_version="v1.22.0" - knative_eventing_version="v1.22.0" - contour_version="v1.22.0" + knative_serving_version="v1.22.1" + knative_eventing_version="v1.22.1" + contour_version="v1.22.1" tekton_version="v1.1.0" pac_version="v0.35.2" keda_version="v2.17.0" From 31a94a17bb60c69f31cbe1a7534aa77ef391a4e8 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:04:06 +0200 Subject: [PATCH 10/44] fixup: wrap list error (#3872) --- cmd/errors.go | 28 +++++++++++++++++++++++++++ cmd/list.go | 53 ++++++++++++++++++++++++++------------------------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/cmd/errors.go b/cmd/errors.go index 60d3361b4a..e820df1d96 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -707,3 +707,31 @@ Installation guide: https://knative.dev/docs/serving/#installation`, e.Err) func (e *ErrListClusterConnection) Unwrap() error { return e.Err } + +// -------------------------------------------------------------------------- // + +type ErrListConflictingNamespaceFlags struct { + Err error +} + +func NewErrListConflictingNamespaceFlags() error { + return &ErrListConflictingNamespaceFlags{ + Err: errors.New("both --namespace and --all-namespaces specified"), + } +} + +func (e *ErrListConflictingNamespaceFlags) Error() string { + return fmt.Sprintf(`%v + +These flags are mutually exclusive: + --namespace List functions in a specific namespace + --all-namespaces List functions across all namespaces + +Use one or the other: + func list --namespace my-ns + func list --all-namespaces`, e.Err) +} + +func (e *ErrListConflictingNamespaceFlags) Unwrap() error { + return e.Err +} diff --git a/cmd/list.go b/cmd/list.go index 451d76e9d1..3e24e561ff 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -2,7 +2,6 @@ package cmd import ( "encoding/json" - "errors" "fmt" "io" "os" @@ -93,29 +92,7 @@ func runList(cmd *cobra.Command, _ []string, newClient ClientFactory) (err error } if len(items) == 0 { - if cfg.Namespace != "" { - fmt.Printf(`no functions found in namespace '%v' - -'func list' shows functions that have been deployed to your cluster. - -To see functions here: - func create --language go myfunction Create a function - func deploy --registry Deploy to cluster - func list See it listed - -Or check other namespaces: - func list --all-namespaces List functions in all namespaces -`, cfg.Namespace) - } else { - fmt.Println(`no functions found - -'func list' shows functions that have been deployed to your cluster. - -To see functions here: - func create --language go myfunction Create a function - func deploy --registry Deploy to cluster - func list See it listed`) - } + printNoFunctionsFound(cmd, cfg.Namespace) return } @@ -145,14 +122,38 @@ func newListConfig(cmd *cobra.Command) (cfg listConfig, err error) { cfg.Namespace = "" } - // specifying both -A and --namespace is logically inconsistent if cmd.Flags().Changed("namespace") && viper.GetBool("all-namespaces") { - err = errors.New("both --namespace and --all-namespaces specified") + err = NewErrListConflictingNamespaceFlags() } return } +func printNoFunctionsFound(cmd *cobra.Command, namespace string) { + msg := "no functions found" + if namespace != "" { + msg = fmt.Sprintf("no functions found in namespace '%v'", namespace) + } + + msg += ` + +'func list' shows functions that have been deployed to your cluster. + +To see functions here: + func create --language go myfunction Create a function + func deploy --registry Deploy to cluster + func list See it listed` + + if namespace != "" { + msg += ` + +Or check other namespaces: + func list --all-namespaces List functions in all namespaces` + } + + fmt.Fprintln(cmd.OutOrStdout(), msg) +} + // Output Formatting (serializers) // ------------------------------- From 118b55ffc96eb94e0a9df8c7bb72774156eb5f7e Mon Sep 17 00:00:00 2001 From: Rosai Date: Thu, 4 Jun 2026 20:28:07 +0530 Subject: [PATCH 11/44] Duplicate env and volume validation (#3878) * duplicate env and volume validation * Validate duplicate volume paths handling trailing slashes and fix nil name validation in ValidateBuildEnvs --- pkg/functions/function_envs.go | 20 ++++ pkg/functions/function_envs_unit_test.go | 120 ++++++++++++++------ pkg/functions/function_volumes.go | 16 ++- pkg/functions/function_volumes_unit_test.go | 44 +++++++ 4 files changed, 161 insertions(+), 39 deletions(-) diff --git a/pkg/functions/function_envs.go b/pkg/functions/function_envs.go index 10d538bed5..325dc4b437 100644 --- a/pkg/functions/function_envs.go +++ b/pkg/functions/function_envs.go @@ -119,7 +119,16 @@ func (e Env) KeyValuePair() string { // value: {{ configMap:configMapName:key }} # ENV from a key in configMap // - value: {{ configMap:configMapName }} # all key-pair values from configMap are set as ENV func ValidateEnvs(envs []Env) (errors []string) { + seenNames := make(map[string]int) for i, env := range envs { + if env.Name != nil { + name := *env.Name + if firstIdx, seen := seenNames[name]; seen { + errors = append(errors, fmt.Sprintf("env entry #%d has duplicate name %q (first defined at entry #%d)", i, name, firstIdx)) + } else { + seenNames[name] = i + } + } if env.Name == nil && env.Value == nil { errors = append(errors, fmt.Sprintf("env entry #%d is not properly set", i)) } else if env.Value == nil { @@ -162,11 +171,22 @@ func ValidateEnvs(envs []Env) (errors []string) { // - name: EXAMPLE2 # ENV from the local ENV var // value: {{ env:MY_ENV }} func ValidateBuildEnvs(envs []Env) (errors []string) { + seenNames := make(map[string]int) for i, env := range envs { + if env.Name != nil { + name := *env.Name + if firstIdx, seen := seenNames[name]; seen { + errors = append(errors, fmt.Sprintf("env entry #%d has duplicate name %q (first defined at entry #%d)", i, name, firstIdx)) + } else { + seenNames[name] = i + } + } if env.Name == nil && env.Value == nil { errors = append(errors, fmt.Sprintf("env entry #%d is not properly set", i)) } else if env.Value == nil { errors = append(errors, fmt.Sprintf("env entry #%d is missing value field, only name '%s' is set", i, *env.Name)) + } else if env.Name == nil { + errors = append(errors, fmt.Sprintf("env entry #%d is missing name field, only value '%s' is set", i, *env.Value)) } else { if err := utils.ValidateEnvVarName(*env.Name); err != nil { diff --git a/pkg/functions/function_envs_unit_test.go b/pkg/functions/function_envs_unit_test.go index ecc0644eb8..9d18ea1e58 100644 --- a/pkg/functions/function_envs_unit_test.go +++ b/pkg/functions/function_envs_unit_test.go @@ -9,6 +9,8 @@ func Test_validateBuildEnvs(t *testing.T) { name := "name" name2 := "name2" + name3 := "name3" + name4 := "name4" value := "value" value2 := "value2" @@ -110,11 +112,11 @@ func Test_validateBuildEnvs(t *testing.T) { Value: &valueLocalEnv, }, { - Name: &name, + Name: &name2, Value: &valueLocalEnv2, }, { - Name: &name, + Name: &name3, Value: &valueLocalEnv3, }, }, @@ -128,26 +130,49 @@ func Test_validateBuildEnvs(t *testing.T) { Value: &valueLocalEnv, }, { - Name: &name, + Name: &name2, Value: &valueLocalEnvIncorrect, }, { - Name: &name, + Name: &name3, Value: &valueLocalEnvIncorrect2, }, { - Name: &name, + Name: &name4, Value: &valueLocalEnvIncorrect3, }, }, 3, }, + { + "incorrect entry - duplicate env names", + []Env{ + { + Name: &name, + Value: &value, + }, + { + Name: &name, + Value: &value2, + }, + }, + 1, + }, + { + "incorrect entry - missing name", + []Env{ + { + Value: &value, + }, + }, + 1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := ValidateEnvs(tt.envs); len(got) != tt.errs { - t.Errorf("validateEnvs() = %v\n got %d errors but want %d", got, len(got), tt.errs) + if got := ValidateBuildEnvs(tt.envs); len(got) != tt.errs { + t.Errorf("ValidateBuildEnvs() = %v\n got %d errors but want %d", got, len(got), tt.errs) } }) } @@ -157,6 +182,13 @@ func Test_validateEnvs(t *testing.T) { name := "name" name2 := "name2" + name3 := "name3" + name4 := "name4" + name5 := "name5" + name6 := "name6" + name7 := "name7" + name8 := "name8" + name9 := "name9" value := "value" value2 := "value2" @@ -293,11 +325,11 @@ func Test_validateEnvs(t *testing.T) { Value: &valueLocalEnv, }, { - Name: &name, + Name: &name2, Value: &valueLocalEnv2, }, { - Name: &name, + Name: &name3, Value: &valueLocalEnv3, }, }, @@ -311,15 +343,15 @@ func Test_validateEnvs(t *testing.T) { Value: &valueLocalEnv, }, { - Name: &name, + Name: &name2, Value: &valueLocalEnvIncorrect, }, { - Name: &name, + Name: &name3, Value: &valueLocalEnvIncorrect2, }, { - Name: &name, + Name: &name4, Value: &valueLocalEnvIncorrect3, }, }, @@ -353,31 +385,31 @@ func Test_validateEnvs(t *testing.T) { Value: &valueConfigMapKey, }, { - Name: &name, + Name: &name2, Value: &valueConfigMapKey2, }, { - Name: &name, + Name: &name3, Value: &valueConfigMapKey3, }, { - Name: &name, + Name: &name4, Value: &valueConfigMapKey4, }, { - Name: &name, + Name: &name5, Value: &valueConfigMapKey5, }, { - Name: &name, + Name: &name6, Value: &valueConfigMapKey6, }, { - Name: &name, + Name: &name7, Value: &valueConfigMapKey7, }, { - Name: &name, + Name: &name8, Value: &valueConfigMapKey8, }, }, @@ -391,31 +423,31 @@ func Test_validateEnvs(t *testing.T) { Value: &valueSecretKey, }, { - Name: &name, + Name: &name2, Value: &valueSecretKey2, }, { - Name: &name, + Name: &name3, Value: &valueSecretKey3, }, { - Name: &name, + Name: &name4, Value: &valueSecretKey4, }, { - Name: &name, + Name: &name5, Value: &valueSecretKey5, }, { - Name: &name, + Name: &name6, Value: &valueSecretKey6, }, { - Name: &name, + Name: &name7, Value: &valueSecretKey7, }, { - Name: &name, + Name: &name8, Value: &valueSecretKey8, }, }, @@ -429,7 +461,7 @@ func Test_validateEnvs(t *testing.T) { Value: &valueSecretKey, }, { - Name: &name, + Name: &name2, Value: &valueConfigMapKey, }, }, @@ -453,15 +485,15 @@ func Test_validateEnvs(t *testing.T) { Value: &valueSecretKey, }, { - Name: &name, + Name: &name2, Value: &valueSecretKeyIncorrect, }, { - Name: &name, + Name: &name3, Value: &valueSecretKeyIncorrect2, }, { - Name: &name, + Name: &name4, Value: &valueSecretKeyIncorrect3, }, }, @@ -563,15 +595,15 @@ func Test_validateEnvs(t *testing.T) { Value: &value2, }, { - Name: &name, + Name: &name3, Value: &valueLocalEnv, }, { - Name: &name, + Name: &name4, Value: &valueLocalEnv2, }, { - Name: &name, + Name: &name5, Value: &valueLocalEnv3, }, { @@ -587,24 +619,38 @@ func Test_validateEnvs(t *testing.T) { Value: &valueConfigMap, }, { - Name: &name, + Name: &name6, Value: &valueSecretKey, }, { - Name: &name, + Name: &name7, Value: &valueSecretKey2, }, { - Name: &name, + Name: &name8, Value: &valueSecretKey3, }, { - Name: &name, + Name: &name9, Value: &valueConfigMapKey, }, }, 0, }, + { + "incorrect entry - duplicate env names", + []Env{ + { + Name: &name, + Value: &value, + }, + { + Name: &name, + Value: &value2, + }, + }, + 1, + }, } for _, tt := range tests { diff --git a/pkg/functions/function_volumes.go b/pkg/functions/function_volumes.go index b15f3bd1b1..923f5386bb 100644 --- a/pkg/functions/function_volumes.go +++ b/pkg/functions/function_volumes.go @@ -1,6 +1,9 @@ package functions -import "fmt" +import ( + "fmt" + "path" +) type Volume struct { Secret *string `yaml:"secret,omitempty" jsonschema:"oneof_required=secret"` @@ -81,8 +84,17 @@ func (v Volume) String() string { // - emptyDir: {} # mount EmptyDir as Volume // path: /etc/configMap-volume func validateVolumes(volumes []Volume) (errors []string) { - + seenPaths := make(map[string]int) for i, vol := range volumes { + if vol.Path != nil { + cleanedPath := path.Clean(*vol.Path) + if firstIdx, seen := seenPaths[cleanedPath]; seen { + errors = append(errors, fmt.Sprintf("volume entry #%d has duplicate path %q (first defined at entry #%d)", i, *vol.Path, firstIdx)) + } else { + seenPaths[cleanedPath] = i + } + } + numVolumes := 0 if vol.Secret != nil { numVolumes++ diff --git a/pkg/functions/function_volumes_unit_test.go b/pkg/functions/function_volumes_unit_test.go index 4a7decc907..adc86e8d51 100644 --- a/pkg/functions/function_volumes_unit_test.go +++ b/pkg/functions/function_volumes_unit_test.go @@ -10,6 +10,8 @@ func Test_validateVolumes(t *testing.T) { path2 := "path2" path3 := "path3" path4 := "path4" + pathSlash := "/path/" + pathNoSlash := "/path" secret := "secret" secret2 := "secret2" cm := "configMap" @@ -173,6 +175,48 @@ func Test_validateVolumes(t *testing.T) { }, 2, }, + { + "incorrect entry - duplicate volume paths", + []Volume{ + { + Secret: &secret, + Path: &path, + }, + { + Secret: &secret2, + Path: &path, + }, + }, + 1, + }, + { + "incorrect entry - duplicate volume paths with trailing slash", + []Volume{ + { + Secret: &secret, + Path: &pathSlash, + }, + { + Secret: &secret2, + Path: &pathNoSlash, + }, + }, + 1, + }, + { + "incorrect entry - duplicate volume paths both with trailing slash", + []Volume{ + { + Secret: &secret, + Path: &pathSlash, + }, + { + Secret: &secret2, + Path: &pathSlash, + }, + }, + 1, + }, } for _, tt := range tests { From a6ba68905bd5bafccf3ffc5df47fa1eff029b188 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Thu, 4 Jun 2026 11:10:06 -0400 Subject: [PATCH 12/44] upgrade to latest dependencies (#3879) bumping knative.dev/client/pkg b367977...b69eeca: > b69eeca upgrade to latest dependencies (# 2234) bumping knative.dev/serving 2a03fe7...516bc43: > 516bc43 Update net-contour nightly (# 16636) Signed-off-by: Knative Automation --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 75326eb6a1..b5eaf722ee 100644 --- a/go.mod +++ b/go.mod @@ -69,11 +69,11 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260603153307-b36797799628 + knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 - knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8 + knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667 sigs.k8s.io/controller-runtime v0.23.3 ) diff --git a/go.sum b/go.sum index a1b647c6bb..d5986cec1e 100644 --- a/go.sum +++ b/go.sum @@ -1875,8 +1875,8 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260603153307-b36797799628 h1:0fB3L3CV59NWGm0IrJjfPj6UgR+l8MHVmwgAU56t0K4= -knative.dev/client/pkg v0.0.0-20260603153307-b36797799628/go.mod h1:KdhDvCgj5Mtj9cJvQ5D2kdAuRr3BUgYc3UzZo2BuuHQ= +knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d h1:MKuXhG0NpOzDahNOho9m+XFuEUVA+5Fpc+GagnzbPYA= +knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d/go.mod h1:kK/0ECrKPUNVmn6kTkRuPAgG3amE5NTBlmmLrTZsVwg= knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 h1:AlTrf8YFMDG/85cST9XKPG0OVB1mboYr734SY7zkcqU= knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= @@ -1885,8 +1885,8 @@ knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82q knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 h1:Lf5I3oaFc0bErDP7yUbOxT+6GaGnVwuAiCAnDAn5F48= knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622/go.mod h1:A6IJjMX0nATYJ4eJAcqXGLiMTg1AXsA9QtPd7nI9Tk0= -knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8 h1:wJbAh+ODH1rKCBzID4+p6UgCOVSi/O5AwubIp72o5DQ= -knative.dev/serving v0.49.1-0.20260603160305-2a03fe72c7b8/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= +knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667 h1:s8HO3mubc8wZGAnZ4Dx0fqUUHWM1lpeKVBZ+/F18Hxc= +knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 165ccda3f1cdbabad537b0f70d9af8fa1b29952c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Mon, 8 Jun 2026 17:10:23 +0200 Subject: [PATCH 13/44] upgrade codecov/codecov-action from v5 to v7 (#3884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matej Vašek --- .github/workflows/functions.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/functions.yaml b/.github/workflows/functions.yaml index 7d809102e8..0cce7d1539 100644 --- a/.github/workflows/functions.yaml +++ b/.github/workflows/functions.yaml @@ -77,7 +77,7 @@ jobs: - name: Run Unit Tests run: make test - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: unit ${{ matrix.os }} @@ -175,7 +175,7 @@ jobs: - name: Run Integration Tests run: make test-integration - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: integration @@ -231,7 +231,7 @@ jobs: - name: Run Basic E2E Tests (core, metadata, remote) run: make test-e2e - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: e2e @@ -294,7 +294,7 @@ jobs: - name: Run E2E Podman Tests run: make test-e2e-podman - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: e2e @@ -384,7 +384,7 @@ jobs: run: make test-e2e-matrix # Coverage and Logs - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: e2e ${{ matrix.runtime }} @@ -439,7 +439,7 @@ jobs: - name: Run Config CI E2E Tests run: make test-e2e-config-ci - - uses: codecov/codecov-action@v5 + - uses: codecov/codecov-action@v7 with: files: ./coverage.txt flags: e2e-config-ci From 213fa333cf120b76153d037e972c7b8cac61c3d0 Mon Sep 17 00:00:00 2001 From: Elvand-Lie Date: Tue, 9 Jun 2026 01:46:22 +0700 Subject: [PATCH 14/44] Fix goroutine leak in SSH DialContext (#3882) * Fix background goroutine leak in ssh.DialContext * Fix goroutine leak in SSH DialContext: delegate to sshClient.DialContext The monitoring goroutine in DialContext violated net.Dialer.DialContext semantics: context should only govern connection establishment, not connection lifetime. The goroutine also leaked when connections were closed manually while the context remained open. Remove the goroutine and connWithDone wrapper entirely. Delegate to ssh.Client.DialContext which already handles context-cancellable dialing correctly per the Go contract. --- pkg/ssh/ssh_dialer.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/pkg/ssh/ssh_dialer.go b/pkg/ssh/ssh_dialer.go index 31ad18aeb7..4300267d98 100644 --- a/pkg/ssh/ssh_dialer.go +++ b/pkg/ssh/ssh_dialer.go @@ -127,17 +127,7 @@ func (n contextDialerFn) DialContext(ctx context.Context, network, address strin } func (d *dialer) DialContext(ctx context.Context, n, a string) (net.Conn, error) { - conn, err := d.Dial(d.network, d.addr) - if err != nil { - return nil, err - } - go func() { - if ctx != nil { - <-ctx.Done() - conn.Close() - } - }() - return conn, nil + return d.sshClient.DialContext(ctx, d.network, d.addr) } func (d *dialer) Dial(n, a string) (net.Conn, error) { From dfabe193b56b8574726eba7b60e7cb5180958477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Mon, 8 Jun 2026 22:01:23 +0200 Subject: [PATCH 15/44] fix: host builder can push to OCP internal registry (#3885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenShift's internal registry returns HTTP 500 for manifest HEAD requests by digest when the repository doesn't exist yet. Since remote.WriteIndex checks child manifests by digest, the push fails on a fresh repository. Before writing the index, perform a HEAD request by tag. If the registry returns 404, push an empty image to create the repository first. The empty image tag is then overwritten by the index push. Signed-off-by: Matej Vašek --- pkg/oci/pusher.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/pkg/oci/pusher.go b/pkg/oci/pusher.go index 1d24ca33e8..0140679f42 100644 --- a/pkg/oci/pusher.go +++ b/pkg/oci/pusher.go @@ -2,6 +2,7 @@ package oci import ( "context" + "errors" "fmt" "net/http" "os" @@ -12,8 +13,10 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" progress "github.com/schollz/progressbar/v3" fn "knative.dev/func/pkg/functions" @@ -181,7 +184,6 @@ func getBuildDir(f fn.Function) (string, error) { func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.ImageIndex, creds Credentials) error { oo := []remote.Option{ remote.WithContext(ctx), - remote.WithProgress(p.updates), remote.WithTransport(p.transport), } @@ -189,5 +191,23 @@ func (p *Pusher) writeIndex(ctx context.Context, ref name.Reference, ii v1.Image oo = append(oo, remote.WithAuth(creds)) } - return remote.WriteIndex(ref, ii, oo...) + // Ensure the repository exists. Some registries (notably + // OpenShift's internal registry) return HTTP 500 for manifest + // HEAD requests by digest when the repository doesn't exist yet. + // WriteIndex checks child manifests by digest, so the repository + // must exist beforehand. A HEAD by tag returns a proper 404, so + // use that to detect the missing repository and create it by + // pushing an empty image. The tag is overwritten by the index. + if _, err := remote.Head(ref, oo...); err != nil { + var terr *transport.Error + if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { + if err := remote.Write(ref, empty.Image, oo...); err != nil { + return fmt.Errorf("cannot create repo: %w", err) + } + } else { + return fmt.Errorf("cannot check repo existence: %w", err) + } + } + + return remote.WriteIndex(ref, ii, append(oo, remote.WithProgress(p.updates))...) } From 081d6639f463437c1a996c1ae931b9b7aacae311 Mon Sep 17 00:00:00 2001 From: Rosai Date: Tue, 9 Jun 2026 05:03:22 +0530 Subject: [PATCH 16/44] feat: implement AWS ECR credentials loader in Kubernetes keychains (#3864) * feat: implement AWS ECR credentials loader in Kubernetes keychains * Added spoofing suffix check test to prevent regression * Added error translation for ambient cred * Added helper for ErrCredentialsNotFound * improved isAWSCredentialsNotFound() * Update go.mod dependencies * add Timeout for ecr loader * resolve the KEDA pipeline test timeout failure by optimizing the ECR credential loader * closure-scoped state and caching only failures * refactor(k8s): resolve ECR credential helper goroutine leaks with synchronous lookup and semaphore * enforcing a wall-clock timeout * eliminates potential goroutine leaks * thread-safe caching * Relax Timing Threshold * remove timing checks * refactor(k8s): remove isECRRegistry and let ECR helper handle detection * fmt fix --- go.mod | 36 +++++------ go.sum | 72 +++++++++++----------- pkg/k8s/keychains.go | 126 +++++++++++++++++++++++++++++++++++++- pkg/k8s/keychains_test.go | 77 +++++++++++++++++++++++ 4 files changed, 255 insertions(+), 56 deletions(-) create mode 100644 pkg/k8s/keychains_test.go diff --git a/go.mod b/go.mod index b5eaf722ee..2ab0e9e960 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.1-0.20260605190426-37f5cc9e58a7 github.com/blang/semver/v4 v4.0.0 github.com/buildpacks/pack v0.40.1 github.com/chainguard-dev/git-urls v1.0.2 @@ -22,7 +23,7 @@ require ( github.com/creack/pty v1.1.24 github.com/docker/cli v29.4.0+incompatible github.com/docker/docker v28.5.2+incompatible - github.com/docker/docker-credential-helpers v0.9.5 + github.com/docker/docker-credential-helpers v0.9.6 github.com/docker/go-connections v0.7.0 github.com/functions-dev/func-operator v0.2.1 github.com/go-git/go-billy/v5 v5.8.0 @@ -99,23 +100,22 @@ require ( github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apex/log v1.9.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect - github.com/aws/smithy-go v1.24.2 // indirect - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.57.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect + github.com/aws/smithy-go v1.25.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/buildpacks/imgutil v0.0.0-20251202182233-51c1c8c186ea // indirect diff --git a/go.sum b/go.sum index d5986cec1e..e77717e627 100644 --- a/go.sum +++ b/go.sum @@ -145,40 +145,40 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= -github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= -github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI= -github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= -github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 h1:RtGctYMmkTerGClvdY6bHXdtly4FeYw9wz/NPz62LF8= -github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3/go.mod h1:vBfBu24Ka3/5UZtepbTV0gnc9VPLT8ok+0oDDaYAzn4= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 h1:1A/sI3LNMi3fhRI5TFLMwwo7ALAALSFVCSGvFlr1Iys= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10/go.mod h1:Diyyyz0b43X13pdi1mVMqlTwDjOmRbJMvDsqnduUYWM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c= +github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= +github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= +github.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM= +github.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15 h1:fyvgWTszojq8hEnMi8PPBTvZdTtEVmAVyo+NFLHBhH4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 h1:IOGsJ1xVWhsi+ZO7/NW8OuZZBtMJLZbk4P5HDjJO0jQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 h1:FPXsW9+gMuIeKmz7j6ENWcWtBGTe1kH8r9thNt5Uxx4= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U= +github.com/aws/aws-sdk-go-v2/service/ecr v1.57.1 h1:G/O4muLF2pe1UJBKEyF7J+kdokEEqFJjm42cU68FqH4= +github.com/aws/aws-sdk-go-v2/service/ecr v1.57.1/go.mod h1:KBzTxiBlQ2bB5XT367+t18i3Qe7NZDRyGKxdzN43aOw= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.14 h1:lbe55pyi/3OpOOPvNUS8qgMxbzufgy2K9lCEfHY5EGg= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.14/go.mod h1:8rnjX9IvRF+HRSu26P59etNYVtyUef2bR2tjxfm5pxw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 h1:oK/njaL8GtyEihkWMD4k3VgHCT64RQKkZwh0DG5j8ak= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 h1:ks8KBcZPh3PYISr5dAiXCM5/Thcuxk8l+PG4+A0exds= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo= +github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= +github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.1-0.20260605190426-37f5cc9e58a7 h1:p3AN+MVIOf3ckCuKV/C/kQi159z/ntOfApZCRDPwxEc= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.1-0.20260605190426-37f5cc9e58a7/go.mod h1:XHSJQyKxVCG2RQ3Yjuvgd2cxuNcE4DUdtcExDx3wy3w= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -307,8 +307,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= -github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0= +github.com/docker/docker-credential-helpers v0.9.6/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= diff --git a/pkg/k8s/keychains.go b/pkg/k8s/keychains.go index 7e65ceb8f3..d1bd747f36 100644 --- a/pkg/k8s/keychains.go +++ b/pkg/k8s/keychains.go @@ -1,12 +1,20 @@ package k8s import ( + "context" "encoding/json" + "errors" "fmt" + "io" "os" "path" "strings" + "sync" + "time" + ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" + dockercreds "github.com/docker/docker-credential-helpers/credentials" + "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/google" @@ -14,11 +22,22 @@ import ( "knative.dev/func/pkg/oci" ) +const ( + defaultECRTimeout = 5 * time.Second + defaultECRCacheTTL = 1 * time.Minute +) + +type cachedCredential struct { + creds oci.Credentials + err error + expiry time.Time +} + func GetGoogleCredentialLoader() []creds.CredentialsCallback { return []creds.CredentialsCallback{ func(registry string) (oci.Credentials, error) { if registry != "gcr.io" { - return oci.Credentials{}, creds.ErrCredentialsNotFound // skip if not GCR + return oci.Credentials{}, creds.ErrCredentialsNotFound } res, err := name.NewRegistry(registry) @@ -44,8 +63,111 @@ func GetGoogleCredentialLoader() []creds.CredentialsCallback { } } +func isAWSCredentialsNotFound(err error) bool { + if err == nil { + return false + } + if dockercreds.IsErrCredentialsNotFound(err) { + return true + } + + type awsError interface { + Code() string + Message() string + } + var awsErr awsError + if errors.As(err, &awsErr) { + if awsErr.Code() == "NoCredentialProviders" { + return true + } + } + + type smithyAPIError interface { + ErrorCode() string + ErrorMessage() string + } + var smithyErr smithyAPIError + if errors.As(err, &smithyErr) { + if smithyErr.ErrorCode() == "NoCredentialProviders" { + return true + } + } + + return false +} + func GetECRCredentialLoader() []creds.CredentialsCallback { - return []creds.CredentialsCallback{} // TODO: Implement ECR credentials loader + var cache sync.Map + + return []creds.CredentialsCallback{ + func(registry string) (oci.Credentials, error) { + + if val, ok := cache.Load(registry); ok { + cached := val.(cachedCredential) + if time.Now().Before(cached.expiry) { + return cached.creds, cached.err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultECRTimeout) + defer cancel() + + ecrHelper := ecr.NewECRHelper(ecr.WithLogger(io.Discard), ecr.WithContext(ctx)) + ecrKeychain := authn.NewKeychainFromHelper(ecrHelper) + + res, err := name.NewRegistry(registry) + if err != nil { + return oci.Credentials{}, fmt.Errorf("parse registry: %w", err) + } + + authenticator, err := ecrKeychain.Resolve(res) + if err != nil { + if isAWSCredentialsNotFound(err) { + err = creds.ErrCredentialsNotFound + } else { + err = fmt.Errorf("resolve ECR keychain: %w", err) + } + cache.Store(registry, cachedCredential{ + err: err, + expiry: time.Now().Add(defaultECRCacheTTL), + }) + return oci.Credentials{}, err + } + + authCfg, err := authenticator.Authorization() + if err != nil { + if isAWSCredentialsNotFound(err) { + err = creds.ErrCredentialsNotFound + } else { + err = fmt.Errorf("get authorization: %w", err) + } + cache.Store(registry, cachedCredential{ + err: err, + expiry: time.Now().Add(defaultECRCacheTTL), + }) + return oci.Credentials{}, err + } + + if authCfg.Username == "" || authCfg.Password == "" { + err = creds.ErrCredentialsNotFound + cache.Store(registry, cachedCredential{ + err: err, + expiry: time.Now().Add(defaultECRCacheTTL), + }) + return oci.Credentials{}, err + } + + credsVal := oci.Credentials{ + Username: authCfg.Username, + Password: authCfg.Password, + } + cache.Store(registry, cachedCredential{ + creds: credsVal, + expiry: time.Now().Add(defaultECRCacheTTL), + }) + return credsVal, nil + }, + } } func GetACRCredentialLoader() []creds.CredentialsCallback { diff --git a/pkg/k8s/keychains_test.go b/pkg/k8s/keychains_test.go new file mode 100644 index 0000000000..b6cefbea2b --- /dev/null +++ b/pkg/k8s/keychains_test.go @@ -0,0 +1,77 @@ +package k8s + +import ( + "errors" + "testing" + + "knative.dev/func/pkg/creds" +) + +func TestGetECRCredentialLoader(t *testing.T) { + loaders := GetECRCredentialLoader() + if len(loaders) == 0 { + t.Fatal("expected at least one ECR credential loader") + } + loader := loaders[0] + + t.Run("non-ECR registry returns ErrCredentialsNotFound", func(t *testing.T) { + _, err := loader("gcr.io") + if !errors.Is(err, creds.ErrCredentialsNotFound) { + t.Errorf("expected ErrCredentialsNotFound, got %v", err) + } + }) + + t.Run("missing AWS credentials returns ErrCredentialsNotFound", func(t *testing.T) { + tmp := t.TempDir() + t.Setenv("HOME", tmp) + t.Setenv("AWS_EC2_METADATA_DISABLED", "true") + t.Setenv("AWS_SHARED_CREDENTIALS_FILE", tmp+"/creds") + t.Setenv("AWS_CONFIG_FILE", tmp+"/config") + t.Setenv("AWS_ACCESS_KEY_ID", "") + t.Setenv("AWS_SECRET_ACCESS_KEY", "") + t.Setenv("AWS_SESSION_TOKEN", "") + t.Setenv("AWS_PROFILE", "") + t.Setenv("AWS_DEFAULT_PROFILE", "") + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", "") + t.Setenv("AWS_ROLE_ARN", "") + t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "") + t.Setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "") + + _, err := loader("123456789012.dkr.ecr.us-east-1.amazonaws.com") + if !errors.Is(err, creds.ErrCredentialsNotFound) { + t.Errorf("expected ErrCredentialsNotFound, got %v", err) + } + }) + + t.Run("caches failures for 1 minute", func(t *testing.T) { + loaders := GetECRCredentialLoader() + if len(loaders) == 0 { + t.Fatal("expected at least one ECR credential loader") + } + loader := loaders[0] + tmp := t.TempDir() + t.Setenv("HOME", tmp) + t.Setenv("AWS_EC2_METADATA_DISABLED", "true") + t.Setenv("AWS_SHARED_CREDENTIALS_FILE", tmp+"/creds") + t.Setenv("AWS_CONFIG_FILE", tmp+"/config") + t.Setenv("AWS_ACCESS_KEY_ID", "") + t.Setenv("AWS_SECRET_ACCESS_KEY", "") + t.Setenv("AWS_SESSION_TOKEN", "") + t.Setenv("AWS_PROFILE", "") + t.Setenv("AWS_DEFAULT_PROFILE", "") + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", "") + t.Setenv("AWS_ROLE_ARN", "") + t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "") + t.Setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "") + + // First call + _, err1 := loader("123456789012.dkr.ecr.us-east-1.amazonaws.com") + + // Second call should be from cache + _, err2 := loader("123456789012.dkr.ecr.us-east-1.amazonaws.com") + + if !errors.Is(err1, creds.ErrCredentialsNotFound) || !errors.Is(err2, creds.ErrCredentialsNotFound) { + t.Fatal("expected ErrCredentialsNotFound") + } + }) +} From e687d36bef5757b082059e099da1b79cd1a2c026 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Tue, 9 Jun 2026 15:48:24 -0400 Subject: [PATCH 17/44] upgrade to latest dependencies (#3880) bumping knative.dev/eventing a962fa5...fd4eb83: > fd4eb83 docs: fix KO_DEFAULTPLATFORMS example in DEVELOPMENT.md (# 9104) bumping knative.dev/client/pkg b69eeca...4784fe6: > 4784fe6 upgrade to latest dependencies (# 2236) > 3bae804 upgrade to latest dependencies (# 2235) Signed-off-by: Knative Automation --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2ab0e9e960..1cc2d33c3c 100644 --- a/go.mod +++ b/go.mod @@ -70,8 +70,8 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d - knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 + knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570 + knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667 diff --git a/go.sum b/go.sum index e77717e627..4294f58e9b 100644 --- a/go.sum +++ b/go.sum @@ -1875,10 +1875,10 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d h1:MKuXhG0NpOzDahNOho9m+XFuEUVA+5Fpc+GagnzbPYA= -knative.dev/client/pkg v0.0.0-20260604025604-b69eecae6f0d/go.mod h1:kK/0ECrKPUNVmn6kTkRuPAgG3amE5NTBlmmLrTZsVwg= -knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06 h1:AlTrf8YFMDG/85cST9XKPG0OVB1mboYr734SY7zkcqU= -knative.dev/eventing v0.49.1-0.20260602160610-a962fa5fbb06/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= +knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570 h1:8uK9OAjbJC2TFA0A2a4jSCQvI7ncI8UUzooxqtGNAR8= +knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570/go.mod h1:6libzpuf1JG45EVcB7fKT1E4Kok+XRI4gHr2Djvv21s= +knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f h1:knNXRMjMEmOcqCDPEQK02FXV+5FT1TaYbaOtshbcYCg= +knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= From cdf3c9640c7f6f2f01707a2e85d9ea293258a034 Mon Sep 17 00:00:00 2001 From: Vishwas Date: Wed, 10 Jun 2026 16:35:25 +0530 Subject: [PATCH 18/44] fix(k8s): prevent goroutine leak and deadlock in pod watcher (#3883) * fix(k8s): prevent goroutine leak and deadlock in pod watcher Signed-off-by: vishwas-droid * Update persistent_volumes.go * Update persistent_volumes.go * Update persistent_volumes.go * Update persistent_volumes.go --------- Signed-off-by: vishwas-droid --- pkg/k8s/persistent_volumes.go | 51 +++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/pkg/k8s/persistent_volumes.go b/pkg/k8s/persistent_volumes.go index c61d7c5aed..266af6a260 100644 --- a/pkg/k8s/persistent_volumes.go +++ b/pkg/k8s/persistent_volumes.go @@ -188,18 +188,33 @@ func runWithVolumeMounted(ctx context.Context, podImage string, podCommand []str return fmt.Errorf("cannot set up the watcher: %w", err) } defer watcher.Stop() + termCh := make(chan corev1.ContainerStateTerminated, 1) + errCh := make(chan error, 1) + go func() { - for event := range watcher.ResultChan() { - p, ok := event.Object.(*corev1.Pod) - if !ok { - continue - } - if len(p.Status.ContainerStatuses) > 0 { - termState := event.Object.(*corev1.Pod).Status.ContainerStatuses[0].State.Terminated - if termState != nil { - termCh <- *termState - break + for { + select { + case <-localCtx.Done(): + return + + case event, ok := <-watcher.ResultChan(): + if !ok { + errCh <- errors.New("kubernetes pod watch channel closed unexpectedly") + return + } + + p, ok := event.Object.(*corev1.Pod) + if !ok { + continue + } + + if len(p.Status.ContainerStatuses) > 0 { + termState := p.Status.ContainerStatuses[0].State.Terminated + if termState != nil { + termCh <- *termState + return + } } } } @@ -211,7 +226,21 @@ func runWithVolumeMounted(ctx context.Context, podImage string, podCommand []str return fmt.Errorf("cannot attach stdio to the pod: %w", err) } - termState := <-termCh + var termState corev1.ContainerStateTerminated + + select { + case termState = <-termCh: + + case watchErr := <-errCh: + if watchErr == nil { + return errors.New("pod execution failed during watch") + } + return fmt.Errorf("pod execution failed during watch: %w", watchErr) + + case <-ctx.Done(): + return ctx.Err() + } + if termState.ExitCode != 0 { cmdOut := strings.Trim(outBuff.String(), "\n") err = fmt.Errorf("the command failed: exitcode=%d, out=%q", termState.ExitCode, cmdOut) From be8b24d755a666f0627ebfa11620cb06ebe7887f Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:29:25 +0530 Subject: [PATCH 19/44] fix: update file permissions for written files in function.go (#3867) Changed file permission mode from os.ModePerm to 0644 for files written in the Stamp and WriteRuntimeBuiltImage functions to ensure proper access control. --- pkg/functions/function.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/functions/function.go b/pkg/functions/function.go index ff07f41225..86c96321ba 100644 --- a/pkg/functions/function.go +++ b/pkg/functions/function.go @@ -574,7 +574,7 @@ func (f Function) Stamp(oo ...stampOption) (err error) { } // Write out the hash - if err = os.WriteFile(filepath.Join(f.Root, RunDataDir, BuiltHash), []byte(hash), os.ModePerm); err != nil { + if err = os.WriteFile(filepath.Join(f.Root, RunDataDir, BuiltHash), []byte(hash), 0644); err != nil { return } @@ -830,7 +830,7 @@ func (f Function) WriteRuntimeBuiltImage(verbose bool) error { fmt.Printf("Writing built image: '%s' at path: '%s'\n", f.Build.Image, path) } - return os.WriteFile(path, []byte(f.Build.Image), os.ModePerm) + return os.WriteFile(path, []byte(f.Build.Image), 0644) } // getLastBuiltImage reads .func/built-image and returns its value or empty string From b5b40bb7426cbffdd10af596e7f872f213cc4ebf Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:52:26 +0530 Subject: [PATCH 20/44] refactor(invoke): remove redundant extension flag validation for cloudevent format (#3839) The check for the extension flag being valid only with the cloudevent format has been simplified by removing the redundant validation. This streamlines the code and clarifies the intended usage of the extension flag. --- cmd/invoke.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/invoke.go b/cmd/invoke.go index 9ff96d37fa..c6b00adaec 100644 --- a/cmd/invoke.go +++ b/cmd/invoke.go @@ -175,9 +175,6 @@ func runInvoke(cmd *cobra.Command, _ []string, newClient ClientFactory) (err err if effectiveFormat != "cloudevent" { return fmt.Errorf("--extension (-e) is only valid with cloudevents") } - if effectiveFormat != "" && effectiveFormat != "cloudevent" { - return fmt.Errorf("--extension flag is only valid with cloudevent format") - } } // Client instance from env vars, flags, args and user prompts (if --confirm) From 4ec9c0d589fe4e739bb653427287795ef533169b Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:06:25 +0530 Subject: [PATCH 21/44] fix(mcp): add readonly guard to buildHandler (#3840) * feat(mcp): add readonly mode check in buildHandler Implement a check in the buildHandler method to prevent build operations when the server is in readonly mode. This ensures that users are informed to enable write access before proceeding with builds, enhancing server state management. Signed-off-by: [Your Name] * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: [Your Name] Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- pkg/mcp/tools_build.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/mcp/tools_build.go b/pkg/mcp/tools_build.go index 52576187aa..e1f6a6758f 100644 --- a/pkg/mcp/tools_build.go +++ b/pkg/mcp/tools_build.go @@ -20,6 +20,11 @@ var buildTool = &mcp.Tool{ } func (s *Server) buildHandler(ctx context.Context, r *mcp.CallToolRequest, input BuildInput) (result *mcp.CallToolResult, output BuildOutput, err error) { + if s.readonly.Load() { + err = fmt.Errorf("the server is currently in read-only mode; to enable write operations, set FUNC_ENABLE_MCP_WRITE in the server environment and restart the server") + return + } + out, err := s.executor.Execute(ctx, "build", input.Args()...) if err != nil { err = fmt.Errorf("%w\n%s", err, string(out)) From b06458b0e05a2edc21fcc3d58a5f04fcfd31444e Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:06:33 +0530 Subject: [PATCH 22/44] fix: return error for malformed --extension flag values (#3852) * Implement extension parsing in invokeConfig with validation tests - Added `extensionsMap` method to `invokeConfig` to parse key=value extensions and return a map. - Introduced error handling for malformed extension entries. - Created unit tests `TestInvokeExtensionsMapValid` and `TestInvokeExtensionsMapMalformed` to validate the parsing logic and error conditions. This enhances the robustness of the extension handling in the invoke command. * Refactor extension handling in invokeConfig with improved validation - Renamed `extensionsMap` method to `parseExtensions` for clarity. - Enhanced validation to check for empty keys in extension entries. - Updated unit tests to cover cases for missing '=' and empty keys in extensions. This change improves the robustness and clarity of extension parsing in the invoke command. --- cmd/invoke.go | 17 +++++++++++------ cmd/invoke_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/cmd/invoke.go b/cmd/invoke.go index c6b00adaec..657edd9b55 100644 --- a/cmd/invoke.go +++ b/cmd/invoke.go @@ -183,6 +183,10 @@ func runInvoke(cmd *cobra.Command, _ []string, newClient ClientFactory) (err err // Message to send the running function built from parameters gathered // from the user (or defaults) + exts, err := cfg.parseExtensions() + if err != nil { + return err + } m := fn.InvokeMessage{ ID: cfg.ID, Source: cfg.Source, @@ -191,7 +195,7 @@ func runInvoke(cmd *cobra.Command, _ []string, newClient ClientFactory) (err err RequestType: strings.ToUpper(cfg.RequestType), Data: cfg.Data, Format: cfg.Format, - Extensions: cfg.extensionsMap(), + Extensions: exts, } // If --file was specified, use its content for message data @@ -312,15 +316,16 @@ func newInvokeConfig() (cfg invokeConfig, err error) { return } -func (c invokeConfig) extensionsMap() map[string]string { - extensionsMap := make(map[string]string) +func (c invokeConfig) parseExtensions() (map[string]string, error) { + result := make(map[string]string) for _, ext := range c.Extensions { parts := strings.SplitN(ext, "=", 2) - if len(parts) == 2 { - extensionsMap[parts[0]] = parts[1] + if len(parts) != 2 || strings.TrimSpace(parts[0]) == "" { + return nil, fmt.Errorf("invalid --extension %q: must be in key=value format", ext) } + result[parts[0]] = parts[1] } - return extensionsMap + return result, nil } func (c invokeConfig) prompt() (invokeConfig, error) { diff --git a/cmd/invoke_test.go b/cmd/invoke_test.go index 7feae43f03..ce4fbb3f3c 100644 --- a/cmd/invoke_test.go +++ b/cmd/invoke_test.go @@ -78,3 +78,40 @@ func TestInvoke(t *testing.T) { t.Fatal("function was not invoked") } } + +// TestInvokeExtensionsMapValid ensures well-formed key=value extensions are +// parsed correctly, including values that themselves contain '='. +func TestInvokeExtensionsMapValid(t *testing.T) { + c := invokeConfig{Extensions: []string{"key=value", "foo=bar=baz"}} + m, err := c.parseExtensions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if m["key"] != "value" { + t.Fatalf("expected key=value, got %q", m["key"]) + } + if m["foo"] != "bar=baz" { + t.Fatalf("expected foo=bar=baz, got %q", m["foo"]) + } +} + +// TestInvokeExtensionsMapMalformed ensures that extension entries missing '=' +// or with an empty key return an error rather than being silently dropped. +func TestInvokeExtensionsMapMalformed(t *testing.T) { + cases := []struct { + name string + exts []string + }{ + {"missing equals", []string{"valid=ok", "badformat"}}, + {"empty key", []string{"valid=ok", "=nokey"}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + c := invokeConfig{Extensions: tc.exts} + _, err := c.parseExtensions() + if err == nil { + t.Fatalf("expected error for %v, got nil", tc.exts) + } + }) + } +} From 6cdafd1c44594b1503fc8ff127d6a8e944be72bc Mon Sep 17 00:00:00 2001 From: Vi-shub <117119879+Vi-shub@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:34:29 +0530 Subject: [PATCH 23/44] git/github: default GitHub webhooks to TLS verification (#3714) Set repository webhook HookConfig insecure_ssl to 0 so GitHub verifies TLS when delivering to HTTPS payload URLs Signed-off-by: Vi-shub --- pkg/git/github/github.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git/github/github.go b/pkg/git/github/github.go index cdda582097..fb8c797003 100644 --- a/pkg/git/github/github.go +++ b/pkg/git/github/github.go @@ -26,7 +26,7 @@ func (c Client) CreateWebHook(ctx context.Context, repoOwner, repoName, payloadU Config: &github.HookConfig{ URL: github.Ptr(payloadURL), ContentType: github.Ptr("json"), - InsecureSSL: github.Ptr("1"), // TODO fix insecure (default should be 0) + InsecureSSL: github.Ptr("0"), Secret: github.Ptr(webhookSecret), }, } From dd23ca580ac8fa58897ca336fc4a485c41584b8a Mon Sep 17 00:00:00 2001 From: Elvand-Lie Date: Fri, 12 Jun 2026 00:04:38 +0700 Subject: [PATCH 24/44] fix(keda): add defensive length check when accessing service Ports (#3813) In pkg/keda/deployer.go, the code accesses service.Spec.Ports[0].Port to build the HTTP ScaledObject. The service object is fetched live from the Kubernetes cluster. While the upstream deployers will always generate a service with exactly one port during normal operations, this code lacks a defensive length check on the Ports slice. If a user or an external controller manually modifies the service in the cluster to remove its ports, the KEDA deployer will crash with an out of bounds panic during reconciliation. Added a defensive check for len(service.Spec.Ports) > 0 to prevent this. Fixes #3812 --- pkg/keda/deployer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/keda/deployer.go b/pkg/keda/deployer.go index 17300bfda1..72ac31cd01 100644 --- a/pkg/keda/deployer.go +++ b/pkg/keda/deployer.go @@ -136,6 +136,10 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResu } func (d *Deployer) httpScaledObject(f fn.Function, namespace string, deployment *v1.Deployment, service *corev1.Service, hosts []string) (*httpv1alpha1.HTTPScaledObject, error) { + if len(service.Spec.Ports) == 0 { + return nil, fmt.Errorf("service %s has no ports defined", service.Name) + } + labels, err := deployer.GenerateCommonLabels(f, d.decorator) if err != nil { return nil, fmt.Errorf("failed to generate common labels: %w", err) From 28dfd0c74a191e4ccfeaa14d344c4b1a6d94be22 Mon Sep 17 00:00:00 2001 From: Elvand-Lie Date: Fri, 12 Jun 2026 00:18:27 +0700 Subject: [PATCH 25/44] fix(k8s): guard against nil vol.Path in ProcessVolumes (#3805) ProcessVolumes dereferences vol.Path (*string) at line 925 without a nil check. If a Volume entry has a source type (secret, configMap, PVC) but no path field set, the nil pointer dereference crashes the process. None of the callers of ProcessVolumes (k8s deployer, knative deployer) gate on validateVolumes before calling ProcessVolumes, so a malformed func.yaml can trigger this crash. Added a nil guard that returns a descriptive error instead of panicking. Fixes #3796 --- pkg/k8s/deployer.go | 3 ++ pkg/k8s/deployer_test.go | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/pkg/k8s/deployer.go b/pkg/k8s/deployer.go index 1341515ac8..f9f88879d6 100644 --- a/pkg/k8s/deployer.go +++ b/pkg/k8s/deployer.go @@ -922,6 +922,9 @@ func ProcessVolumes(volumes []fn.Volume, referencedSecrets, referencedConfigMaps } if volumeName != "" { + if vol.Path == nil { + return nil, nil, fmt.Errorf("volume %q is missing required path field", volumeName) + } if !usedPaths.Has(*vol.Path) { newVolumeMounts = append(newVolumeMounts, corev1.VolumeMount{ Name: volumeName, diff --git a/pkg/k8s/deployer_test.go b/pkg/k8s/deployer_test.go index ec48829493..61fb3658e1 100644 --- a/pkg/k8s/deployer_test.go +++ b/pkg/k8s/deployer_test.go @@ -2,6 +2,7 @@ package k8s import ( "os" + "strings" "testing" corev1 "k8s.io/api/core/v1" @@ -424,3 +425,67 @@ func TestGenerateTriggerName_DifferentBrokers(t *testing.T) { t.Errorf("Different brokers should produce different names: %s, %s, %s", name1, name2, name3) } } + +func Test_ProcessVolumes_NilPath(t *testing.T) { + secretName := "my-secret" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {Secret: &secretName, Path: nil}, + } + + _, _, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err == nil { + t.Fatal("expected error for volume with nil path, got nil") + } + if !strings.Contains(err.Error(), "missing required path") { + t.Fatalf("unexpected error message: %s", err.Error()) + } +} + +func Test_ProcessVolumes_NilPathConfigMap(t *testing.T) { + configMapName := "my-config" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {ConfigMap: &configMapName, Path: nil}, + } + + _, _, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err == nil { + t.Fatal("expected error for volume with nil path, got nil") + } + if !strings.Contains(err.Error(), "missing required path") { + t.Fatalf("unexpected error message: %s", err.Error()) + } +} + +func Test_ProcessVolumes_ValidPath(t *testing.T) { + secretName := "my-secret" + path := "/etc/secret" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {Secret: &secretName, Path: &path}, + } + + vols, mounts, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(vols) != 1 { + t.Fatalf("expected 1 volume, got %d", len(vols)) + } + if len(mounts) != 1 { + t.Fatalf("expected 1 mount, got %d", len(mounts)) + } + if mounts[0].MountPath != "/etc/secret" { + t.Errorf("expected mount path /etc/secret, got %s", mounts[0].MountPath) + } +} From f8880a2a432916e70e05dd5cda0aa0c56c4d1ba8 Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:48:35 +0530 Subject: [PATCH 26/44] fix: update delete tool requirements in instructions (#3824) Clarified that the `delete` tool now requires exactly one of `path` or `name`, removing the previous no-argument CWD mode. This change enhances the accuracy of the documentation regarding the tool's functionality. --- pkg/mcp/instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mcp/instructions.md b/pkg/mcp/instructions.md index b16f30b35d..45af704c81 100644 --- a/pkg/mcp/instructions.md +++ b/pkg/mcp/instructions.md @@ -39,7 +39,7 @@ This is essential because: **Exceptions:** - The `list` tool operates on the cluster, not local files, so it does NOT use a path parameter (it uses namespace instead) -- The `delete` tool can accept an optional named Function to delete, in which case the path is not necessary (no named parameter indicates 'delete the Function in my cwd') +- The `delete` tool requires exactly one of `path` or `name`; it does NOT support a no-argument CWD mode (the MCP server process has its own working directory unrelated to the Function being managed) ## Deployment Behavior From f435de567ef533726dee000e4af15d40f5937044 Mon Sep 17 00:00:00 2001 From: Ankit sisodya <118156935+Ankitsinghsisodya@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:48:44 +0530 Subject: [PATCH 27/44] fix: improve error reporting in TestTool_Create_Args test (#3825) Updated the error handling in the TestTool_Create_Args test to provide a more descriptive message when an unexpected error result occurs. This enhances the clarity of test failures and aids in debugging. --- pkg/mcp/tools_create_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mcp/tools_create_test.go b/pkg/mcp/tools_create_test.go index 737102b8a3..d37b284864 100644 --- a/pkg/mcp/tools_create_test.go +++ b/pkg/mcp/tools_create_test.go @@ -75,7 +75,7 @@ func TestTool_Create_Args(t *testing.T) { t.Fatal(err) } if result.IsError { - t.Fatal(err) + t.Fatalf("unexpected error result: %v", result) } if !executor.ExecuteInvoked { t.Fatal("executor was not invoked") From fa2e1821a8440a9da91e64435ef5e958a590ecd9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:31:27 +0000 Subject: [PATCH 28/44] chore: update Quarkus platform version to 3.36.2 (#3890) Co-authored-by: Knative Automation --- generate/zz_filesystem_generated.go | 136 +++++++++++++------------- templates/quarkus/cloudevents/pom.xml | 2 +- templates/quarkus/http/pom.xml | 2 +- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/generate/zz_filesystem_generated.go b/generate/zz_filesystem_generated.go index 035f87f97b..ad03c739c2 100644 --- a/generate/zz_filesystem_generated.go +++ b/generate/zz_filesystem_generated.go @@ -7038,39 +7038,39 @@ var TemplatesZip = []byte{ 0xfe, 0x7e, 0x84, 0x63, 0x06, 0xb6, 0x4f, 0xe6, 0xb5, 0x86, 0xca, 0x84, 0x99, 0x33, 0xdf, 0xca, 0xf4, 0x9a, 0x54, 0x93, 0x64, 0x7c, 0xdd, 0x1f, 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, - 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x51, 0x6f, - 0xe3, 0x28, 0x10, 0x7e, 0xcf, 0xaf, 0xb0, 0xa2, 0xbe, 0x1a, 0x92, 0xeb, 0xde, 0xdd, 0xaa, 0xe2, 0x58, 0xed, 0xc3, 0x9d, 0xae, 0x52, 0xbb, 0x5b, 0xa9, 0xbd, 0xd5, 0xbd, 0x52, 0x3c, 0x76, 0x68, - 0x6d, 0xf0, 0x02, 0x4e, 0x13, 0x55, 0xf9, 0xef, 0x2b, 0xc0, 0x76, 0x6c, 0xc7, 0x6e, 0xb3, 0x55, 0xb6, 0x6f, 0x66, 0xe6, 0x63, 0x18, 0xbe, 0xf9, 0x0c, 0x03, 0xf9, 0xb4, 0x29, 0xf2, 0x68, 0x0d, - 0xda, 0x08, 0x25, 0xff, 0x9a, 0x2f, 0xd1, 0x62, 0xfe, 0x89, 0xce, 0x48, 0xa9, 0xd5, 0x03, 0x70, 0x1b, 0x6d, 0x8c, 0xb8, 0x30, 0x7c, 0x05, 0x05, 0xbb, 0x52, 0x9c, 0x59, 0x8f, 0x59, 0x59, 0x5b, - 0x5e, 0x60, 0x5c, 0xb0, 0x35, 0x48, 0xc4, 0x4a, 0xc6, 0x57, 0x80, 0x94, 0xce, 0xf0, 0xcd, 0xd7, 0x6b, 0xfc, 0x01, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, - 0x44, 0x1b, 0x93, 0xcc, 0xa3, 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0xdb, 0x62, 0xb1, - 0xc4, 0xff, 0x5f, 0x5f, 0xdd, 0xfa, 0x94, 0x63, 0x21, 0x8d, 0x65, 0x92, 0xc3, 0x9c, 0xce, 0xa2, 0x88, 0x14, 0x2a, 0x81, 0xfc, 0x5b, 0xd8, 0x29, 0xf5, 0x11, 0x09, 0xee, 0xd9, 0x1c, 0x28, 0xd3, - 0xaa, 0x2a, 0x2f, 0x13, 0xaa, 0x74, 0x86, 0x18, 0x2f, 0x80, 0xe0, 0xc6, 0xe2, 0xbc, 0x4c, 0x5b, 0x91, 0x32, 0x6e, 0x2f, 0x13, 0x9a, 0x56, 0x92, 0x3b, 0x3e, 0x08, 0xee, 0x18, 0x1d, 0xa6, 0xe6, - 0x92, 0x2e, 0xdd, 0x0a, 0xf1, 0xed, 0x97, 0xcf, 0x37, 0xb7, 0xff, 0x7e, 0xbd, 0x23, 0x78, 0xdd, 0x59, 0xa5, 0xd4, 0xaa, 0x04, 0x6d, 0x05, 0x18, 0xea, 0x37, 0x45, 0xb8, 0x2a, 0x4a, 0x91, 0x83, - 0x8e, 0xcb, 0xbc, 0xca, 0x84, 0x44, 0x0d, 0xf8, 0x1c, 0x7d, 0x44, 0x4b, 0x82, 0xa7, 0xdc, 0x61, 0x72, 0xa0, 0xac, 0xc1, 0x20, 0x0d, 0x39, 0x30, 0x03, 0x74, 0xb9, 0x24, 0x78, 0xc2, 0x15, 0xe6, - 0xd5, 0x45, 0x46, 0xf7, 0x95, 0xc8, 0x13, 0x64, 0x54, 0xa5, 0x39, 0xfc, 0x2d, 0xb9, 0x4a, 0x84, 0xcc, 0xe8, 0x7f, 0x77, 0xff, 0xc4, 0x1f, 0x09, 0x7e, 0x11, 0xd3, 0x0f, 0xa3, 0xa1, 0x54, 0xda, - 0x0a, 0x99, 0x21, 0x55, 0xd9, 0xb2, 0xb2, 0x53, 0xa1, 0x26, 0x71, 0x21, 0xdc, 0xf7, 0x8a, 0xe9, 0xc7, 0xca, 0xa0, 0x32, 0x67, 0x36, 0x55, 0xba, 0x40, 0x0d, 0xbf, 0xb1, 0x48, 0x68, 0xed, 0x8c, - 0xef, 0x55, 0x41, 0xf0, 0x8b, 0xc8, 0x89, 0x60, 0xbe, 0x9a, 0xce, 0x2f, 0x14, 0x1a, 0x3a, 0x47, 0x22, 0xb6, 0xf0, 0x89, 0x70, 0xfb, 0x3a, 0x9d, 0x7f, 0x40, 0x7f, 0x8c, 0x04, 0xe8, 0x57, 0xca, - 0x3c, 0x8a, 0xf2, 0xf2, 0xce, 0x50, 0xab, 0x2b, 0x20, 0xb8, 0x19, 0xd5, 0xbe, 0x4a, 0x43, 0x2a, 0x34, 0x1c, 0x4a, 0xc0, 0xe9, 0xe8, 0xfa, 0x4f, 0x82, 0xa7, 0x10, 0x4e, 0x51, 0xb8, 0x2f, 0x29, - 0x92, 0x40, 0x09, 0x32, 0x01, 0xc9, 0xb7, 0xd7, 0x4c, 0xb2, 0x0c, 0x0a, 0x90, 0xb6, 0x5e, 0xa8, 0x75, 0xb5, 0xf2, 0xeb, 0xe1, 0x1b, 0x53, 0xe7, 0x67, 0x38, 0x7b, 0x9e, 0x24, 0x66, 0xd7, 0xfb, - 0x41, 0xea, 0x79, 0x9d, 0x3f, 0x62, 0x64, 0x6a, 0xa7, 0x4a, 0xbb, 0xe1, 0xdf, 0x53, 0x07, 0x68, 0x76, 0x36, 0x32, 0xbb, 0x76, 0xed, 0x7a, 0x3f, 0x54, 0x3d, 0xcd, 0x6e, 0x4b, 0xa0, 0xa5, 0x93, - 0x86, 0xff, 0xda, 0x3b, 0x0c, 0x57, 0x25, 0x50, 0x51, 0x38, 0xe1, 0x11, 0x1c, 0x46, 0xcd, 0xce, 0xf1, 0x70, 0xeb, 0x1d, 0x4b, 0xc3, 0x26, 0x9e, 0xa2, 0x73, 0x84, 0xcc, 0x11, 0x2a, 0x5b, 0x22, - 0xf7, 0xa2, 0x3b, 0xa0, 0xad, 0x4b, 0x5a, 0xa3, 0xf2, 0xb4, 0x92, 0xdf, 0xb7, 0xf1, 0xa3, 0x64, 0x56, 0xac, 0x21, 0x86, 0x35, 0x48, 0x6b, 0x0e, 0x19, 0x1b, 0xd9, 0xc1, 0xe9, 0x52, 0x30, 0x05, - 0xcb, 0x73, 0xbd, 0x85, 0x78, 0x05, 0x2c, 0xb7, 0xab, 0x77, 0x5e, 0x9d, 0x69, 0xfe, 0xce, 0x2b, 0x3e, 0x54, 0x52, 0xd8, 0xdf, 0xc7, 0x64, 0x59, 0x8b, 0xc8, 0x82, 0xe9, 0x4b, 0xe8, 0xa7, 0xd3, - 0xd1, 0x60, 0x6c, 0xcc, 0x8c, 0xfb, 0x9b, 0x93, 0x17, 0x73, 0xea, 0x03, 0xdf, 0x9e, 0xd1, 0x88, 0xa0, 0xfd, 0x71, 0x5e, 0xa3, 0xc3, 0x69, 0xb2, 0x3f, 0x0a, 0xc2, 0x98, 0x9e, 0xf4, 0x18, 0x68, - 0xe8, 0x0d, 0x97, 0x7f, 0x58, 0xe1, 0xb4, 0xff, 0x3e, 0x6c, 0x2c, 0x48, 0x67, 0x6b, 0x4e, 0xd7, 0x8e, 0xa1, 0x8b, 0x02, 0x5e, 0xd9, 0xbe, 0xb1, 0x6b, 0xee, 0x5a, 0xdd, 0xce, 0x15, 0xcb, 0x4d, - 0xdf, 0x56, 0x5b, 0xa9, 0x67, 0x90, 0x60, 0xff, 0x3d, 0x0a, 0xc8, 0x40, 0x82, 0x66, 0x16, 0x62, 0xae, 0x12, 0x38, 0x1a, 0x18, 0xbb, 0x6a, 0x9a, 0x31, 0x78, 0xb0, 0xf5, 0xf3, 0xc6, 0x23, 0x89, - 0x77, 0x8c, 0xfb, 0x9a, 0xe2, 0x7e, 0x51, 0x0f, 0x6b, 0xdc, 0x29, 0x45, 0xa8, 0xd1, 0xa0, 0xe5, 0x78, 0xb5, 0x58, 0x13, 0x2d, 0xca, 0x68, 0xad, 0xb8, 0x92, 0xa9, 0xc8, 0x2a, 0xcd, 0x86, 0x94, - 0xb7, 0x7d, 0xd0, 0x67, 0x9d, 0x0d, 0x78, 0x27, 0x4c, 0x67, 0x34, 0x2e, 0x99, 0x66, 0x05, 0x58, 0xd0, 0xfe, 0x1c, 0xcc, 0xfa, 0x64, 0x8c, 0x4f, 0x76, 0xf6, 0x91, 0xe5, 0xde, 0x40, 0xc9, 0xe0, - 0xfe, 0x7d, 0x95, 0x92, 0x89, 0xfb, 0xfa, 0x27, 0x29, 0x31, 0x5b, 0x63, 0xa1, 0xb8, 0x09, 0xd7, 0xfb, 0xf6, 0x1b, 0xd3, 0x82, 0xdd, 0xe7, 0x30, 0x64, 0xe7, 0x81, 0xad, 0x19, 0xaa, 0xac, 0xc8, - 0x51, 0xae, 0xb2, 0xcc, 0x35, 0x57, 0x85, 0xbf, 0xa8, 0xb4, 0xef, 0x64, 0x1f, 0xee, 0x95, 0x31, 0xce, 0x53, 0x1b, 0xd1, 0x95, 0xca, 0xc2, 0x45, 0xa6, 0x09, 0x9e, 0x9e, 0xda, 0x5f, 0x22, 0xf4, - 0x92, 0x2b, 0x55, 0x00, 0x3d, 0x7b, 0xde, 0x0f, 0x76, 0x4d, 0x97, 0xe9, 0x3d, 0xbd, 0x82, 0xbc, 0x9a, 0xfa, 0xe9, 0x6a, 0x93, 0x32, 0x91, 0x1b, 0x96, 0xfe, 0x92, 0xda, 0x9c, 0xee, 0xd0, 0x10, - 0xd2, 0x42, 0x16, 0xf6, 0x1a, 0x87, 0x63, 0x7b, 0xfa, 0x58, 0x58, 0x83, 0x16, 0xe9, 0xf6, 0xc8, 0x93, 0xe0, 0x25, 0x05, 0x45, 0x47, 0xab, 0xc8, 0x23, 0x43, 0xb7, 0x81, 0x44, 0xc1, 0x32, 0x40, - 0x25, 0xb3, 0x2b, 0x7a, 0xf6, 0xdc, 0x7f, 0x05, 0x24, 0x42, 0x03, 0xb7, 0x4a, 0x6f, 0x77, 0x78, 0xe8, 0x4a, 0x85, 0x64, 0xf9, 0x17, 0x56, 0xc0, 0x2e, 0xd6, 0x95, 0x94, 0x4e, 0x5e, 0x87, 0xf1, - 0x0e, 0xd7, 0xfc, 0xf5, 0xea, 0x8d, 0xde, 0xa2, 0xe0, 0xe8, 0x38, 0x15, 0x47, 0xd3, 0x4a, 0x0e, 0xae, 0x37, 0x9d, 0xd2, 0xcd, 0xa8, 0xee, 0x40, 0xdb, 0x1b, 0xdb, 0xbd, 0xb7, 0x52, 0xd1, 0x26, - 0xd0, 0x0c, 0xdb, 0x20, 0x22, 0xa1, 0x81, 0x72, 0x82, 0x45, 0xa7, 0xad, 0xe0, 0x56, 0xac, 0x07, 0xa9, 0x35, 0xcf, 0xd0, 0x6d, 0x2f, 0x5b, 0xc9, 0x0a, 0x68, 0x23, 0xf8, 0x41, 0x27, 0xe9, 0xe1, - 0x0c, 0x82, 0x0f, 0x03, 0x1f, 0xbc, 0x6e, 0xbd, 0xb1, 0x79, 0xec, 0xa4, 0x2c, 0x37, 0xc3, 0xb7, 0x8f, 0x07, 0xb4, 0xb7, 0x3d, 0xe3, 0x8f, 0x4e, 0x2a, 0xbe, 0x85, 0x6f, 0xf2, 0x18, 0x75, 0xce, - 0x06, 0x59, 0xed, 0x5b, 0x70, 0xdc, 0x21, 0xa5, 0x1d, 0x18, 0x3a, 0x6b, 0x1f, 0xa1, 0x74, 0xf6, 0x23, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xa4, 0x76, 0x1e, 0x9c, 0xf9, 0x03, 0x00, + 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0xdf, 0x6f, + 0xe3, 0x28, 0x10, 0x7e, 0xcf, 0x5f, 0x61, 0x45, 0x7d, 0x35, 0x24, 0xdb, 0xfb, 0xb1, 0xaa, 0x38, 0x56, 0xfb, 0x70, 0xa7, 0xab, 0xd4, 0xee, 0x56, 0x6a, 0x6f, 0x75, 0xaf, 0x14, 0x8f, 0x1d, 0x5a, + 0x1b, 0xbc, 0x80, 0xd3, 0x44, 0x55, 0xfe, 0xf7, 0x15, 0x60, 0x3b, 0xb6, 0x63, 0xb7, 0xd9, 0x2a, 0xdb, 0x37, 0x33, 0xf3, 0x31, 0x0c, 0xdf, 0x7c, 0x86, 0x81, 0x7c, 0xda, 0x14, 0x79, 0xb4, 0x06, + 0x6d, 0x84, 0x92, 0x7f, 0xcd, 0x97, 0x68, 0x31, 0xff, 0x44, 0x67, 0xa4, 0xd4, 0xea, 0x01, 0xb8, 0x8d, 0x36, 0x46, 0x5c, 0x18, 0xbe, 0x82, 0x82, 0x5d, 0x29, 0xce, 0xac, 0xc7, 0xac, 0xac, 0x2d, + 0x2f, 0x30, 0x2e, 0xd8, 0x1a, 0x24, 0x62, 0x25, 0xe3, 0x2b, 0x40, 0x4a, 0x67, 0xf8, 0xe6, 0xeb, 0x35, 0xfe, 0x0d, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, + 0x44, 0x1b, 0x93, 0xcc, 0xa3, 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0x61, 0xb1, 0x58, + 0xe2, 0xff, 0xaf, 0xaf, 0x6e, 0x7d, 0xca, 0xb1, 0x90, 0xc6, 0x32, 0xc9, 0x61, 0x4e, 0x67, 0x51, 0x44, 0x0a, 0x95, 0x40, 0xfe, 0x2d, 0xec, 0x94, 0xfa, 0x88, 0x04, 0xf7, 0x6c, 0x0e, 0x94, 0x69, + 0x55, 0x95, 0x97, 0x09, 0x55, 0x3a, 0x43, 0x8c, 0x17, 0x40, 0x70, 0x63, 0x71, 0x5e, 0xa6, 0xad, 0x48, 0x19, 0xb7, 0x97, 0x09, 0x4d, 0x2b, 0xc9, 0x1d, 0x1f, 0x04, 0x77, 0x8c, 0x0e, 0x53, 0x73, + 0x49, 0x97, 0x6e, 0x85, 0xf8, 0xf6, 0xcb, 0xe7, 0x9b, 0xdb, 0x7f, 0xbf, 0xde, 0x11, 0xbc, 0xee, 0xac, 0x52, 0x6a, 0x55, 0x82, 0xb6, 0x02, 0x0c, 0xf5, 0x9b, 0x22, 0x5c, 0x15, 0xa5, 0xc8, 0x41, + 0xc7, 0x65, 0x5e, 0x65, 0x42, 0xa2, 0x06, 0x7c, 0x8e, 0x3e, 0xa2, 0x25, 0xc1, 0x53, 0xee, 0x30, 0x39, 0x50, 0xd6, 0x60, 0x90, 0x86, 0x1c, 0x98, 0x01, 0xba, 0x5c, 0x12, 0x3c, 0xe1, 0x0a, 0xf3, + 0xea, 0x22, 0xa3, 0xfb, 0x4a, 0xe4, 0x09, 0x32, 0xaa, 0xd2, 0x1c, 0xfe, 0x96, 0x5c, 0x25, 0x42, 0x66, 0xf4, 0xbf, 0xbb, 0x7f, 0xe2, 0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, + 0x85, 0xcc, 0x90, 0xaa, 0x6c, 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10, 0xee, 0x7b, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b, 0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, + 0xf7, 0xaa, 0x20, 0xf8, 0x45, 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17, 0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0x7d, 0x9d, 0xce, 0xff, 0x40, 0x1f, 0x46, 0x02, 0xf4, 0x2b, 0x65, + 0x1e, 0x45, 0x79, 0x79, 0x67, 0xa8, 0xd5, 0x15, 0x10, 0xdc, 0x8c, 0x6a, 0x5f, 0xa5, 0x21, 0x15, 0x1a, 0x0e, 0x25, 0xe0, 0x74, 0x74, 0xfd, 0x27, 0xc1, 0x53, 0x08, 0xa7, 0x28, 0xdc, 0x97, 0x14, + 0x49, 0xa0, 0x04, 0x99, 0x80, 0xe4, 0xdb, 0x6b, 0x26, 0x59, 0x06, 0x05, 0x48, 0x5b, 0x2f, 0xd4, 0xba, 0x5a, 0xf9, 0xf5, 0xf0, 0x8d, 0xa9, 0xf3, 0x33, 0x9c, 0x3d, 0x4f, 0x12, 0xb3, 0xeb, 0xfd, + 0x20, 0xf5, 0xbc, 0xce, 0x1f, 0x31, 0x32, 0xb5, 0x53, 0xa5, 0xdd, 0xf0, 0xef, 0xa9, 0x03, 0x34, 0x3b, 0x1b, 0x99, 0x5d, 0xbb, 0x76, 0xbd, 0x1f, 0xaa, 0x9e, 0x66, 0xb7, 0x25, 0xd0, 0xd2, 0x49, + 0xc3, 0x7f, 0xed, 0x1d, 0x86, 0xab, 0x12, 0xa8, 0x28, 0x9c, 0xf0, 0x08, 0x0e, 0xa3, 0x66, 0xe7, 0x78, 0xb8, 0xf5, 0x8e, 0xa5, 0x61, 0x13, 0x4f, 0xd1, 0x39, 0x42, 0xe6, 0x08, 0x95, 0x2d, 0x91, + 0x7b, 0xd1, 0x1d, 0xd0, 0xd6, 0x25, 0xad, 0x51, 0x79, 0x5a, 0xc9, 0xef, 0xdb, 0xf8, 0x51, 0x32, 0x2b, 0xd6, 0x10, 0xc3, 0x1a, 0xa4, 0x35, 0x87, 0x8c, 0x8d, 0xec, 0xe0, 0x74, 0x29, 0x98, 0x82, + 0xe5, 0xb9, 0xde, 0x42, 0xbc, 0x02, 0x96, 0xdb, 0xd5, 0x3b, 0xaf, 0xce, 0x34, 0x7f, 0xe7, 0x15, 0x1f, 0x2a, 0x29, 0xec, 0xef, 0x63, 0xb2, 0xac, 0x45, 0x64, 0xc1, 0xf4, 0x25, 0xf4, 0xd3, 0xe9, + 0x68, 0x30, 0x36, 0x66, 0xc6, 0xfd, 0xcd, 0xc9, 0x8b, 0x39, 0xf5, 0x81, 0x6f, 0xcf, 0x68, 0x44, 0xd0, 0xfe, 0x38, 0xaf, 0xd1, 0xe1, 0x34, 0xd9, 0x1f, 0x05, 0x61, 0x4c, 0x4f, 0x7a, 0x0c, 0x34, + 0xf4, 0x86, 0xcb, 0x3f, 0xac, 0x70, 0xda, 0x7f, 0x1f, 0x36, 0x16, 0xa4, 0xb3, 0x35, 0xa7, 0x6b, 0xc7, 0xd0, 0x45, 0x01, 0xaf, 0x6c, 0xdf, 0xd8, 0x35, 0x77, 0xad, 0x6e, 0xe7, 0x8a, 0xe5, 0xa6, + 0x6f, 0xab, 0xad, 0xd4, 0x33, 0x48, 0xb0, 0xff, 0x1e, 0x05, 0x64, 0x20, 0x41, 0x33, 0x0b, 0x31, 0x57, 0x09, 0x1c, 0x0d, 0x8c, 0x5d, 0x35, 0xcd, 0x18, 0x3c, 0xd8, 0xfa, 0x79, 0xe3, 0x91, 0xc4, + 0x3b, 0xc6, 0x7d, 0x4d, 0x71, 0xbf, 0xa8, 0x87, 0x35, 0xee, 0x94, 0x22, 0xd4, 0x68, 0xd0, 0x72, 0xbc, 0x5a, 0xac, 0x89, 0x16, 0x65, 0xb4, 0x56, 0x5c, 0xc9, 0x54, 0x64, 0x95, 0x66, 0x43, 0xca, + 0xdb, 0x3e, 0xe8, 0xb3, 0xce, 0x06, 0xbc, 0x13, 0xa6, 0x33, 0x1a, 0x97, 0x4c, 0xb3, 0x02, 0x2c, 0x68, 0x7f, 0x0e, 0x66, 0x7d, 0x32, 0xc6, 0x27, 0x3b, 0xfb, 0xc8, 0x72, 0x6f, 0xa0, 0x64, 0x70, + 0xff, 0xbe, 0x4a, 0xc9, 0xc4, 0x7d, 0xfd, 0x93, 0x94, 0x98, 0xad, 0xb1, 0x50, 0xdc, 0x84, 0xeb, 0x7d, 0xfb, 0x8d, 0x69, 0xc1, 0xee, 0x73, 0x18, 0xb2, 0xf3, 0xc0, 0xd6, 0x0c, 0x55, 0x56, 0xe4, + 0x28, 0x57, 0x59, 0xe6, 0x9a, 0xab, 0xc2, 0x5f, 0x54, 0xda, 0x77, 0xb2, 0x0f, 0xf7, 0xca, 0x18, 0xe7, 0xa9, 0x8d, 0xe8, 0x4a, 0x65, 0xe1, 0x22, 0xd3, 0x04, 0x4f, 0x4f, 0xed, 0x2f, 0x11, 0x7a, + 0xc9, 0x95, 0x2a, 0x80, 0x9e, 0x3d, 0xef, 0x07, 0xbb, 0xa6, 0xcb, 0xf4, 0x9e, 0x5e, 0x41, 0x5e, 0x4d, 0xfd, 0x74, 0xb5, 0x49, 0x99, 0xc8, 0x0d, 0x4b, 0x7f, 0x49, 0x6d, 0x4e, 0x77, 0x68, 0x08, + 0x69, 0x21, 0x0b, 0x7b, 0x8d, 0xc3, 0xb1, 0x3d, 0x7d, 0x2c, 0xac, 0x41, 0x8b, 0x74, 0x7b, 0xe4, 0x49, 0xf0, 0x92, 0x82, 0xa2, 0xa3, 0x55, 0xe4, 0x91, 0xa1, 0xdb, 0x40, 0xa2, 0x60, 0x19, 0xa0, + 0x92, 0xd9, 0x15, 0x3d, 0x7b, 0xee, 0xbf, 0x02, 0x12, 0xa1, 0x81, 0x5b, 0xa5, 0xb7, 0x3b, 0x3c, 0x74, 0xa5, 0x42, 0xb2, 0xfc, 0x0b, 0x2b, 0x60, 0x17, 0xeb, 0x4a, 0x4a, 0x27, 0xaf, 0xc3, 0x78, + 0x87, 0x6b, 0xfe, 0x7a, 0xf5, 0x46, 0x6f, 0x51, 0x70, 0x74, 0x9c, 0x8a, 0xa3, 0x69, 0x25, 0x07, 0xd7, 0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0xde, 0x5b, 0xa9, 0x68, 0x13, + 0x68, 0x86, 0x6d, 0x10, 0x91, 0xd0, 0x40, 0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4, 0x9a, 0x67, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, + 0x06, 0xc1, 0x87, 0x81, 0x0f, 0x5e, 0xb7, 0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7, 0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, + 0x83, 0xac, 0xf6, 0x2d, 0x38, 0xee, 0x90, 0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x8f, 0x50, 0x3a, 0xfb, 0x11, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, @@ -7466,39 +7466,39 @@ var TemplatesZip = []byte{ 0xd3, 0xf1, 0xfe, 0x7e, 0x84, 0x63, 0x06, 0xb6, 0x4f, 0xe6, 0xb5, 0x86, 0xca, 0x84, 0x99, 0x33, 0xdf, 0xca, 0xf4, 0x9a, 0x54, 0x93, 0x64, 0x7c, 0xdd, 0x1f, 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x51, 0x6f, 0xe3, 0x28, 0x10, 0x7e, 0xcf, - 0xaf, 0xb0, 0xa2, 0xbe, 0x1a, 0x92, 0xeb, 0xde, 0xdd, 0xaa, 0xe2, 0x58, 0xed, 0xc3, 0x9d, 0xae, 0x52, 0xbb, 0x5b, 0xa9, 0xbd, 0xd5, 0xbd, 0x52, 0x3c, 0x76, 0x68, 0x6d, 0xf0, 0x02, 0x4e, 0x13, - 0x55, 0xf9, 0xef, 0x2b, 0xc0, 0x76, 0x6c, 0xc7, 0x6e, 0xb3, 0x55, 0xb6, 0x6f, 0x66, 0xe6, 0x63, 0x18, 0xbe, 0xf9, 0x0c, 0x03, 0xf9, 0xb4, 0x29, 0xf2, 0x68, 0x0d, 0xda, 0x08, 0x25, 0xff, 0x9a, - 0x2f, 0xd1, 0x62, 0xfe, 0x89, 0xce, 0x48, 0xa9, 0xd5, 0x03, 0x70, 0x1b, 0x6d, 0x8c, 0xb8, 0x30, 0x7c, 0x05, 0x05, 0xbb, 0x52, 0x9c, 0x59, 0x8f, 0x59, 0x59, 0x5b, 0x5e, 0x60, 0x5c, 0xb0, 0x35, - 0x48, 0xc4, 0x4a, 0xc6, 0x57, 0x80, 0x94, 0xce, 0xf0, 0xcd, 0xd7, 0x6b, 0xfc, 0x01, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, 0x44, 0x1b, 0x93, 0xcc, 0xa3, - 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0xdb, 0x62, 0xb1, 0xc4, 0xff, 0x5f, 0x5f, 0xdd, - 0xfa, 0x94, 0x63, 0x21, 0x8d, 0x65, 0x92, 0xc3, 0x9c, 0xce, 0xa2, 0x88, 0x14, 0x2a, 0x81, 0xfc, 0x5b, 0xd8, 0x29, 0xf5, 0x11, 0x09, 0xee, 0xd9, 0x1c, 0x28, 0xd3, 0xaa, 0x2a, 0x2f, 0x13, 0xaa, - 0x74, 0x86, 0x18, 0x2f, 0x80, 0xe0, 0xc6, 0xe2, 0xbc, 0x4c, 0x5b, 0x91, 0x32, 0x6e, 0x2f, 0x13, 0x9a, 0x56, 0x92, 0x3b, 0x3e, 0x08, 0xee, 0x18, 0x1d, 0xa6, 0xe6, 0x92, 0x2e, 0xdd, 0x0a, 0xf1, - 0xed, 0x97, 0xcf, 0x37, 0xb7, 0xff, 0x7e, 0xbd, 0x23, 0x78, 0xdd, 0x59, 0xa5, 0xd4, 0xaa, 0x04, 0x6d, 0x05, 0x18, 0xea, 0x37, 0x45, 0xb8, 0x2a, 0x4a, 0x91, 0x83, 0x8e, 0xcb, 0xbc, 0xca, 0x84, - 0x44, 0x0d, 0xf8, 0x1c, 0x7d, 0x44, 0x4b, 0x82, 0xa7, 0xdc, 0x61, 0x72, 0xa0, 0xac, 0xc1, 0x20, 0x0d, 0x39, 0x30, 0x03, 0x74, 0xb9, 0x24, 0x78, 0xc2, 0x15, 0xe6, 0xd5, 0x45, 0x46, 0xf7, 0x95, - 0xc8, 0x13, 0x64, 0x54, 0xa5, 0x39, 0xfc, 0x2d, 0xb9, 0x4a, 0x84, 0xcc, 0xe8, 0x7f, 0x77, 0xff, 0xc4, 0x1f, 0x09, 0x7e, 0x11, 0xd3, 0x0f, 0xa3, 0xa1, 0x54, 0xda, 0x0a, 0x99, 0x21, 0x55, 0xd9, - 0xb2, 0xb2, 0x53, 0xa1, 0x26, 0x71, 0x21, 0xdc, 0xf7, 0x8a, 0xe9, 0xc7, 0xca, 0xa0, 0x32, 0x67, 0x36, 0x55, 0xba, 0x40, 0x0d, 0xbf, 0xb1, 0x48, 0x68, 0xed, 0x8c, 0xef, 0x55, 0x41, 0xf0, 0x8b, - 0xc8, 0x89, 0x60, 0xbe, 0x9a, 0xce, 0x2f, 0x14, 0x1a, 0x3a, 0x47, 0x22, 0xb6, 0xf0, 0x89, 0x70, 0xfb, 0x3a, 0x9d, 0x7f, 0x40, 0x7f, 0x8c, 0x04, 0xe8, 0x57, 0xca, 0x3c, 0x8a, 0xf2, 0xf2, 0xce, - 0x50, 0xab, 0x2b, 0x20, 0xb8, 0x19, 0xd5, 0xbe, 0x4a, 0x43, 0x2a, 0x34, 0x1c, 0x4a, 0xc0, 0xe9, 0xe8, 0xfa, 0x4f, 0x82, 0xa7, 0x10, 0x4e, 0x51, 0xb8, 0x2f, 0x29, 0x92, 0x40, 0x09, 0x32, 0x01, - 0xc9, 0xb7, 0xd7, 0x4c, 0xb2, 0x0c, 0x0a, 0x90, 0xb6, 0x5e, 0xa8, 0x75, 0xb5, 0xf2, 0xeb, 0xe1, 0x1b, 0x53, 0xe7, 0x67, 0x38, 0x7b, 0x9e, 0x24, 0x66, 0xd7, 0xfb, 0x41, 0xea, 0x79, 0x9d, 0x3f, - 0x62, 0x64, 0x6a, 0xa7, 0x4a, 0xbb, 0xe1, 0xdf, 0x53, 0x07, 0x68, 0x76, 0x36, 0x32, 0xbb, 0x76, 0xed, 0x7a, 0x3f, 0x54, 0x3d, 0xcd, 0x6e, 0x4b, 0xa0, 0xa5, 0x93, 0x86, 0xff, 0xda, 0x3b, 0x0c, - 0x57, 0x25, 0x50, 0x51, 0x38, 0xe1, 0x11, 0x1c, 0x46, 0xcd, 0xce, 0xf1, 0x70, 0xeb, 0x1d, 0x4b, 0xc3, 0x26, 0x9e, 0xa2, 0x73, 0x84, 0xcc, 0x11, 0x2a, 0x5b, 0x22, 0xf7, 0xa2, 0x3b, 0xa0, 0xad, - 0x4b, 0x5a, 0xa3, 0xf2, 0xb4, 0x92, 0xdf, 0xb7, 0xf1, 0xa3, 0x64, 0x56, 0xac, 0x21, 0x86, 0x35, 0x48, 0x6b, 0x0e, 0x19, 0x1b, 0xd9, 0xc1, 0xe9, 0x52, 0x30, 0x05, 0xcb, 0x73, 0xbd, 0x85, 0x78, - 0x05, 0x2c, 0xb7, 0xab, 0x77, 0x5e, 0x9d, 0x69, 0xfe, 0xce, 0x2b, 0x3e, 0x54, 0x52, 0xd8, 0xdf, 0xc7, 0x64, 0x59, 0x8b, 0xc8, 0x82, 0xe9, 0x4b, 0xe8, 0xa7, 0xd3, 0xd1, 0x60, 0x6c, 0xcc, 0x8c, - 0xfb, 0x9b, 0x93, 0x17, 0x73, 0xea, 0x03, 0xdf, 0x9e, 0xd1, 0x88, 0xa0, 0xfd, 0x71, 0x5e, 0xa3, 0xc3, 0x69, 0xb2, 0x3f, 0x0a, 0xc2, 0x98, 0x9e, 0xf4, 0x18, 0x68, 0xe8, 0x0d, 0x97, 0x7f, 0x58, - 0xe1, 0xb4, 0xff, 0x3e, 0x6c, 0x2c, 0x48, 0x67, 0x6b, 0x4e, 0xd7, 0x8e, 0xa1, 0x8b, 0x02, 0x5e, 0xd9, 0xbe, 0xb1, 0x6b, 0xee, 0x5a, 0xdd, 0xce, 0x15, 0xcb, 0x4d, 0xdf, 0x56, 0x5b, 0xa9, 0x67, - 0x90, 0x60, 0xff, 0x3d, 0x0a, 0xc8, 0x40, 0x82, 0x66, 0x16, 0x62, 0xae, 0x12, 0x38, 0x1a, 0x18, 0xbb, 0x6a, 0x9a, 0x31, 0x78, 0xb0, 0xf5, 0xf3, 0xc6, 0x23, 0x89, 0x77, 0x8c, 0xfb, 0x9a, 0xe2, - 0x7e, 0x51, 0x0f, 0x6b, 0xdc, 0x29, 0x45, 0xa8, 0xd1, 0xa0, 0xe5, 0x78, 0xb5, 0x58, 0x13, 0x2d, 0xca, 0x68, 0xad, 0xb8, 0x92, 0xa9, 0xc8, 0x2a, 0xcd, 0x86, 0x94, 0xb7, 0x7d, 0xd0, 0x67, 0x9d, - 0x0d, 0x78, 0x27, 0x4c, 0x67, 0x34, 0x2e, 0x99, 0x66, 0x05, 0x58, 0xd0, 0xfe, 0x1c, 0xcc, 0xfa, 0x64, 0x8c, 0x4f, 0x76, 0xf6, 0x91, 0xe5, 0xde, 0x40, 0xc9, 0xe0, 0xfe, 0x7d, 0x95, 0x92, 0x89, - 0xfb, 0xfa, 0x27, 0x29, 0x31, 0x5b, 0x63, 0xa1, 0xb8, 0x09, 0xd7, 0xfb, 0xf6, 0x1b, 0xd3, 0x82, 0xdd, 0xe7, 0x30, 0x64, 0xe7, 0x81, 0xad, 0x19, 0xaa, 0xac, 0xc8, 0x51, 0xae, 0xb2, 0xcc, 0x35, - 0x57, 0x85, 0xbf, 0xa8, 0xb4, 0xef, 0x64, 0x1f, 0xee, 0x95, 0x31, 0xce, 0x53, 0x1b, 0xd1, 0x95, 0xca, 0xc2, 0x45, 0xa6, 0x09, 0x9e, 0x9e, 0xda, 0x5f, 0x22, 0xf4, 0x92, 0x2b, 0x55, 0x00, 0x3d, - 0x7b, 0xde, 0x0f, 0x76, 0x4d, 0x97, 0xe9, 0x3d, 0xbd, 0x82, 0xbc, 0x9a, 0xfa, 0xe9, 0x6a, 0x93, 0x32, 0x91, 0x1b, 0x96, 0xfe, 0x92, 0xda, 0x9c, 0xee, 0xd0, 0x10, 0xd2, 0x42, 0x16, 0xf6, 0x1a, - 0x87, 0x63, 0x7b, 0xfa, 0x58, 0x58, 0x83, 0x16, 0xe9, 0xf6, 0xc8, 0x93, 0xe0, 0x25, 0x05, 0x45, 0x47, 0xab, 0xc8, 0x23, 0x43, 0xb7, 0x81, 0x44, 0xc1, 0x32, 0x40, 0x25, 0xb3, 0x2b, 0x7a, 0xf6, - 0xdc, 0x7f, 0x05, 0x24, 0x42, 0x03, 0xb7, 0x4a, 0x6f, 0x77, 0x78, 0xe8, 0x4a, 0x85, 0x64, 0xf9, 0x17, 0x56, 0xc0, 0x2e, 0xd6, 0x95, 0x94, 0x4e, 0x5e, 0x87, 0xf1, 0x0e, 0xd7, 0xfc, 0xf5, 0xea, - 0x8d, 0xde, 0xa2, 0xe0, 0xe8, 0x38, 0x15, 0x47, 0xd3, 0x4a, 0x0e, 0xae, 0x37, 0x9d, 0xd2, 0xcd, 0xa8, 0xee, 0x40, 0xdb, 0x1b, 0xdb, 0xbd, 0xb7, 0x52, 0xd1, 0x26, 0xd0, 0x0c, 0xdb, 0x20, 0x22, - 0xa1, 0x81, 0x72, 0x82, 0x45, 0xa7, 0xad, 0xe0, 0x56, 0xac, 0x07, 0xa9, 0x35, 0xcf, 0xd0, 0x6d, 0x2f, 0x5b, 0xc9, 0x0a, 0x68, 0x23, 0xf8, 0x41, 0x27, 0xe9, 0xe1, 0x0c, 0x82, 0x0f, 0x03, 0x1f, - 0xbc, 0x6e, 0xbd, 0xb1, 0x79, 0xec, 0xa4, 0x2c, 0x37, 0xc3, 0xb7, 0x8f, 0x07, 0xb4, 0xb7, 0x3d, 0xe3, 0x8f, 0x4e, 0x2a, 0xbe, 0x85, 0x6f, 0xf2, 0x18, 0x75, 0xce, 0x06, 0x59, 0xed, 0x5b, 0x70, - 0xdc, 0x21, 0xa5, 0x1d, 0x18, 0x3a, 0x6b, 0x1f, 0xa1, 0x74, 0xf6, 0x23, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xa4, 0x76, 0x1e, 0x9c, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0xdf, 0x6f, 0xe3, 0x28, 0x10, 0x7e, 0xcf, + 0x5f, 0x61, 0x45, 0x7d, 0x35, 0x24, 0xdb, 0xfb, 0xb1, 0xaa, 0x38, 0x56, 0xfb, 0x70, 0xa7, 0xab, 0xd4, 0xee, 0x56, 0x6a, 0x6f, 0x75, 0xaf, 0x14, 0x8f, 0x1d, 0x5a, 0x1b, 0xbc, 0x80, 0xd3, 0x44, + 0x55, 0xfe, 0xf7, 0x15, 0x60, 0x3b, 0xb6, 0x63, 0xb7, 0xd9, 0x2a, 0xdb, 0x37, 0x33, 0xf3, 0x31, 0x0c, 0xdf, 0x7c, 0x86, 0x81, 0x7c, 0xda, 0x14, 0x79, 0xb4, 0x06, 0x6d, 0x84, 0x92, 0x7f, 0xcd, + 0x97, 0x68, 0x31, 0xff, 0x44, 0x67, 0xa4, 0xd4, 0xea, 0x01, 0xb8, 0x8d, 0x36, 0x46, 0x5c, 0x18, 0xbe, 0x82, 0x82, 0x5d, 0x29, 0xce, 0xac, 0xc7, 0xac, 0xac, 0x2d, 0x2f, 0x30, 0x2e, 0xd8, 0x1a, + 0x24, 0x62, 0x25, 0xe3, 0x2b, 0x40, 0x4a, 0x67, 0xf8, 0xe6, 0xeb, 0x35, 0xfe, 0x0d, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, 0x44, 0x1b, 0x93, 0xcc, 0xa3, + 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0x61, 0xb1, 0x58, 0xe2, 0xff, 0xaf, 0xaf, 0x6e, + 0x7d, 0xca, 0xb1, 0x90, 0xc6, 0x32, 0xc9, 0x61, 0x4e, 0x67, 0x51, 0x44, 0x0a, 0x95, 0x40, 0xfe, 0x2d, 0xec, 0x94, 0xfa, 0x88, 0x04, 0xf7, 0x6c, 0x0e, 0x94, 0x69, 0x55, 0x95, 0x97, 0x09, 0x55, + 0x3a, 0x43, 0x8c, 0x17, 0x40, 0x70, 0x63, 0x71, 0x5e, 0xa6, 0xad, 0x48, 0x19, 0xb7, 0x97, 0x09, 0x4d, 0x2b, 0xc9, 0x1d, 0x1f, 0x04, 0x77, 0x8c, 0x0e, 0x53, 0x73, 0x49, 0x97, 0x6e, 0x85, 0xf8, + 0xf6, 0xcb, 0xe7, 0x9b, 0xdb, 0x7f, 0xbf, 0xde, 0x11, 0xbc, 0xee, 0xac, 0x52, 0x6a, 0x55, 0x82, 0xb6, 0x02, 0x0c, 0xf5, 0x9b, 0x22, 0x5c, 0x15, 0xa5, 0xc8, 0x41, 0xc7, 0x65, 0x5e, 0x65, 0x42, + 0xa2, 0x06, 0x7c, 0x8e, 0x3e, 0xa2, 0x25, 0xc1, 0x53, 0xee, 0x30, 0x39, 0x50, 0xd6, 0x60, 0x90, 0x86, 0x1c, 0x98, 0x01, 0xba, 0x5c, 0x12, 0x3c, 0xe1, 0x0a, 0xf3, 0xea, 0x22, 0xa3, 0xfb, 0x4a, + 0xe4, 0x09, 0x32, 0xaa, 0xd2, 0x1c, 0xfe, 0x96, 0x5c, 0x25, 0x42, 0x66, 0xf4, 0xbf, 0xbb, 0x7f, 0xe2, 0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, 0x85, 0xcc, 0x90, 0xaa, 0x6c, + 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10, 0xee, 0x7b, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b, 0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, 0xf7, 0xaa, 0x20, 0xf8, 0x45, + 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17, 0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0x7d, 0x9d, 0xce, 0xff, 0x40, 0x1f, 0x46, 0x02, 0xf4, 0x2b, 0x65, 0x1e, 0x45, 0x79, 0x79, 0x67, + 0xa8, 0xd5, 0x15, 0x10, 0xdc, 0x8c, 0x6a, 0x5f, 0xa5, 0x21, 0x15, 0x1a, 0x0e, 0x25, 0xe0, 0x74, 0x74, 0xfd, 0x27, 0xc1, 0x53, 0x08, 0xa7, 0x28, 0xdc, 0x97, 0x14, 0x49, 0xa0, 0x04, 0x99, 0x80, + 0xe4, 0xdb, 0x6b, 0x26, 0x59, 0x06, 0x05, 0x48, 0x5b, 0x2f, 0xd4, 0xba, 0x5a, 0xf9, 0xf5, 0xf0, 0x8d, 0xa9, 0xf3, 0x33, 0x9c, 0x3d, 0x4f, 0x12, 0xb3, 0xeb, 0xfd, 0x20, 0xf5, 0xbc, 0xce, 0x1f, + 0x31, 0x32, 0xb5, 0x53, 0xa5, 0xdd, 0xf0, 0xef, 0xa9, 0x03, 0x34, 0x3b, 0x1b, 0x99, 0x5d, 0xbb, 0x76, 0xbd, 0x1f, 0xaa, 0x9e, 0x66, 0xb7, 0x25, 0xd0, 0xd2, 0x49, 0xc3, 0x7f, 0xed, 0x1d, 0x86, + 0xab, 0x12, 0xa8, 0x28, 0x9c, 0xf0, 0x08, 0x0e, 0xa3, 0x66, 0xe7, 0x78, 0xb8, 0xf5, 0x8e, 0xa5, 0x61, 0x13, 0x4f, 0xd1, 0x39, 0x42, 0xe6, 0x08, 0x95, 0x2d, 0x91, 0x7b, 0xd1, 0x1d, 0xd0, 0xd6, + 0x25, 0xad, 0x51, 0x79, 0x5a, 0xc9, 0xef, 0xdb, 0xf8, 0x51, 0x32, 0x2b, 0xd6, 0x10, 0xc3, 0x1a, 0xa4, 0x35, 0x87, 0x8c, 0x8d, 0xec, 0xe0, 0x74, 0x29, 0x98, 0x82, 0xe5, 0xb9, 0xde, 0x42, 0xbc, + 0x02, 0x96, 0xdb, 0xd5, 0x3b, 0xaf, 0xce, 0x34, 0x7f, 0xe7, 0x15, 0x1f, 0x2a, 0x29, 0xec, 0xef, 0x63, 0xb2, 0xac, 0x45, 0x64, 0xc1, 0xf4, 0x25, 0xf4, 0xd3, 0xe9, 0x68, 0x30, 0x36, 0x66, 0xc6, + 0xfd, 0xcd, 0xc9, 0x8b, 0x39, 0xf5, 0x81, 0x6f, 0xcf, 0x68, 0x44, 0xd0, 0xfe, 0x38, 0xaf, 0xd1, 0xe1, 0x34, 0xd9, 0x1f, 0x05, 0x61, 0x4c, 0x4f, 0x7a, 0x0c, 0x34, 0xf4, 0x86, 0xcb, 0x3f, 0xac, + 0x70, 0xda, 0x7f, 0x1f, 0x36, 0x16, 0xa4, 0xb3, 0x35, 0xa7, 0x6b, 0xc7, 0xd0, 0x45, 0x01, 0xaf, 0x6c, 0xdf, 0xd8, 0x35, 0x77, 0xad, 0x6e, 0xe7, 0x8a, 0xe5, 0xa6, 0x6f, 0xab, 0xad, 0xd4, 0x33, + 0x48, 0xb0, 0xff, 0x1e, 0x05, 0x64, 0x20, 0x41, 0x33, 0x0b, 0x31, 0x57, 0x09, 0x1c, 0x0d, 0x8c, 0x5d, 0x35, 0xcd, 0x18, 0x3c, 0xd8, 0xfa, 0x79, 0xe3, 0x91, 0xc4, 0x3b, 0xc6, 0x7d, 0x4d, 0x71, + 0xbf, 0xa8, 0x87, 0x35, 0xee, 0x94, 0x22, 0xd4, 0x68, 0xd0, 0x72, 0xbc, 0x5a, 0xac, 0x89, 0x16, 0x65, 0xb4, 0x56, 0x5c, 0xc9, 0x54, 0x64, 0x95, 0x66, 0x43, 0xca, 0xdb, 0x3e, 0xe8, 0xb3, 0xce, + 0x06, 0xbc, 0x13, 0xa6, 0x33, 0x1a, 0x97, 0x4c, 0xb3, 0x02, 0x2c, 0x68, 0x7f, 0x0e, 0x66, 0x7d, 0x32, 0xc6, 0x27, 0x3b, 0xfb, 0xc8, 0x72, 0x6f, 0xa0, 0x64, 0x70, 0xff, 0xbe, 0x4a, 0xc9, 0xc4, + 0x7d, 0xfd, 0x93, 0x94, 0x98, 0xad, 0xb1, 0x50, 0xdc, 0x84, 0xeb, 0x7d, 0xfb, 0x8d, 0x69, 0xc1, 0xee, 0x73, 0x18, 0xb2, 0xf3, 0xc0, 0xd6, 0x0c, 0x55, 0x56, 0xe4, 0x28, 0x57, 0x59, 0xe6, 0x9a, + 0xab, 0xc2, 0x5f, 0x54, 0xda, 0x77, 0xb2, 0x0f, 0xf7, 0xca, 0x18, 0xe7, 0xa9, 0x8d, 0xe8, 0x4a, 0x65, 0xe1, 0x22, 0xd3, 0x04, 0x4f, 0x4f, 0xed, 0x2f, 0x11, 0x7a, 0xc9, 0x95, 0x2a, 0x80, 0x9e, + 0x3d, 0xef, 0x07, 0xbb, 0xa6, 0xcb, 0xf4, 0x9e, 0x5e, 0x41, 0x5e, 0x4d, 0xfd, 0x74, 0xb5, 0x49, 0x99, 0xc8, 0x0d, 0x4b, 0x7f, 0x49, 0x6d, 0x4e, 0x77, 0x68, 0x08, 0x69, 0x21, 0x0b, 0x7b, 0x8d, + 0xc3, 0xb1, 0x3d, 0x7d, 0x2c, 0xac, 0x41, 0x8b, 0x74, 0x7b, 0xe4, 0x49, 0xf0, 0x92, 0x82, 0xa2, 0xa3, 0x55, 0xe4, 0x91, 0xa1, 0xdb, 0x40, 0xa2, 0x60, 0x19, 0xa0, 0x92, 0xd9, 0x15, 0x3d, 0x7b, + 0xee, 0xbf, 0x02, 0x12, 0xa1, 0x81, 0x5b, 0xa5, 0xb7, 0x3b, 0x3c, 0x74, 0xa5, 0x42, 0xb2, 0xfc, 0x0b, 0x2b, 0x60, 0x17, 0xeb, 0x4a, 0x4a, 0x27, 0xaf, 0xc3, 0x78, 0x87, 0x6b, 0xfe, 0x7a, 0xf5, + 0x46, 0x6f, 0x51, 0x70, 0x74, 0x9c, 0x8a, 0xa3, 0x69, 0x25, 0x07, 0xd7, 0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0xde, 0x5b, 0xa9, 0x68, 0x13, 0x68, 0x86, 0x6d, 0x10, 0x91, + 0xd0, 0x40, 0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4, 0x9a, 0x67, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, 0x06, 0xc1, 0x87, 0x81, 0x0f, + 0x5e, 0xb7, 0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7, 0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, 0x83, 0xac, 0xf6, 0x2d, 0x38, + 0xee, 0x90, 0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x8f, 0x50, 0x3a, 0xfb, 0x11, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x6d, 0x61, @@ -13610,7 +13610,7 @@ var TemplatesZip = []byte{ 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x23, 0x64, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xa4, 0x76, 0x1e, 0x9c, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x63, 0x6f, 0x03, 0x00, + 0x00, 0x00, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x63, 0x6f, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xa5, 0x73, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, @@ -13672,7 +13672,7 @@ var TemplatesZip = []byte{ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x0c, 0x8b, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xac, 0x99, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, - 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x76, 0x1e, 0x9c, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, + 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0xe5, 0xa4, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x20, 0xa9, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, diff --git a/templates/quarkus/cloudevents/pom.xml b/templates/quarkus/cloudevents/pom.xml index ec9d1b9f32..93e8f1ad8d 100644 --- a/templates/quarkus/cloudevents/pom.xml +++ b/templates/quarkus/cloudevents/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.34.6 + 3.36.2 true 3.0.0-M7 diff --git a/templates/quarkus/http/pom.xml b/templates/quarkus/http/pom.xml index ec9d1b9f32..93e8f1ad8d 100644 --- a/templates/quarkus/http/pom.xml +++ b/templates/quarkus/http/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.34.6 + 3.36.2 true 3.0.0-M7 From 47801becb75f36313abeb8efa1d0c30fc030ddbe Mon Sep 17 00:00:00 2001 From: Elvand-Lie Date: Fri, 12 Jun 2026 00:43:25 +0700 Subject: [PATCH 29/44] mcp: fix appendBoolFlag to handle explicit false values (#3709) * mcp: fix appendBoolFlag to handle explicit false values appendBoolFlag silently dropped the flag when value was non-nil but false. This meant MCP clients could not explicitly disable boolean flags like --push that may be enabled via func.yaml defaults. When value is non-nil and false, append flag=false (e.g. --push=false) to allow explicit disabling. Signed-off-by: elvandlie@gmail.com * mcp: add tests for appendBoolFlag and appendStringFlag helpers Signed-off-by: elvandlie@gmail.com --------- Signed-off-by: elvandlie@gmail.com --- pkg/mcp/mcp_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++ pkg/mcp/tools.go | 12 ++++++--- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/pkg/mcp/mcp_test.go b/pkg/mcp/mcp_test.go index 8d93a71f44..3583106050 100644 --- a/pkg/mcp/mcp_test.go +++ b/pkg/mcp/mcp_test.go @@ -221,3 +221,63 @@ func TestWithPrefix_Validation(t *testing.T) { }) } } + +// TestAppendBoolFlag verifies that appendBoolFlag handles nil, true, and false correctly. +func TestAppendBoolFlag(t *testing.T) { + trueVal := true + falseVal := false + + cases := []struct { + name string + value *bool + want []string + }{ + {"nil omits flag", nil, []string{}}, + {"true appends flag", &trueVal, []string{"--push"}}, + {"false appends flag=false", &falseVal, []string{"--push=false"}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := appendBoolFlag(nil, "--push", tc.value) + if len(got) != len(tc.want) { + t.Fatalf("appendBoolFlag(nil, --push, %v) = %v, want %v", tc.value, got, tc.want) + } + for i := range got { + if got[i] != tc.want[i] { + t.Errorf("result[%d] = %q, want %q", i, got[i], tc.want[i]) + } + } + }) + } +} + +// TestAppendStringFlag verifies that appendStringFlag handles nil, empty, and non-empty values. +func TestAppendStringFlag(t *testing.T) { + empty := "" + val := "prod" + + cases := []struct { + name string + value *string + want []string + }{ + {"nil omits flag", nil, []string{}}, + {"empty omits flag", &empty, []string{}}, + {"non-empty appends flag and value", &val, []string{"--namespace", "prod"}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := appendStringFlag(nil, "--namespace", tc.value) + if len(got) != len(tc.want) { + t.Fatalf("appendStringFlag(nil, --namespace, %v) = %v, want %v", tc.value, got, tc.want) + } + for i := range got { + if got[i] != tc.want[i] { + t.Errorf("result[%d] = %q, want %q", i, got[i], tc.want[i]) + } + } + }) + } +} diff --git a/pkg/mcp/tools.go b/pkg/mcp/tools.go index 0d7ed0fc7d..faea25f0d7 100644 --- a/pkg/mcp/tools.go +++ b/pkg/mcp/tools.go @@ -26,11 +26,15 @@ func appendStringFlag(args []string, flag string, value *string) []string { return args } -// appendBoolFlag adds a boolean flag to args only if the value is non-nil and true. -// This ensures we only pass flags that were explicitly provided by the user. +// appendBoolFlag adds a boolean flag to args only if the value is non-nil. +// When true, appends the flag (e.g. --push). When false, appends +// flag=false (e.g. --push=false) to allow explicit disabling. func appendBoolFlag(args []string, flag string, value *bool) []string { - if value != nil && *value { - return append(args, flag) + if value != nil { + if *value { + return append(args, flag) + } + return append(args, flag+"=false") } return args } From 0692d37ed5c5bd7d60da96d5a1b4ea6d1ec057ba Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Thu, 11 Jun 2026 22:42:24 -0400 Subject: [PATCH 30/44] upgrade to latest dependencies (#3891) bumping knative.dev/client/pkg 4784fe6...84a3162: > 84a3162 upgrade to latest dependencies (# 2237) bumping knative.dev/serving 516bc43...5806b90: > 5806b90 Update net-istio nightly (# 16640) bumping knative.dev/eventing fd4eb83...69b976f: > 69b976f Fix goroutine/memory leak in SubjectAndFiltersPass (# 9151) Signed-off-by: Knative Automation --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 1cc2d33c3c..0e80b9c810 100644 --- a/go.mod +++ b/go.mod @@ -70,11 +70,11 @@ require ( k8s.io/client-go v0.35.5 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570 - knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f + knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351 + knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 - knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667 + knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb sigs.k8s.io/controller-runtime v0.23.3 ) diff --git a/go.sum b/go.sum index 4294f58e9b..e894b1158e 100644 --- a/go.sum +++ b/go.sum @@ -1875,18 +1875,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570 h1:8uK9OAjbJC2TFA0A2a4jSCQvI7ncI8UUzooxqtGNAR8= -knative.dev/client/pkg v0.0.0-20260608152413-4784fe64a570/go.mod h1:6libzpuf1JG45EVcB7fKT1E4Kok+XRI4gHr2Djvv21s= -knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f h1:knNXRMjMEmOcqCDPEQK02FXV+5FT1TaYbaOtshbcYCg= -knative.dev/eventing v0.49.1-0.20260608101721-fd4eb83e1b8f/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= +knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351 h1:NCPsKc6serIwi9sh3afGXFoLB0Edd2lGEVWZaJAoQzw= +knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351/go.mod h1:M69p8Zli8zEoZ4p0N4H+W9KR2UvBUMPjdoiaA3NAbOg= +knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56 h1:AHDySG8Wz6mQuiKAXanlYWLjTM8hRU9P8sBPKcHbOso= +knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 h1:Lf5I3oaFc0bErDP7yUbOxT+6GaGnVwuAiCAnDAn5F48= knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622/go.mod h1:A6IJjMX0nATYJ4eJAcqXGLiMTg1AXsA9QtPd7nI9Tk0= -knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667 h1:s8HO3mubc8wZGAnZ4Dx0fqUUHWM1lpeKVBZ+/F18Hxc= -knative.dev/serving v0.49.1-0.20260604132906-516bc43f3667/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= +knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb h1:esY2sCP5HROvOQ3YGFb7fWxrWm8GFjf+TuT24wMSRH8= +knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From e4013be6d205a84d4073fbe15f7aace081d5ba0e Mon Sep 17 00:00:00 2001 From: Elvand-Lie Date: Fri, 12 Jun 2026 21:31:26 +0700 Subject: [PATCH 31/44] fix(cmd): replace panic with error return in write() and remove phantom xml format (#3845) * fix(cmd): replace panic with error return in write() and remove phantom xml format The write() function in format.go used panic(err) when encountering an unrecognized output format or serialization error. This crashes the CLI process instead of showing a graceful error message. Additionally, list.go advertised 'xml' as a valid output format in its --output flag help text, but XML was never implemented. Passing --output xml would trigger the panic. Changes: - Change write() to return error instead of panicking - Update all three callers (list, describe, version) to propagate the error - Remove 'xml' from list command's --output flag help text Signed-off-by: Elvand-Lie * docs: update func list reference docs for removed xml format --------- Signed-off-by: Elvand-Lie --- cmd/describe.go | 3 +-- cmd/format.go | 22 +++++++++------------- cmd/list.go | 6 ++---- cmd/version.go | 3 +-- docs/reference/func_list.md | 2 +- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/cmd/describe.go b/cmd/describe.go index 5a6f4a8d49..f8beb99af2 100644 --- a/cmd/describe.go +++ b/cmd/describe.go @@ -89,8 +89,7 @@ func runDescribe(cmd *cobra.Command, args []string, newClient ClientFactory) (er } } - write(os.Stdout, info(details), cfg.Output) - return + return write(os.Stdout, info(details), cfg.Output) } // CLI Configuration (parameters) diff --git a/cmd/format.go b/cmd/format.go index f403c6e32d..bcad1bc925 100644 --- a/cmd/format.go +++ b/cmd/format.go @@ -24,25 +24,21 @@ type Formatter interface { URL(io.Writer) error } -// write to the output the output of the formatter's appropriate serilization function. -// the command to exit with value 2. -func write(out io.Writer, s Formatter, formatName string) { - var err error +// write the output using the formatter's appropriate serialization function, +// returning any errors to the caller for graceful handling. +func write(out io.Writer, s Formatter, formatName string) error { switch Format(formatName) { case Human: - err = s.Human(out) + return s.Human(out) case Plain: - err = s.Plain(out) + return s.Plain(out) case JSON: - err = s.JSON(out) + return s.JSON(out) case YAML: - err = s.YAML(out) + return s.YAML(out) case URL: - err = s.URL(out) + return s.URL(out) default: - err = fmt.Errorf("format not recognized: %v", formatName) - } - if err != nil { - panic(err) + return fmt.Errorf("format not recognized: %v", formatName) } } diff --git a/cmd/list.go b/cmd/list.go index 3e24e561ff..834ad09aea 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -67,7 +67,7 @@ Lists deployed functions. // Flags cmd.Flags().BoolP("all-namespaces", "A", false, "List functions in all namespaces. If set, the --namespace flag is ignored.") cmd.Flags().StringP("namespace", "n", defaultNamespace(fn.Function{}, false), "The namespace for which to list functions. ($FUNC_NAMESPACE)") - cmd.Flags().StringP("output", "o", "human", "Output format (human|plain|json|xml|yaml) ($FUNC_OUTPUT)") + cmd.Flags().StringP("output", "o", "human", "Output format (human|plain|json|yaml) ($FUNC_OUTPUT)") addVerboseFlag(cmd, cfg.Verbose) if err := cmd.RegisterFlagCompletionFunc("output", CompleteOutputFormatList); err != nil { @@ -96,9 +96,7 @@ func runList(cmd *cobra.Command, _ []string, newClient ClientFactory) (err error return } - write(os.Stdout, listItems(items), cfg.Output) - - return + return write(os.Stdout, listItems(items), cfg.Output) } // CLI Configuration (parameters) diff --git a/cmd/version.go b/cmd/version.go index ca14a3824b..da82b99b58 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -95,8 +95,7 @@ func runVersion(cmd *cobra.Command, v Version) error { } v.MiddlewareVersions = latestMW - write(cmd.OutOrStdout(), v, output) - return nil + return write(cmd.OutOrStdout(), v, output) } // Version information populated on build. diff --git a/docs/reference/func_list.md b/docs/reference/func_list.md index f743e66334..522620a030 100644 --- a/docs/reference/func_list.md +++ b/docs/reference/func_list.md @@ -34,7 +34,7 @@ func list --all-namespaces --output json -A, --all-namespaces List functions in all namespaces. If set, the --namespace flag is ignored. -h, --help help for list -n, --namespace string The namespace for which to list functions. ($FUNC_NAMESPACE) (default "default") - -o, --output string Output format (human|plain|json|xml|yaml) ($FUNC_OUTPUT) (default "human") + -o, --output string Output format (human|plain|json|yaml) ($FUNC_OUTPUT) (default "human") -v, --verbose Print verbose logs ($FUNC_VERBOSE) ``` From 9249593d27d55e1857e574e1212d9ede4d4975a4 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Mon, 15 Jun 2026 11:58:52 -0400 Subject: [PATCH 32/44] upgrade to latest dependencies (#3892) bumping k8s.io/apiextensions-apiserver e5278bf...020c736: > 020c736 Update dependencies to v0.35.6 tag bumping k8s.io/api 50571fb...3f662f3: > 3f662f3 Update dependencies to v0.35.6 tag bumping k8s.io/apimachinery 475c941...5e12da0: > 5e12da0 Merge pull request # 139509 from lalitc375/automated-cherry-pick-of-# 139480-origin-release-1.35 > f540d67 Fix wrong marking of errors bumping knative.dev/pkg ac97e43...0f785e3: > 0f785e3 Bump the k8s group with 5 updates (# 3369) bumping k8s.io/apiserver 88f88ab...540abe3: > 540abe3 Update dependencies to v0.35.6 tag bumping knative.dev/serving 5806b90...1683170: > 1683170 Update net-gateway-api nightly (# 16641) bumping k8s.io/client-go 906076c...ad0b54e: > ad0b54e Update dependencies to v0.35.6 tag Signed-off-by: Knative Automation --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 0e80b9c810..3a2e924f47 100644 --- a/go.mod +++ b/go.mod @@ -65,16 +65,16 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 - k8s.io/api v0.35.5 - k8s.io/apimachinery v0.35.5 - k8s.io/client-go v0.35.5 + k8s.io/api v0.35.6 + k8s.io/apimachinery v0.35.6 + k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351 knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 - knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb + knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0 + knative.dev/serving v0.49.1-0.20260615142436-1683170667da sigs.k8s.io/controller-runtime v0.23.3 ) @@ -314,8 +314,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiextensions-apiserver v0.35.5 // indirect - k8s.io/apiserver v0.35.5 // indirect + k8s.io/apiextensions-apiserver v0.35.6 // indirect + k8s.io/apiserver v0.35.6 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect knative.dev/networking v0.0.0-20260602144506-c8765a725c2b // indirect diff --git a/go.sum b/go.sum index e894b1158e..78159a6266 100644 --- a/go.sum +++ b/go.sum @@ -1832,26 +1832,26 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/api v0.27.7/go.mod h1:ZNExI/Lhrs9YrLgVWx6jjHZdoWCTXfBXuFjt1X6olro= k8s.io/api v0.27.10/go.mod h1:cDmAF4GtSVRO0+5hOY/Vo3lLCQMOp6FfrXZ94/gQwC0= -k8s.io/api v0.35.5 h1:BrFeUDGY/LBtlA1R5RoxhlYRHs76RnQBc6xbm/y7hsQ= -k8s.io/api v0.35.5/go.mod h1:xWkFhMnoPZdTAQh95Rlw3zZpUUNVlFHcuESUYd06BWM= +k8s.io/api v0.35.6 h1:phPzP79F3kcONsD2TzmDiITNCV6/1Z5U3CCEcjtsXzI= +k8s.io/api v0.35.6/go.mod h1:GWKUaIp24fuDFigAgnhr9EJOKDqspnwPjYlpDca5B4U= k8s.io/apiextensions-apiserver v0.27.7/go.mod h1:x0p+b5a955lfPz9gaDeBy43obM12s+N9dNHK6+dUL+g= -k8s.io/apiextensions-apiserver v0.35.5 h1:HttlJjgsx3ddLsASCqklkKvfBlwUoXma8VLpeMG5YL8= -k8s.io/apiextensions-apiserver v0.35.5/go.mod h1:4xbAgP/jbt8sVHE3H4DfE1gSPLUoSzXrNqhZz1lTHKc= +k8s.io/apiextensions-apiserver v0.35.6 h1:fyzp3i+PAbB/jSNau9LF0rMuUTUUyybR02BpYhT1YKI= +k8s.io/apiextensions-apiserver v0.35.6/go.mod h1:kkCbFS495cT53wOqNwWnQei759bkvgn6OqE0R8b3DEA= k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apimachinery v0.27.7/go.mod h1:jBGQgTjkw99ef6q5hv1YurDd3BqKDk9YRxmX0Ozo0i8= k8s.io/apimachinery v0.27.10/go.mod h1:IHu2ovJ60RqxyPSLmTel7KDLdOCRbpOxwtUBmwBnT/E= -k8s.io/apimachinery v0.35.5 h1:lbjjjUfVeVqFbiOpyhqZHc8DhiYkWOxSNij7lHx2U8Y= -k8s.io/apimachinery v0.35.5/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= +k8s.io/apimachinery v0.35.6 h1:ASSpfmmsOArKb2Hsu8gGlIcbIcEMVTboI3FfsfYuQ8k= +k8s.io/apimachinery v0.35.6/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= k8s.io/apiserver v0.27.7/go.mod h1:OrLG9RwCOerutAlo8QJW5EHzUG9Dad7k6rgcDUNSO/w= -k8s.io/apiserver v0.35.5 h1:ZtFpSEmxf/VmOdbL3bo7hLxyNRorRegqOLmYSW0mxEo= -k8s.io/apiserver v0.35.5/go.mod h1:6NNWFTq/UosCwUmqhQDC+3ApzSx5ekeYMIwzSG+49VU= +k8s.io/apiserver v0.35.6 h1:VWYg2S0wlAmN3URFpVeuLa4PP2RCpTFg1nvlUHOy2C8= +k8s.io/apiserver v0.35.6/go.mod h1:wajGSrXO9w+lx69jYq4SaE4Xxw5KxxwvVD1zbttYA2E= k8s.io/cli-runtime v0.34.1 h1:btlgAgTrYd4sk8vJTRG6zVtqBKt9ZMDeQZo2PIzbL7M= k8s.io/cli-runtime v0.34.1/go.mod h1:aVA65c+f0MZiMUPbseU/M9l1Wo2byeaGwUuQEQVVveE= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= k8s.io/client-go v0.27.7/go.mod h1:dZ2kqcalYp5YZ2EV12XIMc77G6PxHWOJp/kclZr4+5Q= k8s.io/client-go v0.27.10/go.mod h1:PhrjLdIJNy7L8liOPEzm6wNlMjhIRJeVbfvksTxKNqI= -k8s.io/client-go v0.35.5 h1:wUrgqVSmFRw75bgSHY7X0G/hZM/QYpV0Hg7SYYOYpFk= -k8s.io/client-go v0.35.5/go.mod h1:Z0mDcAJsX1Y7RQfuQlJipiRtqf8Mhk2VDu1/JvRqdGo= +k8s.io/client-go v0.35.6 h1:qZQv9a5B4YlIpXhFBwsI9qPOOJC6Z8lk9lkEWmrmus8= +k8s.io/client-go v0.35.6/go.mod h1:LOO6N1EhxdQAzYIZ/73cJVyb3gixrMY6ZDJcJ/ANfsY= k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/RLA= k8s.io/component-base v0.27.7/go.mod h1:YGjlCVL1oeKvG3HSciyPHFh+LCjIEqsxz4BDR3cfHRs= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1883,10 +1883,10 @@ knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= -knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622 h1:Lf5I3oaFc0bErDP7yUbOxT+6GaGnVwuAiCAnDAn5F48= -knative.dev/pkg v0.0.0-20260602142205-ac97e43f6622/go.mod h1:A6IJjMX0nATYJ4eJAcqXGLiMTg1AXsA9QtPd7nI9Tk0= -knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb h1:esY2sCP5HROvOQ3YGFb7fWxrWm8GFjf+TuT24wMSRH8= -knative.dev/serving v0.49.1-0.20260611133425-5806b90b4cdb/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= +knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0 h1:/DuVdR9EfTJQh73U7vymbSYwzNrJSaI1turWaOpfxs8= +knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0/go.mod h1:7YlXMU5kaJVKc15ilLYkVhbv9F3UGIPUpIrQuwSQa4E= +knative.dev/serving v0.49.1-0.20260615142436-1683170667da h1:NCH/hZJYf7B6cqWBYLxk1ejk27fkECr1lLyRm+jLBPk= +knative.dev/serving v0.49.1-0.20260615142436-1683170667da/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From aadbac07fbfae1961cc25501c4e98599a84bf7c4 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Mon, 15 Jun 2026 22:51:47 -0400 Subject: [PATCH 33/44] upgrade to latest dependencies (#3893) bumping golang.org/x/net 7770ec4...9e7fdbf: > 9e7fdbf internal/http3: fix wrong argument being given when validating header value > b686e5f internal/http3: add gzip support to transport > 8a34885 go.mod: update golang.org/x dependencies > 72eaf98 dns/dnsmessage: correctly validate SVCB record parameter order > 82e7868 dns/dnsmessage: avoid panic when parsing SVCB record with truncated data > b64f1fa internal/http3: add server support for "Trailer:" magic prefix > 2707ee2 internal/http3: implement HTTP/3 clientConn methods > 31358cc internal/http3: snapshot response headers at WriteHeader time > 8ecbaa9 html: don't adjust xml:base > 8ae811a html: properly handle end script tag in fragment mode > 3e9891d html: add selectedcontent tests to skip list > 5a0480c html: update parser to match spec select behavior > 86561db html: properly handle fragment foreign content breakout > 93a0bd9 html: switch from the webkit tests to html5lib-tests > 42abb85 http2: enable HTTP/2 in wrap configureTransport > 9d1bca8 all: fix some comments to improve readability > ed393fe html: properly emit comment token when eof in markup declaration open state > d423833 html: properly handle unicode attribute dup detection > 7c3abf5 html: support named entities shorter than their encoding > 2d3cf22 html: properly parse zero numeric entity > 3f77b08 html: avoid overflow when decoding numeric entities > 1e933f5 html: replace nul bytes in tag names and attributes > 088b2ce html: properly handle bogus comment > 65e84c3 html: add html5lib-tests tokenizer test suite > 008e5e7 http2: register h2 ALPN on s.TLSConfig in wrap configureServer > db07875 all: bump x/crypto to 0.52.0 > 657eb13 internal/http3: document possible issue in Darwin when using dual-stack binding bumping golang.org/x/sys 397d5f8...d58dcfa: > d58dcfa unix: add GPIO constants and structs bumping golang.org/x/term 3c3e485...3b43943: > 3b43943 go.mod: update golang.org/x dependencies bumping golang.org/x/text 3ef517e...f4bb632: > f4bb632 go.mod: update golang.org/x dependencies bumping knative.dev/serving 1683170...394d3f9: > 394d3f9 upgrade to latest dependencies (# 16643) bumping golang.org/x/sync ec11c4a...5071ed6: > 5071ed6 all: fix some comments to improve readability bumping knative.dev/pkg 0f785e3...6300c57: > 6300c57 Bump the golang-x group with 3 updates (# 3370) bumping golang.org/x/crypto b8a14a8...45460e0: > 45460e0 go.mod: update golang.org/x dependencies > d37c95e pkcs12: limit PBKDF iteration count to prevent CPU exhaustion > e2ffffe ssh: reject incomplete gssapi-with-mic configurations > 60e158a ssh/test: isolate CLI tests from user SSH config and agent > 1b77d23 ssh/knownhosts: reject lines with multiple or unknown markers > 3872a2b ssh/knownhosts: verify declared key type matches decoded key > 9f72ecc ssh/knownhosts: treat only ASCII space and tab as whitespace > 8f405a4 ssh: validate ECDSA curve matches expected algorithm > bb41b3d ssh: improve DH GEX group selection using PreferredBits > e04e721 ssh/agent: validate ed25519 private key length in Add > b315afd ssh: limit bcrypt KDF rounds in OpenSSH private key decryption > d4a85f4 ssh/agent: limit RSA key size on Add requests > 6f39c52 ssh: limit RSA key size in OpenSSH private key parsing > 4c4d20b ssh: fix spinloop in mux SendRequest drain on closed channel > e3e62d9 ssh: fix spinloop in channel SendRequest drain on closed channel > 5adb68b ssh: cap total userauth attempts per server connection > 9beb694 ssh: prevent malformed exit-status panic > e5306b2 ssh/agent: support parallel signing using request pipelining > d2fe592 ssh: add openssh controlmaster socket support > d59570d ssh: add AuthCallback to ClientConfig > 4aab0d9 ssh: return partial success immediately from publickey auth > a1c0d99 go.mod: update golang.org/x dependencies > 3c7c869 ssh: fix deadlock on unexpected channel responses > 533fb3f ssh: fix source-address critical option bypass > abbc44d ssh: fix incorrect operator order > e052873 ssh: fix infinite loop on large channel writes due to integer overflow > b61cf85 ssh: enforce user presence verification for security keys > 9c2cd33 ssh: enforce strict limits on DSA key parameters > 8907318 ssh: reject RSA keys with excessively large moduli > ffd87b4 ssh: fix panic when authority callbacks are nil > 4e7a738 ssh: fix deadlock on unexpected global responses > b25012b ssh: enforce nil Permissions when returning PartialSuccessError > 6c195c8 ssh: prevent memory leak when rejecting channels > f717e29 ssh/knownhosts: respect @revoked CA keys > e7c36cc ssh/agent: prevent panic on pathological ed25519 inputs > 0fb843a ssh/agent: reject keys with unsupported confirm constraint > e3d1254 ssh/agent: don't accept keys with unsupported constraints > a1ce0fe ssh/agent: preserve constraint extensions when adding keys > a749d17 chacha20poly1305: remove usages of BYTE instr > 7ee5970 chacha20poly1305: drop pre-AVX assembly impl > 44decbf blake2b: merge go125.go into blake2b_test.go bumping knative.dev/eventing 69b976f...9a2ae8b: > 9a2ae8b [main] Upgrade to latest dependencies (# 9155) bumping golang.org/x/mod 643da9b...deb1dfc: > deb1dfc go.mod: update golang.org/x dependencies > 087f651 modfile: use slices.Backward > 343ee60 x/mod: allow for aggressively conslidating requires bumping knative.dev/client/pkg 84a3162...b41d413: > b41d413 upgrade to latest dependencies (# 2238) Signed-off-by: Knative Automation --- go.mod | 22 +++++++++++----------- go.sum | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 3a2e924f47..7b57156a29 100644 --- a/go.mod +++ b/go.mod @@ -55,13 +55,13 @@ require ( github.com/tektoncd/pipeline v1.10.0 gitlab.com/gitlab-org/api/client-go v0.150.0 go.podman.io/image/v5 v5.39.2-0.20260306124909-d48bc74146d6 - golang.org/x/crypto v0.51.0 - golang.org/x/mod v0.36.0 - golang.org/x/net v0.55.0 + golang.org/x/crypto v0.53.0 + golang.org/x/mod v0.37.0 + golang.org/x/net v0.56.0 golang.org/x/oauth2 v0.36.0 - golang.org/x/sync v0.20.0 - golang.org/x/sys v0.45.0 - golang.org/x/term v0.43.0 + golang.org/x/sync v0.21.0 + golang.org/x/sys v0.46.0 + golang.org/x/term v0.44.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 @@ -70,11 +70,11 @@ require ( k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351 - knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56 + knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071 + knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0 - knative.dev/serving v0.49.1-0.20260615142436-1683170667da + knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 + knative.dev/serving v0.49.1-0.20260615163344-394d3f959991 sigs.k8s.io/controller-runtime v0.23.3 ) @@ -303,7 +303,7 @@ require ( go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/text v0.38.0 // indirect golang.org/x/time v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/go.sum b/go.sum index 78159a6266..782095295a 100644 --- a/go.sum +++ b/go.sum @@ -1252,8 +1252,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1300,8 +1300,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1369,8 +1369,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1409,8 +1409,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1503,8 +1503,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1520,8 +1520,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1539,8 +1539,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1619,8 +1619,8 @@ golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= +golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1875,18 +1875,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351 h1:NCPsKc6serIwi9sh3afGXFoLB0Edd2lGEVWZaJAoQzw= -knative.dev/client/pkg v0.0.0-20260611151027-84a3162fd351/go.mod h1:M69p8Zli8zEoZ4p0N4H+W9KR2UvBUMPjdoiaA3NAbOg= -knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56 h1:AHDySG8Wz6mQuiKAXanlYWLjTM8hRU9P8sBPKcHbOso= -knative.dev/eventing v0.49.1-0.20260611075324-69b976f7fd56/go.mod h1:ibEWNiJvssamCNesm0N1BmB5rhWdAyJHYEIcxI+99sQ= +knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071 h1:VlvA+6Zuhfd4kH6bDtZlR/waN73mTc5lj18EVKXxBJ8= +knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071/go.mod h1:S6FRdOxHtFdhe4g4TzUtbCmpjO0ONZ109N4rGHUsB8c= +knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023 h1:ummPeOc6N040T4/W0az4eR1fXKv0Vu24m1DVv9jysVw= +knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023/go.mod h1:1+9UhTLOb6iFQ63L6rC02wZlYlpB8STI+Z7/aG3vodc= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= -knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0 h1:/DuVdR9EfTJQh73U7vymbSYwzNrJSaI1turWaOpfxs8= -knative.dev/pkg v0.0.0-20260615142336-0f785e385ea0/go.mod h1:7YlXMU5kaJVKc15ilLYkVhbv9F3UGIPUpIrQuwSQa4E= -knative.dev/serving v0.49.1-0.20260615142436-1683170667da h1:NCH/hZJYf7B6cqWBYLxk1ejk27fkECr1lLyRm+jLBPk= -knative.dev/serving v0.49.1-0.20260615142436-1683170667da/go.mod h1:fHHSUO7OjvmJmDIQ2VBsAHUjN24c75FmW2vudgbMU48= +knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 h1:l6cYazykii3EtY+DVcQOBMgGQ9WBnhAKeGRSo+15JiI= +knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78/go.mod h1:Ve19ZYW7DwIfQL4oCT9t9zmPp4egv0KacKVPXUcivDQ= +knative.dev/serving v0.49.1-0.20260615163344-394d3f959991 h1:r5GvAQqSlqp2hIYggUG4EqIUaRIa6LnIPAv/vbdwUNk= +knative.dev/serving v0.49.1-0.20260615163344-394d3f959991/go.mod h1:c6/K7I2EwY8LGJw2+9ktka1OOXJPD7Z3XqnjSX07wxw= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From df1ccc4dca22c708fe8e57b7925d28686db2b7b2 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Tue, 16 Jun 2026 22:44:47 -0400 Subject: [PATCH 34/44] upgrade to latest dependencies (#3894) bumping knative.dev/client/pkg b41d413...b809afb: > b809afb upgrade to latest dependencies (# 2240) > 025f9b5 upgrade to latest dependencies (# 2239) bumping knative.dev/eventing 9a2ae8b...2996bf9: > 2996bf9 [main] Upgrade to latest dependencies (# 9156) bumping knative.dev/serving 394d3f9...c03e2fc: > c03e2fc Update net-kourier nightly (# 16646) > b555de5 Update net-istio nightly (# 16647) bumping knative.dev/networking c8765a7...d4e13b7: > d4e13b7 upgrade to latest dependencies (# 1148) > d359045 upgrade to latest dependencies (# 1147) Signed-off-by: Knative Automation --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- hack/component-versions.json | 4 ++-- hack/component-versions.sh | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 7b57156a29..00432db3be 100644 --- a/go.mod +++ b/go.mod @@ -70,11 +70,11 @@ require ( k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071 - knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023 + knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256 + knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 - knative.dev/serving v0.49.1-0.20260615163344-394d3f959991 + knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5 sigs.k8s.io/controller-runtime v0.23.3 ) @@ -318,7 +318,7 @@ require ( k8s.io/apiserver v0.35.6 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - knative.dev/networking v0.0.0-20260602144506-c8765a725c2b // indirect + knative.dev/networking v0.0.0-20260616021346-d4e13b76b133 // indirect sigs.k8s.io/gateway-api v1.4.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.0 // indirect diff --git a/go.sum b/go.sum index 782095295a..cc9c0a0b30 100644 --- a/go.sum +++ b/go.sum @@ -1875,18 +1875,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071 h1:VlvA+6Zuhfd4kH6bDtZlR/waN73mTc5lj18EVKXxBJ8= -knative.dev/client/pkg v0.0.0-20260615160251-b41d413e7071/go.mod h1:S6FRdOxHtFdhe4g4TzUtbCmpjO0ONZ109N4rGHUsB8c= -knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023 h1:ummPeOc6N040T4/W0az4eR1fXKv0Vu24m1DVv9jysVw= -knative.dev/eventing v0.49.1-0.20260615165644-9a2ae8b2b023/go.mod h1:1+9UhTLOb6iFQ63L6rC02wZlYlpB8STI+Z7/aG3vodc= +knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256 h1:lethEVt4zqdXrkENrLi2a7P0KNNRc9cl3TF6ZshFUC0= +knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256/go.mod h1:dndOldiXr8UEyj/lOZ0egWOkgWRSBKAJEW63IxYr3Os= +knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa h1:pZMOuMVy6GVo5nxuqIXFcl+wTe65yV+ipcHGfRuwCLI= +knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa/go.mod h1:qAdl4gctMP0BvhxtkM7QCalOHcwY62MjB4s9XYSwzAw= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= -knative.dev/networking v0.0.0-20260602144506-c8765a725c2b h1:SlEkmc5m5EAaXomG82qYifOXC+YpLg+jPS4VcDkIz3c= -knative.dev/networking v0.0.0-20260602144506-c8765a725c2b/go.mod h1:Dx+BnBfPyYXFP+DiuB9mzLJiJ4gaSo0/dhRGTkU4Tjw= +knative.dev/networking v0.0.0-20260616021346-d4e13b76b133 h1:9gaxONVJQn7tfIfL0PgIvT8o6H5KVE2Y2lbA9jX+wsw= +knative.dev/networking v0.0.0-20260616021346-d4e13b76b133/go.mod h1:U59uUg7O9WeFVVkpm6nhQ89pTv1rwskbBwrf7FdPl1o= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 h1:l6cYazykii3EtY+DVcQOBMgGQ9WBnhAKeGRSo+15JiI= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78/go.mod h1:Ve19ZYW7DwIfQL4oCT9t9zmPp4egv0KacKVPXUcivDQ= -knative.dev/serving v0.49.1-0.20260615163344-394d3f959991 h1:r5GvAQqSlqp2hIYggUG4EqIUaRIa6LnIPAv/vbdwUNk= -knative.dev/serving v0.49.1-0.20260615163344-394d3f959991/go.mod h1:c6/K7I2EwY8LGJw2+9ktka1OOXJPD7Z3XqnjSX07wxw= +knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5 h1:lWVRcqKbGpt6XeMBFjC/5pGsEIKYvhGGytHziwimTig= +knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5/go.mod h1:c6/K7I2EwY8LGJw2+9ktka1OOXJPD7Z3XqnjSX07wxw= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/hack/component-versions.json b/hack/component-versions.json index e2a7f65524..860d72104a 100644 --- a/hack/component-versions.json +++ b/hack/component-versions.json @@ -1,11 +1,11 @@ { "Contour": { - "version": "v1.22.1", + "version": "v1.22.2", "owner": "knative-extensions", "repo": "net-contour" }, "Eventing": { - "version": "v1.22.1", + "version": "v1.22.2", "owner": "knative", "repo": "eventing" }, diff --git a/hack/component-versions.sh b/hack/component-versions.sh index 5eaf39aef7..96c939f3f7 100644 --- a/hack/component-versions.sh +++ b/hack/component-versions.sh @@ -12,8 +12,8 @@ set_versions() { # find source-of-truth in component-versions.json to add/modify components knative_serving_version="v1.22.1" - knative_eventing_version="v1.22.1" - contour_version="v1.22.1" + knative_eventing_version="v1.22.2" + contour_version="v1.22.2" tekton_version="v1.1.0" pac_version="v0.35.2" keda_version="v2.17.0" From f72ecdaf10749b714264730a6b3d6d2312c2a1fa Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:05:48 +0200 Subject: [PATCH 35/44] fix: gracefully shutdown host-built functions (#3895) --- pkg/oci/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/oci/builder.go b/pkg/oci/builder.go index 0050820e2d..19f2486417 100644 --- a/pkg/oci/builder.go +++ b/pkg/oci/builder.go @@ -639,7 +639,7 @@ func newConfigFile(job buildJob, p v1.Platform, base v1.Image, imageLayers []ima Volumes: newConfigVolumes(job), ExposedPorts: map[string]struct{}{"8080/tcp": {}}, WorkingDir: "/func/", - StopSignal: "SIGKILL", + StopSignal: "SIGTERM", User: fmt.Sprintf("%v:%v", DefaultUid, DefaultGid), Labels: job.labels, }, From a17813eb34b0dca91e177bc1230ef367d2a1b66e Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Thu, 18 Jun 2026 02:15:47 +0200 Subject: [PATCH 36/44] e2e: add lifecycle structure + tests for existing python + go (#3899) * e2e: add lifecycle hooks test skeleton, runtime table, and testdata sources * e2e: implement TestLifecycle_Hooks and TestLifecycle_Stop * e2e: create lifecycle makefile target --- Makefile | 8 +- e2e/e2e_lifecycle_test.go | 244 +++++++++++++++++++++++++ e2e/testdata/lifecycle/go/hooks.go | 69 +++++++ e2e/testdata/lifecycle/go/stop.go | 30 +++ e2e/testdata/lifecycle/python/hooks.py | 50 +++++ e2e/testdata/lifecycle/python/stop.py | 33 ++++ 6 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 e2e/e2e_lifecycle_test.go create mode 100644 e2e/testdata/lifecycle/go/hooks.go create mode 100644 e2e/testdata/lifecycle/go/stop.go create mode 100644 e2e/testdata/lifecycle/python/hooks.py create mode 100644 e2e/testdata/lifecycle/python/stop.py diff --git a/Makefile b/Makefile index b528032788..9d4c6db593 100644 --- a/Makefile +++ b/Makefile @@ -294,7 +294,7 @@ test-integration: ## Run integration tests using an available cluster. .PHONY: test-e2e test-e2e: func-instrumented-bin ## Basic E2E tests (includes core, metadata and remote tests) # Runtime and other options can be configured using the FUNC_E2E_* environment variables. see e2e_test.go - go test -tags e2e -timeout 60m ./e2e -v -run "TestCore_|TestMetadata_|TestRemote_" + go test -tags e2e -timeout 60m ./e2e -v -run "TestCore_|TestMetadata_|TestRemote_|TestLifecycle_" go tool covdata textfmt -i=$${FUNC_E2E_GOCOVERDIR:-.coverage} -o coverage.txt .PHONY: test-e2e-podman @@ -309,6 +309,12 @@ test-e2e-matrix: func-instrumented-bin ## Basic E2E tests (includes core, metada FUNC_E2E_MATRIX=true go test -tags e2e -timeout 120m ./e2e -v -run TestMatrix_ go tool covdata textfmt -i=$${FUNC_E2E_GOCOVERDIR:-.coverage} -o coverage.txt +.PHONY: test-e2e-lifecycle +test-e2e-lifecycle: func-instrumented-bin ## Run lifecycle hook E2E tests (Start, Stop, Ready, Alive) + # Runtime and other options can be configured using the FUNC_E2E_* environment variables. see e2e_test.go + go test -tags e2e -timeout 60m ./e2e -v -run TestLifecycle_ + go tool covdata textfmt -i=$${FUNC_E2E_GOCOVERDIR:-.coverage} -o coverage.txt + .PHONY: test-e2e-config-ci test-e2e-config-ci: func-instrumented-bin ## CI tests for generated GitHub Workflows # Runtime and other options can be configured using the FUNC_E2E_* environment variables. see e2e_test.go diff --git a/e2e/e2e_lifecycle_test.go b/e2e/e2e_lifecycle_test.go new file mode 100644 index 0000000000..5e0775d3d0 --- /dev/null +++ b/e2e/e2e_lifecycle_test.go @@ -0,0 +1,244 @@ +//go:build e2e + +package e2e + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +// describe how to set up a Function for a specific runtimes testing lifecycle hooks +type lifecycleRuntime struct { + builder string // "host", "pack", "s2i" + // TODO: expand matrix for each deployer + // deployer string // "knative","raw","keda" + srcPath string // where to write source in func dir (e.g. "function.go") + srcExt string // extension for testdata lookup (".go", ".py") + initArgs []string // extra args for func init +} + +var allRuntimes = []string{ + "go", "python", "node", "typescript", "rust", "quarkus", "springboot", +} + +var lifecycleRuntimes = map[string]*lifecycleRuntime{ + "go": { + builder: "host", + srcPath: "function.go", + srcExt: ".go", + initArgs: []string{}, + }, + "python": { + builder: "host", + srcPath: filepath.Join("function", "func.py"), + srcExt: ".py", + initArgs: []string{}, + }, +} + +// loads testdata/lifecycle/{runtime}/{hook}{extension} +// lifecycleSource reads the testdata source file for the given runtime and hook. +func lifecycleSource(t *testing.T, runtime, hook string, rt *lifecycleRuntime) []byte { + t.Helper() + path := filepath.Join(Testdata, "lifecycle", runtime, hook+rt.srcExt) + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("lifecycleSource: reading %s: %v", path, err) + } + return data +} + +// getBody performs a single HTTP GET and returns the response body as a string. +func getBody(t *testing.T, url string) string { + t.Helper() + resp, err := http.Get(url) //nolint:noctx + if err != nil { + t.Fatalf("getBody: GET %s: %v", url, err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("getBody: reading body from %s: %v", url, err) + } + return string(body) +} + +// extractValue parses KEY=value pairs from a space-separated response body and +// returns the value for the given key, or "" if the key is not present. +// Example: extractValue("ALIVE=true NONCE=12345", "NONCE") == "12345" +func extractValue(body, key string) string { + for _, field := range strings.Fields(body) { + if k, v, ok := strings.Cut(field, "="); ok && k == key { + return v + } + } + return "" +} + +// TestLifecycle_Hooks verifies the Start, Ready, and Alive hooks using a single +// merged function per runtime. The function implements all three hooks +// simultaneously, and the test verifies them in sequence: +// 1. Start — fires during cold start, captures config env var +// 2. Ready — returns false for 15s warm-up, then true +// 3. Alive — defaults true, toggled false via /set-unhealthy, pod restarts +func TestLifecycle_Hooks(t *testing.T) { + for _, name := range allRuntimes { + rt, enabled := lifecycleRuntimes[name] + t.Run(name, func(t *testing.T) { + if !enabled { + t.Skipf("lifecycle hooks not enabled for %s", name) + } + + funcName := fmt.Sprintf("func-e2e-test-lifecycle-hooks-%s", name) + root := fromCleanEnv(t, funcName) + + args := append([]string{"init", "-l=" + name}, rt.initArgs...) + if err := newCmd(t, args...).Run(); err != nil { + t.Fatal(err) + } + + if err := newCmd(t, "config", "envs", "add", + "--name=TEST_CONFIG_VALUE", "--value=e2e-lifecycle-hooks").Run(); err != nil { + t.Fatal(err) + } + + src := lifecycleSource(t, name, "hooks", rt) + if err := os.WriteFile(filepath.Join(root, rt.srcPath), src, 0644); err != nil { + t.Fatal(err) + } + + deployArgs := []string{"deploy"} + if rt.builder != "" { + deployArgs = append(deployArgs, "--builder", rt.builder) + } + if err := newCmd(t, deployArgs...).Run(); err != nil { + t.Fatal(err) + } + defer clean(t, funcName, Namespace) + if rt.builder != "" { + defer cleanImages(t, funcName) + } + + url := ksvcUrl(funcName) + + // Phase 1: Start hook — verify it fired and received config + t.Run("start", func(t *testing.T) { + if !waitFor(t, url, + withContentMatch("START_OK:e2e-lifecycle-hooks"), + withWaitTimeout(5*time.Minute)) { + t.Fatal("Start hook did not fire or did not receive config") + } + }) + + // Phase 2: Ready hook — verify readiness endpoint + t.Run("ready", func(t *testing.T) { + if !waitFor(t, url, + withContentMatch("READY=true"), + withWaitTimeout(5*time.Minute)) { + t.Fatal("Ready hook: function never became ready") + } + + body := getBody(t, url+"/health/readiness") + if !strings.Contains(strings.ToUpper(body), "READY") { + t.Fatalf("readiness endpoint returned unexpected body: %s", body) + } + }) + + // Phase 3: Alive hook — toggle unhealthy, verify pod restart via nonce + t.Run("alive", func(t *testing.T) { + body := getBody(t, url) + nonce1 := extractValue(body, "NONCE") + if nonce1 == "" { + t.Fatalf("no NONCE in response: %s", body) + } + t.Logf("initial nonce: %s", nonce1) + + unhealthyBody := getBody(t, url+"/set-unhealthy") + t.Logf("set-unhealthy response: %s", unhealthyBody) + + if !waitFor(t, url, + withContentMatch("ALIVE=true"), + withWaitTimeout(3*time.Minute)) { + t.Fatal("function did not recover after liveness failure") + } + + body = getBody(t, url) + nonce2 := extractValue(body, "NONCE") + if nonce2 == "" { + t.Fatalf("no NONCE in response after restart: %s", body) + } + t.Logf("post-restart nonce: %s", nonce2) + + if nonce1 == nonce2 { + t.Fatal("nonce unchanged — pod was not restarted by liveness probe failure") + } + }) + }) + } +} + +func TestLifecycle_Stop(t *testing.T) { + for _, name := range allRuntimes { + rt, enabled := lifecycleRuntimes[name] + t.Run(name, func(t *testing.T) { + if !enabled { + t.Skipf("lifecycle hooks not enabled for %s", name) + } + + recName := fmt.Sprintf("func-e2e-lifecycle-stop-rec-%s", name) + rec := deployRecorder(t, recName) + + funcName := fmt.Sprintf("func-e2e-test-lifecycle-stop-%s", name) + root := fromCleanEnv(t, funcName) + + args := append([]string{"init", "-l=" + name}, rt.initArgs...) + if err := newCmd(t, args...).Run(); err != nil { + t.Fatal(err) + } + + if err := newCmd(t, "config", "envs", "add", + "--name=RECORDER_URL", "--value="+rec.internalURL).Run(); err != nil { + t.Fatal(err) + } + + src := lifecycleSource(t, name, "stop", rt) + if err := os.WriteFile(filepath.Join(root, rt.srcPath), src, 0644); err != nil { + t.Fatal(err) + } + + deployArgs := []string{"deploy"} + if rt.builder != "" { + deployArgs = append(deployArgs, "--builder", rt.builder) + } + if err := newCmd(t, deployArgs...).Run(); err != nil { + t.Fatal(err) + } + if rt.builder != "" { + defer cleanImages(t, funcName) + } + + if !waitFor(t, ksvcUrl(funcName), withWaitTimeout(5*time.Minute)) { + t.Fatal("notifier function did not deploy correctly") + } + + if err := newCmd(t, "delete", funcName, "--namespace", Namespace).Run(); err != nil { + t.Logf("error deleting notifier: %v", err) + } + + stopID := fmt.Sprintf("stop-%s", name) + ctx, cancel := context.WithTimeout(t.Context(), 60*time.Second) + defer cancel() + if !rec.awaitReceived(ctx, t, stopID) { + t.Fatalf("recorder did not receive stop event %q within 60s", stopID) + } + t.Logf("Stop hook confirmed: recorder received %q", stopID) + }) + } +} diff --git a/e2e/testdata/lifecycle/go/hooks.go b/e2e/testdata/lifecycle/go/hooks.go new file mode 100644 index 0000000000..d994fac5fc --- /dev/null +++ b/e2e/testdata/lifecycle/go/hooks.go @@ -0,0 +1,69 @@ +package function + +import ( + "context" + "fmt" + "net/http" + "sync" + "time" +) + +type Function struct { + mu sync.Mutex + startCalled bool + configValue string + ready bool + alive bool + nonce string +} + +func New() *Function { + f := &Function{ + alive: true, + nonce: fmt.Sprintf("%d", time.Now().UnixNano()), + } + go func() { + time.Sleep(15 * time.Second) + f.mu.Lock() + f.ready = true + f.mu.Unlock() + }() + return f +} + +func (f *Function) Start(_ context.Context, cfg map[string]string) error { + f.mu.Lock() + defer f.mu.Unlock() + f.startCalled = true + f.configValue = cfg["TEST_CONFIG_VALUE"] + return nil +} + +func (f *Function) Ready(_ context.Context) (bool, error) { + f.mu.Lock() + defer f.mu.Unlock() + return f.ready, nil +} + +func (f *Function) Alive(_ context.Context) (bool, error) { + f.mu.Lock() + defer f.mu.Unlock() + return f.alive, nil +} + +func (f *Function) Handle(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/set-unhealthy" { + f.mu.Lock() + f.alive = false + f.mu.Unlock() + fmt.Fprintln(w, "UNHEALTHY") + return + } + f.mu.Lock() + defer f.mu.Unlock() + if f.startCalled { + fmt.Fprintf(w, "START_OK:%s READY=%t ALIVE=%t NONCE=%s", f.configValue, f.ready, f.alive, f.nonce) + } else { + fmt.Fprintf(w, "START_NOT_CALLED READY=%t ALIVE=%t NONCE=%s", f.ready, f.alive, f.nonce) + } +} diff --git a/e2e/testdata/lifecycle/go/stop.go b/e2e/testdata/lifecycle/go/stop.go new file mode 100644 index 0000000000..707b2f5b94 --- /dev/null +++ b/e2e/testdata/lifecycle/go/stop.go @@ -0,0 +1,30 @@ +package function + +import ( + "context" + "fmt" + "net/http" + "os" +) + +type Function struct { + recorderURL string +} + +func New() *Function { + return &Function{recorderURL: os.Getenv("RECORDER_URL")} +} + +func (f *Function) Stop(_ context.Context) error { + if f.recorderURL != "" { + resp, err := http.Post(f.recorderURL+"/record?id=stop-go", "text/plain", nil) + if err == nil { + resp.Body.Close() + } + } + return nil +} + +func (f *Function) Handle(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, "OK") +} diff --git a/e2e/testdata/lifecycle/python/hooks.py b/e2e/testdata/lifecycle/python/hooks.py new file mode 100644 index 0000000000..c3de1086ac --- /dev/null +++ b/e2e/testdata/lifecycle/python/hooks.py @@ -0,0 +1,50 @@ +import threading +import time + + +def new(): + return Function() + + +class Function: + def __init__(self): + self.start_called = False + self.config_value = "" + self._ready = False + self._alive = True + self._nonce = str(time.time_ns()) + threading.Timer(15.0, self._become_ready).start() + + def _become_ready(self): + self._ready = True + + def start(self, cfg): + self.start_called = True + self.config_value = cfg.get("TEST_CONFIG_VALUE", "") + + def ready(self): + return self._ready, "ready" if self._ready else "warming up" + + def alive(self): + return self._alive, "alive" if self._alive else "unhealthy" + + async def handle(self, scope, receive, send): + path = scope.get("path", "/") + + if path == "/set-unhealthy": + self._alive = False + body = b"UNHEALTHY" + elif self.start_called: + body = f"START_OK:{self.config_value} READY={str(self._ready).lower()} ALIVE={str(self._alive).lower()} NONCE={self._nonce}".encode() + else: + body = f"START_NOT_CALLED READY={str(self._ready).lower()} ALIVE={str(self._alive).lower()} NONCE={self._nonce}".encode() + + await send({ + "type": "http.response.start", + "status": 200, + "headers": [[b"content-type", b"text/plain"]], + }) + await send({ + "type": "http.response.body", + "body": body, + }) diff --git a/e2e/testdata/lifecycle/python/stop.py b/e2e/testdata/lifecycle/python/stop.py new file mode 100644 index 0000000000..d29e9945de --- /dev/null +++ b/e2e/testdata/lifecycle/python/stop.py @@ -0,0 +1,33 @@ +import os +import urllib.request + + +def new(): + return Function() + + +class Function: + def __init__(self): + self._recorder_url = os.environ.get("RECORDER_URL", "") + + def stop(self): + if self._recorder_url: + req = urllib.request.Request( + self._recorder_url + "/record?id=stop-python", + method="POST", + ) + try: + urllib.request.urlopen(req, timeout=5) + except Exception: + pass + + async def handle(self, scope, receive, send): + await send({ + "type": "http.response.start", + "status": 200, + "headers": [[b"content-type", b"text/plain"]], + }) + await send({ + "type": "http.response.body", + "body": b"OK", + }) From 813791a9ede71a14d3ee205f285cb7beae7457bc Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Wed, 17 Jun 2026 22:46:48 -0400 Subject: [PATCH 37/44] upgrade to latest dependencies (#3900) bumping knative.dev/eventing 2996bf9...8cf4c4b: > 8cf4c4b fix: spread vreplicas across minReplicas pods in scheduler (# 9157) bumping knative.dev/client/pkg b809afb...dc5d0a4: > dc5d0a4 upgrade to latest dependencies (# 2242) > 6213d96 upgrade to latest dependencies (# 2241) bumping knative.dev/serving c03e2fc...f8d0736: > f8d0736 upgrade to latest dependencies (# 16644) Signed-off-by: Knative Automation --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 00432db3be..0335201caa 100644 --- a/go.mod +++ b/go.mod @@ -70,11 +70,11 @@ require ( k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256 - knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa + knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb + knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 - knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5 + knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95 sigs.k8s.io/controller-runtime v0.23.3 ) diff --git a/go.sum b/go.sum index cc9c0a0b30..9ebfa4702a 100644 --- a/go.sum +++ b/go.sum @@ -1875,18 +1875,18 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256 h1:lethEVt4zqdXrkENrLi2a7P0KNNRc9cl3TF6ZshFUC0= -knative.dev/client/pkg v0.0.0-20260616154152-b809afb03256/go.mod h1:dndOldiXr8UEyj/lOZ0egWOkgWRSBKAJEW63IxYr3Os= -knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa h1:pZMOuMVy6GVo5nxuqIXFcl+wTe65yV+ipcHGfRuwCLI= -knative.dev/eventing v0.49.1-0.20260616033845-2996bf922daa/go.mod h1:qAdl4gctMP0BvhxtkM7QCalOHcwY62MjB4s9XYSwzAw= +knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb h1:ePWeE/qkznu/b/modsusbX/k21h0oGOLd8x1sXhryOo= +knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb/go.mod h1:MbG2meS4AR6OyGt4DPhZ1wgfMC+MOverk7SHuCIH/tE= +knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 h1:kG/rr63mOgmoEDzdYrDMIMIXfKPO0ycheUVQk94JtGA= +knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303/go.mod h1:qAdl4gctMP0BvhxtkM7QCalOHcwY62MjB4s9XYSwzAw= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260616021346-d4e13b76b133 h1:9gaxONVJQn7tfIfL0PgIvT8o6H5KVE2Y2lbA9jX+wsw= knative.dev/networking v0.0.0-20260616021346-d4e13b76b133/go.mod h1:U59uUg7O9WeFVVkpm6nhQ89pTv1rwskbBwrf7FdPl1o= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 h1:l6cYazykii3EtY+DVcQOBMgGQ9WBnhAKeGRSo+15JiI= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78/go.mod h1:Ve19ZYW7DwIfQL4oCT9t9zmPp4egv0KacKVPXUcivDQ= -knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5 h1:lWVRcqKbGpt6XeMBFjC/5pGsEIKYvhGGytHziwimTig= -knative.dev/serving v0.49.1-0.20260616151854-c03e2fc554f5/go.mod h1:c6/K7I2EwY8LGJw2+9ktka1OOXJPD7Z3XqnjSX07wxw= +knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95 h1:iqolXoSG7gOKehPjJJKUz+VSGp6Hu/kcU5VkTnta3gs= +knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95/go.mod h1:WZgo4qbsdf/kqnnE6mwulg8egXHqw0seZ1OqpiCTggA= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 36896cb720cdb39ed65fa36d3069d8fdcf6952b9 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Thu, 18 Jun 2026 08:57:47 +0200 Subject: [PATCH 38/44] pipelines: never upload .func to the cluster in remote builds (#3897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remote (on-cluster) builds tar the function source and upload it to a PVC. The ignore policy previously honored only .gitignore plus a hardcoded .git prefix, so the local runtime directory .func — scaffolding, built-image records, and any other machine-local state — was uploaded whenever the function had no .gitignore covering it. Apply the same authoritative ignore policy as local builds (fn.IsIgnored with the user's .funcignore patterns): .func is always excluded, and ignored directories are skipped wholesale (filepath.SkipDir) instead of being walked entry by entry. Scaffolding under .func/build is regenerated on-cluster by the func-scaffold step, so nothing needed is lost. --- pkg/pipelines/tekton/pipelines_provider.go | 20 +++++--- .../tekton/pipelines_provider_test.go | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/pkg/pipelines/tekton/pipelines_provider.go b/pkg/pipelines/tekton/pipelines_provider.go index fbb700f16b..6d13178c30 100644 --- a/pkg/pipelines/tekton/pipelines_provider.go +++ b/pkg/pipelines/tekton/pipelines_provider.go @@ -281,14 +281,17 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, fn // Creates tar stream with the function sources as they were in "./source" directory. func sourcesAsTarStream(f fn.Function) *io.PipeReader { - ignored := func(p string) bool { return strings.HasPrefix(p, ".git") } - if gi, err := gitignore.CompileIgnoreFile(filepath.Join(f.Root, ".gitignore")); err == nil { - ignored = func(p string) bool { - if strings.HasPrefix(p, ".git") { - return true - } - return gi.MatchesPath(p) + // Apply the same ignore policy as local builds: fn.IsIgnored always excludes + // the DefaultIgnored plus the user's .funcignore patterns. This guarantees local + // runtime data under .func — scaffolding (regenerated on-cluster by the + // func-scaffold step) + userPatterns := fn.ParseFuncIgnore(f.Root) + gi, giErr := gitignore.CompileIgnoreFile(filepath.Join(f.Root, ".gitignore")) + ignored := func(p string) bool { + if fn.IsIgnored(p, userPatterns) { + return true } + return giErr == nil && gi.MatchesPath(p) } pr, pw := io.Pipe() @@ -327,6 +330,9 @@ func sourcesAsTarStream(f fn.Function) *io.PipeReader { } if ignored(relp) { + if fi.IsDir() { + return filepath.SkipDir + } return nil } diff --git a/pkg/pipelines/tekton/pipelines_provider_test.go b/pkg/pipelines/tekton/pipelines_provider_test.go index 4d65c99c1e..158bc55930 100644 --- a/pkg/pipelines/tekton/pipelines_provider_test.go +++ b/pkg/pipelines/tekton/pipelines_provider_test.go @@ -81,6 +81,54 @@ func TestSourcesAsTarStream(t *testing.T) { } } +// TestSourcesAsTarStream_ExcludesFuncDir ensures the local runtime directory +// (.func) is never uploaded to the cluster — most importantly the credential +// file .func/local.yaml — even when the function has NO .gitignore. The +// exclusion must come from the authoritative DefaultIgnored policy, not from a +// .gitignore happening to list it. Scaffolding under .func/build is regenerated +// on-cluster by the func-scaffold step, so it is not needed in the upload. +func TestSourcesAsTarStream_ExcludesFuncDir(t *testing.T) { + root := t.TempDir() + // deliberately NO .gitignore is written + mustWrite := func(rel, content string) { + p := filepath.Join(root, filepath.FromSlash(rel)) + if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(p, []byte(content), 0600); err != nil { + t.Fatal(err) + } + } + mustWrite("app.js", "module.exports = () => {}\n") // a normal source file + mustWrite(".func/local.yaml", "auth:\n- cluster-url: https://secret\n user:\n token: SECRET\n") + mustWrite(".func/build/service/main.py", "# scaffolding\n") + mustWrite(".func/built-image", "example.com/img@sha256:deadbeef\n") + + rc := sourcesAsTarStream(fn.Function{Root: root}) + t.Cleanup(func() { _ = rc.Close() }) + + var appFound bool + tr := tar.NewReader(rc) + for { + hdr, err := tr.Next() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + t.Fatal(err) + } + if strings.HasPrefix(hdr.Name, "source/.func") { + t.Errorf(".func entry leaked into the upload stream: %q", hdr.Name) + } + if hdr.Name == "source/app.js" { + appFound = true + } + } + if !appFound { + t.Error("the app.js source file is missing from the stream") + } +} + func Test_createPipelinePersistentVolumeClaim(t *testing.T) { type mockType func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClass string) (err error) From 466c4dcb72b064a9cefdde39d7f55bbd71f61755 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Thu, 18 Jun 2026 10:58:49 -0400 Subject: [PATCH 39/44] upgrade to latest dependencies (#3907) bumping knative.dev/client/pkg dc5d0a4...43f7c74: > 43f7c74 upgrade to latest dependencies (# 2243) bumping knative.dev/serving f8d0736...d52dd68: > d52dd68 Update net-contour nightly (# 16648) Signed-off-by: Knative Automation --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0335201caa..85fc29c950 100644 --- a/go.mod +++ b/go.mod @@ -70,11 +70,11 @@ require ( k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb + knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 - knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95 + knative.dev/serving v0.49.1-0.20260618132849-d52dd68d8a9e sigs.k8s.io/controller-runtime v0.23.3 ) diff --git a/go.sum b/go.sum index 9ebfa4702a..c22f6f518a 100644 --- a/go.sum +++ b/go.sum @@ -1875,8 +1875,8 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb h1:ePWeE/qkznu/b/modsusbX/k21h0oGOLd8x1sXhryOo= -knative.dev/client/pkg v0.0.0-20260617150852-dc5d0a493adb/go.mod h1:MbG2meS4AR6OyGt4DPhZ1wgfMC+MOverk7SHuCIH/tE= +knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c h1:8rXuSChlZwownlnaIDliiYCXZ3bHj3JMOaCmFLtfFis= +knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c/go.mod h1:fdsbfxJV1hO4BabXs11CY5C1dR3etHIxzTNXQA2kkAo= knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 h1:kG/rr63mOgmoEDzdYrDMIMIXfKPO0ycheUVQk94JtGA= knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303/go.mod h1:qAdl4gctMP0BvhxtkM7QCalOHcwY62MjB4s9XYSwzAw= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= @@ -1885,8 +1885,8 @@ knative.dev/networking v0.0.0-20260616021346-d4e13b76b133 h1:9gaxONVJQn7tfIfL0Pg knative.dev/networking v0.0.0-20260616021346-d4e13b76b133/go.mod h1:U59uUg7O9WeFVVkpm6nhQ89pTv1rwskbBwrf7FdPl1o= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 h1:l6cYazykii3EtY+DVcQOBMgGQ9WBnhAKeGRSo+15JiI= knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78/go.mod h1:Ve19ZYW7DwIfQL4oCT9t9zmPp4egv0KacKVPXUcivDQ= -knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95 h1:iqolXoSG7gOKehPjJJKUz+VSGp6Hu/kcU5VkTnta3gs= -knative.dev/serving v0.49.1-0.20260617032249-f8d0736fef95/go.mod h1:WZgo4qbsdf/kqnnE6mwulg8egXHqw0seZ1OqpiCTggA= +knative.dev/serving v0.49.1-0.20260618132849-d52dd68d8a9e h1:6Gi6R8JLOu+cxCIrTWnCx7N+B4MFtB6Mtd6+4e+K7Xw= +knative.dev/serving v0.49.1-0.20260618132849-d52dd68d8a9e/go.mod h1:WZgo4qbsdf/kqnnE6mwulg8egXHqw0seZ1OqpiCTggA= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From aa7f2ea1fcfe6629fc11be8ee95581fe0c734d8b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 18:00:48 +0000 Subject: [PATCH 40/44] chore: update Quarkus platform version to 3.36.3 (#3906) Co-authored-by: Knative Automation --- generate/zz_filesystem_generated.go | 136 +++++++++++++------------- templates/quarkus/cloudevents/pom.xml | 2 +- templates/quarkus/http/pom.xml | 2 +- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/generate/zz_filesystem_generated.go b/generate/zz_filesystem_generated.go index ad03c739c2..51e461d501 100644 --- a/generate/zz_filesystem_generated.go +++ b/generate/zz_filesystem_generated.go @@ -7038,39 +7038,39 @@ var TemplatesZip = []byte{ 0xfe, 0x7e, 0x84, 0x63, 0x06, 0xb6, 0x4f, 0xe6, 0xb5, 0x86, 0xca, 0x84, 0x99, 0x33, 0xdf, 0xca, 0xf4, 0x9a, 0x54, 0x93, 0x64, 0x7c, 0xdd, 0x1f, 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, - 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0xdf, 0x6f, - 0xe3, 0x28, 0x10, 0x7e, 0xcf, 0x5f, 0x61, 0x45, 0x7d, 0x35, 0x24, 0xdb, 0xfb, 0xb1, 0xaa, 0x38, 0x56, 0xfb, 0x70, 0xa7, 0xab, 0xd4, 0xee, 0x56, 0x6a, 0x6f, 0x75, 0xaf, 0x14, 0x8f, 0x1d, 0x5a, - 0x1b, 0xbc, 0x80, 0xd3, 0x44, 0x55, 0xfe, 0xf7, 0x15, 0x60, 0x3b, 0xb6, 0x63, 0xb7, 0xd9, 0x2a, 0xdb, 0x37, 0x33, 0xf3, 0x31, 0x0c, 0xdf, 0x7c, 0x86, 0x81, 0x7c, 0xda, 0x14, 0x79, 0xb4, 0x06, - 0x6d, 0x84, 0x92, 0x7f, 0xcd, 0x97, 0x68, 0x31, 0xff, 0x44, 0x67, 0xa4, 0xd4, 0xea, 0x01, 0xb8, 0x8d, 0x36, 0x46, 0x5c, 0x18, 0xbe, 0x82, 0x82, 0x5d, 0x29, 0xce, 0xac, 0xc7, 0xac, 0xac, 0x2d, - 0x2f, 0x30, 0x2e, 0xd8, 0x1a, 0x24, 0x62, 0x25, 0xe3, 0x2b, 0x40, 0x4a, 0x67, 0xf8, 0xe6, 0xeb, 0x35, 0xfe, 0x0d, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, - 0x44, 0x1b, 0x93, 0xcc, 0xa3, 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0x61, 0xb1, 0x58, - 0xe2, 0xff, 0xaf, 0xaf, 0x6e, 0x7d, 0xca, 0xb1, 0x90, 0xc6, 0x32, 0xc9, 0x61, 0x4e, 0x67, 0x51, 0x44, 0x0a, 0x95, 0x40, 0xfe, 0x2d, 0xec, 0x94, 0xfa, 0x88, 0x04, 0xf7, 0x6c, 0x0e, 0x94, 0x69, - 0x55, 0x95, 0x97, 0x09, 0x55, 0x3a, 0x43, 0x8c, 0x17, 0x40, 0x70, 0x63, 0x71, 0x5e, 0xa6, 0xad, 0x48, 0x19, 0xb7, 0x97, 0x09, 0x4d, 0x2b, 0xc9, 0x1d, 0x1f, 0x04, 0x77, 0x8c, 0x0e, 0x53, 0x73, - 0x49, 0x97, 0x6e, 0x85, 0xf8, 0xf6, 0xcb, 0xe7, 0x9b, 0xdb, 0x7f, 0xbf, 0xde, 0x11, 0xbc, 0xee, 0xac, 0x52, 0x6a, 0x55, 0x82, 0xb6, 0x02, 0x0c, 0xf5, 0x9b, 0x22, 0x5c, 0x15, 0xa5, 0xc8, 0x41, - 0xc7, 0x65, 0x5e, 0x65, 0x42, 0xa2, 0x06, 0x7c, 0x8e, 0x3e, 0xa2, 0x25, 0xc1, 0x53, 0xee, 0x30, 0x39, 0x50, 0xd6, 0x60, 0x90, 0x86, 0x1c, 0x98, 0x01, 0xba, 0x5c, 0x12, 0x3c, 0xe1, 0x0a, 0xf3, - 0xea, 0x22, 0xa3, 0xfb, 0x4a, 0xe4, 0x09, 0x32, 0xaa, 0xd2, 0x1c, 0xfe, 0x96, 0x5c, 0x25, 0x42, 0x66, 0xf4, 0xbf, 0xbb, 0x7f, 0xe2, 0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, - 0x85, 0xcc, 0x90, 0xaa, 0x6c, 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10, 0xee, 0x7b, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b, 0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, - 0xf7, 0xaa, 0x20, 0xf8, 0x45, 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17, 0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0x7d, 0x9d, 0xce, 0xff, 0x40, 0x1f, 0x46, 0x02, 0xf4, 0x2b, 0x65, - 0x1e, 0x45, 0x79, 0x79, 0x67, 0xa8, 0xd5, 0x15, 0x10, 0xdc, 0x8c, 0x6a, 0x5f, 0xa5, 0x21, 0x15, 0x1a, 0x0e, 0x25, 0xe0, 0x74, 0x74, 0xfd, 0x27, 0xc1, 0x53, 0x08, 0xa7, 0x28, 0xdc, 0x97, 0x14, - 0x49, 0xa0, 0x04, 0x99, 0x80, 0xe4, 0xdb, 0x6b, 0x26, 0x59, 0x06, 0x05, 0x48, 0x5b, 0x2f, 0xd4, 0xba, 0x5a, 0xf9, 0xf5, 0xf0, 0x8d, 0xa9, 0xf3, 0x33, 0x9c, 0x3d, 0x4f, 0x12, 0xb3, 0xeb, 0xfd, - 0x20, 0xf5, 0xbc, 0xce, 0x1f, 0x31, 0x32, 0xb5, 0x53, 0xa5, 0xdd, 0xf0, 0xef, 0xa9, 0x03, 0x34, 0x3b, 0x1b, 0x99, 0x5d, 0xbb, 0x76, 0xbd, 0x1f, 0xaa, 0x9e, 0x66, 0xb7, 0x25, 0xd0, 0xd2, 0x49, - 0xc3, 0x7f, 0xed, 0x1d, 0x86, 0xab, 0x12, 0xa8, 0x28, 0x9c, 0xf0, 0x08, 0x0e, 0xa3, 0x66, 0xe7, 0x78, 0xb8, 0xf5, 0x8e, 0xa5, 0x61, 0x13, 0x4f, 0xd1, 0x39, 0x42, 0xe6, 0x08, 0x95, 0x2d, 0x91, - 0x7b, 0xd1, 0x1d, 0xd0, 0xd6, 0x25, 0xad, 0x51, 0x79, 0x5a, 0xc9, 0xef, 0xdb, 0xf8, 0x51, 0x32, 0x2b, 0xd6, 0x10, 0xc3, 0x1a, 0xa4, 0x35, 0x87, 0x8c, 0x8d, 0xec, 0xe0, 0x74, 0x29, 0x98, 0x82, - 0xe5, 0xb9, 0xde, 0x42, 0xbc, 0x02, 0x96, 0xdb, 0xd5, 0x3b, 0xaf, 0xce, 0x34, 0x7f, 0xe7, 0x15, 0x1f, 0x2a, 0x29, 0xec, 0xef, 0x63, 0xb2, 0xac, 0x45, 0x64, 0xc1, 0xf4, 0x25, 0xf4, 0xd3, 0xe9, - 0x68, 0x30, 0x36, 0x66, 0xc6, 0xfd, 0xcd, 0xc9, 0x8b, 0x39, 0xf5, 0x81, 0x6f, 0xcf, 0x68, 0x44, 0xd0, 0xfe, 0x38, 0xaf, 0xd1, 0xe1, 0x34, 0xd9, 0x1f, 0x05, 0x61, 0x4c, 0x4f, 0x7a, 0x0c, 0x34, - 0xf4, 0x86, 0xcb, 0x3f, 0xac, 0x70, 0xda, 0x7f, 0x1f, 0x36, 0x16, 0xa4, 0xb3, 0x35, 0xa7, 0x6b, 0xc7, 0xd0, 0x45, 0x01, 0xaf, 0x6c, 0xdf, 0xd8, 0x35, 0x77, 0xad, 0x6e, 0xe7, 0x8a, 0xe5, 0xa6, - 0x6f, 0xab, 0xad, 0xd4, 0x33, 0x48, 0xb0, 0xff, 0x1e, 0x05, 0x64, 0x20, 0x41, 0x33, 0x0b, 0x31, 0x57, 0x09, 0x1c, 0x0d, 0x8c, 0x5d, 0x35, 0xcd, 0x18, 0x3c, 0xd8, 0xfa, 0x79, 0xe3, 0x91, 0xc4, - 0x3b, 0xc6, 0x7d, 0x4d, 0x71, 0xbf, 0xa8, 0x87, 0x35, 0xee, 0x94, 0x22, 0xd4, 0x68, 0xd0, 0x72, 0xbc, 0x5a, 0xac, 0x89, 0x16, 0x65, 0xb4, 0x56, 0x5c, 0xc9, 0x54, 0x64, 0x95, 0x66, 0x43, 0xca, - 0xdb, 0x3e, 0xe8, 0xb3, 0xce, 0x06, 0xbc, 0x13, 0xa6, 0x33, 0x1a, 0x97, 0x4c, 0xb3, 0x02, 0x2c, 0x68, 0x7f, 0x0e, 0x66, 0x7d, 0x32, 0xc6, 0x27, 0x3b, 0xfb, 0xc8, 0x72, 0x6f, 0xa0, 0x64, 0x70, - 0xff, 0xbe, 0x4a, 0xc9, 0xc4, 0x7d, 0xfd, 0x93, 0x94, 0x98, 0xad, 0xb1, 0x50, 0xdc, 0x84, 0xeb, 0x7d, 0xfb, 0x8d, 0x69, 0xc1, 0xee, 0x73, 0x18, 0xb2, 0xf3, 0xc0, 0xd6, 0x0c, 0x55, 0x56, 0xe4, - 0x28, 0x57, 0x59, 0xe6, 0x9a, 0xab, 0xc2, 0x5f, 0x54, 0xda, 0x77, 0xb2, 0x0f, 0xf7, 0xca, 0x18, 0xe7, 0xa9, 0x8d, 0xe8, 0x4a, 0x65, 0xe1, 0x22, 0xd3, 0x04, 0x4f, 0x4f, 0xed, 0x2f, 0x11, 0x7a, - 0xc9, 0x95, 0x2a, 0x80, 0x9e, 0x3d, 0xef, 0x07, 0xbb, 0xa6, 0xcb, 0xf4, 0x9e, 0x5e, 0x41, 0x5e, 0x4d, 0xfd, 0x74, 0xb5, 0x49, 0x99, 0xc8, 0x0d, 0x4b, 0x7f, 0x49, 0x6d, 0x4e, 0x77, 0x68, 0x08, - 0x69, 0x21, 0x0b, 0x7b, 0x8d, 0xc3, 0xb1, 0x3d, 0x7d, 0x2c, 0xac, 0x41, 0x8b, 0x74, 0x7b, 0xe4, 0x49, 0xf0, 0x92, 0x82, 0xa2, 0xa3, 0x55, 0xe4, 0x91, 0xa1, 0xdb, 0x40, 0xa2, 0x60, 0x19, 0xa0, - 0x92, 0xd9, 0x15, 0x3d, 0x7b, 0xee, 0xbf, 0x02, 0x12, 0xa1, 0x81, 0x5b, 0xa5, 0xb7, 0x3b, 0x3c, 0x74, 0xa5, 0x42, 0xb2, 0xfc, 0x0b, 0x2b, 0x60, 0x17, 0xeb, 0x4a, 0x4a, 0x27, 0xaf, 0xc3, 0x78, - 0x87, 0x6b, 0xfe, 0x7a, 0xf5, 0x46, 0x6f, 0x51, 0x70, 0x74, 0x9c, 0x8a, 0xa3, 0x69, 0x25, 0x07, 0xd7, 0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0xde, 0x5b, 0xa9, 0x68, 0x13, - 0x68, 0x86, 0x6d, 0x10, 0x91, 0xd0, 0x40, 0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4, 0x9a, 0x67, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, - 0x06, 0xc1, 0x87, 0x81, 0x0f, 0x5e, 0xb7, 0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7, 0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, - 0x83, 0xac, 0xf6, 0x2d, 0x38, 0xee, 0x90, 0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x8f, 0x50, 0x3a, 0xfb, 0x11, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, + 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x4d, 0x6f, + 0xe4, 0x28, 0x10, 0xbd, 0xf7, 0xaf, 0xb0, 0xa2, 0x5c, 0x0d, 0xe9, 0xcd, 0x7e, 0x8c, 0x22, 0x96, 0xd1, 0x1c, 0x76, 0xb5, 0x91, 0x92, 0x99, 0x48, 0xc9, 0x8e, 0xf6, 0x4a, 0x70, 0xd9, 0x4d, 0x62, + 0xc0, 0x03, 0xb8, 0xd3, 0xad, 0xa8, 0xff, 0xfb, 0x0a, 0xb0, 0xdd, 0xb6, 0xdb, 0x4e, 0x32, 0x51, 0xcf, 0xdc, 0x4c, 0xd5, 0xa3, 0x28, 0x5e, 0x3d, 0x43, 0x41, 0x3e, 0x6e, 0x64, 0x99, 0xac, 0xc1, + 0x58, 0xa1, 0xd5, 0x9f, 0x27, 0x4b, 0x74, 0x76, 0xf2, 0x91, 0x2e, 0x48, 0x65, 0xf4, 0x03, 0x70, 0x97, 0x6c, 0xac, 0xb8, 0xb0, 0x7c, 0x05, 0x92, 0x5d, 0x69, 0xce, 0x5c, 0xc0, 0xac, 0x9c, 0xab, + 0x2e, 0x30, 0x96, 0x6c, 0x0d, 0x0a, 0xb1, 0x8a, 0xf1, 0x15, 0x20, 0x6d, 0x0a, 0x7c, 0xf3, 0xe5, 0x1a, 0xff, 0x8a, 0xce, 0xd0, 0x59, 0xe2, 0x11, 0x76, 0x0a, 0xb2, 0xb1, 0x59, 0x34, 0xa6, 0x01, + 0x88, 0x36, 0x36, 0x3b, 0x49, 0x36, 0xb2, 0x54, 0xf6, 0x0d, 0x61, 0x4f, 0x16, 0x49, 0x92, 0x44, 0xf4, 0xc5, 0xc6, 0x8a, 0x6e, 0xc6, 0xd3, 0xd3, 0x13, 0x7a, 0x3a, 0x0f, 0xd8, 0x5f, 0xce, 0xce, + 0x96, 0xf8, 0xbf, 0xeb, 0xab, 0xdb, 0x90, 0x72, 0x2a, 0x94, 0x75, 0x4c, 0x71, 0x38, 0xa1, 0x8b, 0x24, 0x21, 0x52, 0x67, 0x50, 0x7e, 0x8d, 0x3b, 0xa5, 0x21, 0x22, 0xc1, 0x03, 0x9b, 0x07, 0x15, + 0x46, 0xd7, 0xd5, 0x65, 0x46, 0xb5, 0x29, 0x10, 0xe3, 0x12, 0x08, 0x6e, 0x2d, 0xde, 0xcb, 0x8c, 0x13, 0x39, 0xe3, 0xee, 0x32, 0xa3, 0x79, 0xad, 0xb8, 0xe7, 0x83, 0xe0, 0x9e, 0xd1, 0x63, 0x1a, + 0x2e, 0xe9, 0xd2, 0xaf, 0x90, 0xde, 0x7e, 0xfe, 0x74, 0x73, 0xfb, 0xcf, 0x97, 0x3b, 0x82, 0xd7, 0xbd, 0x55, 0x2a, 0xa3, 0x2b, 0x30, 0x4e, 0x80, 0xa5, 0x61, 0x53, 0x84, 0x6b, 0x59, 0x89, 0x12, + 0x4c, 0x5a, 0x95, 0x75, 0x21, 0x14, 0x6a, 0xc1, 0xe7, 0xe8, 0x03, 0x5a, 0x12, 0x3c, 0xe7, 0x8e, 0x93, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, 0x59, 0xa0, 0xcb, 0x25, 0xc1, 0x33, 0xae, 0x38, + 0xaf, 0x29, 0x32, 0xba, 0xaf, 0x45, 0x99, 0x21, 0xab, 0x6b, 0xc3, 0xe1, 0x2f, 0xc5, 0x75, 0x26, 0x54, 0x41, 0xff, 0xbd, 0xfb, 0x3b, 0xfd, 0x40, 0xf0, 0x8b, 0x98, 0x61, 0x18, 0x03, 0x95, 0x36, + 0x4e, 0xa8, 0x02, 0xe9, 0xda, 0x55, 0xb5, 0x9b, 0x0b, 0x35, 0x8b, 0x8b, 0xe1, 0xbe, 0xd5, 0xcc, 0x3c, 0xd6, 0x16, 0x55, 0x25, 0x73, 0xb9, 0x36, 0x12, 0xb5, 0xfc, 0xa6, 0x22, 0xa3, 0x8d, 0x33, + 0xbd, 0xd7, 0x92, 0xe0, 0x17, 0x91, 0x33, 0xc1, 0x42, 0x35, 0xbd, 0x5f, 0x68, 0x34, 0x76, 0x4e, 0x44, 0xec, 0xe0, 0x33, 0xe1, 0xf6, 0x75, 0x3a, 0xff, 0x1d, 0x9d, 0x4f, 0x04, 0x18, 0x56, 0xca, + 0x3e, 0x8a, 0xea, 0xf2, 0xce, 0x52, 0x67, 0x6a, 0x20, 0xb8, 0x1d, 0x35, 0xbe, 0xda, 0x40, 0x2e, 0x0c, 0x1c, 0x4a, 0xc0, 0xeb, 0xe8, 0xfa, 0x0f, 0x82, 0xe7, 0x10, 0x5e, 0x51, 0x78, 0x28, 0x29, + 0x92, 0x41, 0x05, 0x2a, 0x03, 0xc5, 0xb7, 0xd7, 0x4c, 0xb1, 0x02, 0x24, 0x28, 0xd7, 0x2c, 0xd4, 0xb9, 0x3a, 0xf9, 0x0d, 0xf0, 0xad, 0xa9, 0xf7, 0x33, 0x9c, 0x3e, 0xcf, 0x12, 0xb3, 0x1b, 0xfc, + 0x20, 0xcd, 0xbc, 0xde, 0x1f, 0x31, 0x31, 0xb5, 0x57, 0xa5, 0xdd, 0xf8, 0xef, 0x69, 0x02, 0xb4, 0x3b, 0x9b, 0x98, 0xdd, 0xb8, 0x76, 0x83, 0x1f, 0xaa, 0x99, 0xe6, 0xb6, 0x15, 0xd0, 0xca, 0x4b, + 0x23, 0x7c, 0xed, 0x1d, 0x96, 0xeb, 0x0a, 0xa8, 0x90, 0x5e, 0x78, 0x04, 0xc7, 0x51, 0xbb, 0x73, 0x3c, 0xde, 0x7a, 0xcf, 0xd2, 0xb2, 0x89, 0xe7, 0xe8, 0x9c, 0x20, 0x73, 0x82, 0xca, 0x8e, 0xc8, + 0xbd, 0xe8, 0x0e, 0x68, 0xeb, 0x93, 0xd6, 0xaa, 0x3c, 0xaf, 0xd5, 0xb7, 0x6d, 0xfa, 0xa8, 0x98, 0x13, 0x6b, 0x48, 0x61, 0x0d, 0xca, 0xd9, 0x43, 0xc6, 0x26, 0x76, 0x70, 0xbc, 0x14, 0xac, 0x64, + 0x65, 0x69, 0xb6, 0x90, 0xae, 0x80, 0x95, 0x6e, 0xf5, 0x93, 0x57, 0x67, 0x86, 0xff, 0xe4, 0x15, 0x1f, 0x6a, 0x25, 0xdc, 0x6f, 0x53, 0xb2, 0x6c, 0x44, 0xe4, 0xc0, 0x0e, 0x25, 0xf4, 0xdd, 0xe9, + 0x18, 0xb0, 0x2e, 0x65, 0xd6, 0xff, 0xcd, 0xd9, 0x8b, 0x39, 0x0d, 0x81, 0xef, 0xcf, 0x68, 0x42, 0xd0, 0xe1, 0x38, 0x6f, 0xd0, 0xf1, 0x34, 0xd9, 0x1f, 0x05, 0x71, 0x4c, 0x8f, 0x7a, 0x0c, 0xb4, + 0xf4, 0xc6, 0xcb, 0x3f, 0xae, 0x70, 0xdc, 0x7f, 0x1f, 0x36, 0x0e, 0x94, 0xb7, 0xb5, 0xa7, 0x6b, 0xcf, 0xd0, 0x47, 0x01, 0xaf, 0xdd, 0xd0, 0xd8, 0x37, 0xf7, 0xad, 0x7e, 0xe7, 0x9a, 0x95, 0x76, + 0x68, 0x6b, 0xac, 0x34, 0x30, 0x48, 0x70, 0xf8, 0x9e, 0x04, 0x14, 0xa0, 0xc0, 0x30, 0x07, 0x29, 0xd7, 0x19, 0xbc, 0x19, 0x98, 0xfa, 0x6a, 0xda, 0x29, 0x78, 0xb4, 0x0d, 0xf3, 0xc6, 0x13, 0x89, + 0xf7, 0x8c, 0xfb, 0x9a, 0xe2, 0x61, 0x51, 0x0f, 0x6b, 0xdc, 0x2b, 0x45, 0xac, 0xd1, 0xa8, 0xe5, 0x78, 0xb5, 0x58, 0x33, 0x2d, 0xca, 0x64, 0xad, 0xb8, 0x56, 0xb9, 0x28, 0x6a, 0xc3, 0xc6, 0x94, + 0x77, 0x7d, 0xd0, 0x27, 0x53, 0x8c, 0x78, 0x27, 0xcc, 0x14, 0x34, 0xad, 0x98, 0x61, 0x12, 0x1c, 0x98, 0x70, 0x0e, 0x16, 0x43, 0x32, 0xa6, 0x27, 0x7b, 0xfb, 0xc4, 0x72, 0xef, 0xa0, 0x64, 0x74, + 0xff, 0xbe, 0x4a, 0xc9, 0xcc, 0x7d, 0xfd, 0x9d, 0x94, 0xd8, 0xad, 0x75, 0x20, 0x6f, 0xe2, 0xf5, 0xbe, 0xfd, 0xca, 0x8c, 0x60, 0xf7, 0x25, 0x8c, 0xd9, 0x79, 0x60, 0x6b, 0x86, 0x6a, 0x27, 0x4a, + 0x54, 0xea, 0xa2, 0xf0, 0xcd, 0x95, 0x0c, 0x17, 0x95, 0x09, 0x9d, 0xec, 0xc3, 0xbd, 0xb6, 0xd6, 0x7b, 0x1a, 0x23, 0xba, 0xd2, 0x45, 0xbc, 0xc8, 0x0c, 0xc1, 0xf3, 0x53, 0x87, 0x4b, 0xc4, 0x5e, + 0x72, 0xa5, 0x25, 0xd0, 0xd3, 0xe7, 0xfd, 0x60, 0xd7, 0x76, 0x99, 0xc1, 0x33, 0x28, 0xc8, 0xab, 0xa9, 0x1f, 0xaf, 0x36, 0x39, 0x13, 0xa5, 0x65, 0xf9, 0x0f, 0xa9, 0xcd, 0xf1, 0x0e, 0x0d, 0xa1, + 0x1c, 0x14, 0x71, 0xaf, 0x69, 0x3c, 0xb6, 0xe7, 0x8f, 0x85, 0x35, 0x18, 0x91, 0x6f, 0xdf, 0x78, 0x12, 0xbc, 0xa4, 0xa0, 0xe4, 0xcd, 0x2a, 0x0a, 0xc8, 0xd8, 0x6d, 0x20, 0x21, 0x59, 0x01, 0xa8, + 0x62, 0x6e, 0x45, 0x4f, 0x9f, 0x87, 0xaf, 0x80, 0x4c, 0x18, 0xe0, 0x4e, 0x9b, 0xed, 0x0e, 0x8f, 0x5d, 0xb9, 0x50, 0xac, 0xfc, 0xcc, 0x24, 0xec, 0x52, 0x53, 0x2b, 0xe5, 0xe5, 0x75, 0x18, 0xef, + 0x70, 0xcd, 0x1f, 0xaf, 0xde, 0xe4, 0x3d, 0x0a, 0x4e, 0xde, 0xa6, 0xe2, 0x64, 0x5e, 0xc9, 0xd1, 0xf5, 0xae, 0x53, 0xba, 0x1d, 0x35, 0x1d, 0x68, 0x77, 0x63, 0xfb, 0xf7, 0x56, 0x2e, 0xba, 0x04, + 0xda, 0x61, 0x17, 0x44, 0x64, 0x34, 0x52, 0x4e, 0xb0, 0xe8, 0xb5, 0x15, 0xdc, 0x89, 0xf5, 0x28, 0xb5, 0xf6, 0x19, 0xba, 0x1d, 0x64, 0xab, 0x98, 0x84, 0x2e, 0x42, 0x18, 0xf4, 0x92, 0x1e, 0xcf, + 0x20, 0xf8, 0x30, 0xf0, 0xc1, 0xeb, 0x36, 0x18, 0xdb, 0xc7, 0x4e, 0xce, 0x4a, 0x3b, 0x7e, 0xfb, 0x04, 0x40, 0x77, 0xdb, 0x33, 0xfe, 0xe8, 0xa5, 0x12, 0x5a, 0xf8, 0x36, 0x8f, 0x49, 0xe7, 0x62, + 0x94, 0xd5, 0xbe, 0x05, 0xc7, 0x3d, 0x52, 0xba, 0x81, 0xa5, 0x8b, 0xee, 0x11, 0x4a, 0x17, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x6b, 0x9c, 0x2c, 0x70, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, @@ -7466,39 +7466,39 @@ var TemplatesZip = []byte{ 0xd3, 0xf1, 0xfe, 0x7e, 0x84, 0x63, 0x06, 0xb6, 0x4f, 0xe6, 0xb5, 0x86, 0xca, 0x84, 0x99, 0x33, 0xdf, 0xca, 0xf4, 0x9a, 0x54, 0x93, 0x64, 0x7c, 0xdd, 0x1f, 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0xdf, 0x6f, 0xe3, 0x28, 0x10, 0x7e, 0xcf, - 0x5f, 0x61, 0x45, 0x7d, 0x35, 0x24, 0xdb, 0xfb, 0xb1, 0xaa, 0x38, 0x56, 0xfb, 0x70, 0xa7, 0xab, 0xd4, 0xee, 0x56, 0x6a, 0x6f, 0x75, 0xaf, 0x14, 0x8f, 0x1d, 0x5a, 0x1b, 0xbc, 0x80, 0xd3, 0x44, - 0x55, 0xfe, 0xf7, 0x15, 0x60, 0x3b, 0xb6, 0x63, 0xb7, 0xd9, 0x2a, 0xdb, 0x37, 0x33, 0xf3, 0x31, 0x0c, 0xdf, 0x7c, 0x86, 0x81, 0x7c, 0xda, 0x14, 0x79, 0xb4, 0x06, 0x6d, 0x84, 0x92, 0x7f, 0xcd, - 0x97, 0x68, 0x31, 0xff, 0x44, 0x67, 0xa4, 0xd4, 0xea, 0x01, 0xb8, 0x8d, 0x36, 0x46, 0x5c, 0x18, 0xbe, 0x82, 0x82, 0x5d, 0x29, 0xce, 0xac, 0xc7, 0xac, 0xac, 0x2d, 0x2f, 0x30, 0x2e, 0xd8, 0x1a, - 0x24, 0x62, 0x25, 0xe3, 0x2b, 0x40, 0x4a, 0x67, 0xf8, 0xe6, 0xeb, 0x35, 0xfe, 0x0d, 0x2d, 0xd0, 0x22, 0x72, 0x08, 0x33, 0x06, 0xd9, 0x98, 0x24, 0x18, 0x63, 0x0f, 0x44, 0x1b, 0x93, 0xcc, 0xa3, - 0x4d, 0x91, 0x4b, 0x73, 0x44, 0xd8, 0xf9, 0x2c, 0x8a, 0xa2, 0x80, 0xbe, 0xd8, 0x18, 0xd1, 0xce, 0x78, 0x7a, 0x7a, 0x42, 0x4f, 0xe7, 0x1e, 0xfb, 0x61, 0xb1, 0x58, 0xe2, 0xff, 0xaf, 0xaf, 0x6e, - 0x7d, 0xca, 0xb1, 0x90, 0xc6, 0x32, 0xc9, 0x61, 0x4e, 0x67, 0x51, 0x44, 0x0a, 0x95, 0x40, 0xfe, 0x2d, 0xec, 0x94, 0xfa, 0x88, 0x04, 0xf7, 0x6c, 0x0e, 0x94, 0x69, 0x55, 0x95, 0x97, 0x09, 0x55, - 0x3a, 0x43, 0x8c, 0x17, 0x40, 0x70, 0x63, 0x71, 0x5e, 0xa6, 0xad, 0x48, 0x19, 0xb7, 0x97, 0x09, 0x4d, 0x2b, 0xc9, 0x1d, 0x1f, 0x04, 0x77, 0x8c, 0x0e, 0x53, 0x73, 0x49, 0x97, 0x6e, 0x85, 0xf8, - 0xf6, 0xcb, 0xe7, 0x9b, 0xdb, 0x7f, 0xbf, 0xde, 0x11, 0xbc, 0xee, 0xac, 0x52, 0x6a, 0x55, 0x82, 0xb6, 0x02, 0x0c, 0xf5, 0x9b, 0x22, 0x5c, 0x15, 0xa5, 0xc8, 0x41, 0xc7, 0x65, 0x5e, 0x65, 0x42, - 0xa2, 0x06, 0x7c, 0x8e, 0x3e, 0xa2, 0x25, 0xc1, 0x53, 0xee, 0x30, 0x39, 0x50, 0xd6, 0x60, 0x90, 0x86, 0x1c, 0x98, 0x01, 0xba, 0x5c, 0x12, 0x3c, 0xe1, 0x0a, 0xf3, 0xea, 0x22, 0xa3, 0xfb, 0x4a, - 0xe4, 0x09, 0x32, 0xaa, 0xd2, 0x1c, 0xfe, 0x96, 0x5c, 0x25, 0x42, 0x66, 0xf4, 0xbf, 0xbb, 0x7f, 0xe2, 0x8f, 0x04, 0xbf, 0x88, 0xe9, 0x87, 0xd1, 0x50, 0x2a, 0x6d, 0x85, 0xcc, 0x90, 0xaa, 0x6c, - 0x59, 0xd9, 0xa9, 0x50, 0x93, 0xb8, 0x10, 0xee, 0x7b, 0xc5, 0xf4, 0x63, 0x65, 0x50, 0x99, 0x33, 0x9b, 0x2a, 0x5d, 0xa0, 0x86, 0xdf, 0x58, 0x24, 0xb4, 0x76, 0xc6, 0xf7, 0xaa, 0x20, 0xf8, 0x45, - 0xe4, 0x44, 0x30, 0x5f, 0x4d, 0xe7, 0x17, 0x0a, 0x0d, 0x9d, 0x23, 0x11, 0x5b, 0xf8, 0x44, 0xb8, 0x7d, 0x9d, 0xce, 0xff, 0x40, 0x1f, 0x46, 0x02, 0xf4, 0x2b, 0x65, 0x1e, 0x45, 0x79, 0x79, 0x67, - 0xa8, 0xd5, 0x15, 0x10, 0xdc, 0x8c, 0x6a, 0x5f, 0xa5, 0x21, 0x15, 0x1a, 0x0e, 0x25, 0xe0, 0x74, 0x74, 0xfd, 0x27, 0xc1, 0x53, 0x08, 0xa7, 0x28, 0xdc, 0x97, 0x14, 0x49, 0xa0, 0x04, 0x99, 0x80, - 0xe4, 0xdb, 0x6b, 0x26, 0x59, 0x06, 0x05, 0x48, 0x5b, 0x2f, 0xd4, 0xba, 0x5a, 0xf9, 0xf5, 0xf0, 0x8d, 0xa9, 0xf3, 0x33, 0x9c, 0x3d, 0x4f, 0x12, 0xb3, 0xeb, 0xfd, 0x20, 0xf5, 0xbc, 0xce, 0x1f, - 0x31, 0x32, 0xb5, 0x53, 0xa5, 0xdd, 0xf0, 0xef, 0xa9, 0x03, 0x34, 0x3b, 0x1b, 0x99, 0x5d, 0xbb, 0x76, 0xbd, 0x1f, 0xaa, 0x9e, 0x66, 0xb7, 0x25, 0xd0, 0xd2, 0x49, 0xc3, 0x7f, 0xed, 0x1d, 0x86, - 0xab, 0x12, 0xa8, 0x28, 0x9c, 0xf0, 0x08, 0x0e, 0xa3, 0x66, 0xe7, 0x78, 0xb8, 0xf5, 0x8e, 0xa5, 0x61, 0x13, 0x4f, 0xd1, 0x39, 0x42, 0xe6, 0x08, 0x95, 0x2d, 0x91, 0x7b, 0xd1, 0x1d, 0xd0, 0xd6, - 0x25, 0xad, 0x51, 0x79, 0x5a, 0xc9, 0xef, 0xdb, 0xf8, 0x51, 0x32, 0x2b, 0xd6, 0x10, 0xc3, 0x1a, 0xa4, 0x35, 0x87, 0x8c, 0x8d, 0xec, 0xe0, 0x74, 0x29, 0x98, 0x82, 0xe5, 0xb9, 0xde, 0x42, 0xbc, - 0x02, 0x96, 0xdb, 0xd5, 0x3b, 0xaf, 0xce, 0x34, 0x7f, 0xe7, 0x15, 0x1f, 0x2a, 0x29, 0xec, 0xef, 0x63, 0xb2, 0xac, 0x45, 0x64, 0xc1, 0xf4, 0x25, 0xf4, 0xd3, 0xe9, 0x68, 0x30, 0x36, 0x66, 0xc6, - 0xfd, 0xcd, 0xc9, 0x8b, 0x39, 0xf5, 0x81, 0x6f, 0xcf, 0x68, 0x44, 0xd0, 0xfe, 0x38, 0xaf, 0xd1, 0xe1, 0x34, 0xd9, 0x1f, 0x05, 0x61, 0x4c, 0x4f, 0x7a, 0x0c, 0x34, 0xf4, 0x86, 0xcb, 0x3f, 0xac, - 0x70, 0xda, 0x7f, 0x1f, 0x36, 0x16, 0xa4, 0xb3, 0x35, 0xa7, 0x6b, 0xc7, 0xd0, 0x45, 0x01, 0xaf, 0x6c, 0xdf, 0xd8, 0x35, 0x77, 0xad, 0x6e, 0xe7, 0x8a, 0xe5, 0xa6, 0x6f, 0xab, 0xad, 0xd4, 0x33, - 0x48, 0xb0, 0xff, 0x1e, 0x05, 0x64, 0x20, 0x41, 0x33, 0x0b, 0x31, 0x57, 0x09, 0x1c, 0x0d, 0x8c, 0x5d, 0x35, 0xcd, 0x18, 0x3c, 0xd8, 0xfa, 0x79, 0xe3, 0x91, 0xc4, 0x3b, 0xc6, 0x7d, 0x4d, 0x71, - 0xbf, 0xa8, 0x87, 0x35, 0xee, 0x94, 0x22, 0xd4, 0x68, 0xd0, 0x72, 0xbc, 0x5a, 0xac, 0x89, 0x16, 0x65, 0xb4, 0x56, 0x5c, 0xc9, 0x54, 0x64, 0x95, 0x66, 0x43, 0xca, 0xdb, 0x3e, 0xe8, 0xb3, 0xce, - 0x06, 0xbc, 0x13, 0xa6, 0x33, 0x1a, 0x97, 0x4c, 0xb3, 0x02, 0x2c, 0x68, 0x7f, 0x0e, 0x66, 0x7d, 0x32, 0xc6, 0x27, 0x3b, 0xfb, 0xc8, 0x72, 0x6f, 0xa0, 0x64, 0x70, 0xff, 0xbe, 0x4a, 0xc9, 0xc4, - 0x7d, 0xfd, 0x93, 0x94, 0x98, 0xad, 0xb1, 0x50, 0xdc, 0x84, 0xeb, 0x7d, 0xfb, 0x8d, 0x69, 0xc1, 0xee, 0x73, 0x18, 0xb2, 0xf3, 0xc0, 0xd6, 0x0c, 0x55, 0x56, 0xe4, 0x28, 0x57, 0x59, 0xe6, 0x9a, - 0xab, 0xc2, 0x5f, 0x54, 0xda, 0x77, 0xb2, 0x0f, 0xf7, 0xca, 0x18, 0xe7, 0xa9, 0x8d, 0xe8, 0x4a, 0x65, 0xe1, 0x22, 0xd3, 0x04, 0x4f, 0x4f, 0xed, 0x2f, 0x11, 0x7a, 0xc9, 0x95, 0x2a, 0x80, 0x9e, - 0x3d, 0xef, 0x07, 0xbb, 0xa6, 0xcb, 0xf4, 0x9e, 0x5e, 0x41, 0x5e, 0x4d, 0xfd, 0x74, 0xb5, 0x49, 0x99, 0xc8, 0x0d, 0x4b, 0x7f, 0x49, 0x6d, 0x4e, 0x77, 0x68, 0x08, 0x69, 0x21, 0x0b, 0x7b, 0x8d, - 0xc3, 0xb1, 0x3d, 0x7d, 0x2c, 0xac, 0x41, 0x8b, 0x74, 0x7b, 0xe4, 0x49, 0xf0, 0x92, 0x82, 0xa2, 0xa3, 0x55, 0xe4, 0x91, 0xa1, 0xdb, 0x40, 0xa2, 0x60, 0x19, 0xa0, 0x92, 0xd9, 0x15, 0x3d, 0x7b, - 0xee, 0xbf, 0x02, 0x12, 0xa1, 0x81, 0x5b, 0xa5, 0xb7, 0x3b, 0x3c, 0x74, 0xa5, 0x42, 0xb2, 0xfc, 0x0b, 0x2b, 0x60, 0x17, 0xeb, 0x4a, 0x4a, 0x27, 0xaf, 0xc3, 0x78, 0x87, 0x6b, 0xfe, 0x7a, 0xf5, - 0x46, 0x6f, 0x51, 0x70, 0x74, 0x9c, 0x8a, 0xa3, 0x69, 0x25, 0x07, 0xd7, 0x9b, 0x4e, 0xe9, 0x66, 0x54, 0x77, 0xa0, 0xed, 0x8d, 0xed, 0xde, 0x5b, 0xa9, 0x68, 0x13, 0x68, 0x86, 0x6d, 0x10, 0x91, - 0xd0, 0x40, 0x39, 0xc1, 0xa2, 0xd3, 0x56, 0x70, 0x2b, 0xd6, 0x83, 0xd4, 0x9a, 0x67, 0xe8, 0xb6, 0x97, 0xad, 0x64, 0x05, 0xb4, 0x11, 0xfc, 0xa0, 0x93, 0xf4, 0x70, 0x06, 0xc1, 0x87, 0x81, 0x0f, - 0x5e, 0xb7, 0xde, 0xd8, 0x3c, 0x76, 0x52, 0x96, 0x9b, 0xe1, 0xdb, 0xc7, 0x03, 0xda, 0xdb, 0x9e, 0xf1, 0x47, 0x27, 0x15, 0xdf, 0xc2, 0x37, 0x79, 0x8c, 0x3a, 0x67, 0x83, 0xac, 0xf6, 0x2d, 0x38, - 0xee, 0x90, 0xd2, 0x0e, 0x0c, 0x9d, 0xb5, 0x8f, 0x50, 0x3a, 0xfb, 0x11, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x4d, 0x6f, 0xe4, 0x28, 0x10, 0xbd, 0xf7, + 0xaf, 0xb0, 0xa2, 0x5c, 0x0d, 0xe9, 0xcd, 0x7e, 0x8c, 0x22, 0x96, 0xd1, 0x1c, 0x76, 0xb5, 0x91, 0x92, 0x99, 0x48, 0xc9, 0x8e, 0xf6, 0x4a, 0x70, 0xd9, 0x4d, 0x62, 0xc0, 0x03, 0xb8, 0xd3, 0xad, + 0xa8, 0xff, 0xfb, 0x0a, 0xb0, 0xdd, 0xb6, 0xdb, 0x4e, 0x32, 0x51, 0xcf, 0xdc, 0x4c, 0xd5, 0xa3, 0x28, 0x5e, 0x3d, 0x43, 0x41, 0x3e, 0x6e, 0x64, 0x99, 0xac, 0xc1, 0x58, 0xa1, 0xd5, 0x9f, 0x27, + 0x4b, 0x74, 0x76, 0xf2, 0x91, 0x2e, 0x48, 0x65, 0xf4, 0x03, 0x70, 0x97, 0x6c, 0xac, 0xb8, 0xb0, 0x7c, 0x05, 0x92, 0x5d, 0x69, 0xce, 0x5c, 0xc0, 0xac, 0x9c, 0xab, 0x2e, 0x30, 0x96, 0x6c, 0x0d, + 0x0a, 0xb1, 0x8a, 0xf1, 0x15, 0x20, 0x6d, 0x0a, 0x7c, 0xf3, 0xe5, 0x1a, 0xff, 0x8a, 0xce, 0xd0, 0x59, 0xe2, 0x11, 0x76, 0x0a, 0xb2, 0xb1, 0x59, 0x34, 0xa6, 0x01, 0x88, 0x36, 0x36, 0x3b, 0x49, + 0x36, 0xb2, 0x54, 0xf6, 0x0d, 0x61, 0x4f, 0x16, 0x49, 0x92, 0x44, 0xf4, 0xc5, 0xc6, 0x8a, 0x6e, 0xc6, 0xd3, 0xd3, 0x13, 0x7a, 0x3a, 0x0f, 0xd8, 0x5f, 0xce, 0xce, 0x96, 0xf8, 0xbf, 0xeb, 0xab, + 0xdb, 0x90, 0x72, 0x2a, 0x94, 0x75, 0x4c, 0x71, 0x38, 0xa1, 0x8b, 0x24, 0x21, 0x52, 0x67, 0x50, 0x7e, 0x8d, 0x3b, 0xa5, 0x21, 0x22, 0xc1, 0x03, 0x9b, 0x07, 0x15, 0x46, 0xd7, 0xd5, 0x65, 0x46, + 0xb5, 0x29, 0x10, 0xe3, 0x12, 0x08, 0x6e, 0x2d, 0xde, 0xcb, 0x8c, 0x13, 0x39, 0xe3, 0xee, 0x32, 0xa3, 0x79, 0xad, 0xb8, 0xe7, 0x83, 0xe0, 0x9e, 0xd1, 0x63, 0x1a, 0x2e, 0xe9, 0xd2, 0xaf, 0x90, + 0xde, 0x7e, 0xfe, 0x74, 0x73, 0xfb, 0xcf, 0x97, 0x3b, 0x82, 0xd7, 0xbd, 0x55, 0x2a, 0xa3, 0x2b, 0x30, 0x4e, 0x80, 0xa5, 0x61, 0x53, 0x84, 0x6b, 0x59, 0x89, 0x12, 0x4c, 0x5a, 0x95, 0x75, 0x21, + 0x14, 0x6a, 0xc1, 0xe7, 0xe8, 0x03, 0x5a, 0x12, 0x3c, 0xe7, 0x8e, 0x93, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, 0x59, 0xa0, 0xcb, 0x25, 0xc1, 0x33, 0xae, 0x38, 0xaf, 0x29, 0x32, 0xba, 0xaf, + 0x45, 0x99, 0x21, 0xab, 0x6b, 0xc3, 0xe1, 0x2f, 0xc5, 0x75, 0x26, 0x54, 0x41, 0xff, 0xbd, 0xfb, 0x3b, 0xfd, 0x40, 0xf0, 0x8b, 0x98, 0x61, 0x18, 0x03, 0x95, 0x36, 0x4e, 0xa8, 0x02, 0xe9, 0xda, + 0x55, 0xb5, 0x9b, 0x0b, 0x35, 0x8b, 0x8b, 0xe1, 0xbe, 0xd5, 0xcc, 0x3c, 0xd6, 0x16, 0x55, 0x25, 0x73, 0xb9, 0x36, 0x12, 0xb5, 0xfc, 0xa6, 0x22, 0xa3, 0x8d, 0x33, 0xbd, 0xd7, 0x92, 0xe0, 0x17, + 0x91, 0x33, 0xc1, 0x42, 0x35, 0xbd, 0x5f, 0x68, 0x34, 0x76, 0x4e, 0x44, 0xec, 0xe0, 0x33, 0xe1, 0xf6, 0x75, 0x3a, 0xff, 0x1d, 0x9d, 0x4f, 0x04, 0x18, 0x56, 0xca, 0x3e, 0x8a, 0xea, 0xf2, 0xce, + 0x52, 0x67, 0x6a, 0x20, 0xb8, 0x1d, 0x35, 0xbe, 0xda, 0x40, 0x2e, 0x0c, 0x1c, 0x4a, 0xc0, 0xeb, 0xe8, 0xfa, 0x0f, 0x82, 0xe7, 0x10, 0x5e, 0x51, 0x78, 0x28, 0x29, 0x92, 0x41, 0x05, 0x2a, 0x03, + 0xc5, 0xb7, 0xd7, 0x4c, 0xb1, 0x02, 0x24, 0x28, 0xd7, 0x2c, 0xd4, 0xb9, 0x3a, 0xf9, 0x0d, 0xf0, 0xad, 0xa9, 0xf7, 0x33, 0x9c, 0x3e, 0xcf, 0x12, 0xb3, 0x1b, 0xfc, 0x20, 0xcd, 0xbc, 0xde, 0x1f, + 0x31, 0x31, 0xb5, 0x57, 0xa5, 0xdd, 0xf8, 0xef, 0x69, 0x02, 0xb4, 0x3b, 0x9b, 0x98, 0xdd, 0xb8, 0x76, 0x83, 0x1f, 0xaa, 0x99, 0xe6, 0xb6, 0x15, 0xd0, 0xca, 0x4b, 0x23, 0x7c, 0xed, 0x1d, 0x96, + 0xeb, 0x0a, 0xa8, 0x90, 0x5e, 0x78, 0x04, 0xc7, 0x51, 0xbb, 0x73, 0x3c, 0xde, 0x7a, 0xcf, 0xd2, 0xb2, 0x89, 0xe7, 0xe8, 0x9c, 0x20, 0x73, 0x82, 0xca, 0x8e, 0xc8, 0xbd, 0xe8, 0x0e, 0x68, 0xeb, + 0x93, 0xd6, 0xaa, 0x3c, 0xaf, 0xd5, 0xb7, 0x6d, 0xfa, 0xa8, 0x98, 0x13, 0x6b, 0x48, 0x61, 0x0d, 0xca, 0xd9, 0x43, 0xc6, 0x26, 0x76, 0x70, 0xbc, 0x14, 0xac, 0x64, 0x65, 0x69, 0xb6, 0x90, 0xae, + 0x80, 0x95, 0x6e, 0xf5, 0x93, 0x57, 0x67, 0x86, 0xff, 0xe4, 0x15, 0x1f, 0x6a, 0x25, 0xdc, 0x6f, 0x53, 0xb2, 0x6c, 0x44, 0xe4, 0xc0, 0x0e, 0x25, 0xf4, 0xdd, 0xe9, 0x18, 0xb0, 0x2e, 0x65, 0xd6, + 0xff, 0xcd, 0xd9, 0x8b, 0x39, 0x0d, 0x81, 0xef, 0xcf, 0x68, 0x42, 0xd0, 0xe1, 0x38, 0x6f, 0xd0, 0xf1, 0x34, 0xd9, 0x1f, 0x05, 0x71, 0x4c, 0x8f, 0x7a, 0x0c, 0xb4, 0xf4, 0xc6, 0xcb, 0x3f, 0xae, + 0x70, 0xdc, 0x7f, 0x1f, 0x36, 0x0e, 0x94, 0xb7, 0xb5, 0xa7, 0x6b, 0xcf, 0xd0, 0x47, 0x01, 0xaf, 0xdd, 0xd0, 0xd8, 0x37, 0xf7, 0xad, 0x7e, 0xe7, 0x9a, 0x95, 0x76, 0x68, 0x6b, 0xac, 0x34, 0x30, + 0x48, 0x70, 0xf8, 0x9e, 0x04, 0x14, 0xa0, 0xc0, 0x30, 0x07, 0x29, 0xd7, 0x19, 0xbc, 0x19, 0x98, 0xfa, 0x6a, 0xda, 0x29, 0x78, 0xb4, 0x0d, 0xf3, 0xc6, 0x13, 0x89, 0xf7, 0x8c, 0xfb, 0x9a, 0xe2, + 0x61, 0x51, 0x0f, 0x6b, 0xdc, 0x2b, 0x45, 0xac, 0xd1, 0xa8, 0xe5, 0x78, 0xb5, 0x58, 0x33, 0x2d, 0xca, 0x64, 0xad, 0xb8, 0x56, 0xb9, 0x28, 0x6a, 0xc3, 0xc6, 0x94, 0x77, 0x7d, 0xd0, 0x27, 0x53, + 0x8c, 0x78, 0x27, 0xcc, 0x14, 0x34, 0xad, 0x98, 0x61, 0x12, 0x1c, 0x98, 0x70, 0x0e, 0x16, 0x43, 0x32, 0xa6, 0x27, 0x7b, 0xfb, 0xc4, 0x72, 0xef, 0xa0, 0x64, 0x74, 0xff, 0xbe, 0x4a, 0xc9, 0xcc, + 0x7d, 0xfd, 0x9d, 0x94, 0xd8, 0xad, 0x75, 0x20, 0x6f, 0xe2, 0xf5, 0xbe, 0xfd, 0xca, 0x8c, 0x60, 0xf7, 0x25, 0x8c, 0xd9, 0x79, 0x60, 0x6b, 0x86, 0x6a, 0x27, 0x4a, 0x54, 0xea, 0xa2, 0xf0, 0xcd, + 0x95, 0x0c, 0x17, 0x95, 0x09, 0x9d, 0xec, 0xc3, 0xbd, 0xb6, 0xd6, 0x7b, 0x1a, 0x23, 0xba, 0xd2, 0x45, 0xbc, 0xc8, 0x0c, 0xc1, 0xf3, 0x53, 0x87, 0x4b, 0xc4, 0x5e, 0x72, 0xa5, 0x25, 0xd0, 0xd3, + 0xe7, 0xfd, 0x60, 0xd7, 0x76, 0x99, 0xc1, 0x33, 0x28, 0xc8, 0xab, 0xa9, 0x1f, 0xaf, 0x36, 0x39, 0x13, 0xa5, 0x65, 0xf9, 0x0f, 0xa9, 0xcd, 0xf1, 0x0e, 0x0d, 0xa1, 0x1c, 0x14, 0x71, 0xaf, 0x69, + 0x3c, 0xb6, 0xe7, 0x8f, 0x85, 0x35, 0x18, 0x91, 0x6f, 0xdf, 0x78, 0x12, 0xbc, 0xa4, 0xa0, 0xe4, 0xcd, 0x2a, 0x0a, 0xc8, 0xd8, 0x6d, 0x20, 0x21, 0x59, 0x01, 0xa8, 0x62, 0x6e, 0x45, 0x4f, 0x9f, + 0x87, 0xaf, 0x80, 0x4c, 0x18, 0xe0, 0x4e, 0x9b, 0xed, 0x0e, 0x8f, 0x5d, 0xb9, 0x50, 0xac, 0xfc, 0xcc, 0x24, 0xec, 0x52, 0x53, 0x2b, 0xe5, 0xe5, 0x75, 0x18, 0xef, 0x70, 0xcd, 0x1f, 0xaf, 0xde, + 0xe4, 0x3d, 0x0a, 0x4e, 0xde, 0xa6, 0xe2, 0x64, 0x5e, 0xc9, 0xd1, 0xf5, 0xae, 0x53, 0xba, 0x1d, 0x35, 0x1d, 0x68, 0x77, 0x63, 0xfb, 0xf7, 0x56, 0x2e, 0xba, 0x04, 0xda, 0x61, 0x17, 0x44, 0x64, + 0x34, 0x52, 0x4e, 0xb0, 0xe8, 0xb5, 0x15, 0xdc, 0x89, 0xf5, 0x28, 0xb5, 0xf6, 0x19, 0xba, 0x1d, 0x64, 0xab, 0x98, 0x84, 0x2e, 0x42, 0x18, 0xf4, 0x92, 0x1e, 0xcf, 0x20, 0xf8, 0x30, 0xf0, 0xc1, + 0xeb, 0x36, 0x18, 0xdb, 0xc7, 0x4e, 0xce, 0x4a, 0x3b, 0x7e, 0xfb, 0x04, 0x40, 0x77, 0xdb, 0x33, 0xfe, 0xe8, 0xa5, 0x12, 0x5a, 0xf8, 0x36, 0x8f, 0x49, 0xe7, 0x62, 0x94, 0xd5, 0xbe, 0x05, 0xc7, + 0x3d, 0x52, 0xba, 0x81, 0xa5, 0x8b, 0xee, 0x11, 0x4a, 0x17, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x6b, 0x9c, 0x2c, 0x70, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x6d, 0x61, @@ -13610,7 +13610,7 @@ var TemplatesZip = []byte{ 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x23, 0x64, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x63, 0x6f, 0x03, 0x00, + 0x00, 0x00, 0x6b, 0x9c, 0x2c, 0x70, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x63, 0x6f, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xa5, 0x73, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, @@ -13672,7 +13672,7 @@ var TemplatesZip = []byte{ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x0c, 0x8b, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xac, 0x99, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, - 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x7f, 0xcf, 0xda, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, + 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x9c, 0x2c, 0x70, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0xe5, 0xa4, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x20, 0xa9, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, diff --git a/templates/quarkus/cloudevents/pom.xml b/templates/quarkus/cloudevents/pom.xml index 93e8f1ad8d..00ae3a8478 100644 --- a/templates/quarkus/cloudevents/pom.xml +++ b/templates/quarkus/cloudevents/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.36.2 + 3.36.3 true 3.0.0-M7 diff --git a/templates/quarkus/http/pom.xml b/templates/quarkus/http/pom.xml index 93e8f1ad8d..00ae3a8478 100644 --- a/templates/quarkus/http/pom.xml +++ b/templates/quarkus/http/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.36.2 + 3.36.3 true 3.0.0-M7 From b8086e72597619300008676795d691102981a3b9 Mon Sep 17 00:00:00 2001 From: Knative Automation Date: Thu, 18 Jun 2026 22:54:48 -0400 Subject: [PATCH 41/44] upgrade to latest dependencies (#3908) bumping knative.dev/client/pkg 43f7c74...a189866: > a189866 upgrade to latest dependencies (# 2244) Signed-off-by: Knative Automation --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 85fc29c950..4f65e6d110 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( k8s.io/client-go v0.35.6 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 - knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c + knative.dev/client/pkg v0.0.0-20260618145751-a1898668444a knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b knative.dev/pkg v0.0.0-20260615201544-6300c57a9e78 diff --git a/go.sum b/go.sum index c22f6f518a..ea21df19b1 100644 --- a/go.sum +++ b/go.sum @@ -1875,8 +1875,8 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c h1:8rXuSChlZwownlnaIDliiYCXZ3bHj3JMOaCmFLtfFis= -knative.dev/client/pkg v0.0.0-20260618025148-43f7c74f4e0c/go.mod h1:fdsbfxJV1hO4BabXs11CY5C1dR3etHIxzTNXQA2kkAo= +knative.dev/client/pkg v0.0.0-20260618145751-a1898668444a h1:+Z5br5J4BDQGVI+r9LNoP6GEQXN+vyaql6TeADB1A38= +knative.dev/client/pkg v0.0.0-20260618145751-a1898668444a/go.mod h1:btryCcFBdPCgf8S7FeFnldhYZKI43hTKAQ9Qz2NEW8w= knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303 h1:kG/rr63mOgmoEDzdYrDMIMIXfKPO0ycheUVQk94JtGA= knative.dev/eventing v0.49.1-0.20260617191249-8cf4c4b67303/go.mod h1:qAdl4gctMP0BvhxtkM7QCalOHcwY62MjB4s9XYSwzAw= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= From ddf90289760d6d0503066c9a6093b540e1079e54 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:14:48 +0200 Subject: [PATCH 42/44] e2e: python static sig for backward compat (#3910) --- e2e/e2e_core_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/e2e/e2e_core_test.go b/e2e/e2e_core_test.go index d2bae71d13..5a71b5674f 100644 --- a/e2e/e2e_core_test.go +++ b/e2e/e2e_core_test.go @@ -374,6 +374,49 @@ func Handle(w http.ResponseWriter, r *http.Request) { } } +// TestCore_StaticSignature_Python ensures backward compatibility with the +// static (non-instanced) Python function signature. Python functions can +// export either a `new()` constructor (instanced) or a plain `handle()` +// function (static). The scaffolding imports `new` first and falls back +// to `handle` on ImportError. +func TestCore_StaticSignature_Python(t *testing.T) { + name := "func-e2e-test-core-static-py" + root := fromCleanEnv(t, name) + + if err := newCmd(t, "init", "-l=python", "-t=http").Run(); err != nil { + t.Fatal(err) + } + + staticPy := `async def handle(scope, receive, send): + await send({ + 'type': 'http.response.start', + 'status': 200, + 'headers': [[b'content-type', b'text/plain']], + }) + await send({ + 'type': 'http.response.body', + 'body': b'OK', + }) +` + if err := os.WriteFile(filepath.Join(root, "function", "func.py"), []byte(staticPy), 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "function", "__init__.py"), []byte("from .func import handle\n"), 0644); err != nil { + t.Fatal(err) + } + + if err := newCmd(t, "deploy", "--builder", "host").Run(); err != nil { + t.Fatal(err) + } + defer func() { + clean(t, name, Namespace) + }() + + if !waitFor(t, ksvcUrl(name)) { + t.Fatal("static Python function did not deploy correctly") + } +} + // TestCore_Delete ensures that a function registered as deleted when deleted. // Also tests list as a side-effect. // From ed2f0d85c87136361c1da1a3b2cfe48fd97af846 Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:28:50 +0200 Subject: [PATCH 43/44] e2e: explicit instance signature tests -> persists state (#3909) --- e2e/e2e_instanced_test.go | 135 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 e2e/e2e_instanced_test.go diff --git a/e2e/e2e_instanced_test.go b/e2e/e2e_instanced_test.go new file mode 100644 index 0000000000..d491ef98bc --- /dev/null +++ b/e2e/e2e_instanced_test.go @@ -0,0 +1,135 @@ +//go:build e2e + +package e2e + +import ( + "io" + "net/http" + "os" + "path/filepath" + "strings" + "testing" +) + +// --------------------------------------------------------------------------- +// INSTANCED SIGNATURE TESTS +// Dedicated tests proving the instanced function pattern (New() constructor + +// struct-based Handle) works end-to-end for Go and Python. Each test deploys +// a function with a counter in the constructor, sends two requests, and +// asserts the counter increments — proving the instance persists across +// requests. +// --------------------------------------------------------------------------- + +const goHTTPInstancedSource = `package function + +import ( + "fmt" + "net/http" + "sync/atomic" +) + +type Function struct{ count int64 } + +func New() *Function { return &Function{} } + +func (f *Function) Handle(w http.ResponseWriter, r *http.Request) { + n := atomic.AddInt64(&f.count, 1) + fmt.Fprintf(w, "request:%d", n) +} +` + +const pythonHTTPInstancedSource = `def new(): + return Function() + +class Function: + def __init__(self): + self.count = 0 + + async def handle(self, scope, receive, send): + self.count += 1 + body = f"request:{self.count}".encode() + await send({ + 'type': 'http.response.start', + 'status': 200, + 'headers': [[b'content-type', b'text/plain']], + }) + await send({ + 'type': 'http.response.body', + 'body': body, + }) +` + +// sendRequest performs a single HTTP GET and returns the body as a string. +func sendRequest(t *testing.T, url string) string { + t.Helper() + res, err := http.Get(url) + if err != nil { + t.Fatalf("sendRequest GET %s: %v", url, err) + } + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatalf("sendRequest reading body: %v", err) + } + return string(body) +} + +// TestInstanced_GoHTTP deploys a Go HTTP function using the instanced +// signature and verifies that constructor state persists across requests. +func TestInstanced_GoHTTP(t *testing.T) { + name := "func-e2e-instanced-go-http" + root := fromCleanEnv(t, name) + + if err := newCmd(t, "init", "-l=go").Run(); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "function.go"), []byte(goHTTPInstancedSource), 0644); err != nil { + t.Fatal(err) + } + + if err := newCmd(t, "deploy", "--builder", "host").Run(); err != nil { + t.Fatal(err) + } + defer func() { + clean(t, name, Namespace) + }() + + if !waitFor(t, ksvcUrl(name), withContentMatch("request:1")) { + t.Fatal("instanced Go HTTP function did not return request:1") + } + + body := sendRequest(t, ksvcUrl(name)) + if !strings.Contains(body, "request:2") { + t.Fatalf("expected request:2 on second call, got: %s", body) + } +} + +// TestInstanced_PythonHTTP deploys a Python HTTP function using the instanced +// signature and verifies constructor state persists across requests. +func TestInstanced_PythonHTTP(t *testing.T) { + name := "func-e2e-instanced-py-http" + root := fromCleanEnv(t, name) + + if err := newCmd(t, "init", "-l=python", "-t=http").Run(); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "function", "func.py"), []byte(pythonHTTPInstancedSource), 0644); err != nil { + t.Fatal(err) + } + + if err := newCmd(t, "deploy", "--builder", "host").Run(); err != nil { + t.Fatal(err) + } + defer func() { + clean(t, name, Namespace) + }() + + if !waitFor(t, ksvcUrl(name), withContentMatch("request:1")) { + t.Fatal("instanced Python HTTP function did not return request:1") + } + + body := sendRequest(t, ksvcUrl(name)) + if !strings.Contains(body, "request:2") { + t.Fatalf("expected request:2 on second call, got: %s", body) + } +} From 7e06f8e08e73971dba051165230d94470de7425d Mon Sep 17 00:00:00 2001 From: David Fridrich <49119790+gauron99@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:40:48 +0200 Subject: [PATCH 44/44] e2e: set minScale=1 on recorder to prevent scale-to-zero (#3898) --- e2e/e2e_recorder_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/e2e/e2e_recorder_test.go b/e2e/e2e_recorder_test.go index 4cd2426950..8ebb63ab90 100644 --- a/e2e/e2e_recorder_test.go +++ b/e2e/e2e_recorder_test.go @@ -10,6 +10,8 @@ import ( "path/filepath" "testing" "time" + + fn "knative.dev/func/pkg/functions" ) // The recorder is a tiny HTTP Function deployed into the cluster that @@ -127,6 +129,18 @@ func deployRecorder(t *testing.T, name string) *recorder { []byte(recorderSource), 0644); err != nil { t.Fatal(err) } + + // Keep the recorder scaled to at least 1 replica to avoid cold-start latency + f, err := fn.NewFunction(root) + if err != nil { + t.Fatal(err) + } + minScale := int64(1) + f.Deploy.Options.Scale = &fn.ScaleOptions{Min: &minScale} + if err := f.Write(); err != nil { + t.Fatal(err) + } + if err := newCmd(t, "deploy").Run(); err != nil { t.Fatal(err) }