diff --git a/shell/assets/translations/en-us.yaml b/shell/assets/translations/en-us.yaml
index fba3f0f676..26a54a7e31 100644
--- a/shell/assets/translations/en-us.yaml
+++ b/shell/assets/translations/en-us.yaml
@@ -6064,6 +6064,7 @@ validation:
localhost: If the Server URL is internal to the Rancher server (e.g. localhost) the downstream clusters may not be able to communicate with Rancher.
trailingForwardSlash: Server URL should not have a trailing forward slash.
url: Server URL must be an URL.
+ repo: Repository URL must be a valid HTTPS or SSH URL to a git repository.
stringLength:
between: '"{key}" should be between {min} and {max} {max, plural, =1 {character} other {characters}}'
exactly: '"{key}" should be {count, plural, =1 {# character} other {# characters}}'
diff --git a/shell/edit/fleet.cattle.io.gitrepo.vue b/shell/edit/fleet.cattle.io.gitrepo.vue
index 17828d9237..421ebe3ca6 100644
--- a/shell/edit/fleet.cattle.io.gitrepo.vue
+++ b/shell/edit/fleet.cattle.io.gitrepo.vue
@@ -25,6 +25,7 @@ import { CAPI, CATALOG, FLEET as FLEET_LABELS } from '@shell/config/labels-annot
import { SECRET_TYPES } from '@shell/config/secret';
import { checkSchemasForFindAllHash } from '@shell/utils/auth';
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
+import FormValidation from '@shell/mixins/form-validation';
const _VERIFY = 'verify';
const _SKIP = 'skip';
@@ -50,7 +51,7 @@ export default {
SelectOrCreateAuthSecret,
},
- mixins: [CreateEditView],
+ mixins: [CreateEditView, FormValidation],
async fetch() {
const hash = await checkSchemasForFindAllHash({
@@ -153,7 +154,8 @@ export default {
stepRepoInfo,
stepTargetInfo,
addRepositorySteps,
- displayHelmRepoURLRegex: false
+ displayHelmRepoURLRegex: false,
+ fvFormRuleSets: [{ path: 'spec.repo', rules: ['required', 'repo'] }]
};
},
@@ -253,7 +255,7 @@ export default {
},
stepOneRequires() {
- return !!this.value.metadata.name && !!this.refValue;
+ return !!this.value.metadata.name && !!this.refValue && !!this.fvFormIsValid;
},
},
@@ -265,6 +267,8 @@ export default {
targetAdvanced: 'updateTargets',
tlsMode: 'updateTls',
caBundle: 'updateTls',
+ 'value.metadata.name': 'stepOneReady',
+ 'value.spec.repo': 'stepOneReady',
workspace(neu) {
if ( this.isCreate ) {
@@ -456,10 +460,6 @@ export default {
this.tlsMode = event;
},
- onUpdateRepoName() {
- this.stepOneReady();
- },
-
stepOneReady() {
this.addRepositorySteps[0]['ready'] = this.stepOneRequires;
},
@@ -528,7 +528,6 @@ export default {
:namespaced="false"
:mode="mode"
@update:value="$emit('input', $event)"
- @change="onUpdateRepoName"
/>
@@ -552,6 +551,8 @@ export default {
:mode="mode"
label-key="fleet.gitRepo.repo.label"
:placeholder="t('fleet.gitRepo.repo.placeholder', null, true)"
+ :required="true"
+ :rules="fvGetAndReportPathRules('spec.repo')"
/>
diff --git a/shell/models/fleet.cattle.io.gitrepo.js b/shell/models/fleet.cattle.io.gitrepo.js
index 9d89c3ccd8..30aa0d645e 100644
--- a/shell/models/fleet.cattle.io.gitrepo.js
+++ b/shell/models/fleet.cattle.io.gitrepo.js
@@ -176,6 +176,10 @@ export default class GitRepo extends SteveModel {
get repoDisplay() {
let repo = this.spec.repo;
+ if (!repo) {
+ return null;
+ }
+
repo = repo.replace(/.git$/, '');
repo = repo.replace(/^https:\/\//, '');
repo = repo.replace(/\/+$/, '');
diff --git a/shell/utils/validators/formRules/__tests__/index.test.ts b/shell/utils/validators/formRules/__tests__/index.test.ts
index d021a36091..54e5f95e4e 100644
--- a/shell/utils/validators/formRules/__tests__/index.test.ts
+++ b/shell/utils/validators/formRules/__tests__/index.test.ts
@@ -1112,6 +1112,29 @@ describe('formRules', () => {
expect(formRuleResult).toStrictEqual(expectedResult);
});
+ describe('repo', () => {
+ const message = JSON.stringify({ message: 'validation.setting.repo' });
+ const testCases = [
+ ['', undefined],
+ ['https://github.com/rancher/fleet-examples.git', undefined],
+ ['git@github.com:rancher/fleet-examples.git', undefined],
+ ['http://100.100.100.127', undefined],
+ ['aaaAAAA111//', message],
+ ['/', message],
+ ['+1', message],
+ [undefined, undefined]
+ ];
+
+ it.each(testCases)(
+ 'should return undefined or correct message based on the provided url',
+ (val, expected) => {
+ const formRuleResult = formRules.repo(val);
+
+ expect(formRuleResult).toStrictEqual(expected);
+ }
+ );
+ });
+
describe.each([
['minValue', 2, [3], [1]],
['maxValue', 256, [1], [300]],
diff --git a/shell/utils/validators/formRules/index.ts b/shell/utils/validators/formRules/index.ts
index a17974e286..fd1c84182e 100644
--- a/shell/utils/validators/formRules/index.ts
+++ b/shell/utils/validators/formRules/index.ts
@@ -458,6 +458,8 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
return runValidators(val, [startHyphen('label'), endHyphen('label'), startDot('label'), endDot('label'), required]);
};
+ const repo: Validator = (val) => val && !/((git|ssh|http(s)?)|(git@[\w\.]+))(\:(\/\/)?)([\w\.@\:/\-~]+)(\.git)?(\/)?/.test(val) ? t('validation.setting.repo') : undefined;
+
return {
absolutePath,
alphanumeric,
@@ -500,5 +502,6 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
subDomain,
testRule,
wildcardHostname,
+ repo
};
}