public async Task <bool> DownloadArtifact(CliParams cliParams, string artifactDownloadUrl, string downloadToPath) { try { ConsoleHelper.WriteLine("Downloading build artifact."); var message = GetHttpRequestMessage(HttpMethod.Get, artifactDownloadUrl, cliParams); var response = await _httpClient.SendAsync(message); ConsoleHelper.WriteLine($"Download artifact response status code: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { ConsoleHelper.WriteError("GitHub API call to download build artifact failed. Please check your input parameters."); Environment.Exit(1); } using var responseStream = await response.Content.ReadAsStreamAsync(); using var fileStream = new FileStream(downloadToPath, FileMode.Create); await responseStream.CopyToAsync(fileStream); return(true); } catch (Exception ex) { ConsoleHelper.WriteError($"Error while downloading artifact. Message: {ex.Message}"); return(false); } }
private async Task LaunchExternalInstaller(CliParams cliParams) { ConsoleHelper.WriteLine("Launching install script for selected reverse proxy type."); var resourcesPath = "Remotely.Server.Installer.Resources."; var fileName = cliParams.WebServer.Value switch { WebServerType.UbuntuCaddy => "Ubuntu_Caddy_Install.sh", WebServerType.UbuntuNginx => "Ubuntu_Nginx_Install.sh", WebServerType.CentOsCaddy => "CentOS_Caddy_Install.sh", WebServerType.CentOsNginx => "CentOS_Nginx_Install.sh", WebServerType.IisWindows => "IIS_Windows_Install.ps1", WebServerType.LinuxNginx => "LinuxArm_Nginx_Install.sh", _ => throw new Exception("Unrecognized reverse proxy type."), }; var resourcesFile = resourcesPath + fileName; var filePath = Path.Combine(Path.GetTempPath(), fileName); using (var mrs = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcesFile)) using (var fileStream = new FileStream(filePath, FileMode.Create)) { await mrs.CopyToAsync(fileStream); } var scriptResult = false; ProcessStartInfo psi; if (cliParams.WebServer.Value == WebServerType.IisWindows) { psi = new ProcessStartInfo("powershell.exe") { Arguments = $"-f \"{filePath}\" -AppPoolName Remotely -SiteName Remotely " + $"-SitePath \"{cliParams.InstallDirectory}\" -HostName {cliParams.ServerUrl.Authority} -Quiet", WorkingDirectory = cliParams.InstallDirectory }; } else { Process.Start("sudo", $"chmod +x {filePath}").WaitForExit(); psi = new ProcessStartInfo("sudo") { Arguments = $"\"{filePath}\" --host {cliParams.ServerUrl.Authority} --approot {cliParams.InstallDirectory}", WorkingDirectory = cliParams.InstallDirectory }; } var proc = Process.Start(psi); scriptResult = await Task.Run(() => proc.WaitForExit((int)TimeSpan.FromMinutes(30).TotalMilliseconds)); if (!scriptResult) { ConsoleHelper.WriteError("Installer script is taking longer than expected."); } }
public async Task PerformInstall(CliParams cliParams) { var latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); var latestBuildId = latestBuild?.id; if (cliParams.CreateNew == true) { var dispatchResult = await _githubApi.TriggerDispatch(cliParams); if (!dispatchResult) { ConsoleHelper.WriteError("GitHub API call to trigger build action failed. Do you have " + "Actions enabled on your forked Remotely repo on the Actions tab? If not, enable them and try again. " + "Otherwise, please check your input parameters."); return; } ConsoleHelper.WriteLine("Build action triggered successfully. Waiting for build completion."); while (latestBuild?.id == latestBuildId) { await Task.Delay(TimeSpan.FromMinutes(1)); ConsoleHelper.WriteLine("Waiting for GitHub build completion."); latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); } } else if (latestBuild is null) { ConsoleHelper.WriteError("There are no existing build artifacts, and --create-new was not specified. Exiting."); return; } var filePath = Path.Combine(Path.GetTempPath(), "Remotely_Artifact.zip"); var downloadResult = await _githubApi.DownloadArtifact(cliParams, latestBuild.archive_download_url, filePath); if (!downloadResult) { ConsoleHelper.WriteError("Downloading the build artifact was not successful."); return; } ConsoleHelper.WriteLine("Extracting artifact files."); if (Directory.Exists(cliParams.InstallDirectory)) { Directory.Delete(cliParams.InstallDirectory, true); } Directory.CreateDirectory(cliParams.InstallDirectory); ZipFile.ExtractToDirectory(filePath, cliParams.InstallDirectory); await LaunchExternalInstaller(cliParams); }
public async Task <bool> TriggerDispatch(CliParams cliParams) { try { ConsoleHelper.WriteLine("Trigger GitHub Actions build."); var message = GetHttpRequestMessage( HttpMethod.Post, $"{_apiHost}/repos/{cliParams.GitHubUsername}/Remotely/actions/workflows/build.yml/dispatches", cliParams); var rid = EnvironmentHelper.IsLinux ? "linux-x64" : "win-x64"; var body = new { @ref = cliParams.Reference, inputs = new { serverUrl = cliParams.ServerUrl.ToString(), rid = rid } }; message.Content = new StringContent(JsonSerializer.Serialize(body)); var response = await _httpClient.SendAsync(message); ConsoleHelper.WriteLine($"Dispatch response status code: {response.StatusCode}"); return(response.IsSuccessStatusCode); } catch (Exception ex) { ConsoleHelper.WriteError($"Error: {ex.Message}"); } return(false); }
public async Task <Artifact> GetLatestBuildArtifact(CliParams cliParams) { try { var message = GetHttpRequestMessage(HttpMethod.Get, $"{_apiHost}/repos/{cliParams.GitHubUsername}/Remotely/actions/artifacts", cliParams); var response = await _httpClient.SendAsync(message); ConsoleHelper.WriteLine($"Get artifacts response status code: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { ConsoleHelper.WriteError("GitHub API call to get build artifacts failed. Please check your input parameters."); Environment.Exit(1); } var payload = await response.Content.ReadFromJsonAsync <ArtifactsResponsePayload>(); if (payload?.artifacts?.Any() != true) { return(null); } return(payload.artifacts .OrderByDescending(x => x.created_at) .FirstOrDefault(x => x.name.Equals("Server", StringComparison.OrdinalIgnoreCase))); } catch (Exception ex) { ConsoleHelper.WriteError("Error while trying to retrieve build artifacts." + $"Error: {ex.Message}"); Environment.Exit(1); } return(null); }
private HttpRequestMessage GetHttpRequestMessage(HttpMethod method, string url, CliParams cliParams) { var message = new HttpRequestMessage(method, url); var base64Auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{cliParams.GitHubUsername}:{cliParams.GitHubPat}")); message.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64Auth); message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); return(message); }
public async Task PerformInstall(CliParams cliParams) { var zipPath = Path.Combine(Path.GetTempPath(), "Remotely_Server.zip"); if (cliParams.UsePrebuiltPackage == true) { ConsoleHelper.WriteLine("Downloading pre-built server package."); int progress = 0; var releaseFile = cliParams.WebServer == WebServerType.IisWindows ? "https://github.com/lucent-sea/Remotely/releases/latest/download/Remotely_Server_Win-x64.zip" : "https://github.com/lucent-sea/Remotely/releases/latest/download/Remotely_Server_Linux-x64.zip"; using var webClient = new WebClient(); webClient.DownloadProgressChanged += (sender, args) => { var newProgress = args.ProgressPercentage / 5 * 5; if (newProgress != progress) { progress = newProgress; ConsoleHelper.WriteLine($"Progress: {progress}%"); } }; await webClient.DownloadFileTaskAsync(releaseFile, zipPath); } else { var latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); var latestBuildId = latestBuild?.id; if (cliParams.CreateNew == true) { var dispatchResult = await _githubApi.TriggerDispatch(cliParams); if (!dispatchResult) { ConsoleHelper.WriteError("GitHub API call to trigger build action failed. Do you have " + "Actions enabled on your forked Remotely repo on the Actions tab? If not, enable them and try again. " + "Otherwise, please check your input parameters."); return; } ConsoleHelper.WriteLine("Build action triggered successfully. Waiting for build completion."); while (latestBuild?.id == latestBuildId) { await Task.Delay(TimeSpan.FromMinutes(1)); ConsoleHelper.WriteLine("Waiting for GitHub build completion."); latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); } } else if (latestBuild is null) { ConsoleHelper.WriteError("There are no existing build artifacts, and --create-new was not specified. Exiting."); return; } var downloadResult = await _githubApi.DownloadArtifact(cliParams, latestBuild.archive_download_url, zipPath); if (!downloadResult) { ConsoleHelper.WriteError("Downloading the build artifact was not successful."); return; } } // Files in use can't be overwritten in Windows. Stop the // website process first. if (cliParams.WebServer == WebServerType.IisWindows) { var initialW3wpCount = Process.GetProcessesByName("w3wp").Length; Process.Start("powershell.exe", "-Command & \"{ Stop-WebAppPool -Name Remotely -ErrorAction SilentlyContinue }\"").WaitForExit(); Process.Start("powershell.exe", "-Command & \"{ Stop-Website -Name Remotely -ErrorAction SilentlyContinue }\"").WaitForExit(); ConsoleHelper.WriteLine("Waiting for w3wp processes to close..."); TaskHelper.DelayUntil(() => Process.GetProcessesByName("w3wp").Length < initialW3wpCount, TimeSpan.FromMinutes(5), 100); } ConsoleHelper.WriteLine("Extracting files."); Directory.CreateDirectory(cliParams.InstallDirectory); ZipFile.ExtractToDirectory(zipPath, cliParams.InstallDirectory, true); await LaunchExternalInstaller(cliParams); }
private static bool ParseCliParams(string[] args, out CliParams cliParams) { cliParams = new CliParams(); for (var i = 0; i < args.Length; i += 2) { try { var key = args[i].Trim(); if (i == args.Length - 1) { ConsoleHelper.WriteError("An argument is missing a value."); return(false); } var value = args[i + 1].Trim(); switch (key) { case "--use-prebuilt": case "-b": { if (bool.TryParse(value, out var result)) { cliParams.UsePrebuiltPackage = result; continue; } ConsoleHelper.WriteError("--use-prebuilt parameter is invalid. Must be true or false."); return(false); } case "--web-server": case "-w": { if (int.TryParse(value, out var webServerResult)) { cliParams.WebServer = (WebServerType)webServerResult; continue; } ConsoleHelper.WriteError($"--web-server parameter is invalid. Must be a " + $"number (0 - {Enum.GetValues<WebServerType>().Length})."); return(false); } case "--server-url": case "-s": { if (Uri.TryCreate(value, UriKind.Absolute, out var result)) { cliParams.ServerUrl = result; continue; } ConsoleHelper.WriteError("--server-url parameter is invalid. Must be a valid URL (e.g. https://app.remotely.one)."); return(false); } case "--install-directory": case "-i": cliParams.InstallDirectory = value; continue; case "--github-username": case "-u": cliParams.GitHubUsername = value; continue; case "--github-pat": case "-p": cliParams.GitHubPat = value; continue; case "--reference": case "-r": cliParams.Reference = value; continue; case "--create-new": case "-c": { if (bool.TryParse(value, out var result)) { cliParams.CreateNew = result; continue; } ConsoleHelper.WriteError("--create-new parameter is invalid. Must be true or false."); return(false); } default: return(false); } } catch (Exception ex) { ConsoleHelper.WriteError($"Error while parsing command line arguments: {ex.Message}"); return(false); } } return(true); }
public async Task PerformInstall(CliParams cliParams) { var filePath = Path.Combine(Path.GetTempPath(), "Remotely_Server.zip"); if (cliParams.UsePrebuiltPackage == true) { ConsoleHelper.WriteLine("Downloading pre-built server package."); var progress = 0; var releaseFile = "https://github.com/lucent-sea/Remotely/releases/latest/download/Remotely_Server_Linux-x64.zip"; using var webClient = new WebClient(); webClient.DownloadProgressChanged += (sender, arg) => { var newProgress = arg.ProgressPercentage / 5 * 5; if (newProgress != progress) { progress = newProgress; ConsoleHelper.WriteLine($"Progress: {progress}%"); } }; await webClient.DownloadFileTaskAsync(releaseFile, filePath); } else { var latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); var latestBuildId = latestBuild?.id; if (cliParams.CreateNew == true) { var dispatchResult = await _githubApi.TriggerDispatch(cliParams); if (!dispatchResult) { ConsoleHelper.WriteError("GitHub API call to trigger build action failed. Do you have " + "Actions enabled on your forked Remotely repo on the Actions tab? If not, enable them and try again. " + "Otherwise, please check your input parameters."); return; } ConsoleHelper.WriteLine("Build action triggered successfully. Waiting for build completion."); while (latestBuild?.id == latestBuildId) { await Task.Delay(TimeSpan.FromMinutes(1)); ConsoleHelper.WriteLine("Waiting for GitHub build completion."); latestBuild = await _githubApi.GetLatestBuildArtifact(cliParams); } } else if (latestBuild is null) { ConsoleHelper.WriteError("There are no existing build artifacts, and --create-new was not specified. Exiting."); return; } var downloadResult = await _githubApi.DownloadArtifact(cliParams, latestBuild.archive_download_url, filePath); if (!downloadResult) { ConsoleHelper.WriteError("Downloading the build artifact was not successful."); return; } } ConsoleHelper.WriteLine("Extracting files."); if (Directory.Exists(cliParams.InstallDirectory)) { Directory.Delete(cliParams.InstallDirectory, true); } Directory.CreateDirectory(cliParams.InstallDirectory); ZipFile.ExtractToDirectory(filePath, cliParams.InstallDirectory); await LaunchExternalInstaller(cliParams); }