Skip to content

Commit

Permalink
Merge branch 'main' into nahuelon/gh-0/reuse-select-account-prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
Nahuel Alejandro Ramos authored Jul 3, 2023
2 parents c928552 + 6f13ce3 commit 5e7343a
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 195 deletions.
29 changes: 8 additions & 21 deletions packages/create-cli/src/actions/creates.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'
import prompts from 'prompts'
import { isValidUrl } from '../utils/directory.js'
import { askCreateInitialBrowserCheck, askUserWebsite } from '../utils/prompts.js'

// Default Playwright-based Browser Check
const defaultBrowserCheck = `import { test, expect } from '@playwright/test'
Expand All @@ -16,29 +16,16 @@ test('Custom Browser Check', async ({ page }) => {
})`

export async function createCustomBrowserCheck (
{ onCancel }: { onCancel: () => void },
{ projectDirectory, onCancel }: { projectDirectory: string, onCancel: () => void },
) {
const createInitialBrowserCheckResponse = await prompts({
type: 'confirm',
name: 'value',
message: 'Would you like to create a custom Playwright-based Browser Check to check a URL?',
initial: true,
},
{ onCancel },
)
const { createInitialBrowserCheck } = await askCreateInitialBrowserCheck(onCancel)

if (createInitialBrowserCheckResponse.value) {
const userWebsiteResponse = await prompts({
type: 'text',
name: 'url',
message: 'Please provide the URL of the site you want to check.',
},
{ onCancel },
)
if (createInitialBrowserCheck) {
const { userWebsite } = await askUserWebsite(onCancel)

if (isValidUrl(userWebsiteResponse.url)) {
fs.writeFileSync(path.join(process.cwd(), './__checks__/custom.spec.ts'),
defaultBrowserCheck.replace(/URL_TO_CHECK/i, new URL(userWebsiteResponse.url).toString()))
if (isValidUrl(userWebsite)) {
fs.writeFileSync(path.join(projectDirectory, './__checks__/custom.spec.ts'),
defaultBrowserCheck.replace(/URL_TO_CHECK/i, new URL(userWebsite).toString()))
} else {
process.stdout.write('Custom check wasn\'t created: the specified URL isn\'t valid.\n')
}
Expand Down
19 changes: 7 additions & 12 deletions packages/create-cli/src/actions/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as fs from 'fs'
import * as path from 'path'
import prompts from 'prompts'
import detectPackageManager from 'which-pm-runs'
import { execa } from 'execa'
import { spinner } from '../utils/terminal.js'
import { hint } from '../utils/messages.js'
import { PackageJson } from '../utils/package.js'
import { PackageJson } from '../utils/directory.js'
import { askInstallDependencies } from '../utils/prompts.js'

export function addDevDependecies (packageJson: PackageJson) {
export function addDevDependecies (projectDirectory: string, packageJson: PackageJson) {
if (!Reflect.has(packageJson, 'devDependencies')) {
packageJson.devDependencies = {}
}
Expand All @@ -18,18 +18,13 @@ export function addDevDependecies (packageJson: PackageJson) {
typescript: 'latest',
})

fs.writeFileSync(path.join(process.cwd(), 'package.json'), JSON.stringify(packageJson, null, 2))
fs.writeFileSync(path.join(projectDirectory, 'package.json'), JSON.stringify(packageJson, null, 2))
}

export async function installDependencies (targetDir: string = process.cwd()): Promise<void> {
const installDepsResponse = await prompts({
type: 'confirm',
name: 'installDeps',
message: 'Would you like to install NPM dependencies? (recommended)',
initial: true,
})
export async function installDependencies (targetDir: string): Promise<void> {
const { installDependencies } = await askInstallDependencies()

if (installDepsResponse.installDeps) {
if (installDependencies) {
const packageManager = detectPackageManager()?.name || 'npm'
const installExec = execa(packageManager, ['install'], { cwd: targetDir })
const installSpinner = spinner('installing packages')
Expand Down
17 changes: 6 additions & 11 deletions packages/create-cli/src/actions/git.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import * as fs from 'fs'
import * as path from 'path'
import prompts from 'prompts'
import { hasGitDir, hasGitIgnore } from '../utils/directory.js'
import { execaCommand } from 'execa'
import { askInitializeGit } from '../utils/prompts.js'

export async function initGit (targetDir: string = process.cwd()): Promise<void> {
if (hasGitDir()) {
export async function initGit (targetDir: string): Promise<void> {
if (hasGitDir(targetDir)) {
return
}

const initGitResponse = await prompts({
type: 'confirm',
name: 'initGit',
message: 'Would you like to initialize a new git repo? (optional)',
initial: true,
})
const { initializeGit } = await askInitializeGit()

if (initGitResponse.initGit) {
if (initializeGit) {
await execaCommand('git init', { cwd: targetDir })

if (!hasGitIgnore()) {
if (!hasGitIgnore(targetDir)) {
const gitIgnore = 'node_modules\n.DS_Store'
fs.writeFileSync(path.join(targetDir, '.gitignore'), gitIgnore)
}
Expand Down
147 changes: 26 additions & 121 deletions packages/create-cli/src/commands/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
import Debug from 'debug'
import { Command, Flags } from '@oclif/core'
import { uniqueNamesGenerator, colors, animals } from 'unique-names-generator'
import prompts from 'prompts'
import chalk from 'chalk'
import { isValidProjectDirectory, copyTemporaryFiles, usePackageName } from '../utils/directory.js'
import {
getUserGreeting,
getVersion,
header,
bail,
hint,
footer,
hint,
} from '../utils/messages.js'
import { createCustomBrowserCheck } from '../actions/creates.js'
import { addDevDependecies, installDependencies } from '../actions/dependencies.js'
import { hasPackageJsonFile, readPackageJson } from '../utils/package.js'
import { copyTemplate } from '../actions/template.js'
import { initGit } from '../actions/git.js'
import { hasPackageJsonFile } from '../utils/directory.js'
import {
createProject,
getProjectDirectory,
installDependenciesAndInitGit,
installWithinProject,
} from '../utils/installation.js'
import path from 'path'

/**
* This code is heavily inspired by the amazing create-astro package over at
* https://github.com/withastro/astro/tree/main/packages/create-astro
*/

const debug = Debug('checkly:create-cli')
const templateBaseRepo = 'checkly/checkly-cli/examples'

function generateProjectName (): string {
return uniqueNamesGenerator({
dictionaries: [colors, animals],
separator: '-',
length: 2,
style: 'lowerCase',
})
}

function onCancel (): void {
bail()
process.exit(1)
}

export default class Bootstrap extends Command {
static description = 'Bootstrap a Checkly project'

Expand All @@ -54,6 +36,12 @@ export default class Bootstrap extends Command {
const { flags } = await this.parse(Bootstrap)
const { template } = flags

const onCancel = (): void => {
bail()
// TODO: replace this with oclif error()
process.exit(1)
}

// This overrides the template prompt and skips to the next prompt
if (template) {
prompts.override({ template })
Expand All @@ -63,103 +51,20 @@ export default class Bootstrap extends Command {

await header(version, greeting)

// Init Checkly CLI for an existing project
if (hasPackageJsonFile()) {
debug('Existing package.json detected')

const projectInitResponse = await prompts({
type: 'confirm',
name: 'useDirectory',
message: 'It looks like you are already in a project, would you like to initialize?',
initial: true,
},
{ onCancel },
)

if (projectInitResponse.useDirectory) {
const packageJson = readPackageJson()
const temporaryDir = generateProjectName()

debug('Add dependencies to existing package.json')
addDevDependecies(packageJson)

debug('Copy boilerplate project to temporary folder')
await copyTemplate({
template: 'boilerplate-project',
templatePath: `github:${templateBaseRepo}/boilerplate-project#v${version}`,
targetDir: temporaryDir,
})
const projectDirectory = await getProjectDirectory({ onCancel })

copyTemporaryFiles(temporaryDir)
usePackageName(packageJson.name)

debug('Create custom Browser check')
await createCustomBrowserCheck({ onCancel })

debug('Install npm dependencies')
await installDependencies()

debug('Init .git & .gitignore')
await initGit()

await footer()

return
}
if (hasPackageJsonFile(projectDirectory)) {
// Init Checkly CLI for an existing project
await installWithinProject({ projectDirectory, version, onCancel })
} else {
// Create a project from the scratch using a template
await hint('Cool.', `Your project will be created in the directory "${projectDirectory}"`)
await createProject({ projectDirectory, version, onCancel })
}

debug('Ask for directory name')

const projectDirResponse = await prompts({
type: 'text',
name: 'projectDirectory',
message: 'Where do you want to create your new project?',
initial: generateProjectName(),
validate (dirName) {
if (!isValidProjectDirectory(dirName)) {
return `"${chalk.bold(dirName)}" is not empty!`
}
return true
},
},
{ onCancel },
)

const targetDir = projectDirResponse.projectDirectory

if (!targetDir) {
process.exit(1)
}

await hint('Cool.', `Your project will be created in the directory "${targetDir}"`)

const templateResponse = await prompts({
type: 'select',
name: 'template',
message: 'Which template would you like to use for your new project',
choices: [
{ value: 'advanced-project', title: 'An advanced TypeScript project with multiple examples and best practices (recommended)' },
{ value: 'advanced-project-js', title: 'An advanced JavaScript project with multiple examples and best practices (recommended)' },
{ value: 'boilerplate-project', title: 'A boilerplate TypeScript project with basic config' },
{ value: 'boilerplate-project-js', title: 'A boilerplate JavaScript project with basic config' },
],
},
{ onCancel },
)

debug('Downloading template')
await copyTemplate({
template: templateResponse.template,
templatePath: `github:${templateBaseRepo}/${templateResponse.template}#v${version}`,
targetDir,
})

debug('Install npm dependencies')
await installDependencies(targetDir)

debug('Init .git & .gitignore')
await initGit(targetDir)
// ask and install dependencies and initialize git
await installDependenciesAndInitGit({ projectDirectory })

await footer(targetDir)
await footer(projectDirectory)
}
}
33 changes: 32 additions & 1 deletion packages/create-cli/src/utils/__tests__/directory.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
import * as path from 'path'
import { isValidProjectDirectory } from '../directory'
import { hasPackageJsonFile, isValidProjectDirectory, readPackageJson } from '../directory'

describe('isValidProjectDirectory()', () => {
type TestTuple = [string, boolean]

const cases: TestTuple[] = [
[path.join(__dirname, './fixtures/valid-dir'), true],
[path.join(__dirname, './fixtures/invalid-dir-1'), false],
[path.join(__dirname, './fixtures/empty-project'), false],
[path.join(__dirname, './fixtures/initiated-project'), true],
[path.join(__dirname, './fixtures/checkly-project'), true],
]
test.each(cases)('directory %s should return valid = %s', (dir, expected) => {
expect(isValidProjectDirectory(dir)).toEqual(expected)
})
})

describe('hasPackageJsonFile()', () => {
type TestTuple = [string, boolean]
const cases: TestTuple[] = [
[path.join(__dirname, './fixtures/empty-project/'), false],
[path.join(__dirname, './fixtures/initiated-project/'), true],
]
test.each(cases)('directory %s should return valid = %s', (dir, expected) => {
expect(hasPackageJsonFile(dir)).toEqual(expected)
})
})

describe('readPackageJson()', () => {
type TestTuple = [string, string]
const cases: TestTuple[] = [
[path.join(__dirname, './fixtures/empty-project/'), ''],
[path.join(__dirname, './fixtures/initiated-project/'), 'initiated-project'],
[path.join(__dirname, './fixtures/checkly-project/'), 'checkly-project'],
]
test.each(cases)('directory %s should return name = %s', (dir, expected) => {
try {
expect(readPackageJson(dir).name).toEqual(expected)
} catch (error: any) {
expect(expected).toEqual('')
expect(error.message).toContain('ENOENT: no such file or directory')
}
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineConfig } from 'checkly'

const config = defineConfig({
projectName: 'My Project',
logicalId: 'my-project',
repoUrl: 'https://github.com/checkly/checkly-cli',
checks: {
frequency: 10,
locations: ['us-east-1', 'eu-west-1'],
tags: ['mac'],
runtimeId: '2023.02',
checkMatch: '**/__checks__/**/*.check.ts',
browserChecks: {
testMatch: '**/__checks__/**/*.spec.ts',
},
},
cli: {
runLocation: 'eu-west-1',
reporters: ['list'],
},
})

export default config
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "checkly-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "commonjs",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"checkly": "latest",
"ts-node": "10.9.1",
"typescript": "4.9.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is an empty project
Loading

0 comments on commit 5e7343a

Please sign in to comment.