11import 'dart:io' ;
22
33import 'package:dio/dio.dart' ;
4- import 'package:ground_control_client/ground_control_client.dart' ;
4+ import 'package:ground_control_client/ground_control_client.dart'
5+ show
6+ Client,
7+ DartSdkUnsupportedConstraintException,
8+ InvalidValueException,
9+ ServerpodClientException;
510import 'package:serverpod_cloud_cli/command_logger/command_logger.dart' ;
611import 'package:serverpod_cloud_cli/command_runner/helpers/file_uploader_factory.dart' ;
712import 'package:serverpod_cloud_cli/commands/deploy/script_runner.dart' ;
813import 'package:serverpod_cloud_cli/project_zipper/project_zipper.dart' ;
914import 'package:serverpod_cloud_cli/project_zipper/project_zipper_exceptions.dart' ;
1015import 'package:serverpod_cloud_cli/shared/exceptions/exit_exceptions.dart' ;
11- import 'package:serverpod_cloud_cli/util/dart_version_util.dart' ;
12- import 'package:serverpod_cloud_cli/util/pubspec_validator.dart' ;
16+ import 'package:serverpod_cloud_cli/util/dart_version_util.dart'
17+ show ProjectDartVersionHint;
18+ import 'package:serverpod_cloud_cli/util/pubspec_validator.dart'
19+ show TenantProjectPubspec;
1320import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_io.dart' ;
14- import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_model.dart' ;
1521import 'package:serverpod_cloud_cli/util/scloudignore.dart' show ScloudIgnore;
22+ import 'package:serverpod_cloud_cli/util/tool_versions_io.dart' ;
23+ import 'package:serverpod_cloud_cli/util/upload_description_metadata.dart' ;
1624
1725import 'prepare_workspace.dart' ;
1826
@@ -43,7 +51,7 @@ abstract class Deploy {
4351 throw FailureException (errors: issues);
4452 }
4553
46- var config = ScloudConfigIO .readFromFile (projectConfigFilePath);
54+ final config = ScloudConfigIO .readFromFile (projectConfigFilePath);
4755
4856 if (config != null && config.scripts.preDeploy.isNotEmpty) {
4957 await ScriptRunner .runScripts (
@@ -54,22 +62,74 @@ abstract class Deploy {
5462 );
5563 }
5664
57- final resolvedDartSdk =
58- dartVersionOverride ??
59- config? .dartSdk ??
60- resolveProjectDartSdkVersion (projectDirectory);
61- validateDartVersion (resolvedDartSdk);
65+ final dartVersionHint = ProjectDartVersionHint .resolveDartVersionForDeploy (
66+ override: dartVersionOverride,
67+ configDartSdk: config? .dartSdk,
68+ lazyVersionSources: [
69+ () {
70+ final roots = < Directory > [projectDirectory];
71+ if (pubspecValidator.isWorkspaceResolved ()) {
72+ final (workspaceRoot, _) = WorkspaceProject .findWorkspaceRoot (
73+ projectDirectory,
74+ );
75+ roots.add (workspaceRoot);
76+ }
77+ return ToolVersionsIO .readDartVersionFromToolVersions (roots);
78+ },
79+ pubspecValidator.environmentSdkConstraint,
80+ ],
81+ );
6282
63- if (resolvedDartSdk != config? .dartSdk) {
64- config ?? = ScloudConfig (
65- projectId: projectId,
66- scripts: ScloudScripts .empty (),
67- );
68- config = config.copyWith (dartSdk: resolvedDartSdk);
69- ScloudConfigIO .writeToFile (config, projectConfigFilePath);
70- }
83+ late final String uploadDescription;
7184
72- logger.debug ('Using Dart SDK version: $resolvedDartSdk ' );
85+ if (! dryRun) {
86+ final serverpodVersion = pubspecValidator.serverpodVersion;
87+
88+ await logger.progress ('Retrieving upload description...' , () async {
89+ try {
90+ uploadDescription = await cloudApiClient.deploy
91+ .createUploadDescription (
92+ projectId,
93+ serverpodVersion: serverpodVersion,
94+ dartVersion: dartVersionHint,
95+ );
96+ final resolvedTag = resolvedDartImageTagFromUploadDescription (
97+ uploadDescription,
98+ );
99+ if (resolvedTag != null ) {
100+ logger.debug ('Using Dart SDK $resolvedTag .' );
101+ }
102+ return true ;
103+ } on DartSdkUnsupportedConstraintException catch (e) {
104+ throw FailureException (
105+ error: e.message,
106+ hint: 'Please update the constraint and try again.' ,
107+ );
108+ } on InvalidValueException catch (e) {
109+ throw FailureException (error: e.message);
110+ } on ServerpodClientException catch (e) {
111+ if (e.message.toLowerCase ().contains ('connection timed out' )) {
112+ throw FailureException (
113+ error:
114+ 'Connection timed out. Please check your internet connection and try again.' ,
115+ hint: 'Try increasing the timeout with the --timeout option.' ,
116+ reason: e.toString (),
117+ );
118+ }
119+ throw FailureException .nested (
120+ e,
121+ null ,
122+ 'Failed to fetch upload description.' ,
123+ );
124+ } on Exception catch (e, stackTrace) {
125+ throw FailureException .nested (
126+ e,
127+ stackTrace,
128+ 'Failed to fetch upload description.' ,
129+ );
130+ }
131+ }, newParagraph: true );
132+ }
73133
74134 final Directory rootDirectory;
75135 final Iterable <String > includedSubPaths;
@@ -90,64 +150,60 @@ abstract class Deploy {
90150 }
91151
92152 late final List <int > projectZip;
93- final isZipped = await logger.progress (
94- 'Zipping project...' ,
95- newParagraph: true ,
96- () async {
97- try {
98- projectZip = await ProjectZipper .zipProject (
99- logger: logger,
100- rootDirectory: rootDirectory,
101- beneath: includedSubPaths,
102- fileReadPoolSize: concurrency,
103- showFiles: showFiles,
104- fileContentModifier: (final relativePath, final contentReader) async {
105- final isPubspec =
106- relativePath.endsWith ('pubspec.yaml' ) &&
107- ! relativePath.contains ('.scloud/' );
108- if (isPubspec) {
109- final content = await contentReader ();
110- return WorkspaceProject .stripDevDependenciesFromPubspecContent (
111- content,
112- );
113- }
114- return null ;
115- },
116- );
117- return true ;
118- } on ProjectZipperExceptions catch (e) {
119- switch (e) {
120- case ProjectDirectoryDoesNotExistException ():
121- logger.error ('Project directory does not exist: ${e .path }' );
122- break ;
123- case EmptyProjectException ():
124- logger.error (
125- 'No files to upload.' ,
126- hint:
127- 'Ensure that the correct project directory is selected (either through the --project-dir flag or the current directory) and check '
128- 'that `.gitignore` and `.scloudignore` does not filter out all project files.' ,
129- );
130- break ;
131- case DirectorySymLinkException ():
132- logger.error (
133- 'Serverpod Cloud does not support directory symlinks: `${e .path }`' ,
134- );
135- break ;
136- case NonResolvingSymlinkException ():
137- logger.error (
138- 'Serverpod Cloud does not support non-resolving symlinks: `${e .path }` => `${e .target }`' ,
153+ final isZipped = await logger.progress ('Zipping project...' , () async {
154+ try {
155+ projectZip = await ProjectZipper .zipProject (
156+ logger: logger,
157+ rootDirectory: rootDirectory,
158+ beneath: includedSubPaths,
159+ fileReadPoolSize: concurrency,
160+ showFiles: showFiles,
161+ fileContentModifier: (final relativePath, final contentReader) async {
162+ final isPubspec =
163+ relativePath.endsWith ('pubspec.yaml' ) &&
164+ ! relativePath.contains ('.scloud/' );
165+ if (isPubspec) {
166+ final content = await contentReader ();
167+ return WorkspaceProject .stripDevDependenciesFromPubspecContent (
168+ content,
139169 );
140- break ;
141- case NullZipException ():
142- logger.error (
143- 'Unknown error occurred while zipping project, please try again.' ,
144- );
145- break ;
146- }
147- return false ;
170+ }
171+ return null ;
172+ },
173+ );
174+ return true ;
175+ } on ProjectZipperExceptions catch (e) {
176+ switch (e) {
177+ case ProjectDirectoryDoesNotExistException ():
178+ logger.error ('Project directory does not exist: ${e .path }' );
179+ break ;
180+ case EmptyProjectException ():
181+ logger.error (
182+ 'No files to upload.' ,
183+ hint:
184+ 'Ensure that the correct project directory is selected (either through the --project-dir flag or the current directory) and check '
185+ 'that `.gitignore` and `.scloudignore` does not filter out all project files.' ,
186+ );
187+ break ;
188+ case DirectorySymLinkException ():
189+ logger.error (
190+ 'Serverpod Cloud does not support directory symlinks: `${e .path }`' ,
191+ );
192+ break ;
193+ case NonResolvingSymlinkException ():
194+ logger.error (
195+ 'Serverpod Cloud does not support non-resolving symlinks: `${e .path }` => `${e .target }`' ,
196+ );
197+ break ;
198+ case NullZipException ():
199+ logger.error (
200+ 'Unknown error occurred while zipping project, please try again.' ,
201+ );
202+ break ;
148203 }
149- },
150- );
204+ return false ;
205+ }
206+ });
151207
152208 if (! isZipped) throw ErrorExitException ('Failed to zip project.' );
153209
@@ -172,41 +228,6 @@ abstract class Deploy {
172228 return true ;
173229 });
174230 } else {
175- late final String uploadDescription;
176-
177- final serverpodVersion = pubspecValidator.serverpodVersion;
178-
179- await logger.progress ('Retrieving upload description...' , () async {
180- try {
181- uploadDescription = await cloudApiClient.deploy
182- .createUploadDescription (
183- projectId,
184- serverpodVersion: serverpodVersion,
185- );
186- return true ;
187- } on ServerpodClientException catch (e) {
188- if (e.message.toLowerCase ().contains ('connection timed out' )) {
189- throw FailureException (
190- error:
191- 'Connection timed out. Please check your internet connection and try again.' ,
192- hint: 'Try increasing the timeout with the --timeout option.' ,
193- reason: e.toString (),
194- );
195- }
196- throw FailureException .nested (
197- e,
198- null ,
199- 'Failed to fetch upload description.' ,
200- );
201- } on Exception catch (e, stackTrace) {
202- throw FailureException .nested (
203- e,
204- stackTrace,
205- 'Failed to fetch upload description.' ,
206- );
207- }
208- });
209-
210231 final success = await logger.progress ('Uploading project...' , () async {
211232 try {
212233 final fileUploader = fileUploaderFactory (uploadDescription);
0 commit comments