runFlashDsl function

Future<DslRunResult> runFlashDsl(
  1. void builder(
    1. App app
    ), {
  2. String? apiKey,
  3. String? baseUrl,
  4. String? projectName,
  5. String? projectId,
  6. bool findOrCreate = false,
  7. bool allowNewProject = false,
  8. bool dryRun = false,
  9. String? commitMessage,
  10. PipelineSource? source,
  11. bool validationFilter(
    1. ProjectError error
    )?,
  12. bool refreshContext = false,
  13. String? contextDir,
  14. void logger(
    1. String message
    )?,
  15. @visibleForTesting Flash? sdk,
  16. @visibleForTesting Future<List<ProjectError>> validateOverride(
    1. FFProject
    )?,
  17. @visibleForTesting Future<GeneratedCodeSnapshotExportResult> generatedCodeSnapshotExporter(
    1. String workspacePath, {
    2. required String? apiKey,
    3. required String? baseUrl,
    4. SnapshotLogger? infoLogger,
    5. required String projectId,
    6. SnapshotLogger? warningLogger,
    })?,
})

Builds, compiles, validates, and pushes a DSL app to FlutterFlow.

Resolution order:

  • apiKey, baseUrl, projectName, projectId, commitMessage
  • current-directory .env
  • process environment
  • inferred project name from the DSL app

Implementation

Future<DslRunResult> runFlashDsl(
  void Function(App app) builder, {
  String? apiKey,
  String? baseUrl,
  String? projectName,
  String? projectId,
  bool findOrCreate = false,
  bool allowNewProject = false,
  bool dryRun = false,
  String? commitMessage,
  PipelineSource? source,
  bool Function(ProjectError error)? validationFilter,
  bool refreshContext = false,
  String? contextDir,
  void Function(String message)? logger,
  @visibleForTesting Flash? sdk,
  @visibleForTesting
  Future<List<ProjectError>> Function(FFProject)? validateOverride,
  @visibleForTesting
  Future<GeneratedCodeSnapshotExportResult> Function(
    String workspacePath, {
    required String projectId,
    required String? apiKey,
    required String? baseUrl,
    SnapshotLogger? infoLogger,
    SnapshotLogger? warningLogger,
  })?
  generatedCodeSnapshotExporter,
}) async {
  final log = logger ?? _defaultLogger;
  final app = App();
  builder(app);

  final env = loadMergedEnv();
  // Use validating resolvers so blank explicit values throw ConfigException
  // instead of being silently treated as missing.
  final resolvedApiKey = resolveApiKey(flagValue: apiKey, env: env);
  final resolvedBaseUrl = resolveBaseUrl(flagValue: baseUrl, env: env);
  final resolvedProjectId = projectId ?? env['FF_DSL_PROJECT_ID'];
  final resolvedProjectName =
      projectName ?? env['FF_DSL_PROJECT_NAME'] ?? _inferProjectName(app);
  final resolvedFindOrCreate =
      findOrCreate || _parseBool(env['FF_DSL_FIND_OR_CREATE']);
  final resolvedAllowNewProject =
      allowNewProject || _parseBool(env['FF_DSL_ALLOW_NEW_PROJECT']);
  final resolvedDryRun = dryRun || _parseBool(env['FF_DSL_DRY_RUN']);
  final resolvedCommitMessage =
      commitMessage ??
      env['FF_DSL_COMMIT_MESSAGE'] ??
      'Compile $resolvedProjectName from Flash DSL';
  final resolvedSource = source ?? _defaultSource();

  if (sdk == null && (resolvedApiKey == null || resolvedApiKey.isEmpty)) {
    throw ConfigException(
      'FF_API_KEY is not set. Pass apiKey: or set FF_API_KEY in the '
      'environment or .env file.',
    );
  }
  final compileTask = _CompileDslTask(app);
  final workspaceState = readWorkspaceState(directory: contextDir);
  final wasUnboundWorkspace = workspaceState?.projectId == null;
  if (workspaceState?.projectId case final workspaceProjectId?) {
    log('Workspace binding: $workspaceProjectId.');
  }

  final pipelineProject = _resolveProjectTarget(
    projectId: resolvedProjectId,
    projectName: resolvedProjectName,
    findOrCreate: resolvedFindOrCreate,
  );

  log(
    'Resolved target mode: ${_projectTargetMode(pipelineProject)} '
    '(${_describeProjectTarget(pipelineProject)}).',
  );
  final pipelineResult =
      await FlashPipeline(
        apiKey: resolvedApiKey,
        baseUrl: resolvedBaseUrl,
        project: pipelineProject,
        tasks: [compileTask],
        source: resolvedSource,
        commitMessage: resolvedCommitMessage,
        dryRun: resolvedDryRun,
        allowNewProject: resolvedAllowNewProject,
        validationFilter: validationFilter,
        refreshContext: refreshContext,
        contextDir: contextDir,
        // ignore: invalid_use_of_visible_for_testing_member
        sdk: sdk,
        // ignore: invalid_use_of_visible_for_testing_member
        validateOverride: validateOverride,
      ).run();

  for (final task in pipelineResult.taskResults) {
    log(task.summary);
  }

  if (!pipelineResult.success ||
      (!pipelineResult.dryRun && !pipelineResult.pushed)) {
    throw DslRunException(pipelineResult);
  }

  final compileResult = compileTask.compileResult;
  if (compileResult == null) {
    throw StateError(
      'Flash DSL pipeline succeeded without producing a compile result.',
    );
  }

  final result = DslRunResult(
    app: app,
    compileResult: compileResult,
    projectId: pipelineResult.projectId,
    commitId: pipelineResult.commitId,
    pushed: pipelineResult.pushed,
    dryRun: pipelineResult.dryRun,
    taskSummaries: List.unmodifiable(
      pipelineResult.taskResults.map((task) => task.summary),
    ),
  );

  if (!result.dryRun && result.pushed && wasUnboundWorkspace) {
    final exportResult = await (generatedCodeSnapshotExporter ??
        maybeExportGeneratedCodeSnapshot)(
      contextDir ?? Directory.current.path,
      projectId: result.projectId,
      apiKey: resolvedApiKey,
      baseUrl: resolvedBaseUrl,
      infoLogger: log,
      warningLogger: log,
    );
    if (!exportResult.success) {
      markGeneratedCodeStale(
        projectId: result.projectId,
        changeSummary: pipelineResult.changeSummary,
        commitId: result.commitId,
        directory: contextDir,
      );
    }
  } else if (!result.dryRun && result.pushed) {
    markGeneratedCodeStale(
      projectId: result.projectId,
      changeSummary: pipelineResult.changeSummary,
      commitId: result.commitId,
      directory: contextDir,
    );
  }

  log(
    result.dryRun
        ? 'Dry run succeeded for project ${result.projectId}.'
        : 'Pushed project ${result.projectId}'
            '${result.commitId == null ? '' : ' (commit ${result.commitId})'}.',
  );
  return result;
}