Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/cli/fixtures/fixtures_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class TestRunner {
projectRoot: projectRoot,
projectName: celestProject.projectName,
parentProject: parentProject,
upgradeFromVersion: null,
);
await (_warmUp(projectRoot), migrator.migrate()).wait;
});
Expand Down
26 changes: 1 addition & 25 deletions apps/cli/lib/src/analyzer/celest_analysis_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:celest_cli/src/analyzer/analysis_error.dart';
import 'package:celest_cli/src/analyzer/resolver/project_resolver.dart';
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/init/edits/source_edit.dart';
import 'package:celest_cli/src/utils/analyzer.dart';
import 'package:celest_cli/src/utils/json.dart';
import 'package:logging/logging.dart';
Expand All @@ -27,31 +28,6 @@ enum CustomType {
};
}

final class SourceEdit {
const SourceEdit(this.offset, this.length, this.replacement);

final int offset;
final int length;
final String replacement;

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is SourceEdit &&
other.offset == offset &&
other.length == length &&
other.replacement == replacement;
}

@override
int get hashCode => Object.hash(offset, length, replacement);

@override
String toString() {
return 'SourceEdit(offset: $offset, length: $length, replacement: $replacement)';
}
}

mixin CelestAnalysisHelpers implements CelestErrorReporter {
AnalysisContext get context;
Set<InterfaceElement> get customExceptionTypes;
Expand Down
44 changes: 2 additions & 42 deletions apps/cli/lib/src/analyzer/celest_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:celest_cli/src/ast/ast.dart';
import 'package:celest_cli/src/config/feature_flags.dart';
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/database/cache/cache_database.dart';
import 'package:celest_cli/src/init/edits/source_edit_applier.dart';
import 'package:celest_cli/src/pub/project_dependency.dart';
import 'package:celest_cli/src/pub/pub_action.dart';
import 'package:celest_cli/src/pub/pub_environment.dart';
Expand All @@ -25,7 +26,6 @@ import 'package:celest_cli/src/sdk/dart_sdk.dart';
import 'package:celest_cli/src/types/type_helper.dart';
import 'package:celest_cli/src/utils/analyzer.dart';
import 'package:celest_cli/src/utils/reference.dart';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:source_span/source_span.dart';
Expand Down Expand Up @@ -583,47 +583,7 @@ const project = Project(name: 'cache_warmup');
}

Future<void> _applyMigrations() async {
if (resolver.pendingEdits.isEmpty) {
return;
}

_logger.fine('Applying ${resolver.pendingEdits.length} migrations');

final fileChanges = <Future<void>>[];
for (final entry in resolver.pendingEdits.entries) {
final path = entry.key;

// Sort edits in reserve order to avoid offset changes.
final edits = entry.value.sorted((a, b) {
return -a.offset.compareTo(b.offset);
});

_logger.finest('Applying migrations to $path: $edits');

final file = context.currentSession.getFile(path) as FileResult;
var source = file.content;

for (final edit in edits) {
source = source.replaceRange(
edit.offset,
edit.offset + edit.length,
edit.replacement,
);
}

fileChanges.add(fileSystem.file(path).writeAsString(source));
}

await Future.wait(fileChanges);

_logger.finest('Applied migrations to disk');

for (final path in pendingEdits.keys) {
context.changeFile(path);
}
final changes = await context.applyPendingFileChanges();

_logger.finest('Applied changes in analyzer: $changes');
await SourceEditApplier(resolver.pendingEdits).apply();
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/cli/lib/src/analyzer/resolver/project_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:celest_cli/src/analyzer/celest_analysis_helpers.dart';
import 'package:celest_cli/src/analyzer/resolver/config_value_resolver.dart';
import 'package:celest_cli/src/config/feature_flags.dart';
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/init/edits/source_edit.dart';
import 'package:celest_cli/src/sdk/dart_sdk.dart';
import 'package:celest_cli/src/serialization/common.dart';
import 'package:celest_cli/src/serialization/serialization_verdict.dart';
Expand Down
15 changes: 9 additions & 6 deletions apps/cli/lib/src/commands/uninstall/celest_uninstaller.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';

import 'package:celest_cli/src/cli/cli_runtime.dart';
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/exceptions.dart';
import 'package:celest_cli/src/utils/error.dart';
Expand All @@ -17,12 +18,14 @@ class CelestUninstaller {
Future<void> uninstall() async {
await removeConfig();

if (fileSystem.path.fromUri(platform.script).endsWith('.snapshot')) {
await _uninstallPubGlobal();
} else if (platform.executable.contains('dart')) {
// Celest is running from source. Nothing to uninstall.
} else {
await _uninstallAot();
switch (CliRuntime.current) {
case CliRuntime.pubGlobal:
await _uninstallPubGlobal();
case CliRuntime.local:
// Celest is running from source. Nothing to uninstall.
break;
case CliRuntime.aot:
await _uninstallAot();
}
}

Expand Down
12 changes: 7 additions & 5 deletions apps/cli/lib/src/database/cache/cache_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,23 @@ final class CacheDatabase extends $CacheDatabase {
await database.clear();
await database._setVersionInfo(update: true);
}
if (semver.Version.parse(celest) < semver.Version.parse(packageVersion)) {
database._needsProjectUpgrade = true;
final upgradeFromVersion = semver.Version.parse(celest);
database._upgradeFromVersion = upgradeFromVersion;
if (upgradeFromVersion < currentVersion) {
await database._setVersionInfo(update: true);
}
} else if (versionInfo == null) {
database._needsProjectUpgrade = true;
await database._setVersionInfo(update: false);
}
final rawDb = await rawCompleter.future;
database.byteStore = CachingByteStore(rawDb);
return database;
}

bool _needsProjectUpgrade = false;
bool get needsProjectUpgrade => _needsProjectUpgrade;
semver.Version? _upgradeFromVersion;

/// The version of the Celest package before the current one.
semver.Version? get upgradeFromVersion => _upgradeFromVersion;

Future<void> _setVersionInfo({required bool update}) async {
if (update) {
Expand Down
24 changes: 24 additions & 0 deletions apps/cli/lib/src/init/edits/source_edit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
final class SourceEdit {
const SourceEdit(this.offset, this.length, this.replacement);

final int offset;
final int length;
final String replacement;

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is SourceEdit &&
other.offset == offset &&
other.length == length &&
other.replacement == replacement;
}

@override
int get hashCode => Object.hash(offset, length, replacement);

@override
String toString() {
return 'SourceEdit(offset: $offset, length: $length, replacement: $replacement)';
}
}
60 changes: 60 additions & 0 deletions apps/cli/lib/src/init/edits/source_edit_applier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/init/edits/source_edit.dart';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';

final class SourceEditApplier {
SourceEditApplier(this.edits);

final Map<String, Iterable<SourceEdit>> edits;

static final Logger _logger = Logger('SourceEditApplier');

Future<void> _applyEdits(String path, List<SourceEdit> edits) async {
_logger.finest('Applying migrations to $path: $edits');

var source = await fileSystem.file(path).readAsString();

for (final edit in edits) {
source = source.replaceRange(
edit.offset,
edit.offset + edit.length,
edit.replacement,
);
}

await fileSystem.file(path).writeAsString(source);
}

/// Applies all pendings edits to disk.
Future<void> apply() async {
if (edits.isEmpty) {
_logger.fine('No edits to apply');
return;
}

_logger.fine('Applying ${edits.length} migrations');

final fileChanges = <Future<void>>[];
for (final entry in edits.entries) {
final path = entry.key;
if (entry.value.isEmpty) {
_logger.fine('No edits to apply to $path');
continue;
}

// Sort edits in reserve order to avoid offset changes.
final edits = entry.value.sorted((a, b) {
return -a.offset.compareTo(b.offset);
});

fileChanges.add(_applyEdits(path, edits));
}

await Future.wait(fileChanges);

_logger.finest('Applied migrations to disk');

await celestProject.invalidate(edits.keys);
}
}
27 changes: 12 additions & 15 deletions apps/cli/lib/src/init/migrations/pubspec_updater.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:yaml_edit/yaml_edit.dart' hide SourceEdit;

final class PubspecUpdater extends ProjectMigration {
const PubspecUpdater(super.projectRoot, this.parentProject, this.projectName);
const PubspecUpdater(
super.projectRoot,
this.parentProject,
this.projectName, {
this.upgradeFromVersion,
});

static final _logger = Logger('PubspecUpdater');

final ParentProject? parentProject;
final String projectName;
final Version? upgradeFromVersion;

@override
String get name => 'core.project.pubspec';
Expand Down Expand Up @@ -55,7 +61,7 @@ final class PubspecUpdater extends ProjectMigration {
}

/// Returns the version updated from.
Future<Version?> _updateBackendDependencies({
Future<void> _updateBackendDependencies({
required Pubspec pubspec,
required String pubspecYaml,
required File pubspecFile,
Expand All @@ -65,16 +71,8 @@ final class PubspecUpdater extends ProjectMigration {
if (ProjectDependency.backendDependencies.upToDate(pubspec) &&
currentSdkVersion == requiredSdkVersion) {
_logger.fine('Project dependencies are up to date.');
return null;
return;
}
final fromVersion = switch (pubspec.dependencies['celest']) {
final HostedDependency hosted => switch (hosted.version) {
final Version version => version,
final VersionRange range => range.min,
_ => Version.none,
},
_ => Version.none,
};
_logger.fine('Updating project dependencies to latest versions...');
pubspec = pubspec.copyWith(
environment: {'sdk': PubEnvironment.dartSdkConstraint},
Expand All @@ -95,7 +93,6 @@ final class PubspecUpdater extends ProjectMigration {
);
pubspecYaml = pubspec.toYaml(source: pubspecYaml);
await pubspecFile.writeAsString(pubspecYaml);
return fromVersion;
}

Future<bool> _updateClientDependencies({
Expand Down Expand Up @@ -173,14 +170,14 @@ final class PubspecUpdater extends ProjectMigration {
final pubspecFile = fileSystem.file(p.join(projectRoot, 'pubspec.yaml'));
final pubspecYaml = await pubspecFile.readAsString();
final pubspec = Pubspec.parse(pubspecYaml);
final fromVersion = await _updateBackendDependencies(
await _updateBackendDependencies(
pubspec: pubspec,
pubspecYaml: pubspecYaml,
pubspecFile: pubspecFile,
);
// await _updateProjectName();
needsAnalyzerMigration |=
fromVersion != null && fromVersion < Version(1, 0, 0).firstPreRelease;
needsAnalyzerMigration |= upgradeFromVersion != null &&
upgradeFromVersion! < Version(1, 0, 0).firstPreRelease;
if (needsAnalyzerMigration) {
operations.add(
runPub(action: PubAction.get, workingDirectory: projectRoot),
Expand Down
Loading