Skip to content

Commit 3203875

Browse files
author
serverpod_cloud
committed
feat: 8e027bbaef4860a2b5a96c2a168ca653c4b23216
1 parent 9572502 commit 3203875

13 files changed

Lines changed: 888 additions & 222 deletions

File tree

serverpod_cloud_cli/lib/command_runner/commands/deploy_command.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import 'package:config/config.dart';
2+
import 'package:path/path.dart' as p;
23
import 'package:serverpod_cloud_cli/command_runner/cloud_cli_command.dart';
34
import 'package:serverpod_cloud_cli/command_runner/helpers/command_options.dart';
45
import 'package:serverpod_cloud_cli/commands/deploy/deploy.dart';
6+
import 'package:serverpod_cloud_cli/constants.dart';
57

68
import 'categories.dart';
79

@@ -84,13 +86,20 @@ Examples
8486

8587
final projectDirectory = runner.verifiedProjectDirectory();
8688
logger.debug('Using project directory `${projectDirectory.path}`');
89+
final configFilePath =
90+
globalConfiguration.projectConfigFile?.path ??
91+
p.join(
92+
projectDirectory.path,
93+
ProjectConfigFileConstants.defaultFileName,
94+
);
8795

8896
await Deploy.deploy(
8997
runner.serviceProvider.cloudApiClient,
9098
runner.serviceProvider.fileUploaderFactory,
9199
logger: logger,
92100
projectId: projectId,
93101
projectDir: projectDirectory.path,
102+
projectConfigFilePath: configFilePath,
94103
concurrency: concurrency,
95104
dryRun: dryRun,
96105
showFiles: showFiles,

serverpod_cloud_cli/lib/commands/deploy/deploy.dart

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import 'package:serverpod_cloud_cli/command_runner/helpers/file_uploader_factory
88
import 'package:serverpod_cloud_cli/project_zipper/project_zipper_exceptions.dart';
99
import 'package:serverpod_cloud_cli/project_zipper/project_zipper.dart';
1010
import 'package:serverpod_cloud_cli/util/pubspec_validator.dart';
11+
import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_io.dart';
1112
import 'package:serverpod_cloud_cli/util/scloudignore.dart' show ScloudIgnore;
13+
import 'package:serverpod_cloud_cli/commands/deploy/script_runner.dart';
1214

1315
import 'prepare_workspace.dart';
1416

@@ -19,6 +21,7 @@ abstract class Deploy {
1921
required final CommandLogger logger,
2022
required final String projectId,
2123
required final String projectDir,
24+
required final String projectConfigFilePath,
2225
required final int concurrency,
2326
required final bool dryRun,
2427
required final bool showFiles,
@@ -36,6 +39,17 @@ abstract class Deploy {
3639
throw FailureException(errors: issues);
3740
}
3841

42+
final config = ScloudConfigIO.readFromFile(projectConfigFilePath);
43+
44+
if (config != null && config.scripts.preDeploy.isNotEmpty) {
45+
await ScriptRunner.runScripts(
46+
config.scripts.preDeploy,
47+
projectDir,
48+
logger,
49+
scriptType: 'pre-deploy',
50+
);
51+
}
52+
3953
final Directory rootDirectory;
4054
final Iterable<String> includedSubPaths;
4155
if (pubspecValidator.isWorkspaceResolved()) {
@@ -120,48 +134,62 @@ abstract class Deploy {
120134
await logger.progress('Dry run, skipping upload.', () async {
121135
return true;
122136
});
123-
return;
124-
}
125-
126-
final success = await logger.progress('Uploading project...', () async {
127-
late final String uploadDescription;
128-
try {
129-
uploadDescription = await cloudApiClient.deploy.createUploadDescription(
130-
projectId,
131-
);
132-
} on Exception catch (e, stackTrace) {
133-
throw FailureException.nested(
134-
e,
135-
stackTrace,
136-
'Failed to fetch upload description',
137-
);
138-
}
137+
} else {
138+
final success = await logger.progress('Uploading project...', () async {
139+
late final String uploadDescription;
140+
try {
141+
uploadDescription = await cloudApiClient.deploy
142+
.createUploadDescription(projectId);
143+
} on Exception catch (e, stackTrace) {
144+
throw FailureException.nested(
145+
e,
146+
stackTrace,
147+
'Failed to fetch upload description',
148+
);
149+
}
139150

140-
try {
141-
final fileUploader = fileUploaderFactory(uploadDescription);
142-
final ret = await fileUploader.upload(
143-
Stream.fromIterable([projectZip]),
144-
projectZip.length,
145-
);
146-
if (!ret) {
147-
logger.error('Failed to upload project, please try again.');
151+
try {
152+
final fileUploader = fileUploaderFactory(uploadDescription);
153+
final ret = await fileUploader.upload(
154+
Stream.fromIterable([projectZip]),
155+
projectZip.length,
156+
);
157+
if (!ret) {
158+
logger.error('Failed to upload project, please try again.');
159+
}
160+
return ret;
161+
} on DioException catch (e) {
162+
throw FailureException(
163+
error:
164+
'Failed to upload project: ${_uploadDioExceptionFormatter(e)}',
165+
);
166+
} on Exception catch (e, stackTrace) {
167+
throw FailureException.nested(
168+
e,
169+
stackTrace,
170+
'Failed to upload project',
171+
);
148172
}
149-
return ret;
150-
} on DioException catch (e) {
151-
throw FailureException(
152-
error: 'Failed to upload project: ${_uploadDioExceptionFormatter(e)}',
153-
);
154-
} on Exception catch (e, stackTrace) {
155-
throw FailureException.nested(
156-
e,
157-
stackTrace,
158-
'Failed to upload project',
159-
);
173+
});
174+
175+
if (!success) {
176+
throw ErrorExitException('Failed to upload project.');
160177
}
161-
});
162178

163-
if (!success) {
164-
throw ErrorExitException('Failed to upload project.');
179+
logger.success(
180+
'Project uploaded successfully!',
181+
trailingRocket: true,
182+
newParagraph: true,
183+
);
184+
}
185+
186+
if (config != null && config.scripts.postDeploy.isNotEmpty) {
187+
await ScriptRunner.runScripts(
188+
config.scripts.postDeploy,
189+
projectDir,
190+
logger,
191+
scriptType: 'post-deploy',
192+
);
165193
}
166194
}
167195

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'dart:io';
2+
3+
import 'package:cli_tools/execute.dart';
4+
import 'package:serverpod_cloud_cli/command_logger/command_logger.dart';
5+
import 'package:serverpod_cloud_cli/shared/exceptions/exit_exceptions.dart';
6+
7+
abstract class ScriptRunner {
8+
static Future<void> runScripts(
9+
final List<String> commands,
10+
final String workingDirectory,
11+
final CommandLogger logger, {
12+
required final String scriptType,
13+
}) async {
14+
if (commands.isEmpty) {
15+
return;
16+
}
17+
18+
logger.info('Running $scriptType scripts', newParagraph: true);
19+
for (var i = 0; i < commands.length; i++) {
20+
final command = commands[i];
21+
22+
logger.info(
23+
'(${i + 1}/${commands.length}) $command',
24+
newParagraph: i == 0,
25+
);
26+
27+
try {
28+
await execute(
29+
command,
30+
stderr: stderr,
31+
stdout: stdout,
32+
workingDirectory: Directory(workingDirectory),
33+
);
34+
} on Exception catch (e, stackTrace) {
35+
throw ErrorExitException('$scriptType script failed', e, stackTrace);
36+
}
37+
}
38+
}
39+
}

serverpod_cloud_cli/lib/commands/launch/launch.dart

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,26 @@ The default API domain will be: <project-id>.api.serverpod.space
365365
) async {
366366
logger.info('Launching project...');
367367

368-
final projectId = projectSetup.projectId!;
369-
final projectDir = projectSetup.projectDir!;
370-
final performDeploy = projectSetup.performDeploy!;
368+
final projectId = projectSetup.projectId;
369+
final projectDir = projectSetup.projectDir;
370+
final configFilePath = projectSetup.configFilePath;
371+
final performDeploy = projectSetup.performDeploy;
372+
373+
if (projectId == null) {
374+
throw StateError('ProjectId must be set.');
375+
}
376+
377+
if (projectDir == null) {
378+
throw StateError('ProjectDir must be set.');
379+
}
380+
381+
if (configFilePath == null) {
382+
throw StateError('ConfigFilePath must be set.');
383+
}
384+
385+
if (performDeploy == null) {
386+
throw StateError('PerformDeploy must be set.');
387+
}
371388

372389
if (projectSetup.preexistingProject != true) {
373390
final enableDb = projectSetup.enableDb!;
@@ -377,10 +394,7 @@ The default API domain will be: <project-id>.api.serverpod.space
377394
projectId: projectId,
378395
enableDb: enableDb,
379396
projectDir: projectDir,
380-
configFilePath: p.join(
381-
projectDir,
382-
ProjectConfigFileConstants.defaultFileName,
383-
),
397+
configFilePath: configFilePath,
384398
skipConfirmation: true,
385399
);
386400
}
@@ -399,6 +413,7 @@ The default API domain will be: <project-id>.api.serverpod.space
399413
logger: logger,
400414
projectId: projectId,
401415
projectDir: projectDir,
416+
projectConfigFilePath: configFilePath,
402417
concurrency: 5,
403418
dryRun: false,
404419
showFiles: false,
@@ -473,19 +488,35 @@ The default API domain will be: <project-id>.api.serverpod.space
473488
}
474489

475490
class ProjectLaunch {
476-
String? projectDir;
491+
String? _projectDir;
492+
String? configFilePath;
477493
String? projectId;
478494
bool? enableDb;
479495
bool? preexistingProject;
480496
bool? performDeploy;
481497

482498
ProjectLaunch({
483-
this.projectDir,
499+
final String? projectDir,
484500
this.projectId,
485501
this.enableDb,
486502
this.preexistingProject,
487503
this.performDeploy,
488-
});
504+
}) : _projectDir = projectDir {
505+
if (projectDir != null) {
506+
configFilePath = _constructConfigFilePath(projectDir);
507+
}
508+
}
509+
510+
set projectDir(final String projectDir) {
511+
_projectDir = projectDir;
512+
configFilePath = _constructConfigFilePath(projectDir);
513+
}
514+
515+
String? get projectDir => _projectDir;
516+
517+
String _constructConfigFilePath(final String projectDir) {
518+
return p.join(projectDir, ProjectConfigFileConstants.defaultFileName);
519+
}
489520

490521
@override
491522
String toString() {

serverpod_cloud_cli/lib/commands/project/project.dart

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import 'package:serverpod_cloud_cli/commands/deploy/prepare_workspace.dart'
1010
import 'package:serverpod_cloud_cli/shared/user_interaction/user_confirmations.dart';
1111
import 'package:serverpod_cloud_cli/util/printers/table_printer.dart';
1212
import 'package:serverpod_cloud_cli/util/pubspec_validator.dart';
13-
import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_file.dart';
13+
import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_model.dart';
14+
import 'package:serverpod_cloud_cli/util/scloud_config/scloud_config_io.dart';
1415
import 'package:serverpod_cloud_cli/util/scloudignore.dart';
1516

1617
abstract class ProjectCommands {
@@ -106,16 +107,15 @@ abstract class ProjectCommands {
106107
return;
107108
}
108109

109-
final projectConfig = await _fetchProjectConfig(
110-
logger,
111-
cloudApiClient,
112-
projectId,
110+
final config = ScloudConfig(
111+
projectId: projectId,
112+
scripts: ScloudScripts.empty(),
113113
);
114114

115115
await logger.progress(
116116
'Writing cloud project configuration files.',
117117
() async {
118-
_writeProjectFiles(logger, projectConfig, projectDir, configFilePath);
118+
_writeProjectFiles(logger, config, projectDir, configFilePath);
119119
return true;
120120
},
121121
);
@@ -212,21 +212,15 @@ abstract class ProjectCommands {
212212
required final String projectDirectory,
213213
required final String configFilePath,
214214
}) async {
215-
final projectConfig = await _fetchProjectConfig(
216-
logger,
217-
cloudApiClient,
218-
projectId,
215+
final config = ScloudConfig(
216+
projectId: projectId,
217+
scripts: ScloudScripts.empty(),
219218
);
220219

221220
await logger.progress(
222221
'Writing cloud project configuration files.',
223222
() async {
224-
_writeProjectFiles(
225-
logger,
226-
projectConfig,
227-
projectDirectory,
228-
configFilePath,
229-
);
223+
_writeProjectFiles(logger, config, projectDirectory, configFilePath);
230224
return true;
231225
},
232226
);
@@ -297,24 +291,9 @@ abstract class ProjectCommands {
297291
}
298292
}
299293

300-
/// Fetches the project config from the server.
301-
static Future<ProjectConfig> _fetchProjectConfig(
302-
final CommandLogger logger,
303-
final Client cloudApiClient,
304-
final String projectId,
305-
) async {
306-
try {
307-
return await cloudApiClient.projects.fetchProjectConfig(
308-
cloudProjectId: projectId,
309-
);
310-
} on Exception catch (e, s) {
311-
throw FailureException.nested(e, s, 'Failed to fetch the project config');
312-
}
313-
}
314-
315294
static void _writeProjectFiles(
316295
final CommandLogger logger,
317-
final ProjectConfig projectConfig,
296+
final ScloudConfig config,
318297
final String projectDirectory,
319298
final String configFilePath,
320299
) {
@@ -324,10 +303,10 @@ abstract class ProjectCommands {
324303
);
325304

326305
try {
327-
ScloudConfigFile.writeToFile(projectConfig, configFilePath);
306+
ScloudConfigIO.writeToFile(config, configFilePath);
328307
final relativePath = p.relative(configFilePath);
329308
logger.debug(
330-
"Wrote the '$relativePath' configuration file for '${projectConfig.projectId}'.",
309+
"Wrote the '$relativePath' configuration file for '${config.projectId}'.",
331310
);
332311
} on Exception catch (e, s) {
333312
throw FailureException.nested(
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export 'file_finder.dart';
22
export 'scloud_config_broker.dart';
3-
export 'scloud_config_file.dart';
3+
export 'scloud_config_io.dart';
4+
export 'scloud_config_model.dart';

0 commit comments

Comments
 (0)