diff --git a/pkg/pub_worker/lib/src/testing/docker_utils.dart b/pkg/pub_worker/lib/src/testing/docker_utils.dart new file mode 100644 index 0000000000..b1587fca1e --- /dev/null +++ b/pkg/pub_worker/lib/src/testing/docker_utils.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:pub_worker/payload.dart'; + +final _ciEnv = Platform.environment['CI']?.toLowerCase(); +final _isRunningOnCI = _ciEnv == 'true' || _ciEnv == '1'; + +Future buildDockerImage() async { + final gitRootDir = + (await Process.run('git', ['rev-parse', '--show-toplevel'])) + .stdout + .toString() + .trim(); + final pr = await Process.start( + 'docker', + [ + 'build', + '.', + '--tag=pub_worker', + '--file=Dockerfile.worker', + ], + workingDirectory: gitRootDir, + mode: ProcessStartMode.inheritStdio, + ); + final exitCode = await pr.exitCode; + if (exitCode != 0) { + throw Exception('process returned with exit code $exitCode'); + } +} + +Future startDockerAnalysis(Payload payload) async { + return await Process.start( + 'docker', + [ + 'run', + if (!_isRunningOnCI) '-it', + '--network=host', + '--entrypoint=dart', + '--rm', + 'pub_worker', + 'bin/pub_worker.dart', + json.encode(payload), + ], + mode: ProcessStartMode.inheritStdio, + ); +} diff --git a/pkg/pub_worker/test/dockerized_end2end_test.dart b/pkg/pub_worker/test/dockerized_end2end_test.dart new file mode 100644 index 0000000000..e3ed864783 --- /dev/null +++ b/pkg/pub_worker/test/dockerized_end2end_test.dart @@ -0,0 +1,96 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:pana/pana.dart'; +import 'package:pub_worker/payload.dart'; +import 'package:pub_worker/src/testing/docker_utils.dart'; +import 'package:pub_worker/src/testing/server.dart'; +import 'package:test/test.dart'; + +void main() { + group('Dockerized end2end tests', () { + final server = PubWorkerTestServer( + [], + fallbackPubHostedUrl: 'https://pub.dev', + ); + setUpAll( + () async { + await server.start(); + }, + ); + + tearDownAll(() async { + await server.stop(); + }); + + Future analyzePackage(String package, [String? version]) async { + if (version == null) { + final url = server.baseUrl.resolve('/api/packages/$package'); + final rs = await http.get(url); + if (rs.statusCode != 200) { + throw Exception('Unexpected status code on $url: ${rs.statusCode}'); + } + final map = json.decode(rs.body) as Map; + version = (map['latest'] as Map)['version'] as String; + } + + final payload = Payload( + package: package, + pubHostedUrl: '${server.baseUrl}', + versions: [ + VersionTokenPair( + version: version, + token: 'secret-token', + ), + ], + ); + + final p = await startDockerAnalysis(payload); + final exitCode = await p.exitCode; + if (exitCode != 0) { + throw Exception( + 'Failed to analyze $package $version with exitCode $exitCode'); + } + + return version; + } + + // TODO: investigate why this is not running on GitHub properly + test( + 'build and use docker image to analyze packages', + () async { + await buildDockerImage(); + + final packages = ['retry', 'url_launcher']; + final versions = + await Future.wait(packages.map((p) => analyzePackage(p))); + + for (var i = 0; i < packages.length; i++) { + final package = packages[i]; + final version = versions[i]; + final result = await server.waitForResult(package, version); + + final docIndex = result.index.lookup('doc/index.html'); + expect(docIndex, isNotNull); + + final panaSummaryBytes = result.lookup('summary.json'); + expect(panaSummaryBytes, isNotNull); + final summary = Summary.fromJson( + json.decode(utf8.decode(gzip.decode(panaSummaryBytes!))) + as Map); + final report = summary.report!; + expect(report.maxPoints, greaterThan(100)); + expect(report.grantedPoints, report.maxPoints); + } + + // TODO: consider docker cleanup + }, + timeout: Timeout(Duration(minutes: 15)), + ); + }); +} diff --git a/pkg/pub_worker/tool/trypkg.dart b/pkg/pub_worker/tool/trypkg.dart index d27c800425..dfa524d9bb 100644 --- a/pkg/pub_worker/tool/trypkg.dart +++ b/pkg/pub_worker/tool/trypkg.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:path/path.dart' as p; import 'package:pub_worker/payload.dart'; +import 'package:pub_worker/src/testing/docker_utils.dart'; import 'package:pub_worker/src/testing/server.dart'; final _argParser = ArgParser() @@ -42,44 +43,27 @@ void main(List args) async { final server = PubWorkerTestServer([], fallbackPubHostedUrl: pubHostedUrl); await server.start(); + final payload = Payload( + package: package, + pubHostedUrl: '${server.baseUrl}', + versions: [ + VersionTokenPair( + version: version, + token: 'secret-token', + ), + ], + ); final Process worker; if (argResults['docker'] == true) { - print('Building worker docker image'); - final buildProcess = await Process.start( - 'docker', - ['build', '.', '--tag=pub_worker', '--file=Dockerfile.worker'], - workingDirectory: Platform.script.resolve('../../..').path, - mode: ProcessStartMode.inheritStdio, - ); - final buildExitCode = await buildProcess.exitCode; - if (buildExitCode != 0) { - print('Building docker image failed (exit code $buildExitCode)'); + try { + print('Building worker docker image'); + await buildDockerImage(); + } catch (e) { + print('Building worker docker image failed: $e'); exit(-1); } - worker = await Process.start( - 'docker', - [ - 'run', - '-it', - '--network=host', - '--entrypoint=dart', - '--rm', - 'pub_worker', - 'bin/pub_worker.dart', - json.encode(Payload( - package: package, - pubHostedUrl: '${server.baseUrl}', - versions: [ - VersionTokenPair( - version: version, - token: 'secret-token', - ), - ], - )), - ], - mode: ProcessStartMode.inheritStdio, - ); + worker = await startDockerAnalysis(payload); } else { worker = await Process.start( Platform.resolvedExecutable, @@ -87,16 +71,7 @@ void main(List args) async { 'run', if (argResults['observe'] == true) '--observe', 'pub_worker', - json.encode(Payload( - package: package, - pubHostedUrl: '${server.baseUrl}', - versions: [ - VersionTokenPair( - version: version, - token: 'secret-token', - ), - ], - )), + json.encode(payload), ], mode: ProcessStartMode.inheritStdio, );