Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cb881e8

Browse filesBrowse files
authored
feat(init): prompt to configure build settings in 'no git remote' case (#7258)
* test: add more uses of DEBUG_TESTS in mock api helper * feat(init): prompt to configure build settings in 'no git remote' case We currently do this in the 'happy path' case where a git remote is found (regardless of whether the use chooses to *connect* their site to this git remote (i.e. configure Netlify CD), but not in the case where the user chooses the "Yes, create and deploy site manually" answer to the "Do you want to create a Netlify site without a git repository?". It turns out this is a pretty common choice, and it's confusing that it doesn't detect and recommend build settings for your project and prompt you to write them to a `netlify.toml` This ports over the same flow to this scenario. * fix(init): adjust misleading message deploy builds by default now
1 parent 4e0f92f commit cb881e8
Copy full SHA for cb881e8

File tree

Expand file treeCollapse file tree

5 files changed

+140
-9
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+140
-9
lines changed

‎src/commands/init/init.ts

Copy file name to clipboardExpand all lines: src/commands/init/init.ts
+44-5Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type BaseCommand from '../base-command.js'
1111
import { link } from '../link/link.js'
1212
import { sitesCreate } from '../sites/sites-create.js'
1313
import type { CLIState, SiteInfo } from '../../utils/types.js'
14+
import { getBuildSettings, saveNetlifyToml } from '../../utils/init/utils.js'
1415

1516
const persistState = ({ siteInfo, state }: { siteInfo: SiteInfo; state: CLIState }): void => {
1617
// Save to .netlify/state.json file
@@ -38,15 +39,47 @@ const logExistingAndExit = ({ siteInfo }: { siteInfo: SiteInfo }): never => {
3839
/**
3940
* Creates and new site and exits the process
4041
*/
41-
const createNewSiteAndExit = async ({ command, state }: { command: BaseCommand; state: CLIState }): Promise<never> => {
42+
const createNewSiteAndExit = async ({
43+
command,
44+
state,
45+
disableLinking,
46+
}: {
47+
command: BaseCommand
48+
state: CLIState
49+
disableLinking: boolean
50+
}): Promise<never> => {
4251
const siteInfo = await sitesCreate({}, command)
4352

4453
log(`"${siteInfo.name}" site was created`)
4554
log()
46-
log(`To deploy to this site. Run your site build and then ${chalk.cyanBright.bold('netlify deploy')}`)
4755

4856
persistState({ state, siteInfo })
4957

58+
if (!disableLinking) {
59+
const { shouldConfigureBuild } = await inquirer.prompt<{ shouldConfigureBuild: boolean }>([
60+
{
61+
type: 'confirm',
62+
name: 'shouldConfigureBuild',
63+
message: `Do you want to configure build settings? We'll suggest settings for your project automatically`,
64+
},
65+
])
66+
if (shouldConfigureBuild) {
67+
const {
68+
cachedConfig: { configPath },
69+
config,
70+
repositoryRoot,
71+
} = command.netlify
72+
const { baseDir, buildCmd, buildDir, functionsDir } = await getBuildSettings({
73+
config,
74+
command,
75+
})
76+
await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir })
77+
}
78+
}
79+
80+
log()
81+
log(`To deploy to this site, run ${chalk.cyanBright.bold('netlify deploy')}`)
82+
5083
return exit()
5184
}
5285

@@ -90,10 +123,12 @@ const handleNoGitRemoteAndExit = async ({
90123
command,
91124
error,
92125
state,
126+
disableLinking,
93127
}: {
94128
command: BaseCommand
95129
error?: unknown
96130
state: CLIState
131+
disableLinking: boolean
97132
}): Promise<never> => {
98133
log()
99134
log(chalk.yellow('No git remote was found, would you like to set one up?'))
@@ -124,7 +159,7 @@ git remote add origin https://github.com/YourUserName/RepoName.git
124159
])
125160

126161
if (noGitRemoteChoice === NEW_SITE_NO_GIT) {
127-
return await createNewSiteAndExit({ state, command })
162+
return createNewSiteAndExit({ state, command, disableLinking })
128163
}
129164
return logGitSetupInstructionsAndExit()
130165
}
@@ -153,7 +188,6 @@ const createOrLinkSiteToRepo = async (command: BaseCommand) => {
153188
await track('sites_initStarted', {
154189
type: 'new site',
155190
})
156-
// run site:create command
157191
return await sitesCreate({}, command)
158192
}
159193
// run link command
@@ -188,7 +222,12 @@ export const init = async (options: OptionValues, command: BaseCommand): Promise
188222
// Look for local repo
189223
const repoData = await getRepoData({ workingDir: command.workingDir, remoteName: options.gitRemoteName })
190224
if ('error' in repoData) {
191-
return await handleNoGitRemoteAndExit({ command, error: repoData.error, state })
225+
return await handleNoGitRemoteAndExit({
226+
command,
227+
error: repoData.error,
228+
state,
229+
disableLinking: options.disableLinking,
230+
})
192231
}
193232

194233
const siteInfo = isEmpty(existingSiteInfo) ? await createOrLinkSiteToRepo(command) : existingSiteInfo

‎src/utils/init/utils.ts

Copy file name to clipboardExpand all lines: src/utils/init/utils.ts
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ export const getBuildSettings = async ({
9595
config: NormalizedCachedConfigConfig
9696
}) => {
9797
const settings = await detectBuildSettings(command)
98-
// TODO: add prompt for asking to choose the build command
9998
const setting: Partial<Settings> = settings.length > 0 ? settings[0] : {}
10099
const { defaultBaseDir, defaultBuildCmd, defaultBuildDir, defaultFunctionsDir, recommendedPlugins } =
101100
normalizeSettings(setting, config, command)

‎tests/integration/commands/init/init.test.ts

Copy file name to clipboardExpand all lines: tests/integration/commands/init/init.test.ts
+84-1Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ describe.concurrent('commands/init', () => {
138138
answer: answerWithValue(publish),
139139
},
140140
{
141-
question: 'No netlify.toml detected',
141+
question: 'No netlify.toml detected. Would you like to create one with these build settings?',
142142
answer: CONFIRM,
143143
},
144144
{ question: 'Give this Netlify SSH public key access to your repository', answer: CONFIRM },
@@ -213,6 +213,89 @@ describe.concurrent('commands/init', () => {
213213
})
214214
})
215215

216+
test('prompts to configure build settings when no git remote is found', async (t) => {
217+
const publish = 'custom-publish'
218+
const initQuestions = [
219+
{
220+
question: 'Yes, create and deploy site manually',
221+
answer: answerWithValue(CONFIRM),
222+
},
223+
{ question: 'Team: (Use arrow keys)', answer: CONFIRM },
224+
{
225+
question: 'Site name (leave blank for a random name; you can change it later)',
226+
answer: answerWithValue('test-site-name'),
227+
},
228+
{
229+
question: `Do you want to configure build settings? We'll suggest settings for your project automatically`,
230+
answer: answerWithValue(CONFIRM),
231+
},
232+
{
233+
question: 'Your build command (hugo build/yarn run build/etc)',
234+
answer: CONFIRM,
235+
},
236+
{
237+
question: 'Directory to deploy (blank for current dir)',
238+
answer: answerWithValue(publish),
239+
},
240+
{
241+
question: 'No netlify.toml detected. Would you like to create one with these build settings?',
242+
answer: CONFIRM,
243+
},
244+
]
245+
246+
const routes = [
247+
{
248+
path: 'accounts',
249+
response: [{ slug: 'test-account' }],
250+
},
251+
{
252+
path: 'sites',
253+
response: [],
254+
},
255+
{
256+
path: 'sites/site_id',
257+
response: {
258+
admin_url: 'https://app.netlify.com/sites/site-name/overview',
259+
ssl_url: 'https://site-name.netlify.app/',
260+
id: 'site_id',
261+
name: 'site-name',
262+
build_settings: {},
263+
},
264+
},
265+
{
266+
path: 'user',
267+
response: { name: 'test user', slug: 'test-user', email: 'user@test.com' },
268+
},
269+
{
270+
path: 'test-account/sites',
271+
method: 'POST' as const,
272+
response: { id: 'site_id', name: 'test-site-name' },
273+
},
274+
]
275+
276+
await withSiteBuilder(t, async (builder) => {
277+
await builder.build()
278+
279+
await withMockApi(routes, async ({ apiUrl }) => {
280+
const childProcess = execa(cliPath, ['init'], {
281+
cwd: builder.directory,
282+
env: { NETLIFY_API_URL: apiUrl, NETLIFY_AUTH_TOKEN: 'fake-token' },
283+
encoding: 'utf8',
284+
})
285+
286+
handleQuestions(childProcess, initQuestions)
287+
288+
await childProcess
289+
290+
await assertNetlifyToml(t, builder.directory, {
291+
command: '# no build command',
292+
functions: defaultFunctionsDirectory,
293+
publish,
294+
})
295+
})
296+
})
297+
})
298+
216299
test('netlify init new Next.js site', async (t) => {
217300
const [command, publish] = ['custom-build-command', 'custom-publish']
218301
const initQuestions = [

‎tests/integration/utils/handle-questions.ts

Copy file name to clipboardExpand all lines: tests/integration/utils/handle-questions.ts
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ interface Process {
1717
* Utility to mock the stdin of the cli. You must provide the correct number of
1818
* questions correctly typed or the process will keep waiting for input.
1919
*
20-
* @param process
21-
* @param questions
2220
* @param prompts questions that you know the CLI will ask and respective answers to mock
2321
*/
2422
export const handleQuestions = (

‎tests/integration/utils/mock-api.ts

Copy file name to clipboardExpand all lines: tests/integration/utils/mock-api.ts
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export const startMockApi = ({ routes, silent }: MockApiOptions): Promise<MockAp
5656
app[method.toLowerCase() as 'all' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'](
5757
`/api/v1/${path}`,
5858
function onRequest(req, res) {
59+
if (process.env.DEBUG_TESTS) {
60+
console.debug('[mock-api] ', req.method, req.path, req.body)
61+
}
5962
// validate request body
6063
if (requestBody !== undefined && !isDeepStrictEqual(requestBody, req.body)) {
6164
res.status(500)
@@ -70,18 +73,27 @@ export const startMockApi = ({ routes, silent }: MockApiOptions): Promise<MockAp
7073
})
7174

7275
app.get('/site/site_id/integrations/safe', function onRequest(req, res) {
76+
if (process.env.DEBUG_TESTS) {
77+
console.debug('[mock-api] ', req.method, req.path, req.body)
78+
}
7379
addRequest(requests, req)
7480
res.status(200)
7581
res.json({ site_id: 'site_id', integrations: [] })
7682
})
7783

7884
app.get('/team/account_id/integrations/installations/meta/site_id', function onRequest(req, res) {
85+
if (process.env.DEBUG_TESTS) {
86+
console.debug('[mock-api] ', req.method, req.path, req.body)
87+
}
7988
addRequest(requests, req)
8089
res.status(200)
8190
res.json({ site_id: 'site_id', integrations: [] })
8291
})
8392

8493
app.all('*', function onRequest(req, res) {
94+
if (process.env.DEBUG_TESTS) {
95+
console.debug('[mock-api] ', req.method, req.path, req.body)
96+
}
8597
addRequest(requests, req)
8698
if (!silent) {
8799
console.warn(`Route not found: (${req.method.toUpperCase()}) ${req.url}`)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.