This repository has been archived by the owner on Jan 5, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
init.ts
241 lines (216 loc) · 7.54 KB
/
init.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*!
* Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
*/
import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import * as fs from 'fs';
import * as Listr from 'listr';
import * as path from 'path';
import { boxMessage, getWarnings } from '../utils';
import {
buildScaffold,
copyFiles,
findConflicts,
getCopyDescriptors,
getJestConfig,
getTemplatePaths,
installDependencies,
modifyGitIgnore,
modifyJestConfig,
modifyPackageJson,
parsePackageJson,
shouldBuildScaffold,
usageAnalytics
} from '../utils/';
export default class Init extends Command {
static description = 'Initializes your project for the SAP Cloud SDK, SAP Cloud Platform Cloud Foundry and CI/CD using the SAP Cloud SDK toolkit';
static examples = ['$ sap-cloud-sdk init', '$ sap-cloud-sdk init --help'];
static flags = {
// visible
projectDir: flags.string({
description: 'Path to the directory in which the project should be created.'
}),
addCds: flags.boolean({
description: 'Add a cds configuration and example data to follow the SAP Cloud Application Programming model.'
}),
force: flags.boolean({
description: 'Do not fail if a file or npm script already exist and overwrite it.'
}),
frontendScripts: flags.boolean({
description: 'Add frontend-related npm scripts which are executed by our CI/CD toolkit.'
}),
help: flags.help({
char: 'h',
description: 'Show help for the init command.'
}),
verbose: flags.boolean({
char: 'v',
description: 'Show more detailed output.'
}),
// hidden
projectName: flags.string({
hidden: true,
description: 'Give project name which is used for the Cloud Foundry mainfest.yml.'
}),
startCommand: flags.string({
hidden: true,
description: 'Give a command which is used to start the application productively.'
}),
buildScaffold: flags.boolean({
hidden: true,
description: 'If the folder is empty, use nest-cli to create a project scaffold.'
}),
analytics: flags.boolean({
hidden: true,
allowNo: true,
description: 'Enable or disable collection of anonymous usage data.'
}),
analyticsSalt: flags.string({
hidden: true,
description: 'Set salt for analytics. This should only be used for CI builds.'
}),
skipInstall: flags.boolean({
hidden: true,
description: 'Skip installing npm dependencies. If you use this, make sure to install manually afterwards.'
})
};
static args = [
{
name: 'projectDir',
description: 'Path to the directory in which the project should be created.'
}
];
async run() {
const { flags, args } = this.parse(Init);
const projectDir = args.projectDir || '.';
try {
fs.mkdirSync(projectDir, { recursive: true });
const isScaffold = await shouldBuildScaffold(projectDir, flags.buildScaffold, flags.force);
if (isScaffold) {
await buildScaffold(projectDir, flags.verbose, flags.addCds);
}
const options = await this.getOptions(projectDir, isScaffold ? 'npm run start:prod' : flags.startCommand, flags.projectName).catch(e =>
this.error(flags.verbose ? e.stack : e.message, { exit: 10 })
);
await usageAnalytics(projectDir, flags.analytics, flags.analyticsSalt);
const tasks = new Listr([
{
title: 'Creating files',
task: async () => {
const copyDescriptors = getCopyDescriptors(projectDir, getTemplatePaths(this.getTemplateNames(isScaffold, flags.addCds)));
await findConflicts(copyDescriptors, flags.force).catch(e => this.error(flags.verbose ? e.stack : e.message, { exit: 11 }));
await copyFiles(copyDescriptors, options);
}
},
{
title: 'Modifying test config',
task: () => modifyJestConfig(path.resolve(projectDir, 'test', 'jest-e2e.json'), getJestConfig(false)),
enabled: () => isScaffold
},
{
title: 'Adding dependencies to package.json',
task: async () =>
modifyPackageJson({ projectDir, isScaffold, frontendScripts: flags.frontendScripts, force: flags.force, addCds: flags.addCds }).catch(e =>
this.error(flags.verbose ? e.stack : e.message, { exit: 12 })
)
},
{
title: 'Installing dependencies',
task: async () => installDependencies(projectDir, flags.verbose).catch(e => this.error(flags.verbose ? e.stack : e.message, { exit: 13 })),
enabled: () => !flags.skipInstall
},
{
title: 'Modifying `.gitignore`',
task: () => modifyGitIgnore(projectDir, flags.addCds)
}
]);
await tasks.run();
this.printSuccessMessage(isScaffold, flags.addCds);
} catch (error) {
this.error(flags.verbose ? error.stack : error.message, { exit: 1 });
}
}
private getTemplateNames(isScaffold: boolean, addCds: boolean): string[] {
const templates = ['init'];
if (addCds) {
templates.push('add-cds');
if (isScaffold) {
templates.push('add-cds-scaffold');
}
}
if (isScaffold) {
templates.push('scaffold-readme');
}
return templates;
}
private async getOptions(projectDir: string, startCommand?: string, projectName?: string) {
const { name, scripts } = await parsePackageJson(projectDir);
const options: { [key: string]: string } = {
projectName:
projectName ||
(await cli.prompt('Enter project name (for use in manifest.yml)', {
default: name
})),
command:
startCommand ||
(await cli.prompt('Enter the command to start your application', {
default: scripts.start ? 'npm start' : ''
}))
};
return options;
}
private printSuccessMessage(isScaffold: boolean, addCds: boolean) {
const warnings = getWarnings();
const body = [
'🚀 Next steps:',
...this.getNextSteps(isScaffold, addCds),
'',
'🔨 Consider setting up Jenkins to continuously build your app.',
'Use `sap-cloud-sdk add-cx-server` to create the setup script.'
];
if (warnings) {
this.log(boxMessage(['⚠️ Init finished with warnings:', ...warnings, '', ...body]));
} else {
this.log(boxMessage(['✅ Init finished successfully.', '', ...body]));
}
}
private getNextSteps(isScaffold: boolean, addCds: boolean): string[] {
const message = [];
if (addCds) {
message.push('- Deploy your database locally (`npm run cds-deploy`)');
}
if (isScaffold) {
message.push(...this.nextStepsScaffold());
} else {
if (addCds) {
message.push(...this.nextStepsCdsNoScaffold());
}
message.push(...this.nextStepsNoScaffold());
}
return message;
}
private nextStepsNoScaffold() {
return [
'- Make sure that your app listens to `process.env.PORT`',
'- Build your app if necessary',
'- Run `sap-cloud-sdk package [--include INC][--exclude EXC]`',
'- Push to Cloud Foundry (`cf push`)'
];
}
private nextStepsScaffold() {
return ['- Run the application locally (`npm run start:dev`)', '- Deploy your application (`npm run deploy`)'];
}
private nextStepsCdsNoScaffold() {
return [
'Expose your service:',
'For express apps add the following snippet to your code:',
'',
'cds',
' .connect()',
" .serve('CatalogService')",
' .in(<your-express-app>);',
'',
'For other frameworks please refer to the documentation.'
];
}
}