From 46d8949703364daa1f9d2feb19e95b5288bd9299 Mon Sep 17 00:00:00 2001 From: Erik Engervall Date: Thu, 29 Apr 2021 15:43:41 +0200 Subject: [PATCH] Create annotated tags for promotions as well. Remove duplicate api.getCommit method Signed-off-by: Erik Engervall --- .../src/api/GitReleaseApiClient.test.ts | 3 +- .../src/api/GitReleaseApiClient.ts | 41 ++----- .../hooks/useCreateReleaseCandidate.ts | 4 +- .../PromoteRc/hooks/usePromoteRc.test.ts | 20 +++- .../features/PromoteRc/hooks/usePromoteRc.ts | 102 +++++++++++++++++- .../src/features/Stats/helpers/getTagDate.ts | 2 +- .../src/features/Stats/hooks/useGetCommit.ts | 2 +- .../src/test-helpers/test-helpers.ts | 7 +- 8 files changed, 129 insertions(+), 52 deletions(-) diff --git a/plugins/git-release-manager/src/api/GitReleaseApiClient.test.ts b/plugins/git-release-manager/src/api/GitReleaseApiClient.test.ts index ba697ca99b..1896693c6f 100644 --- a/plugins/git-release-manager/src/api/GitReleaseApiClient.test.ts +++ b/plugins/git-release-manager/src/api/GitReleaseApiClient.test.ts @@ -39,8 +39,8 @@ describe('GitReleaseApiClient', () => { "createRef": [Function], "createTagObject": [Function], "getBranch": [Function], + "getCommit": [Function], "getHost": [Function], - "getLatestCommit": [Function], "getLatestRelease": [Function], "getOwners": [Function], "getRecentCommits": [Function], @@ -66,7 +66,6 @@ describe('GitReleaseApiClient', () => { "stats": Object { "getAllReleases": [Function], "getAllTags": [Function], - "getCommit": [Function], "getSingleTag": [Function], }, } diff --git a/plugins/git-release-manager/src/api/GitReleaseApiClient.ts b/plugins/git-release-manager/src/api/GitReleaseApiClient.ts index 5d263f54a0..44816e860b 100644 --- a/plugins/git-release-manager/src/api/GitReleaseApiClient.ts +++ b/plugins/git-release-manager/src/api/GitReleaseApiClient.ts @@ -195,16 +195,12 @@ export class GitReleaseApiClient implements GitReleaseApi { }; }; - getLatestCommit: GitReleaseApi['getLatestCommit'] = async ({ - owner, - repo, - defaultBranch, - }) => { + getCommit: GitReleaseApi['getCommit'] = async ({ owner, repo, ref }) => { const { octokit } = await this.getOctokit(); const { data: latestCommit } = await octokit.repos.getCommit({ owner, repo, - ref: defaultBranch, + ref, ...DISABLE_CACHE, }); @@ -214,6 +210,7 @@ export class GitReleaseApiClient implements GitReleaseApi { commit: { message: latestCommit.commit.message, }, + createdAt: latestCommit.commit.committer?.date, }; }; @@ -527,20 +524,6 @@ ${selectedPatchCommit.commit.message}`, })); }, - getCommit: async ({ owner, repo, ref }) => { - const { octokit } = await this.getOctokit(); - - const { data: commit } = await octokit.repos.getCommit({ - owner, - repo, - ref, - }); - - return { - createdAt: commit.commit.committer?.date, - }; - }, - getSingleTag: async ({ owner, repo, tagSha }) => { const { octokit } = await this.getOctokit(); const singleTag = await octokit.git.getTag({ @@ -621,9 +604,9 @@ export interface GitReleaseApi { name: string; }>; - getLatestCommit: ( + getCommit: ( args: { - defaultBranch: string; + ref: string; } & OwnerRepo, ) => Promise<{ sha: string; @@ -631,6 +614,7 @@ export interface GitReleaseApi { commit: { message: string; }; + createdAt?: string; }>; getBranch: ( @@ -812,14 +796,6 @@ export interface GitReleaseApi { }> >; - getCommit: ( - args: { - ref: string; - } & OwnerRepo, - ) => Promise<{ - createdAt: string | undefined; - }>; - getSingleTag: ( args: { tagSha: string; @@ -848,7 +824,7 @@ export type GetRepositoryResult = UnboxReturnedPromise< GitReleaseApi['getRepository'] >; export type GetLatestCommitResult = UnboxReturnedPromise< - GitReleaseApi['getLatestCommit'] + GitReleaseApi['getCommit'] >; export type GetBranchResult = UnboxReturnedPromise; export type CreateRefResult = UnboxReturnedPromise; @@ -883,9 +859,6 @@ export type PromoteReleaseResult = UnboxReturnedPromise< export type GetAllTagsResult = UnboxReturnedPromise< GitReleaseApi['stats']['getAllTags'] >; -export type GetCommitResult = UnboxReturnedPromise< - GitReleaseApi['stats']['getCommit'] ->; export type GetAllReleasesResult = UnboxReturnedPromise< GitReleaseApi['stats']['getAllReleases'] >; diff --git a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts index 3e6967ff37..d7cd38effc 100644 --- a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts +++ b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts @@ -71,10 +71,10 @@ export function useCreateReleaseCandidate({ */ const [latestCommitRes, run] = useAsyncFn(async () => { const latestCommit = await pluginApiClient - .getLatestCommit({ + .getCommit({ owner: project.owner, repo: project.repo, - defaultBranch, + ref: defaultBranch, }) .catch(asyncCatcher); diff --git a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts index d682476d33..c1618027bb 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts +++ b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts @@ -21,6 +21,7 @@ import { mockApiClient, mockCalverProject, mockReleaseCandidateCalver, + mockUser, } from '../../../test-helpers/test-helpers'; import { usePromoteRc } from './usePromoteRc'; @@ -33,6 +34,9 @@ jest.mock('../../../contexts/ProjectContext', () => ({ project: mockCalverProject, }), })); +jest.mock('../../../contexts/UserContext', () => ({ + useUserContext: () => ({ user: mockUser }), +})); describe('usePromoteRc', () => { beforeEach(jest.clearAllMocks); @@ -50,7 +54,7 @@ describe('usePromoteRc', () => { }); expect(result.error).toEqual(undefined); - expect(result.current.responseSteps).toHaveLength(1); + expect(result.current.responseSteps).toHaveLength(4); }); it('should return the expected responseSteps and progress (with successCb)', async () => { @@ -66,11 +70,23 @@ describe('usePromoteRc', () => { await waitFor(() => result.current.run()); }); - expect(result.current.responseSteps).toHaveLength(2); + expect(result.current.responseSteps).toHaveLength(5); expect(result.current).toMatchInlineSnapshot(` Object { "progress": 100, "responseSteps": Array [ + Object { + "message": "Fetched most recent commit from release branch", + "secondaryMessage": "with sha \\"latestCommit.sha\\"", + }, + Object { + "message": "Created Tag Object", + "secondaryMessage": "with sha \\"mock_tag_object_sha\\"", + }, + Object { + "message": "Create Tag Reference", + "secondaryMessage": "with ref \\"mock_createRef_ref\\"", + }, Object { "link": "mock_release_html_url", "message": "Promoted \\"mock_release_name\\"", diff --git a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts index 7a106c3e51..bbeacb6b59 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts +++ b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts @@ -23,6 +23,9 @@ import { GetLatestReleaseResult } from '../../../api/GitReleaseApiClient'; import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef'; import { useProjectContext } from '../../../contexts/ProjectContext'; import { useResponseSteps } from '../../../hooks/useResponseSteps'; +import { useUserContext } from '../../../contexts/UserContext'; +import { TAG_OBJECT_MESSAGE } from '../../../constants/constants'; +import { GitReleaseManagerError } from '../../../errors/GitReleaseManagerError'; interface PromoteRc { rcRelease: NonNullable; @@ -36,6 +39,7 @@ export function usePromoteRc({ successCb, }: PromoteRc): CardHook { const pluginApiClient = useApi(gitReleaseManagerApiRef); + const { user } = useUserContext(); const { project } = useProjectContext(); const { responseSteps, @@ -45,9 +49,97 @@ export function usePromoteRc({ } = useResponseSteps(); /** - * (1) Promote Release Candidate to Release Version + * (1) Fetch most recent release branch commit */ - const [promotedReleaseRes, run] = useAsyncFn(async () => { + const [latestReleaseBranchCommitSha, run] = useAsyncFn(async () => { + const latestCommit = await pluginApiClient + .getCommit({ + owner: project.owner, + repo: project.repo, + ref: rcRelease.targetCommitish, + }) + .catch(asyncCatcher); + + addStepToResponseSteps({ + message: 'Fetched most recent commit from release branch', + secondaryMessage: `with sha "${latestCommit.sha}"`, + }); + + return { + ...latestCommit, + }; + }); + + /** + * (2) Create tag object for our soon-to-be-created annotated tag + */ + const tagObjectRes = useAsync(async () => { + abortIfError(latestReleaseBranchCommitSha.error); + if (!latestReleaseBranchCommitSha.value) return undefined; + + const createdTagObject = await pluginApiClient + .createTagObject({ + owner: project.owner, + repo: project.repo, + tag: releaseVersion, + objectSha: latestReleaseBranchCommitSha.value.sha, + taggerName: user.username, + taggerEmail: user.email, + message: TAG_OBJECT_MESSAGE, + }) + .catch(asyncCatcher); + + addStepToResponseSteps({ + message: 'Created Tag Object', + secondaryMessage: `with sha "${createdTagObject.tagSha}"`, + }); + + return { + ...createdTagObject, + }; + }, [latestReleaseBranchCommitSha.value, latestReleaseBranchCommitSha.error]); + + /** + * (3) Create reference for tag object + */ + const createRcRes = useAsync(async () => { + abortIfError(tagObjectRes.error); + if (!tagObjectRes.value) return undefined; + + const createdRef = await pluginApiClient + .createRef({ + owner: project.owner, + repo: project.repo, + ref: `refs/tags/${releaseVersion}`, + sha: tagObjectRes.value.tagSha, + }) + .catch(error => { + if (error?.body?.message === 'Reference already exists') { + throw new GitReleaseManagerError( + `Tag reference "${releaseVersion}" already exists`, + ); + } + throw error; + }) + .catch(asyncCatcher); + + addStepToResponseSteps({ + message: 'Create Tag Reference', + secondaryMessage: `with ref "${createdRef.ref}"`, + }); + + return { + ...createdRef, + }; + }, [tagObjectRes.value, tagObjectRes.error]); + + /** + * (4) Promote Release Candidate to Release Version + */ + const promotedReleaseRes = useAsync(async () => { + abortIfError(createRcRes.error); + if (!createRcRes.value) return undefined; + const promotedRelease = await pluginApiClient.promoteRc .promoteRelease({ owner: project.owner, @@ -66,10 +158,10 @@ export function usePromoteRc({ return { ...promotedRelease, }; - }); + }, [createRcRes.value, createRcRes.error]); /** - * (2) Run successCb if defined + * (5) Run successCb if defined */ useAsync(async () => { if (successCb && !!promotedReleaseRes.value) { @@ -95,7 +187,7 @@ export function usePromoteRc({ } }, [promotedReleaseRes.value]); - const TOTAL_STEPS = 1 + (!!successCb ? 1 : 0); + const TOTAL_STEPS = 4 + (!!successCb ? 1 : 0); const [progress, setProgress] = useState(0); useEffect(() => { setProgress((responseSteps.length / TOTAL_STEPS) * 100); diff --git a/plugins/git-release-manager/src/features/Stats/helpers/getTagDate.ts b/plugins/git-release-manager/src/features/Stats/helpers/getTagDate.ts index f0c3bcd445..8c389e879c 100644 --- a/plugins/git-release-manager/src/features/Stats/helpers/getTagDate.ts +++ b/plugins/git-release-manager/src/features/Stats/helpers/getTagDate.ts @@ -28,7 +28,7 @@ export const getTagDate = async ({ project, tagSha, }: GetTagDate) => { - const commitRes = await pluginApiClient.stats.getCommit({ + const commitRes = await pluginApiClient.getCommit({ owner: project.owner, repo: project.repo, ref: tagSha, diff --git a/plugins/git-release-manager/src/features/Stats/hooks/useGetCommit.ts b/plugins/git-release-manager/src/features/Stats/hooks/useGetCommit.ts index 01ff486a74..83c5324615 100644 --- a/plugins/git-release-manager/src/features/Stats/hooks/useGetCommit.ts +++ b/plugins/git-release-manager/src/features/Stats/hooks/useGetCommit.ts @@ -30,7 +30,7 @@ export const useGetCommit = ({ ref }: { ref?: string }) => { throw new GitReleaseManagerError('Missing ref to get commit'); } - return pluginApiClient.stats.getCommit({ + return pluginApiClient.getCommit({ owner: project.owner, repo: project.repo, ref, diff --git a/plugins/git-release-manager/src/test-helpers/test-helpers.ts b/plugins/git-release-manager/src/test-helpers/test-helpers.ts index 6b2951b9a3..458ffff9a1 100644 --- a/plugins/git-release-manager/src/test-helpers/test-helpers.ts +++ b/plugins/git-release-manager/src/test-helpers/test-helpers.ts @@ -209,12 +209,13 @@ export const mockApiClient: GitReleaseApi = { name: mockRepo, })), - getLatestCommit: jest.fn(async () => ({ + getCommit: jest.fn(async () => ({ sha: 'latestCommit.sha', htmlUrl: 'latestCommit.html_url', commit: { message: 'latestCommit.commit.message', }, + createdAt: '2021-01-01T10:11:12Z', })), getBranch: jest.fn(async () => createMockBranch()), @@ -305,10 +306,6 @@ export const mockApiClient: GitReleaseApi = { }, ]), - getCommit: jest.fn(async () => ({ - createdAt: '2021-01-01T10:11:12Z', - })), - getSingleTag: jest.fn(async () => ({ date: '2021-04-29T12:48:30.120Z', username: 'mock_usersingle_tag_name',