diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..19cd3b4c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 90 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 3cf6920d..00d32822 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,9 +5,6 @@ "[javascript]": { "editor.formatOnSave": true }, - "prettier.singleQuote": true, - "prettier.printWidth": 90, - "prettier.jsxBracketSameLine": true, "prettier.typescriptEnable": [ "typescript", "typescriptreact" diff --git a/bin.ts b/bin.ts new file mode 100644 index 00000000..3bb79015 --- /dev/null +++ b/bin.ts @@ -0,0 +1,19 @@ +#!/usr/bin/env node +import { IArgs, ProjectType } from './lib/Constants'; +import { run } from './lib/Setup'; +export * from './lib/Setup'; + +const argv = require('yargs') + .boolean('debug') + .boolean('uninstall') + .option('type', { + choices: Object.keys(ProjectType), + describe: 'Choose a project type', + }) + .option('url', { + alias: 'u', + default: 'https://sentry.io/', + describe: 'The url to your Sentry installation', + }).argv; + +run(argv as IArgs); diff --git a/index.ts b/index.ts index 9c2562ee..54d91146 100644 --- a/index.ts +++ b/index.ts @@ -1,18 +1,2 @@ -#!/usr/bin/env node -import { IArgs, ProjectType } from './lib/Constants'; -import { run } from './lib/Setup'; - -const argv = require('yargs') - .boolean('debug') - .boolean('uninstall') - .option('type', { - choices: Object.keys(ProjectType), - describe: 'Choose a project type' - }) - .option('url', { - alias: 'u', - default: 'https://sentry.io/', - describe: 'The url to your Sentry installation' - }).argv; - -run(argv as IArgs); +export { IArgs, ProjectType } from './lib/Constants'; +export * from './lib/Setup'; diff --git a/lib/Constants.ts b/lib/Constants.ts index 7068d98b..0e754949 100644 --- a/lib/Constants.ts +++ b/lib/Constants.ts @@ -2,7 +2,34 @@ export enum ProjectType { reactNative = 'reactNative', javascript = 'javascript', - node = 'node' + node = 'node', + cordova = 'cordova', +} + +export function getProjectDescription(type: string) { + switch (type) { + case ProjectType.reactNative: + return 'React Native'; + case ProjectType.cordova: + return 'Cordova'; + case ProjectType.node: + return 'Generic node project'; + default: + return 'Generic javascript project'; + } +} + +export function getProjectTypeChoices() { + const result = []; + for (const type in ProjectType) { + if (ProjectType.hasOwnProperty(type)) { + result.push({ + name: getProjectDescription(type), + value: type, + }); + } + } + return result; } export interface IArgs { diff --git a/lib/Helper.ts b/lib/Helper.ts index b328a192..ff068124 100644 --- a/lib/Helper.ts +++ b/lib/Helper.ts @@ -64,6 +64,7 @@ export class BottomBar { function sanitizeArgs(argv: IArgs) { let baseUrl = argv.url; baseUrl += baseUrl.endsWith('/') ? '' : '/'; + baseUrl = baseUrl.replace(/:\/(?!\/)/g, '://'); argv.url = baseUrl; } diff --git a/lib/steps/ConfigureProject.ts b/lib/steps/ConfigureProject.ts index 11b47faf..40befbc7 100644 --- a/lib/steps/ConfigureProject.ts +++ b/lib/steps/ConfigureProject.ts @@ -1,6 +1,7 @@ import { Answers } from 'inquirer'; import * as _ from 'lodash'; import { ProjectType } from '../Constants'; +import { Cordova } from './configure/Cordova'; import { GenericJavascript } from './configure/GenericJavascript'; import { GenericNode } from './configure/GenericNode'; import { ReactNative } from './configure/ReactNative'; @@ -16,6 +17,8 @@ export class ConfigureProject extends BaseStep { switch (projectType) { case ProjectType.reactNative: return new ReactNative(this.argv).emit(answers); + case ProjectType.cordova: + return new Cordova(this.argv).emit(answers); case ProjectType.node: return new GenericNode(this.argv).emit(answers); default: diff --git a/lib/steps/DetectProjectType.ts b/lib/steps/DetectProjectType.ts index 27670a2b..2865a98a 100644 --- a/lib/steps/DetectProjectType.ts +++ b/lib/steps/DetectProjectType.ts @@ -1,6 +1,6 @@ import { Answers, prompt, Question } from 'inquirer'; import * as _ from 'lodash'; -import { ProjectType } from '../Constants'; +import { getProjectTypeChoices, ProjectType } from '../Constants'; import { green } from '../Helper'; import { BaseStep } from './Step'; @@ -22,25 +22,12 @@ export class DetectProjectType extends BaseStep { const projectType = this.tryDetectingProjectType(); return prompt([ { - choices: [ - { - name: `Generic node project`, - value: ProjectType.node - }, - { - name: `Generic javascript project`, - value: ProjectType.javascript - }, - { - name: `React Native`, - value: ProjectType.reactNative - } - ], + choices: getProjectTypeChoices(), default: projectType, message: 'What kind of project are you running:', name: 'projectType', - type: 'list' - } + type: 'list', + }, ]); } diff --git a/lib/steps/SentryProjectSelector.ts b/lib/steps/SentryProjectSelector.ts index b29d4b47..55c1944d 100644 --- a/lib/steps/SentryProjectSelector.ts +++ b/lib/steps/SentryProjectSelector.ts @@ -20,13 +20,13 @@ export class SentryProjectSelector extends BaseStep { choices: answers.wizard.projects.map((project: any) => { return { name: `${project.organization.name} / ${project.name}`, - value: project + value: project, }; }), message: 'Please select your project in Sentry:', name: 'selectedProject', - type: 'list' - } + type: 'list', + }, ]); } } diff --git a/lib/steps/configure/Cordova.ts b/lib/steps/configure/Cordova.ts new file mode 100644 index 00000000..f8f1b8a7 --- /dev/null +++ b/lib/steps/configure/Cordova.ts @@ -0,0 +1,112 @@ +import * as fs from 'fs'; +import { Answers, prompt } from 'inquirer'; +import * as _ from 'lodash'; +import * as path from 'path'; +import { IArgs } from '../../Constants'; +import { dim, green, l, nl, red } from '../../Helper'; +import { BaseStep } from '../Step'; +import { patchMatchingFile } from './FileHelper'; +import { SentryCliHelper } from './SentryCliHelper'; + +export class Cordova extends BaseStep { + protected answers: Answers; + protected platforms: string[]; + protected sentryCliHelper: SentryCliHelper; + protected folderPrefix = 'platforms'; + + constructor(protected argv: IArgs) { + super(argv); + this.sentryCliHelper = new SentryCliHelper(this.argv); + } + + public async emit(answers: Answers) { + if (this.argv.uninstall) { + // return this.uninstall(); + } + const sentryCliProperties = this.sentryCliHelper.convertSelectedProjectToProperties( + answers + ); + return ['ios', 'android'].map(async (platform: string) => { + try { + await this.addSentryProperties(platform, sentryCliProperties); + green(`Successfully setup ${platform} for cordova`); + } catch (e) { + red(e); + } + }); + + // return new Promise(async (resolve, reject) => { + // this.answers = answers; + // this.platforms = (await this.platformSelector()).platform; + // const promises = this.platforms.map((platform: string) => + // this.shouldConfigurePlatform(platform).then(async () => { + + // }) + // ); + // }); + } + + private addSentryProperties(platform: string, properties: any) { + let rv = Promise.resolve(); + // This will create the ios/android folder before trying to write + // sentry.properties in it which would fail otherwise + + if (!fs.existsSync(this.folderPrefix)) { + dim(`${this.folderPrefix} folder did not exist, creating it.`); + fs.mkdirSync(this.folderPrefix); + } + if (!fs.existsSync(path.join(this.folderPrefix, platform))) { + dim(`${platform} folder did not exist, creating it.`); + fs.mkdirSync(path.join(this.folderPrefix, platform)); + } + const fn = path.join(this.folderPrefix, platform, 'sentry.properties'); + + rv = rv.then(() => + fs.writeFileSync(fn, this.sentryCliHelper.dumpProperties(properties)) + ); + + return rv; + } + + private shouldConfigurePlatform(platform: string) { + // if a sentry.properties file exists for the platform we want to configure + // without asking the user. This means that re-linking later will not + // bring up a useless dialog. + + if ( + fs.existsSync(path.join(this.folderPrefix, platform, 'sentry.properties')) || + fs.existsSync( + path.join(process.cwd(), this.folderPrefix, platform, 'sentry.properties') + ) + ) { + return Promise.reject( + `${platform}/sentry.properties already exists, skipping setup for platform ${ + platform + }` + ); + } + return Promise.resolve(); + } + + private platformSelector() { + return prompt([ + { + choices: [ + { + checked: true, + name: 'iOS', + value: 'ios', + }, + { + checked: true, + name: 'Android', + value: 'android', + }, + ], + message: 'Select the platforms you like to setup:', + name: 'platform', + type: 'checkbox', + }, + ]); + } +} diff --git a/lib/steps/configure/FileHelper.ts b/lib/steps/configure/FileHelper.ts index 92eefd3b..59b92f87 100644 --- a/lib/steps/configure/FileHelper.ts +++ b/lib/steps/configure/FileHelper.ts @@ -1,14 +1,14 @@ +import * as fs from 'fs'; const glob = require('glob'); -const fs = require('fs'); export function patchMatchingFile(pattern: string, func: any) { const matches = glob.sync(pattern, { - ignore: ['node_modules/**', 'ios/Pods/**', '**/Pods/**'] + ignore: ['node_modules/**', 'ios/Pods/**', '**/Pods/**'], }); let rv = Promise.resolve(); matches.forEach((match: string) => { const contents = fs.readFileSync(match, { - encoding: 'utf-8' + encoding: 'utf-8', }); rv = rv.then(() => func(contents, match)).then(newContents => { if (newContents !== null && contents !== undefined && contents !== newContents) { diff --git a/lib/steps/configure/ReactNative.ts b/lib/steps/configure/ReactNative.ts index 4f67b5e1..b5c93958 100644 --- a/lib/steps/configure/ReactNative.ts +++ b/lib/steps/configure/ReactNative.ts @@ -1,5 +1,7 @@ +import * as fs from 'fs'; import { Answers, prompt, Question } from 'inquirer'; import * as _ from 'lodash'; +import * as path from 'path'; import { IArgs } from '../../Constants'; import { dim, green, red } from '../../Helper'; import { BaseStep } from '../Step'; @@ -7,7 +9,7 @@ import { patchMatchingFile } from './FileHelper'; import { SentryCliHelper } from './SentryCliHelper'; const xcode = require('xcode'); -const fs = require('fs'); +// const path = require('path'); const OBJC_HEADER = '\ @@ -95,8 +97,8 @@ export class ReactNative extends BaseStep { // without asking the user. This means that re-linking later will not // bring up a useless dialog. if ( - fs.existsSync(platform + '/sentry.properties') || - fs.existsSync(process.cwd() + platform + '/sentry.properties') + fs.existsSync(path.join(platform, 'sentry.properties')) || + fs.existsSync(path.join(process.cwd(), platform, 'sentry.properties')) ) { return Promise.reject( `${platform}/sentry.properties already exists, skipping setup for platform ${ @@ -113,9 +115,10 @@ export class ReactNative extends BaseStep { // This will create the ios/android folder before trying to write // sentry.properties in it which would fail otherwise if (!fs.existsSync(platform)) { + dim(`${platform} folder did not exist, creating it.`); fs.mkdirSync(platform); } - const fn = platform + '/sentry.properties'; + const fn = path.join(platform, 'sentry.properties'); rv = rv.then(() => fs.writeFileSync(fn, this.sentryCliHelper.dumpProperties(properties)) @@ -245,7 +248,7 @@ export class ReactNative extends BaseStep { shellPath: '/bin/sh', shellScript: 'export SENTRY_PROPERTIES=sentry.properties\\n' + - '../node_modules/sentry-cli-binary/bin/sentry-cli upload-dsym' + '../node_modules/sentry-cli-binary/bin/sentry-cli upload-dsym', } ); } @@ -254,7 +257,7 @@ export class ReactNative extends BaseStep { proj.addPbxGroup([], 'Frameworks', 'Application'); proj.addFramework('libz.tbd', { link: true, - target: proj.getFirstTarget().uuid + target: proj.getFirstTarget().uuid, }); } @@ -432,18 +435,18 @@ export class ReactNative extends BaseStep { { checked: true, name: 'iOS', - value: 'ios' + value: 'ios', }, { checked: true, name: 'Android', - value: 'android' - } + value: 'android', + }, ], message: 'Select the platforms you like to setup:', name: 'platform', - type: 'checkbox' - } + type: 'checkbox', + }, ]); } } diff --git a/lib/steps/configure/SentryCliHelper.ts b/lib/steps/configure/SentryCliHelper.ts index d4b254be..2a83c02d 100644 --- a/lib/steps/configure/SentryCliHelper.ts +++ b/lib/steps/configure/SentryCliHelper.ts @@ -12,13 +12,16 @@ export class SentryCliHelper { props['defaults/url'] = this.argv.url; props['defaults/org'] = _.get(answers, 'selectedProject.organization.slug', null); props['defaults/project'] = _.get(answers, 'selectedProject.slug', null); - props['auth/token'] = _.get(answers, 'wizard.apiKeys.0.token', null); + props['auth/token'] = _.get(answers, 'wizard.apiKeys.token', null); + // TODO: Check if we need this + /* try { const cliPath = require.resolve('sentry-cli-binary/bin/sentry-cli'); props['cli/executable'] = path.relative(process.cwd(), cliPath); } catch (e) { // we do nothing and leave everyting as it is } + */ return props; } diff --git a/package.json b/package.json index 355dd1eb..e10ba095 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,18 @@ "homepage": "https://github.com/getsentry/sentry-wizard", "repository": "https://github.com/getsentry/sentry-wizard", "description": "Sentry wizard helping you to configure your project", - "keywords": ["sentry", "wizard", "sdk", "cli", "project", "setup", "install", "configure"], + "keywords": [ + "sentry", + "wizard", + "sdk", + "cli", + "project", + "setup", + "install", + "configure" + ], "bin": { - "sentry-wizard": "./dist/index.js" + "sentry-wizard": "./dist/bin.js" }, "main": "dist/index.js", "typings": "dist/index.d.ts", @@ -30,7 +39,7 @@ "@types/lodash": "^4.14.82", "@types/node": "^8.0.32", "jest": "^21.2.1", - "prettier": "^1.7.4", + "prettier": "^1.8.0", "ts-jest": "^21.1.2", "ts-node": "^3.3.0", "tslint": "^5.8.0", @@ -46,10 +55,10 @@ }, "scripts": { "clean": "rm -rf ./dist", - "dist": "npm run clean && tsc -p tsconfig.json && chmod +x ./dist/index.js", + "dist": "npm run clean && tsc -p tsconfig.json && chmod +x ./dist/bin.js", "test": "npm run dist && jest", - "try": "yarn dist && ts-node index.ts --url=http://localhost:8000/", - "try:debug": "yarn dist && ts-node index.ts --url=http://localhost:8000/ --debug", + "try": "yarn dist && ts-node bin.ts --url=http://localhost:8000/", + "try:debug": "yarn dist && ts-node bin.ts --url=http://localhost:8000/ --debug", "test:watch": "jest --watch --notify" }, "jest": { diff --git a/tsconfig.json b/tsconfig.json index 0a7b4700..f0a0427e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "include": [ "__tests__", "index.ts", + "bin.ts", "lib/**/*", "spec/**/*" ] diff --git a/yarn.lock b/yarn.lock index bbb13dd9..b599a811 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2155,7 +2155,7 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.7.4: +prettier@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8"