Skip to content
Open
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
3 changes: 3 additions & 0 deletions puro/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## Unreleased

* Added the `prepare` command to pre-download Flutter artifacts for an environment
## 1.5.0

* Fixed several major bugs
Expand Down
2 changes: 2 additions & 0 deletions puro/lib/src/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'commands/internal_generate_ast_parser.dart';
import 'commands/internal_generate_docs.dart';
import 'commands/ls_versions.dart';
import 'commands/prefs.dart';
import 'commands/prepare.dart';
import 'commands/pub.dart';
import 'commands/puro_install.dart';
import 'commands/puro_uninstall.dart';
Expand Down Expand Up @@ -259,6 +260,7 @@ void main(List<String> args) async {
..addCommand(CleanCommand())
..addCommand(EnvRmCommand())
..addCommand(EnvRenameCommand())
..addCommand(PrepareCommand())
..addCommand(FlutterCommand())
..addCommand(DartCommand())
..addCommand(PubCommand())
Expand Down
84 changes: 84 additions & 0 deletions puro/lib/src/commands/prepare.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import '../command.dart';
import '../command_result.dart';
import '../env/default.dart';
import '../env/prepare.dart';
import '../logger.dart';

class PrepareCommand extends PuroCommand {
PrepareCommand() {
argParser
..addFlag(
'force',
help: 'Force re-downloading artifacts even if they already exist.',
negatable: false,
)
..addFlag(
'all-platforms',
help: 'Precache artifacts for every supported Flutter platform.',
negatable: false,
)
..addMultiOption(
'platform',
help:
'Precache artifacts for the provided platforms (android, ios, etc).',
valueHelp: 'name',
allowed: preparePlatformOptions.toList()..sort(),
);
}

@override
final name = 'prepare';

@override
final description =
'Pre-downloads Flutter artifacts for an environment so builds can start immediately.';

@override
String? get argumentUsage => '[env]';

@override
Future<CommandResult> run() async {
final log = PuroLogger.of(scope);
final envName = unwrapSingleOptionalArgument();
final environment = await getProjectEnvOrDefault(
scope: scope,
envName: envName,
);

final force = argResults!['force'] as bool;
final allPlatforms = argResults!['all-platforms'] as bool;
final requestedPlatforms = (argResults!['platform'] as List<String>).map(
(e) => e.toLowerCase(),
);
final sortedRequested = sortPreparePlatforms(requestedPlatforms);
final defaultPlatforms = defaultPreparePlatforms();

final platforms = sortedRequested.isNotEmpty
? sortedRequested
: (allPlatforms ? <String>[] : defaultPlatforms);

log.d(
'Preparing environment `${environment.name}` for platforms: '
'${allPlatforms ? 'all platforms' : (platforms.isEmpty ? 'default set' : platforms.join(', '))}'
'${force ? ' (force)' : ''}',
);

await prepareEnvironment(
scope: scope,
environment: environment,
platforms: platforms,
allPlatforms: allPlatforms,
force: force,
);

final platformSummary = allPlatforms
? 'all platforms'
: (platforms.isEmpty
? 'default platforms (${defaultPlatforms.join(', ')})'
: platforms.join(', '));

return BasicMessageResult(
'Prepared environment `${environment.name}` ($platformSummary${force ? ', forced' : ''})',
);
}
}
80 changes: 80 additions & 0 deletions puro/lib/src/env/prepare.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:io';

import '../command_result.dart';
import '../config.dart';
import '../provider.dart';
import 'command.dart';

const Set<String> preparePlatformOptions = {
'android',
'ios',
'linux',
'macos',
'windows',
'web',
'fuchsia',
};

const List<String> _platformOrder = [
'android',
'ios',
'macos',
'linux',
'windows',
'web',
'fuchsia',
];

List<String> sortPreparePlatforms(Iterable<String> platforms) {
final platformSet = platforms.toSet();
return _platformOrder.where(platformSet.contains).toList();
}

List<String> defaultPreparePlatforms() {
final platforms = <String>{'android', 'web'};
if (Platform.isMacOS) {
platforms
..add('ios')
..add('macos');
}
if (Platform.isLinux) {
platforms.add('linux');
}
if (Platform.isWindows) {
platforms.add('windows');
}
return sortPreparePlatforms(platforms);
}

Future<void> prepareEnvironment({
required Scope scope,
required EnvConfig environment,
List<String>? platforms,
bool allPlatforms = false,
bool force = false,
}) async {
final effectivePlatforms = platforms == null
? const <String>[]
: sortPreparePlatforms(platforms);
final args = <String>['precache'];
if (force) {
args.add('--force');
}
if (allPlatforms) {
args.add('--all-platforms');
}
for (final platform in effectivePlatforms) {
args.add('--$platform');
}
final exitCode = await runFlutterCommand(
scope: scope,
environment: environment,
args: args,
mode: ProcessStartMode.inheritStdio,
);
if (exitCode != 0) {
throw CommandError(
'`flutter ${args.join(' ')}` failed with exit code $exitCode',
);
}
}
15 changes: 15 additions & 0 deletions website/docs/reference/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,18 @@ We can manually delete unused caches with the `puro gc` command:
$> puro gc
[✓] Cleaned up caches and reclaimed 2.7GB
```

### Preparing artifacts

Use `puro prepare` to download Flutter artifacts ahead of time so the first build on a new
or freshly upgraded environment does not need to fetch them on demand:

```
$> puro prepare master
[✓] Prepared environment `master` (default platforms (android, ios, macos, web))
```

Add `--all-platforms` to cache everything Flutter supports, or pass `--platform` multiple
times (for example `--platform android --platform web`) to tailor the download to the
projects you build most often. Include `--force` if you want to refresh artifacts even
when they are already present.