Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ChocolateStore/Arguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
class Arguments
{
public string Directory { get; set; }
public string Url { get; set; }
public string PackageName { get; set; }
}
}
5 changes: 5 additions & 0 deletions src/ChocolateStore/ChocolateStore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="HtmlAgilityPack, Version=1.6.17.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<HintPath>..\..\packages\HtmlAgilityPack.1.6.17\lib\Net40-client\HtmlAgilityPack.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ionic.Zip">
<HintPath>..\..\packages\DotNetZip.1.9.3\lib\net20\Ionic.Zip.dll</HintPath>
</Reference>
Expand All @@ -43,6 +47,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Arguments.cs" />
<Compile Include="PackageInfo.cs" />
<Compile Include="PackageCacher.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
200 changes: 105 additions & 95 deletions src/ChocolateStore/PackageCacher.cs
Original file line number Diff line number Diff line change
@@ -1,101 +1,111 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Ionic.Zip;

namespace ChocolateStore
{
class PackageCacher
{

private const string INSTALL_FILE = "tools/chocolateyInstall.ps1";

public delegate void FileHandler(string fileName);
public delegate void DownloadFailedHandler(string url, Exception ex);

public event FileHandler SkippingFile = delegate { };
public event FileHandler DownloadingFile = delegate { };
public event DownloadFailedHandler DownloadFailed = delegate { };

public void CachePackage(string dir, string url)
{
var packagePath = DownloadFile(url, dir);

using (var zip = ZipFile.Read(packagePath))
{
var entry = zip.FirstOrDefault(x => string.Equals(x.FileName, INSTALL_FILE, StringComparison.OrdinalIgnoreCase));

if (entry != null) {
string content = null;
var packageName = Path.GetFileNameWithoutExtension(packagePath);

using (MemoryStream ms = new MemoryStream()) {
entry.Extract(ms);
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Ionic.Zip;
using HtmlAgilityPack;

namespace ChocolateStore
{
class PackageCacher
{

private const string INSTALL_FILE = "tools/chocolateyInstall.ps1";

public delegate void FileHandler(string fileName);
public delegate void DownloadFailedHandler(string url, Exception ex);

public event FileHandler SkippingFile = delegate { };
public event FileHandler DownloadingFile = delegate { };
public event DownloadFailedHandler DownloadFailed = delegate { };

public void CachePackage(string packageName, string directory)
{
var packageInfo = new PackageInfo(packageName);

var packagePath = DownloadFile(packageInfo.url, directory);

using (var zip = ZipFile.Read(packagePath))
{
var entry = zip.FirstOrDefault(x => string.Equals(x.FileName, INSTALL_FILE, StringComparison.OrdinalIgnoreCase));

if (entry != null) {
string content = null;

using (MemoryStream ms = new MemoryStream()) {
entry.Extract(ms);
ms.Position = 0;
using (StreamReader reader = new StreamReader(ms, true))
{
content = reader.ReadToEnd();
}
}

content = CacheUrlFiles(Path.Combine(dir, packageName), content);
zip.UpdateEntry(INSTALL_FILE, content);
zip.Save();

}

}

}

private string CacheUrlFiles(string folder, string content)
{

const string pattern = "(?<=['\"])http[\\S ]*(?=['\"])";

if (!Directory.Exists(folder)) {
Directory.CreateDirectory(folder);
}

return Regex.Replace(content, pattern, new MatchEvaluator(m => DownloadFile(m.Value, folder)));

}

private string DownloadFile(string url, string destination)
{

try
{
var request = WebRequest.Create(url);
var response = request.GetResponse();
var fileName = Path.GetFileName(response.ResponseUri.LocalPath);
var filePath = Path.Combine(destination, fileName);

if (File.Exists(filePath))
{
SkippingFile(fileName);
}
else
{
DownloadingFile(fileName);
using (var fs = File.Create(filePath))
{
response.GetResponseStream().CopyTo(fs);
}
}

return filePath;
}
catch (Exception ex)
{
DownloadFailed(url, ex);
return url;
}

}

}
}

content = CacheUrlFiles(Path.Combine(directory, packageName), content);
zip.UpdateEntry(INSTALL_FILE, content);
zip.Save();

}

}

if (packageInfo.dependencies != null)
{
foreach (var dep in packageInfo.dependencies)
{
CachePackage(dep, directory);
}
}

}

private string CacheUrlFiles(string folder, string content)
{

const string pattern = "(?<=['\"])http[\\S ]*(?=['\"])";

if (!Directory.Exists(folder)) {
Directory.CreateDirectory(folder);
}

return Regex.Replace(content, pattern, new MatchEvaluator(m => DownloadFile(m.Value, folder)));

}

private string DownloadFile(string url, string destination)
{

try
{
var request = WebRequest.Create(url);
var response = request.GetResponse();
var fileName = Path.GetFileName(response.ResponseUri.LocalPath);
var filePath = Path.Combine(destination, fileName);

if (File.Exists(filePath))
{
SkippingFile(fileName);
}
else
{
DownloadingFile(fileName);
using (var fs = File.Create(filePath))
{
response.GetResponseStream().CopyTo(fs);
}
}

return filePath;
}
catch (Exception ex)
{
DownloadFailed(url, ex);
return url;
}

}

}
}
29 changes: 29 additions & 0 deletions src/ChocolateStore/PackageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Linq;
using HtmlAgilityPack;

namespace ChocolateStore
{
class PackageInfo
{
public string name { get; set; }
public string url { get; set; }
public string[] dependencies { get; set; }
public PackageInfo(string packageName)
{
Console.WriteLine("Reading package '{0}'", packageName);
this.name = packageName;
var web = new HtmlWeb();
var doc = web.Load("https://chocolatey.org/packages/" + packageName);
this.url = doc.DocumentNode
.SelectSingleNode("//a[contains(@title, 'nupkg')]")
.Attributes["href"].Value;
Console.WriteLine("package URL: '{0}'", this.url);
try
{
this.dependencies = doc.DocumentNode.SelectNodes("//ul[@id='dependencySets']//a").Select(node => node.InnerText).ToArray<string>();
} catch (Exception) { }

}
}
}
31 changes: 20 additions & 11 deletions src/ChocolateStore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static void Main(string[] args)

if (arguments != null)
{
cacher.CachePackage(arguments.Directory, arguments.Url);
cacher.CachePackage(arguments.PackageName, arguments.Directory);
}

}
Expand All @@ -39,25 +39,24 @@ private static Arguments ParseArguments(string[] args)

if (args.Length != 2)
{
WriteError("USAGE: ChocolateStore <directory> <url>");
WriteError("USAGE: ChocolateStore <directory> <package>");
return null;
}

arguments.Directory = args[0];

if (!Directory.Exists(arguments.Directory))
{
WriteError("Directory '{0}' does not exist.", arguments.Directory);
return null;
if (!PromptConfirm("Directory '{0}' does not exist. Create?", arguments.Directory))
{
WriteError("Directory '{0}' does not exist.", arguments.Directory);
return null;
}
Directory.CreateDirectory(arguments.Directory);
Console.WriteLine("Created Directory '{0}'", arguments.Directory);
}

arguments.Url = args[1];

if (!Uri.IsWellFormedUriString(arguments.Url, UriKind.Absolute))
{
WriteError("URL '{0}' is invalid.", arguments.Url);
return null;
}
arguments.PackageName = args[1];

return arguments;

Expand Down Expand Up @@ -100,5 +99,15 @@ private static void WriteError(string format, params object[] arg)
Console.ResetColor();
}

private static bool PromptConfirm(string format, params object[] arg)
{
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write(format + " [y/n] ", arg);
Console.ResetColor();
ConsoleKey response = Console.ReadKey(false).Key;
Console.WriteLine();
return response == ConsoleKey.Y;
}

}
}
1 change: 1 addition & 0 deletions src/ChocolateStore/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.9.3" targetFramework="net40-Client" />
<package id="HtmlAgilityPack" version="1.6.17" targetFramework="net40-client" />
</packages>