diff --git a/packages/plugins/git/src/helpers.test.ts b/packages/plugins/git/src/helpers.test.ts index 789fecee6..a25f98838 100644 --- a/packages/plugins/git/src/helpers.test.ts +++ b/packages/plugins/git/src/helpers.test.ts @@ -2,7 +2,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. -import { getRepositoryData } from '@dd/internal-git-plugin/helpers'; +import { getRepositoryData, gitRemote } from '@dd/internal-git-plugin/helpers'; import { addFixtureFiles } from '@dd/tests/_jest/helpers/mocks'; jest.mock('@dd/core/helpers/fs', () => { @@ -70,4 +70,17 @@ describe('Git Plugin helpers', () => { expect(files).toStrictEqual(['src/core/plugins/git/helpers.test.ts']); }); }); + + describe('gitRemote', () => { + test('Should reject when no remotes are available', async () => { + const mockGitNoRemotes = { + getConfig: (arg: string) => ({ value: 'origin' }), + getRemotes: (arg: boolean) => [], + }; + + await expect(gitRemote(mockGitNoRemotes as any)).rejects.toThrow( + 'No git remotes available', + ); + }); + }); }); diff --git a/packages/plugins/git/src/index.test.ts b/packages/plugins/git/src/index.test.ts index afe666fb7..f105d681d 100644 --- a/packages/plugins/git/src/index.test.ts +++ b/packages/plugins/git/src/index.test.ts @@ -4,18 +4,20 @@ import type { LogLevel, Options, RepositoryData } from '@dd/core/types'; import { uploadSourcemaps } from '@dd/error-tracking-plugin/sourcemaps/index'; -import { getRepositoryData } from '@dd/internal-git-plugin/helpers'; +import { getRepositoryData, newSimpleGit } from '@dd/internal-git-plugin/helpers'; import { defaultPluginOptions, getRepositoryDataMock, getSourcemapsConfiguration, } from '@dd/tests/_jest/helpers/mocks'; import { BUNDLERS, runBundlers } from '@dd/tests/_jest/helpers/runBundlers'; +import type { SimpleGit } from 'simple-git'; jest.mock('@dd/internal-git-plugin/helpers', () => { const originalModule = jest.requireActual('@dd/internal-git-plugin/helpers'); return { ...originalModule, + newSimpleGit: jest.fn(), getRepositoryData: jest.fn(), }; }); @@ -30,6 +32,14 @@ jest.mock('@dd/error-tracking-plugin/sourcemaps/index', () => { const uploadSourcemapsMocked = jest.mocked(uploadSourcemaps); const getRepositoryDataMocked = jest.mocked(getRepositoryData); +const newSimpleGitMocked = jest.mocked(newSimpleGit); + +const oneRemote = [{ refs: { push: 'git@github.com:user/repository.git' } }]; + +const createMockSimpleGit = (remotes: Array<{ refs: { push: string } }>): SimpleGit => { + const stub = { getRemotes: () => remotes }; + return stub as unknown as SimpleGit; +}; const pluginOptions = { ...defaultPluginOptions, @@ -70,6 +80,10 @@ describe('Git Plugin', () => { return Promise.resolve(); }); + newSimpleGitMocked.mockImplementation(() => + Promise.resolve(createMockSimpleGit(oneRemote)), + ); + getRepositoryDataMocked.mockImplementation(() => { nbCallsToGetRepositoryData += 1; return Promise.resolve(mockGitData); @@ -143,4 +157,58 @@ describe('Git Plugin', () => { expect(getRepositoryDataMocked).not.toHaveBeenCalled(); }); }); + + describe('No remotes', () => { + const gitReports: Record = {}; + const gitHookReports: Record = {}; + let nbCallsToGetRepositoryData = 0; + let nbGitHookCalls = 0; + + beforeAll(async () => { + const pluginConfig: Options = { + ...pluginOptions, + errorTracking: { + sourcemaps: getSourcemapsConfiguration(), + }, + customPlugins: ({ context }) => { + return [ + { + name: 'custom-test-hook-plugin', + git(repoData) { + nbGitHookCalls += 1; + gitHookReports[context.bundler.name] = repoData; + }, + }, + ]; + }, + }; + + uploadSourcemapsMocked.mockImplementation((options, context, log) => { + gitReports[context.bundlerName] = context.git; + return Promise.resolve(); + }); + + newSimpleGitMocked.mockImplementation(() => Promise.resolve(createMockSimpleGit([]))); + + getRepositoryDataMocked.mockImplementation(() => { + nbCallsToGetRepositoryData += 1; + return Promise.resolve(getRepositoryDataMock()); + }); + + await runBundlers(pluginConfig); + }); + + test('Should not call getRepositoryData when there are no remotes.', () => { + expect(nbCallsToGetRepositoryData).toBe(0); + }); + + test('Should not fire the git hook.', () => { + expect(nbGitHookCalls).toBe(0); + }); + + test.each(BUNDLERS)('[$name|$version] Should leave context.git undefined.', ({ name }) => { + expect(gitReports[name]).toBeUndefined(); + expect(gitHookReports[name]).toBeUndefined(); + }); + }); }); diff --git a/packages/plugins/git/src/index.ts b/packages/plugins/git/src/index.ts index 9833b114d..4a5e3028e 100644 --- a/packages/plugins/git/src/index.ts +++ b/packages/plugins/git/src/index.ts @@ -17,9 +17,18 @@ export const getGitPlugins: GetInternalPlugins = (arg: GetPluginsArg) => { const timeGit = log.time('get git information', { start: false }); const processGit = async (gitDir: string) => { try { - const repositoryData = await getRepositoryData( - await newSimpleGit(path.dirname(gitDir!)), - ); + const git = await newSimpleGit(path.dirname(gitDir)); + const remotes = await git.getRemotes(true); + if (remotes.length === 0) { + log.warn( + 'No git remotes available, skipping git plugin. ' + + 'This is expected for a repository that has not been pushed yet.', + ); + timeGit.end(); + return; + } + + const repositoryData = await getRepositoryData(git); context.git = repositoryData; timeGit.end();