Introduce ability to add custom features

Also, add more metadata to success callbacks

Signed-off-by: Erik Engervall <erik.engervall@gmail.com>
This commit is contained in:
Erik Engervall
2021-07-21 10:59:17 +02:00
parent 91088c3877
commit 11e0652f86
12 changed files with 166 additions and 65 deletions
+60 -16
View File
@@ -16,7 +16,7 @@
import React from 'react';
import { createDevApp } from '@backstage/dev-utils';
import { Box, Typography } from '@material-ui/core';
import { Box, Button, Typography } from '@material-ui/core';
import { gitReleaseManagerPlugin, GitReleaseManagerPage } from '../src/plugin';
import { InfoCardPlus } from '../src/components/InfoCardPlus';
@@ -44,10 +44,16 @@ createDevApp()
<Box padding={4}>
<InfoCardPlus>
<Typography variant="h4">Dev notes</Typography>
<Typography>
Configure plugin statically by passing props to the
`GitHubReleaseManagerPage` component
</Typography>
<Typography variant="body2">
Note that the static configuration points towards private
repositories and will thus not work for everyone.
</Typography>
</InfoCardPlus>
<GitReleaseManagerPage
@@ -67,8 +73,10 @@ createDevApp()
<Box padding={4}>
<InfoCardPlus>
<Typography variant="h4">Dev notes</Typography>
<Typography>Each feature can be omitted</Typography>
<Typography>Success callbacks can also be added</Typography>
<Typography>
Each feature can be individually omitted as well as have success
callback attached to them
</Typography>
</InfoCardPlus>
<GitReleaseManagerPage
@@ -79,22 +87,12 @@ createDevApp()
}}
features={{
createRc: {
onSuccess: ({
comparisonUrl,
createdTag,
gitReleaseName,
gitReleaseUrl,
previousTag,
}) => {
onSuccess: args => {
// eslint-disable-next-line no-console
console.log(
'Custom success callback for Create RC',
comparisonUrl,
createdTag,
gitReleaseName,
gitReleaseUrl,
previousTag,
'Custom success callback for Create RC with the following args',
);
console.log(JSON.stringify(args, null, 2)); // eslint-disable-line no-console
},
},
promoteRc: {
@@ -108,4 +106,50 @@ createDevApp()
</Box>
),
})
.addPage({
title: 'Custom',
path: '/custom',
element: (
<Box padding={4}>
<InfoCardPlus>
<Typography variant="h4">Dev notes</Typography>
<Typography>
The custom feature's return value can either be a React Element or
an array of React Elements.
</Typography>
</InfoCardPlus>
<GitReleaseManagerPage
project={{
owner: 'eengervall-playground',
repo: 'playground-semver',
versioningStrategy: 'semver',
}}
features={{
custom: {
factory: args => {
return (
<InfoCardPlus>
<Typography variant="h4">I'm a custom feature</Typography>
<Button
variant="contained"
color="primary"
onClick={() => {
console.log(`Here's my args 🚀`); // eslint-disable-line no-console
console.log(JSON.stringify(args, null, 2)); // eslint-disable-line no-console
}}
>
View the arguments for this feature in the console by
pressing this button
</Button>
</InfoCardPlus>
);
},
},
}}
/>
</Box>
),
})
.render();
@@ -18,12 +18,14 @@ import React from 'react';
import { useAsync } from 'react-use';
import { Alert } from '@material-ui/lab';
import { Box } from '@material-ui/core';
import { useApi } from '@backstage/core-plugin-api';
import { ContentHeader, Progress } from '@backstage/core-components';
import {
ComponentConfig,
ComponentConfigCreateRc,
ComponentConfigPatch,
ComponentConfigPromoteRc,
CreateRcOnSuccessArgs,
PatchOnSuccessArgs,
PromoteRcOnSuccessArgs,
} from './types/types';
import { Features } from './features/Features';
import { gitReleaseManagerApiRef } from './api/serviceApiRef';
@@ -33,18 +35,33 @@ import { ProjectContext, Project } from './contexts/ProjectContext';
import { RepoDetailsForm } from './features/RepoDetailsForm/RepoDetailsForm';
import { useQueryHandler } from './hooks/useQueryHandler';
import { UserContext } from './contexts/UserContext';
import { useApi } from '@backstage/core-plugin-api';
import { ContentHeader, Progress } from '@backstage/core-components';
import {
GetBranchResult,
GetLatestReleaseResult,
GetRepositoryResult,
} from './api/GitReleaseClient';
interface GitReleaseManagerProps {
project?: Omit<Project, 'isProvidedViaProps'>;
features?: {
info?: Pick<ComponentConfig<void>, 'omit'>;
stats?: Pick<ComponentConfig<void>, 'omit'>;
createRc?: ComponentConfigCreateRc;
promoteRc?: ComponentConfigPromoteRc;
patch?: ComponentConfigPatch;
createRc?: ComponentConfig<CreateRcOnSuccessArgs>;
promoteRc?: ComponentConfig<PromoteRcOnSuccessArgs>;
patch?: ComponentConfig<PatchOnSuccessArgs>;
custom?: {
factory: ({
latestRelease,
project,
releaseBranch,
repository,
}: {
latestRelease: GetLatestReleaseResult['latestRelease'] | null;
project: Project;
releaseBranch: GetBranchResult['branch'] | null;
repository: GetRepositoryResult['repository'];
}) => React.ReactElement | React.ReactElement[];
};
};
}
@@ -31,7 +31,7 @@ import {
GetLatestReleaseResult,
GetRepositoryResult,
} from '../../api/GitReleaseClient';
import { ComponentConfigCreateRc } from '../../types/types';
import { ComponentConfig, CreateRcOnSuccessArgs } from '../../types/types';
import { Differ } from '../../components/Differ';
import { getReleaseCandidateGitInfo } from '../../helpers/getReleaseCandidateGitInfo';
import { InfoCardPlus } from '../../components/InfoCardPlus';
@@ -45,7 +45,7 @@ interface CreateReleaseCandidateProps {
defaultBranch: GetRepositoryResult['repository']['defaultBranch'];
latestRelease: GetLatestReleaseResult['latestRelease'];
releaseBranch: GetBranchResult['branch'] | null;
onSuccess?: ComponentConfigCreateRc['onSuccess'];
onSuccess?: ComponentConfig<CreateRcOnSuccessArgs>['onSuccess'];
}
const InfoCardPlusWrapper = ({ children }: { children: React.ReactNode }) => {
@@ -21,7 +21,11 @@ import {
GetRepositoryResult,
} from '../../../api/GitReleaseClient';
import { CardHook, ComponentConfigCreateRc } from '../../../types/types';
import {
CardHook,
ComponentConfig,
CreateRcOnSuccessArgs,
} from '../../../types/types';
import { getReleaseCandidateGitInfo } from '../../../helpers/getReleaseCandidateGitInfo';
import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef';
import { GitReleaseManagerError } from '../../../errors/GitReleaseManagerError';
@@ -31,12 +35,12 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps';
import { useUserContext } from '../../../contexts/UserContext';
import { useApi } from '@backstage/core-plugin-api';
interface UseCreateReleaseCandidate {
export interface UseCreateReleaseCandidate {
defaultBranch: GetRepositoryResult['repository']['defaultBranch'];
latestRelease: GetLatestReleaseResult['latestRelease'];
releaseCandidateGitInfo: ReturnType<typeof getReleaseCandidateGitInfo>;
project: Project;
onSuccess?: ComponentConfigCreateRc['onSuccess'];
onSuccess?: ComponentConfig<CreateRcOnSuccessArgs>['onSuccess'];
}
export function useCreateReleaseCandidate({
@@ -266,6 +270,12 @@ export function useCreateReleaseCandidate({
try {
await onSuccess({
input: {
defaultBranch,
latestRelease,
releaseCandidateGitInfo,
project,
},
comparisonUrl: getComparisonRes.value.htmlUrl,
createdTag: createReleaseRes.value.tagName,
gitReleaseName: createReleaseRes.value.name,
@@ -142,6 +142,14 @@ export function Features({
onSuccess={features?.patch?.onSuccess}
/>
)}
{features?.custom?.factory &&
features.custom.factory({
latestRelease: gitBatchInfo.value.latestRelease,
project,
releaseBranch: gitBatchInfo.value.releaseBranch,
repository: gitBatchInfo.value.repository,
})}
</ErrorBoundary>
</RefetchContext.Provider>
);
@@ -22,7 +22,7 @@ import {
GetBranchResult,
GetLatestReleaseResult,
} from '../../api/GitReleaseClient';
import { ComponentConfigPatch } from '../../types/types';
import { ComponentConfig, PatchOnSuccessArgs } from '../../types/types';
import { getBumpedTag } from '../../helpers/getBumpedTag';
import { InfoCardPlus } from '../../components/InfoCardPlus';
import { NoLatestRelease } from '../../components/NoLatestRelease';
@@ -32,7 +32,7 @@ import { useProjectContext } from '../../contexts/ProjectContext';
interface PatchProps {
latestRelease: GetLatestReleaseResult['latestRelease'];
releaseBranch: GetBranchResult['branch'] | null;
onSuccess?: ComponentConfigPatch['onSuccess'];
onSuccess?: ComponentConfig<PatchOnSuccessArgs>['onSuccess'];
}
export const Patch = ({
@@ -38,7 +38,7 @@ import {
GetLatestReleaseResult,
} from '../../api/GitReleaseClient';
import { CalverTagParts } from '../../helpers/tagParts/getCalverTagParts';
import { ComponentConfigPatch } from '../../types/types';
import { ComponentConfig, PatchOnSuccessArgs } from '../../types/types';
import { Differ } from '../../components/Differ';
import { getPatchCommitSuffix } from './helpers/getPatchCommitSuffix';
import { gitReleaseManagerApiRef } from '../../api/serviceApiRef';
@@ -56,7 +56,7 @@ interface PatchBodyProps {
bumpedTag: string;
latestRelease: NonNullable<GetLatestReleaseResult['latestRelease']>;
releaseBranch: GetBranchResult['branch'];
onSuccess?: ComponentConfigPatch['onSuccess'];
onSuccess?: ComponentConfig<PatchOnSuccessArgs>['onSuccess'];
tagParts: NonNullable<CalverTagParts | SemverTagParts>;
}
@@ -22,7 +22,11 @@ import {
} from '../../../api/GitReleaseClient';
import { CalverTagParts } from '../../../helpers/tagParts/getCalverTagParts';
import { ComponentConfigPatch, CardHook } from '../../../types/types';
import {
CardHook,
ComponentConfig,
PatchOnSuccessArgs,
} from '../../../types/types';
import { getPatchCommitSuffix } from '../helpers/getPatchCommitSuffix';
import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef';
import { Project } from '../../../contexts/ProjectContext';
@@ -32,12 +36,12 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps';
import { useUserContext } from '../../../contexts/UserContext';
import { useApi } from '@backstage/core-plugin-api';
interface Patch {
export interface UsePatch {
bumpedTag: string;
latestRelease: NonNullable<GetLatestReleaseResult['latestRelease']>;
project: Project;
tagParts: NonNullable<CalverTagParts | SemverTagParts>;
onSuccess?: ComponentConfigPatch['onSuccess'];
onSuccess?: ComponentConfig<PatchOnSuccessArgs>['onSuccess'];
}
// Inspiration: https://stackoverflow.com/questions/53859199/how-to-cherry-pick-through-githubs-api
@@ -47,7 +51,7 @@ export function usePatch({
project,
tagParts,
onSuccess,
}: Patch): CardHook<GetRecentCommitsResultSingle> {
}: UsePatch): CardHook<GetRecentCommitsResultSingle> {
const pluginApiClient = useApi(gitReleaseManagerApiRef);
const { user } = useUserContext();
const {
@@ -337,13 +341,19 @@ ${selectedPatchCommit.commit.message}`,
try {
await onSuccess?.({
updatedReleaseUrl: updatedReleaseRes.value.htmlUrl,
updatedReleaseName: updatedReleaseRes.value.name,
previousTag: latestRelease.tagName,
patchedTag: updatedReleaseRes.value.tagName,
patchCommitUrl: releaseBranchRes.value.selectedPatchCommit.htmlUrl,
input: {
bumpedTag,
latestRelease,
project,
tagParts,
},
patchCommitMessage:
releaseBranchRes.value.selectedPatchCommit.commit.message,
patchCommitUrl: releaseBranchRes.value.selectedPatchCommit.htmlUrl,
patchedTag: updatedReleaseRes.value.tagName,
previousTag: latestRelease.tagName,
updatedReleaseName: updatedReleaseRes.value.name,
updatedReleaseUrl: updatedReleaseRes.value.htmlUrl,
});
} catch (error) {
asyncCatcher(error);
@@ -18,7 +18,7 @@ import React from 'react';
import { Alert, AlertTitle } from '@material-ui/lab';
import { Box, Typography } from '@material-ui/core';
import { ComponentConfigPromoteRc } from '../../types/types';
import { ComponentConfig, PromoteRcOnSuccessArgs } from '../../types/types';
import { GetLatestReleaseResult } from '../../api/GitReleaseClient';
import { InfoCardPlus } from '../../components/InfoCardPlus';
import { NoLatestRelease } from '../../components/NoLatestRelease';
@@ -27,7 +27,7 @@ import { TEST_IDS } from '../../test-helpers/test-ids';
interface PromoteRcProps {
latestRelease: GetLatestReleaseResult['latestRelease'];
onSuccess?: ComponentConfigPromoteRc['onSuccess'];
onSuccess?: ComponentConfig<PromoteRcOnSuccessArgs>['onSuccess'];
}
export const PromoteRc = ({ latestRelease, onSuccess }: PromoteRcProps) => {
@@ -17,7 +17,7 @@
import React from 'react';
import { Button, Typography, Box } from '@material-ui/core';
import { ComponentConfigPromoteRc } from '../../types/types';
import { ComponentConfig, PromoteRcOnSuccessArgs } from '../../types/types';
import { Differ } from '../../components/Differ';
import { GetLatestReleaseResult } from '../../api/GitReleaseClient';
import { ResponseStepDialog } from '../../components/ResponseStepDialog/ResponseStepDialog';
@@ -26,7 +26,7 @@ import { usePromoteRc } from './hooks/usePromoteRc';
interface PromoteRcBodyProps {
rcRelease: NonNullable<GetLatestReleaseResult['latestRelease']>;
onSuccess?: ComponentConfigPromoteRc['onSuccess'];
onSuccess?: ComponentConfig<PromoteRcOnSuccessArgs>['onSuccess'];
}
export const PromoteRcBody = ({ rcRelease, onSuccess }: PromoteRcBodyProps) => {
@@ -16,7 +16,11 @@
import { useState, useEffect } from 'react';
import { useAsync, useAsyncFn } from 'react-use';
import { CardHook, ComponentConfigPromoteRc } from '../../../types/types';
import {
CardHook,
ComponentConfig,
PromoteRcOnSuccessArgs,
} from '../../../types/types';
import { GetLatestReleaseResult } from '../../../api/GitReleaseClient';
import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef';
@@ -27,17 +31,17 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps';
import { useUserContext } from '../../../contexts/UserContext';
import { useApi } from '@backstage/core-plugin-api';
interface PromoteRc {
export interface UsePromoteRc {
rcRelease: NonNullable<GetLatestReleaseResult['latestRelease']>;
releaseVersion: string;
onSuccess?: ComponentConfigPromoteRc['onSuccess'];
onSuccess?: ComponentConfig<PromoteRcOnSuccessArgs>['onSuccess'];
}
export function usePromoteRc({
rcRelease,
releaseVersion,
onSuccess,
}: PromoteRc): CardHook<void> {
}: UsePromoteRc): CardHook<void> {
const pluginApiClient = useApi(gitReleaseManagerApiRef);
const { user } = useUserContext();
const { project } = useProjectContext();
@@ -170,12 +174,16 @@ export function usePromoteRc({
try {
await onSuccess?.({
gitReleaseUrl: promotedReleaseRes.value.htmlUrl,
input: {
rcRelease,
releaseVersion,
},
gitReleaseName: promotedReleaseRes.value.name,
previousTagUrl: rcRelease.htmlUrl,
gitReleaseUrl: promotedReleaseRes.value.htmlUrl,
previousTag: rcRelease.tagName,
updatedTagUrl: promotedReleaseRes.value.htmlUrl,
previousTagUrl: rcRelease.htmlUrl,
updatedTag: promotedReleaseRes.value.tagName,
updatedTagUrl: promotedReleaseRes.value.htmlUrl,
});
} catch (error) {
asyncCatcher(error);
+15 -11
View File
@@ -14,21 +14,26 @@
* limitations under the License.
*/
export type ComponentConfig<Args> = {
import { UseCreateReleaseCandidate } from '../features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate';
import { UsePatch } from '../features/Patch/hooks/usePatch';
import { UsePromoteRc } from '../features/PromoteRc/hooks/usePromoteRc';
export type ComponentConfig<OnSuccessArgs> = {
omit?: boolean;
onSuccess?: (args: Args) => Promise<void> | void;
onSuccess?: (args: OnSuccessArgs) => Promise<void> | void;
};
interface CreateRcOnSuccessArgs {
gitReleaseUrl: string;
gitReleaseName: string | null;
export interface CreateRcOnSuccessArgs {
input: Omit<UseCreateReleaseCandidate, 'onSuccess'>;
comparisonUrl: string;
previousTag?: string;
createdTag: string;
gitReleaseName: string | null;
gitReleaseUrl: string;
previousTag?: string;
}
export type ComponentConfigCreateRc = ComponentConfig<CreateRcOnSuccessArgs>;
interface PromoteRcOnSuccessArgs {
export interface PromoteRcOnSuccessArgs {
input: Omit<UsePromoteRc, 'onSuccess'>;
gitReleaseUrl: string;
gitReleaseName: string | null;
previousTagUrl: string;
@@ -36,9 +41,9 @@ interface PromoteRcOnSuccessArgs {
updatedTagUrl: string;
updatedTag: string;
}
export type ComponentConfigPromoteRc = ComponentConfig<PromoteRcOnSuccessArgs>;
interface PatchOnSuccessArgs {
export interface PatchOnSuccessArgs {
input: Omit<UsePatch, 'onSuccess'>;
updatedReleaseUrl: string;
updatedReleaseName: string | null;
previousTag: string;
@@ -46,7 +51,6 @@ interface PatchOnSuccessArgs {
patchCommitUrl: string;
patchCommitMessage: string;
}
export type ComponentConfigPatch = ComponentConfig<PatchOnSuccessArgs>;
export interface ResponseStep {
message: string | React.ReactNode;