Skip to content

Commit

Permalink
Merge pull request #17 from bostrot/feature_networksync
Browse files Browse the repository at this point in the history
Synchronize instances over local network
  • Loading branch information
bostrot authored Feb 15, 2022
2 parents 32e3ec3 + 555df1e commit 0dcf2b9
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 36 deletions.
5 changes: 4 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ build:
- dpkg -i gh_2.4.0_linux_amd64.deb
#- git remote add github https://github.com/bostrot/wsl2-distro-manager.git
- ./release-script.sh

tags:
- docker
only:
- main
Binary file modified build/windows/runner/Release/data/app.so
Binary file not shown.
Binary file modified build/windows/runner/Release/data/flutter_assets/NOTICES.Z
Binary file not shown.
Binary file modified build/windows/runner/Release/wsl2distromanager.exe
Binary file not shown.
45 changes: 31 additions & 14 deletions lib/components/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,9 @@ import 'dart:io';
import 'dart:convert' show utf8, json;
import 'package:dio/dio.dart';
import 'constants.dart';
import 'helpers.dart';
// import 'package:package_info_plus/package_info_plus.dart';

const String updateUrl =
'https://api.github.com/repos/bostrot/wsl2-distro-manager/releases';

const String motdUrl =
'https://raw.githubusercontent.com/bostrot/wsl2-distro-manager/main/motd.json';

const String defaultPath = 'C:\\WSL2-Distros\\';

class Instances {
List<String> running = [];
List<String> all = [];
Expand Down Expand Up @@ -74,11 +67,6 @@ class App {

/// WSL API
class WSLApi {
/// Constructor: create Root Directory
WSLApi() {
mkRootDir();
}

/// Create directory
void mkRootDir() async {
//await Process.run('help', []);
Expand Down Expand Up @@ -110,6 +98,11 @@ class WSLApi {
}
Process.start('start', args,
mode: ProcessStartMode.detached, runInShell: true);

//exec(distribution, [
// 'cp /mnt/$path/startup.sh ~/startup.sh',
// '/bin/bash ~/startup.sh',
//]);
}

/// Stop a WSL distro by name
Expand All @@ -121,6 +114,22 @@ class WSLApi {
return results.stdout;
}

/// Open bashrc with notepad from WSL
/// @param distribution: String
Future<String> openBashrc(String distribution) async {
List<String> argsRc = ['wsl', '-d', distribution, 'notepad.exe', '.bashrc'];
Process results = await Process.start('start', argsRc,
mode: ProcessStartMode.normal, runInShell: true);
return results.stdout.toString();
}

/// Shutdown WSL
/// @return Future<String>
Future<String> shutdown() async {
ProcessResult results = await Process.run('wsl', ['--shutdown']);
return results.stdout;
}

/// Start VSCode
/// @param distribution: String
void startVSCode(String distribution, {String path = ''}) async {
Expand Down Expand Up @@ -173,6 +182,14 @@ class WSLApi {
mode: ProcessStartMode.normal, runInShell: true);
}

/// Open distro config file
void editDistroConfig(String distroname) async {
String path =
prefs.getString("Path_$distroname") ?? defaultPath + distroname;
Process.start('start', ['notepad.exe', '$path\\startup.sh'],
mode: ProcessStartMode.normal, runInShell: true);
}

/// Start Explorer
/// @param distribution: String
void startExplorer(String distribution, {String path = ''}) async {
Expand All @@ -185,7 +202,7 @@ class WSLApi {
mode: ProcessStartMode.normal, runInShell: true);
}

/// Start a WSL distro by name
/// Copy a WSL distro by name
/// @param distribution: String
/// @param newName: String
/// @param location: String (optional)
Expand Down
9 changes: 8 additions & 1 deletion lib/components/constants.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
// TODO: Update on release
const String currentVersion = "v0.7.4";
const String currentVersion = "v0.8.0";
const String windowsStoreUrl = "https://www.microsoft.com/store/"
"productId/9NWS9K95NMJB";
const String defaultPath = 'C:\\WSL2-Distros\\';

const String updateUrl =
'https://api.github.com/repos/bostrot/wsl2-distro-manager/releases';

const String motdUrl =
'https://raw.githubusercontent.com/bostrot/wsl2-distro-manager/main/motd.json';

// https://docs.microsoft.com/en-us/windows/wsl/install-on-server
const Map<String, String> distroRootfsLinks = {
Expand Down
16 changes: 8 additions & 8 deletions lib/components/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ class DistroList extends StatefulWidget {

class _DistroListState extends State<DistroList> {
Map<String, bool> hover = {};
bool isSyncing = false;
void update(var item, bool enter) {
setState(() {
hover[item] = enter;
});
}

void syncing(var item) {
setState(() {
isSyncing = item;
});
}

@override
void initState() {
initPrefs();
Expand Down Expand Up @@ -70,14 +77,7 @@ FutureBuilder<Instances> distroList(
}
for (String item in list) {
newList.add(listItem(
item,
update,
hover,
isRunning,
running,
statusMsg,
context,
));
item, update, hover, isRunning, running, statusMsg, context));
}
return Expanded(
child: ListView.custom(
Expand Down
84 changes: 84 additions & 0 deletions lib/components/sync.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_static/shelf_static.dart';
import 'package:wsl2distromanager/components/api.dart';
import 'helpers.dart';

class Sync {
late Function(String, {bool loading}) statusMsg;
late String distroName;
late String distroLocation;
static late HttpServer server;

Sync();

/// Constructor
/// @param {String} distroName
/// @param {Function} statusMsg
Sync.instance(this.distroName, this.statusMsg) {
String? distroLocation = prefs.getString('Path_' + distroName);
if (distroLocation == null) {
statusMsg('Distro not found', loading: false);
return;
}
this.distroLocation = distroLocation.replaceAll('/', '\\');
}

/// Check if distro has path in settings
/// @param {String} distroName
bool hasPath(String distroName) {
String? distroLocation = prefs.getString('Path_' + distroName);
if (distroLocation == null) {
return false;
}
return true;
}

/// Start the server
void startServer() async {
// Get path for distro filesystem
// Serve filesystem file
var handler = createFileHandler(distroLocation + '\\ext4.vhdx',
contentType: "application/octet-stream");
// Listen on network
try {
server = await io.serve(handler, '0.0.0.0', 59132);
} catch (e) {
// Do nothing
}
}

/// Stop the server
void stopServer() {
server.close();
}

/// Download from sync IP
void download() async {
// Get path for distro filesystem
String? syncIP = prefs.getString('SyncIP');
if (syncIP == null) {
statusMsg('Sync IP not set. Please set it in the settings.',
loading: false);
return;
}
statusMsg('Shutting down WSL...', loading: true);
// Shutdown WSL
await WSLApi().shutdown();
statusMsg('Connecting to IP: "$syncIP"...', loading: true);
Dio().download(
'http://$syncIP:59132/ext4.vhdx', distroLocation + '\\ext4.vhdx',
onReceiveProgress: (received, total) {
String rec = (received / 1024 / 1024).toStringAsFixed(2);
String tot = (total / 1024 / 1024).toStringAsFixed(2);
statusMsg('Downloading $distroName, $rec MB / $tot MB', loading: true);
if (received == total) {
statusMsg('Downloaded $distroName');
}
}).catchError((e) {
statusMsg('Error downloading $distroName', loading: false);
});
}
}
3 changes: 3 additions & 0 deletions lib/dialogs/copy_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:wsl2distromanager/components/api.dart';
import 'package:wsl2distromanager/dialogs/base_dialog.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:wsl2distromanager/components/helpers.dart';
import 'package:wsl2distromanager/components/constants.dart';

/// Copy Dialog
/// @param context: context
Expand Down Expand Up @@ -30,6 +31,8 @@ copyDialog(context, item, Function(String, {bool loading}) statusMsg) {
String? startName = prefs.getString('StartUser_' + item) ?? '';
prefs.setString('StartPath_' + inputText, startPath);
prefs.setString('StartUser_' + inputText, startName);
// Save distro path
prefs.setString('Path_' + inputText, defaultPath + inputText);
statusMsg('DONE: Copied ${distroLabel(item)} to $inputText.');
} else {
statusMsg('ERROR: Please enter a name for the new instance.');
Expand Down
2 changes: 2 additions & 0 deletions lib/dialogs/create_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ createDialog(context, Function(String, {bool loading}) statusMsg) {
Navigator.pop(context);
statusMsg('DONE: creating instance');
}
// Save distro path
prefs.setString('Path_' + name, location);
}
// Download distro check
} else {
Expand Down
1 change: 1 addition & 0 deletions lib/dialogs/dialogs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'info_dialog.dart';
export 'install_dialog.dart';
export 'rename_dialog.dart';
export 'settings_dialog.dart';
export 'sync_dialog.dart';
1 change: 0 additions & 1 deletion lib/dialogs/info_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:wsl2distromanager/components/api.dart';
import 'package:wsl2distromanager/components/analytics.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:url_launcher/url_launcher.dart';
Expand Down
59 changes: 59 additions & 0 deletions lib/dialogs/settings_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:wsl2distromanager/components/analytics.dart';
import 'package:fluent_ui/fluent_ui.dart';
// import 'package:wsl2distromanager/components/api.dart';
import 'package:wsl2distromanager/components/helpers.dart';
import 'package:wsl2distromanager/components/sync.dart';
import 'package:wsl2distromanager/dialogs/sync_dialog.dart';

/// Rename Dialog
/// @param context: context
Expand All @@ -13,6 +16,7 @@ settingsDialog(context, item, Function(String, {bool loading}) statusMsg) {
final userController = TextEditingController();
userController.text = prefs.getString('StartUser_' + item) ?? '';
plausible.event(page: title.split(' ')[0].toLowerCase());
bool isSyncing = false;
showDialog(
context: context,
builder: (context) {
Expand Down Expand Up @@ -48,6 +52,61 @@ settingsDialog(context, item, Function(String, {bool loading}) statusMsg) {
child: Text(
'(empty the fields for default or if your WSL version does not support it)'),
),
Sync().hasPath(item)
? Tooltip(
message: 'Upload',
child: Button(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Start/Stop serving on network'),
Icon(FluentIcons.upload),
]),
onPressed: () {
//plausible.event(name: "wsl_started");
Sync sync = Sync.instance(item, statusMsg);
if (!isSyncing) {
isSyncing = true;
sync.startServer();
statusMsg('Serving $item on network.');
} else {
isSyncing = false;
sync.stopServer();
statusMsg('Stopped serving $item on network.');
}
},
),
)
: Container(),
const SizedBox(height: 8.0),
Sync().hasPath(item)
? Tooltip(
message: 'Download',
child: Button(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Download/Override from network'),
Icon(FluentIcons.download),
]),
onPressed: () {
//plausible.event(name: "wsl_started");
syncDialog(context, item, statusMsg);
},
),
)
: Container(),
const SizedBox(
height: 8.0,
),
/* Button(
child: const Text('Edit startup file'),
style: ButtonStyle(
padding: ButtonState.all(const EdgeInsets.only(
left: 15.0, right: 15.0, top: 10.0, bottom: 10.0))),
onPressed: () {
WSLApi().openBashrc(item);
}), */
],
),
actions: [
Expand Down
29 changes: 29 additions & 0 deletions lib/dialogs/sync_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:wsl2distromanager/components/sync.dart';
import 'package:wsl2distromanager/dialogs/base_dialog.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:wsl2distromanager/components/helpers.dart';

/// Sync Dialog
/// @param context: context
/// @param item: distro name
/// @param statusMsg: Function(String, {bool loading})
syncDialog(context, item, Function(String, {bool loading}) statusMsg) {
dialog(
context: context,
item: item,
statusMsg: statusMsg,
title: 'Sync \'${distroLabel(item)}\' from the server',
body: 'Warning: Syncing will shutdown WSL and override the distro '
'"$item" completely! There is no way to turn back! A backup is advised.'
'\n\nAre you sure you want to continue?',
submitText: 'Yes, sync (override)',
submitInput: false,
submitStyle: ButtonStyle(
backgroundColor: ButtonState.all(Colors.red),
foregroundColor: ButtonState.all(Colors.white),
),
onSubmit: (inputText) {
Sync sync = Sync.instance(item, statusMsg);
sync.download();
});
}
Loading

0 comments on commit 0dcf2b9

Please sign in to comment.