UNPKG

netlify-cli

Version:

Netlify command line tool

235 lines • 10.7 kB
import path from 'node:path'; import { fileURLToPath } from 'node:url'; import inquirer from 'inquirer'; import pick from 'lodash/pick.js'; import { render } from 'prettyjson'; import { v4 as uuid } from 'uuid'; import { chalk, logAndThrowError, getTerminalLink, log, logJson, warn, GitHubAPIError, } from '../../utils/command-helpers.js'; import execa from '../../utils/execa.js'; import getRepoData from '../../utils/get-repo-data.js'; import { getGitHubToken } from '../../utils/init/config-github.js'; import { configureRepo } from '../../utils/init/config.js'; import { deployedSiteExists, getGitHubLink, getTemplateName } from '../../utils/sites/create-template.js'; import { callLinkSite, createRepo, validateTemplate } from '../../utils/sites/utils.js'; import { track } from '../../utils/telemetry/index.js'; import { getSiteNameInput } from './sites-create.js'; export const sitesCreateTemplate = async (repository, options, command) => { const { accounts, api } = command.netlify; await command.authenticate(); const { globalConfig } = command.netlify; const ghToken = await getGitHubToken({ globalConfig }); const templateName = await getTemplateName({ ghToken, options, repository }); const { exists, isTemplate } = await validateTemplate({ templateName, ghToken }); if (!exists) { const githubLink = getGitHubLink({ options, templateName }); return logAndThrowError(`Could not find template ${chalk.bold(templateName)}. Please verify it exists and you can ${getTerminalLink('access to it on GitHub', githubLink)}`); } if (!isTemplate) { const githubLink = getGitHubLink({ options, templateName }); return logAndThrowError(`${getTerminalLink(chalk.bold(templateName), githubLink)} is not a valid GitHub template`); } let { accountSlug } = options; if (!accountSlug) { const { accountSlug: accountSlugInput } = await inquirer.prompt([ { type: 'list', name: 'accountSlug', message: 'Team:', choices: accounts.map((account) => ({ value: account.slug, name: account.name, })), }, ]); accountSlug = accountSlugInput; } const { name: nameFlag } = options; let site; let repoResp; // Allow the user to reenter site name if selected one isn't available const inputSiteName = async (name, hasExistingRepo) => { const { name: inputName } = await getSiteNameInput(name); const siteName = inputName.trim(); if (siteName && (await deployedSiteExists(siteName))) { log('A site with that name already exists'); return inputSiteName(); } try { const sites = await api.listSites({ name: siteName, filter: 'all' }); const siteFoundByName = sites.find((filteredSite) => filteredSite.name === siteName); if (siteFoundByName) { log('A site with that name already exists on your account'); return inputSiteName(); } } catch (error_) { return logAndThrowError(error_); } if (!hasExistingRepo) { try { // Create new repo from template let gitHubInputName = siteName || templateName; repoResp = await createRepo(templateName, ghToken, gitHubInputName); if (repoResp.errors && repoResp.errors[0].includes('Name already exists on this account')) { if (gitHubInputName === templateName) { gitHubInputName += `-${uuid().split('-')[0]}`; repoResp = await createRepo(templateName, ghToken, gitHubInputName); } else { warn(`It seems you have already created a repository with the name ${gitHubInputName}.`); return inputSiteName(); } } if (!repoResp.id) { throw new GitHubAPIError(repoResp.status, repoResp.message); } hasExistingRepo = true; } catch (error_) { if (error_.status === '404') { return logAndThrowError(`Could not create repository: ${error_.message}. Ensure that your GitHub personal access token grants permission to create repositories`); } else { return logAndThrowError(`Something went wrong trying to create the repository. We're getting the following error: '${error_.message}'. You can try to re-run this command again or open an issue in our repository: https://github.com/netlify/cli/issues`); } } } try { // FIXME(serhalp): `id` and `name` should be required in `netlify` package type site = (await api.createSiteInTeam({ accountSlug, body: { repo: { provider: 'github', // @ts-expect-error -- FIXME(serhalp): Supposedly this is does not exist. Investigate. repo: repoResp.full_name, // FIXME(serhalp): Supposedly this should be `public_repo`. Investigate. private: repoResp.private, // FIXME(serhalp): Supposedly this should be `repo_branch`. Investigate. branch: repoResp.default_branch, }, name: siteName, }, })); } catch (error_) { if (error_.status === 422) { log(`createSiteInTeam error: ${error_.status}: ${error_.message}`); log('Cannot create a site with that name. Site name may already exist. Please try a new name.'); return inputSiteName(undefined, hasExistingRepo); } return logAndThrowError(`createSiteInTeam error: ${error_.status}: ${error_.message}`); } return [site, repoResp]; }; [site, repoResp] = await inputSiteName(nameFlag); log(); log(chalk.greenBright.bold.underline(`Site Created`)); log(); const siteUrl = site.ssl_url || site.url; log(render({ 'Admin URL': site.admin_url, URL: siteUrl, 'Site ID': site.id, 'Repo URL': site.build_settings?.repo_url ?? '', })); track('sites_createdFromTemplate', { siteId: site.id, adminUrl: site.admin_url, siteUrl, }); const { cloneConfirm } = await inquirer.prompt({ type: 'confirm', name: 'cloneConfirm', message: `Do you want to clone the repository to your local machine?`, default: true, }); if (cloneConfirm) { log(); if (repoResp.clone_url) { await execa('git', ['clone', repoResp.clone_url, `${repoResp.name}`]); } log(`🚀 Repository cloned successfully. You can find it under the ${chalk.magenta(repoResp.name)} folder`); const { linkConfirm } = await inquirer.prompt({ type: 'confirm', name: 'linkConfirm', message: `Do you want to link the cloned directory to the site?`, default: true, }); if (linkConfirm) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const cliPath = path.resolve(__dirname, '../../../bin/run.js'); let stdout; // TODO(serhalp): Why is this condition here? We've asked the user multiple prompts, but we already knew we had // invalid repo data. Move upstream. if (repoResp.name) { stdout = await callLinkSite(cliPath, repoResp.name, '\n'); } else { return logAndThrowError('Failed to fetch the repo'); } const linkedSiteUrlRegex = /Site url:\s+(\S+)/; const lineMatch = linkedSiteUrlRegex.exec(stdout); const urlMatch = lineMatch ? lineMatch[1] : undefined; if (urlMatch) { log(`\nDirectory ${chalk.cyanBright(repoResp.name)} linked to site ${chalk.cyanBright(urlMatch)}\n`); log(`${chalk.cyanBright.bold('cd', repoResp.name)} to use other netlify cli commands in the cloned directory.\n`); } else { const linkedSiteMatch = /Site already linked to\s+(\S+)/.exec(stdout); const linkedSiteNameMatch = linkedSiteMatch ? linkedSiteMatch[1] : undefined; if (linkedSiteNameMatch) { log(`\nThis directory appears to be linked to ${chalk.cyanBright(linkedSiteNameMatch)}`); log('This can happen if you cloned the template into a subdirectory of an existing Netlify project.'); log(`You may need to move the ${chalk.cyanBright(repoResp.name)} directory out of its parent directory and then re-run the ${chalk.cyanBright('link')} command manually\n`); } else { log('A problem occurred linking the site'); log('You can try again manually by running:'); log(chalk.cyanBright(`cd ${repoResp.name} && netlify link\n`)); } } } else { log('To link the cloned directory manually, run:'); log(chalk.cyanBright(`cd ${repoResp.name} && netlify link\n`)); } } if (options.withCi) { log('Configuring CI'); const repoData = await getRepoData({ workingDir: command.workingDir }); if ('error' in repoData) { return logAndThrowError('Failed to get repo data'); } await configureRepo({ command, siteId: site.id, repoData, manual: options.manual }); } if (options.json) { logJson(pick(site, [ 'id', 'state', 'plan', 'name', 'custom_domain', 'domain_aliases', 'url', 'ssl_url', 'admin_url', 'screenshot_url', 'created_at', 'updated_at', 'user_id', 'ssl', 'force_ssl', 'managed_dns', 'deploy_url', 'account_name', 'account_slug', 'git_provider', 'deploy_hook', 'capabilities', 'id_domain', ])); } return site; }; //# sourceMappingURL=sites-create-template.js.map