From 4eae5b72e156d82f9b4d0a5f55864d6a74694adf Mon Sep 17 00:00:00 2001 From: Derroylo <72140754+Derroylo@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:22:39 +0100 Subject: [PATCH] Simple implementation for a workspace sync (#79) --- Program.cs | 20 +++- commands/sync/SyncWorkspaceCommand.cs | 154 ++++++++++++++++++++++++++ gpt.sh | 17 ++- 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 commands/sync/SyncWorkspaceCommand.cs diff --git a/Program.cs b/Program.cs index 6edec33..c3252e4 100644 --- a/Program.cs +++ b/Program.cs @@ -19,6 +19,7 @@ using System.Security.Cryptography; using System.Text; using Gitpod.Tool.Commands.Persist; +using Gitpod.Tool.Commands.Sync; namespace Gitpod.Tool { @@ -87,9 +88,10 @@ static void Main(string[] args) config.AddBranch("php", branch => AddPhpCommandBranch(branch, additionalCommands)); config.AddBranch("restore", branch => AddRestoreCommandBranch(branch, additionalCommands)); config.AddBranch("services", branch => AddServicesCommandBranch(branch, additionalCommands)); + config.AddBranch("sync", branch => AddSyncCommandBranch(branch, additionalCommands)); config.AddCommand("update").WithDescription("Update this tool to the latest version"); - List reservedBranches = new() { "default", "config", "php", "nodejs", "apache", "mysql", "services", "restore", "environment" }; + List reservedBranches = new() { "default", "config", "php", "nodejs", "apache", "mysql", "services", "restore", "environment", "sync" }; // Add branches that havenĀ“t been added yet via custom commands foreach (KeyValuePair entry in additionalCommands.Where(x => !reservedBranches.Contains(x.Key))) { @@ -357,5 +359,21 @@ private static void AddPersistCommandBranch(IConfigurator branc } } } + + private static void AddSyncCommandBranch(IConfigurator branch, Dictionary additionalCommands) + { + branch.SetDescription("Sync files from your local machine with a workspace or between workspaces."); + + branch.AddCommand("workspace") + .WithDescription("Connect to another workspace and sync the content of a folder"); + + if (additionalCommands.TryGetValue("sync", out CustomBranch customBranch)) { + foreach (CustomCommand cmd in customBranch.Commands) { + branch.AddCommand(cmd.Command) + .WithData(cmd) + .WithDescription(cmd.Description); + } + } + } } } diff --git a/commands/sync/SyncWorkspaceCommand.cs b/commands/sync/SyncWorkspaceCommand.cs new file mode 100644 index 0000000..4c2aa23 --- /dev/null +++ b/commands/sync/SyncWorkspaceCommand.cs @@ -0,0 +1,154 @@ +using System.IO; +using Gitpod.Tool.Helper; +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Gitpod.Tool.Commands.Sync +{ + class SyncWorkspaceCommand : Command + { + public class Settings : CommandSettings + { + } + + public override int Execute(CommandContext context, Settings settings) + { + AnsiConsole.MarkupLine("Sync a folder from this machine to another workspace."); + AnsiConsole.MarkupLine(""); + + if (!ExecCommand.Exec("which rclone").Contains("rclone")) { + if (!AnsiConsole.Confirm("rclone needs to be installed for this function. Do you want to install it now?")) { + return 1; + } + + ExecCommand.Exec("sudo -v ; curl https://rclone.org/install.sh | sudo bash"); + + if (!ExecCommand.Exec("which rclone").Contains("rclone")) { + AnsiConsole.MarkupLine("[red]Installation of rclone failed. You need to install it manually[/]"); + + return 1; + } + } + + if (!ExecCommand.Exec("which inotifywait").Contains("inotifywait")) { + if (!AnsiConsole.Confirm("inotifywait needs to be installed for this function. Do you want to install it now?")) { + return 1; + } + + ExecCommand.Exec("sudo -v ; curl https://rclone.org/install.sh | sudo bash"); + + if (!ExecCommand.Exec("which inotifywait").Contains("inotifywait")) { + AnsiConsole.MarkupLine("[red]Installation of inotifywait failed. You need to install it manually[/]"); + + return 1; + } + } + + bool inputInvalid = false; + + var workspaceHost = string.Empty; + var workspaceUser = string.Empty; + var workspaceAccessToken = string.Empty; + var localFolder = string.Empty; + var remoteFolder = string.Empty; + + do { + AnsiConsole.WriteLine("Enter the url of the workspace you want to connect to."); + AnsiConsole.MarkupLine("[orange3]Info: The format should look like this: https://WORKSPACE-ID.ws-REGION.gitpod.io/[/]"); + + workspaceHost = AnsiConsole.Ask("Host:"); + + if (workspaceHost == string.Empty) { + AnsiConsole.MarkupLine("[red]Enter a valid gitpod workspace url.[/]"); + inputInvalid = true; + } else { + inputInvalid = false; + } + } while (inputInvalid); + + do { + AnsiConsole.WriteLine("Enter the name of the user."); + AnsiConsole.MarkupLine("[orange3]Info: This is the name of the workspace[/]"); + + workspaceUser = AnsiConsole.Ask("User:"); + + if (workspaceUser == string.Empty) { + AnsiConsole.MarkupLine("[red]Enter a valid user.[/]"); + inputInvalid = true; + } else { + inputInvalid = false; + } + } while (inputInvalid); + + do { + AnsiConsole.WriteLine("Enter the access token."); + + workspaceAccessToken = AnsiConsole.Ask("Access Token:"); + + if (workspaceAccessToken == string.Empty) { + AnsiConsole.MarkupLine("[red]Enter a valid access token.[/]"); + inputInvalid = true; + } else { + inputInvalid = false; + } + } while (inputInvalid); + + do { + AnsiConsole.WriteLine("Enter the local folder"); + AnsiConsole.MarkupLine("[orange3]Info: This is the folder you want to sync over to the remove machine.[/]"); + + localFolder = AnsiConsole.Ask("Local folder:"); + + if (localFolder == string.Empty) { + AnsiConsole.MarkupLine("[red]Enter a valid local folder.[/]"); + inputInvalid = true; + } else { + inputInvalid = false; + } + } while (inputInvalid); + + do { + AnsiConsole.WriteLine("Enter the remote folder"); + AnsiConsole.MarkupLine("[orange3]Info: The folder on the remove machine to which you want to sync the files to.[/]"); + + remoteFolder = AnsiConsole.Ask("Remote folder:"); + + if (remoteFolder == string.Empty) { + AnsiConsole.MarkupLine("[red]Enter a valid remote folder.[/]"); + inputInvalid = true; + } else { + inputInvalid = false; + } + } while (inputInvalid); + + var accessTokenObscured = ExecCommand.Exec("echo \"" + workspaceAccessToken + "\" | rclone obscure -"); + + var command = "rclone sync"; + + // Set local folder + command += " " + localFolder; + + // Set remote folder + command += " :sftp:" + remoteFolder; + + // Set Host + command += " --sftp-host " + workspaceHost; + + // Set User + command += " --sftp-user " + workspaceUser; + + // Set the password + command += " --sftp-pass " + accessTokenObscured; + + // Set shell type + command += " --sftp-shell-type unix"; + + // Set hash commands + command += " --sftp-md5sum-command md5sum --sftp-sha1sum-command sha1sum"; + + File.WriteAllText(".sync", command); + + return 0; + } + } +} \ No newline at end of file diff --git a/gpt.sh b/gpt.sh index 9003975..ec7f0ac 100644 --- a/gpt.sh +++ b/gpt.sh @@ -71,9 +71,24 @@ if [ -f "$GPTDIR/.nodejs" ]; then nvm alias default $version fi -# Check if we want to change the nodejs version +# Restore env variables if [ -f "$GPTDIR/.env_restore" ]; then source "$GPTDIR/.env_restore" rm "$GPTDIR/.env_restore" +fi + +# Execute the sync commands +if [ -f "$GPTDIR/.sync" ]; then + syncCommand=$(<"$GPTDIR/.sync") + + rm "$GPTDIR/.sync" + + eval $syncCommand + + inotifywait --recursive --monitor --format "%e %w%f" --event modify,move,create,delete ./ \ + | while read changed; do + echo $changed + eval $syncCommand + done fi \ No newline at end of file