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
1 change: 1 addition & 0 deletions lib/src/cli/commands/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class BuildCommand extends FlutterpiCommand {
usesTargetOption();
usesLocalFlutterpiExecutableArg(verboseHelp: verboseHelp);
usesFilesystemLayoutArg(verboseHelp: verboseHelp);
usesGithubArtifactsOptions(verboseHelp: verboseHelp);

argParser
..addSeparator('Target options')
Expand Down
34 changes: 34 additions & 0 deletions lib/src/cli/flutterpi_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,40 @@ mixin FlutterpiCommandMixin on fl.FlutterCommand {
);
}

void usesGithubArtifactsOptions({bool verboseHelp = false}) {
argParser.addOption(
'github-artifacts-repo',
help:
'Use a custom GitHub repository for engine artifacts instead of ardera/flutter-ci.',
valueHelp: 'owner/repo',
hide: !verboseHelp,
);

argParser.addOption(
'github-artifacts-runid',
help:
'Download engine artifacts from a specific GitHub Actions workflow run instead of releases.',
valueHelp: 'run-id',
hide: !verboseHelp,
);

argParser.addOption(
'github-artifacts-engine-version',
help:
'Specify the engine version available in the workflow run artifacts.',
valueHelp: 'engine-hash',
hide: !verboseHelp,
);

argParser.addOption(
'github-artifacts-auth-token',
help:
'GitHub authentication token for accessing artifacts. Can also be set via GITHUB_TOKEN environment variable.',
valueHelp: 'token',
hide: !verboseHelp,
);
}

bool getIncludeDebugSymbols() {
return boolArg('debug-symbols');
}
Expand Down
49 changes: 42 additions & 7 deletions lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,68 @@ import 'package:flutterpi_tool/src/fltool/common.dart' as fl;
import 'package:flutterpi_tool/src/fltool/globals.dart' as globals;
import 'package:flutterpi_tool/src/github.dart';
import 'package:flutterpi_tool/src/more_os_utils.dart';
import 'package:github/github.dart' as gh;

// ignore: implementation_imports
import 'package:flutter_tools/src/context_runner.dart' as fl;

Future<V> runInContext<V>(
FutureOr<V> Function() fn, {
bool verbose = false,
String? githubArtifactsRepo,
String? githubArtifactsRunId,
String? githubArtifactsEngineVersion,
String? githubArtifactsAuthToken,
}) async {
return await fl.runInContext(
fn,
overrides: {
Analytics: () => const NoOpAnalytics(),
fl.TemplateRenderer: () => const fl.MustacheTemplateRenderer(),
fl.Cache: () => FlutterpiCache(
fl.Cache: () {
// Check environment variable for token if not provided
final token = githubArtifactsAuthToken ??
globals.platform.environment['GITHUB_TOKEN'];

final github = MyGithub.caching(
httpClient: http.IOClient(
globals.httpClientFactory?.call() ?? io.HttpClient(),
),
auth: token != null ? gh.Authentication.bearerToken(token) : null,
);

if (githubArtifactsRunId != null) {
return FlutterpiCache.fromWorkflow(
hooks: globals.shutdownHooks,
logger: globals.logger,
fileSystem: globals.fs,
platform: globals.platform,
osUtils: globals.os as MoreOperatingSystemUtils,
projectFactory: globals.projectFactory,
processManager: globals.processManager,
github: MyGithub.caching(
httpClient: http.IOClient(
globals.httpClientFactory?.call() ?? io.HttpClient(),
),
),
),
repo: githubArtifactsRepo != null
? gh.RepositorySlug.full(githubArtifactsRepo)
: null,
runId: githubArtifactsRunId,
availableEngineVersion: githubArtifactsEngineVersion,
github: github,
);
} else {
return FlutterpiCache(
hooks: globals.shutdownHooks,
logger: globals.logger,
fileSystem: globals.fs,
platform: globals.platform,
osUtils: globals.os as MoreOperatingSystemUtils,
projectFactory: globals.projectFactory,
processManager: globals.processManager,
repo: githubArtifactsRepo != null
? gh.RepositorySlug.full(githubArtifactsRepo)
: null,
github: github,
);
}
},
fl.OperatingSystemUtils: () => MoreOperatingSystemUtils(
fileSystem: globals.fs,
logger: globals.logger,
Expand Down
26 changes: 26 additions & 0 deletions lib/src/executable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ FlutterpiToolCommandRunner createFlutterpiCommandRunner({
return runner;
}

// Helper to extract option value from args
String? _extractOption(List<String> args, String optionName) {
for (var i = 0; i < args.length; i++) {
final arg = args[i];
if (arg == '--$optionName' && i + 1 < args.length) {
return args[i + 1];
}
if (arg.startsWith('--$optionName=')) {
return arg.substring('--$optionName='.length);
}
}
return null;
}

Comment on lines +39 to +52
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does the job, but it's a bit hacky

command parsing should be done inside the command, with the argParser. Two reasons:

  1. All the tests are done by calling FlutterpiCommandRunner with fake objects in the context. Since the logic here is outside of commands and the command runner, this is hard to test as it is right now.
  2. Harder to get the parsing wrong. (For example, your parser doesn't handle a user passing -- to terminate options, or a user passing --my-option-with-following-value --github-workflow-artifacts... (not that the latter would ever happen))

Maybe it's better to just add settable fields to the FlutterpiCache:
specifiedRepo, specifiedWorkflowRunId, specifiedAuthToken

And if e.g. workflow run id is set, source from workflow instead of releases.

That's a bit similar to how it's done for the device discovery.

IMO it's fine to leave away the auth token cmdline arg and just allow setting it via GH_TOKEN env var, if that makes implementation easier. And no need to differentiate here whether it's workflow / releases cache, GH_TOKEN is potentially useful for both.

Future<void> main(List<String> args) async {
final verbose =
args.contains('-v') || args.contains('--verbose') || args.contains('-vv');
Expand All @@ -50,6 +64,14 @@ Future<void> main(List<String> args) async {
(args.length == 1 && verbose);
final verboseHelp = help && verbose;

// Parse github-artifacts options early (before context is set up)
final githubArtifactsRepo = _extractOption(args, 'github-artifacts-repo');
final githubArtifactsRunId = _extractOption(args, 'github-artifacts-runid');
final githubArtifactsEngineVersion =
_extractOption(args, 'github-artifacts-engine-version');
final githubArtifactsAuthToken =
_extractOption(args, 'github-artifacts-auth-token');

final runner = createFlutterpiCommandRunner(verboseHelp: verboseHelp);

fltool.Cache.flutterRoot = await getFlutterRoot();
Expand Down Expand Up @@ -86,5 +108,9 @@ Future<void> main(List<String> args) async {
}
},
verbose: verbose,
githubArtifactsRepo: githubArtifactsRepo,
githubArtifactsRunId: githubArtifactsRunId,
githubArtifactsEngineVersion: githubArtifactsEngineVersion,
githubArtifactsAuthToken: githubArtifactsAuthToken,
);
}
Loading