Skip to content

Commit

Permalink
Implemented updater for wizard. Closes #109.
Browse files Browse the repository at this point in the history
  • Loading branch information
3F committed Jan 25, 2020
1 parent 94c1f13 commit a998821
Show file tree
Hide file tree
Showing 14 changed files with 525 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Manager/batch/.compressor
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
var vdict = gencomb(cdict, 2, (int i) => { return char.IsLetter(cdict[i]) || cdict[i] == '_'; });
// to skip processing for:
var exvar = new[] { "__p_call", "wMgrArgs", "wPkgVer", "wSlnDir", "wPkgPath", "wMetaLib", "wMetaCor", "wDxpTarget", "wAction", "wSlnFile", "wRootPath", "ngserver" };
var exvar = new[] { "__p_call", "wMgrArgs", "wPkgVer", "wProxy", "wSlnDir", "wPkgPath", "wMetaLib", "wMetaCor", "wDxpTarget", "wAction", "wSlnFile", "wRootPath", "ngserver" };
const string VNAME = "[a-z_][a-z_0-9]+";
const string VERS = "[Minified version]";
Expand Down
3 changes: 2 additions & 1 deletion Manager/batch/Manager.bat
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ set key=!arg[%idx%]!
) else if [!key!]==[-proxy] ( set /a "idx+=1" & call :eval arg[!idx!] v

set proxy=!v!
set wProxy=!v!

goto continue
) else if [!key!]==[-pkg-link] ( set /a "idx+=1" & call :eval arg[!idx!] v
Expand Down Expand Up @@ -523,7 +524,7 @@ if defined xmgrtest (

:: keep it as the last one-line command before final exit!
if defined mgrUp (
(copy /B/Y "!wPkgPath!\\DllExport.bat" "!fManager!" > nul) && ( echo Manager has been updated. & exit /B !EXIT_CODE! ) || ( echo -mgr-up failed. & exit /B %ERRORLEVEL% )
(copy /B/Y "!wPkgPath!\\DllExport.bat" "!fManager!" > nul) && ( echo Manager has been updated. & exit /B 0 ) || ( (echo -mgr-up failed:!EXIT_CODE! 1>&2) & exit /B 1 )
)

exit /B !EXIT_CODE!
Expand Down
96 changes: 96 additions & 0 deletions Wizard/Caller.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.Diagnostics;

namespace net.r_eg.DllExport.Wizard
{
internal class Caller
{
internal const int WAIT_INF = -1;
internal const int WAIT_NOT = 0;

protected string workingDir;

public int Shell(string cmd, int wait = WAIT_NOT, Action<Process> after = null, DataReceivedEventHandler stdout = null, DataReceivedEventHandler stderr = null)
{
return Run("cmd", $"/C \"{cmd}\"", wait, after, stdout, stderr);
}

public int Run(string file, string args = "", int wait = WAIT_NOT, Action<Process> after = null, DataReceivedEventHandler stdout = null, DataReceivedEventHandler stderr = null)
{
Process p = Prepare(file, args, stdout != null || stderr != null);

if(stdout != null) {
p.OutputDataReceived += stdout;
}

if(stderr != null) {
p.ErrorDataReceived += stderr;
}

p.Start();

if(p.StartInfo.RedirectStandardOutput) {
p.BeginOutputReadLine();
}

if(p.StartInfo.RedirectStandardError) {
p.BeginErrorReadLine();
}

p.WaitForExit(wait);
after?.Invoke(p);

return p.HasExited ? p.ExitCode : -1;
}

public Caller(string slnDir)
{
workingDir = slnDir ?? throw new ArgumentNullException(nameof(slnDir));
}

protected Process Prepare(string file, string args, bool hidden)
{
var p = new Process();

p.StartInfo.FileName = file;
p.StartInfo.Arguments = args;
p.StartInfo.WorkingDirectory = workingDir;

if(!hidden) {
p.StartInfo.UseShellExecute = true;
return p;
}

p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
return p;
}
}
}
11 changes: 11 additions & 0 deletions Wizard/DllExportCfgTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ public string MgrArgs
/// Version of the package that invokes target.
/// </summary>
public string PkgVer
{
get => _pkgVer;
set => _pkgVer = value.Trim();
}
private string _pkgVer;

/// <summary>
/// Proxy configuration if presented in `-proxy` key.
/// </summary>
public string Proxy
{
get;
set;
Expand Down Expand Up @@ -309,6 +319,7 @@ private void PrintKeys(Message.Level level)
LSender.Send(this, $"MetaCor: '{MetaCor}'", level);
LSender.Send(this, $"MgrArgs: '{MgrArgs}'", level);
LSender.Send(this, $"PkgVer: '{PkgVer}'", level);
LSender.Send(this, $"Proxy: '{Proxy}'", level);
LSender.Send(this, $"DxpTarget: '{DxpTarget}'", level);
LSender.Send(this, $"RootPath: '{RootPath}'", level);
LSender.Send(this, $"Storage: '{CfgStorage}'", level);
Expand Down
14 changes: 13 additions & 1 deletion Wizard/Extensions/CollectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,25 @@ public static class CollectionExtension
/// <param name="items"></param>
/// <param name="act">The action that should be executed for each item.</param>
public static void ForEach<T>(this IEnumerable<T> items, Action<T> act)
{
items?.ForEach((x, i) => act(x));
}

/// <summary>
/// Foreach in Linq manner.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="act">The action that should be executed for each item.</param>
public static void ForEach<T>(this IEnumerable<T> items, Action<T, long> act)
{
if(items == null) {
return;
}

long n = 0;
foreach(var item in items) {
act(item);
act(item, n++);
}
}

Expand Down
5 changes: 5 additions & 0 deletions Wizard/IWizardConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public interface IWizardConfig
/// </summary>
string PkgVer { get; }

/// <summary>
/// Proxy configuration if presented in `-proxy` key.
/// </summary>
string Proxy { get; }

/// <summary>
/// Path to external storage if used.
/// </summary>
Expand Down
149 changes: 149 additions & 0 deletions Wizard/PackageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using net.r_eg.MvsSln.Log;

namespace net.r_eg.DllExport.Wizard
{
internal class PackageInfo
{
protected readonly IExecutor exec;
private readonly string proxy;

public string Activated => exec.Config.PkgVer;

public Task<IEnumerable<string>> GetFromGitHubAsync()
=> GetFromRemoteAsync("https://3F.github.io/DllExport/data/pkgrel");

public Task<IEnumerable<string>> GetFromRemoteAsync(string url)
=> RcvStringOrActivatedAsync(url)
.ContinueWith(r => Detect301(r.Result))
.ContinueWith(r => Regex.Matches(r.Result, @"^\s*([^#\r\n].+)$", RegexOptions.Multiline)
.Cast<Match>()
.Select(x => x.Groups[1].Value));

public PackageInfo(IExecutor exec)
{
this.exec = exec ?? throw new ArgumentNullException(nameof(exec));
proxy = exec.Config.Proxy?.Trim();
}

/// <summary>
/// Emulates an emergency 301 through special command due to unsupported servers like GitHub pages.
/// Format: `@301=url` EOF
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected string Detect301(string input)
{
const string R301 = "@301=";

if(input?.StartsWith(R301) == false) {
return input;
}
var url = input.Substring(R301.Length);

LSender.Send(this, $"{R301}{url}", Message.Level.Debug);
return RcvStringOrActivatedAsync(url).Result;
}

protected Task<string> RcvStringOrActivatedAsync(string target)
=> RcvStringAsync(target, (ex) => Activated);

// while we're still using .netfx 4.0
protected Task<string> RcvStringAsync(string target, Func<Exception, string> failed)
{
var tcs = new TaskCompletionSource<string>();
var url = new Uri(target);

using(var wc = new WebClient())
{
if(!string.IsNullOrEmpty(proxy)) {
wc.Proxy = GetProxy(proxy);
}

wc.UseDefaultCredentials = true;

if(wc.Proxy.Credentials == null) {
wc.Proxy.Credentials = CredentialCache.DefaultCredentials;
}

long bytesReceived = 0;
wc.DownloadStringCompleted += (sender, e) =>
{
if(e.Error != null)
{
//tcs.TrySetException(e.Error);
LSender.Send(this, $"Rcv failed: {e.Error.Message}", Message.Level.Debug);
tcs.TrySetResult(failed(e.Error));
}
else if(e.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(e.Result);
LSender.Send(this, $"Rcv Done: {bytesReceived} bytes.", Message.Level.Debug);
}
};

wc.DownloadProgressChanged += (sender, e) => bytesReceived = e.BytesReceived;

LSender.Send(this, $"Get data: {url}", Message.Level.Debug);
wc.DownloadStringAsync(url);
return tcs.Task;
}
}

private WebProxy GetProxy(string cfg)
{
LSender.Send(this, $"Configure proxy: {cfg}", Message.Level.Debug);

var auth = cfg.Split('@');
if(auth.Length <= 1)
{
LSender.Send(this, $"Use proxy: `{auth[0]}`", Message.Level.Debug);
return new WebProxy(auth[0], false);
}

var login = auth[0].Split(':');
LSender.Send(this, $"Use proxy: `{auth[1]}` /login: {login[0]}", Message.Level.Debug);

return new WebProxy(auth[1], false)
{
Credentials = new NetworkCredential(
login[0],
(login.Length > 1) ? login[1] : null
)
};
}
}
}
Loading

0 comments on commit a998821

Please sign in to comment.