Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
204 changes: 162 additions & 42 deletions src/commands/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
uploadDropFiles,
waitForDropDeploy,
} from '../../utils/deploy/drop-api.js'
import type { UploadFile } from '../../utils/deploy/upload-files.js'
import { getUploadList } from '../../utils/deploy/util.js'
import hashFiles from '../../utils/deploy/hash-files.js'
import { deployFileNormalizer, getEdgeFunctionsDistPathIfExists } from '../../utils/deploy/process-files.js'
Expand Down Expand Up @@ -583,6 +584,7 @@ const runDeploy = async ({
functionLogsUrl: string
edgeFunctionLogsUrl: string
sourceZipFileName?: string
uploadList: UploadFile[]
}> => {
let results
let deployId = existingDeployId
Expand Down Expand Up @@ -710,6 +712,7 @@ const runDeploy = async ({
functionLogsUrl,
edgeFunctionLogsUrl,
sourceZipFileName: uploadSourceZipResult?.sourceZipFileName,
uploadList: results.uploadList,
}
}

Expand Down Expand Up @@ -832,20 +835,65 @@ interface JsonData {
edge_function_logs: string
url?: string
source_zip_filename?: string
uploaded_files?: string[]
uploaded_functions?: string[]
uploaded_edge_functions?: string[]
}

const printResults = ({

export const printUploadedAssets = (uploadList: UploadFile[]): void => {
const staticFiles = uploadList.filter((f) => f.assetType === 'file').map((f) => f.normalizedPath)
const functions = uploadList.filter((f) => f.assetType === 'function').map((f) => f.normalizedPath)
const edgeFunctions = uploadList.filter((f) => f.assetType === 'edge-function').map((f) => f.normalizedPath)

log('')
log(chalk.cyanBright.bold(`Uploaded assets (${uploadList.length} total)`))
log('')

log(` Static files (${staticFiles.length}):`)
if (staticFiles.length === 0) {
log(' (none)')
} else {
for (const file of staticFiles) {
log(` ${file}`)
}
}
log('')

log(` Functions (${functions.length}):`)
if (functions.length === 0) {
log(' (none)')
} else {
for (const fn of functions) {
log(` ${fn}`)
}
}
log('')

log(` Edge functions (${edgeFunctions.length}):`)
if (edgeFunctions.length === 0) {
log(' (none)')
} else {
for (const ef of edgeFunctions) {
log(` ${ef}`)
}
}
}

export const printResults = ({
deployToProduction,
uploadSourceZip,
json,
results,
runBuildCommand,
showUploaded,
}: {
deployToProduction: boolean
uploadSourceZip: boolean
json: boolean
results: Awaited<ReturnType<typeof prepAndRunDeploy>>
runBuildCommand: boolean
showUploaded: boolean
}): void => {
const msgData: Record<string, string> = {
'Build logs': terminalLink(results.logsUrl, results.logsUrl, { fallback: false }),
Expand Down Expand Up @@ -876,6 +924,18 @@ const printResults = ({
jsonData.source_zip_filename = results.sourceZipFileName
}

if (showUploaded) {
jsonData.uploaded_files = results.uploadList
.filter((f) => f.assetType === 'file')
.map((f) => f.normalizedPath)
jsonData.uploaded_functions = results.uploadList
.filter((f) => f.assetType === 'function')
.map((f) => f.normalizedPath)
jsonData.uploaded_edge_functions = results.uploadList
.filter((f) => f.assetType === 'edge-function')
.map((f) => f.normalizedPath)
}

logJson(jsonData)
exit(0)
} else if (!isInteractive()) {
Expand All @@ -889,6 +949,10 @@ const printResults = ({
log(`Function logs: <${results.functionLogsUrl}>`)
log(`Edge function logs: <${results.edgeFunctionLogsUrl}>`)

if (showUploaded) {
printUploadedAssets(results.uploadList)
}

if (!deployToProduction) {
log()
log('If everything looks good on your draft URL, deploy it to your main project URL with the --prod flag:')
Expand Down Expand Up @@ -917,6 +981,10 @@ const printResults = ({

log(prettyjson.render(msgData))

if (showUploaded) {
printUploadedAssets(results.uploadList)
}

if (!deployToProduction) {
log()
log('If everything looks good on your draft URL, deploy it to your main project URL with the --prod flag:')
Expand Down Expand Up @@ -1154,6 +1222,83 @@ const ensureSiteExists = async (
return promptForSiteAction(options, command, site)
}

export const printAnonymousDeployResults = ({
claimCommand,
claimUrl,
deployId,
isPasswordProtected,
json,
showUploaded,
siteId,
siteUrl,
uploadList,
}: {
claimCommand: string
claimUrl: string
deployId: string
isPasswordProtected: boolean
json: boolean
showUploaded: boolean
siteId: string
siteUrl: string
uploadList: UploadFile[]
}): void => {
if (json) {
const jsonData: Record<string, unknown> = {
site_id: siteId,
site_url: siteUrl,
deploy_id: deployId,
claim_url: claimUrl,
claim_command: claimCommand,
...(isPasswordProtected ? { password: 'My-Drop-Site' } : {}),
}

if (showUploaded) {
jsonData.uploaded_files = uploadList.filter((f) => f.assetType === 'file').map((f) => f.normalizedPath)
jsonData.uploaded_functions = uploadList.filter((f) => f.assetType === 'function').map((f) => f.normalizedPath)
jsonData.uploaded_edge_functions = uploadList
.filter((f) => f.assetType === 'edge-function')
.map((f) => f.normalizedPath)
}

logJson(jsonData)
return
}

log('')
log(chalk.cyanBright.bold(`πŸš€ Deploy complete\n${'─'.repeat(64)}`))
log('')

const boxContent = isPasswordProtected
? `Site URL: ${terminalLink(siteUrl, siteUrl, { fallback: false })}\n\nPassword: My-Drop-Site`
: `Site URL: ${terminalLink(siteUrl, siteUrl, { fallback: false })}`

log(
boxen(boxContent, {
padding: 1,
margin: 1,
textAlignment: 'center',
borderStyle: 'round',
borderColor: NETLIFY_CYAN_HEX,
title: `β¬₯ Anonymous deploy is live β¬₯ `,
titleAlignment: 'center',
}),
)
log(` ${chalk.bold('Claim on Netlify:')}`)
log(` ${claimUrl}`)
log('')
log(` ${chalk.bold('Claim via CLI:')}`)
log(` ${claimCommand}`)
log('')
warn('Anonymously deployed sites need to be claimed within 60 minutes.')

if (showUploaded) {
printUploadedAssets(uploadList)
}

log('')
}

const anonymousDeploy = async (options: DeployOptionValues, command: BaseCommand) => {
const { workingDir } = command
const { site, config } = command.netlify
Expand Down Expand Up @@ -1264,10 +1409,12 @@ const anonymousDeploy = async (options: DeployOptionValues, command: BaseCommand
throw error
}

const uploadList = getUploadList(deployInfo.required, filesShaMap) as UploadListItem[]
// Anonymous deploys only allow static files (checkForFunctions() above blocks functions and
// edge functions), so every entry in filesShaMap has assetType: 'file'.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
const uploadList = getUploadList(deployInfo.required, filesShaMap) as UploadFile[]

if (uploadList.length > 0) {
await uploadDropFiles(dropApiOptions, deployInfo.deploy_id, uploadList, dropToken, {
await uploadDropFiles(dropApiOptions, deployInfo.deploy_id, uploadList as unknown as UploadListItem[], dropToken, {
statusCb,
})
}
Expand All @@ -1285,45 +1432,17 @@ const anonymousDeploy = async (options: DeployOptionValues, command: BaseCommand
const isPasswordProtected = !options.createdVia || options.createdVia === 'drop'
const claimUrl = `https://app.netlify.com/drop/${deployInfo.subdomain}#drop_token=${dropToken}`

if (options.json) {
logJson({
site_id: deployInfo.id,
site_url: siteUrl,
deploy_id: deployInfo.deploy_id,
claim_url: claimUrl,
claim_command: `netlify claim --site ${deployInfo.id} --token ${dropToken}`,
...(isPasswordProtected ? { password: 'My-Drop-Site' } : {}),
})
return
}

log('')
log(chalk.cyanBright.bold(`πŸš€ Deploy complete\n${'─'.repeat(64)}`))
log('')

const boxContent = isPasswordProtected
? `Site URL: ${terminalLink(siteUrl, siteUrl, { fallback: false })}\n\nPassword: My-Drop-Site`
: `Site URL: ${terminalLink(siteUrl, siteUrl, { fallback: false })}`

log(
boxen(boxContent, {
padding: 1,
margin: 1,
textAlignment: 'center',
borderStyle: 'round',
borderColor: NETLIFY_CYAN_HEX,
title: `β¬₯ Anonymous deploy is live β¬₯ `,
titleAlignment: 'center',
}),
)
log(` ${chalk.bold('Claim on Netlify:')}`)
log(` ${claimUrl}`)
log('')
log(` ${chalk.bold('Claim via CLI:')}`)
log(` netlify claim --site ${deployInfo.id} --token ${dropToken}`)
log('')
warn('Anonymously deployed sites need to be claimed within 60 minutes.')
log('')
printAnonymousDeployResults({
claimCommand: `netlify claim --site ${deployInfo.id} --token ${dropToken}`,
claimUrl,
deployId: deployInfo.deploy_id,
isPasswordProtected,
json: options.json ?? false,
showUploaded: options.showUploaded ?? false,
siteId: deployInfo.id,
siteUrl,
uploadList,
})
}

export const deploy = async (options: DeployOptionValues, command: BaseCommand) => {
Expand Down Expand Up @@ -1478,6 +1597,7 @@ export const deploy = async (options: DeployOptionValues, command: BaseCommand)
results,
deployToProduction,
uploadSourceZip: !!options.uploadSourceZip,
showUploaded: !!options.showUploaded,
})

if (options.open) {
Expand Down
2 changes: 2 additions & 0 deletions src/commands/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ For detailed configuration options, see the Netlify documentation.`,
false,
)
.option('--created-via <source>', 'Specify the source of the deploy (e.g., "cli", "drop")')
.option('--show-uploaded', 'Show list of files uploaded to the CDN during this deploy')
.addExamples([
'netlify deploy',
'netlify deploy --site my-first-project',
Expand All @@ -125,6 +126,7 @@ For detailed configuration options, see the Netlify documentation.`,
'netlify deploy --env "NODE_ENV=production" --secret-env "DATABASE_PASSWORD=$DB_PASSWORD"',
'netlify deploy --site-name my-new-site --team my-team # Create site and deploy',
'netlify deploy --allow-anonymous --dir ./public --no-build # Deploy without auth',
'netlify deploy --show-uploaded # Show which files were uploaded to the CDN',
])
.addHelpText('after', () => {
const docsUrl = 'https://docs.netlify.com/site-deploys/overview/'
Expand Down
1 change: 1 addition & 0 deletions src/commands/deploy/option_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type DeployOptionValues = BaseOptionValues & {
prod: boolean
prodIfUnlocked: boolean
secretEnv?: DeployEnvironmentVariable[]
showUploaded?: boolean
site?: string
siteName?: string
skipFunctionsCache: boolean
Expand Down
Loading