mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Implement working UpdateTask in Dart
Dart code now supports the full flow that the C++ code used to: version check, download, replace app bundle. Bonus: the Dart code is much easier to follow, thanks to async/await! This is part 2 of a 3-part change. The first part added new mojom interfaces, PathService and UpdateService, to the sky_services package.
This commit is contained in:
parent
ce4c630881
commit
62bd083e14
@ -25,6 +25,8 @@ action("updater") {
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//sky/services/activity:interfaces",
|
||||
"//sky/services/updater:interfaces",
|
||||
"//sky/tools/sky_snapshot($host_toolchain)",
|
||||
]
|
||||
}
|
||||
|
||||
@ -2,13 +2,104 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mojo/core.dart';
|
||||
import 'package:sky/services.dart';
|
||||
import 'package:sky_services/updater/update_service.mojom.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:yaml/yaml.dart' as yaml;
|
||||
|
||||
import 'version.dart';
|
||||
import 'pipe_to_file.dart';
|
||||
|
||||
const String kManifestFile = 'sky.yaml';
|
||||
const String kBundleFile = 'app.skyx';
|
||||
|
||||
UpdateServiceProxy _initUpdateService() {
|
||||
UpdateServiceProxy updateService = new UpdateServiceProxy.unbound();
|
||||
shell.requestService(null, updateService);
|
||||
return updateService;
|
||||
}
|
||||
|
||||
final UpdateServiceProxy _updateService = _initUpdateService();
|
||||
|
||||
String cachedDataDir = null;
|
||||
Future<String> getDataDir() async {
|
||||
if (cachedDataDir == null)
|
||||
cachedDataDir = await getAppDataDir();
|
||||
return cachedDataDir;
|
||||
}
|
||||
|
||||
class UpdateTask {
|
||||
UpdateTask() {}
|
||||
|
||||
String toString() => "UpdateTask()";
|
||||
run() async {
|
||||
try {
|
||||
await _runImpl();
|
||||
} catch(e) {
|
||||
print('Update failed: $e');
|
||||
} finally {
|
||||
_updateService.ptr.notifyUpdateCheckComplete();
|
||||
}
|
||||
}
|
||||
|
||||
_runImpl() async {
|
||||
_dataDir = await getDataDir();
|
||||
|
||||
await _readLocalManifest();
|
||||
yaml.YamlMap remoteManifest = await _fetchManifest();
|
||||
if (!_shouldUpdate(remoteManifest)) {
|
||||
print('Update skipped. No new version.');
|
||||
return;
|
||||
}
|
||||
MojoResult result = await _fetchBundle();
|
||||
if (!result.isOk) {
|
||||
print('Update failed while fetching new skyx bundle.');
|
||||
return;
|
||||
}
|
||||
await _replaceBundle();
|
||||
print('Update success.');
|
||||
}
|
||||
|
||||
yaml.YamlMap _currentManifest;
|
||||
String _dataDir;
|
||||
String _tempPath;
|
||||
|
||||
_readLocalManifest() async {
|
||||
String manifestPath = path.join(_dataDir, kManifestFile);
|
||||
String manifestData = await new File(manifestPath).readAsString();
|
||||
_currentManifest = yaml.loadYaml(manifestData, sourceUrl: manifestPath);
|
||||
}
|
||||
|
||||
Future<yaml.YamlMap> _fetchManifest() async {
|
||||
String manifestUrl = _currentManifest['update_url'] + '/' + kManifestFile;
|
||||
String manifestData = await fetchString(manifestUrl);
|
||||
return yaml.loadYaml(manifestData, sourceUrl: manifestUrl);
|
||||
}
|
||||
|
||||
bool _shouldUpdate(yaml.YamlMap remoteManifest) {
|
||||
Version currentVersion = new Version(_currentManifest['version']);
|
||||
Version remoteVersion = new Version(remoteManifest['version']);
|
||||
return (currentVersion < remoteVersion);
|
||||
}
|
||||
|
||||
Future<MojoResult> _fetchBundle() async {
|
||||
// TODO(mpcomplete): Use the cache dir. We need an equivalent of mkstemp().
|
||||
_tempPath = path.join(_dataDir, 'tmp.skyx');
|
||||
String bundleUrl = _currentManifest['update_url'] + '/' + kBundleFile;
|
||||
UrlResponse response = await fetchUrl(bundleUrl);
|
||||
return PipeToFile.copyToFile(response.body, _tempPath);
|
||||
}
|
||||
|
||||
_replaceBundle() async {
|
||||
String bundlePath = path.join(_dataDir, kBundleFile);
|
||||
await new File(_tempPath).rename(bundlePath);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
var x = new UpdateTask();
|
||||
print("Success: $x");
|
||||
var task = new UpdateTask();
|
||||
task.run();
|
||||
}
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
// Copyright 2015 The Chromium Authors. 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:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:mojo/core.dart';
|
||||
|
||||
// Helper class to drain the contents of a mojo data pipe to a file.
|
||||
class PipeToFile {
|
||||
MojoDataPipeConsumer _consumer;
|
||||
MojoEventStream _eventStream;
|
||||
IOSink _outputStream;
|
||||
|
||||
PipeToFile(this._consumer, String outputPath) {
|
||||
_eventStream = new MojoEventStream(_consumer.handle);
|
||||
_outputStream = new File(outputPath).openWrite();
|
||||
}
|
||||
|
||||
Future<MojoResult> _doRead() async {
|
||||
ByteData thisRead = _consumer.beginRead();
|
||||
if (thisRead == null) {
|
||||
throw 'Data pipe beginRead failed: ${_consumer.status}';
|
||||
}
|
||||
// TODO(mpcomplete): Should I worry about the _eventStream listen callback
|
||||
// being invoked again before this completes?
|
||||
await _outputStream.add(thisRead.buffer.asUint8List());
|
||||
return _consumer.endRead(thisRead.lengthInBytes);
|
||||
}
|
||||
|
||||
Future<MojoResult> drain() async {
|
||||
var completer = new Completer();
|
||||
// TODO(mpcomplete): Is it legit to pass an async callback to listen?
|
||||
_eventStream.listen((List<int> event) async {
|
||||
var mojoSignals = new MojoHandleSignals(event[1]);
|
||||
if (mojoSignals.isReadable) {
|
||||
var result = await _doRead();
|
||||
if (!result.isOk) {
|
||||
_eventStream.close();
|
||||
_eventStream = null;
|
||||
_outputStream.close();
|
||||
completer.complete(result);
|
||||
} else {
|
||||
_eventStream.enableReadEvents();
|
||||
}
|
||||
} else if (mojoSignals.isPeerClosed) {
|
||||
_eventStream.close();
|
||||
_eventStream = null;
|
||||
_outputStream.close();
|
||||
completer.complete(MojoResult.OK);
|
||||
} else {
|
||||
throw 'Unexpected handle event: $mojoSignals';
|
||||
}
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
static Future<MojoResult> copyToFile(MojoDataPipeConsumer consumer, String outputPath) {
|
||||
var drainer = new PipeToFile(consumer, outputPath);
|
||||
return drainer.drain();
|
||||
}
|
||||
}
|
||||
32
engine/src/flutter/sky/packages/updater/lib/version.dart
Normal file
32
engine/src/flutter/sky/packages/updater/lib/version.dart
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2015 The Chromium Authors. 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:math';
|
||||
|
||||
// This class represents a dot-separated version string. Used for comparing
|
||||
// versions.
|
||||
// Usage: assert(new Version('1.1.0') < new Version('1.2.1'));
|
||||
class Version {
|
||||
Version(String versionStr) :
|
||||
_parts = versionStr.split('.').map((val) => int.parse(val)).toList();
|
||||
|
||||
List<int> _parts;
|
||||
|
||||
bool operator<(Version other) => _compare(other) < 0;
|
||||
bool operator==(dynamic other) => other is Version && _compare(other) == 0;
|
||||
bool operator>(Version other) => _compare(other) > 0;
|
||||
|
||||
int _compare(Version other) {
|
||||
int length = min(_parts.length, other._parts.length);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
if (_parts[i] < other._parts[i])
|
||||
return -1;
|
||||
if (_parts[i] > other._parts[i])
|
||||
return 1;
|
||||
}
|
||||
return _parts.length - other._parts.length; // results in 1.0 < 1.0.0
|
||||
}
|
||||
|
||||
int get hashCode => _parts.fold(373, (acc, part) => 37*acc + part);
|
||||
}
|
||||
@ -1,9 +1,16 @@
|
||||
name: sky_updater
|
||||
name: updater
|
||||
version: 0.0.1
|
||||
author: Chromium Authors <sky-dev@googlegroups.com>
|
||||
description: The autoupdater for flutter
|
||||
homepage: http://flutter.io
|
||||
dependencies:
|
||||
mojo: ^0.0.21
|
||||
mojo: '>=0.1.0 <0.2.0'
|
||||
sky: any
|
||||
sky_services: any
|
||||
path: any
|
||||
yaml: any
|
||||
dependency_overrides:
|
||||
sky:
|
||||
path: ../sky
|
||||
environment:
|
||||
sdk: '>=1.12.0 <2.0.0'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user