flutter/packages/updater/lib/main.dart
Matt Perry af3a10f4e4 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.
2015-09-30 18:13:11 -04:00

106 lines
3.0 KiB
Dart

// 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 '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() {}
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 task = new UpdateTask();
task.run();
}