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
54 changes: 54 additions & 0 deletions .github/workflows/celest_cli.e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: celest_cli (E2E)
on:
pull_request:
paths:
- ".github/workflows/apps_cli_e2e.yaml"
- "apps/cli/lib/src/version.dart"
- "apps/cli/test/e2e/**"
push:
branches:
- main
paths:
- "apps/cli/**"

# Prevent duplicate runs due to Graphite
# https://graphite.dev/docs/troubleshooting#why-are-my-actions-running-twice
concurrency:
group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/main' && github.sha || ''}}
cancel-in-progress: true

# Uncomment if using self-hosted runners for consistency with other workflows which run setup-dart with OIDC enabled.
# permissions:
# contents: read
# id-token: write

env:
CELEST_LOCAL_PATH: ${{ github.workspace }}

jobs:
test:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-large
- macos-latest-xlarge
- windows-large
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- name: Git Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7
- name: Setup Flutter
uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # 2.16.0
with:
cache: true
- name: Setup Deps
if: runner.os == 'Linux'
run: tool/setup-ci.sh
- name: Get Packages
working-directory: apps/cli
run: dart pub upgrade
- name: Test
working-directory: apps/cli
run: dart test -t e2e-local --fail-fast
28 changes: 21 additions & 7 deletions apps/cli/lib/src/commands/init_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import 'package:celest_cli/src/init/project_init.dart';
import 'package:mason_logger/mason_logger.dart';

final class InitCommand extends CelestCommand with Configure, ProjectCreator {
InitCommand();
InitCommand() {
argParser.addFlag(
'precache',
help: 'Precache assets and warm up analyzer in the background.',
negatable: true,
hide: true,
defaultsTo: true,
);
}

@override
String get description => 'Creates a new Celest project.';
Expand All @@ -32,18 +40,20 @@ final class InitCommand extends CelestCommand with Configure, ProjectCreator {
'celest_cli:celest',
'precache',
projectPaths.projectRoot,
if (verbose) '--verbose',
],
CliRuntime.local => <String>[
platform.resolvedExecutable,
'run',
platform.script.toFilePath(),
'precache',
projectPaths.projectRoot,
if (verbose) '--verbose',
],
CliRuntime.aot => <String>[
platform.resolvedExecutable,
'precache',
projectPaths.projectRoot,
if (verbose) '--verbose',
],
};
try {
Expand All @@ -59,13 +69,17 @@ final class InitCommand extends CelestCommand with Configure, ProjectCreator {
}
}

bool get precache => argResults!.flag('precache');

@override
Future<int> run() async {
await super.run();

await checkForLatestVersion();
await configure();
await _precacheInBackground();
if (precache) {
await _precacheInBackground();
}

final projectRoot = projectPaths.projectRoot;

Expand All @@ -82,10 +96,10 @@ final class InitCommand extends CelestCommand with Configure, ProjectCreator {
}
stdout.writeln();
cliLogger.success('🚀 To start a local development server, run:');
stdout
..writeln()
..writeln(' $command')
..writeln();
cliLogger
..write(Platform.lineTerminator)
..write(' $command${Platform.lineTerminator}')
..write(Platform.lineTerminator);

return 0;
}
Expand Down
12 changes: 10 additions & 2 deletions apps/cli/lib/src/commands/precache_command.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:isolate';

import 'package:celest_cli/src/analyzer/celest_analyzer.dart';
import 'package:celest_cli/src/cli/cli.dart';
import 'package:celest_cli/src/commands/celest_command.dart';
import 'package:celest_cli/src/context.dart' as ctx;
import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/pub/pub_cache.dart';

Expand All @@ -10,7 +12,8 @@ final class PrecacheCommand extends CelestCommand {
String get name => 'precache';

@override
String get description => 'Precaches assets for a Celest Cloud project.';
String get description =>
'Precaches assets and warms up the analyzer for a Celest project.';

@override
bool get hidden => true;
Expand All @@ -20,7 +23,12 @@ final class PrecacheCommand extends CelestCommand {

static Future<void> _warmUp() async {
final projectRoot = projectPaths.projectRoot;
await Isolate.run(() => CelestAnalyzer.warmUp(projectRoot));
final verbose = ctx.verbose;
await Isolate.run(() async {
await Cli.configure(verbose: verbose);
await init(projectRoot: projectRoot);
await CelestAnalyzer.warmUp(projectRoot);
});
}

@override
Expand Down
27 changes: 9 additions & 18 deletions apps/cli/lib/src/init/migrations/add_generated_folder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,29 @@

import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/init/project_migration.dart';
import 'package:file/file.dart';

final class GeneratedFolder extends ProjectMigration {
const GeneratedFolder(super.projectRoot);
GeneratedFolder(super.projectRoot);

late final _readmeFile = fileSystem
.directory(projectRoot)
.childDirectory('generated')
.childFile('README.md');

@override
bool get needsMigration => false;
bool get needsMigration => !_readmeFile.existsSync();

@override
String get name => 'core.layout.generated';

@override
Future<ProjectMigrationResult> create() async {
final generatedDir =
fileSystem.directory(projectRoot).childDirectory('generated');
await _createIfNotExists(
generatedDir.childFile('README.md'),
generated_README,
);
await _readmeFile.create(recursive: true);
await _readmeFile.writeAsString(generated_README);
return const ProjectMigrationSuccess();
}
}

Future<void> _createIfNotExists(File file, String content) async {
if (!file.existsSync()) {
await file.create(recursive: true);
await file.writeAsString(content);
}
}

const generated_README = '''
# Generated Celest code

Expand All @@ -40,6 +33,4 @@ your backend.

This code can be safely checked into version control, but it should not be
modified directly.

It is planned to replace this directory with macros when they become stable.
''';
27 changes: 14 additions & 13 deletions apps/cli/lib/src/init/project_init.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ base mixin Configure on CelestCommand {
currentProgress?.complete();
currentProgress = cliLogger.progress('Generating project');
case CreatedProject():
currentProgress?.complete('Project successfully generated');
currentProgress?.complete('Project generated successfully');
currentProgress = null;
case MigratingProject():
currentProgress?.complete();
currentProgress = cliLogger.progress('Migrating project');
case MigratedProject():
currentProgress?.complete('Project successfully migrated');
currentProgress?.complete('Project migrated successfully');
currentProgress = null;
case Initialized(needsAnalyzerMigration: final needsMigration):
currentProgress?.complete();
Expand Down Expand Up @@ -292,23 +292,24 @@ base mixin Configure on CelestCommand {
// TODO(dnys1): Improve logic here so that we don't run pub upgrade if
// the dependencies in the lockfile are already up to date.
Future<void> _pubUpgrade() async {
await Future.wait([
runPub(
action: PubAction.upgrade,
workingDirectory: projectPaths.projectRoot,
),
runPub(
action: PubAction.get,
workingDirectory: projectPaths.clientRoot,
),
]);
await runPub(
action: PubAction.upgrade,
workingDirectory: projectPaths.projectRoot,
);
// Run serially to avoid `flutter pub` locks.
if (celestProject.parentProject case final parentProject?) {
await runPub(
exe: parentProject.type.name,
exe: parentProject.type.executable,
action: PubAction.get,
workingDirectory: parentProject.path,
);
}

// Get dependencies in client so that the analyzer does not show red
// everywhere, but ignore to avoid blocking the current command.
runPub(
action: PubAction.get,
workingDirectory: projectPaths.clientRoot,
).ignore();
}
}
65 changes: 39 additions & 26 deletions apps/cli/lib/src/project/celest_project.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import 'dart:io';
import 'dart:isolate';

import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
Expand All @@ -18,6 +19,7 @@ import 'package:celest_cli/src/env/env_manager.dart';
import 'package:celest_cli/src/project/project_paths.dart';
import 'package:celest_cli/src/pub/cached_pubspec.dart';
import 'package:celest_cli/src/sdk/dart_sdk.dart';
import 'package:celest_cli/src/utils/error.dart';
import 'package:celest_cli/src/utils/run.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -225,36 +227,39 @@ final class CelestProject {
// the project is generated.
throw StateError('No project.dart file found in the project root.');
}
final projectLibrary = analysisContext.currentSession.getParsedLibrary(
projectDart.path,

final parseResult = parseFile(
path: projectDart.path,
featureSet: FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Sdk.current.version,
flags: _analysisOptions.enabledExperiments,
),
throwIfDiagnostics: true,
);
switch (projectLibrary) {
case ParsedLibraryResult(:final units):
final declarations = units
.expand((unit) => unit.unit.declarations)
.whereType<TopLevelVariableDeclaration>()
.expand((declaration) => declaration.variables.variables);
for (final declaration in declarations) {
if (declaration.initializer
case MethodInvocation(
methodName: SimpleIdentifier(name: 'Project'),
:final argumentList,
final projectLibrary = parseResult.unit;

final declarations = projectLibrary.declarations
.whereType<TopLevelVariableDeclaration>()
.expand((declaration) => declaration.variables.variables);
for (final declaration in declarations) {
if (declaration.initializer
case MethodInvocation(
methodName: SimpleIdentifier(name: 'Project'),
:final argumentList,
)) {
for (final argument in argumentList.arguments) {
if (argument
case NamedExpression(
name: Label(label: SimpleIdentifier(name: 'name')),
expression: SimpleStringLiteral(value: final projectName),
)) {
for (final argument in argumentList.arguments) {
if (argument
case NamedExpression(
name: Label(label: SimpleIdentifier(name: 'name')),
expression: SimpleStringLiteral(value: final projectName),
)) {
return projectName;
}
}
return projectName;
}
}
throw StateError('No Project(name: "name") found in project.dart');
default:
throw StateError('Failed to parse project.dart');
}
}

throw StateError('No Project(name: "name") found in project.dart');
}

Future<void> close() async {
Expand Down Expand Up @@ -291,3 +296,11 @@ extension CelestProjectUriStorage on IsolatedNativeStorage {
return uri;
}
}

extension SdkExe on SdkType {
String get executable => switch (this) {
SdkType.dart => Sdk.current.dart,
SdkType.flutter => Sdk.current.flutter!,
_ => unreachable(),
};
}
5 changes: 3 additions & 2 deletions apps/cli/lib/src/pub/pub_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:celest_cli/src/context.dart';
import 'package:celest_cli/src/exceptions.dart';
import 'package:celest_cli/src/project/celest_project.dart';
import 'package:celest_cli/src/sdk/dart_sdk.dart';
import 'package:celest_core/_internal.dart';
import 'package:cli_script/cli_script.dart';
Expand Down Expand Up @@ -48,8 +49,8 @@ Future<void> runPub({
required String workingDirectory,
}) async {
exe ??= kDebugMode
? platform.resolvedExecutable
: (await celestProject.determineProjectType()).name;
? Sdk.current.dart
: (await celestProject.determineProjectType()).executable;

final command = <String>[exe, 'pub', action.name];
final logger = Logger(command.join(' '));
Expand Down
7 changes: 7 additions & 0 deletions apps/cli/lib/src/sdk/dart_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ class Sdk {

String get dart => p.join(sdkPath, 'bin', 'dart');

String? get flutter {
if (flutterSdkRoot case final flutterRoot?) {
return p.join(flutterRoot, 'bin', 'flutter');
}
return null;
}

String get dartAotRuntime => p.join(sdkPath, 'bin', 'dartaotruntime');

String get analysisServerSnapshot =>
Expand Down
Loading
Loading