Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanoltman committed May 20, 2024
2 parents df76151 + 6ab6972 commit 608d45e
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 22 deletions.
2 changes: 1 addition & 1 deletion shorebird_code_push/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies:
meta: ^1.9.1

dev_dependencies:
ffigen: ">=8.0.2 <12.0.0"
ffigen: ">=8.0.2 <13.0.0"
mocktail: ">=0.3.0 <2.0.0"
test: ^1.19.2
very_good_analysis: ^5.0.0
Expand Down
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;
}
}
5 changes: 0 additions & 5 deletions updater_tools/lib/src/commands/package_patch_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,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
35 changes: 20 additions & 15 deletions updater_tools/lib/src/packager/patch_packager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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 @@ -126,7 +131,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 @@ -170,19 +175,19 @@ class PatchPackager {
final patchAppDirectory =
_getIosAppDirectory(xcarchiveDirectory: patchDirectory);

final File patchBaseFile;
try {
// If the aot_tools executable supports the dump_blobs command, we
// can generate a stable diff base and use that to create a patch.
patchBaseFile = await aotTools.generatePatchDiffBase(
analyzeSnapshotPath: analyzeSnapshotPath,
releaseSnapshot: releaseArtifactFile,
);
patchBaseProgress.complete();
} catch (error) {
patchBaseProgress.fail('$error');
return exit(ExitCode.software.code);
}
// final File patchBaseFile;
// try {
// // If the aot_tools executable supports the dump_blobs command, we
// // can generate a stable diff base and use that to create a patch.
// patchBaseFile = await aotTools.generatePatchDiffBase(
// analyzeSnapshotPath: analyzeSnapshotPath,
// releaseSnapshot: releaseArtifactFile,
// );
// patchBaseProgress.complete();
// } catch (error) {
// patchBaseProgress.fail('$error');
// return exit(ExitCode.software.code);
// }

// final releaseArtifactFile = File(
// p.join(
Expand Down Expand Up @@ -212,7 +217,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
174 changes: 174 additions & 0 deletions updater_tools/test/src/commands/diff_command_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:scoped_deps/scoped_deps.dart';
import 'package:test/test.dart';
import 'package:updater_tools/src/commands/diff_command.dart';
import 'package:updater_tools/src/logger.dart';
import 'package:updater_tools/src/packager/patch_packager.dart';

import '../../matchers/matchers.dart';

class _MockArgResults extends Mock implements ArgResults {}

class _MockLogger extends Mock implements Logger {}

class _MockPatchPackager extends Mock implements PatchPackager {}

void main() {
group(DiffCommand, () {
late ArgResults argResults;
late Logger logger;
late PatchPackager patchPackager;
late DiffCommand command;

late File releaseFile;
late File patchFile;
late File patchExecutable;
late File outputFile;

R runWithOverrides<R>(R Function() body) {
return runScoped(
body,
values: {
loggerRef.overrideWith(() => logger),
},
);
}

setUpAll(() {
registerFallbackValue(Directory(''));
registerFallbackValue(File(''));
});

setUp(() {
argResults = _MockArgResults();
logger = _MockLogger();
patchPackager = _MockPatchPackager();

final tempDir = Directory.systemTemp.createTempSync();
releaseFile = File(p.join(tempDir.path, 'release'))
..createSync(recursive: true);
patchFile = File(p.join(tempDir.path, 'patch'))
..createSync(recursive: true);
patchExecutable = File(p.join(tempDir.path, 'patch.exe'))
..createSync(recursive: true);
outputFile = File(p.join(tempDir.path, 'output'));
when(() => argResults[releaseCliArg]).thenReturn(releaseFile.path);
when(() => argResults[patchCliArg]).thenReturn(patchFile.path);
when(() => argResults[patchExecutableCliArg])
.thenReturn(patchExecutable.path);
when(() => argResults[outputCliArg]).thenReturn(outputFile.path);

command = DiffCommand(
({required File patchExecutable}) => patchPackager,
)..testArgResults = argResults;
});

test('has a non-empty name', () {
expect(command.name, isNotEmpty);
});

test('has a non-empty description', () {
expect(command.description, isNotEmpty);
});

group('arg validation', () {
group('when release file does not exist', () {
setUp(() {
releaseFile.deleteSync();
});

test('logs error and exits with code 64', () async {
expect(
await runWithOverrides(command.run),
equals(ExitCode.usage.code),
);

verify(
() => logger.err(
any(that: contains('The release file does not exist')),
),
);
});
});

group('when patch file does not exist', () {
setUp(() {
patchFile.deleteSync();
});

test('logs error and exits with code 64', () async {
expect(
await runWithOverrides(command.run),
equals(ExitCode.usage.code),
);

verify(
() => logger.err(
any(that: contains('The patch file does not exist')),
),
);
});
});

group('when patch executable does not exist', () {
setUp(() {
patchExecutable.deleteSync();
});

test('logs error and exits with code 64', () async {
expect(
await runWithOverrides(command.run),
equals(ExitCode.usage.code),
);

verify(
() => logger.err(
any(that: contains('The patch-executable file does not exist')),
),
);
});
});
});

group('when args are valid', () {
setUp(() {
when(
() => patchPackager.makeDiff(
base: any(named: 'base'),
patch: any(named: 'patch'),
outFile: any(named: 'outFile'),
),
).thenAnswer((_) async {});
});

test('forwards values to patchPackager', () {
expect(
runWithOverrides(command.run),
completion(ExitCode.success.code),
);

verify(
() => patchPackager.makeDiff(
base: any(
named: 'base',
that: equalsFileSystemEntity(releaseFile),
),
patch: any(
named: 'patch',
that: equalsFileSystemEntity(patchFile),
),
outFile: any(
named: 'outFile',
that: equalsFileSystemEntity(outputFile),
),
),
).called(1);
});
});
});
}

0 comments on commit 608d45e

Please sign in to comment.