feat(cli): use default template when GH is offline (#12194)

fix #12136 

I add a prompt if there is an error when trying to download example files.
Maybe could be better to add an error class and in create-app.ts on every "console.error" trow a new Exception and manage it in catch. What you think ? 👯
This commit is contained in:
Marco Moretti 2020-05-26 18:39:18 +02:00 committed by GitHub
parent 2828b01950
commit b3e45fab5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 35 deletions

View file

@ -1,16 +1,16 @@
import retry from 'async-retry'
import chalk from 'chalk'
import cpy from 'cpy'
import fs from 'fs'
import makeDir from 'make-dir'
import os from 'os'
import path from 'path'
import {
hasExample,
hasRepo,
getRepoInfo,
downloadAndExtractExample,
downloadAndExtractRepo,
getRepoInfo,
hasExample,
hasRepo,
RepoInfo,
} from './helpers/examples'
import { tryGitInit } from './helpers/git'
@ -19,6 +19,8 @@ import { isFolderEmpty } from './helpers/is-folder-empty'
import { getOnline } from './helpers/is-online'
import { shouldUseYarn } from './helpers/should-use-yarn'
export class DownloadError extends Error {}
export async function createApp({
appPath,
useNpm,
@ -75,7 +77,7 @@ export async function createApp({
)
process.exit(1)
}
} else {
} else if (example !== '__internal-testing-retry') {
const found = await hasExample(example)
if (!found) {
@ -109,24 +111,32 @@ export async function createApp({
process.chdir(root)
if (example) {
if (repoInfo) {
console.log(
`Downloading files from repo ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await downloadAndExtractRepo(root, repoInfo)
} else {
console.log(
`Downloading files for example ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await downloadAndExtractExample(root, example)
try {
if (repoInfo) {
const repoInfo2 = repoInfo
console.log(
`Downloading files from repo ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await retry(() => downloadAndExtractRepo(root, repoInfo2), {
retries: 3,
})
} else {
console.log(
`Downloading files for example ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await retry(() => downloadAndExtractExample(root, example), {
retries: 3,
})
}
} catch (reason) {
throw new DownloadError(reason)
}
// Copy our default `.gitignore` if the application did not provide one
const ignorePath = path.join(root, '.gitignore')
if (!fs.existsSync(ignorePath)) {

View file

@ -86,6 +86,10 @@ export async function downloadAndExtractExample(
root: string,
name: string
): Promise<void> {
if (name === '__internal-testing-retry') {
throw new Error('This is an internal example for testing the CLI.')
}
try {
return await pipeline(
got.stream('https://codeload.github.com/zeit/next.js/tar.gz/canary'),

View file

@ -4,12 +4,11 @@ import Commander from 'commander'
import path from 'path'
import prompts from 'prompts'
import checkForUpdate from 'update-check'
import { createApp } from './create-app'
import { createApp, DownloadError } from './create-app'
import { listExamples } from './helpers/examples'
import { shouldUseYarn } from './helpers/should-use-yarn'
import { validateNpmName } from './helpers/validate-pkg'
import packageJson from './package.json'
import { shouldUseYarn } from './helpers/should-use-yarn'
import { listExamples } from './helpers/examples'
let projectPath: string = ''
@ -166,12 +165,32 @@ async function run(): Promise<void> {
}
const example = typeof program.example === 'string' && program.example.trim()
await createApp({
appPath: resolvedProjectPath,
useNpm: !!program.useNpm,
example: example && example !== 'default' ? example : undefined,
examplePath: program.examplePath,
})
try {
await createApp({
appPath: resolvedProjectPath,
useNpm: !!program.useNpm,
example: example && example !== 'default' ? example : undefined,
examplePath: program.examplePath,
})
} catch (reason) {
if (!(reason instanceof DownloadError)) {
throw reason
}
const res = await prompts({
type: 'confirm',
name: 'builtin',
message:
`Could not download "${example}" because of a connectivity issue between your machine and GitHub.\n` +
`Do you want to use the default template instead?`,
initial: true,
})
if (!res.builtin) {
throw reason
}
await createApp({ appPath: resolvedProjectPath, useNpm: !!program.useNpm })
}
}
const update = checkForUpdate(packageJson).catch(() => null)

View file

@ -27,12 +27,14 @@
"prepublish": "yarn release"
},
"devDependencies": {
"@types/async-retry": "1.4.2",
"@types/node": "^12.6.8",
"@types/prompts": "2.0.1",
"@types/rimraf": "3.0.0",
"@types/tar": "4.0.3",
"@types/validate-npm-package-name": "3.0.0",
"@zeit/ncc": "^0.20.4",
"async-retry": "1.3.1",
"chalk": "2.4.2",
"commander": "2.20.0",
"cpy": "7.3.0",

View file

@ -1,12 +1,12 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs-extra'
import execa from 'execa'
import fs from 'fs-extra'
import os from 'os'
import path from 'path'
const cli = require.resolve('create-next-app/dist/index.js')
jest.setTimeout(1000 * 60 * 2)
jest.setTimeout(1000 * 60 * 5)
const run = (cwd, ...args) => execa('node', [cli, ...args], { cwd })
const runStarter = (cwd, ...args) => {
@ -229,6 +229,36 @@ describe('create next app', () => {
expect(res.stdout).toMatch(/Downloading files for example hello-world/)
})
})
it('should fall back to default template', async () => {
await usingTempDir(async (cwd) => {
const runExample = (...args) => {
const res = run(cwd, ...args)
function fallbackToTemplate(data) {
if (
/Do you want to use the default template instead/.test(
data.toString()
)
) {
res.stdout.removeListener('data', fallbackToTemplate)
res.stdin.write('\n')
}
}
res.stdout.on('data', fallbackToTemplate)
return res
}
const res = await runExample(
'fail-example',
'--example',
'__internal-testing-retry'
)
expect(res.exitCode).toBe(0)
})
})
}
it('should allow an example named default', async () => {

View file

@ -2407,6 +2407,13 @@
version "1.3.1"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
"@types/async-retry@1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.4.2.tgz#7f910188cd3893b51e32df51765ee8d5646053e3"
integrity sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==
dependencies:
"@types/retry" "*"
"@types/babel__code-frame@7.0.1":
version "7.0.1"
resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.1.tgz#baf2529c4abbfb5e4008c845efcfe39a187e2f99"
@ -2778,6 +2785,11 @@
dependencies:
"@types/node" "*"
"@types/retry@*":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
"@types/rimraf@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f"
@ -3585,6 +3597,13 @@ async-retry@1.2.3:
dependencies:
retry "0.12.0"
async-retry@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55"
integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==
dependencies:
retry "0.12.0"
async-sema@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.0.0.tgz#9e22d6783f0ab66a1cf330e21a905e39b3b3a975"