deploy static method

Future<void> deploy(
  1. Client cloudApiClient,
  2. FileUploaderFactory fileUploaderFactory, {
  3. required CommandLogger logger,
  4. required String projectId,
  5. required String projectDir,
  6. required int concurrency,
  7. required bool dryRun,
})

Implementation

static Future<void> deploy(
  final Client cloudApiClient,
  final FileUploaderFactory fileUploaderFactory, {
  required final CommandLogger logger,
  required final String projectId,
  required final String projectDir,
  required final int concurrency,
  required final bool dryRun,
}) async {
  logger.init('Deploying Serverpod Cloud project "$projectId".');

  final projectDirectory = Directory(projectDir);

  final pubspecValidator = TenantProjectPubspec.fromProjectDir(
    projectDirectory,
  );

  final issues = pubspecValidator.projectDependencyIssues();
  if (issues.isNotEmpty) {
    throw FailureException(errors: issues);
  }

  final Directory rootDirectory;
  final Iterable<String> includedSubPaths;
  if (pubspecValidator.isWorkspaceResolved()) {
    (rootDirectory, includedSubPaths) =
        WorkspaceProject.prepareWorkspacePaths(projectDirectory);

    logger.list(
      title: 'Including workspace packages',
      includedSubPaths.where(
        (final path) => path != ScloudIgnore.scloudDirName,
      ),
      level: LogLevel.debug,
    );
  } else {
    rootDirectory = projectDirectory;
    includedSubPaths = const ['.'];
  }

  late final List<int> projectZip;
  final isZipped = await logger.progress(
    'Zipping project...',
    newParagraph: true,
    () async {
      try {
        projectZip = await ProjectZipper.zipProject(
          logger: logger,
          rootDirectory: rootDirectory,
          beneath: includedSubPaths,
          fileReadPoolSize: concurrency,
        );
        return true;
      } on ProjectZipperExceptions catch (e) {
        switch (e) {
          case ProjectDirectoryDoesNotExistException():
            logger.error(
              'Project directory does not exist: ${e.path}',
            );
            break;
          case EmptyProjectException():
            logger.error(
              'No files to upload.',
              hint:
                  'Ensure that the correct project directory is selected (either through the --project-dir flag or the current directory) and check '
                  'that `.gitignore` and `.scloudignore` does not filter out all project files.',
            );
            break;
          case DirectorySymLinkException():
            logger.error(
              'Serverpod Cloud does not support directory symlinks: `${e.path}`',
            );
            break;
          case NonResolvingSymlinkException():
            logger.error(
              'Serverpod Cloud does not support non-resolving symlinks: `${e.path}` => `${e.target}`',
            );
            break;
          case NullZipException():
            logger.error(
              'Unknown error occurred while zipping project, please try again.',
            );
            break;
        }
        return false;
      }
    },
  );

  if (!isZipped) throw ErrorExitException('Failed to zip project.');

  if (dryRun) {
    logger.info('Dry run, skipping upload.');
    return;
  }

  final success = await logger.progress('Uploading project...', () async {
    late final String uploadDescription;
    try {
      uploadDescription = await cloudApiClient.deploy.createUploadDescription(
        projectId,
      );
    } on Exception catch (e, stackTrace) {
      throw FailureException.nested(
          e, stackTrace, 'Failed to fetch upload description');
    }

    try {
      final fileUploader = fileUploaderFactory(uploadDescription);
      final ret = await fileUploader.upload(
        Stream.fromIterable([projectZip]),
        projectZip.length,
      );
      if (!ret) {
        logger.error('Failed to upload project, please try again.');
      }
      return ret;
    } on DioException catch (e) {
      throw FailureException(
        error: 'Failed to upload project: ${_uploadDioExceptionFormatter(e)}',
      );
    } on Exception catch (e, stackTrace) {
      throw FailureException.nested(
          e, stackTrace, 'Failed to upload project');
    }
  });

  if (!success) {
    throw ErrorExitException('Failed to upload project.');
  }

  const tenantHost = 'serverpod.space';

  logger.success(
    'Project uploaded successfully!',
    trailingRocket: true,
    newParagraph: true,
    followUp: 'When the server has started, you can access it at:\n'
        'Web:      https://$projectId.$tenantHost/\n'
        'API:      https://$projectId.api.$tenantHost/\n'
        'Insights: https://$projectId.insights.$tenantHost/',
  );
  logger.terminalCommand(
    message: 'Set up your custom domain by running:',
    'scloud domain',
    newParagraph: true,
  );
}