Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.

Commit 3f9e339

Browse files
feat: add deployment environment to register repository page (#170)
This change updates the Register Repository Page to request the deployment environment when the repository content type is set to deployments.
1 parent 22c8061 commit 3f9e339

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

plugins/cad/src/components/RegisterRepositoryPage/RegisterRepositoryPage.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import { Secret } from '../../types/Secret';
4040
import { allowFunctionRepositoryRegistration } from '../../utils/featureFlags';
4141
import {
4242
ContentSummary,
43+
DeploymentEnvironment,
44+
DeploymentEnvironmentDetails,
4345
getRepositoryGitDetails,
4446
getRepositoryOciDetails,
4547
getRepositoryResource,
@@ -79,6 +81,7 @@ export const RegisterRepositoryPage = () => {
7981
name: '',
8082
repoUrl: '',
8183
contentSummary: '',
84+
deploymentEnvironment: '',
8285
type: '',
8386
description: '',
8487
repoBranch: '',
@@ -133,6 +136,7 @@ export const RegisterRepositoryPage = () => {
133136

134137
let gitDetails: RepositoryGitDetails | undefined = undefined;
135138
let ociDetails: RepositoryOciDetails | undefined = undefined;
139+
let deploymentEnvironment: DeploymentEnvironment | undefined = undefined;
136140

137141
if (state.type === RepositoryType.GIT) {
138142
const createBranch = true;
@@ -152,12 +156,18 @@ export const RegisterRepositoryPage = () => {
152156

153157
const contentSummary = state.contentSummary as ContentSummary;
154158

159+
if (contentSummary === ContentSummary.DEPLOYMENT) {
160+
deploymentEnvironment =
161+
state.deploymentEnvironment as DeploymentEnvironment;
162+
}
163+
155164
const resource = getRepositoryResource(
156165
state.name,
157166
state.description,
158167
contentSummary,
159168
gitDetails,
160169
ociDetails,
170+
deploymentEnvironment,
161171
);
162172

163173
return resource;
@@ -234,6 +244,14 @@ export const RegisterRepositoryPage = () => {
234244
}
235245
}
236246

247+
for (const [environment, details] of Object.entries(
248+
DeploymentEnvironmentDetails,
249+
)) {
250+
if (repositoryUrl.includes(details.shortName)) {
251+
toSet.deploymentEnvironment = environment;
252+
}
253+
}
254+
237255
const suggestedName = kebabCase(
238256
repositoryUrl
239257
.split('/')
@@ -289,6 +307,16 @@ export const RegisterRepositoryPage = () => {
289307
return selectItems;
290308
}, []);
291309

310+
const deploymentEnvironmentRadioOptions = useMemo(
311+
() =>
312+
Object.keys(DeploymentEnvironmentDetails).map(env => ({
313+
label: env,
314+
value: env,
315+
description: DeploymentEnvironmentDetails[env].description,
316+
})),
317+
[],
318+
);
319+
292320
return (
293321
<div>
294322
<Breadcrumbs>
@@ -442,6 +470,22 @@ export const RegisterRepositoryPage = () => {
442470
</div>
443471
</SimpleStepperStep>
444472

473+
{state.contentSummary === ContentSummary.DEPLOYMENT && (
474+
<SimpleStepperStep title="Deployment Environment">
475+
<div className={classes.stepContent}>
476+
<RadioGroup
477+
label="Development Environment"
478+
onChange={value =>
479+
updateStateValue('deploymentEnvironment', value)
480+
}
481+
value={state.deploymentEnvironment}
482+
options={deploymentEnvironmentRadioOptions}
483+
helperText="Select the environment that maps to your repository."
484+
/>
485+
</div>
486+
</SimpleStepperStep>
487+
)}
488+
445489
<SimpleStepperStep
446490
title="Confirm"
447491
actions={{ nextText: 'Register Repository', onNext: registerRepo }}

plugins/cad/src/components/RepositoryPage/components/RepositoryDetails.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import { InfoCard, StructuredMetadataTable } from '@backstage/core-components';
1818
import React from 'react';
1919
import { Repository } from '../../../types/Repository';
2020
import { RepositorySummary } from '../../../types/RepositorySummary';
21-
import { getPackageDescriptor } from '../../../utils/repository';
21+
import {
22+
getDeploymentEnvironment,
23+
getPackageDescriptor,
24+
isDeploymentRepository,
25+
} from '../../../utils/repository';
2226

2327
type RepositoryDetailsProps = {
2428
repositorySummary: RepositorySummary;
@@ -60,9 +64,14 @@ const getRepositoryMetadata = (repository: Repository): Metadata => {
6064
name: repository.metadata.name,
6165
description: repository.spec.description ?? '',
6266
content: `${getPackageDescriptor(repository)}s`,
67+
deploymentEnvironment: getDeploymentEnvironment(repository),
6368
...getRepositoryStoreMetadata(repository),
6469
};
6570

71+
if (!isDeploymentRepository(repository)) {
72+
delete metadata.deploymentEnvironment;
73+
}
74+
6675
return metadata;
6776
};
6877

plugins/cad/src/utils/repository.ts

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,20 @@ type ContentCloneToDetail = {
4444
message?: string;
4545
};
4646

47+
type EnvironmentDetails = {
48+
[key: string]: EnvironmentDetail;
49+
};
50+
51+
type EnvironmentDetail = {
52+
shortName: string;
53+
description: string;
54+
repositoryEnvironmentLabelValue?: string;
55+
notDeploymentEnvironment?: DeploymentEnvironment[];
56+
};
57+
4758
const REPOSITORY_CONTENT_LABEL = 'kpt.dev/repository-content';
59+
const REPOSITORY_DEPLOYMENT_ENVIRONMENT_LABEL =
60+
'kpt.dev/deployment-environment';
4861

4962
export enum ContentSummary {
5063
EXTERNAL_BLUEPRINT = 'External Blueprint',
@@ -54,6 +67,12 @@ export enum ContentSummary {
5467
FUNCTION = 'Function',
5568
}
5669

70+
export enum DeploymentEnvironment {
71+
DEVELOPMENT = 'Development',
72+
STAGING = 'Staging',
73+
PRODUCTION = 'Production',
74+
}
75+
5776
export const PackageContentSummaryOrder = [
5877
ContentSummary.DEPLOYMENT,
5978
ContentSummary.TEAM_BLUEPRINT,
@@ -77,7 +96,7 @@ export const RepositoryContentDetails: ContentDetails = {
7796
[ContentSummary.DEPLOYMENT]: {
7897
repositoryContent: RepositoryContent.PACKAGE,
7998
description:
80-
'Deployment Packages are packages ready for deployment to live clusters.',
99+
"Deployment Packages are packages ready for deployment to live clusters. If selected, you'll need to specify if the repository is for a development, staging, or production cluster.",
81100
isDeployment: true,
82101
cloneTo: [],
83102
},
@@ -135,6 +154,30 @@ export const RepositoryContentDetails: ContentDetails = {
135154
},
136155
};
137156

157+
export const DeploymentEnvironmentDetails: EnvironmentDetails = {
158+
[DeploymentEnvironment.DEVELOPMENT]: {
159+
shortName: 'dev',
160+
description:
161+
'The development environment is the environment your team uses for day-to-day development. A Team Blueprint package is expected to be cloned to this environment first.',
162+
notDeploymentEnvironment: [
163+
DeploymentEnvironment.STAGING,
164+
DeploymentEnvironment.PRODUCTION,
165+
],
166+
},
167+
[DeploymentEnvironment.STAGING]: {
168+
shortName: 'staging',
169+
description:
170+
'The staging environment is similar to the production environment, except it does not receive live traffic. A Team Blueprint package is expected to be cloned to this environment after it is cloned, published, and tested in the development environment.',
171+
repositoryEnvironmentLabelValue: 'staging',
172+
},
173+
[DeploymentEnvironment.PRODUCTION]: {
174+
shortName: 'prod',
175+
description:
176+
'The production environment receives live traffic. A Team Blueprint package is expected to be cloned to this environment after it is cloned, published, and tested in the staging environment.',
177+
repositoryEnvironmentLabelValue: 'production',
178+
},
179+
};
180+
138181
const isRepositoryContent = (
139182
repository: Repository,
140183
contentType: ContentSummary,
@@ -170,6 +213,36 @@ export const getPackageDescriptor = (repository: Repository): string => {
170213
return 'Unknown';
171214
};
172215

216+
const isDeploymentEnviroment = (
217+
repository: Repository,
218+
environment: DeploymentEnvironment,
219+
): boolean => {
220+
const environmentDetails = DeploymentEnvironmentDetails[environment];
221+
222+
const isLabelMatch =
223+
!environmentDetails.repositoryEnvironmentLabelValue ||
224+
repository.metadata.labels?.[REPOSITORY_DEPLOYMENT_ENVIRONMENT_LABEL] ===
225+
environmentDetails.repositoryEnvironmentLabelValue;
226+
227+
const isDeployment = isDeploymentRepository(repository);
228+
const notContent = environmentDetails.notDeploymentEnvironment ?? [];
229+
const noDisqualifiers = !notContent
230+
.map(env => isDeploymentEnviroment(repository, env))
231+
.includes(true);
232+
233+
return isDeployment && isLabelMatch && noDisqualifiers;
234+
};
235+
236+
export const getDeploymentEnvironment = (repository: Repository): string => {
237+
for (const env of Object.keys(DeploymentEnvironmentDetails)) {
238+
if (isDeploymentEnviroment(repository, env as DeploymentEnvironment)) {
239+
return env;
240+
}
241+
}
242+
243+
return 'Unknown';
244+
};
245+
173246
export const getRepository = (
174247
allRepositories: Repository[],
175248
repositoryName: string,
@@ -212,6 +285,7 @@ export const getRepositoryResource = (
212285
contentSummary: ContentSummary,
213286
git?: RepositoryGitDetails,
214287
oci?: RepositoryOciDetails,
288+
deploymentEnvironment?: DeploymentEnvironment,
215289
): Repository => {
216290
const namespace = 'default';
217291

@@ -229,6 +303,16 @@ export const getRepositoryResource = (
229303
contentDetails.repositoryContentLabelValue;
230304
}
231305

306+
if (contentSummary === ContentSummary.DEPLOYMENT && deploymentEnvironment) {
307+
const environmentDetails =
308+
DeploymentEnvironmentDetails[deploymentEnvironment];
309+
310+
if (environmentDetails.repositoryEnvironmentLabelValue) {
311+
labels[REPOSITORY_DEPLOYMENT_ENVIRONMENT_LABEL] =
312+
environmentDetails.repositoryEnvironmentLabelValue;
313+
}
314+
}
315+
232316
const resource: Repository = {
233317
apiVersion: 'config.porch.kpt.dev/v1alpha1',
234318
kind: 'Repository',

0 commit comments

Comments
 (0)