Skip to content

Commit

Permalink
feat(updater_tools): add diff command (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanoltman authored May 17, 2024
1 parent 80937e6 commit 6c3aa53
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 35 deletions.
93 changes: 93 additions & 0 deletions updater_tools/lib/src/commands/diff_command.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'dart:io';

import 'package:mason_logger/mason_logger.dart';
import 'package:updater_tools/src/commands/updater_tool_command.dart';
import 'package:updater_tools/src/extensions/arg_results.dart';
import 'package:updater_tools/src/logger.dart';
import 'package:updater_tools/src/packager/patch_packager.dart';

/// The arg name to specify the path to the release binary.
const releaseCliArg = 'release';

/// The arg name to specify the path to the patch binary.
const patchCliArg = 'patch';

/// The arg name to specify the path to the patch executable.
const patchExecutableCliArg = 'patch-executable';

/// The arg name to specify the output file.
const outputCliArg = 'output';

/// {@template diff_command}
/// A wrapper around the patch executable
/// {@endtemplate}
class DiffCommand extends UpdaterToolCommand {
/// {@macro diff_command}
DiffCommand([MakePatchPackager? makePatchPackager])
: _makePatchPackagerOverride = makePatchPackager,
super() {
argParser
..addOption(
releaseCliArg,
abbr: 'r',
mandatory: true,
help: 'The path to the release artifact which will be patched',
)
..addOption(
patchCliArg,
abbr: 'p',
mandatory: true,
help: 'The path to the patch artifact which will be packaged',
)
..addOption(
patchExecutableCliArg,
mandatory: true,
help:
'''The path to the patch executable that creates a binary diff between two files''',
)
..addOption(
outputCliArg,
abbr: 'o',
mandatory: true,
help: '''
Where to write the packaged patch archives.
This should be a directory, and will contain patch archives for each architecture.''',
);
}

final MakePatchPackager? _makePatchPackagerOverride;

@override
String get name => 'diff';

@override
String get description =>
'''Outputs a binary diff of the provided release and patch files, using release as a base.''';

@override
Future<int> run() async {
final File releaseFile;
final File patchFile;
final File patchExecutable;
try {
releaseFile = results.asExistingFile(releaseCliArg);
patchFile = results.asExistingFile(patchCliArg);
patchExecutable = results.asExistingFile(patchExecutableCliArg);
} catch (e) {
logger.err('$e');
return ExitCode.usage.code;
}

final patchPackager = (_makePatchPackagerOverride ?? PatchPackager.new)(
patchExecutable: patchExecutable,
);
await patchPackager.makeDiff(
base: releaseFile,
patch: patchFile,
outFile: File(results[outputCliArg] as String),
);

return ExitCode.success.code;
}
}
39 changes: 7 additions & 32 deletions updater_tools/lib/src/commands/package_patch_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';
import 'package:mason_logger/mason_logger.dart';
import 'package:updater_tools/src/artifact_type.dart';
import 'package:updater_tools/src/commands/updater_tool_command.dart';
import 'package:updater_tools/src/extensions/arg_results.dart';
import 'package:updater_tools/src/logger.dart';
import 'package:updater_tools/src/packager/patch_packager.dart';

Expand All @@ -21,11 +22,6 @@ const patchExecutableCliArg = 'patch-executable';
/// The arg name to specify the output directory.
const outputCliArg = 'output';

/// Function signature for the [PatchPackager] constructor.
typedef MakePatchPackager = PatchPackager Function({
required File patchExecutable,
});

/// {@template package_patch_command}
/// A command to package patch artifacts.
/// {@endtemplate}
Expand Down Expand Up @@ -81,16 +77,18 @@ This should be a directory, and will contain patch archives for each architectur

@override
Future<int> run() async {
final releaseFile = File(results[releaseCliArg] as String);
final patchFile = File(results[patchCliArg] as String);
final patchExecutable = File(results[patchExecutableCliArg] as String);
final outputDirectory = Directory(results[outputCliArg] as String);
final archiveType = ArchiveType.values.byName(
results[archiveTypeCliArg] as String,
);

final File releaseFile;
final File patchFile;
final File patchExecutable;
try {
_assertCliArgsValid();
releaseFile = results.asExistingFile(releaseCliArg);
patchFile = results.asExistingFile(patchCliArg);
patchExecutable = results.asExistingFile(patchExecutableCliArg);
} catch (e) {
logger.err('$e');
return ExitCode.usage.code;
Expand All @@ -115,27 +113,4 @@ This should be a directory, and will contain patch archives for each architectur

return ExitCode.success.code;
}

/// Verifies that CLI arguments point to existing files. Throws an
/// [ArgumentError] if any of the args are not valid.
void _assertCliArgsValid() {
final releaseFilePath = results[releaseCliArg] as String;
final patchFilePath = results[patchCliArg] as String;
final patchExecutablePath = results[patchExecutableCliArg] as String;

_verifyFileExists(releaseFilePath, releaseCliArg);
_verifyFileExists(patchFilePath, patchCliArg);
_verifyFileExists(patchExecutablePath, patchExecutableCliArg);
}

/// Throws an [ArgumentError] if a file at [path] does not exist.
void _verifyFileExists(String path, String name) {
if (!File(path).existsSync()) {
throw ArgumentError.value(
path,
name,
'The $name file does not exist',
);
}
}
}
19 changes: 19 additions & 0 deletions updater_tools/lib/src/extensions/arg_results.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'dart:io';

import 'package:args/args.dart';

/// Extension methods for validating options provided to [ArgResults].
extension ArgResultsValidation on ArgResults {
File asExistingFile(String name) {
final file = File(this[name] as String);
if (!file.existsSync()) {
throw ArgumentError.value(
file.path,
name,
'The $name file does not exist',
);
}

return file;
}
}
9 changes: 7 additions & 2 deletions updater_tools/lib/src/packager/patch_packager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class PackagingException implements Exception {
String toString() => 'PackagingException: $message';
}

/// Function signature for the [PatchPackager] constructor.
typedef MakePatchPackager = PatchPackager Function({
required File patchExecutable,
});

/// {@template patch_packager}
/// Creates and packages patch artifacts.
/// {@endtemplate }
Expand Down Expand Up @@ -120,7 +125,7 @@ class PatchPackager {
final diffArchDir = Directory(p.join(outDir.path, archName))
..createSync(recursive: true);
final diffFile = File(p.join(diffArchDir.path, 'dlc.vmcode'));
await _makeDiff(
await makeDiff(
base: releaseElf,
patch: patchElf,
outFile: diffFile,
Expand Down Expand Up @@ -149,7 +154,7 @@ class PatchPackager {

/// Create a binary diff between [base] and [patch]. Returns the path to the
/// diff file.
Future<void> _makeDiff({
Future<void> makeDiff({
required File base,
required File patch,
required File outFile,
Expand Down
3 changes: 2 additions & 1 deletion updater_tools/lib/src/updater_tools_command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:updater_tools/src/commands/commands.dart';
import 'package:updater_tools/src/commands/diff_command.dart';
import 'package:updater_tools/src/logger.dart';
import 'package:updater_tools/version.dart';

Expand Down Expand Up @@ -30,7 +31,7 @@ class UpdaterToolsCommandRunner extends CommandRunner<int> {
help: 'Noisy logging, including all shell commands executed.',
);

// Add sub commands
addCommand(DiffCommand());
addCommand(PackagePatchCommand());
}

Expand Down
Loading

0 comments on commit 6c3aa53

Please sign in to comment.