@cat-factory/app 0.49.0 → 0.49.2
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.
- package/app/components/settings/KubernetesEnvironmentForm.vue +383 -0
- package/app/components/settings/ProviderConnectionTab.vue +42 -18
- package/app/composables/api/providerConnections.ts +15 -10
- package/i18n/locales/en.json +40 -1
- package/i18n/locales/es.json +40 -1
- package/i18n/locales/fr.json +40 -1
- package/i18n/locales/pl.json +40 -1
- package/i18n/locales/uk.json +40 -1
- package/package.json +2 -2
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The Kubernetes "ephemeral environment backend" connect form — one option of the
|
|
3
|
+
// environment tab's backend-type selector (the other being the BYO HTTP manifest editor).
|
|
4
|
+
// It builds the discriminated `{ kind: 'kubernetes', kubernetes }` config plus the
|
|
5
|
+
// `apiToken` secret bundle and emits test/save to the parent tab (which calls the shared
|
|
6
|
+
// provider-connections store). The K8s env config differs from the runner K8s config
|
|
7
|
+
// (manifest source + URL derivation vs an executor image + namespace), so it has its own
|
|
8
|
+
// form rather than reusing KubernetesRunnerForm.
|
|
9
|
+
import { computed, reactive, ref, watch } from 'vue'
|
|
10
|
+
import { KUBERNETES_ENV_TOKEN_SECRET_KEY } from '@cat-factory/contracts'
|
|
11
|
+
import type { ProviderConnection } from '~/types/providerConnections'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
connection: ProviderConnection | null
|
|
15
|
+
supportsTest: boolean
|
|
16
|
+
testing: boolean
|
|
17
|
+
busy: boolean
|
|
18
|
+
testResult: { ok: boolean; message?: string } | null
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits<{
|
|
22
|
+
test: [payload: { config: Record<string, unknown>; secrets: Record<string, string> }]
|
|
23
|
+
save: [payload: { config: Record<string, unknown>; secrets: Record<string, string> }]
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
const { t } = useI18n()
|
|
27
|
+
|
|
28
|
+
const form = reactive({
|
|
29
|
+
label: '',
|
|
30
|
+
apiServerUrl: '',
|
|
31
|
+
caCertPem: '',
|
|
32
|
+
insecureSkipTlsVerify: false,
|
|
33
|
+
namespaceTemplate: '',
|
|
34
|
+
imageTemplate: '',
|
|
35
|
+
// manifest source
|
|
36
|
+
manifestSourceType: 'colocated' as 'colocated' | 'separate',
|
|
37
|
+
manifestPath: '',
|
|
38
|
+
manifestRepo: '',
|
|
39
|
+
manifestRef: '',
|
|
40
|
+
// url derivation
|
|
41
|
+
urlSource: 'ingressTemplate' as 'ingressTemplate' | 'ingressStatus' | 'serviceStatus',
|
|
42
|
+
hostTemplate: '',
|
|
43
|
+
ingressName: '',
|
|
44
|
+
serviceName: '',
|
|
45
|
+
servicePort: '',
|
|
46
|
+
urlScheme: '' as '' | 'http' | 'https',
|
|
47
|
+
})
|
|
48
|
+
const apiToken = ref('')
|
|
49
|
+
|
|
50
|
+
const manifestSourceItems = computed(() => [
|
|
51
|
+
{ label: t('settings.providerConnection.kubernetesEnv.sourceColocated'), value: 'colocated' },
|
|
52
|
+
{ label: t('settings.providerConnection.kubernetesEnv.sourceSeparate'), value: 'separate' },
|
|
53
|
+
])
|
|
54
|
+
const urlSourceItems = computed(() => [
|
|
55
|
+
{
|
|
56
|
+
label: t('settings.providerConnection.kubernetesEnv.urlIngressTemplate'),
|
|
57
|
+
value: 'ingressTemplate',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: t('settings.providerConnection.kubernetesEnv.urlIngressStatus'),
|
|
61
|
+
value: 'ingressStatus',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: t('settings.providerConnection.kubernetesEnv.urlServiceStatus'),
|
|
65
|
+
value: 'serviceStatus',
|
|
66
|
+
},
|
|
67
|
+
])
|
|
68
|
+
const schemeItems = computed(() => [
|
|
69
|
+
{ label: t('settings.providerConnection.kubernetesEnv.schemeDefault'), value: '' },
|
|
70
|
+
{ label: 'https', value: 'https' },
|
|
71
|
+
{ label: 'http', value: 'http' },
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
// A registered k8s env connection exposes its non-secret config, so prefill every
|
|
75
|
+
// non-secret field from it (never the token — secrets are write-only and re-entered on
|
|
76
|
+
// update). This lets an edit change one field without re-typing the whole form.
|
|
77
|
+
watch(
|
|
78
|
+
() => props.connection,
|
|
79
|
+
(c) => {
|
|
80
|
+
if (c?.kind !== 'kubernetes') return
|
|
81
|
+
const k =
|
|
82
|
+
c.config && (c.config as { kind?: string }).kind === 'kubernetes'
|
|
83
|
+
? (c.config as { kubernetes: Record<string, unknown> }).kubernetes
|
|
84
|
+
: undefined
|
|
85
|
+
if (!k) return
|
|
86
|
+
form.label = typeof k.label === 'string' ? k.label : ''
|
|
87
|
+
form.apiServerUrl = typeof k.apiServerUrl === 'string' ? k.apiServerUrl : ''
|
|
88
|
+
form.caCertPem = typeof k.caCertPem === 'string' ? k.caCertPem : ''
|
|
89
|
+
form.insecureSkipTlsVerify = k.insecureSkipTlsVerify === true
|
|
90
|
+
form.namespaceTemplate = typeof k.namespaceTemplate === 'string' ? k.namespaceTemplate : ''
|
|
91
|
+
form.imageTemplate = typeof k.imageTemplate === 'string' ? k.imageTemplate : ''
|
|
92
|
+
const src = k.manifestSource as Record<string, unknown> | undefined
|
|
93
|
+
if (src?.type === 'separate') {
|
|
94
|
+
form.manifestSourceType = 'separate'
|
|
95
|
+
form.manifestRepo = typeof src.repo === 'string' ? src.repo : ''
|
|
96
|
+
form.manifestRef = typeof src.ref === 'string' ? src.ref : ''
|
|
97
|
+
form.manifestPath = typeof src.path === 'string' ? src.path : ''
|
|
98
|
+
} else if (src?.type === 'colocated') {
|
|
99
|
+
form.manifestSourceType = 'colocated'
|
|
100
|
+
form.manifestPath = typeof src.path === 'string' ? src.path : ''
|
|
101
|
+
}
|
|
102
|
+
const url = k.url as Record<string, unknown> | undefined
|
|
103
|
+
if (url?.source === 'ingressTemplate') {
|
|
104
|
+
form.urlSource = 'ingressTemplate'
|
|
105
|
+
form.hostTemplate = typeof url.hostTemplate === 'string' ? url.hostTemplate : ''
|
|
106
|
+
} else if (url?.source === 'ingressStatus') {
|
|
107
|
+
form.urlSource = 'ingressStatus'
|
|
108
|
+
form.ingressName = typeof url.ingressName === 'string' ? url.ingressName : ''
|
|
109
|
+
} else if (url?.source === 'serviceStatus') {
|
|
110
|
+
form.urlSource = 'serviceStatus'
|
|
111
|
+
form.serviceName = typeof url.serviceName === 'string' ? url.serviceName : ''
|
|
112
|
+
form.servicePort = typeof url.port === 'number' ? String(url.port) : ''
|
|
113
|
+
}
|
|
114
|
+
if (url && (url.scheme === 'http' || url.scheme === 'https')) form.urlScheme = url.scheme
|
|
115
|
+
},
|
|
116
|
+
{ immediate: true },
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
// Mirror kubernetesManifestSourceSchema's `owner/repo` regex so a slashless value is
|
|
120
|
+
// caught here with a field hint instead of a generic 422 from the backend.
|
|
121
|
+
const repoShapeValid = computed(() => /^[^/\s]+\/[^/\s]+$/.test(form.manifestRepo.trim()))
|
|
122
|
+
const manifestSourceValid = computed(() =>
|
|
123
|
+
form.manifestSourceType === 'separate'
|
|
124
|
+
? repoShapeValid.value && !!form.manifestPath.trim()
|
|
125
|
+
: !!form.manifestPath.trim(),
|
|
126
|
+
)
|
|
127
|
+
// serviceStatus.port is an optional integer 1..65535 (kubernetesUrlSourceSchema). Validate
|
|
128
|
+
// it here so a decimal isn't silently dropped and an out-of-range value isn't sent then 422'd.
|
|
129
|
+
const servicePortValid = computed(() => {
|
|
130
|
+
const raw = form.servicePort.trim()
|
|
131
|
+
if (!raw) return true
|
|
132
|
+
const port = Number(raw)
|
|
133
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535
|
|
134
|
+
})
|
|
135
|
+
const urlValid = computed(() => {
|
|
136
|
+
if (form.urlSource === 'ingressTemplate') return !!form.hostTemplate.trim()
|
|
137
|
+
if (form.urlSource === 'serviceStatus') return !!form.serviceName.trim() && servicePortValid.value
|
|
138
|
+
return true // ingressStatus has no required field
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const canSave = computed(
|
|
142
|
+
() =>
|
|
143
|
+
!!form.label.trim() &&
|
|
144
|
+
!!form.apiServerUrl.trim() &&
|
|
145
|
+
!!apiToken.value.trim() &&
|
|
146
|
+
manifestSourceValid.value &&
|
|
147
|
+
urlValid.value,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
function buildManifestSource(): Record<string, unknown> {
|
|
151
|
+
if (form.manifestSourceType === 'separate') {
|
|
152
|
+
const src: Record<string, unknown> = {
|
|
153
|
+
type: 'separate',
|
|
154
|
+
repo: form.manifestRepo.trim(),
|
|
155
|
+
path: form.manifestPath.trim(),
|
|
156
|
+
}
|
|
157
|
+
if (form.manifestRef.trim()) src.ref = form.manifestRef.trim()
|
|
158
|
+
return src
|
|
159
|
+
}
|
|
160
|
+
return { type: 'colocated', path: form.manifestPath.trim() }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildUrl(): Record<string, unknown> {
|
|
164
|
+
const url: Record<string, unknown> = { source: form.urlSource }
|
|
165
|
+
if (form.urlSource === 'ingressTemplate') {
|
|
166
|
+
url.hostTemplate = form.hostTemplate.trim()
|
|
167
|
+
} else if (form.urlSource === 'ingressStatus') {
|
|
168
|
+
if (form.ingressName.trim()) url.ingressName = form.ingressName.trim()
|
|
169
|
+
} else {
|
|
170
|
+
url.serviceName = form.serviceName.trim()
|
|
171
|
+
const port = Number(form.servicePort)
|
|
172
|
+
if (form.servicePort.trim() && Number.isInteger(port)) url.port = port
|
|
173
|
+
}
|
|
174
|
+
if (form.urlScheme) url.scheme = form.urlScheme
|
|
175
|
+
return url
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildPayload(): { config: Record<string, unknown>; secrets: Record<string, string> } {
|
|
179
|
+
const kubernetes: Record<string, unknown> = {
|
|
180
|
+
label: form.label.trim(),
|
|
181
|
+
apiServerUrl: form.apiServerUrl.trim(),
|
|
182
|
+
manifestSource: buildManifestSource(),
|
|
183
|
+
url: buildUrl(),
|
|
184
|
+
}
|
|
185
|
+
if (form.caCertPem.trim()) kubernetes.caCertPem = form.caCertPem.trim()
|
|
186
|
+
if (form.insecureSkipTlsVerify) kubernetes.insecureSkipTlsVerify = true
|
|
187
|
+
if (form.namespaceTemplate.trim()) kubernetes.namespaceTemplate = form.namespaceTemplate.trim()
|
|
188
|
+
if (form.imageTemplate.trim()) kubernetes.imageTemplate = form.imageTemplate.trim()
|
|
189
|
+
return {
|
|
190
|
+
config: { kind: 'kubernetes', kubernetes },
|
|
191
|
+
secrets: { [KUBERNETES_ENV_TOKEN_SECRET_KEY]: apiToken.value.trim() },
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function optional(label: string): string {
|
|
196
|
+
return t('settings.providerConnection.form.optionalLabel', { label })
|
|
197
|
+
}
|
|
198
|
+
</script>
|
|
199
|
+
|
|
200
|
+
<template>
|
|
201
|
+
<div class="rounded-lg border border-dashed border-slate-700 p-3 space-y-3">
|
|
202
|
+
<p class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
203
|
+
{{
|
|
204
|
+
connection?.kind === 'kubernetes'
|
|
205
|
+
? t('settings.providerConnection.form.updateConfiguration')
|
|
206
|
+
: t('settings.providerConnection.form.connect')
|
|
207
|
+
}}
|
|
208
|
+
</p>
|
|
209
|
+
|
|
210
|
+
<UFormField :label="t('settings.providerConnection.kubernetesEnv.label')">
|
|
211
|
+
<UInput
|
|
212
|
+
v-model="form.label"
|
|
213
|
+
:placeholder="t('settings.providerConnection.kubernetesEnv.labelPlaceholder')"
|
|
214
|
+
/>
|
|
215
|
+
</UFormField>
|
|
216
|
+
|
|
217
|
+
<UFormField
|
|
218
|
+
:label="t('settings.providerConnection.kubernetesEnv.apiServerUrl')"
|
|
219
|
+
:help="t('settings.providerConnection.kubernetesEnv.apiServerUrlHelp')"
|
|
220
|
+
>
|
|
221
|
+
<UInput v-model="form.apiServerUrl" class="font-mono" placeholder="https://10.0.0.1:6443" />
|
|
222
|
+
</UFormField>
|
|
223
|
+
|
|
224
|
+
<UFormField
|
|
225
|
+
:label="t('settings.providerConnection.kubernetesEnv.apiToken')"
|
|
226
|
+
:help="t('settings.providerConnection.kubernetesEnv.apiTokenHelp')"
|
|
227
|
+
>
|
|
228
|
+
<UInput v-model="apiToken" type="password" class="font-mono" />
|
|
229
|
+
</UFormField>
|
|
230
|
+
|
|
231
|
+
<!-- Manifest source: where the per-PR resources are read from. -->
|
|
232
|
+
<UFormField :label="t('settings.providerConnection.kubernetesEnv.manifestSourceLabel')">
|
|
233
|
+
<USelect v-model="form.manifestSourceType" :items="manifestSourceItems" />
|
|
234
|
+
</UFormField>
|
|
235
|
+
|
|
236
|
+
<UFormField
|
|
237
|
+
v-if="form.manifestSourceType === 'separate'"
|
|
238
|
+
:label="t('settings.providerConnection.kubernetesEnv.repo')"
|
|
239
|
+
:help="t('settings.providerConnection.kubernetesEnv.repoHelp')"
|
|
240
|
+
>
|
|
241
|
+
<UInput v-model="form.manifestRepo" class="font-mono" placeholder="acme/preview-manifests" />
|
|
242
|
+
</UFormField>
|
|
243
|
+
|
|
244
|
+
<UFormField
|
|
245
|
+
v-if="form.manifestSourceType === 'separate'"
|
|
246
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.ref'))"
|
|
247
|
+
:help="t('settings.providerConnection.kubernetesEnv.refHelp')"
|
|
248
|
+
>
|
|
249
|
+
<UInput v-model="form.manifestRef" class="font-mono" placeholder="main" />
|
|
250
|
+
</UFormField>
|
|
251
|
+
|
|
252
|
+
<UFormField
|
|
253
|
+
:label="t('settings.providerConnection.kubernetesEnv.path')"
|
|
254
|
+
:help="t('settings.providerConnection.kubernetesEnv.pathHelp')"
|
|
255
|
+
>
|
|
256
|
+
<UInput v-model="form.manifestPath" class="font-mono" placeholder="k8s/preview" />
|
|
257
|
+
</UFormField>
|
|
258
|
+
|
|
259
|
+
<!-- URL derivation: how the live environment URL is resolved once applied. -->
|
|
260
|
+
<UFormField :label="t('settings.providerConnection.kubernetesEnv.urlSourceLabel')">
|
|
261
|
+
<USelect v-model="form.urlSource" :items="urlSourceItems" />
|
|
262
|
+
</UFormField>
|
|
263
|
+
|
|
264
|
+
<UFormField
|
|
265
|
+
v-if="form.urlSource === 'ingressTemplate'"
|
|
266
|
+
:label="t('settings.providerConnection.kubernetesEnv.hostTemplate')"
|
|
267
|
+
:help="t('settings.providerConnection.kubernetesEnv.hostTemplateHelp')"
|
|
268
|
+
>
|
|
269
|
+
<UInput
|
|
270
|
+
v-model="form.hostTemplate"
|
|
271
|
+
class="font-mono"
|
|
272
|
+
placeholder="{{branch}}.preview.example.com"
|
|
273
|
+
/>
|
|
274
|
+
</UFormField>
|
|
275
|
+
|
|
276
|
+
<UFormField
|
|
277
|
+
v-if="form.urlSource === 'ingressStatus'"
|
|
278
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.ingressName'))"
|
|
279
|
+
:help="t('settings.providerConnection.kubernetesEnv.ingressNameHelp')"
|
|
280
|
+
>
|
|
281
|
+
<UInput v-model="form.ingressName" class="font-mono" />
|
|
282
|
+
</UFormField>
|
|
283
|
+
|
|
284
|
+
<UFormField
|
|
285
|
+
v-if="form.urlSource === 'serviceStatus'"
|
|
286
|
+
:label="t('settings.providerConnection.kubernetesEnv.serviceName')"
|
|
287
|
+
>
|
|
288
|
+
<UInput v-model="form.serviceName" class="font-mono" />
|
|
289
|
+
</UFormField>
|
|
290
|
+
|
|
291
|
+
<UFormField
|
|
292
|
+
v-if="form.urlSource === 'serviceStatus'"
|
|
293
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.port'))"
|
|
294
|
+
>
|
|
295
|
+
<UInput
|
|
296
|
+
v-model="form.servicePort"
|
|
297
|
+
type="number"
|
|
298
|
+
:min="1"
|
|
299
|
+
:max="65535"
|
|
300
|
+
class="font-mono"
|
|
301
|
+
placeholder="80"
|
|
302
|
+
/>
|
|
303
|
+
</UFormField>
|
|
304
|
+
|
|
305
|
+
<UFormField :label="optional(t('settings.providerConnection.kubernetesEnv.scheme'))">
|
|
306
|
+
<USelect v-model="form.urlScheme" :items="schemeItems" />
|
|
307
|
+
</UFormField>
|
|
308
|
+
|
|
309
|
+
<!-- Optional refinements. -->
|
|
310
|
+
<UFormField
|
|
311
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.namespaceTemplate'))"
|
|
312
|
+
:help="t('settings.providerConnection.kubernetesEnv.namespaceTemplateHelp')"
|
|
313
|
+
>
|
|
314
|
+
<UInput
|
|
315
|
+
v-model="form.namespaceTemplate"
|
|
316
|
+
class="font-mono"
|
|
317
|
+
placeholder="cf-env-{{pullNumber}}"
|
|
318
|
+
/>
|
|
319
|
+
</UFormField>
|
|
320
|
+
|
|
321
|
+
<UFormField
|
|
322
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.imageTemplate'))"
|
|
323
|
+
:help="t('settings.providerConnection.kubernetesEnv.imageTemplateHelp')"
|
|
324
|
+
>
|
|
325
|
+
<UInput v-model="form.imageTemplate" class="font-mono" />
|
|
326
|
+
</UFormField>
|
|
327
|
+
|
|
328
|
+
<UFormField
|
|
329
|
+
:label="optional(t('settings.providerConnection.kubernetesEnv.caCertPem'))"
|
|
330
|
+
:help="t('settings.providerConnection.kubernetesEnv.caCertPemHelp')"
|
|
331
|
+
>
|
|
332
|
+
<UTextarea
|
|
333
|
+
v-model="form.caCertPem"
|
|
334
|
+
:rows="3"
|
|
335
|
+
class="font-mono"
|
|
336
|
+
placeholder="-----BEGIN CERTIFICATE-----"
|
|
337
|
+
/>
|
|
338
|
+
</UFormField>
|
|
339
|
+
|
|
340
|
+
<UFormField :help="t('settings.providerConnection.kubernetesEnv.insecureSkipTlsVerifyHelp')">
|
|
341
|
+
<UCheckbox
|
|
342
|
+
v-model="form.insecureSkipTlsVerify"
|
|
343
|
+
:label="t('settings.providerConnection.kubernetesEnv.insecureSkipTlsVerify')"
|
|
344
|
+
/>
|
|
345
|
+
</UFormField>
|
|
346
|
+
|
|
347
|
+
<div v-if="supportsTest" class="flex items-center gap-2">
|
|
348
|
+
<UButton
|
|
349
|
+
color="neutral"
|
|
350
|
+
variant="soft"
|
|
351
|
+
size="sm"
|
|
352
|
+
icon="i-lucide-plug-zap"
|
|
353
|
+
:loading="testing"
|
|
354
|
+
:disabled="!canSave"
|
|
355
|
+
@click="emit('test', buildPayload())"
|
|
356
|
+
>
|
|
357
|
+
{{ t('settings.providerConnection.test.button') }}
|
|
358
|
+
</UButton>
|
|
359
|
+
<span v-if="testResult && testResult.ok" class="text-xs text-emerald-400">
|
|
360
|
+
{{ testResult.message ?? t('settings.providerConnection.test.ok') }}
|
|
361
|
+
</span>
|
|
362
|
+
<span v-else-if="testResult" class="text-xs text-rose-400">
|
|
363
|
+
{{ testResult.message ?? t('settings.providerConnection.test.failed') }}
|
|
364
|
+
</span>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<div class="flex justify-end">
|
|
368
|
+
<UButton
|
|
369
|
+
color="primary"
|
|
370
|
+
size="sm"
|
|
371
|
+
:loading="busy"
|
|
372
|
+
:disabled="!canSave"
|
|
373
|
+
@click="emit('save', buildPayload())"
|
|
374
|
+
>
|
|
375
|
+
{{
|
|
376
|
+
connection?.kind === 'kubernetes'
|
|
377
|
+
? t('common.save')
|
|
378
|
+
: t('settings.providerConnection.form.connect')
|
|
379
|
+
}}
|
|
380
|
+
</UButton>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</template>
|
|
@@ -11,6 +11,7 @@ import type { ProviderConnectionKind } from '~/types/providerConnections'
|
|
|
11
11
|
import ProvisioningLogsDrawer from '~/components/provisioning/ProvisioningLogsDrawer.vue'
|
|
12
12
|
import ProviderManifestEditor from '~/components/settings/ProviderManifestEditor.vue'
|
|
13
13
|
import KubernetesRunnerForm from '~/components/settings/KubernetesRunnerForm.vue'
|
|
14
|
+
import KubernetesEnvironmentForm from '~/components/settings/KubernetesEnvironmentForm.vue'
|
|
14
15
|
|
|
15
16
|
const props = defineProps<{ kind: ProviderConnectionKind }>()
|
|
16
17
|
|
|
@@ -185,18 +186,32 @@ async function saveManifest(payload: {
|
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
// ---
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
189
|
+
// --- Backend selector -----------------------------------------------------------------
|
|
190
|
+
// Both infrastructure tabs can configure either the BYO manifest backend OR a native
|
|
191
|
+
// Kubernetes backend (a runner cluster for runner-pool, per-PR namespaces for environment).
|
|
192
|
+
// The two K8s backends have different config shapes, so each kind renders its own form.
|
|
193
|
+
// Defaults to the saved connection's kind.
|
|
194
|
+
const BACKEND_KINDS = ['manifest', 'kubernetes'] as const
|
|
195
|
+
type BackendKind = (typeof BACKEND_KINDS)[number]
|
|
196
|
+
const backendKind = ref<BackendKind>('manifest')
|
|
197
|
+
const showBackendSelector = computed(() => true)
|
|
198
|
+
const backendSelectorLabel = computed(() =>
|
|
199
|
+
t(
|
|
200
|
+
props.kind === 'environment'
|
|
201
|
+
? 'settings.providerConnection.backend.environmentSelectorLabel'
|
|
202
|
+
: 'settings.providerConnection.backend.selectorLabel',
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
function backendKindLabel(k: BackendKind): string {
|
|
206
|
+
if (k === 'kubernetes') return t('settings.providerConnection.backend.kubernetes')
|
|
207
|
+
return t(
|
|
208
|
+
props.kind === 'environment'
|
|
209
|
+
? 'settings.providerConnection.backend.environmentManifest'
|
|
210
|
+
: 'settings.providerConnection.backend.manifest',
|
|
211
|
+
)
|
|
212
|
+
}
|
|
195
213
|
const backendKindItems = computed(() =>
|
|
196
|
-
|
|
197
|
-
label: t(`settings.providerConnection.backend.${k}`),
|
|
198
|
-
value: k,
|
|
199
|
-
})),
|
|
214
|
+
BACKEND_KINDS.map((k) => ({ label: backendKindLabel(k), value: k })),
|
|
200
215
|
)
|
|
201
216
|
watch(
|
|
202
217
|
() => connection.value,
|
|
@@ -317,17 +332,26 @@ function fieldHelp(key: string): string | undefined {
|
|
|
317
332
|
}}
|
|
318
333
|
</div>
|
|
319
334
|
|
|
320
|
-
<!--
|
|
321
|
-
<UFormField
|
|
322
|
-
v-if="showBackendSelector"
|
|
323
|
-
:label="t('settings.providerConnection.backend.selectorLabel')"
|
|
324
|
-
>
|
|
335
|
+
<!-- Backend selector: the BYO manifest backend or a native Kubernetes backend. -->
|
|
336
|
+
<UFormField v-if="showBackendSelector" :label="backendSelectorLabel">
|
|
325
337
|
<USelect v-model="backendKind" :items="backendKindItems" />
|
|
326
338
|
</UFormField>
|
|
327
339
|
|
|
328
|
-
<!-- Native Kubernetes runner backend. -->
|
|
340
|
+
<!-- Native Kubernetes runner backend (runner-pool). -->
|
|
329
341
|
<KubernetesRunnerForm
|
|
330
|
-
v-if="
|
|
342
|
+
v-if="kind === 'runner-pool' && backendKind === 'kubernetes'"
|
|
343
|
+
:connection="connection"
|
|
344
|
+
:supports-test="descriptor.supportsTest"
|
|
345
|
+
:testing="testing"
|
|
346
|
+
:busy="busy"
|
|
347
|
+
:test-result="testResult"
|
|
348
|
+
@test="testConfig"
|
|
349
|
+
@save="saveConfig"
|
|
350
|
+
/>
|
|
351
|
+
|
|
352
|
+
<!-- Native Kubernetes ephemeral-environment backend (environment). -->
|
|
353
|
+
<KubernetesEnvironmentForm
|
|
354
|
+
v-else-if="kind === 'environment' && backendKind === 'kubernetes'"
|
|
331
355
|
:connection="connection"
|
|
332
356
|
:supports-test="descriptor.supportsTest"
|
|
333
357
|
:testing="testing"
|
|
@@ -70,12 +70,15 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
|
|
|
70
70
|
kind === 'environment'
|
|
71
71
|
? send(CONTRACTS.environment.register, {
|
|
72
72
|
pathPrefix: ws(workspaceId),
|
|
73
|
-
body:
|
|
73
|
+
body: {
|
|
74
|
+
config: backendConfig(body),
|
|
75
|
+
secrets: body.secrets,
|
|
76
|
+
} as RegisterEnvironmentProviderInput,
|
|
74
77
|
})
|
|
75
78
|
: send(CONTRACTS['runner-pool'].register, {
|
|
76
79
|
pathPrefix: ws(workspaceId),
|
|
77
80
|
body: {
|
|
78
|
-
config:
|
|
81
|
+
config: backendConfig(body),
|
|
79
82
|
secrets: body.secrets,
|
|
80
83
|
} as RegisterRunnerPoolInput,
|
|
81
84
|
}),
|
|
@@ -94,12 +97,15 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
|
|
|
94
97
|
kind === 'environment'
|
|
95
98
|
? send(CONTRACTS.environment.test, {
|
|
96
99
|
pathPrefix: ws(workspaceId),
|
|
97
|
-
body:
|
|
100
|
+
body: {
|
|
101
|
+
...(body.manifest || body.config ? { config: backendConfig(body) } : {}),
|
|
102
|
+
...(body.secrets ? { secrets: body.secrets } : {}),
|
|
103
|
+
} as TestEnvironmentConnectionInput,
|
|
98
104
|
})
|
|
99
105
|
: send(CONTRACTS['runner-pool'].test, {
|
|
100
106
|
pathPrefix: ws(workspaceId),
|
|
101
107
|
body: {
|
|
102
|
-
...(body.manifest || body.config ? { config:
|
|
108
|
+
...(body.manifest || body.config ? { config: backendConfig(body) } : {}),
|
|
103
109
|
...(body.secrets ? { secrets: body.secrets } : {}),
|
|
104
110
|
} as TestRunnerPoolConnectionInput,
|
|
105
111
|
}),
|
|
@@ -110,13 +116,12 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
|
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
/**
|
|
113
|
-
* Resolve the discriminated
|
|
114
|
-
* explicit `config` (the Kubernetes form) wins; otherwise a bare `manifest` (the
|
|
115
|
-
* editor) is wrapped into the manifest backend kind.
|
|
119
|
+
* Resolve the discriminated backend config (runner-pool OR environment) from a connect-form
|
|
120
|
+
* payload: an explicit `config` (the Kubernetes form) wins; otherwise a bare `manifest` (the
|
|
121
|
+
* manifest editor) is wrapped into the `manifest` backend kind. Both subsystems now take a
|
|
122
|
+
* discriminated `config`, so the same shape serves each.
|
|
116
123
|
*/
|
|
117
|
-
function
|
|
118
|
-
body: RegisterProviderInput | TestProviderInput,
|
|
119
|
-
): Record<string, unknown> {
|
|
124
|
+
function backendConfig(body: RegisterProviderInput | TestProviderInput): Record<string, unknown> {
|
|
120
125
|
if (body.config) return body.config
|
|
121
126
|
return { kind: 'manifest', manifest: body.manifest ?? {} }
|
|
122
127
|
}
|
package/i18n/locales/en.json
CHANGED
|
@@ -1280,7 +1280,46 @@
|
|
|
1280
1280
|
"backend": {
|
|
1281
1281
|
"selectorLabel": "Runner backend",
|
|
1282
1282
|
"manifest": "Self-hosted pool (manifest)",
|
|
1283
|
-
"kubernetes": "Kubernetes"
|
|
1283
|
+
"kubernetes": "Kubernetes",
|
|
1284
|
+
"environmentSelectorLabel": "Environment backend",
|
|
1285
|
+
"environmentManifest": "Custom HTTP API (manifest)"
|
|
1286
|
+
},
|
|
1287
|
+
"kubernetesEnv": {
|
|
1288
|
+
"label": "Name",
|
|
1289
|
+
"labelPlaceholder": "Preview cluster",
|
|
1290
|
+
"apiServerUrl": "API server URL",
|
|
1291
|
+
"apiServerUrlHelp": "The kube-apiserver root, e.g. https://10.0.0.1:6443. The orchestrator applies each PR's manifests through the apiserver, so only this endpoint must be reachable.",
|
|
1292
|
+
"apiToken": "ServiceAccount token",
|
|
1293
|
+
"apiTokenHelp": "A bearer token with RBAC to create namespaces and apply the operator's resources. Stored encrypted; never shown again.",
|
|
1294
|
+
"manifestSourceLabel": "Manifest source",
|
|
1295
|
+
"sourceColocated": "Co-located in the PR repo",
|
|
1296
|
+
"sourceSeparate": "Separate repo",
|
|
1297
|
+
"repo": "Manifests repo",
|
|
1298
|
+
"repoHelp": "The owner/repo that holds the Kubernetes manifests, e.g. acme/preview-manifests.",
|
|
1299
|
+
"ref": "Ref",
|
|
1300
|
+
"refHelp": "Branch, tag or SHA to read the manifests at. Empty uses the repo's default branch.",
|
|
1301
|
+
"path": "Manifest path",
|
|
1302
|
+
"pathHelp": "File or directory within the repo holding the resources to apply.",
|
|
1303
|
+
"urlSourceLabel": "Environment URL",
|
|
1304
|
+
"urlIngressTemplate": "Ingress host template",
|
|
1305
|
+
"urlIngressStatus": "Read Ingress status",
|
|
1306
|
+
"urlServiceStatus": "Read Service status",
|
|
1307
|
+
"hostTemplate": "Host template",
|
|
1308
|
+
"hostTemplateHelp": "Host template rendered with the provision vars such as branch and pullNumber; the rendered host becomes the environment URL.",
|
|
1309
|
+
"ingressName": "Ingress name",
|
|
1310
|
+
"ingressNameHelp": "Ingress to read the load-balancer host from. Empty uses the only Ingress applied.",
|
|
1311
|
+
"serviceName": "Service name",
|
|
1312
|
+
"port": "Port",
|
|
1313
|
+
"scheme": "URL scheme",
|
|
1314
|
+
"schemeDefault": "https (default)",
|
|
1315
|
+
"namespaceTemplate": "Namespace template",
|
|
1316
|
+
"namespaceTemplateHelp": "Per-PR namespace name rendered from the provision vars such as pullNumber. Empty derives one from the PR number.",
|
|
1317
|
+
"imageTemplate": "Image template",
|
|
1318
|
+
"imageTemplateHelp": "Image reference exposed to the manifests, rendered over the provision vars such as branch and sha.",
|
|
1319
|
+
"caCertPem": "Cluster CA certificate (PEM)",
|
|
1320
|
+
"caCertPemHelp": "Paste the cluster CA bundle so the apiserver's TLS certificate verifies. Omit only for a publicly-trusted CA.",
|
|
1321
|
+
"insecureSkipTlsVerify": "Skip TLS verification",
|
|
1322
|
+
"insecureSkipTlsVerifyHelp": "Strongly discouraged. Disables apiserver TLS verification; use only for kind/dev clusters."
|
|
1284
1323
|
},
|
|
1285
1324
|
"kubernetes": {
|
|
1286
1325
|
"label": "Name",
|
package/i18n/locales/es.json
CHANGED
|
@@ -1288,7 +1288,46 @@
|
|
|
1288
1288
|
"backend": {
|
|
1289
1289
|
"selectorLabel": "Backend de ejecución",
|
|
1290
1290
|
"manifest": "Pool autohospedado (manifiesto)",
|
|
1291
|
-
"kubernetes": "Kubernetes"
|
|
1291
|
+
"kubernetes": "Kubernetes",
|
|
1292
|
+
"environmentSelectorLabel": "Backend de entorno",
|
|
1293
|
+
"environmentManifest": "API HTTP personalizada (manifiesto)"
|
|
1294
|
+
},
|
|
1295
|
+
"kubernetesEnv": {
|
|
1296
|
+
"label": "Nombre",
|
|
1297
|
+
"labelPlaceholder": "Clúster de vista previa",
|
|
1298
|
+
"apiServerUrl": "URL del API server",
|
|
1299
|
+
"apiServerUrlHelp": "La raíz del kube-apiserver, p. ej. https://10.0.0.1:6443. El orquestador aplica los manifiestos de cada PR a través del apiserver, así que solo este endpoint debe ser accesible.",
|
|
1300
|
+
"apiToken": "Token de ServiceAccount",
|
|
1301
|
+
"apiTokenHelp": "Un token bearer con permisos RBAC para crear namespaces y aplicar los recursos del operador. Se almacena cifrado; no se vuelve a mostrar.",
|
|
1302
|
+
"manifestSourceLabel": "Origen de los manifiestos",
|
|
1303
|
+
"sourceColocated": "En el mismo repo de la PR",
|
|
1304
|
+
"sourceSeparate": "Repo aparte",
|
|
1305
|
+
"repo": "Repo de manifiestos",
|
|
1306
|
+
"repoHelp": "El owner/repo que contiene los manifiestos de Kubernetes, p. ej. acme/preview-manifests.",
|
|
1307
|
+
"ref": "Ref",
|
|
1308
|
+
"refHelp": "Rama, etiqueta o SHA donde leer los manifiestos. Vacío usa la rama por defecto del repo.",
|
|
1309
|
+
"path": "Ruta del manifiesto",
|
|
1310
|
+
"pathHelp": "Archivo o directorio dentro del repo que contiene los recursos a aplicar.",
|
|
1311
|
+
"urlSourceLabel": "URL del entorno",
|
|
1312
|
+
"urlIngressTemplate": "Plantilla de host del Ingress",
|
|
1313
|
+
"urlIngressStatus": "Leer estado del Ingress",
|
|
1314
|
+
"urlServiceStatus": "Leer estado del Service",
|
|
1315
|
+
"hostTemplate": "Plantilla de host",
|
|
1316
|
+
"hostTemplateHelp": "Plantilla de host renderizada con las variables de aprovisionamiento como branch y pullNumber; el host resultante se convierte en la URL del entorno.",
|
|
1317
|
+
"ingressName": "Nombre del Ingress",
|
|
1318
|
+
"ingressNameHelp": "Ingress del que leer el host del balanceador. Vacío usa el único Ingress aplicado.",
|
|
1319
|
+
"serviceName": "Nombre del Service",
|
|
1320
|
+
"port": "Puerto",
|
|
1321
|
+
"scheme": "Esquema de URL",
|
|
1322
|
+
"schemeDefault": "https (por defecto)",
|
|
1323
|
+
"namespaceTemplate": "Plantilla de namespace",
|
|
1324
|
+
"namespaceTemplateHelp": "Nombre del namespace por PR renderizado con las variables de aprovisionamiento como pullNumber. Vacío deriva uno del número de la PR.",
|
|
1325
|
+
"imageTemplate": "Plantilla de imagen",
|
|
1326
|
+
"imageTemplateHelp": "Referencia de imagen expuesta a los manifiestos, renderizada con las variables de aprovisionamiento como branch y sha.",
|
|
1327
|
+
"caCertPem": "Certificado CA del clúster (PEM)",
|
|
1328
|
+
"caCertPemHelp": "Pega el bundle CA del clúster para que el certificado TLS del apiserver se verifique. Omítelo solo para una CA de confianza pública.",
|
|
1329
|
+
"insecureSkipTlsVerify": "Omitir verificación TLS",
|
|
1330
|
+
"insecureSkipTlsVerifyHelp": "Muy desaconsejado. Desactiva la verificación TLS del apiserver; úsalo solo para clústeres kind/dev."
|
|
1292
1331
|
},
|
|
1293
1332
|
"kubernetes": {
|
|
1294
1333
|
"label": "Nombre",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1288,7 +1288,46 @@
|
|
|
1288
1288
|
"backend": {
|
|
1289
1289
|
"selectorLabel": "Backend d'exécution",
|
|
1290
1290
|
"manifest": "Pool auto-hébergé (manifeste)",
|
|
1291
|
-
"kubernetes": "Kubernetes"
|
|
1291
|
+
"kubernetes": "Kubernetes",
|
|
1292
|
+
"environmentSelectorLabel": "Backend d'environnement",
|
|
1293
|
+
"environmentManifest": "API HTTP personnalisée (manifeste)"
|
|
1294
|
+
},
|
|
1295
|
+
"kubernetesEnv": {
|
|
1296
|
+
"label": "Nom",
|
|
1297
|
+
"labelPlaceholder": "Cluster de prévisualisation",
|
|
1298
|
+
"apiServerUrl": "URL du serveur d'API",
|
|
1299
|
+
"apiServerUrlHelp": "La racine du kube-apiserver, p. ex. https://10.0.0.1:6443. L'orchestrateur applique les manifestes de chaque PR via l'apiserver, donc seul ce point d'accès doit être accessible.",
|
|
1300
|
+
"apiToken": "Jeton de ServiceAccount",
|
|
1301
|
+
"apiTokenHelp": "Un jeton bearer avec les droits RBAC pour créer des namespaces et appliquer les ressources de l'opérateur. Stocké chiffré ; jamais réaffiché.",
|
|
1302
|
+
"manifestSourceLabel": "Source des manifestes",
|
|
1303
|
+
"sourceColocated": "Dans le dépôt de la PR",
|
|
1304
|
+
"sourceSeparate": "Dépôt séparé",
|
|
1305
|
+
"repo": "Dépôt des manifestes",
|
|
1306
|
+
"repoHelp": "Le owner/repo qui contient les manifestes Kubernetes, p. ex. acme/preview-manifests.",
|
|
1307
|
+
"ref": "Réf",
|
|
1308
|
+
"refHelp": "Branche, tag ou SHA où lire les manifestes. Vide utilise la branche par défaut du dépôt.",
|
|
1309
|
+
"path": "Chemin du manifeste",
|
|
1310
|
+
"pathHelp": "Fichier ou répertoire du dépôt contenant les ressources à appliquer.",
|
|
1311
|
+
"urlSourceLabel": "URL de l'environnement",
|
|
1312
|
+
"urlIngressTemplate": "Modèle d'hôte Ingress",
|
|
1313
|
+
"urlIngressStatus": "Lire l'état de l'Ingress",
|
|
1314
|
+
"urlServiceStatus": "Lire l'état du Service",
|
|
1315
|
+
"hostTemplate": "Modèle d'hôte",
|
|
1316
|
+
"hostTemplateHelp": "Modèle d'hôte rendu avec les variables de provisionnement telles que branch et pullNumber ; l'hôte obtenu devient l'URL de l'environnement.",
|
|
1317
|
+
"ingressName": "Nom de l'Ingress",
|
|
1318
|
+
"ingressNameHelp": "Ingress dont lire l'hôte du load-balancer. Vide utilise le seul Ingress appliqué.",
|
|
1319
|
+
"serviceName": "Nom du Service",
|
|
1320
|
+
"port": "Port",
|
|
1321
|
+
"scheme": "Schéma d'URL",
|
|
1322
|
+
"schemeDefault": "https (par défaut)",
|
|
1323
|
+
"namespaceTemplate": "Modèle de namespace",
|
|
1324
|
+
"namespaceTemplateHelp": "Nom du namespace par PR rendu avec les variables de provisionnement telles que pullNumber. Vide en dérive un à partir du numéro de la PR.",
|
|
1325
|
+
"imageTemplate": "Modèle d'image",
|
|
1326
|
+
"imageTemplateHelp": "Référence d'image exposée aux manifestes, rendue avec les variables de provisionnement telles que branch et sha.",
|
|
1327
|
+
"caCertPem": "Certificat CA du cluster (PEM)",
|
|
1328
|
+
"caCertPemHelp": "Collez le bundle CA du cluster pour que le certificat TLS de l'apiserver soit vérifié. À omettre uniquement pour une CA publiquement approuvée.",
|
|
1329
|
+
"insecureSkipTlsVerify": "Ignorer la vérification TLS",
|
|
1330
|
+
"insecureSkipTlsVerifyHelp": "Fortement déconseillé. Désactive la vérification TLS de l'apiserver ; à utiliser uniquement pour des clusters kind/dev."
|
|
1292
1331
|
},
|
|
1293
1332
|
"kubernetes": {
|
|
1294
1333
|
"label": "Nom",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -1288,7 +1288,46 @@
|
|
|
1288
1288
|
"backend": {
|
|
1289
1289
|
"selectorLabel": "Backend wykonawczy",
|
|
1290
1290
|
"manifest": "Własny pool (manifest)",
|
|
1291
|
-
"kubernetes": "Kubernetes"
|
|
1291
|
+
"kubernetes": "Kubernetes",
|
|
1292
|
+
"environmentSelectorLabel": "Backend środowiska",
|
|
1293
|
+
"environmentManifest": "Własne API HTTP (manifest)"
|
|
1294
|
+
},
|
|
1295
|
+
"kubernetesEnv": {
|
|
1296
|
+
"label": "Nazwa",
|
|
1297
|
+
"labelPlaceholder": "Klaster podglądu",
|
|
1298
|
+
"apiServerUrl": "URL serwera API",
|
|
1299
|
+
"apiServerUrlHelp": "Główny adres kube-apiserver, np. https://10.0.0.1:6443. Orkiestrator stosuje manifesty każdego PR przez apiserver, więc tylko ten endpoint musi być osiągalny.",
|
|
1300
|
+
"apiToken": "Token ServiceAccount",
|
|
1301
|
+
"apiTokenHelp": "Token bearer z uprawnieniami RBAC do tworzenia namespace’ów i stosowania zasobów operatora. Przechowywany w postaci zaszyfrowanej; nie jest ponownie pokazywany.",
|
|
1302
|
+
"manifestSourceLabel": "Źródło manifestów",
|
|
1303
|
+
"sourceColocated": "W repozytorium PR",
|
|
1304
|
+
"sourceSeparate": "Osobne repozytorium",
|
|
1305
|
+
"repo": "Repozytorium manifestów",
|
|
1306
|
+
"repoHelp": "Owner/repo zawierające manifesty Kubernetes, np. acme/preview-manifests.",
|
|
1307
|
+
"ref": "Ref",
|
|
1308
|
+
"refHelp": "Gałąź, tag lub SHA, z którego czytać manifesty. Puste używa domyślnej gałęzi repozytorium.",
|
|
1309
|
+
"path": "Ścieżka manifestu",
|
|
1310
|
+
"pathHelp": "Plik lub katalog w repozytorium zawierający zasoby do zastosowania.",
|
|
1311
|
+
"urlSourceLabel": "URL środowiska",
|
|
1312
|
+
"urlIngressTemplate": "Szablon hosta Ingress",
|
|
1313
|
+
"urlIngressStatus": "Odczytaj stan Ingress",
|
|
1314
|
+
"urlServiceStatus": "Odczytaj stan Service",
|
|
1315
|
+
"hostTemplate": "Szablon hosta",
|
|
1316
|
+
"hostTemplateHelp": "Szablon hosta renderowany ze zmiennymi provisioningu, takimi jak branch i pullNumber; wynikowy host staje się URL-em środowiska.",
|
|
1317
|
+
"ingressName": "Nazwa Ingress",
|
|
1318
|
+
"ingressNameHelp": "Ingress, z którego odczytać host load-balancera. Puste używa jedynego zastosowanego Ingress.",
|
|
1319
|
+
"serviceName": "Nazwa Service",
|
|
1320
|
+
"port": "Port",
|
|
1321
|
+
"scheme": "Schemat URL",
|
|
1322
|
+
"schemeDefault": "https (domyślnie)",
|
|
1323
|
+
"namespaceTemplate": "Szablon namespace",
|
|
1324
|
+
"namespaceTemplateHelp": "Nazwa namespace per PR renderowana ze zmiennych provisioningu, takich jak pullNumber. Puste wyprowadza ją z numeru PR.",
|
|
1325
|
+
"imageTemplate": "Szablon obrazu",
|
|
1326
|
+
"imageTemplateHelp": "Referencja obrazu udostępniana manifestom, renderowana ze zmiennych provisioningu, takich jak branch i sha.",
|
|
1327
|
+
"caCertPem": "Certyfikat CA klastra (PEM)",
|
|
1328
|
+
"caCertPemHelp": "Wklej pakiet CA klastra, aby certyfikat TLS apiservera został zweryfikowany. Pomiń tylko dla publicznie zaufanego CA.",
|
|
1329
|
+
"insecureSkipTlsVerify": "Pomiń weryfikację TLS",
|
|
1330
|
+
"insecureSkipTlsVerifyHelp": "Zdecydowanie odradzane. Wyłącza weryfikację TLS apiservera; używaj tylko dla klastrów kind/dev."
|
|
1292
1331
|
},
|
|
1293
1332
|
"kubernetes": {
|
|
1294
1333
|
"label": "Nazwa",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1288,7 +1288,46 @@
|
|
|
1288
1288
|
"backend": {
|
|
1289
1289
|
"selectorLabel": "Бекенд виконавця",
|
|
1290
1290
|
"manifest": "Власний пул (маніфест)",
|
|
1291
|
-
"kubernetes": "Kubernetes"
|
|
1291
|
+
"kubernetes": "Kubernetes",
|
|
1292
|
+
"environmentSelectorLabel": "Бекенд середовища",
|
|
1293
|
+
"environmentManifest": "Власний HTTP API (маніфест)"
|
|
1294
|
+
},
|
|
1295
|
+
"kubernetesEnv": {
|
|
1296
|
+
"label": "Назва",
|
|
1297
|
+
"labelPlaceholder": "Кластер попереднього перегляду",
|
|
1298
|
+
"apiServerUrl": "URL сервера API",
|
|
1299
|
+
"apiServerUrlHelp": "Кореневий адрес kube-apiserver, напр. https://10.0.0.1:6443. Оркестратор застосовує маніфести кожного PR через apiserver, тож лише цей endpoint має бути доступним.",
|
|
1300
|
+
"apiToken": "Токен ServiceAccount",
|
|
1301
|
+
"apiTokenHelp": "Bearer-токен з правами RBAC на створення просторів імен і застосування ресурсів оператора. Зберігається зашифрованим; більше не показується.",
|
|
1302
|
+
"manifestSourceLabel": "Джерело маніфестів",
|
|
1303
|
+
"sourceColocated": "У репозиторії PR",
|
|
1304
|
+
"sourceSeparate": "Окремий репозиторій",
|
|
1305
|
+
"repo": "Репозиторій маніфестів",
|
|
1306
|
+
"repoHelp": "Owner/repo, що містить маніфести Kubernetes, напр. acme/preview-manifests.",
|
|
1307
|
+
"ref": "Реф",
|
|
1308
|
+
"refHelp": "Гілка, тег або SHA, з якого читати маніфести. Порожнє використовує гілку за замовчуванням репозиторію.",
|
|
1309
|
+
"path": "Шлях маніфесту",
|
|
1310
|
+
"pathHelp": "Файл або каталог у репозиторії, що містить ресурси для застосування.",
|
|
1311
|
+
"urlSourceLabel": "URL середовища",
|
|
1312
|
+
"urlIngressTemplate": "Шаблон хоста Ingress",
|
|
1313
|
+
"urlIngressStatus": "Зчитати стан Ingress",
|
|
1314
|
+
"urlServiceStatus": "Зчитати стан Service",
|
|
1315
|
+
"hostTemplate": "Шаблон хоста",
|
|
1316
|
+
"hostTemplateHelp": "Шаблон хоста, відрендерений зі змінними провіженингу, такими як branch і pullNumber; отриманий хост стає URL середовища.",
|
|
1317
|
+
"ingressName": "Назва Ingress",
|
|
1318
|
+
"ingressNameHelp": "Ingress, з якого зчитати хост балансувальника. Порожнє використовує єдиний застосований Ingress.",
|
|
1319
|
+
"serviceName": "Назва Service",
|
|
1320
|
+
"port": "Порт",
|
|
1321
|
+
"scheme": "Схема URL",
|
|
1322
|
+
"schemeDefault": "https (за замовчуванням)",
|
|
1323
|
+
"namespaceTemplate": "Шаблон простору імен",
|
|
1324
|
+
"namespaceTemplateHelp": "Назва простору імен на кожен PR, відрендерена зі змінних провіженингу, таких як pullNumber. Порожнє виводить її з номера PR.",
|
|
1325
|
+
"imageTemplate": "Шаблон образу",
|
|
1326
|
+
"imageTemplateHelp": "Посилання на образ, доступне маніфестам, відрендерене зі змінними провіженингу, такими як branch і sha.",
|
|
1327
|
+
"caCertPem": "Сертифікат CA кластера (PEM)",
|
|
1328
|
+
"caCertPemHelp": "Вставте CA-набір кластера, щоб TLS-сертифікат apiserver проходив перевірку. Пропустіть лише для публічно довіреного CA.",
|
|
1329
|
+
"insecureSkipTlsVerify": "Пропустити перевірку TLS",
|
|
1330
|
+
"insecureSkipTlsVerifyHelp": "Наполегливо не рекомендується. Вимикає перевірку TLS apiserver; використовуйте лише для кластерів kind/dev."
|
|
1292
1331
|
},
|
|
1293
1332
|
"kubernetes": {
|
|
1294
1333
|
"label": "Назва",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.2",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"valibot": "^1.4.2",
|
|
35
35
|
"vue": "^3.5.39",
|
|
36
36
|
"wretch": "^3.0.9",
|
|
37
|
-
"@cat-factory/contracts": "0.
|
|
37
|
+
"@cat-factory/contracts": "0.51.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@toad-contracts/testing": "0.3.2",
|