1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.UpdateCommand = void 0;
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const core_1 = require("@angular-devkit/core");
|
12 | const node_1 = require("@angular-devkit/core/node");
|
13 | const schematics_1 = require("@angular-devkit/schematics");
|
14 | const tools_1 = require("@angular-devkit/schematics/tools");
|
15 | const child_process_1 = require("child_process");
|
16 | const fs = require("fs");
|
17 | const path = require("path");
|
18 | const semver = require("semver");
|
19 | const schema_1 = require("../lib/config/schema");
|
20 | const command_1 = require("../models/command");
|
21 | const install_package_1 = require("../tasks/install-package");
|
22 | const color_1 = require("../utilities/color");
|
23 | const log_file_1 = require("../utilities/log-file");
|
24 | const package_manager_1 = require("../utilities/package-manager");
|
25 | const package_metadata_1 = require("../utilities/package-metadata");
|
26 | const package_tree_1 = require("../utilities/package-tree");
|
27 | const npa = require('npm-package-arg');
|
28 | const pickManifest = require('npm-pick-manifest');
|
29 | const oldConfigFileNames = ['.angular-cli.json', 'angular-cli.json'];
|
30 | const NG_VERSION_9_POST_MSG = color_1.colors.cyan('\nYour project has been updated to Angular version 9!\n' +
|
31 | 'For more info, please see: https://v9.angular.io/guide/updating-to-version-9');
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | const disableVersionCheckEnv = process.env['NG_DISABLE_VERSION_CHECK'];
|
37 | const disableVersionCheck = disableVersionCheckEnv !== undefined &&
|
38 | disableVersionCheckEnv !== '0' &&
|
39 | disableVersionCheckEnv.toLowerCase() !== 'false';
|
40 | class UpdateCommand extends command_1.Command {
|
41 | constructor() {
|
42 | super(...arguments);
|
43 | this.allowMissingWorkspace = true;
|
44 | this.packageManager = schema_1.PackageManager.Npm;
|
45 | }
|
46 | async initialize() {
|
47 | this.packageManager = await package_manager_1.getPackageManager(this.workspace.root);
|
48 | this.workflow = new tools_1.NodeWorkflow(new core_1.virtualFs.ScopedHost(new node_1.NodeJsSyncHost(), core_1.normalize(this.workspace.root)), {
|
49 | packageManager: this.packageManager,
|
50 | root: core_1.normalize(this.workspace.root),
|
51 |
|
52 |
|
53 | resolvePaths: [__dirname, this.workspace.root],
|
54 | });
|
55 | this.workflow.engineHost.registerOptionsTransform(tools_1.validateOptionsWithSchema(this.workflow.registry));
|
56 | }
|
57 | async executeSchematic(collection, schematic, options = {}) {
|
58 | let error = false;
|
59 | let logs = [];
|
60 | const files = new Set();
|
61 | const reporterSubscription = this.workflow.reporter.subscribe(event => {
|
62 |
|
63 | const eventPath = event.path.startsWith('/') ? event.path.substr(1) : event.path;
|
64 | switch (event.kind) {
|
65 | case 'error':
|
66 | error = true;
|
67 | const desc = event.description == 'alreadyExist' ? 'already exists' : 'does not exist.';
|
68 | this.logger.error(`ERROR! ${eventPath} ${desc}.`);
|
69 | break;
|
70 | case 'update':
|
71 | logs.push(`${color_1.colors.whiteBright('UPDATE')} ${eventPath} (${event.content.length} bytes)`);
|
72 | files.add(eventPath);
|
73 | break;
|
74 | case 'create':
|
75 | logs.push(`${color_1.colors.green('CREATE')} ${eventPath} (${event.content.length} bytes)`);
|
76 | files.add(eventPath);
|
77 | break;
|
78 | case 'delete':
|
79 | logs.push(`${color_1.colors.yellow('DELETE')} ${eventPath}`);
|
80 | files.add(eventPath);
|
81 | break;
|
82 | case 'rename':
|
83 | const eventToPath = event.to.startsWith('/') ? event.to.substr(1) : event.to;
|
84 | logs.push(`${color_1.colors.blue('RENAME')} ${eventPath} => ${eventToPath}`);
|
85 | files.add(eventPath);
|
86 | break;
|
87 | }
|
88 | });
|
89 | const lifecycleSubscription = this.workflow.lifeCycle.subscribe(event => {
|
90 | if (event.kind == 'end' || event.kind == 'post-tasks-start') {
|
91 | if (!error) {
|
92 |
|
93 | logs.forEach(log => this.logger.info(log));
|
94 | logs = [];
|
95 | }
|
96 | }
|
97 | });
|
98 |
|
99 | try {
|
100 | await this.workflow
|
101 | .execute({
|
102 | collection,
|
103 | schematic,
|
104 | options,
|
105 | logger: this.logger,
|
106 | })
|
107 | .toPromise();
|
108 | reporterSubscription.unsubscribe();
|
109 | lifecycleSubscription.unsubscribe();
|
110 | return { success: !error, files };
|
111 | }
|
112 | catch (e) {
|
113 | if (e instanceof schematics_1.UnsuccessfulWorkflowExecution) {
|
114 | this.logger.error(`${color_1.colors.symbols.cross} Migration failed. See above for further details.\n`);
|
115 | }
|
116 | else {
|
117 | const logPath = log_file_1.writeErrorToLogFile(e);
|
118 | this.logger.fatal(`${color_1.colors.symbols.cross} Migration failed: ${e.message}\n` +
|
119 | ` See "${logPath}" for further details.\n`);
|
120 | }
|
121 | return { success: false, files };
|
122 | }
|
123 | }
|
124 | |
125 |
|
126 |
|
127 | async executeMigration(packageName, collectionPath, migrationName, commit) {
|
128 | const collection = this.workflow.engine.createCollection(collectionPath);
|
129 | const name = collection.listSchematicNames().find(name => name === migrationName);
|
130 | if (!name) {
|
131 | this.logger.error(`Cannot find migration '${migrationName}' in '${packageName}'.`);
|
132 | return false;
|
133 | }
|
134 | const schematic = this.workflow.engine.createSchematic(name, collection);
|
135 | this.logger.info(color_1.colors.cyan(`** Executing '${migrationName}' of package '${packageName}' **\n`));
|
136 | return this.executePackageMigrations([schematic.description], packageName, commit);
|
137 | }
|
138 | |
139 |
|
140 |
|
141 | async executeMigrations(packageName, collectionPath, range, commit) {
|
142 | const collection = this.workflow.engine.createCollection(collectionPath);
|
143 | const migrations = [];
|
144 | for (const name of collection.listSchematicNames()) {
|
145 | const schematic = this.workflow.engine.createSchematic(name, collection);
|
146 | const description = schematic.description;
|
147 | description.version = coerceVersionNumber(description.version) || undefined;
|
148 | if (!description.version) {
|
149 | continue;
|
150 | }
|
151 | if (semver.satisfies(description.version, range, { includePrerelease: true })) {
|
152 | migrations.push(description);
|
153 | }
|
154 | }
|
155 | migrations.sort((a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name));
|
156 | if (migrations.length === 0) {
|
157 | return true;
|
158 | }
|
159 | this.logger.info(color_1.colors.cyan(`** Executing migrations of package '${packageName}' **\n`));
|
160 | return this.executePackageMigrations(migrations, packageName, commit);
|
161 | }
|
162 | async executePackageMigrations(migrations, packageName, commit = false) {
|
163 | for (const migration of migrations) {
|
164 | this.logger.info(`${color_1.colors.symbols.pointer} ${migration.description.replace(/\. /g, '.\n ')}`);
|
165 | const result = await this.executeSchematic(migration.collection.name, migration.name);
|
166 | if (!result.success) {
|
167 | return false;
|
168 | }
|
169 | this.logger.info(' Migration completed.');
|
170 |
|
171 | if (commit) {
|
172 | const commitPrefix = `${packageName} migration - ${migration.name}`;
|
173 | const commitMessage = migration.description
|
174 | ? `${commitPrefix}\n\n${migration.description}`
|
175 | : commitPrefix;
|
176 | const committed = this.commit(commitMessage);
|
177 | if (!committed) {
|
178 |
|
179 | return false;
|
180 | }
|
181 | }
|
182 | this.logger.info('');
|
183 | }
|
184 | return true;
|
185 | }
|
186 |
|
187 | async run(options) {
|
188 |
|
189 |
|
190 |
|
191 | try {
|
192 | require.resolve('@angular-devkit/schematics', { paths: [this.workspace.root] });
|
193 | }
|
194 | catch (e) {
|
195 | if (e.code === 'MODULE_NOT_FOUND') {
|
196 | this.logger.fatal('The "@angular-devkit/schematics" package cannot be resolved from the workspace root directory. ' +
|
197 | 'This may be due to an unsupported node modules structure.\n' +
|
198 | 'Please remove both the "node_modules" directory and the package lock file; and then reinstall.\n' +
|
199 | 'If this does not correct the problem, ' +
|
200 | 'please temporarily install the "@angular-devkit/schematics" package within the workspace. ' +
|
201 | 'It can be removed once the update is complete.');
|
202 | return 1;
|
203 | }
|
204 | throw e;
|
205 | }
|
206 |
|
207 | if (!disableVersionCheck && await this.checkCLILatestVersion(options.verbose, options.next)) {
|
208 | this.logger.warn(`The installed local Angular CLI version is older than the latest ${options.next ? 'pre-release' : 'stable'} version.\n` +
|
209 | 'Installing a temporary version to perform the update.');
|
210 | return install_package_1.runTempPackageBin(`@angular/cli@${options.next ? 'next' : 'latest'}`, this.logger, this.packageManager, process.argv.slice(2));
|
211 | }
|
212 | const packages = [];
|
213 | for (const request of options['--'] || []) {
|
214 | try {
|
215 | const packageIdentifier = npa(request);
|
216 |
|
217 | if (!packageIdentifier.registry) {
|
218 | this.logger.error(`Package '${request}' is not a registry package identifer.`);
|
219 | return 1;
|
220 | }
|
221 | if (packages.some(v => v.name === packageIdentifier.name)) {
|
222 | this.logger.error(`Duplicate package '${packageIdentifier.name}' specified.`);
|
223 | return 1;
|
224 | }
|
225 | if (options.migrateOnly && packageIdentifier.rawSpec) {
|
226 | this.logger.warn('Package specifier has no effect when using "migrate-only" option.');
|
227 | }
|
228 |
|
229 | if (options.next && !packageIdentifier.rawSpec) {
|
230 | packageIdentifier.fetchSpec = 'next';
|
231 | }
|
232 | packages.push(packageIdentifier);
|
233 | }
|
234 | catch (e) {
|
235 | this.logger.error(e.message);
|
236 | return 1;
|
237 | }
|
238 | }
|
239 | if (options.all && packages.length > 0) {
|
240 | this.logger.error('Cannot specify packages when using the "all" option.');
|
241 | return 1;
|
242 | }
|
243 | else if (options.all && options.migrateOnly) {
|
244 | this.logger.error('Cannot use "all" option with "migrate-only" option.');
|
245 | return 1;
|
246 | }
|
247 | else if (!options.migrateOnly && (options.from || options.to)) {
|
248 | this.logger.error('Can only use "from" or "to" options with "migrate-only" option.');
|
249 | return 1;
|
250 | }
|
251 |
|
252 |
|
253 | const statusCheck = packages.length === 0 && !options.all;
|
254 | if (!statusCheck && !this.checkCleanGit()) {
|
255 | if (options.allowDirty) {
|
256 | this.logger.warn('Repository is not clean. Update changes will be mixed with pre-existing changes.');
|
257 | }
|
258 | else {
|
259 | this.logger.error('Repository is not clean. Please commit or stash any changes before updating.');
|
260 | return 2;
|
261 | }
|
262 | }
|
263 | this.logger.info(`Using package manager: '${this.packageManager}'`);
|
264 |
|
265 | if (options.migrateOnly === undefined &&
|
266 | options.from === undefined &&
|
267 | !options.all &&
|
268 | packages.length === 1 &&
|
269 | packages[0].name === '@angular/cli' &&
|
270 | this.workspace.configFile &&
|
271 | oldConfigFileNames.includes(this.workspace.configFile)) {
|
272 | options.migrateOnly = true;
|
273 | options.from = '1.0.0';
|
274 | }
|
275 | this.logger.info('Collecting installed dependencies...');
|
276 | const packageTree = await package_tree_1.readPackageTree(this.workspace.root);
|
277 | const rootDependencies = package_tree_1.findNodeDependencies(packageTree);
|
278 | this.logger.info(`Found ${Object.keys(rootDependencies).length} dependencies.`);
|
279 | if (options.all) {
|
280 |
|
281 |
|
282 | for (const dep of Object.keys(rootDependencies)) {
|
283 | const packageIdentifier = npa(dep);
|
284 | if (options.next) {
|
285 | packageIdentifier.fetchSpec = 'next';
|
286 | }
|
287 | packages.push(packageIdentifier);
|
288 | }
|
289 | }
|
290 | else if (packages.length === 0) {
|
291 |
|
292 | const { success } = await this.executeSchematic('@schematics/update', 'update', {
|
293 | force: options.force || false,
|
294 | next: options.next || false,
|
295 | verbose: options.verbose || false,
|
296 | packageManager: this.packageManager,
|
297 | packages: options.all ? Object.keys(rootDependencies) : [],
|
298 | });
|
299 | return success ? 0 : 1;
|
300 | }
|
301 | if (options.migrateOnly) {
|
302 | if (!options.from && typeof options.migrateOnly !== 'string') {
|
303 | this.logger.error('"from" option is required when using the "migrate-only" option without a migration name.');
|
304 | return 1;
|
305 | }
|
306 | else if (packages.length !== 1) {
|
307 | this.logger.error('A single package must be specified when using the "migrate-only" option.');
|
308 | return 1;
|
309 | }
|
310 | if (options.next) {
|
311 | this.logger.warn('"next" option has no effect when using "migrate-only" option.');
|
312 | }
|
313 | const packageName = packages[0].name;
|
314 | const packageDependency = rootDependencies[packageName];
|
315 | let packageNode = packageDependency && packageDependency.node;
|
316 | if (packageDependency && !packageNode) {
|
317 | this.logger.error('Package found in package.json but is not installed.');
|
318 | return 1;
|
319 | }
|
320 | else if (!packageDependency) {
|
321 |
|
322 |
|
323 |
|
324 | const child = packageTree.children.find(c => c.name === packageName);
|
325 | if (child) {
|
326 | packageNode = child;
|
327 | }
|
328 | }
|
329 | if (!packageNode) {
|
330 | this.logger.error('Package is not installed.');
|
331 | return 1;
|
332 | }
|
333 | const updateMetadata = packageNode.package['ng-update'];
|
334 | let migrations = updateMetadata && updateMetadata.migrations;
|
335 | if (migrations === undefined) {
|
336 | this.logger.error('Package does not provide migrations.');
|
337 | return 1;
|
338 | }
|
339 | else if (typeof migrations !== 'string') {
|
340 | this.logger.error('Package contains a malformed migrations field.');
|
341 | return 1;
|
342 | }
|
343 | else if (path.posix.isAbsolute(migrations) || path.win32.isAbsolute(migrations)) {
|
344 | this.logger.error('Package contains an invalid migrations field. Absolute paths are not permitted.');
|
345 | return 1;
|
346 | }
|
347 |
|
348 | migrations = migrations.replace(/\\/g, '/');
|
349 | if (migrations.startsWith('../')) {
|
350 | this.logger.error('Package contains an invalid migrations field. ' +
|
351 | 'Paths outside the package root are not permitted.');
|
352 | return 1;
|
353 | }
|
354 |
|
355 | const localMigrations = path.join(packageNode.path, migrations);
|
356 | if (fs.existsSync(localMigrations)) {
|
357 | migrations = localMigrations;
|
358 | }
|
359 | else {
|
360 |
|
361 |
|
362 | try {
|
363 | migrations = require.resolve(migrations, { paths: [packageNode.path] });
|
364 | }
|
365 | catch (e) {
|
366 | if (e.code === 'MODULE_NOT_FOUND') {
|
367 | this.logger.error('Migrations for package were not found.');
|
368 | }
|
369 | else {
|
370 | this.logger.error(`Unable to resolve migrations for package. [${e.message}]`);
|
371 | }
|
372 | return 1;
|
373 | }
|
374 | }
|
375 | let success = false;
|
376 | if (typeof options.migrateOnly == 'string') {
|
377 | success = await this.executeMigration(packageName, migrations, options.migrateOnly, options.createCommits);
|
378 | }
|
379 | else {
|
380 | const from = coerceVersionNumber(options.from);
|
381 | if (!from) {
|
382 | this.logger.error(`"from" value [${options.from}] is not a valid version.`);
|
383 | return 1;
|
384 | }
|
385 | const migrationRange = new semver.Range('>' + from + ' <=' + (options.to || packageNode.package.version));
|
386 | success = await this.executeMigrations(packageName, migrations, migrationRange, options.createCommits);
|
387 | }
|
388 | if (success) {
|
389 | if (packageName === '@angular/core'
|
390 | && options.from
|
391 | && +options.from.split('.')[0] < 9
|
392 | && (options.to || packageNode.package.version).split('.')[0] === '9') {
|
393 | this.logger.info(NG_VERSION_9_POST_MSG);
|
394 | }
|
395 | return 0;
|
396 | }
|
397 | return 1;
|
398 | }
|
399 | const requests = [];
|
400 |
|
401 | for (const pkg of packages) {
|
402 | const node = rootDependencies[pkg.name] && rootDependencies[pkg.name].node;
|
403 | if (!node) {
|
404 | this.logger.error(`Package '${pkg.name}' is not a dependency.`);
|
405 | return 1;
|
406 | }
|
407 |
|
408 | if (pkg.type === 'version' && node.package.version === pkg.fetchSpec) {
|
409 | this.logger.info(`Package '${pkg.name}' is already at '${pkg.fetchSpec}'.`);
|
410 | continue;
|
411 | }
|
412 | requests.push({ identifier: pkg, node });
|
413 | }
|
414 | if (requests.length === 0) {
|
415 | return 0;
|
416 | }
|
417 | const packagesToUpdate = [];
|
418 | this.logger.info('Fetching dependency metadata from registry...');
|
419 | for (const { identifier: requestIdentifier, node } of requests) {
|
420 | const packageName = requestIdentifier.name;
|
421 | let metadata;
|
422 | try {
|
423 |
|
424 |
|
425 | metadata = await package_metadata_1.fetchPackageMetadata(packageName, this.logger, {
|
426 | verbose: options.verbose,
|
427 | });
|
428 | }
|
429 | catch (e) {
|
430 | this.logger.error(`Error fetching metadata for '${packageName}': ` + e.message);
|
431 | return 1;
|
432 | }
|
433 |
|
434 |
|
435 | let manifest;
|
436 | if (requestIdentifier.type === 'version' ||
|
437 | requestIdentifier.type === 'range' ||
|
438 | requestIdentifier.type === 'tag') {
|
439 | try {
|
440 | manifest = pickManifest(metadata, requestIdentifier.fetchSpec);
|
441 | }
|
442 | catch (e) {
|
443 | if (e.code === 'ETARGET') {
|
444 |
|
445 |
|
446 | if (requestIdentifier.type === 'tag' &&
|
447 | requestIdentifier.fetchSpec === 'next' &&
|
448 | !requestIdentifier.rawSpec) {
|
449 | try {
|
450 | manifest = pickManifest(metadata, 'latest');
|
451 | }
|
452 | catch (e) {
|
453 | if (e.code !== 'ETARGET' && e.code !== 'ENOVERSIONS') {
|
454 | throw e;
|
455 | }
|
456 | }
|
457 | }
|
458 | }
|
459 | else if (e.code !== 'ENOVERSIONS') {
|
460 | throw e;
|
461 | }
|
462 | }
|
463 | }
|
464 | if (!manifest) {
|
465 | this.logger.error(`Package specified by '${requestIdentifier.raw}' does not exist within the registry.`);
|
466 | return 1;
|
467 | }
|
468 | if (manifest.version === node.package.version) {
|
469 | this.logger.info(`Package '${packageName}' is already up to date.`);
|
470 | continue;
|
471 | }
|
472 | packagesToUpdate.push(requestIdentifier.toString());
|
473 | }
|
474 | if (packagesToUpdate.length === 0) {
|
475 | return 0;
|
476 | }
|
477 | const { success } = await this.executeSchematic('@schematics/update', 'update', {
|
478 | verbose: options.verbose || false,
|
479 | force: options.force || false,
|
480 | next: !!options.next,
|
481 | packageManager: this.packageManager,
|
482 | packages: packagesToUpdate,
|
483 | migrateExternal: true,
|
484 | });
|
485 | if (success && options.createCommits) {
|
486 | const committed = this.commit(`Angular CLI update for packages - ${packagesToUpdate.join(', ')}`);
|
487 | if (!committed) {
|
488 | return 1;
|
489 | }
|
490 | }
|
491 |
|
492 |
|
493 | const migrations = global.externalMigrations;
|
494 | if (success && migrations) {
|
495 | for (const migration of migrations) {
|
496 | const result = await this.executeMigrations(migration.package,
|
497 |
|
498 |
|
499 | require.resolve(migration.collection, { paths: [this.workspace.root] }), new semver.Range('>' + migration.from + ' <=' + migration.to), options.createCommits);
|
500 | if (!result) {
|
501 | return 0;
|
502 | }
|
503 | }
|
504 | if (migrations.some(m => m.package === '@angular/core' && m.to.split('.')[0] === '9' && +m.from.split('.')[0] < 9)) {
|
505 | this.logger.info(NG_VERSION_9_POST_MSG);
|
506 | }
|
507 | }
|
508 | return success ? 0 : 1;
|
509 | }
|
510 | |
511 |
|
512 |
|
513 | commit(message) {
|
514 |
|
515 | let commitNeeded;
|
516 | try {
|
517 | commitNeeded = hasChangesToCommit();
|
518 | }
|
519 | catch (err) {
|
520 | this.logger.error(` Failed to read Git tree:\n${err.stderr}`);
|
521 | return false;
|
522 | }
|
523 | if (!commitNeeded) {
|
524 | this.logger.info(' No changes to commit after migration.');
|
525 | return true;
|
526 | }
|
527 |
|
528 | try {
|
529 | createCommit(message);
|
530 | }
|
531 | catch (err) {
|
532 | this.logger.error(`Failed to commit update (${message}):\n${err.stderr}`);
|
533 | return false;
|
534 | }
|
535 |
|
536 | const hash = findCurrentGitSha();
|
537 | const shortMessage = message.split('\n')[0];
|
538 | if (hash) {
|
539 | this.logger.info(` Committed migration step (${getShortHash(hash)}): ${shortMessage}.`);
|
540 | }
|
541 | else {
|
542 |
|
543 |
|
544 | this.logger.info(` Committed migration step: ${shortMessage}.`);
|
545 | this.logger.warn(' Failed to look up hash of most recent commit, continuing anyways.');
|
546 | }
|
547 | return true;
|
548 | }
|
549 | checkCleanGit() {
|
550 | try {
|
551 | const topLevel = child_process_1.execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: 'pipe' });
|
552 | const result = child_process_1.execSync('git status --porcelain', { encoding: 'utf8', stdio: 'pipe' });
|
553 | if (result.trim().length === 0) {
|
554 | return true;
|
555 | }
|
556 |
|
557 | for (const entry of result.split('\n')) {
|
558 | const relativeEntry = path.relative(path.resolve(this.workspace.root), path.resolve(topLevel.trim(), entry.slice(3).trim()));
|
559 | if (!relativeEntry.startsWith('..') && !path.isAbsolute(relativeEntry)) {
|
560 | return false;
|
561 | }
|
562 | }
|
563 | }
|
564 | catch (_a) { }
|
565 | return true;
|
566 | }
|
567 | |
568 |
|
569 |
|
570 |
|
571 | async checkCLILatestVersion(verbose = false, next = false) {
|
572 | const { version: installedCLIVersion } = require('../package.json');
|
573 | const LatestCLIManifest = await package_metadata_1.fetchPackageManifest(`@angular/cli@${next ? 'next' : 'latest'}`, this.logger, {
|
574 | verbose,
|
575 | usingYarn: this.packageManager === schema_1.PackageManager.Yarn,
|
576 | });
|
577 | return semver.lt(installedCLIVersion, LatestCLIManifest.version);
|
578 | }
|
579 | }
|
580 | exports.UpdateCommand = UpdateCommand;
|
581 |
|
582 |
|
583 |
|
584 | function hasChangesToCommit() {
|
585 |
|
586 | const files = child_process_1.execSync('git ls-files -m -d -o --exclude-standard').toString();
|
587 |
|
588 | return files !== '';
|
589 | }
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 | function createCommit(message) {
|
596 |
|
597 | child_process_1.execSync('git add -A', { encoding: 'utf8', stdio: 'pipe' });
|
598 |
|
599 | child_process_1.execSync('git commit --no-verify -F -', { encoding: 'utf8', stdio: 'pipe', input: message });
|
600 | }
|
601 |
|
602 |
|
603 |
|
604 | function findCurrentGitSha() {
|
605 | try {
|
606 | const hash = child_process_1.execSync('git rev-parse HEAD', { encoding: 'utf8', stdio: 'pipe' });
|
607 | return hash.trim();
|
608 | }
|
609 | catch (_a) {
|
610 | return null;
|
611 | }
|
612 | }
|
613 | function getShortHash(commitHash) {
|
614 | return commitHash.slice(0, 9);
|
615 | }
|
616 | function coerceVersionNumber(version) {
|
617 | if (!version) {
|
618 | return null;
|
619 | }
|
620 | if (!version.match(/^\d{1,30}\.\d{1,30}\.\d{1,30}/)) {
|
621 | const match = version.match(/^\d{1,30}(\.\d{1,30})*/);
|
622 | if (!match) {
|
623 | return null;
|
624 | }
|
625 | if (!match[1]) {
|
626 | version = version.substr(0, match[0].length) + '.0.0' + version.substr(match[0].length);
|
627 | }
|
628 | else if (!match[2]) {
|
629 | version = version.substr(0, match[0].length) + '.0' + version.substr(match[0].length);
|
630 | }
|
631 | else {
|
632 | return null;
|
633 | }
|
634 | }
|
635 | return semver.valid(version);
|
636 | }
|