@intentius/chant-lexicon-k8s 0.41.20 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/dist/api/classify.d.ts.map +1 -1
  2. package/dist/api/connect.d.ts.map +1 -1
  3. package/dist/codegen/docs.d.ts.map +1 -1
  4. package/dist/components/argo-app.d.ts +104 -0
  5. package/dist/components/argo-app.d.ts.map +1 -0
  6. package/dist/components/builders.d.ts +8 -0
  7. package/dist/components/builders.d.ts.map +1 -1
  8. package/dist/components/capability-plugin.d.ts +1 -0
  9. package/dist/components/capability-plugin.d.ts.map +1 -1
  10. package/dist/components/flux-reconcile.d.ts +84 -0
  11. package/dist/components/flux-reconcile.d.ts.map +1 -0
  12. package/dist/components/index.d.ts +2 -0
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/components/kustomize-apply.d.ts +29 -7
  15. package/dist/components/kustomize-apply.d.ts.map +1 -1
  16. package/dist/composites/flux-app.d.ts +132 -0
  17. package/dist/composites/flux-app.d.ts.map +1 -0
  18. package/dist/composites/index.d.ts +2 -0
  19. package/dist/composites/index.d.ts.map +1 -1
  20. package/dist/config-schema.d.ts +3 -0
  21. package/dist/config-schema.d.ts.map +1 -1
  22. package/dist/config.d.ts +32 -5
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/crd/crd-sources.d.ts.map +1 -1
  25. package/dist/crd/parser.d.ts.map +1 -1
  26. package/dist/deep-observe-hooks.d.ts +8 -0
  27. package/dist/deep-observe-hooks.d.ts.map +1 -1
  28. package/dist/describe-resources.d.ts +10 -8
  29. package/dist/describe-resources.d.ts.map +1 -1
  30. package/dist/generated/index.d.ts +15 -4
  31. package/dist/generated/index.d.ts.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/integrity.json +10 -6
  35. package/dist/kube/connect.d.ts +5 -4
  36. package/dist/kube/connect.d.ts.map +1 -1
  37. package/dist/kube/get.d.ts +4 -4
  38. package/dist/kustomize/render.d.ts +15 -0
  39. package/dist/kustomize/render.d.ts.map +1 -0
  40. package/dist/kustomize/rendered-entity.d.ts +39 -0
  41. package/dist/kustomize/rendered-entity.d.ts.map +1 -0
  42. package/dist/kustomize/root.d.ts +19 -0
  43. package/dist/kustomize/root.d.ts.map +1 -0
  44. package/dist/lint/audit-catalog.d.ts.map +1 -1
  45. package/dist/lint/post-synth/argo-helpers.d.ts +2 -0
  46. package/dist/lint/post-synth/argo-helpers.d.ts.map +1 -1
  47. package/dist/lint/post-synth/flux002.d.ts +14 -0
  48. package/dist/lint/post-synth/flux002.d.ts.map +1 -0
  49. package/dist/lint/post-synth/flux003.d.ts +17 -0
  50. package/dist/lint/post-synth/flux003.d.ts.map +1 -0
  51. package/dist/lint/post-synth/index.d.ts.map +1 -1
  52. package/dist/lint/rules/flux-source-ref-pin.d.ts +16 -0
  53. package/dist/lint/rules/flux-source-ref-pin.d.ts.map +1 -0
  54. package/dist/manifest.json +1 -1
  55. package/dist/meta.json +92 -16
  56. package/dist/okf/index.md +256 -0
  57. package/dist/okf/rules/ARGO001.md +16 -0
  58. package/dist/okf/rules/ARGO002.md +16 -0
  59. package/dist/okf/rules/ARGO003.md +15 -0
  60. package/dist/okf/rules/ARGO004.md +17 -0
  61. package/dist/okf/rules/ARGO005.md +15 -0
  62. package/dist/okf/rules/FLUX001.md +17 -0
  63. package/dist/okf/rules/FLUX002.md +19 -0
  64. package/dist/okf/rules/FLUX003.md +15 -0
  65. package/dist/okf/rules/WK8001.md +16 -0
  66. package/dist/okf/rules/WK8002.md +20 -0
  67. package/dist/okf/rules/WK8003.md +19 -0
  68. package/dist/okf/rules/WK8005.md +11 -0
  69. package/dist/okf/rules/WK8006.md +11 -0
  70. package/dist/okf/rules/WK8041.md +11 -0
  71. package/dist/okf/rules/WK8042.md +11 -0
  72. package/dist/okf/rules/WK8101.md +11 -0
  73. package/dist/okf/rules/WK8102.md +11 -0
  74. package/dist/okf/rules/WK8103.md +11 -0
  75. package/dist/okf/rules/WK8104.md +15 -0
  76. package/dist/okf/rules/WK8105.md +11 -0
  77. package/dist/okf/rules/WK8201.md +11 -0
  78. package/dist/okf/rules/WK8202.md +11 -0
  79. package/dist/okf/rules/WK8203.md +11 -0
  80. package/dist/okf/rules/WK8204.md +11 -0
  81. package/dist/okf/rules/WK8205.md +11 -0
  82. package/dist/okf/rules/WK8207.md +11 -0
  83. package/dist/okf/rules/WK8208.md +11 -0
  84. package/dist/okf/rules/WK8209.md +11 -0
  85. package/dist/okf/rules/WK8301.md +16 -0
  86. package/dist/okf/rules/WK8302.md +11 -0
  87. package/dist/okf/rules/WK8303.md +15 -0
  88. package/dist/okf/rules/WK8304.md +15 -0
  89. package/dist/okf/rules/WK8305.md +15 -0
  90. package/dist/okf/rules/WK8306.md +11 -0
  91. package/dist/okf/rules/WK8401.md +15 -0
  92. package/dist/okf/rules/WK8402.md +15 -0
  93. package/dist/okf/rules/WK8403.md +15 -0
  94. package/dist/okf/types/APIGroup.md +16 -0
  95. package/dist/okf/types/APIGroupList.md +13 -0
  96. package/dist/okf/types/APIResourceList.md +14 -0
  97. package/dist/okf/types/APIService.md +14 -0
  98. package/dist/okf/types/APIServiceList.md +14 -0
  99. package/dist/okf/types/APIVersions.md +14 -0
  100. package/dist/okf/types/Addon.md +14 -0
  101. package/dist/okf/types/Alert.md +14 -0
  102. package/dist/okf/types/AppProject.md +19 -0
  103. package/dist/okf/types/Application.md +23 -0
  104. package/dist/okf/types/ApplicationSet.md +18 -0
  105. package/dist/okf/types/Backup.md +14 -0
  106. package/dist/okf/types/BatchJob.md +14 -0
  107. package/dist/okf/types/Binding.md +14 -0
  108. package/dist/okf/types/Bucket.md +18 -0
  109. package/dist/okf/types/CM.md +16 -0
  110. package/dist/okf/types/CSIDriver.md +14 -0
  111. package/dist/okf/types/CSIDriverList.md +14 -0
  112. package/dist/okf/types/CSINode.md +14 -0
  113. package/dist/okf/types/CSINodeList.md +14 -0
  114. package/dist/okf/types/CSIStorageCapacity.md +17 -0
  115. package/dist/okf/types/CSIStorageCapacityList.md +14 -0
  116. package/dist/okf/types/Certificate.md +18 -0
  117. package/dist/okf/types/CertificateRequest.md +14 -0
  118. package/dist/okf/types/CertificateSigningRequest.md +14 -0
  119. package/dist/okf/types/CertificateSigningRequestList.md +14 -0
  120. package/dist/okf/types/Challenge.md +14 -0
  121. package/dist/okf/types/Cluster.md +14 -0
  122. package/dist/okf/types/ClusterIssuer.md +14 -0
  123. package/dist/okf/types/ClusterRole.md +15 -0
  124. package/dist/okf/types/ClusterRoleBinding.md +15 -0
  125. package/dist/okf/types/ClusterRoleBindingList.md +14 -0
  126. package/dist/okf/types/ClusterRoleList.md +14 -0
  127. package/dist/okf/types/ClusterTrustBundle.md +14 -0
  128. package/dist/okf/types/ClusterTrustBundleList.md +14 -0
  129. package/dist/okf/types/ComponentStatus.md +14 -0
  130. package/dist/okf/types/ComponentStatusList.md +14 -0
  131. package/dist/okf/types/ConfigMapList.md +14 -0
  132. package/dist/okf/types/ControllerRevision.md +15 -0
  133. package/dist/okf/types/ControllerRevisionList.md +14 -0
  134. package/dist/okf/types/CoreEventList.md +14 -0
  135. package/dist/okf/types/CrdbCluster.md +14 -0
  136. package/dist/okf/types/CronJob.md +20 -0
  137. package/dist/okf/types/CronJobList.md +14 -0
  138. package/dist/okf/types/CustomResourceDefinition.md +14 -0
  139. package/dist/okf/types/CustomResourceDefinitionList.md +14 -0
  140. package/dist/okf/types/DaemonSet.md +19 -0
  141. package/dist/okf/types/DaemonSetList.md +14 -0
  142. package/dist/okf/types/DeleteOptions.md +18 -0
  143. package/dist/okf/types/Deploy.md +14 -0
  144. package/dist/okf/types/DeploymentList.md +14 -0
  145. package/dist/okf/types/DeviceClass.md +14 -0
  146. package/dist/okf/types/DeviceClassList.md +14 -0
  147. package/dist/okf/types/DeviceTaintRule.md +14 -0
  148. package/dist/okf/types/DeviceTaintRuleList.md +14 -0
  149. package/dist/okf/types/EndpointSlice.md +16 -0
  150. package/dist/okf/types/EndpointSliceList.md +14 -0
  151. package/dist/okf/types/Endpoints.md +14 -0
  152. package/dist/okf/types/EndpointsList.md +14 -0
  153. package/dist/okf/types/Event.md +27 -0
  154. package/dist/okf/types/EventsEvent.md +27 -0
  155. package/dist/okf/types/EventsEventList.md +14 -0
  156. package/dist/okf/types/Eviction.md +14 -0
  157. package/dist/okf/types/FlowSchema.md +14 -0
  158. package/dist/okf/types/FlowSchemaList.md +14 -0
  159. package/dist/okf/types/FluxHelmChart.md +14 -0
  160. package/dist/okf/types/FluxInstance.md +14 -0
  161. package/dist/okf/types/FluxReport.md +14 -0
  162. package/dist/okf/types/GRPCRoute.md +14 -0
  163. package/dist/okf/types/Gateway.md +14 -0
  164. package/dist/okf/types/GatewayClass.md +14 -0
  165. package/dist/okf/types/GitRepository.md +19 -0
  166. package/dist/okf/types/HTTPRoute.md +14 -0
  167. package/dist/okf/types/HelmChartConfig.md +14 -0
  168. package/dist/okf/types/HelmRelease.md +14 -0
  169. package/dist/okf/types/HelmRepository.md +14 -0
  170. package/dist/okf/types/HorizontalPodAutoscaler.md +14 -0
  171. package/dist/okf/types/HorizontalPodAutoscalerList.md +14 -0
  172. package/dist/okf/types/IPAddress.md +14 -0
  173. package/dist/okf/types/IPAddressList.md +14 -0
  174. package/dist/okf/types/ImagePolicy.md +14 -0
  175. package/dist/okf/types/ImageRepository.md +14 -0
  176. package/dist/okf/types/ImageUpdateAutomation.md +14 -0
  177. package/dist/okf/types/InfisicalDynamicSecret.md +14 -0
  178. package/dist/okf/types/InfisicalPushSecret.md +14 -0
  179. package/dist/okf/types/InfisicalSecret.md +14 -0
  180. package/dist/okf/types/Ing.md +14 -0
  181. package/dist/okf/types/IngressClass.md +14 -0
  182. package/dist/okf/types/IngressClassList.md +14 -0
  183. package/dist/okf/types/IngressList.md +14 -0
  184. package/dist/okf/types/IngressRoute.md +14 -0
  185. package/dist/okf/types/IngressRouteTCP.md +14 -0
  186. package/dist/okf/types/IngressRouteUDP.md +14 -0
  187. package/dist/okf/types/Issuer.md +14 -0
  188. package/dist/okf/types/JobList.md +14 -0
  189. package/dist/okf/types/K3sHelmChart.md +14 -0
  190. package/dist/okf/types/Kustomization.md +20 -0
  191. package/dist/okf/types/Lease.md +14 -0
  192. package/dist/okf/types/LeaseCandidate.md +14 -0
  193. package/dist/okf/types/LeaseCandidateList.md +14 -0
  194. package/dist/okf/types/LeaseList.md +14 -0
  195. package/dist/okf/types/LimitRange.md +14 -0
  196. package/dist/okf/types/LimitRangeList.md +14 -0
  197. package/dist/okf/types/LocalSubjectAccessReview.md +14 -0
  198. package/dist/okf/types/MicroVM.md +14 -0
  199. package/dist/okf/types/MicroVMClass.md +14 -0
  200. package/dist/okf/types/MicroVMImage.md +14 -0
  201. package/dist/okf/types/MicroVMNetwork.md +14 -0
  202. package/dist/okf/types/MicroVMReplicaSet.md +14 -0
  203. package/dist/okf/types/Middleware.md +14 -0
  204. package/dist/okf/types/MiddlewareTCP.md +14 -0
  205. package/dist/okf/types/MutatingAdmissionPolicy.md +14 -0
  206. package/dist/okf/types/MutatingAdmissionPolicyBinding.md +14 -0
  207. package/dist/okf/types/MutatingAdmissionPolicyBindingList.md +14 -0
  208. package/dist/okf/types/MutatingAdmissionPolicyList.md +14 -0
  209. package/dist/okf/types/MutatingWebhookConfiguration.md +14 -0
  210. package/dist/okf/types/MutatingWebhookConfigurationList.md +14 -0
  211. package/dist/okf/types/Namespace.md +19 -0
  212. package/dist/okf/types/NamespaceList.md +14 -0
  213. package/dist/okf/types/NetPol.md +14 -0
  214. package/dist/okf/types/NetworkPolicyList.md +14 -0
  215. package/dist/okf/types/Node.md +22 -0
  216. package/dist/okf/types/NodeList.md +14 -0
  217. package/dist/okf/types/OCIRepository.md +18 -0
  218. package/dist/okf/types/ObjectStore.md +14 -0
  219. package/dist/okf/types/Order.md +14 -0
  220. package/dist/okf/types/PDB.md +18 -0
  221. package/dist/okf/types/PersistentVolume.md +14 -0
  222. package/dist/okf/types/PersistentVolumeClaim.md +14 -0
  223. package/dist/okf/types/PersistentVolumeClaimList.md +14 -0
  224. package/dist/okf/types/PersistentVolumeList.md +14 -0
  225. package/dist/okf/types/Pod.md +18 -0
  226. package/dist/okf/types/PodCertificateRequest.md +14 -0
  227. package/dist/okf/types/PodCertificateRequestList.md +14 -0
  228. package/dist/okf/types/PodDisruptionBudgetList.md +14 -0
  229. package/dist/okf/types/PodList.md +14 -0
  230. package/dist/okf/types/PodTemplate.md +14 -0
  231. package/dist/okf/types/PodTemplateList.md +14 -0
  232. package/dist/okf/types/Pooler.md +14 -0
  233. package/dist/okf/types/PriorityClass.md +17 -0
  234. package/dist/okf/types/PriorityClassList.md +14 -0
  235. package/dist/okf/types/PriorityLevelConfiguration.md +14 -0
  236. package/dist/okf/types/PriorityLevelConfigurationList.md +14 -0
  237. package/dist/okf/types/PrometheusRule.md +14 -0
  238. package/dist/okf/types/Provider.md +14 -0
  239. package/dist/okf/types/RayCluster.md +20 -0
  240. package/dist/okf/types/RayJob.md +14 -0
  241. package/dist/okf/types/RayService.md +14 -0
  242. package/dist/okf/types/Receiver.md +14 -0
  243. package/dist/okf/types/ReferenceGrant.md +14 -0
  244. package/dist/okf/types/ReplicaSet.md +19 -0
  245. package/dist/okf/types/ReplicaSetList.md +14 -0
  246. package/dist/okf/types/ReplicationController.md +14 -0
  247. package/dist/okf/types/ReplicationControllerList.md +14 -0
  248. package/dist/okf/types/ResourceClaim.md +14 -0
  249. package/dist/okf/types/ResourceClaimList.md +14 -0
  250. package/dist/okf/types/ResourceClaimTemplate.md +14 -0
  251. package/dist/okf/types/ResourceClaimTemplateList.md +14 -0
  252. package/dist/okf/types/ResourceQuota.md +14 -0
  253. package/dist/okf/types/ResourceQuotaList.md +14 -0
  254. package/dist/okf/types/ResourceSet.md +14 -0
  255. package/dist/okf/types/ResourceSetInputProvider.md +14 -0
  256. package/dist/okf/types/ResourceSlice.md +14 -0
  257. package/dist/okf/types/ResourceSliceList.md +14 -0
  258. package/dist/okf/types/Role.md +14 -0
  259. package/dist/okf/types/RoleBinding.md +15 -0
  260. package/dist/okf/types/RoleBindingList.md +14 -0
  261. package/dist/okf/types/RoleList.md +14 -0
  262. package/dist/okf/types/RuntimeClass.md +16 -0
  263. package/dist/okf/types/RuntimeClassList.md +14 -0
  264. package/dist/okf/types/SA.md +16 -0
  265. package/dist/okf/types/Scale.md +14 -0
  266. package/dist/okf/types/ScheduledBackup.md +14 -0
  267. package/dist/okf/types/Sec.md +17 -0
  268. package/dist/okf/types/SecretList.md +14 -0
  269. package/dist/okf/types/SelfSubjectAccessReview.md +14 -0
  270. package/dist/okf/types/SelfSubjectReview.md +13 -0
  271. package/dist/okf/types/SelfSubjectRulesReview.md +14 -0
  272. package/dist/okf/types/ServersTransport.md +14 -0
  273. package/dist/okf/types/ServersTransportTCP.md +14 -0
  274. package/dist/okf/types/Service.md +20 -0
  275. package/dist/okf/types/ServiceAccountList.md +14 -0
  276. package/dist/okf/types/ServiceCIDR.md +14 -0
  277. package/dist/okf/types/ServiceCIDRList.md +14 -0
  278. package/dist/okf/types/ServiceList.md +14 -0
  279. package/dist/okf/types/ServiceMonitor.md +14 -0
  280. package/dist/okf/types/StatefulSet.md +19 -0
  281. package/dist/okf/types/StatefulSetList.md +14 -0
  282. package/dist/okf/types/Status.md +17 -0
  283. package/dist/okf/types/StorageClass.md +20 -0
  284. package/dist/okf/types/StorageClassList.md +14 -0
  285. package/dist/okf/types/StorageVersionMigration.md +14 -0
  286. package/dist/okf/types/StorageVersionMigrationList.md +14 -0
  287. package/dist/okf/types/SubjectAccessReview.md +14 -0
  288. package/dist/okf/types/TLSOption.md +14 -0
  289. package/dist/okf/types/TLSStore.md +14 -0
  290. package/dist/okf/types/TokenRequest.md +14 -0
  291. package/dist/okf/types/TokenReview.md +14 -0
  292. package/dist/okf/types/TraefikService.md +14 -0
  293. package/dist/okf/types/ValidatingAdmissionPolicy.md +14 -0
  294. package/dist/okf/types/ValidatingAdmissionPolicyBinding.md +14 -0
  295. package/dist/okf/types/ValidatingAdmissionPolicyBindingList.md +14 -0
  296. package/dist/okf/types/ValidatingAdmissionPolicyList.md +14 -0
  297. package/dist/okf/types/ValidatingWebhookConfiguration.md +14 -0
  298. package/dist/okf/types/ValidatingWebhookConfigurationList.md +14 -0
  299. package/dist/okf/types/VolumeAttachment.md +14 -0
  300. package/dist/okf/types/VolumeAttachmentList.md +14 -0
  301. package/dist/okf/types/VolumeAttributesClass.md +15 -0
  302. package/dist/okf/types/VolumeAttributesClassList.md +14 -0
  303. package/dist/okf/types/WatchEvent.md +14 -0
  304. package/dist/plugin.d.ts.map +1 -1
  305. package/dist/rules/argo-helpers.ts +2 -0
  306. package/dist/rules/flux-source-ref-pin.ts +52 -0
  307. package/dist/rules/flux002.ts +77 -0
  308. package/dist/rules/flux003.ts +75 -0
  309. package/dist/serializer.d.ts.map +1 -1
  310. package/dist/skills/chant-k8s-flux.md +162 -0
  311. package/dist/types/index.d.ts +41 -3
  312. package/dist/validate.d.ts.map +1 -1
  313. package/package.json +3 -3
  314. package/src/api/classify.test.ts +20 -0
  315. package/src/api/classify.ts +8 -1
  316. package/src/api/connect.ts +10 -0
  317. package/src/codegen/docs.ts +11 -0
  318. package/src/components/argo-app.test.ts +137 -0
  319. package/src/components/argo-app.ts +163 -0
  320. package/src/components/builders.ts +8 -0
  321. package/src/components/capability-plugin.ts +11 -1
  322. package/src/components/flux-reconcile.test.ts +145 -0
  323. package/src/components/flux-reconcile.ts +159 -0
  324. package/src/components/index.ts +13 -0
  325. package/src/components/kustomize-apply.ts +12 -41
  326. package/src/composites/composites.test.ts +122 -0
  327. package/src/composites/flux-app.ts +274 -0
  328. package/src/composites/index.ts +9 -0
  329. package/src/config-augmentation.test.ts +72 -0
  330. package/src/config-schema.ts +5 -0
  331. package/src/config.ts +33 -5
  332. package/src/crd/crd-sources.ts +37 -0
  333. package/src/crd/parser.ts +6 -0
  334. package/src/deep-observe-hooks.ts +60 -0
  335. package/src/deep-observe.test.ts +114 -0
  336. package/src/describe-resources.test.ts +215 -6
  337. package/src/describe-resources.ts +197 -118
  338. package/src/generated/index.d.ts +41 -3
  339. package/src/generated/index.ts +15 -4
  340. package/src/generated/lexicon-k8s.json +92 -16
  341. package/src/generated/operations.json +48 -0
  342. package/src/index.ts +4 -0
  343. package/src/kube/connect.ts +5 -4
  344. package/src/kube/get.ts +4 -4
  345. package/src/kustomize/render.ts +84 -0
  346. package/src/kustomize/rendered-entity.ts +72 -0
  347. package/src/kustomize/root.test.ts +268 -0
  348. package/src/kustomize/root.ts +109 -0
  349. package/src/lint/audit-catalog.ts +2 -0
  350. package/src/lint/post-synth/argo-helpers.ts +2 -0
  351. package/src/lint/post-synth/flux002.ts +77 -0
  352. package/src/lint/post-synth/flux003.ts +75 -0
  353. package/src/lint/post-synth/index.ts +4 -0
  354. package/src/lint/post-synth/post-synth.test.ts +132 -0
  355. package/src/lint/rules/flux-source-ref-pin.ts +52 -0
  356. package/src/lint/rules/rules.test.ts +51 -0
  357. package/src/plugin.test.ts +2 -0
  358. package/src/plugin.ts +47 -0
  359. package/src/serializer.ts +34 -4
  360. package/src/skills/chant-k8s-flux.md +162 -0
  361. package/src/testdata/kustomize-root/base/deployment.yaml +17 -0
  362. package/src/testdata/kustomize-root/base/kustomization.yaml +3 -0
  363. package/src/testdata/kustomize-root/base/service.yaml +9 -0
  364. package/src/testdata/kustomize-root/overlays/prod/kustomization.yaml +6 -0
  365. package/src/validate.ts +4 -0
@@ -14,32 +14,26 @@
14
14
  * `stack` doubles as the ownership identity and the deploy unit, exactly as
15
15
  * `kubectl-apply` — one name, both jobs.
16
16
  *
17
- * Renderer resolution: `kustomize build`, falling back to
17
+ * Renderer resolution lives in `../kustomize/render.ts` (shared with the
18
+ * build-root path, #1548 piece 3): `kustomize build`, falling back to
18
19
  * `kubectl kustomize` (the same renderer vendored into kubectl) when the
19
20
  * standalone binary is absent. Both run through an injectable runner so tests
20
- * assert the exact command without either binary installed. The 64MiB
21
- * maxBuffer mirrors helm-upgrade's — a big overlay renders megabytes.
21
+ * assert the exact command without either binary installed.
22
22
  *
23
23
  * The #1074 boundary applies as it does to kubectl-apply: nothing exported
24
24
  * from the lexicon entry point may statically import the API-client chain, so
25
25
  * the applier is reached by dynamic import inside `run()` and its argument
26
26
  * shape is a structural mirror.
27
27
  */
28
- import { exec } from "node:child_process";
29
- import { promisify } from "node:util";
30
- import { loadAll } from "js-yaml";
31
28
  import type { Capability, DeployContext } from "@intentius/chant/components/capability";
29
+ import {
30
+ defaultKustomizeRunner,
31
+ renderCommand,
32
+ renderKustomizeDocuments,
33
+ type KustomizeRunner,
34
+ } from "../kustomize/render";
32
35
 
33
- const execAsync = promisify(exec);
34
-
35
- /** Injectable shell runner — the helm-upgrade seam, same buffer bound. */
36
- export type KustomizeRunner = (command: string) => Promise<{ stdout: string }>;
37
- const defaultRunner: KustomizeRunner = (command) => execAsync(command, { maxBuffer: 64 * 1024 * 1024 });
38
-
39
- /** Single-quote shell escaping, as helm-upgrade quotes its argv. */
40
- function q(v: string): string {
41
- return `'${v.replace(/'/g, "'\\''")}'`;
42
- }
36
+ export { renderCommand, type KustomizeRunner };
43
37
 
44
38
  /** Structural mirrors of the activity module's shapes (see the module doc). */
45
39
  interface AppliedRef {
@@ -81,33 +75,10 @@ export interface KustomizeApplyInput {
81
75
  delete?: "never" | "owned-only";
82
76
  }
83
77
 
84
- /** The exact render command, standalone binary first. Pure; exported for tests. */
85
- export function renderCommand(dir: string, tool: "kustomize" | "kubectl" = "kustomize"): string {
86
- return tool === "kustomize" ? `kustomize build ${q(dir)}` : `kubectl kustomize ${q(dir)}`;
87
- }
88
-
89
- /** Render the overlay, falling back to kubectl's vendored kustomize when the
90
- * standalone binary is missing (ENOENT/127), and parse the emitted YAML. */
91
- async function renderDocuments(dir: string, run: KustomizeRunner): Promise<Array<Record<string, unknown>>> {
92
- let stdout: string;
93
- try {
94
- ({ stdout } = await run(renderCommand(dir, "kustomize")));
95
- } catch (err) {
96
- const message = err instanceof Error ? err.message : String(err);
97
- if (!/ENOENT|not found|command not found|127/.test(message)) throw err;
98
- ({ stdout } = await run(renderCommand(dir, "kubectl")));
99
- }
100
- const documents: Array<Record<string, unknown>> = [];
101
- for (const doc of loadAll(stdout)) {
102
- if (doc && typeof doc === "object" && !Array.isArray(doc)) documents.push(doc as Record<string, unknown>);
103
- }
104
- return documents;
105
- }
106
-
107
78
  /** Factory with injectable renderer + applier, so tests assert the render
108
79
  * command and the exact applier delegation without kustomize or a cluster. */
109
80
  export function createKustomizeApplyCapability(
110
- run: KustomizeRunner = defaultRunner,
81
+ run: KustomizeRunner = defaultKustomizeRunner,
111
82
  apply?: Applier,
112
83
  ): Capability<KustomizeApplyInput, KustomizeApplyOutcome> {
113
84
  return {
@@ -116,7 +87,7 @@ export function createKustomizeApplyCapability(
116
87
  // undo of its own — same posture as kubectl-apply.
117
88
  rollbackPolicy: "needs-opt-out",
118
89
  async run(ctx: DeployContext, input: KustomizeApplyInput): Promise<KustomizeApplyOutcome> {
119
- const documents = await renderDocuments(input.dir, run);
90
+ const documents = await renderKustomizeDocuments(input.dir, run);
120
91
  const applier: Applier = apply ?? ((await import("../op/activities/kubectl")).applyManifest as unknown as Applier);
121
92
  return applier({
122
93
  manifest: `kustomize:${input.dir}`,
@@ -9,6 +9,7 @@ function p(member: unknown): Record<string, unknown> {
9
9
  }
10
10
  import { StatefulApp } from "./stateful-app";
11
11
  import { ArgoAppFor, ArgoAppSetForRegions, registerArgoCluster } from "./argo-app";
12
+ import { FluxGitSource, FluxAppFor } from "./flux-app";
12
13
  import { CronWorkload } from "./cron-workload";
13
14
  import { AutoscaledService } from "./autoscaled-service";
14
15
  import { WorkerPool } from "./worker-pool";
@@ -3359,6 +3360,121 @@ describe("registerArgoCluster", () => {
3359
3360
  });
3360
3361
  });
3361
3362
 
3363
+ describe("FluxGitSource", () => {
3364
+ test("returns a single GitRepository", () => {
3365
+ const result = FluxGitSource("home-chant", { url: "https://github.com/jhgaylor/home-chant" });
3366
+ expect(result.gitRepository).toBeDefined();
3367
+ expect((result.gitRepository as any).entityType).toBe("K8s::Flux::GitRepository");
3368
+ });
3369
+
3370
+ test("defaults: 5m interval, main branch, flux-system namespace", () => {
3371
+ const result = FluxGitSource("home-chant", { url: "https://github.com/jhgaylor/home-chant" });
3372
+ const meta = p(result.gitRepository).metadata as any;
3373
+ const spec = p(result.gitRepository).spec as any;
3374
+ expect(meta.namespace).toBe("flux-system");
3375
+ expect(meta.labels["app.kubernetes.io/managed-by"]).toBe("chant");
3376
+ expect(spec.interval).toBe("5m");
3377
+ expect(spec.url).toBe("https://github.com/jhgaylor/home-chant");
3378
+ expect(spec.ref).toEqual({ branch: "main" });
3379
+ });
3380
+
3381
+ test("a tag pin wins over the branch default", () => {
3382
+ const spec = p(FluxGitSource("app", { url: "u", tag: "v1.2.3" }).gitRepository).spec as any;
3383
+ expect(spec.ref).toEqual({ tag: "v1.2.3" });
3384
+ });
3385
+
3386
+ test("secretRef renders as a name reference", () => {
3387
+ const spec = p(FluxGitSource("app", { url: "u", secretRef: "git-creds" }).gitRepository).spec as any;
3388
+ expect(spec.secretRef).toEqual({ name: "git-creds" });
3389
+ });
3390
+ });
3391
+
3392
+ describe("FluxAppFor", () => {
3393
+ const source = () => FluxGitSource("home-chant", { url: "https://github.com/jhgaylor/home-chant" });
3394
+
3395
+ test("returns a single Kustomization (the source is not duplicated)", () => {
3396
+ const result = FluxAppFor("hello-chant", { source: source(), path: "./apps/hello-chant/k8s" });
3397
+ expect(result.kustomization).toBeDefined();
3398
+ expect((result.kustomization as any).entityType).toBe("K8s::Flux::Kustomization");
3399
+ expect((result as any).gitRepository).toBeUndefined();
3400
+ });
3401
+
3402
+ test("defaults: 10m interval, prune, wait, flux-system namespace", () => {
3403
+ const result = FluxAppFor("hello-chant", { source: source(), path: "./apps/hello-chant/k8s" });
3404
+ const meta = p(result.kustomization).metadata as any;
3405
+ const spec = p(result.kustomization).spec as any;
3406
+ expect(meta.name).toBe("hello-chant");
3407
+ expect(meta.namespace).toBe("flux-system");
3408
+ expect(spec.interval).toBe("10m");
3409
+ expect(spec.prune).toBe(true);
3410
+ expect(spec.wait).toBe(true);
3411
+ expect(spec.path).toBe("./apps/hello-chant/k8s");
3412
+ });
3413
+
3414
+ test("derives sourceRef from a FluxGitSource result", () => {
3415
+ const spec = p(FluxAppFor("hello", { source: source(), path: "./k8s" }).kustomization).spec as any;
3416
+ expect(spec.sourceRef).toEqual({ kind: "GitRepository", name: "home-chant", namespace: "flux-system" });
3417
+ });
3418
+
3419
+ test("many apps share one source", () => {
3420
+ const shared = source();
3421
+ const a = FluxAppFor("mealie", { source: shared, path: "./apps/mealie/k8s" });
3422
+ const b = FluxAppFor("ntfy", { source: shared, path: "./apps/ntfy/k8s" });
3423
+ expect((p(a.kustomization).spec as any).sourceRef.name).toBe("home-chant");
3424
+ expect((p(b.kustomization).spec as any).sourceRef.name).toBe("home-chant");
3425
+ });
3426
+
3427
+ test("a string source names an existing GitRepository (flux-system)", () => {
3428
+ const spec = p(FluxAppFor("mealie", { source: "flux-system", path: "./apps/mealie/generated" }).kustomization)
3429
+ .spec as any;
3430
+ expect(spec.sourceRef).toEqual({ kind: "GitRepository", name: "flux-system" });
3431
+ });
3432
+
3433
+ test("an explicit ref carries a non-git source kind", () => {
3434
+ const spec = p(
3435
+ FluxAppFor("fountain", { source: { kind: "OCIRepository", name: "fountain" }, path: "./k8s" }).kustomization,
3436
+ ).spec as any;
3437
+ expect(spec.sourceRef).toEqual({ kind: "OCIRepository", name: "fountain" });
3438
+ });
3439
+
3440
+ test("dependsOn is a plain name list rendered to name objects", () => {
3441
+ const spec = p(
3442
+ FluxAppFor("hello", {
3443
+ source: "flux-system",
3444
+ path: "./k8s",
3445
+ dependsOn: ["cert-manager", "traefik"],
3446
+ }).kustomization,
3447
+ ).spec as any;
3448
+ expect(spec.dependsOn).toEqual([{ name: "cert-manager" }, { name: "traefik" }]);
3449
+ });
3450
+
3451
+ test("omits dependsOn when the list is empty", () => {
3452
+ const spec = p(FluxAppFor("hello", { source: "flux-system", path: "./k8s" }).kustomization).spec as any;
3453
+ expect(spec.dependsOn).toBeUndefined();
3454
+ });
3455
+
3456
+ test("maps targetNamespace and opt-outs onto the spec", () => {
3457
+ const spec = p(
3458
+ FluxAppFor("cloudflared", {
3459
+ source: "flux-system",
3460
+ path: "./apps/cloudflared/generated",
3461
+ targetNamespace: "cloudflared",
3462
+ wait: false,
3463
+ prune: false,
3464
+ suspend: true,
3465
+ timeout: "3m",
3466
+ serviceAccountName: "flux-applier",
3467
+ }).kustomization,
3468
+ ).spec as any;
3469
+ expect(spec.targetNamespace).toBe("cloudflared");
3470
+ expect(spec.wait).toBe(false);
3471
+ expect(spec.prune).toBe(false);
3472
+ expect(spec.suspend).toBe(true);
3473
+ expect(spec.timeout).toBe("3m");
3474
+ expect(spec.serviceAccountName).toBe("flux-applier");
3475
+ });
3476
+ });
3477
+
3362
3478
  describe("package index re-exports (regression guard)", () => {
3363
3479
  test("Argo composites are reachable from the package entry", async () => {
3364
3480
  const pkg: any = await import("../index");
@@ -3366,4 +3482,10 @@ describe("package index re-exports (regression guard)", () => {
3366
3482
  expect(typeof pkg.ArgoAppSetForRegions).toBe("function");
3367
3483
  expect(typeof pkg.registerArgoCluster).toBe("function");
3368
3484
  });
3485
+
3486
+ test("Flux composites are reachable from the package entry", async () => {
3487
+ const pkg: any = await import("../index");
3488
+ expect(typeof pkg.FluxGitSource).toBe("function");
3489
+ expect(typeof pkg.FluxAppFor).toBe("function");
3490
+ });
3369
3491
  });
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Flux CD composites — turn Chant build targets into Flux sources and
3
+ * `Kustomization`s.
4
+ *
5
+ * The k8s lexicon stays runtime-agnostic: it only emits manifests. These
6
+ * composites are the opt-in bridge to Flux's reconciliation layer, the
7
+ * `ArgoAppFor` analogue for the source-controller/kustomize-controller pair.
8
+ * Authoring a `GitRepository` + `Kustomization` by hand is ~40 lines of YAML
9
+ * per app; `FluxAppFor` collapses the pair to one call with defaults taken
10
+ * from real Flux estates.
11
+ *
12
+ * - FluxGitSource(name, opts) → a single K8s::Flux::GitRepository
13
+ * - FluxAppFor(target, opts) → a single K8s::Flux::Kustomization
14
+ *
15
+ * The two are split deliberately: the common estate shape is one source
16
+ * shared by many Kustomizations. A `GitRepository` per app is the mistake
17
+ * this split makes hard — declare the source once, hand its result to every
18
+ * `FluxAppFor` that reconciles a path out of it.
19
+ */
20
+
21
+ import { Composite, mergeDefaults } from "@intentius/chant";
22
+ import {
23
+ GitRepository as GitRepositoryResource,
24
+ Kustomization as KustomizationResource,
25
+ } from "../generated";
26
+
27
+ // ── Shared types ─────────────────────────────────────────────────────────────
28
+
29
+ /** The namespace the Flux controllers run in and watch by default. */
30
+ const FLUX_NAMESPACE = "flux-system";
31
+
32
+ /** Source kinds a Kustomization can reconcile from. */
33
+ export type FluxSourceKind = "GitRepository" | "OCIRepository" | "Bucket";
34
+
35
+ /** An explicit reference to a source declared elsewhere. */
36
+ export interface FluxSourceRef {
37
+ /** Source kind (default "GitRepository"). */
38
+ kind?: FluxSourceKind;
39
+ /** Name of the source object. */
40
+ name: string;
41
+ /** Namespace of the source, when it differs from the Kustomization's. */
42
+ namespace?: string;
43
+ }
44
+
45
+ // ── FluxGitSource ────────────────────────────────────────────────────────────
46
+
47
+ export interface FluxGitSourceOptions {
48
+ /** Git repository URL the source-controller fetches. */
49
+ url: string;
50
+ /** Branch to track (default "main"). Ignored when `tag` is set. */
51
+ branch?: string;
52
+ /** Tag to pin instead of a branch. */
53
+ tag?: string;
54
+ /** Fetch interval (default "5m"). */
55
+ interval?: string;
56
+ /** Name of the Secret holding git credentials, for private repos. */
57
+ secretRef?: string;
58
+ /** Namespace the GitRepository lives in (default "flux-system"). */
59
+ fluxNamespace?: string;
60
+ /** Extra labels applied to the GitRepository. */
61
+ labels?: Record<string, string>;
62
+ defaults?: { gitRepository?: Partial<Record<string, unknown>> };
63
+ }
64
+
65
+ export type FluxGitSourceResult = {
66
+ gitRepository: InstanceType<typeof GitRepositoryResource>;
67
+ }
68
+
69
+ const FluxGitRepository = Composite<{ name: string } & FluxGitSourceOptions, FluxGitSourceResult>(
70
+ (props) => {
71
+ const {
72
+ name,
73
+ url,
74
+ branch = "main",
75
+ tag,
76
+ interval = "5m",
77
+ secretRef,
78
+ fluxNamespace = FLUX_NAMESPACE,
79
+ labels = {},
80
+ defaults,
81
+ } = props;
82
+
83
+ const gitRepository = new GitRepositoryResource(mergeDefaults({
84
+ metadata: {
85
+ name,
86
+ namespace: fluxNamespace,
87
+ labels: {
88
+ "app.kubernetes.io/name": name,
89
+ "app.kubernetes.io/managed-by": "chant",
90
+ ...labels,
91
+ },
92
+ },
93
+ spec: {
94
+ interval,
95
+ url,
96
+ // An unset spec.ref falls back to `master` — always pin (FLUX001).
97
+ ref: tag !== undefined ? { tag } : { branch },
98
+ ...(secretRef !== undefined && { secretRef: { name: secretRef } }),
99
+ },
100
+ }, defaults?.gitRepository));
101
+
102
+ return { gitRepository };
103
+ },
104
+ "FluxGitRepository",
105
+ );
106
+
107
+ /**
108
+ * Declare a Flux `GitRepository` source in one call.
109
+ *
110
+ * Declare it once and share it: every `FluxAppFor` that reconciles a path out
111
+ * of the same repo should take this result as its `source`.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * import { FluxGitSource } from "@intentius/chant-lexicon-k8s";
116
+ *
117
+ * export const source = FluxGitSource("home-chant", {
118
+ * url: "https://github.com/jhgaylor/home-chant",
119
+ * branch: "main",
120
+ * });
121
+ * ```
122
+ */
123
+ export function FluxGitSource(name: string, options: FluxGitSourceOptions): FluxGitSourceResult {
124
+ return FluxGitRepository({ name, ...options });
125
+ }
126
+
127
+ // ── FluxAppFor ───────────────────────────────────────────────────────────────
128
+
129
+ export interface FluxAppForOptions {
130
+ /**
131
+ * The source to reconcile from — a `FluxGitSource` result, the name of an
132
+ * existing `GitRepository` (e.g. the bootstrap-created "flux-system"), or an
133
+ * explicit `{ kind, name }` reference for OCIRepository/Bucket sources.
134
+ */
135
+ source: FluxGitSourceResult | FluxSourceRef | string;
136
+ /** Path within the source holding the manifests (e.g. "./apps/api/k8s"). */
137
+ path: string;
138
+ /** Namespace the reconciled resources land in (sets spec.targetNamespace). */
139
+ targetNamespace?: string;
140
+ /** Reconcile interval (default "10m"). */
141
+ interval?: string;
142
+ /** Delete resources that disappear from the source (default true). */
143
+ prune?: boolean;
144
+ /** Wait for reconciled resources to become ready (default true). */
145
+ wait?: boolean;
146
+ /**
147
+ * Names of Kustomizations that must be ready first, as a plain name list
148
+ * (rendered to spec.dependsOn). Validated against the build by FLUX003.
149
+ */
150
+ dependsOn?: string[];
151
+ /** Timeout for health checks and apply operations. */
152
+ timeout?: string;
153
+ /** Pause reconciliation without deleting anything. */
154
+ suspend?: boolean;
155
+ /** ServiceAccount the kustomize-controller impersonates for this app. */
156
+ serviceAccountName?: string;
157
+ /** Namespace the Kustomization object itself lives in (default "flux-system"). */
158
+ fluxNamespace?: string;
159
+ /** Extra labels applied to the Kustomization. */
160
+ labels?: Record<string, string>;
161
+ defaults?: { kustomization?: Partial<Record<string, unknown>> };
162
+ }
163
+
164
+ export type FluxAppForResult = {
165
+ kustomization: InstanceType<typeof KustomizationResource>;
166
+ }
167
+
168
+ /** Resolve the `source` option to a rendered sourceRef. */
169
+ function resolveSourceRef(
170
+ target: string,
171
+ source: FluxGitSourceResult | FluxSourceRef | string,
172
+ ): Record<string, unknown> {
173
+ if (typeof source === "string") {
174
+ return { kind: "GitRepository", name: source };
175
+ }
176
+ if ("gitRepository" in source && source.gitRepository !== undefined) {
177
+ const metadata = (source.gitRepository as { props?: { metadata?: { name?: unknown; namespace?: unknown } } })
178
+ .props?.metadata;
179
+ const name = metadata?.name;
180
+ if (typeof name !== "string" || name === "") {
181
+ throw new Error(
182
+ `FluxAppFor("${target}"): the supplied FluxGitSource result has no metadata.name to reference.`,
183
+ );
184
+ }
185
+ return {
186
+ kind: "GitRepository",
187
+ name,
188
+ ...(typeof metadata?.namespace === "string" && { namespace: metadata.namespace }),
189
+ };
190
+ }
191
+ const ref = source as FluxSourceRef;
192
+ return {
193
+ kind: ref.kind ?? "GitRepository",
194
+ name: ref.name,
195
+ ...(ref.namespace !== undefined && { namespace: ref.namespace }),
196
+ };
197
+ }
198
+
199
+ const FluxKustomization = Composite<{ target: string } & FluxAppForOptions, FluxAppForResult>(
200
+ (props) => {
201
+ const {
202
+ target,
203
+ source,
204
+ path,
205
+ targetNamespace,
206
+ interval = "10m",
207
+ prune = true,
208
+ wait = true,
209
+ dependsOn = [],
210
+ timeout,
211
+ suspend,
212
+ serviceAccountName,
213
+ fluxNamespace = FLUX_NAMESPACE,
214
+ labels = {},
215
+ defaults,
216
+ } = props;
217
+
218
+ const sourceRef = resolveSourceRef(target, source);
219
+
220
+ // The Kustomization's namespace also scopes the sourceRef: an in-spec
221
+ // sourceRef without a namespace resolves in the Kustomization's own.
222
+ const kustomization = new KustomizationResource(mergeDefaults({
223
+ metadata: {
224
+ name: target,
225
+ namespace: fluxNamespace,
226
+ labels: {
227
+ "app.kubernetes.io/name": target,
228
+ "app.kubernetes.io/managed-by": "chant",
229
+ ...labels,
230
+ },
231
+ },
232
+ spec: {
233
+ interval,
234
+ path,
235
+ prune,
236
+ wait,
237
+ sourceRef,
238
+ ...(targetNamespace !== undefined && { targetNamespace }),
239
+ ...(dependsOn.length > 0 && { dependsOn: dependsOn.map((name) => ({ name })) }),
240
+ ...(timeout !== undefined && { timeout }),
241
+ ...(suspend !== undefined && { suspend }),
242
+ ...(serviceAccountName !== undefined && { serviceAccountName }),
243
+ },
244
+ }, defaults?.kustomization));
245
+
246
+ return { kustomization };
247
+ },
248
+ "FluxKustomization",
249
+ );
250
+
251
+ /**
252
+ * Reconcile a Chant build target with Flux in one call — the `Kustomization`
253
+ * half of the GitRepository + Kustomization pair. The source is passed in,
254
+ * not created here, so many apps share one repo declaration.
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * import { FluxGitSource, FluxAppFor } from "@intentius/chant-lexicon-k8s";
259
+ *
260
+ * export const source = FluxGitSource("home-chant", {
261
+ * url: "https://github.com/jhgaylor/home-chant",
262
+ * });
263
+ *
264
+ * export const hello = FluxAppFor("hello-chant", {
265
+ * source,
266
+ * path: "./apps/hello-chant/k8s",
267
+ * targetNamespace: "default",
268
+ * dependsOn: ["cert-manager", "traefik"],
269
+ * });
270
+ * ```
271
+ */
272
+ export function FluxAppFor(target: string, options: FluxAppForOptions): FluxAppForResult {
273
+ return FluxKustomization({ target, ...options });
274
+ }
@@ -93,3 +93,12 @@ export type {
93
93
  RegisterArgoClusterOptions,
94
94
  RegisterArgoClusterResult,
95
95
  } from "./argo-app";
96
+ export { FluxGitSource, FluxAppFor } from "./flux-app";
97
+ export type {
98
+ FluxSourceKind,
99
+ FluxSourceRef,
100
+ FluxGitSourceOptions,
101
+ FluxGitSourceResult,
102
+ FluxAppForOptions,
103
+ FluxAppForResult,
104
+ } from "./flux-app";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The `k8s` cluster-binding block typechecks under `satisfies ChantConfig`
3
+ * (#1455), and its schema rejects the typos that land a read on the wrong
4
+ * cluster (#1344).
5
+ *
6
+ * #1455's reproduction is the first test, verbatim: `ChantConfig` did not
7
+ * declare the `k8s` key the lexicon reads, so the one block that stops a live
8
+ * read hitting the wrong cluster was also the one block that could not
9
+ * typecheck — and the path of least resistance was dropping `satisfies
10
+ * ChantConfig`, which silently gives up checking on everything else in the
11
+ * file. The lexicon's module augmentation (declared in `./config.ts` and
12
+ * `./config-schema.ts`) is what makes it compile; this file is the forgejo
13
+ * lexicon's `config-augmentation.test.ts` guard, ported.
14
+ */
15
+
16
+ import { describe, test, expect } from "vitest";
17
+ import type { ChantConfig } from "@intentius/chant";
18
+ import type { K8sChantConfig } from "./config";
19
+ import { k8sConfigSchema } from "./config-schema";
20
+
21
+ describe("the k8s config namespace (#1455, #1344)", () => {
22
+ test("the issue's reproduction typechecks as a ChantConfig", () => {
23
+ // The compile-time assertion is the point; the runtime one keeps vitest happy.
24
+ const config = {
25
+ lexicons: ["k8s"],
26
+ environments: ["local"],
27
+ k8s: {
28
+ profiles: { local: { context: "k3d-fountain-local" } },
29
+ } satisfies K8sChantConfig,
30
+ } satisfies ChantConfig;
31
+ expect(config.k8s.profiles.local.context).toBe("k3d-fountain-local");
32
+ });
33
+
34
+ test("an empty namespace is valid — every key is optional", () => {
35
+ expect(k8sConfigSchema.safeParse({}).success).toBe(true);
36
+ });
37
+
38
+ test("the schema accepts the documented shape", () => {
39
+ const parsed = k8sConfigSchema.safeParse({
40
+ profiles: { prod: { context: "prod-eks" }, staging: { context: "staging-eks" } },
41
+ execCredentialPlugins: ["aws", "my-org-oidc-helper"],
42
+ });
43
+ expect(parsed.success).toBe(true);
44
+ });
45
+
46
+ test("an unknown key at the top of the namespace is rejected, not ignored", () => {
47
+ // Core applies `.strict()` when validating a declared namespace
48
+ // (`validateLexiconConfig`), so `profile` instead of `profiles` errors
49
+ // rather than silently leaving every environment unbound.
50
+ expect(k8sConfigSchema.strict().safeParse({ profile: { local: { context: "x" } } }).success).toBe(false);
51
+ });
52
+
53
+ test("a typo inside a profile is rejected too — the nesting is strict", () => {
54
+ // `contxt` used to be accepted and ignored, which meant the ambient
55
+ // kubectl context won and the wrong cluster was read (#1488's setup).
56
+ const parsed = k8sConfigSchema.safeParse({ profiles: { prod: { contxt: "prod-eks" } } });
57
+ expect(parsed.success).toBe(false);
58
+ });
59
+
60
+ test("a wrong value type is rejected", () => {
61
+ expect(k8sConfigSchema.safeParse({ profiles: { prod: { context: 42 } } }).success).toBe(false);
62
+ expect(k8sConfigSchema.safeParse({ execCredentialPlugins: "aws" }).success).toBe(false);
63
+ });
64
+
65
+ test("the schema and the exported interface describe the same namespace", () => {
66
+ const value: K8sChantConfig = {
67
+ profiles: { local: { context: "k3d-fountain-local" } },
68
+ execCredentialPlugins: ["aws"],
69
+ };
70
+ expect(k8sConfigSchema.safeParse(value).success).toBe(true);
71
+ });
72
+ });
@@ -23,6 +23,11 @@ export const k8sClusterProfileSchema = z.strictObject({
23
23
  export const k8sConfigSchema = z.strictObject({
24
24
  profiles: z.record(z.string(), k8sClusterProfileSchema).optional(),
25
25
  execCredentialPlugins: z.array(z.string()).optional(),
26
+ kustomize: z
27
+ .strictObject({
28
+ roots: z.array(z.string()).optional(),
29
+ })
30
+ .optional(),
26
31
  });
27
32
 
28
33
  declare module "@intentius/chant/config" {
package/src/config.ts CHANGED
@@ -29,13 +29,15 @@
29
29
  * resolves this via `@intentius/chant/kubectl-context`'s
30
30
  * `resolveClusterTarget`:
31
31
  *
32
- * - A declared binding is passed explicitly as `kubectl ... --context <bound>`
33
- * on every invocation, and checked against the ambient context first — a
34
- * mismatch refuses loudly (naming the environment, the expected context,
35
- * and the ambient one) instead of silently reading the wrong cluster.
32
+ * - A declared binding is passed explicitly to the typed client on every
33
+ * read and write, regardless of what `kubectl` is ambiently pointed at
34
+ * (#1488 — the binding is a selection, not a check). A bound context the
35
+ * kubeconfig does not have fails with an error naming the context and the
36
+ * `k8s.profiles.<env>.context` binding — never by falling back to ambient.
36
37
  * - No binding for the environment keeps today's behavior — the ambient
37
38
  * context — but logs a visible note that nothing is pinned, so the
38
- * fallback is never silent.
39
+ * fallback is never silent. Read failures name the context that was read
40
+ * and the missing binding.
39
41
  *
40
42
  * `ChantConfig` uses `.passthrough()` in its Zod schema so the `k8s` key is
41
43
  * accepted at runtime without core changes, exactly like `temporal.profiles`
@@ -83,6 +85,32 @@ export interface K8sChantConfig {
83
85
  * ```
84
86
  */
85
87
  execCredentialPlugins?: string[];
88
+
89
+ /**
90
+ * Kustomize settings (#1548 piece 3).
91
+ *
92
+ * `roots` names kustomization directories that are build roots: each is
93
+ * rendered (`kustomize build`, `kubectl kustomize` fallback) at build time
94
+ * and the rendered documents join the manifest set — serialized into the
95
+ * build output, ownership-stamped, checked by post-synth rules, and
96
+ * observed by `lifecycle diff --live` like any declared resource. This is
97
+ * how an estate that keeps its overlay tree gets a declared side without
98
+ * converting a single manifest to typed source.
99
+ *
100
+ * Paths are relative to the project root (the directory holding
101
+ * `chant.config.*`), NOT `sourceDir` — the overlay tree usually lives
102
+ * beside the typed source, not inside it.
103
+ *
104
+ * ```ts
105
+ * k8s: {
106
+ * kustomize: { roots: ["overlays/prod"] },
107
+ * } satisfies K8sChantConfig
108
+ * ```
109
+ */
110
+ kustomize?: {
111
+ /** Kustomization directories to render into the build. */
112
+ roots?: string[];
113
+ };
86
114
  }
87
115
 
88
116
  declare module "@intentius/chant/config" {