using Katteker; using KattekerCreator.Helper; using KattekerCreator.Types; using McMaster.Extensions.CommandLineUtils; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace KattekerCreator { internal class Program : ProgramArguments { private static readonly string BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; private static string MakeNsis => Path.Combine(BaseDirectory, "nsis", "makensis.exe"); private AssemblyFileInfo _assemblyFileInfo; private TemporaryDirectory _tempDir; private Releases _releases; static int Main(string[] args) { return CommandLineApplication.Execute(args); } protected override int OnExecute(CommandLineApplication app) { //Override commandline arguments, if 'ConfigurationPath' is set. if (TryReadConfigFile(ConfigurationPath, out var dictionary)) { OverridePropertyValues(this, dictionary); } //Create temp-dir using (_tempDir = TemporaryDirectory.Create()) { _releases = new Releases(OutputDir); //Modify AppStub var appStubFile = ModifyAppStub(); //Acquire infos from Executable. _assemblyFileInfo = new AssemblyFileInfo(ProgramFile, Version, _tempDir.Path); var configFile = CreateConfigFile(); //Generate NSIS-Script var additionalFiles = new[] { new PhysicalFile(appStubFile, Path.GetFileName(ProgramFile)), new PhysicalFile(configFile, Path.Combine($"app-{_assemblyFileInfo.AssemblyVersion}", Path.GetFileName(configFile) ?? string.Empty)) }; var templateFile = GenerateNsisTemplate(additionalFiles); //Start makensis.exe var setupFilePath = CompileSetupScript(templateFile); //TODO Sign setup Utils.SignExecutable(CertificatePath, CertificatePassword, setupFilePath); //Copy to Output-Folder CopyToOutputFolder(setupFilePath); //Create/Modify RELEASE File var releaseEntry = AddPackageToReleaseFile(setupFilePath); //Copy installer as setup.exe CopyAsSetup(setupFilePath, releaseEntry); return 0; } } private void CopyToOutputFolder(string setupFilePath) { if (setupFilePath == null) throw new ArgumentNullException(nameof(setupFilePath)); var setupFile = Path.GetFileName(setupFilePath); if (string.IsNullOrEmpty(setupFile)) throw new ArgumentException(); if (!File.Exists(setupFilePath)) throw new FileNotFoundException(setupFile); if (!Directory.Exists(OutputDir)) Directory.CreateDirectory(OutputDir); if (!string.IsNullOrEmpty(ChangeLog)) { var changeLogFilename = Path.GetFileName(ChangeLog); if (!File.Exists(ChangeLog)) throw new FileNotFoundException(ChangeLog); File.Copy(ChangeLog, Path.Combine(OutputDir, changeLogFilename), true); } File.Copy(setupFilePath, Path.Combine(OutputDir, setupFile), true); } private string ModifyAppStub() { var baseFile = Path.Combine(BaseDirectory, "contrib", Constants.ExecutionStub); var targetFile = Path.Combine(_tempDir.Path, Constants.ExecutionStub); File.Copy(baseFile, targetFile); Utils.CopyResources(ProgramFile, targetFile); Utils.SignExecutable(CertificatePath, CertificatePassword, targetFile); return targetFile; } private string CreateConfigFile() { var pathToFile = Path.Combine(_tempDir.Path, Constants.KattekerConfig); var kattekerConfig = new KattekerConfig { Publish = PublishDir ?? OutputDir, Changelog = Path.GetFileName(ChangeLog), Channel = Channel }; kattekerConfig.WriteToFile(pathToFile); return pathToFile; } private static string GenerateFilename(string name, string version, string extension, bool isDelta = false) => $"{name}-{version}-{(isDelta ? "delta" : "full")}.{extension}"; private string GenerateNsisTemplate(IEnumerable additionalFiles) { try { var assemblyName = Path.GetFileNameWithoutExtension(_assemblyFileInfo.FileInfo.Name); var outFile = Path.Combine(_tempDir.Path, GenerateFilename(assemblyName, _assemblyFileInfo.AssemblyVersion.ToString(), "nsi")); var setupTemplate = new SetupTemplate { Executable = _assemblyFileInfo.FileInfo.Name, AssemblyName = assemblyName, AppName = _assemblyFileInfo.ProductName, CompanyName = _assemblyFileInfo.Company, Description = _assemblyFileInfo.Description, IconPath = _assemblyFileInfo.AssemblyIconPath, Version = _assemblyFileInfo.AssemblyVersion, HelpUrl = "http://example.org", //Not used at the moment. UserInterface = Path.Combine(BaseDirectory, "contrib", "LoadingBar_Icon.exe"), UninstallIcon = Path.Combine(BaseDirectory, "contrib", "uninstall.ico"), OutFile = Path.GetFileName(Path.ChangeExtension(outFile, "exe")), ReleaseChannel = Channel }; var path = Path.GetDirectoryName(ProgramFile) ?? string.Empty; setupTemplate.Files.AddRange(additionalFiles); var files = Utils.EnumerateFiles(path, Filter).ToArray(); setupTemplate.InstallSize = (long)Math.Floor(files.Sum(x => x.Length) / 1024f); setupTemplate.Files.AddRange(files.Select(x => new PhysicalFile(x.FullName, x.FullName.Replace(path, $"app-{_assemblyFileInfo.AssemblyVersion}")))); var transformText = setupTemplate.TransformText(); File.WriteAllText(outFile, transformText); return outFile; } catch (Exception e) { Log.WriteErrorLine(e.Message); throw; } } private static string CompileSetupScript(string templateFile) { if (!File.Exists(MakeNsis)) throw new FileNotFoundException("NSIS not installed properly."); var exitCode = Utils.ExecuteProcess(MakeNsis, $"\"{templateFile}\""); if (exitCode != 0) throw new Exception($"{Path.GetFileName(MakeNsis)} has exited with exit code: {exitCode}"); return Path.ChangeExtension(templateFile, "exe"); } private ReleaseEntry AddPackageToReleaseFile(string setupFilePath) { try { var result = _releases.Add(setupFilePath, _assemblyFileInfo.AssemblyVersion); _releases.WriteReleaseFile(); return result; } catch (Exception e) { Log.WriteErrorLine(e.Message); throw; } } private void CopyAsSetup(string setupFilePath, ReleaseEntry releaseEntry) { if (!_releases.IsLatestEntry(releaseEntry)) return; var target = Path.Combine(OutputDir, Constants.SetupFile); File.Delete(target); File.Copy(setupFilePath, target); } } }