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

Commit 03e56b0

Browse files
feat: add support for readonly repositories (#194)
This change updates the Register Repository Page to request if a repository has read-only access when a repository is being registered. If a repository is read-only, the UI will hide any options to add or update packages in the repository.
1 parent 4665b23 commit 03e56b0

File tree

9 files changed

+162
-23
lines changed

9 files changed

+162
-23
lines changed

plugins/cad/src/components/AddPackagePage/AddPackagePage.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import {
5656
ContentSummary,
5757
getPackageDescriptor,
5858
getRepository,
59+
isReadOnlyRepository,
5960
RepositoryContentDetails,
6061
} from '../../utils/repository';
6162
import { sortByLabel } from '../../utils/selectItem';
@@ -554,6 +555,15 @@ export const AddPackagePage = ({ action }: AddPackagePageProps) => {
554555
helperText={`The repository to create the new ${targetRepositoryPackageDescriptorLowercase} in.`}
555556
/>
556557

558+
{!!targetRepository && isReadOnlyRepository(targetRepository) && (
559+
<Alert severity="info" icon={false}>
560+
A new {targetRepositoryPackageDescriptorLowercase} cannot be
561+
created in the {targetRepository.metadata.name} repository
562+
since the repository is read-only. Another destination
563+
repository will need to be selected.
564+
</Alert>
565+
)}
566+
557567
{!!addPackageAction?.message && (
558568
<Alert severity="info" icon={false}>
559569
{addPackageAction.message}

plugins/cad/src/components/PackageRevisionPage/PackageRevisionPage.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
findRepository,
7575
getPackageDescriptor,
7676
isDeploymentRepository,
77+
isReadOnlyRepository,
7778
} from '../../utils/repository';
7879
import {
7980
getRepositorySummaries,
@@ -819,7 +820,23 @@ export const PackageRevisionPage = ({ mode }: PackageRevisionPageProps) => {
819820
return upgradeMessage;
820821
};
821822

822-
const alertMessages = isUpgradeAvailable ? [getUpgradeMessage()] : [];
823+
const alertMessages: AlertMessage[] = [];
824+
825+
if (isReadOnlyRepository(repository)) {
826+
alertMessages.push({
827+
key: 'read-only',
828+
message: (
829+
<Fragment>
830+
This {toLowerCase(packageDescriptor)} is read-only since this{' '}
831+
{toLowerCase(packageDescriptor)} exists in a read-only repository.
832+
</Fragment>
833+
),
834+
});
835+
}
836+
837+
if (isUpgradeAvailable) {
838+
alertMessages.push(getUpgradeMessage());
839+
}
823840

824841
if (isLatestPublishedPackageRevision) {
825842
const downstreamPackagesPendingUpgrade = downstreamPackageSummaries.filter(

plugins/cad/src/components/PackageRevisionPage/components/AdvancedPackageRevisionOptions.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@
1616

1717
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
1818
import { Button } from '@material-ui/core';
19+
import { Alert } from '@material-ui/lab';
1920
import React, { Fragment, useState } from 'react';
2021
import { useNavigate } from 'react-router-dom';
2122
import { configAsDataApiRef } from '../../../apis';
2223
import { repositoryRouteRef } from '../../../routes';
2324
import { Repository } from '../../../types/Repository';
2425
import { RootSync } from '../../../types/RootSync';
26+
import {
27+
getPackageDescriptor,
28+
isReadOnlyRepository,
29+
} from '../../../utils/repository';
30+
import { toLowerCase } from '../../../utils/string';
2531
import { ConfirmationDialog } from '../../Controls/ConfirmationDialog';
2632

2733
type AdvancedPackageRevisionOptionsProps = {
@@ -86,6 +92,15 @@ export const AdvancedPackageRevisionOptions = ({
8692
navigate(repositoryRef({ repositoryName }));
8793
};
8894

95+
if (isReadOnlyRepository(repository)) {
96+
return (
97+
<Alert severity="info">
98+
Advanced options are hidden since this{' '}
99+
{toLowerCase(getPackageDescriptor(repository))} is read-only.
100+
</Alert>
101+
);
102+
}
103+
89104
return (
90105
<Fragment>
91106
<ConfirmationDialog

plugins/cad/src/components/PackageRevisionPage/components/PackageRevisionOptions.tsx

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
import {
3838
getPackageDescriptor,
3939
isDeploymentRepository,
40+
isReadOnlyRepository,
4041
RepositoryContentDetails,
4142
} from '../../../utils/repository';
4243
import { PackageRevisionPageMode } from '../PackageRevisionPage';
@@ -64,6 +65,7 @@ type PackageRevisionOptionsProps = {
6465
};
6566

6667
const DraftPackageRevisionOptions = ({
68+
repositorySummary,
6769
packageRevision,
6870
mode,
6971
onClick,
@@ -76,6 +78,11 @@ const DraftPackageRevisionOptions = ({
7678
const repositoryName = packageRevision.spec.repository;
7779

7880
const isEditMode = mode === PackageRevisionPageMode.EDIT;
81+
const isViewOnly = isReadOnlyRepository(repositorySummary.repository);
82+
83+
if (isViewOnly) {
84+
return <Fragment />;
85+
}
7986

8087
if (isEditMode) {
8188
return (
@@ -125,9 +132,16 @@ const DraftPackageRevisionOptions = ({
125132
};
126133

127134
const ProposedPackageRevisionOptions = ({
135+
repositorySummary,
128136
onClick,
129137
disabled,
130138
}: PackageRevisionOptionsProps) => {
139+
const isViewOnly = isReadOnlyRepository(repositorySummary.repository);
140+
141+
if (isViewOnly) {
142+
return <Fragment />;
143+
}
144+
131145
return (
132146
<Fragment>
133147
<MaterialButton
@@ -169,6 +183,7 @@ const PublishedPackageRevisionOptions = ({
169183
const latestRevision = packageRevisions[0];
170184
const latestPublishedRevision = findLatestPublishedRevision(packageRevisions);
171185

186+
const isReadOnly = isReadOnlyRepository(repositorySummary.repository);
172187
const isLatestPublishedPackageRevision =
173188
packageRevision && isLatestPublishedRevision(packageRevision);
174189

@@ -179,14 +194,16 @@ const PublishedPackageRevisionOptions = ({
179194
if (!isLatestPublishedPackageRevision) {
180195
return (
181196
<Fragment>
182-
<MaterialButton
183-
onClick={() => onClick(RevisionOption.RESTORE_REVISION)}
184-
color="primary"
185-
variant="outlined"
186-
disabled={disabled}
187-
>
188-
Restore Revision
189-
</MaterialButton>
197+
{!isReadOnly && (
198+
<MaterialButton
199+
onClick={() => onClick(RevisionOption.RESTORE_REVISION)}
200+
color="primary"
201+
variant="outlined"
202+
disabled={disabled}
203+
>
204+
Restore Revision
205+
</MaterialButton>
206+
)}
190207

191208
<Button
192209
to={packageRef({
@@ -205,18 +222,23 @@ const PublishedPackageRevisionOptions = ({
205222

206223
const packageContentType = getPackageDescriptor(repositorySummary.repository);
207224

225+
const isNewerUnpublishedRevision = latestRevision !== latestPublishedRevision;
226+
227+
const showUpgrade =
228+
!isReadOnly && isUpgradeAvailable && !isNewerUnpublishedRevision;
229+
const showCreateNewRevision = !isReadOnly && !isNewerUnpublishedRevision;
208230
const showClone =
209231
RepositoryContentDetails[packageContentType].cloneTo.length > 0 &&
210232
canCloneRevision(packageRevision);
211233

212-
const isNewerUnpublishedRevision = latestRevision !== latestPublishedRevision;
213-
214234
const showCreateSync =
215-
isDeploymentRepository(repositorySummary.repository) && rootSync === null;
235+
!isReadOnly &&
236+
isDeploymentRepository(repositorySummary.repository) &&
237+
rootSync === null;
216238

217239
return (
218240
<Fragment>
219-
{isUpgradeAvailable && !isNewerUnpublishedRevision && (
241+
{showUpgrade && (
220242
<MaterialButton
221243
variant="outlined"
222244
color="primary"
@@ -227,7 +249,7 @@ const PublishedPackageRevisionOptions = ({
227249
</MaterialButton>
228250
)}
229251

230-
{!isNewerUnpublishedRevision && (
252+
{showCreateNewRevision && (
231253
<MaterialButton
232254
variant="outlined"
233255
color="primary"

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
getRepositoryResource,
4848
getSecretRef,
4949
PackageContentSummaryOrder,
50+
RepositoryAccess,
5051
RepositoryContentDetails,
5152
} from '../../utils/repository';
5253
import { getBasicAuthSecret, isBasicAuthSecret } from '../../utils/secret';
@@ -86,7 +87,8 @@ export const RegisterRepositoryPage = () => {
8687
description: '',
8788
repoBranch: '',
8889
repoDir: '',
89-
authType: '',
90+
authType: AuthenticationType.GITHUB_ACCESS_TOKEN,
91+
repositoryAccess: RepositoryAccess.FULL,
9092
useSecret: 'new',
9193
authSecretName: '',
9294
authPassword: '',
@@ -155,6 +157,7 @@ export const RegisterRepositoryPage = () => {
155157
}
156158

157159
const contentSummary = state.contentSummary as ContentSummary;
160+
const repositoryAccess = state.repositoryAccess as RepositoryAccess;
158161

159162
if (contentSummary === ContentSummary.DEPLOYMENT) {
160163
deploymentEnvironment =
@@ -165,6 +168,7 @@ export const RegisterRepositoryPage = () => {
165168
state.name,
166169
state.description,
167170
contentSummary,
171+
repositoryAccess,
168172
gitDetails,
169173
ociDetails,
170174
deploymentEnvironment,
@@ -384,7 +388,9 @@ export const RegisterRepositoryPage = () => {
384388
<div className={classes.stepContent}>
385389
<Select
386390
label="Authentication Type"
387-
onChange={value => setState({ ...state, authType: value })}
391+
onChange={value =>
392+
setState({ ...state, authType: value as AuthenticationType })
393+
}
388394
selected={state.authType}
389395
items={selectAuthTypeItems}
390396
helperText="The authentication type of the repository. Select None if the repository does not require any authentication."
@@ -432,6 +438,28 @@ export const RegisterRepositoryPage = () => {
432438
/>
433439
</Fragment>
434440
)}
441+
442+
<Select
443+
label="Repository Access"
444+
onChange={value =>
445+
setState({
446+
...state,
447+
repositoryAccess: value as RepositoryAccess,
448+
})
449+
}
450+
selected={state.repositoryAccess}
451+
items={[
452+
{
453+
value: RepositoryAccess.FULL,
454+
label: 'Write access',
455+
},
456+
{
457+
value: RepositoryAccess.READ_ONLY,
458+
label: 'Read-only access',
459+
},
460+
]}
461+
helperText="The access anyone using the UI will have to the repository. Write access allows packages to be created and updated in the repository, whereas read-only access allows packages to be viewed. Select read-only access if the repository authentication only allows read access."
462+
/>
435463
</div>
436464
</SimpleStepperStep>
437465

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
isDeploymentRepository,
4646
isFunctionRepository,
4747
isPackageRepository,
48+
isReadOnlyRepository,
4849
} from '../../utils/repository';
4950
import {
5051
getRepositorySummaries,
@@ -167,6 +168,7 @@ export const RepositoryPage = () => {
167168

168169
const thisRepository = repositorySummary.repository;
169170
const repoTitle = getRepositoryTitle(thisRepository);
171+
const isReadOnly = isReadOnlyRepository(thisRepository);
170172

171173
const packageDescriptor = getPackageDescriptor(thisRepository);
172174

@@ -178,13 +180,15 @@ export const RepositoryPage = () => {
178180
</Breadcrumbs>
179181

180182
<ContentHeader title={repoTitle}>
181-
<Button
182-
to={addPackageRef({ repositoryName: repositoryName })}
183-
color="primary"
184-
variant="contained"
185-
>
186-
Add {packageDescriptor}
187-
</Button>
183+
{!isReadOnly && (
184+
<Button
185+
to={addPackageRef({ repositoryName: repositoryName })}
186+
color="primary"
187+
variant="contained"
188+
>
189+
Add {packageDescriptor}
190+
</Button>
191+
)}
188192
</ContentHeader>
189193

190194
<Tabs

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { makeStyles } from '@material-ui/core';
1718
import Alert from '@material-ui/lab/Alert';
1819
import React, { Fragment } from 'react';
1920
import { Function } from '../../../types/Function';
@@ -24,7 +25,9 @@ import {
2425
isDeploymentRepository,
2526
isFunctionRepository,
2627
isPackageRepository,
28+
isReadOnlyRepository,
2729
} from '../../../utils/repository';
30+
import { toLowerCase } from '../../../utils/string';
2831
import { PackagesTable } from '../../PackagesTable';
2932
import { FunctionsTable } from '../components/FunctionsTable';
3033

@@ -35,20 +38,37 @@ type PackagesTabContentProps = {
3538
packagesError?: Error;
3639
};
3740

41+
const useStyles = makeStyles({
42+
messageBanner: {
43+
marginBottom: '16px',
44+
},
45+
});
46+
3847
export const PackagesTabContent = ({
3948
repository,
4049
packages,
4150
functions,
4251
packagesError,
4352
}: PackagesTabContentProps) => {
53+
const classes = useStyles();
54+
4455
const pluralPackageDescriptor = `${getPackageDescriptor(repository)}s`;
56+
const isReadOnly = isReadOnlyRepository(repository);
4557

4658
if (packagesError) {
4759
return <Alert severity="error">{packagesError.message}</Alert>;
4860
}
4961

5062
return (
5163
<Fragment>
64+
{isReadOnly && (
65+
<Alert className={classes.messageBanner} severity="info">
66+
This repository is read-only. You will not be able to add or make any
67+
changes to the {toLowerCase(pluralPackageDescriptor)} in this
68+
repository.
69+
</Alert>
70+
)}
71+
5272
{isPackageRepository(repository) && (
5373
<PackagesTable
5474
title={pluralPackageDescriptor}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getDeploymentEnvironment,
2323
getPackageDescriptor,
2424
isDeploymentRepository,
25+
isReadOnlyRepository,
2526
} from '../../../utils/repository';
2627

2728
type RepositoryDetailsProps = {
@@ -83,11 +84,14 @@ const getRepositoryStatusConditions = (repository: Repository): Metadata => {
8384
};
8485

8586
const getRepositoryMetadata = (repository: Repository): Metadata => {
87+
const isReadOnly = isReadOnlyRepository(repository);
88+
8689
const metadata: Metadata = {
8790
name: repository.metadata.name,
8891
description: repository.spec.description ?? '',
8992
content: `${getPackageDescriptor(repository)}s`,
9093
deploymentEnvironment: getDeploymentEnvironment(repository),
94+
repositoryAccess: isReadOnly ? 'read-only access' : 'write access',
9195
...getRepositoryStoreMetadata(repository),
9296
...getRepositoryStatusConditions(repository),
9397
};

0 commit comments

Comments
 (0)