Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion src/commands/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { getSiteByName } from '../utils/get-site.js'
import openBrowser from '../utils/open-browser.js'
import { isInteractive } from '../utils/scripted-commands.js'
import { identify, reportError, setCommandForErrorReporting, track } from '../utils/telemetry/index.js'
import { getRequestUserAgent } from '../utils/user-agent.js'
import type { NetlifyOptions } from './types.js'
import type { CachedConfig } from '../lib/build.js'
import type { MinimalAccount } from '../utils/types.js'
Expand Down Expand Up @@ -666,7 +667,7 @@ export default class BaseCommand extends Command {
host?: string
pathPrefix?: string
} = {
userAgent: USER_AGENT,
userAgent: getRequestUserAgent(),
}

if (process.env.NETLIFY_API_URL) {
Expand Down
5 changes: 4 additions & 1 deletion src/commands/blobs/blobs-delete.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getStore } from '@netlify/blobs'

import { chalk, logAndThrowError, log } from '../../utils/command-helpers.js'
import { netlifyFetchForOrigin } from '../../utils/netlify-fetch.js'
import { promptBlobDelete } from '../../utils/prompts/blob-delete-prompts.js'

/**
Expand All @@ -10,8 +11,10 @@ export const blobsDelete = async (storeName: string, key: string, _options: Reco
const { api, siteInfo } = command.netlify
const { force } = _options

const apiURL = `${api.scheme}://${api.host}`
const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
apiURL,
fetch: netlifyFetchForOrigin(apiURL),
name: storeName,
siteID: siteInfo.id ?? '',
token: api.accessToken ?? '',
Expand Down
5 changes: 4 additions & 1 deletion src/commands/blobs/blobs-get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getStore } from '@netlify/blobs'
import { OptionValues } from 'commander'

import { chalk, logAndThrowError } from '../../utils/command-helpers.js'
import { netlifyFetchForOrigin } from '../../utils/netlify-fetch.js'
import BaseCommand from '../base-command.js'

interface Options extends OptionValues {
Expand All @@ -14,8 +15,10 @@ interface Options extends OptionValues {
export const blobsGet = async (storeName: string, key: string, options: Options, command: BaseCommand) => {
const { api, siteInfo } = command.netlify
const { output } = options
const apiURL = `${api.scheme}://${api.host}`
const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
apiURL,
fetch: netlifyFetchForOrigin(apiURL),
name: storeName,
siteID: siteInfo?.id ?? '',
token: api.accessToken ?? '',
Expand Down
5 changes: 4 additions & 1 deletion src/commands/blobs/blobs-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import AsciiTable from 'ascii-table'
import { OptionValues } from 'commander'

import { chalk, logAndThrowError, log, logJson } from '../../utils/command-helpers.js'
import { netlifyFetchForOrigin } from '../../utils/netlify-fetch.js'
import BaseCommand from '../base-command.js'

interface Options extends OptionValues {
Expand All @@ -13,8 +14,10 @@ interface Options extends OptionValues {

export const blobsList = async (storeName: string, options: Options, command: BaseCommand) => {
const { api, siteInfo } = command.netlify
const apiURL = `${api.scheme}://${api.host}`
const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
apiURL,
fetch: netlifyFetchForOrigin(apiURL),
name: storeName,
siteID: siteInfo.id,
token: api.accessToken ?? '',
Expand Down
5 changes: 4 additions & 1 deletion src/commands/blobs/blobs-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getStore } from '@netlify/blobs'
import { OptionValues } from 'commander'

import { chalk, logAndThrowError, isNodeError, log } from '../../utils/command-helpers.js'
import { netlifyFetchForOrigin } from '../../utils/netlify-fetch.js'
import { promptBlobSetOverwrite } from '../../utils/prompts/blob-set-prompt.js'
import BaseCommand from '../base-command.js'

Expand All @@ -22,8 +23,10 @@ export const blobsSet = async (
) => {
const { api, siteInfo } = command.netlify
const { force, input } = options
const apiURL = `${api.scheme}://${api.host}`
const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
apiURL,
fetch: netlifyFetchForOrigin(apiURL),
name: storeName,
siteID: siteInfo.id,
token: api.accessToken ?? '',
Expand Down
6 changes: 1 addition & 5 deletions src/commands/claim/claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import type BaseCommand from '../base-command.js'
export const claim = async (siteId: string, dropToken: string, command: BaseCommand) => {
await command.authenticate()

const apiBase = command.netlify.api.basePath
const dropApiOptions = {
apiBase,
userAgent: command.netlify.api.defaultHeaders['User-agent'] || 'netlify-cli',
}
const dropApiOptions = { apiBase: command.netlify.api.basePath }

const authToken = command.netlify.api.accessToken
if (!authToken) {
Expand Down
5 changes: 3 additions & 2 deletions src/commands/database/db-migration-pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { dirname, resolve, isAbsolute } from 'path'
import inquirer from 'inquirer'

import { log, logJson } from '../../utils/command-helpers.js'
import { netlifyFetch } from '../../utils/netlify-fetch.js'
import execa from '../../utils/execa.js'
import BaseCommand from '../base-command.js'
import { readApiErrorMessage } from './util/api-errors.js'
Expand Down Expand Up @@ -81,7 +82,7 @@ const fetchMigrations = async (ctx: ApiContext, branch: string): Promise<Migrati
const url = new URL(`${ctx.basePath}/sites/${encodeURIComponent(ctx.siteId)}/database/migrations`)
url.searchParams.set('branch', branch)

const response = await fetch(url, {
const response = await netlifyFetch(url, {
headers: { Authorization: `Bearer ${ctx.token}` },
})

Expand All @@ -100,7 +101,7 @@ const fetchMigrationContent = async (ctx: ApiContext, name: string, branch: stri
)
url.searchParams.set('branch', branch)

const response = await fetch(url, {
const response = await netlifyFetch(url, {
headers: { Authorization: `Bearer ${ctx.token}` },
})

Expand Down
5 changes: 3 additions & 2 deletions src/commands/database/db-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readdir } from 'fs/promises'
import { join } from 'path'

import { chalk, log, logJson, netlifyCommand } from '../../utils/command-helpers.js'
import { netlifyFetch } from '../../utils/netlify-fetch.js'
import BaseCommand from '../base-command.js'
import {
type AppliedMigrationsFetcher,
Expand Down Expand Up @@ -157,7 +158,7 @@ const fetchBranchConnectionString = async (ctx: ServerContext, branchId: string)
`${ctx.basePath}/sites/${encodeURIComponent(ctx.siteId)}/database/branch/${encodeURIComponent(branchId)}`,
)

const response = await fetch(url, {
const response = await netlifyFetch(url, {
headers: { Authorization: `Bearer ${token}` },
})

Expand All @@ -183,7 +184,7 @@ const fetchSiteDatabase = async (ctx: ServerContext): Promise<{ connectionString

let response: Response
try {
response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } })
response = await netlifyFetch(url, { headers: { Authorization: `Bearer ${token}` } })
} catch {
return null
}
Expand Down
4 changes: 3 additions & 1 deletion src/commands/database/util/applied-migrations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { type SQLExecutor } from '@netlify/dev'

import { netlifyFetch } from '../../../utils/netlify-fetch.js'

import { readApiErrorMessage } from './api-errors.js'
import { MIGRATIONS_TABLE } from './constants.js'

Expand Down Expand Up @@ -47,7 +49,7 @@ export const remoteAppliedMigrations =
const url = new URL(`${options.basePath}/sites/${encodeURIComponent(options.siteId)}/database/migrations`)
url.searchParams.set('branch', options.branch)

const response = await fetch(url, {
const response = await netlifyFetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
Expand Down
7 changes: 1 addition & 6 deletions src/commands/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1237,12 +1237,7 @@ const anonymousDeploy = async (options: DeployOptionValues, command: BaseCommand

log(`\n${NETLIFYDEVLOG} Deploying ${filesCount} files anonymously...`)

const apiBase = command.netlify.api.basePath

const dropApiOptions = {
apiBase,
userAgent: command.netlify.api.defaultHeaders['User-agent'] || 'netlify-cli',
}
const dropApiOptions = { apiBase: command.netlify.api.basePath }

const statusCb = options.json ? () => {} : deployProgressCb()

Expand Down
3 changes: 2 additions & 1 deletion src/commands/logs/log-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { NetlifyAPI } from '@netlify/api'
import parseDuration from 'parse-duration'

import { chalk } from '../../utils/command-helpers.js'
import { netlifyFetch } from '../../utils/netlify-fetch.js'

import { LOG_LEVELS } from './log-levels.js'

Expand Down Expand Up @@ -95,7 +96,7 @@ const debugLog = (message: string) => {
export const debugFetch = async (url: string, init?: RequestInit): Promise<Response> => {
debugLog(`→ ${init?.method ?? 'GET'} ${url}`)
const start = performance.now()
const response = await fetch(url, init)
const response = await netlifyFetch(url, init)
const elapsed = (performance.now() - start).toFixed(0)
debugLog(`← ${response.status.toString()} ${response.statusText} (${elapsed}ms)`)
return response
Expand Down
5 changes: 3 additions & 2 deletions src/lib/geo-location.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fetch from 'node-fetch'
import { type Geolocation, mockLocation } from '@netlify/dev-utils'

import { netlifyFetch } from '../utils/netlify-fetch.js'

const API_URL = 'https://netlifind.netlify.app'
const STATE_GEO_PROPERTY = 'geolocation'
// 24 hours
Expand Down Expand Up @@ -93,7 +94,7 @@ export const getGeoLocation = async ({
* Returns geolocation data from a remote API.
*/
const getGeoLocationFromAPI = async (): Promise<Geolocation> => {
const res = await fetch(API_URL, {
const res = await netlifyFetch(API_URL, {
method: 'GET',
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
})
Expand Down
42 changes: 23 additions & 19 deletions src/recipes/ai-context/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { promises as fs } from 'node:fs'
import { dirname, resolve } from 'node:path'
import semver from 'semver'
import { chalk, logAndThrowError, log, version } from '../../utils/command-helpers.js'
import { chalk, log, version } from '../../utils/command-helpers.js'
import { netlifyFetch } from '../../utils/netlify-fetch.js'
import type { RunRecipeOptions } from '../../commands/recipes/recipes.js'

const ATTRIBUTES_REGEX = /(\S*)="([^\s"]*)"/gim
Expand Down Expand Up @@ -41,16 +42,12 @@ export interface ConsumerConfig {
}

let contextConsumers: ConsumerConfig[] = []
export const getContextConsumers = async (cliVersion: string) => {
export const getContextConsumers = async () => {
if (contextConsumers.length > 0) {
return contextConsumers
}
try {
const res = await fetch(`${BASE_URL}/context-consumers`, {
headers: {
'user-agent': `NetlifyCLI ${cliVersion}`,
},
})
const res = await netlifyFetch(`${BASE_URL}/context-consumers`)

if (!res.ok) {
return []
Expand All @@ -63,7 +60,7 @@ export const getContextConsumers = async (cliVersion: string) => {
return contextConsumers
}

export const downloadFile = async (cliVersion: string, contextConfig: ContextConfig, consumer: ConsumerConfig) => {
export const downloadFile = async (contextConfig: ContextConfig, consumer: ConsumerConfig) => {
try {
if (!contextConfig.endpoint) {
return null
Expand All @@ -79,11 +76,7 @@ export const downloadFile = async (cliVersion: string, contextConfig: ContextCon
url.protocol = overridingUrl.protocol
}

const res = await fetch(url, {
headers: {
'user-agent': `NetlifyCLI ${cliVersion}`,
},
})
const res = await netlifyFetch(url)

if (!res.ok) {
return null
Expand Down Expand Up @@ -221,21 +214,24 @@ export const deleteFile = async (path: string) => {
}
}

export const downloadAndWriteContextFiles = async (consumer: ConsumerConfig, { command }: RunRecipeOptions) => {
await Promise.allSettled(
export const downloadAndWriteContextFiles = async (
consumer: ConsumerConfig,
{ command }: RunRecipeOptions,
): Promise<boolean> => {
const results = await Promise.allSettled(
Object.keys(consumer.contextScopes).map(async (contextKey) => {
const contextConfig = consumer.contextScopes[contextKey]

const { contents: downloadedFile, minimumCLIVersion } =
(await downloadFile(version, contextConfig, consumer).catch(() => null)) ?? {}
(await downloadFile(contextConfig, consumer).catch(() => null)) ?? {}

if (!downloadedFile) {
return logAndThrowError(
throw new Error(
`An error occurred when pulling the latest context file for scope ${contextConfig.scope}. Please try again.`,
)
}
if (minimumCLIVersion && semver.lt(version, minimumCLIVersion)) {
return logAndThrowError(
throw new Error(
`This command requires version ${minimumCLIVersion} or above of the Netlify CLI. Refer to ${chalk.underline(
'https://ntl.fyi/update-cli',
)} for information on how to update.`,
Expand Down Expand Up @@ -264,7 +260,7 @@ export const downloadAndWriteContextFiles = async (consumer: ConsumerConfig, { c
absoluteFilePath,
)} contains the latest version of the context files.`,
)
return
return false
}

// We must preserve any overrides found in the existing file.
Expand All @@ -289,6 +285,14 @@ export const downloadAndWriteContextFiles = async (consumer: ConsumerConfig, { c
await writeFile(absoluteFilePath, contents)

log(`${existing ? 'Updated' : 'Created'} context files at ${chalk.underline(absoluteFilePath)}`)
return true
}),
)

const failure = results.find((result): result is PromiseRejectedResult => result.status === 'rejected')
if (failure) {
throw failure.reason
}

return results.some((result) => result.status === 'fulfilled' && result.value)
}
12 changes: 9 additions & 3 deletions src/recipes/ai-context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import inquirer from 'inquirer'
import execa from 'execa'

import type { RunRecipeOptions } from '../../commands/recipes/recipes.js'
import { logAndThrowError, log, version } from '../../utils/command-helpers.js'
import { logAndThrowError, log } from '../../utils/command-helpers.js'
import { track } from '../../utils/telemetry/index.js'

import {
getExistingContext,
Expand All @@ -20,7 +21,7 @@ export const description = 'Manage context files for AI tools'
// context consumers endpoints returns all supported IDE and other consumers
// that can be used to pull context files. It also includes a catchall consumer
// for outlining all context that an unspecified consumer would handle.
const allContextConsumers = await getContextConsumers(version)
const allContextConsumers = await getContextConsumers()
const cliContextConsumers = allContextConsumers.filter((consumer) => !consumer.hideFromCLI)

const rulesForDefaultConsumer = allContextConsumers.find((consumer) => consumer.key === 'catchall-consumer') ?? {
Expand Down Expand Up @@ -156,8 +157,9 @@ export const run = async (runOptions: RunRecipeOptions) => {
return
}

let wroteFiles = false
try {
await downloadAndWriteContextFiles(consumer, runOptions)
wroteFiles = await downloadAndWriteContextFiles(consumer, runOptions)

// the deprecated MCP file path
// let's remove that file if it exists.
Expand All @@ -171,4 +173,8 @@ export const run = async (runOptions: RunRecipeOptions) => {
} catch (error) {
logAndThrowError(error)
}

if (wroteFiles) {
await track('sites_aiContextInstalled', { consumer: consumer.key })
}
}
5 changes: 4 additions & 1 deletion src/recipes/blobs-migrate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import pMap from 'p-map'

import BaseCommand from '../../commands/base-command.js'
import { logAndThrowError, log } from '../../utils/command-helpers.js'
import { netlifyFetchForOrigin } from '../../utils/netlify-fetch.js'

export const description = 'Migrate legacy Netlify Blobs stores'

Expand All @@ -21,8 +22,10 @@ export const run = async ({ args, command }: Options) => {

const [storeName] = args
const { api, siteInfo } = command.netlify
const apiURL = `${api.scheme}://${api.host}`
const clientOptions = {
apiURL: `${api.scheme}://${api.host}`,
apiURL,
fetch: netlifyFetchForOrigin(apiURL),
siteID: siteInfo.id,
token: api.accessToken ?? '',
}
Expand Down
Loading
Loading