using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using HtmlAgilityPack;
namespace ReleaseBot
{
class Program
{
private const string V = "1.34"; // <--- numpy.net version!
private const string PythonNetVersion = "3.0.1";
private const string PythonVersion = "3.11"; // relevant only for Numpy with included binaries
private const string NumpyVersion = "1.23.5";
private const string ProjectPath = "../../../Numpy";
private const string ProjectName = "Numpy.csproj";
private const string ProjectPath2 = "../../../Numpy.Bare";
private const string ProjectName2 = "Numpy.Bare.csproj";
private const string Description = "C# bindings for NumPy on {0} - a fundamental library for scientific computing, machine learning and AI. Does require Python {1} with NumPy {2} installed!";
private const string Tags = "Data science, Machine Learning, ML, AI, Scientific Computing, NumPy, Linear Algebra, FFT, SVD, BLAS, Vector, Matrix, Python";
static void Main(string[] args)
{
// ==> Numpy
ProcessNumpy();
// ==> Numpy Bare
// TODO: release numpy bare for the new Pythonnet 3.0.0
//// first delete old packages as to not upload them again
//foreach (var nuget in Directory.GetFiles(Path.Combine(ProjectPath2, "bin", "Release"), "*.nupkg"))
//{
// File.Delete(nuget);
//}
//var specs = new ReleaseSpec[]
//{
// // linux
// new ReleaseSpec() { CPythonVersion = "2.7", Platform="Linux", },
// new ReleaseSpec() { CPythonVersion = "3.5", Platform="Linux", },
// new ReleaseSpec() { CPythonVersion = "3.6", Platform="Linux", },
// new ReleaseSpec() { CPythonVersion = "3.8", Platform="Linux", },
// new ReleaseSpec() { CPythonVersion = "3.7", Platform="Linux", },
// // mac
// new ReleaseSpec() { CPythonVersion = "2.7", Platform="OSX", },
// new ReleaseSpec() { CPythonVersion = "3.5", Platform="OSX", },
// new ReleaseSpec() { CPythonVersion = "3.6", Platform="OSX", },
// new ReleaseSpec() { CPythonVersion = "3.8", Platform="OSX", },
// new ReleaseSpec() { CPythonVersion = "3.7", Platform="OSX", },
// // win
// new ReleaseSpec() { CPythonVersion = "2.7", Platform="Win64", },
// new ReleaseSpec() { CPythonVersion = "3.5", Platform="Win64", },
// new ReleaseSpec() { CPythonVersion = "3.6", Platform="Win64", },
// new ReleaseSpec() { CPythonVersion = "3.8", Platform="Win64", },
// new ReleaseSpec() { CPythonVersion = "3.7", Platform="Win64", },
//};
//foreach (var spec in specs)
//{
// spec.Version = $"{spec.CPythonVersion}.{V}";
// spec.PythonNetVersion = $"{PythonNetVersion}";
// spec.NumpyVersion = NumpyVersion;
// spec.Description = string.Format(Description, spec.Platform, spec.CPythonVersion, spec.NumpyVersion);
// spec.PackageTags = Tags;
// spec.RelativeProjectPath = ProjectPath2;
// spec.ProjectName = ProjectName2;
// var py = spec.CPythonVersion.Replace(".", "");
// switch (spec.Platform)
// {
// case "Linux":
// spec.PackageId = "Numpy.Bare.Mono";
// spec.PythonNet = $"pythonnet_netstandard_py{py}_linux";
// break;
// case "OSX":
// spec.PackageId = "Numpy.Bare.OSX";
// spec.PythonNet = $"pythonnet_netstandard_py{py}_osx";
// break;
// case "Win64":
// spec.PackageId = "Numpy.Bare";
// spec.PythonNet = $"pythonnet_netstandard_py{py}_win";
// break;
// }
// spec.Process();
//}
var key = File.ReadAllText("../../nuget.key").Trim();
foreach (var nuget in Directory.GetFiles(Path.Combine(ProjectPath2, "bin", "Release"), "*.nupkg"))
{
Console.WriteLine("Push " + nuget);
var arg = $"push -Source https://api.nuget.org/v3/index.json -ApiKey {key} {nuget}";
var p = new Process() { StartInfo = new ProcessStartInfo("nuget.exe", arg) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false } };
p.OutputDataReceived += (x, data) => Console.WriteLine(data.Data);
p.ErrorDataReceived += (x, data) => Console.WriteLine("Error: " + data.Data);
p.Start();
p.WaitForExit();
Console.WriteLine("... pushed");
}
Thread.Sleep(3000);
}
private static void ProcessNumpy()
{
// first delete old packages as to not upload them again
foreach (var nuget in Directory.GetFiles(Path.Combine(ProjectPath, "bin", "Release"), "*.nupkg"))
{
File.Delete(nuget);
}
var spec = new ReleaseSpec()
{
Version = $"{PythonVersion}.{V}",
ProjectName = ProjectName,
RelativeProjectPath = ProjectPath,
PackageId = "Numpy",
Description = @"C# bindings for NumPy - a fundamental library for scientific computing, machine learning and AI. Does not require a local Python installation!",
PackageTags = "Data science, Machine Learning, ML, AI, Scientific Computing, NumPy, Linear Algebra, FFT, SVD, Matrix, Python",
UsePythonIncluded = true,
};
spec.Process();
// nuget
var key = File.ReadAllText("../../nuget.key").Trim();
foreach (var nuget in Directory.GetFiles(Path.Combine(ProjectPath, "bin", "Release"), "*.nupkg"))
{
Console.WriteLine("Push " + nuget);
var arg = $"push -Source https://api.nuget.org/v3/index.json -ApiKey {key} {nuget}";
var p = new Process() { StartInfo = new ProcessStartInfo("nuget.exe", arg) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false } };
p.OutputDataReceived += (x, data) => Console.WriteLine(data.Data);
p.ErrorDataReceived += (x, data) => Console.WriteLine("Error: " + data.Data);
p.Start();
p.WaitForExit();
Console.WriteLine("... pushed");
}
}
}
public class ReleaseSpec
{
///
/// The assembly / nuget package version
///
public string Version;
public string CPythonVersion;
public string Platform;
///
/// Project description
///
public string Description;
///
/// Project description
///
public string PackageTags;
///
/// Nuget package id
///
public string PackageId;
///
/// PythonNet package name
///
public string PythonNet;
///
/// PythonNet Version
///
public string PythonNetVersion;
///
/// Name of the csproj file
///
public string ProjectName;
///
/// Path to the csproj file, relative to the execution directory of ReleaseBot
///
public string RelativeProjectPath;
public string FullProjectPath => Path.Combine(RelativeProjectPath, ProjectName);
///
/// Uses Python.Included
///
public bool UsePythonIncluded { get; set; }
// Numpy Version
public string NumpyVersion { get; set; }
public void Process()
{
if (!File.Exists(FullProjectPath))
throw new InvalidOperationException("Project not found at: " + FullProjectPath);
// modify csproj
var doc = new HtmlDocument() { OptionOutputOriginalCase = true, OptionWriteEmptyNodes = true };
doc.Load(FullProjectPath);
var group0 = doc.DocumentNode.Descendants("propertygroup").FirstOrDefault();
SetInnerText(group0.Element("version"), Version);
Console.WriteLine("Version: " + group0.Element("version").InnerText);
SetInnerText(group0.Element("description"), Description);
Console.WriteLine("Description: " + group0.Element("description").InnerText);
if (!UsePythonIncluded)
{
SetInnerText(group0.Element("packageid"), PackageId);
var group1 = doc.DocumentNode.Descendants("itemgroup").FirstOrDefault(g => g.Element("packagereference") != null);
var reference = group1.Descendants("packagereference").ToArray()[1];
reference.Attributes["Include"].Value = PythonNet;
reference.Attributes["Version"].Value = PythonNetVersion;
}
doc.Save(FullProjectPath);
// now build in release mode
RestoreNugetDependencies();
Build();
}
private void RestoreNugetDependencies()
{
Console.WriteLine("Fetch Nugets " + Description);
var p = new Process()
{
StartInfo = new ProcessStartInfo("dotnet", "restore")
{ WorkingDirectory = Path.GetFullPath(RelativeProjectPath) }
};
p.Start();
p.WaitForExit();
}
private void Build()
{
Console.WriteLine("Build " + Description);
var p = new Process()
{
StartInfo = new ProcessStartInfo("dotnet", "build -c Release")
{ WorkingDirectory = Path.GetFullPath(RelativeProjectPath) }
};
p.Start();
p.WaitForExit();
}
private void SetInnerText(HtmlNode node, string text)
{
node.ReplaceChild(HtmlTextNode.CreateNode(text), node.FirstChild);
}
}
}