mirror of
https://github.com/toyota-connected/emb_cli.git
synced 2026-06-21 07:19:37 -07:00
Merge pull request #30 from toyota-connected/feat/flutter-env-setup
fix(env): make setup_env.sh follow the -w workspace
This commit is contained in:
@@ -215,17 +215,20 @@ emb sync --config ../configs -j 8
|
||||
|
||||
### `emb flutter`
|
||||
|
||||
Install the Flutter SDK into `<workspace>/flutter`.
|
||||
Install the Flutter SDK into `<workspace>/flutter`, then emit
|
||||
`<workspace>/setup_env.sh` so `FLUTTER_WORKSPACE` (and the SDK's Flutter/Dart on
|
||||
`PATH`) match the workspace just provisioned — i.e. the `-w` target.
|
||||
|
||||
| Option | Default | Description |
|
||||
|---|---|---|
|
||||
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
|
||||
| `-w`, `--workspace <dir>` | resolution order | Workspace root. Also becomes `FLUTTER_WORKSPACE` in the emitted `setup_env.sh`. |
|
||||
| `--flutter-version <ref>` | `globals.json` `flutter_version` | Version/tag/branch to check out. |
|
||||
| `-c`, `--config <dir>` | `configs` | Directory to read `globals.json` from. |
|
||||
| `--configure` | off | Run `flutter config` (desktop + custom devices) and `flutter doctor` after install. |
|
||||
|
||||
```sh
|
||||
emb flutter --flutter-version 3.44.2 --configure
|
||||
emb flutter -w /tmp/ws1 --flutter-version 3.44.2
|
||||
. /tmp/ws1/setup_env.sh # FLUTTER_WORKSPACE=/tmp/ws1
|
||||
```
|
||||
|
||||
---
|
||||
@@ -462,17 +465,25 @@ emb cross . --target rpi5 --build
|
||||
|
||||
### `emb env`
|
||||
|
||||
Write `setup_env.sh` (`PATH` for Flutter/Dart, `FLUTTER_WORKSPACE`, `PUB_CACHE`,
|
||||
`XDG_CONFIG_HOME`, the engine version, …).
|
||||
(Re)generate or print `setup_env.sh` (`PATH` for Flutter/Dart,
|
||||
`FLUTTER_WORKSPACE`, `PUB_CACHE`, `XDG_CONFIG_HOME`, the engine version, …).
|
||||
|
||||
`emb setup` and `emb flutter` already emit this file, so the standalone command
|
||||
is for what they don't cover: regenerating after the workspace has **moved**
|
||||
(the baked-in `FLUTTER_WORKSPACE` is absolute), previewing the env on stdout, or
|
||||
writing it to a custom path. It has no side effects beyond the one file — it
|
||||
never clones or installs, and warns if `<workspace>/flutter` is absent (the env
|
||||
would point at a missing SDK; run `emb flutter -w <root>` first).
|
||||
|
||||
| Option | Default | Description |
|
||||
|---|---|---|
|
||||
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
|
||||
| `-w`, `--workspace <dir>` | resolution order | Workspace root (becomes `FLUTTER_WORKSPACE`). |
|
||||
| `-o`, `--output <path>` | `<workspace>/setup_env.sh` | Output file path. |
|
||||
| `--print` | off | Print to stdout instead of writing a file. |
|
||||
|
||||
```sh
|
||||
emb env # write <workspace>/setup_env.sh
|
||||
emb env # (re)write <workspace>/setup_env.sh
|
||||
emb env -w /tmp/ws1 # regenerate after moving the workspace
|
||||
emb env --print # preview on stdout
|
||||
```
|
||||
|
||||
|
||||
@@ -8,8 +8,15 @@ import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
/// {@template env_command}
|
||||
/// `emb env` — write `setup_env.sh` for the workspace (Flutter/Dart on PATH,
|
||||
/// FLUTTER_WORKSPACE, PUB_CACHE, …). Source it with `. ./setup_env.sh`.
|
||||
/// `emb env` — (re)generate or print `setup_env.sh` for a workspace
|
||||
/// (Flutter/Dart on PATH, FLUTTER_WORKSPACE, PUB_CACHE, …). Source it with
|
||||
/// `. ./setup_env.sh`.
|
||||
///
|
||||
/// `emb setup` and `emb flutter` already emit this file as part of their work,
|
||||
/// so the standalone command is for the cases they don't cover: regenerating
|
||||
/// after the workspace has moved (the baked-in FLUTTER_WORKSPACE is absolute),
|
||||
/// printing the env to stdout (`--print`), or writing it to a custom path. It
|
||||
/// has no side effects beyond the one file — it never clones or installs.
|
||||
/// {@endtemplate}
|
||||
class EnvCommand extends Command<int> {
|
||||
/// {@macro env_command}
|
||||
@@ -39,7 +46,7 @@ class EnvCommand extends Command<int> {
|
||||
|
||||
@override
|
||||
String get description =>
|
||||
'Write setup_env.sh (PATH, FLUTTER_WORKSPACE, PUB_CACHE, …).';
|
||||
'(Re)generate or print setup_env.sh (PATH, FLUTTER_WORKSPACE, …).';
|
||||
|
||||
@override
|
||||
String get name => 'env';
|
||||
@@ -50,6 +57,16 @@ class EnvCommand extends Command<int> {
|
||||
final host = _host ?? HostInfo.detect();
|
||||
final workspace = Workspace.resolve(override: args['workspace'] as String?);
|
||||
|
||||
// The env points PATH at <workspace>/flutter; warn if no SDK is there yet
|
||||
// (e.g. `emb env` run in a bare dir), since the script will reference a
|
||||
// not-yet-present SDK. Install it with `emb flutter -w <root>`.
|
||||
if (!workspace.flutterDir.existsSync()) {
|
||||
_logger.warn(
|
||||
'No Flutter SDK at ${workspace.flutterDir.path} — the env will point '
|
||||
'at a missing SDK. Run: emb flutter -w ${workspace.root.path}',
|
||||
);
|
||||
}
|
||||
|
||||
final script = generateSetupEnv(
|
||||
workspace: workspace,
|
||||
host: host,
|
||||
@@ -64,7 +81,11 @@ class EnvCommand extends Command<int> {
|
||||
final out =
|
||||
(args['output'] as String?) ??
|
||||
p.join(workspace.root.path, 'setup_env.sh');
|
||||
File(out).writeAsStringSync(script);
|
||||
// Create the target dir first: `emb env -w <new path>` is a normal way to
|
||||
// seed env for a workspace that doesn't exist on disk yet.
|
||||
File(out)
|
||||
..parent.createSync(recursive: true)
|
||||
..writeAsStringSync(script);
|
||||
_logger
|
||||
..info('Wrote $out')
|
||||
..info('Source it: . ${p.relative(out)}');
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:emb_cli/src/env/env_script.dart';
|
||||
import 'package:emb_cli/src/flutter/flutter_sdk.dart';
|
||||
import 'package:emb_cli/src/host/host_info.dart';
|
||||
import 'package:emb_cli/src/workspace/workspace.dart';
|
||||
@@ -103,6 +104,19 @@ class FlutterCommand extends Command<int> {
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit setup_env.sh into the resolved workspace so FLUTTER_WORKSPACE (and
|
||||
// the SDK's Flutter/Dart on PATH) match where the SDK was just installed —
|
||||
// i.e. the -w target, not the cwd a later `emb env` would default to.
|
||||
final envFile = File(p.join(workspace.root.path, 'setup_env.sh'))
|
||||
..writeAsStringSync(
|
||||
generateSetupEnv(
|
||||
workspace: workspace,
|
||||
host: host,
|
||||
engineVersion: result.engineCommit,
|
||||
),
|
||||
);
|
||||
_logger.info('Source the env: . ${p.relative(envFile.path)}');
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:emb_cli/src/commands/env_command.dart';
|
||||
import 'package:emb_cli/src/host/host_info.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const _host = HostInfo(
|
||||
os: HostOs.linux,
|
||||
machineArch: 'x86_64',
|
||||
archAliases: {'x86_64', 'x64', 'amd64'},
|
||||
hostType: 'fedora',
|
||||
versionId: '43',
|
||||
);
|
||||
|
||||
void main() {
|
||||
late Directory tmp;
|
||||
setUp(() => tmp = Directory.systemTemp.createTempSync('emb_envcmd_'));
|
||||
tearDown(() => tmp.deleteSync(recursive: true));
|
||||
|
||||
Future<int?> run(List<String> args) {
|
||||
final runner = CommandRunner<int>('emb', 'test')
|
||||
..addCommand(EnvCommand(logger: Logger(), host: _host));
|
||||
return runner.run(args);
|
||||
}
|
||||
|
||||
test('writes setup_env.sh with FLUTTER_WORKSPACE from -w', () async {
|
||||
final ws = p.join(tmp.path, 'ws1');
|
||||
final code = await run(['env', '-w', ws]);
|
||||
expect(code, ExitCode.success.code);
|
||||
|
||||
final text = File(p.join(ws, 'setup_env.sh')).readAsStringSync();
|
||||
expect(text, contains('export FLUTTER_WORKSPACE="$ws"'));
|
||||
});
|
||||
|
||||
test('succeeds (warns, does not fail) when the SDK is absent', () async {
|
||||
// <workspace>/flutter does not exist — the command warns but still writes.
|
||||
final code = await run(['env', '-w', tmp.path]);
|
||||
expect(code, ExitCode.success.code);
|
||||
expect(File(p.join(tmp.path, 'setup_env.sh')).existsSync(), isTrue);
|
||||
});
|
||||
|
||||
test('--print does not write a file', () async {
|
||||
final code = await run(['env', '-w', tmp.path, '--print']);
|
||||
expect(code, ExitCode.success.code);
|
||||
expect(File(p.join(tmp.path, 'setup_env.sh')).existsSync(), isFalse);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:emb_cli/src/commands/flutter_command.dart';
|
||||
import 'package:emb_cli/src/flutter/flutter_sdk.dart';
|
||||
import 'package:emb_cli/src/host/host_info.dart';
|
||||
import 'package:emb_cli/src/repo/git_repo.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const _host = HostInfo(
|
||||
os: HostOs.linux,
|
||||
machineArch: 'x86_64',
|
||||
archAliases: {'x86_64', 'x64', 'amd64'},
|
||||
hostType: 'fedora',
|
||||
versionId: '43',
|
||||
);
|
||||
|
||||
/// Simulates a successful Flutter SDK install without touching git: creates the
|
||||
/// SDK dir (and thus the workspace root) and reports an engine commit.
|
||||
class _FakeSdk extends FlutterSdk {
|
||||
_FakeSdk(super.workspace);
|
||||
|
||||
@override
|
||||
Future<FlutterInstallResult> install(
|
||||
String version, {
|
||||
GitRunner runner = defaultGitRunner,
|
||||
}) async {
|
||||
workspace.flutterDir.createSync(recursive: true);
|
||||
return FlutterInstallResult(
|
||||
success: true,
|
||||
path: workspace.flutterDir.path,
|
||||
engineCommit: 'deadbeefcafe',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
late Directory tmp;
|
||||
setUp(() => tmp = Directory.systemTemp.createTempSync('emb_flcmd_'));
|
||||
tearDown(() => tmp.deleteSync(recursive: true));
|
||||
|
||||
Future<int?> run(List<String> args) {
|
||||
final runner = CommandRunner<int>('emb', 'test')
|
||||
..addCommand(
|
||||
FlutterCommand(
|
||||
logger: Logger(),
|
||||
host: _host,
|
||||
sdkFactory: (ws, host) => _FakeSdk(ws),
|
||||
),
|
||||
);
|
||||
return runner.run(args);
|
||||
}
|
||||
|
||||
test('writes setup_env.sh with FLUTTER_WORKSPACE set to -w', () async {
|
||||
final ws = p.join(tmp.path, 'ws1');
|
||||
final code = await run([
|
||||
'flutter',
|
||||
'-w',
|
||||
ws,
|
||||
'--flutter-version',
|
||||
'3.44.2',
|
||||
]);
|
||||
expect(code, ExitCode.success.code);
|
||||
|
||||
final env = File(p.join(ws, 'setup_env.sh'));
|
||||
expect(env.existsSync(), isTrue);
|
||||
final text = env.readAsStringSync();
|
||||
// The -w target is the workspace, not the cwd a later bare `emb env` uses.
|
||||
expect(text, contains('export FLUTTER_WORKSPACE="$ws"'));
|
||||
// The engine commit read during install is carried into the env script.
|
||||
expect(text, contains('FLUTTER_ENGINE_VERSION="deadbeefcafe"'));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user