1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const sdk_client_1 = require("@haiku/sdk-client");
|
4 | const sdk_inkstone_1 = require("@haiku/sdk-inkstone");
|
5 | const errors_1 = require("@haiku/sdk-inkstone/lib/errors");
|
6 | const ProjectDefinitions_1 = require("@haiku/sdk-client/lib/ProjectDefinitions");
|
7 | const bootstrapSceneFilesSync_1 = require("@haiku/sdk-client/lib/bootstrapSceneFilesSync");
|
8 | const createProjectFiles_1 = require("@haiku/sdk-client/lib/createProjectFiles");
|
9 | const chalk = require("chalk");
|
10 | const child_process_1 = require("child_process");
|
11 | const dedent = require("dedent");
|
12 | const fs = require("fs");
|
13 |
|
14 | const hasbin = require("hasbin");
|
15 | const inquirer = require("inquirer");
|
16 | const _ = require("lodash");
|
17 | const path = require("path");
|
18 |
|
19 | const prependFile = require("prepend-file");
|
20 | const nib_1 = require("./nib");
|
21 |
|
22 | const pkg = require('./../package.json');
|
23 | const cli = new nib_1.Nib({
|
24 | name: 'haiku',
|
25 | version: pkg.version,
|
26 | description: 'The Haiku CLI — developer utilities for automating Haiku actions and performing local and' +
|
27 | ' server-enabled actions without requiring the desktop app.',
|
28 | preAction(context) {
|
29 | sdk_client_1.client.config.getenv();
|
30 | },
|
31 | commands: [
|
32 | {
|
33 | name: 'list',
|
34 | action: doList,
|
35 | flags: [
|
36 | {
|
37 | name: 'organizations',
|
38 | defaultValue: undefined,
|
39 | description: 'include to list organizations your account is a member of instead of projects',
|
40 | },
|
41 | ],
|
42 | description: 'Lists your team projects',
|
43 | },
|
44 | {
|
45 | name: 'change-password',
|
46 | action: doChangePassword,
|
47 | description: 'Changes your Haiku account password (interactive)',
|
48 | },
|
49 | {
|
50 | name: 'clone',
|
51 | action: doClone,
|
52 | description: 'Clone a Haiku project to your filesystem, passing through to git clone',
|
53 | args: [
|
54 | {
|
55 | name: 'project-name',
|
56 | required: true,
|
57 | usage: 'Clone a Haiku project to your filesystem, passing through to git clone',
|
58 | },
|
59 | {
|
60 | name: 'destination',
|
61 | required: false,
|
62 | usage: 'Optional: location on the file system where the project should be cloned',
|
63 | },
|
64 | ],
|
65 | },
|
66 | {
|
67 | name: 'delete',
|
68 | action: doDelete,
|
69 | description: 'Deletes a Haiku project for your entire team. Cannot be undone.',
|
70 | args: [
|
71 | {
|
72 | name: 'project-name',
|
73 | required: false,
|
74 | usage: 'Specifies the name of the project to delete (case-sensitive.) If this isn\'t provided, the action' +
|
75 | ' will be interactive.',
|
76 | },
|
77 | ],
|
78 | },
|
79 | {
|
80 | name: 'init',
|
81 | action: doInit,
|
82 | description: 'Inits a project for installing @haiku modules. ' +
|
83 | 'Will write or append to a .npmrc in this directory.',
|
84 | },
|
85 | {
|
86 | name: 'install',
|
87 | action: doInstall,
|
88 | description: 'Install a Haiku project as an npm module, requires a package.json',
|
89 | args: [
|
90 | {
|
91 | name: 'project-name',
|
92 | required: true,
|
93 | usage: 'Specifies the name of the project to install as a dependency. Case-sensitive.',
|
94 | },
|
95 | ],
|
96 | },
|
97 | {
|
98 | name: 'login',
|
99 | action: doLogin,
|
100 | description: 'Logs into Haiku services. (interactive)',
|
101 | },
|
102 | {
|
103 | name: 'logout',
|
104 | action: doLogout,
|
105 | description: 'Logs out of Haiku services.',
|
106 | },
|
107 | {
|
108 | name: 'update',
|
109 | aliases: ['upgrade'],
|
110 | args: [
|
111 | {
|
112 | name: 'project-name',
|
113 | required: false,
|
114 | usage: 'Specifies the name of the project to update as a dependency. Case-sensitive. If not provided,' +
|
115 | ' will update all detected Haiku projects.',
|
116 | },
|
117 | {
|
118 | name: 'version',
|
119 | required: false,
|
120 | usage: 'Specifies the version to update specified dependency to. If not provided, will update to the' +
|
121 | ' latest available version.',
|
122 | },
|
123 | ],
|
124 | action: doUpdate,
|
125 | description: 'Updates dependencies',
|
126 | },
|
127 | {
|
128 | name: 'generate',
|
129 | aliases: ['g'],
|
130 | args: [
|
131 | {
|
132 | name: 'component-name',
|
133 | required: true,
|
134 | usage: 'Specifies the name of new component to be generated. Case-sensitive and must be unique.',
|
135 | },
|
136 | ],
|
137 | action: generateComponent,
|
138 | description: 'Generate new component',
|
139 | },
|
140 | ],
|
141 | });
|
142 | exports.cli = cli;
|
143 | function ensureAuth(context, cb) {
|
144 | const authToken = sdk_client_1.client.config.getAuthToken();
|
145 | if (authToken) {
|
146 | sdk_inkstone_1.inkstone.setConfig({ authToken });
|
147 | cb(authToken);
|
148 | return;
|
149 | }
|
150 | context.writeLine('You must be authenticated to do that.');
|
151 | doLogin(context, () => {
|
152 | const newToken = sdk_client_1.client.config.getAuthToken();
|
153 | if (newToken) {
|
154 | sdk_inkstone_1.inkstone.setConfig({ authToken: newToken });
|
155 | cb(newToken);
|
156 | return;
|
157 | }
|
158 | context.writeLine('Hm, that didn\'t work. Let\'s try again.');
|
159 | ensureAuth(context, cb);
|
160 | });
|
161 | }
|
162 | function doChangePassword(context) {
|
163 | ensureAuth(context, (token) => {
|
164 | inquirer.prompt([
|
165 | {
|
166 | type: 'password',
|
167 | name: 'OldPassword',
|
168 | message: 'Old Password:',
|
169 | },
|
170 | {
|
171 | type: 'password',
|
172 | name: 'NewPassword',
|
173 | message: 'New Password:',
|
174 | },
|
175 | {
|
176 | type: 'password',
|
177 | name: 'NewPassword2',
|
178 | message: 'New Password (confirm):',
|
179 | },
|
180 | ]).then((answers) => {
|
181 | if (answers.NewPassword !== answers.NewPassword2) {
|
182 | context.writeLine(chalk.red('New passwords do not match.'));
|
183 | process.exit(1);
|
184 | }
|
185 | const params = {
|
186 | OldPassword: answers.OldPassword,
|
187 | NewPassword: answers.NewPassword,
|
188 | };
|
189 | sdk_inkstone_1.inkstone.user.changePassword(token, params, (err, responseBody, response) => {
|
190 | if (err) {
|
191 | context.writeLine(chalk.bold(`Unabled to change password: `) + err);
|
192 | process.exit(1);
|
193 | }
|
194 | else {
|
195 | context.writeLine(chalk.green('Password updated.'));
|
196 | }
|
197 | });
|
198 | });
|
199 | });
|
200 | }
|
201 | function doClone(context) {
|
202 | const projectName = context.args['project-name'];
|
203 | let destination = context.args.destination || projectName;
|
204 | if (destination.charAt(destination.length - 1) !== '/') {
|
205 | destination += '/';
|
206 | }
|
207 | ensureAuth(context, (token) => {
|
208 | context.writeLine('Cloning project...');
|
209 | sdk_inkstone_1.inkstone.project.get({ Name: projectName }, (getByNameErr, projectAndCredentials) => {
|
210 | if (getByNameErr) {
|
211 | switch (getByNameErr.message) {
|
212 | case errors_1.ErrorCode.ErrorCodeProjectNotFound:
|
213 | context.writeLine(chalk.bold(`Project ${projectName} not found.`));
|
214 | break;
|
215 | case errors_1.ErrorCode.ErrorCodeProjectNameRequired:
|
216 | context.writeLine(chalk.bold(`Project name is required.`));
|
217 | break;
|
218 | }
|
219 | process.exit(1);
|
220 | }
|
221 | sdk_client_1.client.git.cloneRepo(projectAndCredentials.RepositoryUrl, destination, (cloneErr) => {
|
222 | if (cloneErr) {
|
223 | context.writeLine(chalk.red('Error cloning project. Use the --verbose flag for more information.'));
|
224 | process.exit(1);
|
225 | }
|
226 | else {
|
227 | context.writeLine(`Project ${chalk.bold(projectName)} cloned to ${chalk.bold(destination)}`);
|
228 | process.exit(0);
|
229 | }
|
230 | });
|
231 | });
|
232 | });
|
233 | }
|
234 | function doDelete(context) {
|
235 | ensureAuth(context, (token) => {
|
236 | context.writeLine(chalk.bold('Please note that deleting this project will delete it for your entire team.'));
|
237 | context.writeLine(chalk.red('Deleting a project cannot be undone!'));
|
238 | const actuallyDelete = (finalProjectName) => {
|
239 | sdk_inkstone_1.inkstone.project.deleteByName({ Name: finalProjectName }, (err) => {
|
240 | if (err) {
|
241 | context.writeLine(chalk.red('Error deleting project. Does this project exist?'));
|
242 | process.exit(1);
|
243 | }
|
244 | else {
|
245 | context.writeLine(chalk.green('Project deleted!'));
|
246 | process.exit(0);
|
247 | }
|
248 | });
|
249 | };
|
250 | let projectName = context.args['project-name'];
|
251 | if (projectName) {
|
252 | actuallyDelete(projectName);
|
253 | }
|
254 | else {
|
255 | inquirer
|
256 | .prompt([
|
257 | {
|
258 | type: 'input',
|
259 | name: 'name',
|
260 | message: 'Project Name:',
|
261 | },
|
262 | ])
|
263 | .then((answers) => {
|
264 | projectName = answers.name;
|
265 | context.writeLine('Deleting project...');
|
266 | actuallyDelete(projectName);
|
267 | });
|
268 | }
|
269 | });
|
270 | }
|
271 | function doInit(context) {
|
272 |
|
273 | let npmrc = '';
|
274 | try {
|
275 | npmrc = fs.readFileSync('.npmrc').toString();
|
276 | }
|
277 | catch (exception) {
|
278 | if (exception.code === 'ENOENT') {
|
279 |
|
280 | }
|
281 | else {
|
282 |
|
283 | throw (exception);
|
284 | }
|
285 | }
|
286 | if (npmrc.indexOf('@haiku') === -1) {
|
287 | prependFile.sync('.npmrc', dedent `
|
288 | //reservoir.haiku.ai:8910/:_authToken=
|
289 | @haiku:registry=https://reservoir.haiku.ai:8910/\n
|
290 | `);
|
291 | }
|
292 | }
|
293 | function doInstall(context) {
|
294 | const projectName = context.args['project-name'];
|
295 | ensureAuth(context, () => {
|
296 |
|
297 | hasbin('npm', (result) => {
|
298 | if (result) {
|
299 |
|
300 | if (fs.existsSync(process.cwd() + '/package.json')) {
|
301 | context.writeLine('Installing ' + projectName + '...');
|
302 | const packageJson = sdk_client_1.client.npm.readPackageJson();
|
303 | if (!packageJson.dependencies) {
|
304 | packageJson.dependencies = {};
|
305 | }
|
306 |
|
307 | let projectString = '@haiku/';
|
308 | sdk_inkstone_1.inkstone.organization.list((listErr, orgs) => {
|
309 | if (listErr) {
|
310 | context.writeLine(chalk.red('There was an error retrieving your account information.') +
|
311 | ' Please ensure that you have internet access.' +
|
312 | ' If this problem persists, please contact support@haiku.ai and tell us that you don\'t have an' +
|
313 | ' organization associated with your account.');
|
314 | process.exit(1);
|
315 | }
|
316 |
|
317 | projectString += orgs[0].Name.toLowerCase() + '-';
|
318 | sdk_inkstone_1.inkstone.project.get({ Name: projectName }, (getByNameErr, projectAndCredentials) => {
|
319 | if (getByNameErr) {
|
320 | context.writeLine(chalk.red('That project wasn\'t found.') +
|
321 | ' Note that project names are CaseSensitive. ' +
|
322 | 'Please ensure that you have the correct project name, that you\'re logged into the correct' +
|
323 | ' account, and that you have internet access.');
|
324 | process.exit(1);
|
325 | }
|
326 | projectString += projectAndCredentials.Name.toLowerCase();
|
327 |
|
328 | packageJson.dependencies[projectString] = 'latest';
|
329 |
|
330 | doInit(context);
|
331 | sdk_client_1.client.npm.writePackageJson(packageJson);
|
332 | try {
|
333 | child_process_1.execSync('npm install');
|
334 | }
|
335 | catch (e) {
|
336 | context.writeLine(`${chalk.red('npm install failed.')} Your Haiku packages have been injected` +
|
337 | ' into package.json, but npm install failed. Please try again.');
|
338 | process.exit(1);
|
339 | }
|
340 | context.writeLine(chalk.green('Haiku project installed successfully.'));
|
341 | process.exit(0);
|
342 | });
|
343 | });
|
344 | }
|
345 | else {
|
346 | context.writeLine(chalk.red('haiku install can only be used at the root of a project with a package.json.'));
|
347 | context.writeLine('You can use ' + chalk.bold('haiku clone ProjectName [/Optional/Destination]') +
|
348 | ' to clone the project\'s git repo directly.');
|
349 | process.exit(1);
|
350 | }
|
351 | }
|
352 | else {
|
353 | context.writeLine(chalk.red('npm was not found on this machine. ') +
|
354 | ' We recommend installing it with nvm: https://github.com/creationix/nvm');
|
355 | process.exit(1);
|
356 | }
|
357 | });
|
358 | });
|
359 | }
|
360 | function doList(context) {
|
361 | ensureAuth(context, () => {
|
362 | if (context.flags.organizations) {
|
363 | sdk_inkstone_1.inkstone.organization.list((err, organizations, resp) => {
|
364 | if (organizations === undefined || organizations.length === 0) {
|
365 | context.writeLine('You are not a member of any organizations.');
|
366 | }
|
367 | else {
|
368 | context.writeLine(chalk.cyan('Your Organizations:'));
|
369 | _.forEach(organizations, (org) => {
|
370 | context.writeLine(' ' + org.Name);
|
371 | });
|
372 | }
|
373 | process.exit(0);
|
374 | });
|
375 | }
|
376 | else {
|
377 | sdk_inkstone_1.inkstone.project.list((err, projects) => {
|
378 | if (!projects || projects.length === 0) {
|
379 | context.writeLine('No existing projects. Use ' + chalk.bold('haiku generate') + ' to make a new one!');
|
380 | process.exit(0);
|
381 | }
|
382 | else {
|
383 | context.writeLine(chalk.cyan('Your team\'s Haiku projects:'));
|
384 | context.writeLine('(To work with one, call ' + chalk.bold('haiku clone project_name') + ' or ' +
|
385 | chalk.bold('haiku install project_name'));
|
386 | _.forEach(projects, (project) => {
|
387 | context.writeLine(' ' + project.Name);
|
388 | });
|
389 | process.exit(0);
|
390 | }
|
391 | });
|
392 | }
|
393 | });
|
394 | }
|
395 | function doLogin(context, cb) {
|
396 | context.writeLine('Enter your Haiku credentials.');
|
397 | let username = '';
|
398 | let password = '';
|
399 | inquirer.prompt([
|
400 | {
|
401 | type: 'input',
|
402 | name: 'username',
|
403 | message: 'Email:',
|
404 | },
|
405 | {
|
406 | type: 'password',
|
407 | name: 'password',
|
408 | message: 'Password:',
|
409 | },
|
410 | ]).then((answers) => {
|
411 | username = answers.username;
|
412 | password = answers.password;
|
413 | sdk_inkstone_1.inkstone.user.authenticate(username, password, (err, authResponse, httpResponse) => {
|
414 | if (err !== undefined) {
|
415 | if (httpResponse && httpResponse.statusCode === 403) {
|
416 | context.writeLine(chalk.bold.yellow('You must verify your email address before logging in.'));
|
417 | }
|
418 | else {
|
419 | context.writeLine(chalk.bold.red('Username or password incorrect.'));
|
420 | }
|
421 | if (context.flags.verbose) {
|
422 | context.writeLine(err.toString());
|
423 | }
|
424 | }
|
425 | else {
|
426 | sdk_client_1.client.config.setAuthToken(authResponse.Token);
|
427 | context.writeLine(chalk.bold.green(`Welcome ${username}!`));
|
428 | }
|
429 | if (cb) {
|
430 | cb();
|
431 | }
|
432 | else {
|
433 | process.exit(0);
|
434 | }
|
435 | });
|
436 | });
|
437 | }
|
438 | function doLogout() {
|
439 |
|
440 | sdk_client_1.client.config.setAuthToken('');
|
441 | process.exit(0);
|
442 | }
|
443 |
|
444 | function doUpdate(context) {
|
445 | hasbin('npm', (result) => {
|
446 | if (result) {
|
447 | try {
|
448 | context.writeLine('Updating packages...');
|
449 | child_process_1.execSync('npm update');
|
450 | context.writeLine(chalk.green('Haiku packages updated successfully.'));
|
451 | process.exit(0);
|
452 | }
|
453 | catch (e) {
|
454 | context.writeLine(chalk.red('npm update failed.') +
|
455 | ' This may be a configuration issue with npm. Try running npm install and then running haiku update again.');
|
456 | process.exit(1);
|
457 | }
|
458 | }
|
459 | else {
|
460 | context.writeLine(chalk.red('npm was not found on this machine. ') +
|
461 | ' We recommend installing it with nvm: https://github.com/creationix/nvm');
|
462 | process.exit(1);
|
463 | }
|
464 | });
|
465 | }
|
466 | function generateComponent(context) {
|
467 | const componentName = context.args['component-name'];
|
468 | context.writeLine('Creating component...');
|
469 | const projectPath = path.join(process.cwd(), componentName);
|
470 | const projectName = componentName;
|
471 | const authorName = null;
|
472 | const organizationName = null;
|
473 | ProjectDefinitions_1.storeConfigValues(projectPath, {
|
474 | username: authorName,
|
475 | branch: ProjectDefinitions_1.DEFAULT_BRANCH_NAME,
|
476 | version: ProjectDefinitions_1.getHaikuComponentInitialVersion(),
|
477 | organization: organizationName,
|
478 | project: projectName,
|
479 | });
|
480 | const projectOptions = {
|
481 | organizationName,
|
482 | projectName,
|
483 | projectPath,
|
484 | authorName,
|
485 | skipContentCreation: false,
|
486 | };
|
487 | createProjectFiles_1.createProjectFiles(projectOptions, () => {
|
488 | context.writeLine('Created initial project files');
|
489 | ProjectDefinitions_1.fetchProjectConfigInfo(projectPath, (err, userconfig) => {
|
490 | if (err) {
|
491 | throw err;
|
492 | }
|
493 | bootstrapSceneFilesSync_1.bootstrapSceneFilesSync(projectPath, 'main', userconfig);
|
494 | context.writeLine('Created main component');
|
495 | });
|
496 | });
|
497 | context.writeLine('Project created');
|
498 | process.exit(0);
|
499 | }
|
500 |
|
501 |
|
\ | No newline at end of file |