Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
472e5ee
feat: fix handling of gitlab repos
sarahetter Feb 13, 2026
a2b7181
fix: types
sarahetter Feb 13, 2026
7e420b0
Merge branch 'main' into sarahetter/gitlab
sarahetter Feb 18, 2026
bcddb62
chore: improve reliability of deploy integration tests (#7951)
serhalp Feb 18, 2026
7fda9dd
feat: begin integrating `@netlify/dev` (#7950)
eduardoboucas Feb 19, 2026
ac45c4d
feat!: remove `sites:create-template` command (#7946)
sarahetter Feb 19, 2026
94c0146
chore(ci): fix flakey integration tests (#7958)
eduardoboucas Feb 21, 2026
6adbd47
fix: remove dead path
sarahetter Feb 23, 2026
ebc8b07
Merge branch 'main' into sarahetter/gitlab
sarahetter Feb 23, 2026
4b636c3
Merge branch 'main' into sarahetter/gitlab
sarahetter Mar 31, 2026
f49c2e8
Merge remote-tracking branch 'origin/main' into sarahetter/gitlab
sarahetter May 5, 2026
f849e35
test: address review feedback on get-repo-data and config-manual tests
sarahetter May 5, 2026
8a3ba28
Merge branch 'sarahetter/gitlab' of github.com:netlify/cli into sarah…
sarahetter May 5, 2026
73b5ecb
Merge branch 'main' into sarahetter/gitlab
sarahetter May 7, 2026
deb8939
Merge branch 'main' into sarahetter/gitlab
sarahetter Jun 2, 2026
82b4aad
Merge branch 'main' into sarahetter/gitlab
sarahetter Jun 9, 2026
93db64d
Merge branch 'main' into sarahetter/gitlab
sarahetter Jun 10, 2026
9544148
fix: fallback to manual provider for unsupported Git hosts
jaredm563 Jun 12, 2026
69318bb
Merge branch 'main' into sarahetter/gitlab
jaredm563 Jun 12, 2026
f4a009d
fix: remove bold formatting from package name in db-status output and…
jaredm563 Jun 12, 2026
c31135d
Merge branch 'sarahetter/gitlab' of https://github.com/netlify/cli in…
jaredm563 Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/commands/database/db-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,16 +265,16 @@ const renderPretty = (params: RenderParams) => {
}
} else {
primary(STATUS_WARN, 'Netlify Database is not enabled for this project')
secondary(`Install the ${chalk.bold(NETLIFY_DATABASE_PACKAGE)} package and deploy your site to automatically`)
secondary(`Install the ${NETLIFY_DATABASE_PACKAGE} package and deploy your site to automatically`)
secondary(`provision a database. Refer to ${DOCS_URL} for more information.`)
}
log('')

if (packageInstalled) {
primary(STATUS_GOOD, `The ${chalk.bold(NETLIFY_DATABASE_PACKAGE)} package is installed`)
primary(STATUS_GOOD, `The ${NETLIFY_DATABASE_PACKAGE} package is installed`)
secondary(`For a full API reference, visit ${DOCS_URL}`)
} else {
primary(STATUS_WARN, `The ${chalk.bold(NETLIFY_DATABASE_PACKAGE)} package is not installed`)
primary(STATUS_WARN, `The ${NETLIFY_DATABASE_PACKAGE} package is not installed`)
secondary(`Install it with \`npm install ${NETLIFY_DATABASE_PACKAGE}\``)
secondary(`Refer to ${DOCS_URL} for more information`)
}
Expand Down
7 changes: 5 additions & 2 deletions src/utils/init/config-manual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ const addDeployHook = async (deployHook: string | undefined): Promise<boolean> =
return deployHookAdded
}

const isSupportedProvider = (provider: string | null): provider is 'github' | 'gitlab' =>
provider === 'github' || provider === 'gitlab'

export default async function configManual({
command,
repoData,
Expand All @@ -86,9 +89,9 @@ export default async function configManual({
const deployKey = await createDeployKey({ api })
await addDeployKey(deployKey)

const repoPath = await getRepoPath({ repoData })
const repoPath = repoData.repo ?? (await getRepoPath({ repoData }))
const repo = {
provider: 'manual',
provider: isSupportedProvider(repoData.provider) ? repoData.provider : 'manual',
repo_path: repoPath,
repo_branch: repoData.branch,
allowed_branches: [repoData.branch],
Expand Down
12 changes: 8 additions & 4 deletions src/utils/telemetry/report-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ export const reportError = async function (error, config = {}) {

// spawn detached child process to handle send and wait for the http request to finish
// otherwise it can get canceled
await execa(process.execPath, [join(dirPath, 'request.js'), options], {
detached: true,
stdio: 'ignore',
})
try {
await execa(process.execPath, [join(dirPath, 'request.js'), options], {
detached: true,
stdio: 'ignore',
})
} catch {
return
}
}
6 changes: 5 additions & 1 deletion tests/unit/commands/database/db-status.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { relative, sep } from 'path'

import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest'
import { describe, expect, test, vi, beforeEach, afterEach, afterAll } from 'vitest'

const {
mockReaddir,
Expand Down Expand Up @@ -255,6 +255,10 @@ afterEach(() => {
delete process.env.NETLIFY_DB_BRANCH
})

afterAll(() => {
vi.unstubAllGlobals()
})

describe('statusDb', () => {
describe('enabled flag', () => {
test('reports enabled=true when NETLIFY_DB_URL is set', async () => {
Expand Down
143 changes: 143 additions & 0 deletions tests/unit/utils/get-repo-data.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'

import getRepoData from '../../../src/utils/get-repo-data.js'

const mockGitConfig = vi.fn()
const mockFindUp = vi.fn()
const mockGitRepoInfo = vi.fn()

vi.mock('gitconfiglocal', () => ({
default: (workingDir: string, cb: (err: Error | null, config: unknown) => void) => {
try {
cb(null, mockGitConfig(workingDir))
} catch (err) {
cb(err as Error, null)
}
},
}))

vi.mock('find-up', () => ({
findUp: (...args: unknown[]): unknown => mockFindUp(...args),
}))

vi.mock('git-repo-info', () => ({
default: (): unknown => mockGitRepoInfo(),
}))

vi.mock('../../../src/utils/command-helpers.js', () => ({
log: vi.fn(),
}))

describe('getRepoData', () => {
beforeEach(() => {
vi.clearAllMocks()
mockFindUp.mockResolvedValue('/test/.git')
mockGitRepoInfo.mockReturnValue({ branch: 'main' })
})

it('parses GitHub SSH URLs', async () => {
mockGitConfig.mockReturnValue({ remote: { origin: { url: 'git@github.com:ownername/test.git' } } })

const result = await getRepoData({ workingDir: '/test' })

expect(result).toEqual({
name: 'test',
owner: 'ownername',
repo: 'ownername/test',
url: 'git@github.com:ownername/test.git',
branch: 'main',
provider: 'github',
httpsUrl: 'https://github.com/ownername/test',
})
})

it('parses GitLab SSH URLs', async () => {
mockGitConfig.mockReturnValue({ remote: { origin: { url: 'git@gitlab.com:ownername/test.git' } } })

const result = await getRepoData({ workingDir: '/test' })

expect(result).toEqual({
name: 'test',
owner: 'ownername',
repo: 'ownername/test',
url: 'git@gitlab.com:ownername/test.git',
branch: 'main',
provider: 'gitlab',
httpsUrl: 'https://gitlab.com/ownername/test',
})
})

it('parses GitHub HTTPS URLs', async () => {
mockGitConfig.mockReturnValue({ remote: { origin: { url: 'https://github.com/ownername/test.git' } } })

const result = await getRepoData({ workingDir: '/test' })

expect(result).toMatchObject({
provider: 'github',
repo: 'ownername/test',
httpsUrl: 'https://github.com/ownername/test',
})
})

it('parses GitLab HTTPS URLs', async () => {
mockGitConfig.mockReturnValue({ remote: { origin: { url: 'https://gitlab.com/ownername/test.git' } } })

const result = await getRepoData({ workingDir: '/test' })

expect(result).toMatchObject({
provider: 'gitlab',
repo: 'ownername/test',
httpsUrl: 'https://gitlab.com/ownername/test',
})
})

it('uses host as provider for unknown Git hosts', async () => {
mockGitConfig.mockReturnValue({
remote: { origin: { url: 'git@custom-git.example.com:user/test.git' } },
})

const result = await getRepoData({ workingDir: '/test' })

expect(result).toMatchObject({
provider: 'custom-git.example.com',
repo: 'user/test',
httpsUrl: 'https://custom-git.example.com/user/test',
})
})

it('uses the specified remote name when provided', async () => {
mockGitConfig.mockReturnValue({
remote: {
origin: { url: 'git@github.com:owner/origin-repo.git' },
upstream: { url: 'git@gitlab.com:owner/upstream-repo.git' },
},
})

const result = await getRepoData({ workingDir: '/test', remoteName: 'upstream' })

expect(result).toMatchObject({
provider: 'gitlab',
repo: 'owner/upstream-repo',
})
})

it('returns an error when no Git remote is found', async () => {
mockFindUp.mockResolvedValue(undefined)
mockGitConfig.mockReturnValue({ remote: {} })

const result = await getRepoData({ workingDir: '/test' })

expect(result).toEqual({ error: 'No Git remote found' })
})

it('returns an error when the requested remote is not defined', async () => {
mockGitConfig.mockReturnValue({ remote: { origin: { url: 'git@github.com:owner/repo.git' } } })

const result = await getRepoData({ workingDir: '/test', remoteName: 'missing' })

expect(result).toEqual({
error:
'The specified remote "missing" is not defined in Git repo. Please use --git-remote-name flag to specify a remote.',
})
})
Comment thread
sarahetter marked this conversation as resolved.
})
Loading
Loading