diff --git a/package-lock.json b/package-lock.json index 850fc986971..2b8d045d074 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5813,6 +5813,7 @@ }, "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { "version": "1.1.0", + "extraneous": true, "inBundle": true, "license": "MIT" }, diff --git a/src/utils/init/config-manual.ts b/src/utils/init/config-manual.ts index 7b8c6779a3e..90ff5477fee 100644 --- a/src/utils/init/config-manual.ts +++ b/src/utils/init/config-manual.ts @@ -88,8 +88,8 @@ export default async function configManual({ const repoPath = await getRepoPath({ repoData }) const repo = { - provider: 'manual', - repo_path: repoPath, + provider: repoData.provider ?? 'manual', + repo_path: repoData.repo ?? repoPath, repo_branch: repoData.branch, allowed_branches: [repoData.branch], deploy_key_id: deployKey.id, diff --git a/tests/unit/utils/get-repo-data.test.ts b/tests/unit/utils/get-repo-data.test.ts new file mode 100644 index 00000000000..feaa1c8fb36 --- /dev/null +++ b/tests/unit/utils/get-repo-data.test.ts @@ -0,0 +1,120 @@ +import { describe, expect, it, vi } from 'vitest' +import type { RepoData } from '../../../src/utils/get-repo-data.js' + +vi.mock('../../../src/utils/command-helpers.js', () => ({ + log: vi.fn(), +})) + +describe('getRepoData', () => { + describe('RepoData structure for different Git providers', () => { + it('should construct correct httpsUrl for GitHub SSH URLs', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'git@github.com:ownername/test.git', + branch: 'main', + provider: 'github', + httpsUrl: 'https://github.com/ownername/test', + } + + expect(mockRepoData.httpsUrl).toBe('https://github.com/ownername/test') + expect(mockRepoData.provider).toBe('github') + expect(mockRepoData.repo).toBe('ownername/test') + }) + + it('should construct correct httpsUrl for GitLab SSH URLs', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'git@gitlab.com:ownername/test.git', + branch: 'main', + provider: 'gitlab', + httpsUrl: 'https://gitlab.com/ownername/test', + } + + expect(mockRepoData.httpsUrl).toBe('https://gitlab.com/ownername/test') + expect(mockRepoData.provider).toBe('gitlab') + expect(mockRepoData.repo).toBe('ownername/test') + }) + + it('should construct correct httpsUrl for GitHub HTTPS URLs', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'https://github.com/ownername/test.git', + branch: 'main', + provider: 'github', + httpsUrl: 'https://github.com/ownername/test', + } + + expect(mockRepoData.httpsUrl).toBe('https://github.com/ownername/test') + expect(mockRepoData.provider).toBe('github') + expect(mockRepoData.repo).toBe('ownername/test') + }) + + it('should construct correct httpsUrl for GitLab HTTPS URLs', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'https://gitlab.com/ownername/test.git', + branch: 'main', + provider: 'gitlab', + httpsUrl: 'https://gitlab.com/ownername/test', + } + + expect(mockRepoData.httpsUrl).toBe('https://gitlab.com/ownername/test') + expect(mockRepoData.provider).toBe('gitlab') + expect(mockRepoData.repo).toBe('ownername/test') + }) + + it('should use host as provider for unknown Git hosts', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'user', + repo: 'user/test', + url: 'git@custom-git.example.com:user/test.git', + branch: 'main', + provider: 'custom-git.example.com', + httpsUrl: 'https://custom-git.example.com/user/test', + } + + expect(mockRepoData.httpsUrl).toBe('https://custom-git.example.com/user/test') + expect(mockRepoData.provider).toBe('custom-git.example.com') + expect(mockRepoData.repo).toBe('user/test') + }) + }) + + describe('provider field mapping', () => { + it('should map github.com to "github" provider', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'user', + repo: 'user/test', + url: 'git@github.com:user/test.git', + branch: 'main', + provider: 'github', + httpsUrl: 'https://github.com/user/test', + } + + expect(mockRepoData.provider).toBe('github') + }) + + it('should map gitlab.com to "gitlab" provider', () => { + const mockRepoData: RepoData = { + name: 'test', + owner: 'user', + repo: 'user/test', + url: 'git@gitlab.com:user/test.git', + branch: 'main', + provider: 'gitlab', + httpsUrl: 'https://gitlab.com/user/test', + } + + expect(mockRepoData.provider).toBe('gitlab') + }) + }) +}) diff --git a/tests/unit/utils/init/config-manual.test.ts b/tests/unit/utils/init/config-manual.test.ts new file mode 100644 index 00000000000..0cdc807d28f --- /dev/null +++ b/tests/unit/utils/init/config-manual.test.ts @@ -0,0 +1,167 @@ +import { describe, expect, it, vi, beforeEach } from 'vitest' +import type { RepoData } from '../../../../src/utils/get-repo-data.js' +import type { NetlifyAPI } from '@netlify/api' +import type BaseCommand from '../../../../src/commands/base-command.js' + +const mockPrompt = vi.fn() +const mockLog = vi.fn() +const mockExit = vi.fn() +const mockCreateDeployKey = vi.fn() +const mockGetBuildSettings = vi.fn() +const mockSaveNetlifyToml = vi.fn() +const mockSetupSite = vi.fn() + +vi.mock('inquirer', () => ({ + default: { + prompt: mockPrompt, + }, +})) + +vi.mock('../../../../src/utils/command-helpers.js', () => ({ + log: mockLog, + exit: mockExit, +})) + +vi.mock('../../../../src/utils/init/utils.js', () => ({ + createDeployKey: mockCreateDeployKey, + getBuildSettings: mockGetBuildSettings, + saveNetlifyToml: mockSaveNetlifyToml, + setupSite: mockSetupSite, +})) + +describe('config-manual', () => { + let mockApi: Partial + let mockCommand: Pick + + beforeEach(() => { + vi.clearAllMocks() + + mockApi = {} + mockCommand = { + netlify: { + api: mockApi as NetlifyAPI, + cachedConfig: { configPath: '/test/netlify.toml' } as BaseCommand['netlify']['cachedConfig'], + config: { plugins: [] } as BaseCommand['netlify']['config'], + repositoryRoot: '/test', + } as BaseCommand['netlify'], + } + + mockPrompt.mockResolvedValue({ + sshKeyAdded: true, + repoPath: 'git@gitlab.com:test/repo.git', + deployHookAdded: true, + }) + + mockCreateDeployKey.mockResolvedValue({ id: 'key-123', public_key: 'ssh-rsa test' }) + mockGetBuildSettings.mockResolvedValue({ + baseDir: '', + buildCmd: 'npm run build', + buildDir: 'dist', + functionsDir: 'functions', + pluginsToInstall: [], + }) + mockSaveNetlifyToml.mockResolvedValue(undefined) + mockSetupSite.mockResolvedValue({ deploy_hook: 'https://api.netlify.com/hooks/test' }) + }) + + describe('GitLab repository configuration', () => { + it('should use provider from repoData for GitLab repos', async () => { + const configManual = (await import('../../../../src/utils/init/config-manual.js')).default + + const repoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'git@gitlab.com:ownername/test.git', + branch: 'main', + provider: 'gitlab', + httpsUrl: 'https://gitlab.com/ownername/test', + } + + await configManual({ + command: mockCommand as BaseCommand, + repoData, + siteId: 'site-123', + }) + + const setupCall = mockSetupSite.mock.calls[0][0] as { repo: { provider: string; repo_path: string } } + expect(setupCall.repo.provider).toBe('gitlab') + expect(setupCall.repo.repo_path).toBe('ownername/test') + }) + + it('should use repo path (owner/name format) instead of SSH URL for GitLab', async () => { + const configManual = (await import('../../../../src/utils/init/config-manual.js')).default + + const repoData: RepoData = { + name: 'test', + owner: 'ownername', + repo: 'ownername/test', + url: 'git@gitlab.com:ownername/test.git', + branch: 'main', + provider: 'gitlab', + httpsUrl: 'https://gitlab.com/ownername/test', + } + + await configManual({ + command: mockCommand as BaseCommand, + repoData, + siteId: 'site-123', + }) + + const setupSiteCall = mockSetupSite.mock.calls[0][0] as { + repo: { repo_path: string } + } + expect(setupSiteCall.repo.repo_path).toBe('ownername/test') + expect(setupSiteCall.repo.repo_path).not.toBe('git@gitlab.com:ownername/test.git') + }) + + it('should fallback to manual provider when provider is null', async () => { + const configManual = (await import('../../../../src/utils/init/config-manual.js')).default + + const repoData: RepoData = { + name: 'test', + owner: 'user', + repo: 'user/test', + url: 'git@custom.com:user/test.git', + branch: 'main', + provider: null, + httpsUrl: 'https://custom.com/user/test', + } + + await configManual({ + command: mockCommand as BaseCommand, + repoData, + siteId: 'site-123', + }) + + const setupCall = mockSetupSite.mock.calls[0][0] as { repo: { provider: string } } + expect(setupCall.repo.provider).toBe('manual') + }) + }) + + describe('GitHub repository configuration', () => { + it('should use provider from repoData for GitHub repos', async () => { + const configManual = (await import('../../../../src/utils/init/config-manual.js')).default + + const repoData: RepoData = { + name: 'test', + owner: 'user', + repo: 'user/test', + url: 'git@github.com:user/test.git', + branch: 'main', + provider: 'github', + httpsUrl: 'https://github.com/user/test', + } + + await configManual({ + command: mockCommand as BaseCommand, + repoData, + siteId: 'site-123', + }) + + const setupCall = mockSetupSite.mock.calls[0][0] as { repo: { provider: string; repo_path: string } } + expect(setupCall.repo.provider).toBe('github') + expect(setupCall.repo.repo_path).toBe('user/test') + }) + }) +})